doing 1.0.8pre → 1.0.10pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fe65d7638817ae91c0b28db474b25e1db315c32d
4
- data.tar.gz: 2f3f3b3100bd5b9495be019787201b773d7f9e16
3
+ metadata.gz: f80fbe9f636b31d608391b7723a14407b515dbe7
4
+ data.tar.gz: 9aefb646ddc4465ed3b86a0dfc3403d3e1fc69be
5
5
  SHA512:
6
- metadata.gz: 945b6b6794717f6dfaa41b180209e28cf5cd219852c2098a862e940cb6989010c3aa79fdec5010bfffc82c43bc177667988d6357427d4f6c936efa26950b77e9
7
- data.tar.gz: afcc84a5dd4b5723e0b003aa68c444a44ead9b99b56e7e625508482bdd92982acb4c8dcee2859191b912cc3c0500c1dc5d7d37896c569f657c13314a5ffe0fc7
6
+ metadata.gz: fc9e0fa945d7cebb0402b3098a0879373907323b3ce6c6a3a5e26a710a2a11d3ec57bd1f1d9a1d3f0a0ec4ebc17b226f92f61f32faa4187e276ce029d43f90be
7
+ data.tar.gz: d559d691bd3ba1c9a360d08500b720790a99267c84ae05cd7962764b1cddf137f60197bae59fe418f9079609427420b12a9bc959f9e26dc66175e6cf6b8c6e5b
data/README.md CHANGED
@@ -89,7 +89,13 @@ A basic configuration looks like this:
89
89
  :include_notes: true
90
90
 
91
91
 
92
- The config file is stored in "~/.doingrc", and is created on the first run.
92
+ The config file is stored in "~/.doingrc", and is created on the first run.
93
+
94
+ ### Per-folder configuration
95
+
96
+ Any options found in a `.doingrc` anywhere in the hierarchy between your current folder and your home folder will be appended to the base configuration, overriding or extending existing options. This allows you to put a `.doingrc` file into the base of a project and add specific configurations (such as default tags) when working in that project on the command line.
97
+
98
+ Any part of the configuration can be copied into these local files and modified. You only need to include the parts you want to change or add.
93
99
 
94
100
  ### Doing file location
95
101
 
@@ -125,6 +131,7 @@ The config also contains templates for various command outputs. Include placehol
125
131
  - `%note`: Any note in the entry will be included here, a newline and tabs are automatically added.
126
132
  - `%odnote`: The notes with a leading tab removed (outdented note)
127
133
  - `%chompnote`: Notes on one line, beginning and trailing whitespace removed.
134
+ - `%section`: The section/project the entry is currently in
128
135
  - `%hr`: a horizontal rule (`-`) the width of the terminal
129
136
  - `%hr_under`: a horizontal rule (`_`) the width of the terminal
130
137
  - `%n`: inserts a newline
