doing 0.2.5 → 1.0.0pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +115 -16
- data/bin/doing +222 -47
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +178 -23
- data/lib/doing.rb +1 -1
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74966c0e6ac08312d5b1ebb980795e47a19ae278
|
4
|
+
data.tar.gz: 746b50bfae984c7f95e3b64a7bb095a185d2208e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7968dfa76f2c4b6aef6a8ae098f86df6286d5968330a1cebb471d1d13978c5ccfe1aa0bbda49e2bc74af83cadd7aae37197719f33b4336f51d25bc6f001c8ff1
|
7
|
+
data.tar.gz: 5fcf33fccaa418ba41c5f1c79515bb341024b9363a95adc07b7479163bd4eb5de96912d8c0b9994d4e6d6bdd0649ff8baf82628f996040ee993d90b8099b08bd
|
data/README.md
CHANGED
@@ -263,11 +263,13 @@ Outputs:
|
|
263
263
|
done - Add a completed item with @done(date). No argument finishes last entry.
|
264
264
|
meanwhile - Finish any @meanwhile tasks and optionally create a new one
|
265
265
|
|
266
|
-
The `doing now` command can accept `-s section_name` to send the new entry straight to a non-default section.
|
266
|
+
The `doing now` command can accept `-s section_name` to send the new entry straight to a non-default section. It also accepts `--back AMOUNT` to let you specify a start date in the past using "natural language." For example, `doing now --back 25m ENTRY` or `doing now --back "yesterday 3:30pm" ENTRY`.
|
267
|
+
|
268
|
+
You can finish the last unfinished task when starting a new one using `doing now` with the `-f` switch. It will look for the last task not marked `@done` and add the `@done` tag with the start time of the new task (either the current time or what you specified with `--back`).
|
267
269
|
|
268
270
|
`doing done` is used to add an entry that you've already completed. Like `now`, you can specify a section with `-s section_name`. You can also skip straight to Archive with `-a`.
|
269
271
|
|
270
|
-
You can also backdate entries using natural language with `--back 15m` or `--back "3/15 3pm"`. That will modify the timestamp of the entry. When used with `doing done`, this allows time intervals to be accurately counted when entering items after the fact.
|
272
|
+
You can also backdate entries using natural language with `--back 15m` or `--back "3/15 3pm"`. That will modify the starting timestamp of the entry. You can also use `--took 1h20m` or `--took 1:20` to set the finish date based on a "natural language" time interval. When used with `doing done`, this allows time intervals to be accurately counted when entering items after the fact. `--took` is also available for the `doing finish` command, but cannot be used in conjunction with `--back`. (In `finish` they both set the end date, and neither has priority. `--back` allows specific days/times, `--took` uses time intervals.)
|
271
273
|
|
272
274
|
All of these commands accept a `-e` argument. This opens your command line editor as defined in the environment variable `$EDITOR`. Add your entry, save the temp file and close it, and the new entry will be added. Anything after the first line is included as a note on the entry.
|
273
275
|
|
@@ -277,15 +279,24 @@ All of these commands accept a `-e` argument. This opens your command line edito
|
|
277
279
|
|
278
280
|
finish - Mark last X entries as @done
|
279
281
|
tag - Tag last entry
|
282
|
+
note - Add a note to the last entry
|
280
283
|
|
281
284
|
`doing finish` by itself is the same as `doing done` by itself. It adds `@done(timestamp)` to the last entry. It also accepts a numeric argument to complete X number of tasks back in history. Add `-a` to also archive the affected entries.
|
282
285
|
|
286
|
+
`doing finish` also provides an `--auto` flag, which you can use to set the end time of any entry to 1 minute before the start time of the next. Running a command such as `doing finish --auto 10` will go through the last 10 entries and sequentially update any without a `@done` tag with one set to the time just before the next entry in the list.
|
287
|
+
|
288
|
+
As mentioned above, `finish` also accepts `--back "2 hours"` (sets the date from time now minus interval) or `--took 30m` (sets the date from time started plus interval) so you can accurately add times to completed tasks, even if you don't do it in the moment.
|
289
|
+
|
283
290
|
`tag` adds one or more tags to the last entry, or specify a count with `-c X`. Tags are specified as basic arguments, separated by spaces. For example:
|
284
291
|
|
285
292
|
doing tag -c 3 client cancelled
|
286
293
|
|
287
294
|
... will mark the last three entries as "@client @cancelled." Add `-r` as a switch to remove the listed tags instead.
|
288
295
|
|
296
|
+
`note` lets you enter a note on the last entry. You can specify a section with `-s section_name`. `-e` will open your $EDITOR for typing the note, but you can just include it on the command line after any arguments. You can also pipe a note in on STDIN (`echo "fun stuff"|doing note`). The `-r` switch will remove/replace a note; if there's new note text passed when using the `-r` switch, it will replace any existing note. If the `-r` switch is used alone, any existing note will be removed.
|
297
|
+
|
298
|
+
You can also add notes at the time of entry by using the `-n` or `--note` flag with `doing now`, `doing later`, or `doing done`. If you pass in text to any of the creation commands that has multiple lines, everything after the first line break will become the note.
|
299
|
+
|
289
300
|
#### Displaying entries:
|
290
301
|
|
291
302
|
show - List all entries
|
@@ -293,6 +304,7 @@ All of these commands accept a `-e` argument. This opens your command line edito
|
|
293
304
|
today - List entries from today
|
294
305
|
yesterday - List entries from yesterday
|
295
306
|
last - Show the last entry
|
307
|
+
grep - Show entries matching text or pattern
|
296
308
|
|
297
309
|
`doing show` on its own will list all entries in the "Currently" section. Add a section name as an argument to display that section instead. Use "all" to display all entries from all sections.
|
298
310
|
|
@@ -413,19 +425,106 @@ That said, you can get support from other users (and occasionally me) on GitHub.
|
|
413
425
|
|
414
426
|
Please try not to email me directly about GitHub projects.
|
415
427
|
|
428
|
+
### Developer notes
|
429
|
+
|
430
|
+
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).
|
431
|
+
|
416
432
|
### Changelog
|
417
433
|
|
418
|
-
#### 0.2.
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
434
|
+
#### 0.2.6pre
|
435
|
+
|
436
|
+
* --totals, --[no-]times, --output [csv,html] options for yesterday command.
|
437
|
+
* Add tests for Darwin to hide OS X-only features on other systems
|
438
|
+
* -f flag to `now` command for finishing last task when starting a new one (Looks back for the last unfinished task in the list)
|
439
|
+
* --took option for `done` and `finish` for specifying intervals from the start date for the completion date
|
440
|
+
* Basic command line reporting
|
441
|
+
* --auto flag for `finish` and `done` that will automatically set the completion time to 1 minute before the next start time in the list. You can use it retroactively to add times to sequential todos.
|
442
|
+
* `doing grep` for searching by text or regex
|
443
|
+
|
444
|
+
#### 0.2.5
|
445
|
+
|
446
|
+
* Default to showing times #26, show totals even if no tags exist #27, fix indentation #29
|
447
|
+
* Add section label to archived tasks automatically, excepting Currently section
|
448
|
+
* Today outputs and backdate for finish
|
449
|
+
* html styling and fix for 1.8.7 haml errors
|
450
|
+
* Look, HTML output! (`--output html`)
|
451
|
+
* Also, `--output csv`
|
452
|
+
* let doing archive function on all sections
|
453
|
+
* option to exclude date from `@done`,
|
454
|
+
* output newlines in sections and views
|
455
|
+
* Flagging (`doing mark`)
|
456
|
+
* fix for view/section guess error
|
457
|
+
* Adding tag filtering to archive command (`doing archive \@done`)
|
458
|
+
* `doing yesterday`
|
459
|
+
* `doing done -r` to remove last doing tag (optionally from `-s Section`)
|
460
|
+
* Add -f flag to specify alternate doing file
|
461
|
+
* Meanwhile command
|
462
|
+
|
463
|
+
#### 0.2.1
|
464
|
+
|
465
|
+
- CSV output for show command (`--csv`)
|
466
|
+
- HTML output for show command (`--output html`)
|
467
|
+
- fuzzy searching for all commands that specify a view.
|
468
|
+
- On the terminal you'll see "Assume you meant XXX" to show what match it found, but this is output to STDERR and won't show up if you're redirecting the output or using it in GeekTool, etc.
|
469
|
+
- tags_color in view config to highlight tags at the end of the lines. Can be set to any of the %colors.
|
470
|
+
- Basic time tracking.
|
471
|
+
- `-t` on `show` and `view` will turn on time calculations
|
472
|
+
- Intervals between timestamps and dated `@done` tags are calculated for each line, if the tag exists.
|
473
|
+
- You must include a %interval token in the appropriate template for it to show
|
474
|
+
- `@start(date)` tags can optionally be used to override the time stamp in the calculation
|
475
|
+
- Any other tags in the line have that line's total added to them
|
476
|
+
- Totals for tags can be displayed at the end of output with `--totals`
|
477
|
+
|
478
|
+
|
479
|
+
#### 0.2.0
|
480
|
+
|
481
|
+
- `doing done` without argument tags last entry done
|
482
|
+
- `-a` archives them
|
483
|
+
- `doing finish` or `doing finish X` marks last X entries done
|
484
|
+
- `-a` archives them
|
485
|
+
- `doing tag tag1 [tag2]` tags last entry or `-c X` entries
|
486
|
+
- `doing tag -r tag1 [tag2]` removes said tag(s)
|
487
|
+
- custom views additions
|
488
|
+
- custom views can include `tags` and `tags_bool`
|
489
|
+
- tags is a space separated list of tags to filter the results by
|
490
|
+
- tags_bool defines AND (all tags must exist), OR (any tag exists), or NONE (none of the tags exist)
|
491
|
+
- order key (asc or desc) defines output sort order by date
|
492
|
+
- section key can be set to "All" to combine sections
|
493
|
+
- `doing show` updates
|
494
|
+
- accepts "all" as a section
|
495
|
+
- arguments following section name are tags to filter by
|
496
|
+
- `-b` sets boolean (AND, OR, NONE) or (ALL, ANY, NONE) (default OR/ANY)
|
497
|
+
- use `-c X` to limit results
|
498
|
+
- use `-s` to set sort order (asc or desc)
|
499
|
+
- use `-a` to set age (newest or oldest)
|
500
|
+
- fuzzy section guessing when specified section isn't found
|
501
|
+
- fuzzy view guessing for `doing view` command
|
502
|
+
|
503
|
+
----
|
504
|
+
|
505
|
+
#### 0.1.9
|
506
|
+
|
507
|
+
- colors in templated output
|
508
|
+
- open command
|
509
|
+
- opens in the default app for file type
|
510
|
+
- -a APPNAME (`doing open -a TaskPaper`)
|
511
|
+
- -b bundle_id (`doing open -b com.sublimetext.3`)
|
512
|
+
- -e switch for `now`, `later` and `done` commands
|
513
|
+
- save a tmp file and open it in an editor
|
514
|
+
- allows multi-line entries, anything after first line is considered a note
|
515
|
+
- assumed when no input is provided (`doing now`)
|
516
|
+
- `doing views` shows all available custom views
|
517
|
+
- `doing view` without a view name will let you choose a view from a menu
|
518
|
+
- `doing archive` fixed so that `-k X` works to keep X number of entries in the section
|
519
|
+
|
520
|
+
#### 0.1.7
|
521
|
+
|
522
|
+
- colors in templated output
|
523
|
+
- open command
|
524
|
+
- opens in the default app for file type
|
525
|
+
- -a APPNAME (`doing open -a TaskPaper`)
|
526
|
+
- -b bundle_id (`doing open -b com.sublimetext.3`)
|
527
|
+
- -e switch for `now`, `later` and `done` commands
|
528
|
+
- save a tmp file and open it in an editor
|
529
|
+
- allows multi-line entries, anything after first line is considered a note
|
530
|
+
- assumed when no input is provided (`doing now`)
|
data/bin/doing
CHANGED
@@ -39,9 +39,18 @@ command :now do |c|
|
|
39
39
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
40
40
|
c.switch [:e,:editor]
|
41
41
|
|
42
|
-
c.desc 'Backdate
|
42
|
+
c.desc 'Backdate start time [4pm|20m|2h|yesterday noon]'
|
43
43
|
c.flag [:back]
|
44
44
|
|
45
|
+
c.desc 'Timed entry, marks last entry in section as @done'
|
46
|
+
c.default_value false
|
47
|
+
c.switch [:f,:finish_last], :negatable => false, :default_value => false
|
48
|
+
|
49
|
+
c.desc 'Note'
|
50
|
+
c.arg_name 'note_text'
|
51
|
+
c.default_value wwid.current_section
|
52
|
+
c.flag [:n,:note]
|
53
|
+
|
45
54
|
# c.desc "Edit entry with specified app"
|
46
55
|
# c.arg_name 'editor_app'
|
47
56
|
# c.default_value wwid.config.has_key?('editor_app') && wwid.config['editor_app'] ? wwid.config['editor_app'] : false
|
@@ -65,7 +74,8 @@ command :now do |c|
|
|
65
74
|
input = wwid.fork_editor(input)
|
66
75
|
if input
|
67
76
|
title, note = wwid.format_input(input)
|
68
|
-
|
77
|
+
note.push(options[:n]) if options[:n]
|
78
|
+
wwid.add_item(title.cap_first, section, {:note => note, :back => date, :timed => options[:f]})
|
69
79
|
wwid.write(wwid.doing_file)
|
70
80
|
else
|
71
81
|
raise "No content"
|
@@ -73,11 +83,13 @@ command :now do |c|
|
|
73
83
|
else
|
74
84
|
if args.length > 0
|
75
85
|
title, note = wwid.format_input(args.join(" "))
|
76
|
-
|
86
|
+
note.push(options[:n]) if options[:n]
|
87
|
+
wwid.add_item(title.cap_first, section, {:note => note, :back => date, :timed => options[:f]})
|
77
88
|
wwid.write(wwid.doing_file)
|
78
89
|
elsif STDIN.stat.size > 0
|
79
90
|
title, note = wwid.format_input(STDIN.read)
|
80
|
-
|
91
|
+
note.push(options[:n]) if options[:n]
|
92
|
+
wwid.add_item(title.cap_first, section, {:note => note, :back => date, :timed => options[:f]})
|
81
93
|
wwid.write(wwid.doing_file)
|
82
94
|
else
|
83
95
|
raise "You must provide content when creating a new entry"
|
@@ -86,6 +98,57 @@ command :now do |c|
|
|
86
98
|
end
|
87
99
|
end
|
88
100
|
|
101
|
+
desc 'Add a note to the last entry'
|
102
|
+
arg_name 'note_text'
|
103
|
+
command :note do |c|
|
104
|
+
c.desc 'Section'
|
105
|
+
c.arg_name 'section_name'
|
106
|
+
c.default_value wwid.current_section
|
107
|
+
c.flag [:s,:section], :default_value => wwid.current_section
|
108
|
+
|
109
|
+
c.desc "Edit entry with #{ENV['EDITOR']}"
|
110
|
+
c.switch [:e,:editor], :negatable => false, :default_value => false
|
111
|
+
|
112
|
+
c.desc "Replace/Remove last entry's note"
|
113
|
+
c.long_desc "If -r is provided with no other arguments, the last note is removed. If new content is specified through arguments or STDIN, any previous note will be replaced with the new one."
|
114
|
+
c.switch [:r,:remove], :negatable => false, :default_value => false
|
115
|
+
|
116
|
+
c.action do |global_options,options,args|
|
117
|
+
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
118
|
+
|
119
|
+
if options[:e] || (args.length == 0 && STDIN.stat.size == 0 && !options[:r])
|
120
|
+
raise "No EDITOR variable defined in environment" if ENV['EDITOR'].nil?
|
121
|
+
input = ""
|
122
|
+
input += args.join(" ") if args.length > 0
|
123
|
+
input = wwid.fork_editor(input)
|
124
|
+
if input
|
125
|
+
title, note = wwid.format_input(input)
|
126
|
+
note.insert(0, title)
|
127
|
+
wwid.note_last(section, note, options[:r])
|
128
|
+
else
|
129
|
+
raise "No content"
|
130
|
+
end
|
131
|
+
else
|
132
|
+
if args.length > 0
|
133
|
+
title, note = wwid.format_input(args.join(" "))
|
134
|
+
note.insert(0, title)
|
135
|
+
wwid.note_last(section, note, options[:r])
|
136
|
+
elsif STDIN.stat.size > 0
|
137
|
+
title, note = wwid.format_input(STDIN.read)
|
138
|
+
note.insert(0, title)
|
139
|
+
wwid.note_last(section, note, options[:r])
|
140
|
+
else
|
141
|
+
if options[:r]
|
142
|
+
wwid.note_last(section, [], true)
|
143
|
+
else
|
144
|
+
raise "You must provide content when adding a note"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
wwid.write(wwid.doing_file)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
89
152
|
desc 'Finish any running @meanwhile tasks and optionally create a new one'
|
90
153
|
arg_name 'entry'
|
91
154
|
command :meanwhile do |c|
|
@@ -100,9 +163,14 @@ command :meanwhile do |c|
|
|
100
163
|
c.desc "Archive previous @meanwhile entry"
|
101
164
|
c.switch [:a,:archive], :default_value => false
|
102
165
|
|
103
|
-
c.desc 'Backdate to
|
166
|
+
c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
|
104
167
|
c.flag [:back]
|
105
168
|
|
169
|
+
c.desc 'Note'
|
170
|
+
c.arg_name 'note_text'
|
171
|
+
c.default_value wwid.current_section
|
172
|
+
c.flag [:n,:note]
|
173
|
+
|
106
174
|
c.action do |global_options,options,args|
|
107
175
|
if options[:back]
|
108
176
|
date = wwid.chronify(options[:back])
|
@@ -128,7 +196,8 @@ command :meanwhile do |c|
|
|
128
196
|
end
|
129
197
|
input = false unless input && input.length > 0
|
130
198
|
|
131
|
-
|
199
|
+
note = options[:n] ? options[:n] : false
|
200
|
+
wwid.stop_start("meanwhile",{:new_item => input, :back => date, :section => section, :archive => options[:a], :note => note})
|
132
201
|
wwid.write(wwid.doing_file)
|
133
202
|
end
|
134
203
|
end
|
@@ -144,9 +213,14 @@ command :later do |c|
|
|
144
213
|
c.default_value wwid.config.has_key?('editor_app') && wwid.config['editor_app'] ? wwid.config['editor_app'] : false
|
145
214
|
c.flag [:a,:app]
|
146
215
|
|
147
|
-
c.desc 'Backdate to
|
216
|
+
c.desc 'Backdate start time to date string [4pm|20m|2h|yesterday noon]'
|
148
217
|
c.flag [:back]
|
149
218
|
|
219
|
+
c.desc 'Note'
|
220
|
+
c.arg_name 'note_text'
|
221
|
+
c.default_value wwid.current_section
|
222
|
+
c.flag [:n,:note]
|
223
|
+
|
150
224
|
c.action do |global_options,options,args|
|
151
225
|
if options[:back]
|
152
226
|
date = wwid.chronify(options[:back])
|
@@ -162,6 +236,7 @@ command :later do |c|
|
|
162
236
|
input = wwid.fork_editor(input)
|
163
237
|
if input
|
164
238
|
title, note = wwid.format_input(input)
|
239
|
+
note.push(options[:n]) if options[:n]
|
165
240
|
wwid.add_item(title.cap_first, "Later", {:note => note, :back => date})
|
166
241
|
wwid.write(wwid.doing_file)
|
167
242
|
else
|
@@ -170,10 +245,12 @@ command :later do |c|
|
|
170
245
|
else
|
171
246
|
if args.length > 0
|
172
247
|
title, note = wwid.format_input(args.join(" "))
|
248
|
+
note.push(options[:n]) if options[:n]
|
173
249
|
wwid.add_item(title.cap_first, "Later", {:note => note, :back => date})
|
174
250
|
wwid.write(wwid.doing_file)
|
175
251
|
elsif STDIN.stat.size > 0
|
176
252
|
title, note = wwid.format_input(STDIN.read)
|
253
|
+
note.push(options[:n]) if options[:n]
|
177
254
|
wwid.add_item(title.cap_first, "Later", {:note => note, :back => date})
|
178
255
|
wwid.write(wwid.doing_file)
|
179
256
|
else
|
@@ -192,14 +269,17 @@ command :done do |c|
|
|
192
269
|
|
193
270
|
c.desc 'Include date'
|
194
271
|
c.default_value true
|
195
|
-
c.switch [:d,:date], :default_value => true
|
272
|
+
c.switch [:d,:date], :negatable => true, :default_value => true
|
196
273
|
|
197
274
|
c.desc 'Immediately archive the entry'
|
198
275
|
c.default_value false
|
199
276
|
c.switch [:a,:archive], :negatable => false, :default_value => false
|
200
277
|
|
201
|
-
c.desc 'Backdate to
|
202
|
-
c.flag [:back]
|
278
|
+
c.desc 'Backdate start date to date string [4pm|20m|2h|yesterday noon]'
|
279
|
+
c.flag [:b,:back]
|
280
|
+
|
281
|
+
c.desc 'Set completion date to start date plus XX[mhd] or [HH:MM]'
|
282
|
+
c.flag [:t,:took]
|
203
283
|
|
204
284
|
c.desc 'Section'
|
205
285
|
c.default_value wwid.current_section
|
@@ -216,13 +296,21 @@ command :done do |c|
|
|
216
296
|
c.action do |global_options,options,args|
|
217
297
|
if options[:back]
|
218
298
|
date = wwid.chronify(options[:back])
|
219
|
-
raise "Unable to parse date string" if date.nil?
|
299
|
+
raise "Unable to parse date string for --back" if date.nil?
|
220
300
|
else
|
221
301
|
date = Time.now
|
222
302
|
end
|
223
303
|
|
304
|
+
if options[:took]
|
305
|
+
minutes = wwid.chronify_qty(options[:took])
|
306
|
+
finish_date = date + minutes
|
307
|
+
raise "Unable to parse date string for --took" if date.nil?
|
308
|
+
else
|
309
|
+
finish_date = Time.now
|
310
|
+
end
|
311
|
+
|
224
312
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
225
|
-
donedate = options[:d] ? "(#{
|
313
|
+
donedate = options[:d] ? "(#{finish_date.strftime('%F %R')})" : ""
|
226
314
|
|
227
315
|
if options[:e]
|
228
316
|
raise "No EDITOR variable defined in environment" if ENV['EDITOR'].nil?
|
@@ -242,11 +330,12 @@ command :done do |c|
|
|
242
330
|
if options[:r]
|
243
331
|
wwid.tag_last({:tags => ["done"], :count => 1, :section => section, :remove => true })
|
244
332
|
else
|
245
|
-
wwid.tag_last({:tags => ["done"], :count => 1, :section => section, :archive => options[:a], :back =>
|
333
|
+
wwid.tag_last({:tags => ["done"], :count => 1, :section => section, :archive => options[:a], :back => finish_date, :date => options[:d]})
|
246
334
|
end
|
247
335
|
else
|
248
336
|
if args.length > 0
|
249
337
|
title, note = wwid.format_input(args.join(" "))
|
338
|
+
title.chomp!
|
250
339
|
title += " @done#{donedate}"
|
251
340
|
section = "Archive" if options[:a]
|
252
341
|
wwid.add_item(title.cap_first, section.cap_first, {:note => note, :back => date})
|
@@ -265,14 +354,23 @@ command :done do |c|
|
|
265
354
|
end
|
266
355
|
|
267
356
|
desc 'Mark last X entries as @done'
|
357
|
+
long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
|
268
358
|
arg_name 'count'
|
269
359
|
command :finish do |c|
|
270
360
|
c.desc 'Include date'
|
271
361
|
c.default_value true
|
272
362
|
c.switch [:d,:date], :default_value => true
|
273
363
|
|
274
|
-
c.desc 'Backdate to
|
275
|
-
c.flag [:back]
|
364
|
+
c.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
|
365
|
+
c.flag [:b,:back]
|
366
|
+
|
367
|
+
c.desc 'Set the completed date to the start date plus XX[hmd]'
|
368
|
+
c.flag [:t,:took]
|
369
|
+
|
370
|
+
c.desc 'Auto-generate finish dates from next entry\'s start time'
|
371
|
+
c.long_desc 'Automatically generate completion dates 1 minute before next start date. --auto overrides the --date and --back parameters.'
|
372
|
+
c.default_value false
|
373
|
+
c.switch [:auto], :negatable => false, :default_value => false
|
276
374
|
|
277
375
|
c.desc 'Archive entries'
|
278
376
|
c.default_value false
|
@@ -286,19 +384,25 @@ command :finish do |c|
|
|
286
384
|
|
287
385
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
288
386
|
|
289
|
-
|
290
|
-
|
387
|
+
unless options[:auto]
|
388
|
+
raise "--back and --took cannot be used together" if options[:back] and options[:took]
|
291
389
|
|
292
|
-
|
293
|
-
|
294
|
-
|
390
|
+
if options[:back]
|
391
|
+
date = wwid.chronify(options[:back])
|
392
|
+
|
393
|
+
raise "Unable to parse date string" if date.nil?
|
394
|
+
elsif options[:took]
|
395
|
+
date = wwid.chronify_qty(options[:took])
|
396
|
+
else
|
397
|
+
date = Time.now
|
398
|
+
end
|
295
399
|
end
|
296
400
|
|
297
401
|
if args.length > 1
|
298
402
|
raise "Only one argument allowed"
|
299
403
|
elsif args.length == 0 || args[0] =~ /\d+/
|
300
404
|
count = args[0] ? args[0].to_i : 1
|
301
|
-
wwid.tag_last({:tags => ["done"], :count => count, :section => section, :archive => options[:a], :date => options[:d], :back => date })
|
405
|
+
wwid.tag_last({:tags => ["done"], :count => count, :section => section, :archive => options[:a], :sequential => options[:auto], :date => options[:d], :back => date })
|
302
406
|
else
|
303
407
|
raise "Invalid argument (specify number of recent items to mark @done)"
|
304
408
|
end
|
@@ -383,11 +487,11 @@ command :show do |c|
|
|
383
487
|
c.default_value 'asc'
|
384
488
|
c.flag [:s,:sort], :default_value => 'asc'
|
385
489
|
|
386
|
-
c.desc 'Output to format'
|
490
|
+
c.desc 'Output to export format (csv|html)'
|
387
491
|
c.flag [:o,:output]
|
388
492
|
|
389
493
|
c.desc 'Show time intervals on @done tasks'
|
390
|
-
c.default_value
|
494
|
+
c.default_value true
|
391
495
|
c.switch [:t,:times], :default_value => true
|
392
496
|
|
393
497
|
c.desc 'Show intervals with totals at the end of output'
|
@@ -430,6 +534,37 @@ command :show do |c|
|
|
430
534
|
end
|
431
535
|
end
|
432
536
|
|
537
|
+
desc 'Search for entries'
|
538
|
+
long_desc 'Search all sections (or limit to a single section) for entries matching text or regular expression.'
|
539
|
+
arg_name 'search_pattern'
|
540
|
+
command :grep do |c|
|
541
|
+
c.desc 'Section'
|
542
|
+
c.default_value "all"
|
543
|
+
c.flag [:s,:section], :default_value => "All"
|
544
|
+
|
545
|
+
c.desc 'Output to export format (csv|html)'
|
546
|
+
c.flag [:o,:output]
|
547
|
+
|
548
|
+
c.desc 'Show time intervals on @done tasks'
|
549
|
+
c.default_value true
|
550
|
+
c.switch [:t,:times], :default_value => true
|
551
|
+
|
552
|
+
c.desc 'Show intervals with totals at the end of output'
|
553
|
+
c.default_value false
|
554
|
+
c.switch [:totals], :default_value => false, :negatable => true
|
555
|
+
|
556
|
+
c.action do |global_options,options,args|
|
557
|
+
|
558
|
+
section = wwid.guess_section(options[:s]) if options[:s]
|
559
|
+
|
560
|
+
options[:t] = true if options[:totals]
|
561
|
+
|
562
|
+
puts wwid.list_section({:search => args.join, :section => section, :output => options[:output], :times => options[:t], :highlight => true})
|
563
|
+
|
564
|
+
puts wwid.tag_times if options[:totals]
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
433
568
|
desc 'List recent entries'
|
434
569
|
default_value 10
|
435
570
|
arg_name 'count'
|
@@ -439,7 +574,7 @@ command :recent do |c|
|
|
439
574
|
c.flag [:s,:section], :default_value => wwid.current_section
|
440
575
|
|
441
576
|
c.desc 'Show time intervals on @done tasks'
|
442
|
-
c.default_value
|
577
|
+
c.default_value true
|
443
578
|
c.switch [:t,:times], :default_value => true
|
444
579
|
|
445
580
|
c.desc 'Show intervals with totals at the end of output'
|
@@ -468,14 +603,14 @@ end
|
|
468
603
|
desc 'List entries from today'
|
469
604
|
command :today do |c|
|
470
605
|
c.desc 'Show time intervals on @done tasks'
|
471
|
-
c.default_value
|
606
|
+
c.default_value true
|
472
607
|
c.switch [:t,:times], :default_value => true
|
473
608
|
|
474
609
|
c.desc 'Show time totals at the end of output'
|
475
610
|
c.default_value false
|
476
611
|
c.switch [:totals], :default_value => false, :negatable => true
|
477
612
|
|
478
|
-
c.desc 'Output to format'
|
613
|
+
c.desc 'Output to export format (csv|html)'
|
479
614
|
c.flag [:o,:output]
|
480
615
|
|
481
616
|
c.action do |global_options,options,args|
|
@@ -492,9 +627,22 @@ desc 'List entries from yesterday'
|
|
492
627
|
default_value wwid.current_section
|
493
628
|
arg_name 'section'
|
494
629
|
command :yesterday do |c|
|
630
|
+
c.desc 'Output to export format (csv|html)'
|
631
|
+
c.flag [:o,:output]
|
632
|
+
|
633
|
+
c.desc 'Show time intervals on @done tasks'
|
634
|
+
c.default_value true
|
635
|
+
c.switch [:t,:times], :default_value => true
|
636
|
+
|
637
|
+
c.desc 'Show time totals at the end of output'
|
638
|
+
c.default_value false
|
639
|
+
c.switch [:totals], :default_value => false, :negatable => true
|
640
|
+
|
495
641
|
c.action do |global_options, options,args|
|
496
642
|
section = args.length > 0 ? args.join(" ") : wwid.current_section
|
497
|
-
puts wwid.yesterday(section).
|
643
|
+
puts wwid.yesterday(section,options[:t],options[:o]).chomp
|
644
|
+
|
645
|
+
puts wwid.tag_times if options[:totals]
|
498
646
|
end
|
499
647
|
end
|
500
648
|
|
@@ -542,11 +690,11 @@ command :view do |c|
|
|
542
690
|
c.desc 'Count to display (override view settings)'
|
543
691
|
c.flag [:c,:count], :must_match => /^\d+$/, :type => Integer
|
544
692
|
|
545
|
-
c.desc 'Output to format'
|
693
|
+
c.desc 'Output to export format (csv|html)'
|
546
694
|
c.flag [:o,:output]
|
547
695
|
|
548
696
|
c.desc 'Show time intervals on @done tasks'
|
549
|
-
c.default_value
|
697
|
+
c.default_value true
|
550
698
|
c.switch [:t,:times], :default_value => true
|
551
699
|
|
552
700
|
c.desc 'Show intervals with totals at the end of output'
|
@@ -635,8 +783,9 @@ command :archive do |c|
|
|
635
783
|
end
|
636
784
|
end
|
637
785
|
|
638
|
-
desc 'Open the "doing" file in an editor
|
786
|
+
desc 'Open the "doing" file in an editor'
|
639
787
|
command :open do |c|
|
788
|
+
if `uname` =~ /Darwin/
|
640
789
|
c.desc 'open with app name'
|
641
790
|
c.arg_name 'app_name'
|
642
791
|
c.flag [:a]
|
@@ -644,7 +793,7 @@ command :open do |c|
|
|
644
793
|
c.desc 'open with app bundle id'
|
645
794
|
c.arg_name 'bundle_id'
|
646
795
|
c.flag [:b]
|
647
|
-
|
796
|
+
end
|
648
797
|
c.desc 'open with $EDITOR'
|
649
798
|
c.switch [:e], :negatable => false
|
650
799
|
|
@@ -653,7 +802,7 @@ command :open do |c|
|
|
653
802
|
params.delete_if { |k,v|
|
654
803
|
k.class == String || v.nil? || v == false
|
655
804
|
}
|
656
|
-
|
805
|
+
if `uname` =~ /Darwin/
|
657
806
|
if params.length < 2
|
658
807
|
if options[:a]
|
659
808
|
system %Q{open -a "#{options[:a]}" "#{File.expand_path(wwid.doing_file)}"}
|
@@ -672,41 +821,66 @@ command :open do |c|
|
|
672
821
|
else
|
673
822
|
raise "The open command takes a single parameter. #{params.length} specified."
|
674
823
|
end
|
824
|
+
else
|
825
|
+
raise "No EDITOR variable defined in environment" if ENV['EDITOR'].nil?
|
826
|
+
system %Q{$EDITOR "#{File.expand_path(wwid.doing_file)}"}
|
827
|
+
end
|
675
828
|
end
|
676
829
|
end
|
677
830
|
|
831
|
+
|
678
832
|
desc 'Edit the configuration file'
|
679
833
|
command :config do |c|
|
680
834
|
c.desc 'Editor to use'
|
681
835
|
c.default_value ENV['EDITOR']
|
682
836
|
c.flag [:e,:editor], :default_value => nil
|
683
837
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
c.desc "Use the editor_app defined in ~/.doingrc (#{wwid.config['editor_app']})"
|
688
|
-
c.switch [:x]
|
838
|
+
if `uname` =~ /Darwins/
|
839
|
+
c.desc 'Application to use'
|
840
|
+
c.flag [:a]
|
689
841
|
|
690
|
-
|
691
|
-
|
842
|
+
c.desc "Use the editor_app defined in ~/.doingrc (#{wwid.config['editor_app']})"
|
843
|
+
c.switch [:x]
|
692
844
|
|
845
|
+
c.desc 'Application bundle id to use'
|
846
|
+
c.flag [:b]
|
847
|
+
end
|
693
848
|
c.action do |global_options,options,args|
|
694
|
-
if
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
849
|
+
if `uname` =~ /Darwins/
|
850
|
+
if options[:x]
|
851
|
+
%x{open -a "#{wwid.config['editor_app']}" "#{File.join(Dir.home, DOING_CONFIG_NAME)}"}
|
852
|
+
elsif options[:a] || options[:b]
|
853
|
+
if options[:a]
|
854
|
+
%x{open -a "#{options[:a]}" "#{File.join(Dir.home, DOING_CONFIG_NAME)}"}
|
855
|
+
elsif options[:b]
|
856
|
+
%x{open -b #{options[:b]} "#{File.join(Dir.home, DOING_CONFIG_NAME)}"}
|
857
|
+
end
|
858
|
+
else
|
859
|
+
raise "No EDITOR variable defined in environment" if options[:e].nil? && ENV['EDITOR'].nil?
|
860
|
+
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
861
|
+
system %Q{#{editor} "#{File.expand_path(DOING_CONFIG)}"}
|
701
862
|
end
|
702
863
|
else
|
703
864
|
raise "No EDITOR variable defined in environment" if options[:e].nil? && ENV['EDITOR'].nil?
|
704
865
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
705
|
-
system %Q{#{editor} "#{File.
|
866
|
+
system %Q{#{editor} "#{File.join(Dir.home, DOING_CONFIG_NAME)}"}
|
706
867
|
end
|
707
868
|
end
|
708
869
|
end
|
709
870
|
|
871
|
+
desc 'Undo the last change to the doing_file'
|
872
|
+
command :undo do |c|
|
873
|
+
c.desc 'Specify alternate doing file'
|
874
|
+
c.default_value wwid.doing_file
|
875
|
+
c.flag [:f,:file], :default_value => wwid.doing_file
|
876
|
+
|
877
|
+
c.action do |global_options,options,args|
|
878
|
+
file = options[:f] || wwid.doing_file
|
879
|
+
wwid.restore_backup(file)
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
|
710
884
|
pre do |global,command,options,args|
|
711
885
|
if global[:doing_file]
|
712
886
|
wwid.init_doing_file(input=global[:doing_file])
|
@@ -728,6 +902,7 @@ end
|
|
728
902
|
post do |global,command,options,args|
|
729
903
|
# Use skips_post before a command to skip this
|
730
904
|
# block on that command only
|
905
|
+
puts wwid.results.join("\n")
|
731
906
|
end
|
732
907
|
|
733
908
|
on_error do |exception|
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'deep_merge'
|
4
|
+
|
2
5
|
class String
|
3
6
|
def cap_first
|
4
7
|
self.sub(/^\w/) do |m|
|
@@ -9,13 +12,14 @@ class String
|
|
9
12
|
end
|
10
13
|
|
11
14
|
class WWID
|
12
|
-
attr_accessor :content, :sections, :current_section, :doing_file, :config
|
15
|
+
attr_accessor :content, :sections, :current_section, :doing_file, :config, :results
|
13
16
|
|
14
17
|
|
15
18
|
def initialize
|
16
19
|
@content = {}
|
17
20
|
@timers = {}
|
18
21
|
@config = read_config
|
22
|
+
@results = []
|
19
23
|
|
20
24
|
@config['doing_file'] ||= "~/what_was_i_doing.md"
|
21
25
|
@config['current_section'] ||= 'Currently'
|
@@ -63,6 +67,7 @@ class WWID
|
|
63
67
|
}
|
64
68
|
@config['marker_tag'] ||= 'flagged'
|
65
69
|
@config['marker_color'] ||= 'red'
|
70
|
+
@config['default_tags'] ||= []
|
66
71
|
|
67
72
|
@current_section = config['current_section']
|
68
73
|
@default_template = config['templates']['default']['template']
|
@@ -70,7 +75,7 @@ class WWID
|
|
70
75
|
|
71
76
|
@config[:include_notes] ||= true
|
72
77
|
|
73
|
-
File.open(
|
78
|
+
File.open(home_config, 'w') { |yf| YAML::dump(config, yf) }
|
74
79
|
end
|
75
80
|
|
76
81
|
def init_doing_file(input=nil)
|
@@ -141,12 +146,23 @@ class WWID
|
|
141
146
|
end
|
142
147
|
end
|
143
148
|
|
149
|
+
def home_config
|
150
|
+
File.join(Dir.home, DOING_CONFIG_NAME)
|
151
|
+
end
|
152
|
+
|
144
153
|
def read_config
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
154
|
+
config = {}
|
155
|
+
dir = Dir.pwd
|
156
|
+
while(dir != '/')
|
157
|
+
if File.exists? File.join(dir, DOING_CONFIG_NAME)
|
158
|
+
config = YAML.load_file(File.join(dir, DOING_CONFIG_NAME)).deep_merge!(config)
|
159
|
+
end
|
160
|
+
dir = File.dirname(dir)
|
161
|
+
end
|
162
|
+
if config.empty? && File.exists?(home_config)
|
163
|
+
config = YAML.load_file(home_config)
|
149
164
|
end
|
165
|
+
config
|
150
166
|
end
|
151
167
|
|
152
168
|
def fork_editor(input="")
|
@@ -182,7 +198,10 @@ class WWID
|
|
182
198
|
end
|
183
199
|
|
184
200
|
# This takes a multi-line string and formats it as an entry
|
185
|
-
#
|
201
|
+
# Params:
|
202
|
+
# +input+:: String
|
203
|
+
# Returns:
|
204
|
+
# [title(String), note(Array)]
|
186
205
|
def format_input(input)
|
187
206
|
raise "No content in entry" if input.nil? || input.strip.length == 0
|
188
207
|
input_lines = input.split(/[\n\r]+/)
|
@@ -197,16 +216,21 @@ class WWID
|
|
197
216
|
[title, note]
|
198
217
|
end
|
199
218
|
|
219
|
+
# Converts simple strings into seconds that can be added to a Time object
|
220
|
+
# Params:
|
221
|
+
# +qty+:: HH:MM or XX[dhm][[XXhm][XXm]] (1d2h30m, 45m, 1.5d, 1h20m, etc.)
|
222
|
+
# Returns:
|
223
|
+
# seconds(Integer)
|
200
224
|
def chronify(input)
|
201
|
-
if input =~ /^(\d+)([mhd])
|
225
|
+
if input =~ /^(\d+)\s*([mhd])?/i
|
202
226
|
amt = $1
|
203
227
|
type = $2.nil? ? "m" : $2
|
204
228
|
input = case type.downcase
|
205
|
-
when
|
229
|
+
when 'm'
|
206
230
|
amt + " minutes ago"
|
207
|
-
when
|
231
|
+
when 'h'
|
208
232
|
amt + " hours ago"
|
209
|
-
when
|
233
|
+
when 'd'
|
210
234
|
amt + " days ago"
|
211
235
|
else
|
212
236
|
input
|
@@ -216,6 +240,32 @@ class WWID
|
|
216
240
|
Chronic.parse(input, {:context => :past, :ambiguous_time_range => 8})
|
217
241
|
end
|
218
242
|
|
243
|
+
|
244
|
+
# Converts simple strings into seconds that can be added to a Time object
|
245
|
+
# Params:
|
246
|
+
# +qty+:: HH:MM or XX[dhm][[XXhm][XXm]] (1d2h30m, 45m, 1.5d, 1h20m, etc.)
|
247
|
+
# Returns seconds (Integer)
|
248
|
+
def chronify_qty(qty)
|
249
|
+
minutes = 0
|
250
|
+
if qty.strip =~ /^(\d+):(\d\d)$/
|
251
|
+
minutes += $1.to_i * 60
|
252
|
+
minutes += $2.to_i
|
253
|
+
elsif qty.strip =~ /\d+\s*[hmd]/
|
254
|
+
matches = qty.scan(/([\d\.]+)\s*([hmd])/)
|
255
|
+
matches.each {|m|
|
256
|
+
case m[1]
|
257
|
+
when "m"
|
258
|
+
minutes += m[0].to_i
|
259
|
+
when "h"
|
260
|
+
minutes += (m[0].to_f * 60).round
|
261
|
+
when "d"
|
262
|
+
minutes += (m[0].to_f * 60 * 24).round
|
263
|
+
end
|
264
|
+
}
|
265
|
+
end
|
266
|
+
minutes * 60
|
267
|
+
end
|
268
|
+
|
219
269
|
def sections
|
220
270
|
@content.keys
|
221
271
|
end
|
@@ -282,12 +332,29 @@ class WWID
|
|
282
332
|
opt[:date] ||= Time.now
|
283
333
|
opt[:note] ||= []
|
284
334
|
opt[:back] ||= Time.now
|
335
|
+
opt[:timed] ||= false
|
285
336
|
|
286
|
-
|
337
|
+
title = [title.strip.cap_first] + @config['default_tags'].map{|t| '@' + t.sub(/^ *@/,'').chomp}
|
338
|
+
entry = {'title' => title.join(' '), 'date' => opt[:back]}
|
287
339
|
unless opt[:note] =~ /^\s*$/s
|
288
340
|
entry['note'] = opt[:note]
|
289
341
|
end
|
290
|
-
@content[section]['items']
|
342
|
+
items = @content[section]['items']
|
343
|
+
if opt[:timed]
|
344
|
+
items.reverse!
|
345
|
+
items.each_with_index {|i,x|
|
346
|
+
if i['title'] =~ / @done/
|
347
|
+
next
|
348
|
+
else
|
349
|
+
items[x]['title'] = "#{i['title']} @done(#{opt[:back].strftime('%F %R')})"
|
350
|
+
break
|
351
|
+
end
|
352
|
+
}
|
353
|
+
items.reverse!
|
354
|
+
end
|
355
|
+
items.push(entry)
|
356
|
+
@content[section]['items'] = items
|
357
|
+
@results.push(%Q{Added "#{entry['title']}" to #{section}})
|
291
358
|
end
|
292
359
|
|
293
360
|
def tag_last(opt={})
|
@@ -295,39 +362,68 @@ class WWID
|
|
295
362
|
opt[:count] ||= 1
|
296
363
|
opt[:archive] ||= false
|
297
364
|
opt[:tags] ||= ["done"]
|
365
|
+
opt[:sequential] ||= false
|
298
366
|
opt[:date] ||= false
|
299
367
|
opt[:remove] ||= false
|
300
368
|
opt[:back] ||= Time.now
|
301
369
|
|
370
|
+
|
371
|
+
|
302
372
|
opt[:section] = guess_section(opt[:section])
|
303
373
|
|
304
374
|
if @content.has_key?(opt[:section])
|
305
375
|
# sort_section(opt[:section])
|
306
|
-
|
376
|
+
items = @content[opt[:section]]['items'].dup.sort_by{|item| item['date'] }.reverse
|
377
|
+
|
378
|
+
index = 0
|
379
|
+
done_date = Time.now
|
380
|
+
next_start = Time.now
|
381
|
+
items.map! {|item|
|
382
|
+
break if index == opt[:count]
|
383
|
+
if opt[:sequential]
|
384
|
+
done_date = next_start - 1
|
385
|
+
next_start = item['date']
|
386
|
+
elsif opt[:back].instance_of? Fixnum
|
387
|
+
done_date = item['date'] + opt[:back]
|
388
|
+
else
|
389
|
+
done_date = opt[:back]
|
390
|
+
end
|
307
391
|
|
308
|
-
@content[opt[:section]]['items'].each_with_index {|item, i|
|
309
|
-
break if i == opt[:count]
|
310
392
|
title = item['title']
|
311
393
|
opt[:tags].each {|tag|
|
312
394
|
if opt[:remove]
|
313
395
|
title.gsub!(/(^| )@#{tag.strip}(\([^\)]*\))?/,'')
|
396
|
+
@results.push("Updated: #{title}")
|
314
397
|
else
|
315
398
|
unless title =~ /@#{tag}/
|
316
|
-
|
317
|
-
|
399
|
+
title.chomp!
|
400
|
+
if opt[:date]
|
401
|
+
title += " @#{tag}(#{done_date.strftime('%F %R')})"
|
318
402
|
else
|
319
403
|
title += " @#{tag}"
|
320
404
|
end
|
405
|
+
@results.push("Updated: #{title}")
|
321
406
|
end
|
322
407
|
end
|
323
408
|
}
|
324
|
-
|
409
|
+
|
410
|
+
item['title'] = title
|
411
|
+
index += 1
|
412
|
+
item
|
325
413
|
}
|
326
414
|
|
415
|
+
@content[opt[:section]]['items'] = items
|
416
|
+
|
327
417
|
if opt[:archive] && opt[:section] != "Archive"
|
418
|
+
# concat [count] items from [section] and archive section
|
328
419
|
archived = @content[opt[:section]]['items'][0..opt[:count]-1].concat(@content['Archive']['items'])
|
420
|
+
# chop [count] items off of [section] items
|
329
421
|
@content[opt[:section]]['items'] = @content[opt[:section]]['items'][opt[:count]..-1]
|
422
|
+
# overwrite archive section with concatenated array
|
330
423
|
@content['Archive']['items'] = archived
|
424
|
+
# log it
|
425
|
+
result = opt[:count] == 1 ? "1 entry" : "#{opt[:count]} entries"
|
426
|
+
@results.push("Archived #{result}")
|
331
427
|
end
|
332
428
|
|
333
429
|
write(@doing_file)
|
@@ -336,6 +432,41 @@ class WWID
|
|
336
432
|
end
|
337
433
|
end
|
338
434
|
|
435
|
+
def note_last(section, note, replace=false)
|
436
|
+
section = guess_section(section)
|
437
|
+
|
438
|
+
if @content.has_key?(section)
|
439
|
+
# sort_section(opt[:section])
|
440
|
+
items = @content[section]['items'].dup.sort_by{|item| item['date'] }.reverse
|
441
|
+
|
442
|
+
current_note = items[0]['note']
|
443
|
+
current_note = [] if current_note.nil?
|
444
|
+
title = items[0]['title']
|
445
|
+
if replace
|
446
|
+
items[0]['note'] = note
|
447
|
+
if note.empty? && !current_note.empty?
|
448
|
+
@results.push(%Q{Removed note from "#{title}"})
|
449
|
+
elsif current_note.length > 0 && note.length > 0
|
450
|
+
@results.push(%Q{Replaced note from "#{title}"})
|
451
|
+
elsif note.length > 0
|
452
|
+
@results.push(%Q{Added note to #{title}})
|
453
|
+
else
|
454
|
+
@results.push(%Q{Entry "#{title}" has no note})
|
455
|
+
end
|
456
|
+
elsif current_note.class == Array
|
457
|
+
items[0]['note'] = current_note.concat(note)
|
458
|
+
@results.push(%Q{Added note to "#{title}"}) if note.length > 0
|
459
|
+
else
|
460
|
+
items[0]['note'] = note
|
461
|
+
@results.push(%Q{Added note to "#{title}"}) if note.length > 0
|
462
|
+
end
|
463
|
+
|
464
|
+
@content[section]['items'] = items
|
465
|
+
else
|
466
|
+
raise "Section not found"
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
339
470
|
# accepts one tag and the raw text of a new item
|
340
471
|
# if the passed tag is on any item, it's replaced with @done
|
341
472
|
# if new_item is not nil, it's tagged with the passed tag and inserted
|
@@ -345,6 +476,7 @@ class WWID
|
|
345
476
|
opt[:archive] ||= false
|
346
477
|
opt[:back] ||= Time.now
|
347
478
|
opt[:new_item] ||= false
|
479
|
+
opt[:note] ||= false
|
348
480
|
|
349
481
|
opt[:section] = guess_section(opt[:section])
|
350
482
|
|
@@ -358,14 +490,18 @@ class WWID
|
|
358
490
|
@content[opt[:section]]['items'][i]['title'] = title
|
359
491
|
|
360
492
|
if opt[:archive] && opt[:section] != "Archive"
|
493
|
+
@results.push(%Q{Completed and archived "#{@content[opt[:section]]['items'][i]['title']}"})
|
361
494
|
@content['Archive']['items'].push(@content[opt[:section]]['items'][i])
|
362
495
|
@content[opt[:section]]['items'].delete_at(i)
|
496
|
+
else
|
497
|
+
@results.push(%Q{Completed "#{@content[opt[:section]]['items'][i]['title']}"})
|
363
498
|
end
|
364
499
|
end
|
365
500
|
}
|
366
501
|
|
367
502
|
if opt[:new_item]
|
368
503
|
title, note = format_input(opt[:new_item])
|
504
|
+
note.push(opt[:note]) if opt[:note]
|
369
505
|
title += " @#{tag}"
|
370
506
|
add_item(title.cap_first, opt[:section], {:note => note, :back => opt[:back]})
|
371
507
|
end
|
@@ -374,7 +510,7 @@ class WWID
|
|
374
510
|
end
|
375
511
|
|
376
512
|
def write(file=nil)
|
377
|
-
|
513
|
+
unless @other_content_top
|
378
514
|
output = ""
|
379
515
|
else
|
380
516
|
output = @other_content_top.join("\n") + "\n"
|
@@ -383,11 +519,12 @@ class WWID
|
|
383
519
|
output += section['original'] + "\n"
|
384
520
|
output += list_section({:section => title, :template => "\t- %date | %title%note", :highlight => false})
|
385
521
|
}
|
386
|
-
output += @other_content_bottom.join("\n")
|
522
|
+
output += @other_content_bottom.join("\n") unless @other_content_bottom.nil?
|
387
523
|
if file.nil?
|
388
524
|
$stdout.puts output
|
389
525
|
else
|
390
526
|
if File.exists?(File.expand_path(file))
|
527
|
+
FileUtils.cp(file,file+"~")
|
391
528
|
File.open(File.expand_path(file),'w+') do |f|
|
392
529
|
f.puts output
|
393
530
|
end
|
@@ -395,6 +532,15 @@ class WWID
|
|
395
532
|
end
|
396
533
|
end
|
397
534
|
|
535
|
+
def restore_backup(file)
|
536
|
+
if File.exists?(file+"~")
|
537
|
+
puts file+"~"
|
538
|
+
FileUtils.cp(file+"~",file)
|
539
|
+
@results.push("Restored #{file}")
|
540
|
+
end
|
541
|
+
end
|
542
|
+
|
543
|
+
|
398
544
|
def choose_section
|
399
545
|
sections.each_with_index {|section, i|
|
400
546
|
puts "% 3d: %s" % [i+1, section]
|
@@ -438,6 +584,8 @@ class WWID
|
|
438
584
|
opt[:tag_filter] ||= false
|
439
585
|
opt[:tags_color] ||= false
|
440
586
|
opt[:times] ||= false
|
587
|
+
opt[:search] ||= false
|
588
|
+
|
441
589
|
# opt[:highlight] ||= true
|
442
590
|
section = ""
|
443
591
|
if opt[:section].nil?
|
@@ -488,6 +636,13 @@ class WWID
|
|
488
636
|
}
|
489
637
|
end
|
490
638
|
|
639
|
+
if opt[:search]
|
640
|
+
items.keep_if {|item|
|
641
|
+
text = item['note'] ? item['title'] + item['note'].join(" ") : item['title']
|
642
|
+
text =~ /#{opt[:search]}/i
|
643
|
+
}
|
644
|
+
end
|
645
|
+
|
491
646
|
if opt[:today]
|
492
647
|
items.delete_if {|item|
|
493
648
|
item['date'] < Date.today.to_time
|
@@ -792,9 +947,9 @@ EOT
|
|
792
947
|
list_section({:section => @current_section, :wrap_width => cfg['wrap_width'], :count => 0, :format => cfg['date_format'], :template => cfg['template'], :order => "asc", :today => true, :times => times, :output => output})
|
793
948
|
end
|
794
949
|
|
795
|
-
def yesterday(section)
|
950
|
+
def yesterday(section,times=nil,output=nil)
|
796
951
|
section = guess_section(section)
|
797
|
-
list_section({:section => section, :count => 0, :order => "asc", :yesterday => true})
|
952
|
+
list_section({:section => section, :count => 0, :order => "asc", :yesterday => true, :times => times, :output => output })
|
798
953
|
end
|
799
954
|
|
800
955
|
def recent(count=10,section=nil,opt={})
|
data/lib/doing.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0pre
|
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-
|
11
|
+
date: 2014-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -106,6 +106,20 @@ dependencies:
|
|
106
106
|
- - '>='
|
107
107
|
- !ruby/object:Gem::Version
|
108
108
|
version: 0.10.2
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: deep_merge
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
type: :runtime
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - '>='
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
109
123
|
description: A tool for managing a TaskPaper-like file of recent activites. Perfect
|
110
124
|
for the late-night hacker on too much caffeine to remember what they accomplished
|
111
125
|
at 2 in the morning.
|
@@ -144,9 +158,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
144
158
|
version: '0'
|
145
159
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
160
|
requirements:
|
147
|
-
- - '
|
161
|
+
- - '>'
|
148
162
|
- !ruby/object:Gem::Version
|
149
|
-
version:
|
163
|
+
version: 1.3.1
|
150
164
|
requirements: []
|
151
165
|
rubyforge_project:
|
152
166
|
rubygems_version: 2.2.2
|