#!/usr/bin/ruby require 'time' require 'date' require 'yaml' require 'pp' class String def cap_first self.sub(/^\w/) do |m| m.upcase end end end class WWID attr_accessor :content, :sections, :current_section, :doing_file, :config def initialize(input=nil) @content = {} @config = read_config @config['doing_file'] ||= "~/what_was_i_doing.md" @config['current_section'] ||= 'Currently' @config['templates'] ||= {} @config['templates']['default'] ||= { 'date_format' => '%Y-%m-%d %H:%M', 'template' => '%date | %title%note', 'wrap_width' => 0 } @config['templates']['today'] ||= { 'date_format' => '%_I:%M%P', 'template' => '%date: %title%note', 'wrap_width' => 0 } @config['templates']['last'] ||= { 'date_format' => '%_I:%M%P', 'template' => '%date: %title%note', 'wrap_width' => 60 } @config['templates']['recent'] ||= { 'date_format' => '%_I:%M%P', 'template' => '%shortdate: %title', 'wrap_width' => 60 } @doing_file = File.expand_path(config['doing_file']) @current_section = config['current_section'] @default_template = config['templates']['default']['template'] @default_date_format = config['templates']['default']['date_format'] @config[:include_notes] ||= true File.open(File.expand_path(DOING_CONFIG), 'w') { |yf| YAML::dump(config, yf) } if input.nil? create(@doing_file) unless File.exists?(@doing_file) input = IO.read(@doing_file) input = input.force_encoding('utf-8') if input.respond_to? :force_encoding elsif File.exists?(File.expand_path(input)) && File.file?(File.expand_path(input)) && File.stat(File.expand_path(input)).size > 0 input = IO.read(File.expand_path(input)) input = input.force_encoding('utf-8') if input.respond_to? :force_encoding @doing_file = File.expand_path(input) elsif input.length < 256 create(input) end @other_content_top = [] @other_content_bottom = [] section = "Uncategorized" lines = input.split(/\n/) current = 0 lines.each {|line| next if line =~ /^\s*$/ if line =~ /^(\w[\w ]+):\s*(@\S+\s*)*$/ section = $1 @content[section] = {} @content[section]['original'] = line @content[section]['items'] = [] current = 0 elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*)/ date = Time.parse($1) title = $2 @content[section]['items'].push({'title' => title, 'date' => date}) current += 1 else # if content[section]['items'].length - 1 == current if current == 0 @other_content_top.push(line) else if line =~ /^\S/ @other_content_bottom.push(line) else unless @content[section]['items'][current - 1].has_key? 'note' @content[section]['items'][current - 1]['note'] = [] end @content[section]['items'][current - 1]['note'].push(line) end end # end end } end def create(filename=nil) filename = @doing_file unless File.exists?(filename) && File.stat(filename).size > 0 File.open(filename,'w+') do |f| f.puts @current_section + ":" end end end def read_config if File.exists? File.expand_path(DOING_CONFIG) return YAML.load_file(File.expand_path(DOING_CONFIG)) else return {} end end def sections @content.keys end def add_section(title) @content[title.cap_first] = {'original' => "#{title}:", 'items' => []} end def add_item(title,section=nil,opt={}) section = @current_section if section.nil? add_section(section) unless @content.has_key?(section) opt[:date] ||= Time.now opt[:note] ||= "" entry = {'title' => title.strip.cap_first, 'date' => opt[:date]} unless opt[:note] =~ /^\s*$/s entry['note'] = opt[:note] end @content[section]['items'].push(entry) end def write(file=nil) if @other_content_top.empty? output = "" else output = @other_content_top.join("\n") + "\n" end @content.each {|title, section| output += section['original'] + "\n" output += list_section({:section => title, :template => "\t- %date | %title%note"}) } output += @other_content_bottom.join("\n") if file.nil? $stdout.puts output else if File.exists?(File.expand_path(file)) File.open(File.expand_path(file),'w+') do |f| f.puts output end end end end def choose_section sections.each_with_index {|section, i| puts "% 3d: %s" % [i+1, section] } print "> " num = STDIN.gets return false if num =~ /^[a-z ]*$/i return sections[num.to_i - 1] end def list_section(opt={}) opt[:count] ||= 0 count = opt[:count] - 1 opt[:section] ||= nil opt[:format] ||= @default_date_format opt[:template] ||= @default_template opt[:order] ||= "desc" opt[:today] ||= false if opt[:section].nil? opt[:section] = @content[choose_section] elsif opt[:section].class == String if @content.has_key? opt[:section] opt[:section] = @content[opt[:section]] else $stderr.puts "Section '#{opt[:section]}' not found" return end end if opt[:section].class != Hash $stderr.puts "Invalid section object" return end items = opt[:section]['items'].sort_by{|item| item['date'] } if opt[:today] items.delete_if {|item| item['date'] < Date.today.to_time }.reverse! else items = items.reverse[0..count] end items.reverse! if opt[:order] =~ /^asc/i out = "" items.each {|item| if (item.has_key?('note') && !item['note'].empty?) && @config[:include_notes] note_lines = item['note'].delete_if{|line| line =~ /^\s*$/ }.map{|line| "\t\t" + line.sub(/^\t\t/,'') } if opt[:wrap_width] && opt[:wrap_width] > 0 width = opt[:wrap_width] note_lines.map! {|line| line.strip.gsub(/(.{1,#{width}})(\s+|\Z)/, "\t\\1\n") } end note = "\n#{note_lines.join("\n").chomp}" else note = "" end output = opt[:template].dup output.sub!(/%date/,item['date'].strftime(opt[:format])) output.sub!(/%shortdate/) { if item['date'] > Date.today.to_time item['date'].strftime('%_I:%M%P') elsif item['date'] > (Date.today - 7).to_time item['date'].strftime('%a %-I:%M%P') elsif item['date'].year == Date.today.year item['date'].strftime('%b %d, %-I:%M%P') else item['date'].strftime('%b %d %Y, %-I:%M%P') end } output.sub!(/%title/) {|m| if opt[:wrap_width] && opt[:wrap_width] > 0 item['title'].gsub(/(.{1,#{opt[:wrap_width]}})(\s+|\Z)/, "\\1\n\t ").strip else item['title'].strip end } output.sub!(/%note/,note) output.sub!(/%odnote/,note.gsub(/\t\t/,"\t")) output.gsub!(/%hr(_under)?/) do |m| o = "" `tput cols`.to_i.times do o += $1.nil? ? "-" : "_" end o end out += output + "\n" } return out end def archive(section=nil) section = choose_section if section.nil? || section =~ /choose/i if sections.include?(section) items = @content[section]['items'] return if items.length < 10 @content[section]['items'] = items[0..5] add_section('Archive') unless sections.include?('Archive') @content['Archive']['items'] += items[6..-1] write(@doing_file) end end def all(order="") order = "asc" if order == "" cfg = @config['templates']['default_template'] list_section({:section => @current_section, :wrap_width => cfg['wrap_width'], :count => 0, :format => cfg['date_format'], :template => cfg['template'], :order => order}) end def today cfg = @config['templates']['today'] list_section({:section => @current_section, :wrap_width => cfg['wrap_width'], :count => 0, :format => cfg['date_format'], :template => cfg['template'], :order => "asc", :today => true}) end def recent(count=10,section=nil) cfg = @config['templates']['recent'] section ||= @current_section list_section({:section => section, :wrap_width => cfg['wrap_width'], :count => count, :format => cfg['date_format'], :template => cfg['template'], :order => "asc"}) end def last cfg = @config['templates']['last'] list_section({:section => @current_section, :wrap_width => cfg['wrap_width'], :count => 1, :format => cfg['date_format'], :template => cfg['template']}) end end # infile = "~/Dropbox/nvALT2.2/?? What was I doing.md" # wwid = WWID.new(infile) # wwid.add_item("Getting freaky with wwid CLI","Currently",{:date => Time.now}) # wwid.write(infile)