@@ -322,7 +329,7 @@ As mentioned above, `finish` also accepts `--back "2 hours"` (sets the finish da
322
329
 
323
330
  ... will mark the last three entries as "@client @cancelled." Add `-r` as a switch to remove the listed tags instead.
324
331
 
325
- You can optionally define keywords for common tasks and projects in your `.doingrc` file. When these keywords appear in an item title, they'll automatically be converted into @tags. The "whitelist" tags are exact (but case insensitive) matches. You can also define "synonyms" which will add a tag at the end based on keywords associated with it.
332
+ You can optionally define keywords for common tasks and projects in your `.doingrc` file. When these keywords appear in an item title, they'll automatically be converted into @tags. The "whitelist" tags are exact (but case insensitive) matches. You can also define "synonyms" which will add a tag at the end based on keywords associated with it. When defining synonym keys, be sure to indent but _not_ hyphenate the keys themselves, while hyphenating the list of synonyms at the same indent level as their key. See "playing" and "writing" in the list below for illustration. Follow standard yaml syntax.
326
333
 
327
334
  To add autotagging, include a section like this in your `~/.doingrc` file:
328
335
 
@@ -489,7 +496,7 @@ Please try not to email me directly about GitHub projects.
489
496
 
490
497
  I'll try to document some of the code structure as I flesh it out. I'm currently working on adding a CLI reporting structure and logging methods, as well as santizing and standardizing all the flags and switches for consistency. Feel free to [poke around](http://github.com/ttscoff/doing/), I'll try to add more comments in the future (and retroactively).
491
498
 
492
- ### Changelog
499
+ ## Changelog
493
500
 
494
501
  #### 1.0.8pre
495
502
 
data/bin/doing CHANGED
@@ -1,17 +1,29 @@
1
1
  #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
2
3
  require 'gli'
3
4
  require 'doing'
4
5
  require 'tempfile'
5
6
 
6
- if RUBY_VERSION.to_f > 1.9
7
- Encoding.default_external = Encoding::UTF_8
8
- Encoding.default_internal = Encoding::UTF_8
7
+ def class_exists?(class_name)
8
+ klass = Module.const_get(class_name)
9
+ return klass.is_a?(Class)
10
+ rescue NameError
11
+ return false
9
12
  end
10
13
 
14
+ if class_exists? 'Encoding'
15
+ Encoding.default_external = Encoding::UTF_8 if Encoding.respond_to?('default_external')
16
+ Encoding.default_internal = Encoding::UTF_8 if Encoding.respond_to?('default_internal')
17
+ end
18
+
19
+
11
20
  include GLI::App
12
21
  version Doing::VERSION
13
22
 
14
23
  wwid = WWID.new
24
+ wwid.user_home = %x{echo $HOME}.strip
25
+ wwid.configure
26
+
15
27
 
16
28
  program_desc 'A CLI for a What Was I Doing system'
17
29
 
@@ -120,7 +132,7 @@ command :note do |c|
120
132
 
121
133
  input = args.length > 0 ? args.join(" ") : ""
122
134
 
123
- prev_input = wwid.last_note(section)
135
+ prev_input = wwid.last_note(section) || ""
124
136
  if prev_input.class == Array
125
137
  prev_input = prev_input.join("\n")
126
138
  end
@@ -726,8 +738,13 @@ command :on do |c|
726
738
  start = wwid.chronify(date_string)
727
739
  finish = false
728
740
  end
741
+
729
742
  exit_now! "Unrecognized date string" unless start
730
743
 
744
+ message = "Date interpreted as #{start}"
745
+ message += " to #{finish}" if finish
746
+ wwid.results.push(message)
747
+
731
748
  options[:t] = true if options[:totals]
732
749
 
733
750
  puts wwid.list_date([start,finish],options[:s],options[:t],options[:output],{:totals => options[:totals]}).chomp
@@ -993,22 +1010,22 @@ command :config do |c|
993
1010
  c.action do |global_options,options,args|
994
1011
  if `uname` =~ /Darwins/
995
1012
  if options[:x]
996
- %x{open -a "#{wwid.config['editor_app']}" "#{File.join(Dir.home, DOING_CONFIG_NAME)}"}
1013
+ %x{open -a "#{wwid.config['editor_app']}" "#{File.join(Dir.home, wwid.default_config_file)}"}
997
1014
  elsif options[:a] || options[:b]
998
1015
  if options[:a]
999
- %x{open -a "#{options[:a]}" "#{File.join(Dir.home, DOING_CONFIG_NAME)}"}
1016
+ %x{open -a "#{options[:a]}" "#{File.join(Dir.home, wwid.default_config_file)}"}
1000
1017
  elsif options[:b]
1001
- %x{open -b #{options[:b]} "#{File.join(Dir.home, DOING_CONFIG_NAME)}"}
1018
+ %x{open -b #{options[:b]} "#{File.join(Dir.home, wwid.default_config_file)}"}
1002
1019
  end
1003
1020
  else
1004
1021
  raise "No EDITOR variable defined in environment" if options[:e].nil? && ENV['EDITOR'].nil?
1005
1022
  editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
1006
- system %Q{#{editor} "#{File.expand_path(DOING_CONFIG)}"}
1023
+ system %Q{#{editor} "#{File.join(Dir.home, wwid.default_config_file)}"}
1007
1024
  end
1008
1025
  else
1009
1026
  raise "No EDITOR variable defined in environment" if options[:e].nil? && ENV['EDITOR'].nil?
1010
1027
  editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
1011
- system %Q{#{editor} "#{File.join(Dir.home, DOING_CONFIG_NAME)}"}
1028
+ system %Q{#{editor} "#{File.join(Dir.home, wwid.default_config_file)}"}
1012
1029
  end
1013
1030
  end
1014
1031
  end
@@ -1047,7 +1064,7 @@ end
1047
1064
  post do |global,command,options,args|
1048
1065
  # Use skips_post before a command to skip this
1049
1066
  # block on that command only
1050
- puts wwid.results.join("\n")
1067
+ $stderr.puts wwid.results.join("\n")
1051
1068
  end
1052
1069
 
1053
1070
  on_error do |exception|
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '1.0.8pre'
2
+ VERSION = '1.0.10pre'
3
3
  end
data/lib/doing/wwid.rb CHANGED
@@ -9,18 +9,47 @@ class String
9
9
  end
10
10
  end
11
11
 
12
+ def link_urls(opt={})
13
+ opt[:format] ||= :html
14
+ if opt[:format] == :html
15
+ self.gsub(/(?mi)((http|https):\/\/)?([\w\-_]+(\.[\w\-_]+)+)([\w\-\.,\@?^=%&:\/~\+#]*[\w\-\@^=%&\/~\+#])?/) {|match|
16
+ m = Regexp.last_match
17
+ proto = m[1].nil? ? "http://" : ""
18
+ %Q{<a href="#{proto}#{m[0]}" title="link to #{m[0]}">[#{m[3]}]</a>}
19
+ }.gsub(/\<(\w+:.*?)\>/) {|match|
20
+ m = Regexp.last_match
21
+ unless m[1] =~ /<a href/
22
+ %Q{<a href="#{m[1]}" title="Link to #{m[1]}">[link]</a>}
23
+ else
24
+ match
25
+ end
26
+ }
27
+ else
28
+ self
29
+ end
30
+ end
12
31
  end
13
32
 
14
33
  class WWID
15
- attr_accessor :content, :sections, :current_section, :doing_file, :config, :results
16
-
34
+ attr_accessor :content, :sections, :current_section, :doing_file, :config, :user_home, :default_config_file, :results
17
35
 
18
36
  def initialize
19
37
  @content = {}
38
+ @doingrc_needs_update = false
39
+ @default_config_file = '.doingrc'
40
+ end
41
+
42
+ def configure(opt={})
20
43
  @timers = {}
44
+ @config_file == File.join(@user_home, @default_config_file)
45
+
21
46
  @config = read_config
47
+ user_config = @config.dup
22
48
  @results = []
23
49
 
50
+ @config['autotag'] ||= {}
51
+ @config['autotag']['whitelist'] ||= []
52
+ @config['autotag']['synonyms'] ||= {}
24
53
  @config['doing_file'] ||= "~/what_was_i_doing.md"
25
54
  @config['current_section'] ||= 'Currently'
26
55
  @config['editor_app'] ||= nil
@@ -75,11 +104,19 @@ class WWID
75
104
 
76
105
  @config[:include_notes] ||= true
77
106
 
78
- File.open(home_config, 'w') { |yf| YAML::dump(config, yf) }
107
+ File.open(@config_file, 'w') { |yf| YAML::dump(@config, yf) } if @config != user_config || @doingrc_needs_update
108
+
109
+
110
+ @config = @config.deep_merge(@local_config)
111
+
112
+ @current_section = @config['current_section']
113
+ @default_template = @config['templates']['default']['template']
114
+ @default_date_format = @config['templates']['default']['date_format']
115
+
79
116
  end
80
117
 
81
118
  def init_doing_file(input=nil)
82
- @doing_file = File.expand_path(config['doing_file'])
119
+ @doing_file = File.expand_path(@config['doing_file'])
83
120
 
84
121
  if input.nil?
85
122
  create(@doing_file) unless File.exists?(@doing_file)
@@ -114,7 +151,7 @@ class WWID
114
151
  elsif line =~ /^\s*- (\d{4}-\d\d-\d\d \d\d:\d\d) \| (.*)/
115
152
  date = Time.parse($1)
116
153
  title = $2
117
- @content[section]['items'].push({'title' => title, 'date' => date})
154
+ @content[section]['items'].push({'title' => title, 'date' => date, 'section' => section})
118
155
  current += 1
119
156
  else
120
157
  # if content[section]['items'].length - 1 == current
@@ -127,7 +164,7 @@ class WWID
127
164
  unless @content[section]['items'][current - 1].has_key? 'note'
128
165
  @content[section]['items'][current - 1]['note'] = []
129
166
  end
130
- @content[section]['items'][current - 1]['note'].push(line)
167
+ @content[section]['items'][current - 1]['note'].push(line.gsub(/ *$/,''))
131
168
  end
132
169
  end
133
170
  # end
@@ -146,27 +183,42 @@ class WWID
146
183
  end
147
184
  end
148
185
 
149
- def home_config
150
- if Dir.respond_to?('home')
151
- File.join(Dir.home, DOING_CONFIG_NAME)
152
- else
153
- File.join(File.expand_path("~"), DOING_CONFIG_NAME)
154
- end
155
- end
186
+ def find_local_config
156
187
 
157
- def read_config
158
188
  config = {}
159
189
  dir = Dir.pwd
160
- while(dir != '/')
161
- if File.exists? File.join(dir, DOING_CONFIG_NAME)
162
- config = YAML.load_file(File.join(dir, DOING_CONFIG_NAME)).deep_merge!(config)
190
+
191
+ local_config_file = nil
192
+
193
+ while (dir != '/' && (dir =~ /[A-Z]:\//) == nil)
194
+ if File.exists? File.join(dir, @default_config_file)
195
+ return File.join(dir, @default_config_file)
163
196
  end
197
+
164
198
  dir = File.dirname(dir)
165
199
  end
166
- if config.empty? && File.exists?(home_config)
167
- config = YAML.load_file(home_config)
200
+
201
+ false
202
+ end
203
+
204
+ def read_config
205
+ if Dir.respond_to?('home')
206
+ @config_file = File.join(Dir.home, @default_config_file)
207
+ else
208
+ @config_file = File.join(File.expand_path("~"), @default_config_file)
209
+ end
210
+ @doingrc_needs_update = true if File.exists? @config_file
211
+ additional = find_local_config
212
+
213
+ begin
214
+ @config = YAML.load_file(@config_file) || {} if File.exists?(@config_file)
215
+ @local_config = YAML.load_file(additional) || {} if additional
216
+ @config.deep_merge(@local_config)
217
+ rescue
218
+ @config = {}
219
+ @local_config = {}
220
+ # raise "error reading config"
168
221
  end
169
- config
170
222
  end
171
223
 
172
224
  def fork_editor(input="")
@@ -220,38 +272,31 @@ class WWID
220
272
  [title, note]
221
273
  end
222
274
 
223
- # Converts simple strings into seconds that can be added to a Time object
224
- # Params:
225
- # +qty+:: HH:MM or XX[dhm][[XXhm][XXm]] (1d2h30m, 45m, 1.5d, 1h20m, etc.)
226
- # Returns:
227
- # seconds(Integer)
275
+ # Converts `input` string into a Time object when `input` takes on the
276
+ # following formats:
277
+ # - interval format e.g. '1d2h30m', '45m' etc.
278
+ # - a semantic phrase e.g. 'yesterday 5:30pm'
279
+ # - a strftime e.g. '2016-03-15 15:32:04 PDT'
228
280
  def chronify(input)
281
+ now = Time.now
282
+ raise "Invalid time expression #{input.inspect}" if input.to_s.strip == ""
283
+ secs_ago = if input.match /^(\d+)$/
284
+ # plain number, assume minutes
285
+ $1.to_i * 60
286
+ elsif (m = input.match /^((?<day>\d+)d)?((?<hour>\d+)h)?((?<min>\d+)m)?$/i)
287
+ # day/hour/minute format e.g. 1d2h30m
288
+ [[m['day'], 24*3600],
289
+ [m['hour'], 3600],
290
+ [m['min'], 60]].map {|qty, secs| qty ? (qty.to_i * secs) : 0 }.reduce(0, :+)
291
+ end
229
292
 
230
- had_to_try = Time.parse(input) rescue false
231
-
232
- if had_to_try.class == FalseClass
233
- if input =~ /^(\d+)([mhd])?$/i
234
- amt = $1
235
- type = $2.nil? ? "m" : $2
236
- input = case type.downcase
237
- when 'm'
238
- amt + " minutes ago"
239
- when 'h'
240
- amt + " hours ago"
241
- when 'd'
242
- amt + " days ago"
243
- else
244
- input
245
- end
246
- end
247
-
248
- Chronic.parse(input, {:context => :past, :ambiguous_time_range => 8})
293
+ if secs_ago
294
+ now - secs_ago
249
295
  else
250
- had_to_try
296
+ Chronic.parse(input, {:context => :past, :ambiguous_time_range => 8})
251
297
  end
252
298
  end
253
299
 
254
-
255
300
  # Converts simple strings into seconds that can be added to a Time object
256
301
  # Params:
257
302
  # +qty+:: HH:MM or XX[dhm][[XXhm][XXm]] (1d2h30m, 45m, 1.5d, 1h20m, etc.)
@@ -308,13 +353,13 @@ class WWID
308
353
  input = STDIN.gets
309
354
  if input =~ /^y/i
310
355
  add_section(frag.cap_first)
311
- write(doing_file)
356
+ write(@doing_file)
312
357
  return frag.cap_first
313
358
  end
314
359
  raise "Unknown section: #{frag}"
315
360
  end
316
361
  end
317
- section ? section.cap_first : section
362
+ section ? section.cap_first : guessed
318
363
  end
319
364
 
320
365
  def guess_view(frag,guessed=false)
@@ -349,9 +394,21 @@ class WWID
349
394
 
350
395
  title = [title.strip.cap_first] + @config['default_tags'].map{|t| '@' + t.sub(/^ *@/,'').chomp}
351
396
  title = autotag(title.join(' '))
352
- entry = {'title' => title, 'date' => opt[:back]}
397
+ unless @config['default_tags'].empty?
398
+ title += @config['default_tags'].map{|t|
399
+ unless t.nil?
400
+ dt = t.sub(/^ *@/,'').chomp
401
+ if title =~ /@#{dt}/
402
+ ""
403
+ else
404
+ '@' + dt
405
+ end
406
+ end
407
+ }.delete_if {|t| t == "" }.join(" ")
408
+ end
409
+ entry = {'title' => title.strip, 'date' => opt[:back]}
353
410
  unless opt[:note] =~ /^\s*$/s
354
- entry['note'] = opt[:note]
411
+ entry['note'] = opt[:note].map {|n| n.gsub(/ *$/,'')}
355
412
  end
356
413
  items = @content[section]['items']
357
414
  if opt[:timed]
@@ -462,7 +519,9 @@ class WWID
462
519
 
463
520
  if opt[:archive] && section != "Archive" && opt[:count] > 0
464
521
  # concat [count] items from [section] and archive section
465
- archived = @content[section]['items'][0..opt[:count]-1].concat(@content['Archive']['items'])
522
+ archived = @content[section]['items'][0..opt[:count]-1].map {|i|
523
+ i['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{i['section']}")
524
+ }.concat(@content['Archive']['items'])
466
525
  # chop [count] items off of [section] items
467
526
  @content[opt[:section]]['items'] = @content[opt[:section]]['items'][opt[:count]..-1]
468
527
  # overwrite archive section with concatenated array
@@ -543,7 +602,9 @@ class WWID
543
602
 
544
603
  if opt[:archive] && opt[:section] != "Archive"
545
604
  @results.push(%Q{Completed and archived "#{@content[opt[:section]]['items'][i]['title']}"})
546
- @content['Archive']['items'].push(@content[opt[:section]]['items'][i])
605
+ archive_item = @content[opt[:section]]['items'][i]
606
+ archive_item['title'] = i['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{i['section']}")
607
+ @content['Archive']['items'].push(archive_item)
547
608
  @content[opt[:section]]['items'].delete_at(i)
548
609
  else
549
610
  @results.push(%Q{Completed "#{@content[opt[:section]]['items'][i]['title']}"})
@@ -555,9 +616,9 @@ class WWID
555
616
 
556
617
  if opt[:new_item]
557
618
  title, note = format_input(opt[:new_item])
558
- note.push(opt[:note]) if opt[:note]
619
+ note.push(opt[:note].gsub(/ *$/,'')) if opt[:note]
559
620
  title += " @#{tag}"
560
- add_item(title.cap_first, opt[:section], {:note => note, :back => opt[:back]})
621
+ add_item(title.cap_first, opt[:section], {:note => note.gsub(/ *$/,''), :back => opt[:back]})
561
622
  end
562
623
 
563
624
  write(@doing_file)
@@ -748,7 +809,7 @@ class WWID
748
809
  raise "Unknown output format" unless opt[:output] =~ /(template|html|csv|json|timeline)/
749
810
  end
750
811
  if opt[:output] == "csv"
751
- output = [CSV.generate_line(['date','title','note','timer'])]
812
+ output = [CSV.generate_line(['date','title','note','timer','section'])]
752
813
  items.each {|i|
753
814
  note = ""
754
815
  if i['note']
@@ -759,7 +820,7 @@ class WWID
759
820
  interval = get_interval(i, false)
760
821
  end
761
822
  interval ||= 0
762
- output.push(CSV.generate_line([i['date'],i['title'],note,interval]))
823
+ output.push(CSV.generate_line([i['date'],i['title'],note,interval,i['section']]))
763
824
  }
764
825
  out = output.join("")
765
826
  elsif opt[:output] == "json" || opt[:output] == "timeline"
@@ -768,12 +829,12 @@ class WWID
768
829
  max = items[-1]['date'].strftime('%F')
769
830
  min = items[0]['date'].strftime('%F')
770
831
  items.each_with_index {|i,index|
771
- if RUBY_VERSION.to_f > 1.8
832
+ if String.method_defined? :force_encoding
772
833
  title = i['title'].force_encoding('utf-8')
773
834
  note = i['note'].map {|line| line.force_encoding('utf-8').strip } if i['note']
774
835
  else
775
836
  title = i['title']
776
- note = i['note'].map { |line| line.strip }
837
+ note = i['note'].map { |line| line.strip } if i['note']
777
838
  end
778
839
 
779
840
  if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
@@ -869,12 +930,12 @@ EOTEMPLATE
869
930
  # else
870
931
  # note = ''
871
932
  # end
872
- if RUBY_VERSION.to_f > 1.8
873
- title = i['title'].force_encoding('utf-8')
874
- note = i['note'].map {|line| line.force_encoding('utf-8').strip } if i['note']
933
+ if String.method_defined? :force_encoding
934
+ title = i['title'].force_encoding('utf-8').link_urls
935
+ note = i['note'].map {|line| line.force_encoding('utf-8').strip.link_urls } if i['note']
875
936
  else
876
- title = i['title']
877
- note = i['note'].map { |line| line.strip }
937
+ title = i['title'].link_urls
938
+ note = i['note'].map { |line| line.strip.link_urls } if i['note']
878
939
  end
879
940
 
880
941
  if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
@@ -886,11 +947,12 @@ EOTEMPLATE
886
947
  :date => i['date'].strftime('%a %-I:%M%p'),
887
948
  :title => title.gsub(/(@[^ \(]+(\(.*?\))?)/im,'<span class="tag">\1</span>').strip, #+ " #{note}"
888
949
  :note => note,
889
- :time => interval
950
+ :time => interval,
951
+ :section => i['section']
890
952
  }
891
953
  }
892
954
 
893
- style = "body{background:#fff;color:#333;font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:16px;line-height:120%;text-align:justify;padding:20px}h1{text-align:left;position:relative;left:220px;margin-bottom:1em}ul{list-style-position:outside;position:relative;left:170px;margin-right:170px;text-align:left}ul li{list-style-type:none;border-left:solid 1px #ccc;padding-left:10px;line-height:2;position:relative}ul li .date{font-size:14px;position:absolute;left:-122px;color:#7d9ca2;text-align:right;width:110px;line-height:2}ul li .tag{color:#999}ul li .note{display:block;color:#666;padding:0 0 0 22px;line-height:1.4;font-size:15px}ul li .note:before{content:'\\25BA';font-weight:300;position:absolute;left:40px;font-size:8px;color:#aaa;line-height:3}ul li:hover .note{display:block}span.time{color:#729953;float:left;position:relative;padding:0 5px;font-size:15px;border-bottom:dashed 1px #ccc;text-align:right;background:#f9fced;margin-right:4px}table td{border-bottom:solid 1px #ddd;height:24px}caption{text-align:left;border-bottom:solid 1px #aaa;margin:10px 0}table{width:400px;margin:50px 0 0 211px}th{padding-bottom:10px}th,td{padding-right:20px}table{max-width:400px;margin:50px 0 0 221px}"
955
+ style = "body{background:#fff;color:#333;font-family:Helvetica,arial,freesans,clean,sans-serif;font-size:16px;line-height:120%;text-align:justify;padding:20px}h1{text-align:left;position:relative;left:220px;margin-bottom:1em}ul{list-style-position:outside;position:relative;left:170px;margin-right:170px;text-align:left}ul li{list-style-type:none;border-left:solid 1px #ccc;padding-left:10px;line-height:2;position:relative}ul li .date{font-size:14px;position:absolute;left:-122px;color:#7d9ca2;text-align:right;width:110px;line-height:2}ul li .tag{color:#999}ul li .note{display:block;color:#666;padding:0 0 0 22px;line-height:1.4;font-size:15px}ul li .note:before{content:'\\25BA';font-weight:300;position:absolute;left:40px;font-size:8px;color:#aaa;line-height:3}ul li:hover .note{display:block}span.time{color:#729953;float:left;position:relative;padding:0 5px;font-size:15px;border-bottom:dashed 1px #ccc;text-align:right;background:#f9fced;margin-right:4px}table td{border-bottom:solid 1px #ddd;height:24px}caption{text-align:left;border-bottom:solid 1px #aaa;margin:10px 0}table{width:400px;margin:50px 0 0 211px}th{padding-bottom:10px}th,td{padding-right:20px}table{max-width:400px;margin:50px 0 0 221px}ul li .section{color:#dbbfad;border-left:solid 1px #dbbfad;border-right:solid 1px #dbbfad;border-radius:25px;padding:0 4px;line-height:1 !important;font-size:.8em}ul li .section:hover{color:#c5753f}ul li a:link{color:#64a9a5;text-decoration:none;background-color:rgba(203,255,251,.15)}"
894
956
  template =<<EOT
895
957
  !!!
896
958
  %html
@@ -908,6 +970,7 @@ EOTEMPLATE
908
970
  %li
909
971
  %span.date= i[:date]
910
972
  = i[:title]
973
+ %span.section= i[:section]
911
974
  - if i[:time] && i[:time] != "00:00:00"
912
975
  %span.time= i[:time]
913
976
  - if i[:note]
@@ -977,6 +1040,9 @@ EOT
977
1040
  flag+item['title'].chomp+reset
978
1041
  end
979
1042
  }
1043
+
1044
+ output.sub!(/%section/,item['section']) if item['section']
1045
+
980
1046
  if opt[:tags_color]
981
1047
  output.gsub!(/\s(@\S+(?:\(.*?\))?)/," #{colors[opt[:tags_color]]}\\1")
982
1048
  end
@@ -1066,7 +1132,8 @@ EOT
1066
1132
  }
1067
1133
  moved_items.each {|item|
1068
1134
  if label
1069
- item['title'] += " @from(#{section})" unless section == "Currently" || item['title'] =~ /@from\(/
1135
+ item['title'] = item['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{section}") unless section == "Currently"
1136
+ # item['title'] += " @from(#{section})" unless section == "Currently" || item['title'] =~ /@from\(/
1070
1137
  end
1071
1138
  }
1072
1139
  @content[section]['items'] = moved_items
@@ -1083,9 +1150,11 @@ EOT
1083
1150
 
1084
1151
  items.each{|item|
1085
1152
  if label
1086
- item['title'] += " @from(#{section})" unless section == "Currently"
1153
+ item['title'] = item['title'].sub(/(?:@from\(.*?\))?(.*)$/,"\\1 @from(#{section}") unless section == "Currently"
1154
+ # item['title'] += " @from(#{section})" unless section == "Currently"
1087
1155
  end
1088
1156
  }
1157
+
1089
1158
  @content[destination]['items'] += items[count..-1]
1090
1159
  @results.push("Archived #{items.length - count} items from #{section} to #{destination}")
1091
1160
  end
@@ -1254,7 +1323,10 @@ EOS
1254
1323
  def autotag(title)
1255
1324
  return unless title
1256
1325
  @config['autotag']['whitelist'].each {|tag|
1257
- title.sub!(/(?<!@)(#{tag.strip})\b/i,'@\1') unless title =~ /@#{tag}\b/i
1326
+ title.sub!(/(?<!@)(#{tag.strip})\b/i) do |m|
1327
+ m.downcase! if tag =~ /[a-z]/
1328
+ "@#{m}"
1329
+ end unless title =~ /@#{tag}\b/i
1258
1330
  }
1259
1331
  tail_tags = []
1260
1332
  @config['autotag']['synonyms'].each {|tag, v|
@@ -1264,7 +1336,11 @@ EOS
1264
1336
  end
1265
1337
  }
1266
1338
  }
1267
- title + tail_tags.uniq.map {|t| '@'+t }.join(' ')
1339
+ if tail_tags.length > 0
1340
+ title + ' ' + tail_tags.uniq.map {|t| '@'+t }.join(' ')
1341
+ else
1342
+ title
1343
+ end
1268
1344
  end
1269
1345
 
1270
1346
  def autotag_item(item)
@@ -1293,7 +1369,7 @@ EOS
1293
1369
  seconds = (done - start).to_i
1294
1370
 
1295
1371
  item['title'].scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each {|m|
1296
- k = m[0] == "done" ? "All" : m[0]
1372
+ k = m[0] == "done" ? "All" : m[0].downcase
1297
1373
  if @timers.has_key?(k)
1298
1374
  @timers[k] += seconds
1299
1375
  else
@@ -1310,8 +1386,9 @@ EOS
1310
1386
  minutes = (seconds / 60).to_i
1311
1387
  hours = (minutes / 60).to_i
1312
1388
  days = (hours / 24).to_i
1313
- hours = (hours % 60).to_i
1389
+ hours = (hours % 24).to_i
1314
1390
  minutes = (minutes % 60).to_i
1315
1391
  [days, hours, minutes]
1316
1392
  end
1393
+
1317
1394
  end
data/lib/doing.rb CHANGED
@@ -10,4 +10,3 @@ require 'haml'
10
10
  require 'json'
11
11
  require 'doing/wwid.rb'
12
12
 
13
- DOING_CONFIG_NAME = ".doingrc"
metadata CHANGED
@@ -1,77 +1,77 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doing
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.8pre
4
+ version: 1.0.10pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-28 00:00:00.000000000 Z
11
+ date: 2017-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rdoc
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '4.1'
34
- - - '>='
34
+ - - ">="
35
35
  - !ruby/object:Gem::Version
36
36
  version: 4.1.1
37
37
  type: :development
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - ~>
41
+ - - "~>"
42
42
  - !ruby/object:Gem::Version
43
43
  version: '4.1'
44
- - - '>='
44
+ - - ">="
45
45
  - !ruby/object:Gem::Version
46
46
  version: 4.1.1
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: aruba
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ~>
51
+ - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0'
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ~>
58
+ - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '0'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: gli
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - '='
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: 2.9.0
67
+ version: 2.17.1
68
68
  type: :runtime
69
69
  prerelease: false
70
70
  version_requirements: !ruby/object:Gem::Requirement
71
71
  requirements:
72
- - - '='
72
+ - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: 2.9.0
74
+ version: 2.17.1
75
75
  - !ruby/object:Gem::Dependency
76
76
  name: haml
77
77
  requirement: !ruby/object:Gem::Requirement
@@ -90,48 +90,48 @@ dependencies:
90
90
  name: chronic
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
- - - ~>
93
+ - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0.10'
96
- - - '>='
96
+ - - ">="
97
97
  - !ruby/object:Gem::Version
98
98
  version: 0.10.2
99
99
  type: :runtime
100
100
  prerelease: false
101
101
  version_requirements: !ruby/object:Gem::Requirement
102
102
  requirements:
103
- - - ~>
103
+ - - "~>"
104
104
  - !ruby/object:Gem::Version
105
105
  version: '0.10'
106
- - - '>='
106
+ - - ">="
107
107
  - !ruby/object:Gem::Version
108
108
  version: 0.10.2
109
109
  - !ruby/object:Gem::Dependency
110
110
  name: deep_merge
111
111
  requirement: !ruby/object:Gem::Requirement
112
112
  requirements:
113
- - - '>='
113
+ - - ">="
114
114
  - !ruby/object:Gem::Version
115
115
  version: '0'
116
116
  type: :runtime
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
- - - '>='
120
+ - - ">="
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
123
  - !ruby/object:Gem::Dependency
124
124
  name: json
125
125
  requirement: !ruby/object:Gem::Requirement
126
126
  requirements:
127
- - - ~>
127
+ - - "~>"
128
128
  - !ruby/object:Gem::Version
129
129
  version: 1.8.1
130
130
  type: :runtime
131
131
  prerelease: false
132
132
  version_requirements: !ruby/object:Gem::Requirement
133
133
  requirements:
134
- - - ~>
134
+ - - "~>"
135
135
  - !ruby/object:Gem::Version
136
136
  version: 1.8.1
137
137
  description: A tool for managing a TaskPaper-like file of recent activites. Perfect
@@ -155,29 +155,29 @@ licenses:
155
155
  metadata: {}
156
156
  post_install_message:
157
157
  rdoc_options:
158
- - --title
158
+ - "--title"
159
159
  - doing
160
- - --main
160
+ - "--main"
161
161
  - README.md
162
- - --markup
162
+ - "--markup"
163
163
  - markdown
164
- - -ri
164
+ - "-ri"
165
165
  require_paths:
166
166
  - lib
167
167
  - lib
168
168
  required_ruby_version: !ruby/object:Gem::Requirement
169
169
  requirements:
170
- - - '>='
170
+ - - ">="
171
171
  - !ruby/object:Gem::Version
172
172
  version: '0'
173
173
  required_rubygems_version: !ruby/object:Gem::Requirement
174
174
  requirements:
175
- - - '>'
175
+ - - ">"
176
176
  - !ruby/object:Gem::Version
177
177
  version: 1.3.1
178
178
  requirements: []
179
179
  rubyforge_project:
180
- rubygems_version: 2.2.2
180
+ rubygems_version: 2.6.13
181
181
  signing_key:
182
182
  specification_version: 4
183
183
  summary: A command line tool for managing What Was I Doing reminders