doing 1.0.81 → 1.0.85
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +10 -4
- data/bin/doing +167 -39
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +175 -45
- data/lib/doing.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f11196376a7860f14164c80a107ce4151b18399f785cd607ba4f3a27205828fb
|
4
|
+
data.tar.gz: 1033e528416e1e3660db44ae55973009aa9b766f2566c0ff6e4bb942fdce4c05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db7dc195eedf329ec7aad7e29830cf0d42c57178570a4675ce607e29761f733d2371b390f8c8ea73425a86240a28a91f7e05842ad9b49dd89257affbca12e5e6
|
7
|
+
data.tar.gz: 2b992ffea5a41c6f81ab7b5b1fb9b244197cca7eb1d7bbbae1eb6f9911db6ee16293b19e32c8c0b31cd03a3e181c4df9f8e1de893cc37c479fa2bdfd4accf6c4
|
data/README.md
CHANGED
@@ -27,7 +27,7 @@ If there's something I want to look at later but doesn't need to be added to a t
|
|
27
27
|
|
28
28
|
## Installation
|
29
29
|
|
30
|
-
The current version of `doing` is <!--VER-->1.0.
|
30
|
+
The current version of `doing` is <!--VER-->1.0.84<!--END VER-->.
|
31
31
|
|
32
32
|
$ [sudo] gem install doing
|
33
33
|
|
@@ -245,7 +245,7 @@ You can create your own "views" in the `~/.doingrc` file and view them with `doi
|
|
245
245
|
|
246
246
|
views:
|
247
247
|
old:
|
248
|
-
section:
|
248
|
+
section: Archive
|
249
249
|
count: 5
|
250
250
|
wrap_width: 0
|
251
251
|
date_format: '%F %_I:%M%P'
|
@@ -253,6 +253,10 @@ You can create your own "views" in the `~/.doingrc` file and view them with `doi
|
|
253
253
|
order: asc
|
254
254
|
tags: done finished cancelled
|
255
255
|
tags_bool: ANY
|
256
|
+
only_timed: false
|
257
|
+
tag_sort: time
|
258
|
+
tag_order: asc
|
259
|
+
totals: true
|
256
260
|
|
257
261
|
You can add additional custom views. Just nest them under the `views` key (indented two spaces from the edge). Multiple views would look like this:
|
258
262
|
|
@@ -276,7 +280,9 @@ You can add new sections with `doing add_section section_name`. You can also cre
|
|
276
280
|
|
277
281
|
The `tags` and `tags_bool` keys allow you to specify tags that the view is filtered by. You can list multiple tags separated by spaces, and then use `tags_bool` to specify `ALL`, `ANY`, or `NONE` to determine how it handles the multiple tags.
|
278
282
|
|
279
|
-
The `order` key defines the sort order of the output. This is applied _after_ the tasks are retrieved and cut off at the maximum number specified in `count`.
|
283
|
+
The `order` key defines the sort order of the output (asc or desc). This is applied _after_ the tasks are retrieved and cut off at the maximum number specified in `count`.
|
284
|
+
|
285
|
+
You can include tag timers and totals in the output with `totals: true`. Control tag output using `tag_sort` (name or title) and `tag_order` (asc or desc). You can also output only timed entries using `only_timed: true`. All of these options can be overridden using flags on the `doing view` command.
|
280
286
|
|
281
287
|
Regarding colors, you can use them to create very nice displays if you're outputting to a color terminal. Example:
|
282
288
|
|
@@ -291,7 +297,7 @@ Outputs:
|
|
291
297
|
|
292
298
|

|
293
299
|
|
294
|
-
You can also specify a default output format for a view. Most of the optional output formats override the template specification (`html`, `csv`, `json`). If the `view` command is used with the `-o` flag, it will override what's specified in the
|
300
|
+
You can also specify a default output format for a view. Most of the optional output formats override the template specification (`html`, `csv`, `json`). If the `view` command is used with the `-o` flag, it will override what's specified for the view in the config.
|
295
301
|
|
296
302
|
### Colors
|
297
303
|
|
data/bin/doing
CHANGED
@@ -55,7 +55,7 @@ arg_name 'ENTRY'
|
|
55
55
|
command %i[now next] do |c|
|
56
56
|
c.desc 'Section'
|
57
57
|
c.arg_name 'NAME'
|
58
|
-
c.flag %i[s section]
|
58
|
+
c.flag %i[s section]
|
59
59
|
|
60
60
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
61
61
|
c.switch %i[e editor], negatable: false, default_value: false
|
@@ -84,7 +84,11 @@ command %i[now next] do |c|
|
|
84
84
|
date = Time.now
|
85
85
|
end
|
86
86
|
|
87
|
-
|
87
|
+
if options[:section]
|
88
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
89
|
+
else
|
90
|
+
options[:section] = wwid.config['current_section']
|
91
|
+
end
|
88
92
|
|
89
93
|
if options[:e] || (args.empty? && $stdin.stat.size.zero?)
|
90
94
|
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
@@ -135,7 +139,9 @@ command :note do |c|
|
|
135
139
|
c.switch %i[r remove], negatable: false, default_value: false
|
136
140
|
|
137
141
|
c.action do |_global_options, options, args|
|
138
|
-
|
142
|
+
if options[:section]
|
143
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
144
|
+
end
|
139
145
|
|
140
146
|
if options[:e] || (args.empty? && $stdin.stat.size.zero? && !options[:r])
|
141
147
|
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
@@ -176,7 +182,7 @@ arg_name 'ENTRY'
|
|
176
182
|
command :meanwhile do |c|
|
177
183
|
c.desc 'Section'
|
178
184
|
c.arg_name 'NAME'
|
179
|
-
c.flag %i[s section]
|
185
|
+
c.flag %i[s section]
|
180
186
|
|
181
187
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
182
188
|
c.switch %i[e editor], negatable: false, default_value: false
|
@@ -201,7 +207,11 @@ command :meanwhile do |c|
|
|
201
207
|
date = Time.now
|
202
208
|
end
|
203
209
|
|
204
|
-
|
210
|
+
if options[:section]
|
211
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
212
|
+
else
|
213
|
+
section = wwid.config['current_section']
|
214
|
+
end
|
205
215
|
input = ''
|
206
216
|
|
207
217
|
if options[:e]
|
@@ -308,7 +318,7 @@ command :select do |c|
|
|
308
318
|
c.arg_name 'FILE'
|
309
319
|
c.flag %i[save_to]
|
310
320
|
|
311
|
-
c.desc 'Output
|
321
|
+
c.desc 'Output entries to format (doing|taskpaper|csv|html|json|template|timeline)'
|
312
322
|
c.arg_name 'FORMAT'
|
313
323
|
c.flag %i[o output], must_match: /^(?:doing|taskpaper|html|csv|json|template|timeline)$/i
|
314
324
|
|
@@ -323,10 +333,6 @@ command :later do |c|
|
|
323
333
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
324
334
|
c.switch %i[e editor], negatable: false, default_value: false
|
325
335
|
|
326
|
-
c.desc 'Edit entry with specified app'
|
327
|
-
c.arg_name 'APP'
|
328
|
-
c.flag %i[a app]
|
329
|
-
|
330
336
|
c.desc 'Backdate start time to date string [4pm|20m|2h|yesterday noon]'
|
331
337
|
c.arg_name 'DATE_STRING'
|
332
338
|
c.flag %i[b back]
|
@@ -343,7 +349,7 @@ command :later do |c|
|
|
343
349
|
date = Time.now
|
344
350
|
end
|
345
351
|
|
346
|
-
if options[:
|
352
|
+
if options[:editor] || (args.empty? && $stdin.stat.size.zero?)
|
347
353
|
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
348
354
|
|
349
355
|
input = args.empty? ? '' : args.join(' ')
|
@@ -399,7 +405,7 @@ command %i[done did] do |c|
|
|
399
405
|
|
400
406
|
c.desc 'Section'
|
401
407
|
c.arg_name 'NAME'
|
402
|
-
c.flag %i[s section]
|
408
|
+
c.flag %i[s section]
|
403
409
|
|
404
410
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
405
411
|
c.switch %i[e editor], negatable: false, default_value: false
|
@@ -440,9 +446,13 @@ command %i[done did] do |c|
|
|
440
446
|
donedate = options[:date] ? "(#{finish_date.strftime('%F %R')})" : ''
|
441
447
|
end
|
442
448
|
|
443
|
-
|
449
|
+
if options[:section]
|
450
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
451
|
+
else
|
452
|
+
section = wwid.config['current_section']
|
453
|
+
end
|
444
454
|
|
445
|
-
if options[:
|
455
|
+
if options[:editor]
|
446
456
|
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
447
457
|
|
448
458
|
input = ''
|
@@ -497,7 +507,7 @@ command :cancel do |c|
|
|
497
507
|
|
498
508
|
c.desc 'Section'
|
499
509
|
c.arg_name 'NAME'
|
500
|
-
c.flag %i[s section]
|
510
|
+
c.flag %i[s section]
|
501
511
|
|
502
512
|
c.desc 'Cancel the last X entries containing TAG. Separate multiple tags with comma (--tag=tag1,tag2)'
|
503
513
|
c.arg_name 'TAG'
|
@@ -511,7 +521,11 @@ command :cancel do |c|
|
|
511
521
|
c.switch %i[u unfinished], negatable: false, default_value: false
|
512
522
|
|
513
523
|
c.action do |_global_options, options, args|
|
514
|
-
|
524
|
+
if options[:section]
|
525
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
526
|
+
else
|
527
|
+
section = wwid.config['current_section']
|
528
|
+
end
|
515
529
|
|
516
530
|
if options[:tag].nil?
|
517
531
|
tags = []
|
@@ -564,6 +578,10 @@ command :finish do |c|
|
|
564
578
|
c.arg_name 'INTERVAL'
|
565
579
|
c.flag %i[t took]
|
566
580
|
|
581
|
+
c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm). If used, ignores --back.)
|
582
|
+
c.arg_name 'DATE_STRING'
|
583
|
+
c.flag [:at]
|
584
|
+
|
567
585
|
c.desc 'Finish the last X entries containing TAG.
|
568
586
|
Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool'
|
569
587
|
c.arg_name 'TAG'
|
@@ -590,17 +608,31 @@ command :finish do |c|
|
|
590
608
|
|
591
609
|
c.desc 'Section'
|
592
610
|
c.arg_name 'NAME'
|
593
|
-
c.flag %i[s section]
|
611
|
+
c.flag %i[s section]
|
594
612
|
|
595
613
|
c.action do |_global_options, options, args|
|
596
|
-
|
614
|
+
if options[:section]
|
615
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
616
|
+
else
|
617
|
+
section = wwid.config['current_section']
|
618
|
+
end
|
597
619
|
|
598
620
|
unless options[:auto]
|
621
|
+
if options[:took]
|
622
|
+
took = wwid.chronify_qty(options[:took])
|
623
|
+
exit_now! 'Unable to parse date string for --took' if took.nil?
|
624
|
+
end
|
625
|
+
|
599
626
|
exit_now! '--back and --took cannot be used together' if options[:back] && options[:took]
|
600
627
|
|
601
628
|
exit_now! '--search and --tag cannot be used together' if options[:search] && options[:tag]
|
602
629
|
|
603
|
-
if options[:
|
630
|
+
if options[:at]
|
631
|
+
finish_date = wwid.chronify(options[:at])
|
632
|
+
exit_now! 'Unable to parse date string for --at' if finish_date.nil?
|
633
|
+
|
634
|
+
date = options[:took] ? finish_date - took : finish_date
|
635
|
+
elsif options[:back]
|
604
636
|
date = wwid.chronify(options[:back])
|
605
637
|
|
606
638
|
exit_now! 'Unable to parse date string' if date.nil?
|
@@ -826,7 +858,7 @@ desc 'Mark last entry as highlighted'
|
|
826
858
|
command [:mark, :flag] do |c|
|
827
859
|
c.desc 'Section'
|
828
860
|
c.arg_name 'NAME'
|
829
|
-
c.flag %i[s section]
|
861
|
+
c.flag %i[s section]
|
830
862
|
|
831
863
|
c.desc 'Remove mark'
|
832
864
|
c.switch %i[r remove], negatable: false, default_value: false
|
@@ -1285,7 +1317,7 @@ command :last do |c|
|
|
1285
1317
|
|
1286
1318
|
end
|
1287
1319
|
|
1288
|
-
if options[:
|
1320
|
+
if options[:editor]
|
1289
1321
|
wwid.edit_last(section: options[:s], options: { search: options[:search], tag: tags, tag_bool: options[:bool] })
|
1290
1322
|
else
|
1291
1323
|
puts wwid.last(times: true, section: options[:s],
|
@@ -1344,13 +1376,14 @@ command :colors do |c|
|
|
1344
1376
|
end
|
1345
1377
|
|
1346
1378
|
desc 'Display a user-created view'
|
1379
|
+
long_desc 'Command line options override associated view settings'
|
1347
1380
|
arg_name 'VIEW_NAME'
|
1348
1381
|
command :view do |c|
|
1349
|
-
c.desc 'Section
|
1382
|
+
c.desc 'Section'
|
1350
1383
|
c.arg_name 'NAME'
|
1351
1384
|
c.flag %i[s section]
|
1352
1385
|
|
1353
|
-
c.desc 'Count to display
|
1386
|
+
c.desc 'Count to display'
|
1354
1387
|
c.arg_name 'COUNT'
|
1355
1388
|
c.flag %i[c count], must_match: /^\d+$/, type: Integer
|
1356
1389
|
|
@@ -1368,12 +1401,14 @@ command :view do |c|
|
|
1368
1401
|
c.switch [:color], default_value: true, negatable: true
|
1369
1402
|
|
1370
1403
|
c.desc 'Sort tags by (name|time)'
|
1371
|
-
default = 'time'
|
1372
|
-
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1373
1404
|
c.arg_name 'KEY'
|
1374
|
-
c.flag [:tag_sort], must_match: /^(?:name|time)$/i
|
1405
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i
|
1375
1406
|
|
1376
|
-
c.desc '
|
1407
|
+
c.desc 'Tag sort direction (asc|desc)'
|
1408
|
+
c.arg_name 'DIRECTION'
|
1409
|
+
c.flag [:tag_order], must_match: /^(?:a(?:sc)?|d(?:esc)?)$/i
|
1410
|
+
|
1411
|
+
c.desc 'Only show items with recorded time intervals (override view settings)'
|
1377
1412
|
c.switch [:only_timed], default_value: false, negatable: false
|
1378
1413
|
|
1379
1414
|
c.action do |_global_options, options, args|
|
@@ -1383,7 +1418,11 @@ command :view do |c|
|
|
1383
1418
|
wwid.guess_view(args[0])
|
1384
1419
|
end
|
1385
1420
|
|
1386
|
-
|
1421
|
+
if options[:section]
|
1422
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
1423
|
+
else
|
1424
|
+
section = wwid.config['current_section']
|
1425
|
+
end
|
1387
1426
|
|
1388
1427
|
view = wwid.get_view(title)
|
1389
1428
|
if view
|
@@ -1422,10 +1461,31 @@ command :view do |c|
|
|
1422
1461
|
end
|
1423
1462
|
order = view.key?('order') ? view['order'] : 'asc'
|
1424
1463
|
|
1425
|
-
|
1464
|
+
totals = if options[:totals]
|
1465
|
+
true
|
1466
|
+
else
|
1467
|
+
view.key?('totals') ? view['totals'] : false
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
options[:t] = true if totals
|
1426
1471
|
options[:output]&.downcase!
|
1427
|
-
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1428
1472
|
|
1473
|
+
options[:sort_tags] = if options[:tag_sort]
|
1474
|
+
options[:tag_sort] =~ /^n/i ? true : false
|
1475
|
+
elsif view.key?('tag_sort')
|
1476
|
+
view['tag_sort'] =~ /^n/i ? true : false
|
1477
|
+
else
|
1478
|
+
false
|
1479
|
+
end
|
1480
|
+
|
1481
|
+
tag_order = if options[:tag_order]
|
1482
|
+
options[:tag_order] =~ /^d/i ? 'desc' : 'asc'
|
1483
|
+
elsif view.key?('tag_order')
|
1484
|
+
view['tag_order'] =~ /^d/i ? 'desc' : 'asc'
|
1485
|
+
else
|
1486
|
+
'asc'
|
1487
|
+
end
|
1488
|
+
warn "TAG ORDER: #{options[:tag_order]}"
|
1429
1489
|
opts = {
|
1430
1490
|
count: count,
|
1431
1491
|
format: format,
|
@@ -1436,10 +1496,11 @@ command :view do |c|
|
|
1436
1496
|
section: section,
|
1437
1497
|
sort_tags: options[:sort_tags],
|
1438
1498
|
tag_filter: tag_filter,
|
1499
|
+
tag_order: tag_order,
|
1439
1500
|
tags_color: tags_color,
|
1440
1501
|
template: template,
|
1441
1502
|
times: options[:t],
|
1442
|
-
totals:
|
1503
|
+
totals: totals
|
1443
1504
|
}
|
1444
1505
|
|
1445
1506
|
puts wwid.list_section(opts)
|
@@ -1489,6 +1550,11 @@ command :archive do |c|
|
|
1489
1550
|
c.arg_name 'QUERY'
|
1490
1551
|
c.flag [:search]
|
1491
1552
|
|
1553
|
+
c.desc 'Archive entries older than date
|
1554
|
+
(Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
|
1555
|
+
c.arg_name 'DATE_STRING'
|
1556
|
+
c.flag [:before]
|
1557
|
+
|
1492
1558
|
c.action do |_global_options, options, args|
|
1493
1559
|
if args.empty?
|
1494
1560
|
section = wwid.current_section
|
@@ -1518,6 +1584,7 @@ command :archive do |c|
|
|
1518
1584
|
'AND'
|
1519
1585
|
end
|
1520
1586
|
opts = {
|
1587
|
+
before: options[:before],
|
1521
1588
|
bool: options[:bool],
|
1522
1589
|
destination: options[:to],
|
1523
1590
|
keep: options[:keep],
|
@@ -1528,17 +1595,64 @@ command :archive do |c|
|
|
1528
1595
|
end
|
1529
1596
|
end
|
1530
1597
|
|
1598
|
+
desc 'Move entries to archive file'
|
1599
|
+
command :rotate do |c|
|
1600
|
+
c.desc 'How many items to keep in each section (most recent)'
|
1601
|
+
c.arg_name 'X'
|
1602
|
+
c.flag %i[k keep], must_match: /^\d+$/, type: Integer
|
1603
|
+
|
1604
|
+
c.desc 'Section to rotate'
|
1605
|
+
c.arg_name 'SECTION_NAME'
|
1606
|
+
c.flag %i[s section], default_value: 'All'
|
1607
|
+
|
1608
|
+
c.desc 'Tag filter, combine multiple tags with a comma. Added for compatibility with other commands.'
|
1609
|
+
c.arg_name 'TAG'
|
1610
|
+
c.flag [:tag]
|
1611
|
+
|
1612
|
+
c.desc 'Tag boolean (AND|OR|NOT)'
|
1613
|
+
c.arg_name 'BOOLEAN'
|
1614
|
+
c.flag [:bool], must_match: /(?:and|all|any|or|not|none)/i, default_value: 'AND'
|
1615
|
+
|
1616
|
+
c.desc 'Search filter'
|
1617
|
+
c.arg_name 'QUERY'
|
1618
|
+
c.flag [:search]
|
1619
|
+
|
1620
|
+
c.desc 'Rotate entries older than date
|
1621
|
+
(Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
|
1622
|
+
c.arg_name 'DATE_STRING'
|
1623
|
+
c.flag [:before]
|
1624
|
+
|
1625
|
+
c.action do |_global_options, options, args|
|
1626
|
+
if options[:section] && options[:section] !~ /^all$/i
|
1627
|
+
options[:section] = wwid.guess_section(options[:section])
|
1628
|
+
end
|
1629
|
+
|
1630
|
+
options[:bool] = case options[:bool]
|
1631
|
+
when /(and|all)/i
|
1632
|
+
'AND'
|
1633
|
+
when /(any|or)/i
|
1634
|
+
'OR'
|
1635
|
+
when /(not|none)/i
|
1636
|
+
'NOT'
|
1637
|
+
else
|
1638
|
+
'AND'
|
1639
|
+
end
|
1640
|
+
|
1641
|
+
wwid.rotate(options)
|
1642
|
+
end
|
1643
|
+
end
|
1644
|
+
|
1531
1645
|
desc 'Open the "doing" file in an editor'
|
1532
1646
|
long_desc "`doing open` defaults to using the editor_app setting in #{wwid.config_file} (#{wwid.config.key?('editor_app') ? wwid.config['editor_app'] : 'not set'})"
|
1533
1647
|
command :open do |c|
|
1534
1648
|
if `uname` =~ /Darwin/
|
1535
1649
|
c.desc 'Open with app name'
|
1536
1650
|
c.arg_name 'APP_NAME'
|
1537
|
-
c.flag [
|
1651
|
+
c.flag %i[a app]
|
1538
1652
|
|
1539
1653
|
c.desc 'Open with app bundle id'
|
1540
1654
|
c.arg_name 'BUNDLE_ID'
|
1541
|
-
c.flag [
|
1655
|
+
c.flag %i[b bundle_id]
|
1542
1656
|
end
|
1543
1657
|
c.desc "Open with $EDITOR (#{ENV['EDITOR']})"
|
1544
1658
|
c.switch %i[e editor], negatable: false, default_value: false
|
@@ -1549,11 +1663,11 @@ command :open do |c|
|
|
1549
1663
|
k.instance_of?(String) || v.nil? || v == false
|
1550
1664
|
end
|
1551
1665
|
if `uname` =~ /Darwin/
|
1552
|
-
if options[:
|
1666
|
+
if options[:app]
|
1553
1667
|
system %(open -a "#{options[:a]}" "#{File.expand_path(wwid.doing_file)}")
|
1554
|
-
elsif options[:
|
1668
|
+
elsif options[:bundle_id]
|
1555
1669
|
system %(open -b "#{options[:b]}" "#{File.expand_path(wwid.doing_file)}")
|
1556
|
-
elsif options[:
|
1670
|
+
elsif options[:editor]
|
1557
1671
|
exit_now! 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
1558
1672
|
|
1559
1673
|
system %($EDITOR "#{File.expand_path(wwid.doing_file)}")
|
@@ -1637,12 +1751,15 @@ command :import do |c|
|
|
1637
1751
|
|
1638
1752
|
c.desc 'Target section'
|
1639
1753
|
c.arg_name 'NAME'
|
1640
|
-
c.flag %i[s section]
|
1754
|
+
c.flag %i[s section]
|
1641
1755
|
|
1642
1756
|
c.desc 'Tag all imported entries'
|
1643
1757
|
c.arg_name 'TAGS'
|
1644
1758
|
c.flag :tag
|
1645
1759
|
|
1760
|
+
c.desc 'Autotag entries'
|
1761
|
+
c.switch :autotag, negatable: true, default_value: true
|
1762
|
+
|
1646
1763
|
c.desc 'Prefix entries with'
|
1647
1764
|
c.arg_name 'PREFIX'
|
1648
1765
|
c.flag :prefix
|
@@ -1652,11 +1769,22 @@ command :import do |c|
|
|
1652
1769
|
|
1653
1770
|
c.action do |_global_options, options, args|
|
1654
1771
|
|
1655
|
-
|
1772
|
+
if options[:section]
|
1773
|
+
section = wwid.guess_section(options[:section]) || options[:section].cap_first
|
1774
|
+
else
|
1775
|
+
section = wwid.config['current_section']
|
1776
|
+
end
|
1656
1777
|
|
1657
1778
|
if options[:type] =~ /^tim/i
|
1658
1779
|
args.each do |path|
|
1659
|
-
|
1780
|
+
options = {
|
1781
|
+
autotag: options[:autotag],
|
1782
|
+
no_overlap: !options[:overlap],
|
1783
|
+
prefix: options[:prefix],
|
1784
|
+
section: section,
|
1785
|
+
tag: options[:tag]
|
1786
|
+
}
|
1787
|
+
wwid.import_timing(path, options)
|
1660
1788
|
wwid.write(wwid.doing_file)
|
1661
1789
|
end
|
1662
1790
|
else
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid.rb
CHANGED
@@ -84,6 +84,7 @@ class WWID
|
|
84
84
|
##
|
85
85
|
def configure(opt = {})
|
86
86
|
@timers = {}
|
87
|
+
@recorded_items = []
|
87
88
|
opt[:ignore_local] ||= false
|
88
89
|
|
89
90
|
@config_file ||= File.join(@user_home, @default_config_file)
|
@@ -424,8 +425,8 @@ class WWID
|
|
424
425
|
## @param guessed (Boolean) already guessed and failed
|
425
426
|
##
|
426
427
|
def guess_section(frag, guessed: false)
|
427
|
-
return 'All' if frag =~
|
428
|
-
|
428
|
+
return 'All' if frag =~ /^all$/i
|
429
|
+
frag ||= @current_section
|
429
430
|
sections.each { |section| return section.cap_first if frag.downcase == section.downcase }
|
430
431
|
section = false
|
431
432
|
re = frag.split('').join('.*?')
|
@@ -583,17 +584,17 @@ class WWID
|
|
583
584
|
end
|
584
585
|
|
585
586
|
def same_time?(item_a, item_b)
|
586
|
-
item_a['date'] == item_b['date'] ? get_interval(item_a, false) == get_interval(item_b, false) : false
|
587
|
+
item_a['date'] == item_b['date'] ? get_interval(item_a, formatted: false, record: false) == get_interval(item_b, formatted: false, record: false) : false
|
587
588
|
end
|
588
589
|
|
589
590
|
def overlapping_time?(item_a, item_b)
|
590
591
|
return true if same_time?(item_a, item_b)
|
591
592
|
|
592
593
|
start_a = item_a['date']
|
593
|
-
interval = get_interval(item_a, false)
|
594
|
+
interval = get_interval(item_a, formatted: false, record: false)
|
594
595
|
end_a = interval ? start_a + interval.to_i : start_a
|
595
596
|
start_b = item_b['date']
|
596
|
-
interval = get_interval(item_b, false)
|
597
|
+
interval = get_interval(item_b, formatted: false, record: false)
|
597
598
|
end_b = interval ? start_b + interval.to_i : start_b
|
598
599
|
(start_a >= start_b && start_a <= end_b) || (end_a >= start_b && end_a <= end_b) || (start_a < start_b && end_a > end_b)
|
599
600
|
end
|
@@ -626,6 +627,7 @@ class WWID
|
|
626
627
|
def import_timing(path, opt = {})
|
627
628
|
section = opt[:section] || @current_section
|
628
629
|
opt[:no_overlap] ||= false
|
630
|
+
opt[:autotag] ||= @auto_tag
|
629
631
|
|
630
632
|
add_section(section) unless @content.has_key?(section)
|
631
633
|
|
@@ -656,7 +658,7 @@ class WWID
|
|
656
658
|
title += " @#{tag}"
|
657
659
|
end
|
658
660
|
end
|
659
|
-
title = autotag(title) if
|
661
|
+
title = autotag(title) if opt[:autotag]
|
660
662
|
title += " @done(#{end_time.strftime('%Y-%m-%d %H:%M')})"
|
661
663
|
title.gsub!(/ +/, ' ')
|
662
664
|
title.strip!
|
@@ -1098,7 +1100,6 @@ class WWID
|
|
1098
1100
|
items = @content[section]['items'].dup.sort_by { |item| item['date'] }.reverse
|
1099
1101
|
idx = 0
|
1100
1102
|
done_date = Time.now
|
1101
|
-
next_start = Time.now
|
1102
1103
|
count = (opt[:count]).zero? ? items.length : opt[:count]
|
1103
1104
|
items.map! do |item|
|
1104
1105
|
break if idx == count
|
@@ -1117,8 +1118,8 @@ class WWID
|
|
1117
1118
|
end
|
1118
1119
|
else
|
1119
1120
|
if opt[:sequential]
|
1120
|
-
|
1121
|
-
|
1121
|
+
next_entry = next_item(item)
|
1122
|
+
done_date = next_entry['date'] - 60 if next_entry
|
1122
1123
|
elsif opt[:took]
|
1123
1124
|
if item['date'] + opt[:took] > Time.now
|
1124
1125
|
item['date'] = Time.now - opt[:took]
|
@@ -1207,6 +1208,24 @@ class WWID
|
|
1207
1208
|
return new_item
|
1208
1209
|
end
|
1209
1210
|
|
1211
|
+
##
|
1212
|
+
## @brief Get next item in the index
|
1213
|
+
##
|
1214
|
+
## @param old_item
|
1215
|
+
##
|
1216
|
+
def next_item(old_item)
|
1217
|
+
section = old_item['section']
|
1218
|
+
|
1219
|
+
section_items = @content[section]['items'].sort_by { |entry| entry['date'] }
|
1220
|
+
idx = section_items.index(old_item)
|
1221
|
+
|
1222
|
+
if section_items.size > idx
|
1223
|
+
section_items[idx + 1]
|
1224
|
+
else
|
1225
|
+
nil
|
1226
|
+
end
|
1227
|
+
end
|
1228
|
+
|
1210
1229
|
##
|
1211
1230
|
## @brief Delete an item from the index
|
1212
1231
|
##
|
@@ -1476,7 +1495,7 @@ class WWID
|
|
1476
1495
|
##
|
1477
1496
|
## @param file (String) The filepath to write to
|
1478
1497
|
##
|
1479
|
-
def write(file = nil)
|
1498
|
+
def write(file = nil, backup: true)
|
1480
1499
|
output = @other_content_top ? "#{@other_content_top.join("\n")}\n" : ''
|
1481
1500
|
|
1482
1501
|
@content.each do |title, section|
|
@@ -1488,7 +1507,7 @@ class WWID
|
|
1488
1507
|
$stdout.puts output
|
1489
1508
|
else
|
1490
1509
|
file = File.expand_path(file)
|
1491
|
-
if File.exist?(file)
|
1510
|
+
if File.exist?(file) && backup
|
1492
1511
|
# Create a backup copy for the undo command
|
1493
1512
|
FileUtils.cp(file, "#{file}~")
|
1494
1513
|
end
|
@@ -1520,6 +1539,88 @@ class WWID
|
|
1520
1539
|
end
|
1521
1540
|
end
|
1522
1541
|
|
1542
|
+
##
|
1543
|
+
## @brief Rename doing file with date and start fresh one
|
1544
|
+
##
|
1545
|
+
def rotate(opt = {})
|
1546
|
+
count = opt[:keep] || 0
|
1547
|
+
tags = []
|
1548
|
+
tags.concat(opt[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if opt[:tag]
|
1549
|
+
bool = opt[:bool] || :and
|
1550
|
+
sect = opt[:section] !~ /^all$/i ? guess_section(opt[:section]) : 'all'
|
1551
|
+
|
1552
|
+
if sect =~ /^all$/i
|
1553
|
+
all_sections = sections.dup
|
1554
|
+
else
|
1555
|
+
all_sections = [sect]
|
1556
|
+
end
|
1557
|
+
|
1558
|
+
counter = 0
|
1559
|
+
new_content = {}
|
1560
|
+
|
1561
|
+
|
1562
|
+
all_sections.each do |section|
|
1563
|
+
items = @content[section]['items'].dup
|
1564
|
+
new_content[section] = {}
|
1565
|
+
new_content[section]['original'] = @content[section]['original']
|
1566
|
+
new_content[section]['items'] = []
|
1567
|
+
|
1568
|
+
moved_items = []
|
1569
|
+
if !tags.empty? || opt[:search] || opt[:before]
|
1570
|
+
if opt[:before]
|
1571
|
+
time_string = opt[:before]
|
1572
|
+
time_string += ' 12am' if time_string !~ /(\d+:\d+|\d+[ap])/
|
1573
|
+
cutoff = chronify(time_string)
|
1574
|
+
end
|
1575
|
+
|
1576
|
+
items.delete_if do |item|
|
1577
|
+
if ((!tags.empty? && item.has_tags?(tags, bool)) || (opt[:search] && item.matches_search?(opt[:search].to_s)) || (opt[:before] && item['date'] < cutoff))
|
1578
|
+
moved_items.push(item)
|
1579
|
+
counter += 1
|
1580
|
+
true
|
1581
|
+
else
|
1582
|
+
false
|
1583
|
+
end
|
1584
|
+
end
|
1585
|
+
@content[section]['items'] = items
|
1586
|
+
new_content[section]['items'] = moved_items
|
1587
|
+
@results.push("Rotated #{moved_items.length} items from #{section}")
|
1588
|
+
else
|
1589
|
+
new_content[section]['items'] = []
|
1590
|
+
moved_items = []
|
1591
|
+
|
1592
|
+
count = items.length if items.length < count
|
1593
|
+
|
1594
|
+
if items.count > count
|
1595
|
+
moved_items.concat(items[count..-1])
|
1596
|
+
else
|
1597
|
+
moved_items.concat(items)
|
1598
|
+
end
|
1599
|
+
|
1600
|
+
@content[section]['items'] = if count.zero?
|
1601
|
+
[]
|
1602
|
+
else
|
1603
|
+
items[0..count - 1]
|
1604
|
+
end
|
1605
|
+
new_content[section]['items'] = moved_items
|
1606
|
+
|
1607
|
+
@results.push("Rotated #{items.length - count} items from #{section}")
|
1608
|
+
end
|
1609
|
+
end
|
1610
|
+
|
1611
|
+
write(@doing_file)
|
1612
|
+
|
1613
|
+
file = @doing_file.sub(/(\.\w+)$/, "_#{Time.now.strftime('%Y-%m-%d')}\\1")
|
1614
|
+
if File.exist?(file)
|
1615
|
+
init_doing_file(file)
|
1616
|
+
@content.deep_merge(new_content)
|
1617
|
+
else
|
1618
|
+
@content = new_content
|
1619
|
+
end
|
1620
|
+
|
1621
|
+
write(file, backup: false)
|
1622
|
+
end
|
1623
|
+
|
1523
1624
|
##
|
1524
1625
|
## @brief Generate a menu of sections and allow user selection
|
1525
1626
|
##
|
@@ -1569,20 +1670,21 @@ class WWID
|
|
1569
1670
|
def list_section(opt = {})
|
1570
1671
|
opt[:count] ||= 0
|
1571
1672
|
count = opt[:count] - 1
|
1572
|
-
opt[:section] ||= nil
|
1573
|
-
opt[:format] ||= @default_date_format
|
1574
|
-
opt[:template] ||= @default_template
|
1575
1673
|
opt[:age] ||= 'newest'
|
1674
|
+
opt[:date_filter] ||= []
|
1675
|
+
opt[:format] ||= @default_date_format
|
1676
|
+
opt[:only_timed] ||= false
|
1576
1677
|
opt[:order] ||= 'desc'
|
1577
|
-
opt[:
|
1678
|
+
opt[:search] ||= false
|
1679
|
+
opt[:section] ||= nil
|
1680
|
+
opt[:sort_tags] ||= false
|
1578
1681
|
opt[:tag_filter] ||= false
|
1682
|
+
opt[:tag_order] ||= 'asc'
|
1579
1683
|
opt[:tags_color] ||= false
|
1684
|
+
opt[:template] ||= @default_template
|
1580
1685
|
opt[:times] ||= false
|
1686
|
+
opt[:today] ||= false
|
1581
1687
|
opt[:totals] ||= false
|
1582
|
-
opt[:sort_tags] ||= false
|
1583
|
-
opt[:search] ||= false
|
1584
|
-
opt[:only_timed] ||= false
|
1585
|
-
opt[:date_filter] ||= []
|
1586
1688
|
|
1587
1689
|
# opt[:highlight] ||= true
|
1588
1690
|
section = ''
|
@@ -1635,7 +1737,7 @@ class WWID
|
|
1635
1737
|
|
1636
1738
|
if opt[:only_timed]
|
1637
1739
|
items.delete_if do |item|
|
1638
|
-
get_interval(item) == false
|
1740
|
+
get_interval(item, record: false) == false
|
1639
1741
|
end
|
1640
1742
|
end
|
1641
1743
|
|
@@ -1670,7 +1772,7 @@ class WWID
|
|
1670
1772
|
arr = i['note'].map { |line| line.strip }.delete_if { |e| e =~ /^\s*$/ }
|
1671
1773
|
note = arr.join("\n") unless arr.nil?
|
1672
1774
|
end
|
1673
|
-
interval = get_interval(i, false) if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1775
|
+
interval = get_interval(i, formatted: false) if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1674
1776
|
interval ||= 0
|
1675
1777
|
output.push(CSV.generate_line([i['date'], i['title'], note, interval, i['section']]))
|
1676
1778
|
end
|
@@ -1689,7 +1791,7 @@ class WWID
|
|
1689
1791
|
end
|
1690
1792
|
if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1691
1793
|
end_date = Time.parse(Regexp.last_match(1))
|
1692
|
-
interval = get_interval(i, false)
|
1794
|
+
interval = get_interval(i, formatted: false)
|
1693
1795
|
end
|
1694
1796
|
end_date ||= ''
|
1695
1797
|
interval ||= 0
|
@@ -1738,7 +1840,7 @@ class WWID
|
|
1738
1840
|
out = {
|
1739
1841
|
'section' => section,
|
1740
1842
|
'items' => items_out,
|
1741
|
-
'timers' => tag_times('json', opt[:sort_tags])
|
1843
|
+
'timers' => tag_times(format: 'json', sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order])
|
1742
1844
|
}.to_json
|
1743
1845
|
elsif opt[:output] == 'timeline'
|
1744
1846
|
template = <<~EOTEMPLATE
|
@@ -1819,7 +1921,7 @@ class WWID
|
|
1819
1921
|
css_template
|
1820
1922
|
end
|
1821
1923
|
|
1822
|
-
totals = opt[:totals] ? tag_times('html', opt[:sort_tags]) : ''
|
1924
|
+
totals = opt[:totals] ? tag_times(format: 'html', sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
1823
1925
|
engine = Haml::Engine.new(template)
|
1824
1926
|
out = engine.render(Object.new,
|
1825
1927
|
{ :@items => items_out, :@page_title => page_title, :@style => style, :@totals => totals })
|
@@ -1860,7 +1962,7 @@ class WWID
|
|
1860
1962
|
|
1861
1963
|
output.sub!(/%date/, item['date'].strftime(opt[:format]))
|
1862
1964
|
|
1863
|
-
interval = get_interval(item) if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1965
|
+
interval = get_interval(item, record: true) if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1864
1966
|
interval ||= ''
|
1865
1967
|
output.sub!(/%interval/, interval)
|
1866
1968
|
|
@@ -1910,7 +2012,8 @@ class WWID
|
|
1910
2012
|
|
1911
2013
|
out += "#{output}\n"
|
1912
2014
|
end
|
1913
|
-
|
2015
|
+
|
2016
|
+
out += tag_times(format: 'text', sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) if opt[:totals]
|
1914
2017
|
end
|
1915
2018
|
out
|
1916
2019
|
end
|
@@ -1922,7 +2025,7 @@ class WWID
|
|
1922
2025
|
## @param section (String) The source section
|
1923
2026
|
## @param options (Hash) Options
|
1924
2027
|
##
|
1925
|
-
def archive(section =
|
2028
|
+
def archive(section = @current_section, options = {})
|
1926
2029
|
count = options[:keep] || 0
|
1927
2030
|
destination = options[:destination] || 'Archive'
|
1928
2031
|
tags = options[:tags] || []
|
@@ -1937,7 +2040,7 @@ class WWID
|
|
1937
2040
|
destination = guess_section(destination)
|
1938
2041
|
|
1939
2042
|
if sections.include?(destination) && (sections.include?(section) || archive_all)
|
1940
|
-
do_archive(section, destination, { count: count, tags: tags, bool: bool, search: options[:search], label: options[:label] })
|
2043
|
+
do_archive(section, destination, { count: count, tags: tags, bool: bool, search: options[:search], label: options[:label], before: options[:before] })
|
1941
2044
|
write(doing_file)
|
1942
2045
|
else
|
1943
2046
|
exit_now! 'Either source or destination does not exist'
|
@@ -1970,9 +2073,15 @@ class WWID
|
|
1970
2073
|
items = @content[section]['items'].dup
|
1971
2074
|
|
1972
2075
|
moved_items = []
|
1973
|
-
if !tags.empty? || opt[:search]
|
2076
|
+
if !tags.empty? || opt[:search] || opt[:before]
|
2077
|
+
if opt[:before]
|
2078
|
+
time_string = opt[:before]
|
2079
|
+
time_string += ' 12am' if time_string !~ /(\d+:\d+|\d+[ap])/
|
2080
|
+
cutoff = chronify(time_string)
|
2081
|
+
end
|
2082
|
+
|
1974
2083
|
items.delete_if do |item|
|
1975
|
-
if (!tags.empty? && item.has_tags?(tags, bool) || (opt[:search] && item.matches_search?(opt[:search].to_s)))
|
2084
|
+
if ((!tags.empty? && item.has_tags?(tags, bool)) || (opt[:search] && item.matches_search?(opt[:search].to_s)) || (opt[:before] && item['date'] < cutoff))
|
1976
2085
|
moved_items.push(item)
|
1977
2086
|
counter += 1
|
1978
2087
|
true
|
@@ -1981,7 +2090,7 @@ class WWID
|
|
1981
2090
|
end
|
1982
2091
|
end
|
1983
2092
|
moved_items.each do |item|
|
1984
|
-
if label && section !=
|
2093
|
+
if label && section != @current_section
|
1985
2094
|
item['title'] =
|
1986
2095
|
item['title'].sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{section})")
|
1987
2096
|
end
|
@@ -1994,7 +2103,7 @@ class WWID
|
|
1994
2103
|
count = items.length if items.length < count
|
1995
2104
|
|
1996
2105
|
items.map! do |item|
|
1997
|
-
if label && section !=
|
2106
|
+
if label && section != @current_section
|
1998
2107
|
item['title'] =
|
1999
2108
|
item['title'].sub(/(?:@from\(.*?\))?(.*)$/, "\\1 @from(#{section})")
|
2000
2109
|
end
|
@@ -2173,11 +2282,15 @@ class WWID
|
|
2173
2282
|
end
|
2174
2283
|
|
2175
2284
|
##
|
2176
|
-
## @brief Get total elapsed time for all tags in
|
2285
|
+
## @brief Get total elapsed time for all tags in
|
2286
|
+
## selection
|
2177
2287
|
##
|
2178
|
-
## @param format
|
2288
|
+
## @param format (String) return format (html,
|
2289
|
+
## json, or text)
|
2290
|
+
## @param sort_by_name (Boolean) Sort by name if true, otherwise by time
|
2291
|
+
## @param sort_order (String) The sort order (asc or desc)
|
2179
2292
|
##
|
2180
|
-
def tag_times(format
|
2293
|
+
def tag_times(format: 'text', sort_by_name: false, sort_order: 'asc')
|
2181
2294
|
return '' if @timers.empty?
|
2182
2295
|
|
2183
2296
|
max = @timers.keys.sort_by { |k| k.length }.reverse[0].length + 1
|
@@ -2186,11 +2299,13 @@ class WWID
|
|
2186
2299
|
|
2187
2300
|
tags_data = @timers.delete_if { |_k, v| v == 0 }
|
2188
2301
|
sorted_tags_data = if sort_by_name
|
2189
|
-
tags_data.sort_by { |k, _v| k }
|
2302
|
+
tags_data.sort_by { |k, _v| k }
|
2190
2303
|
else
|
2191
2304
|
tags_data.sort_by { |_k, v| v }
|
2192
2305
|
end
|
2193
2306
|
|
2307
|
+
sorted_tags_data.reverse! if sort_order =~ /^asc/i
|
2308
|
+
|
2194
2309
|
if format == 'html'
|
2195
2310
|
output = <<EOS
|
2196
2311
|
<table>
|
@@ -2324,19 +2439,20 @@ EOS
|
|
2324
2439
|
## @param item (Hash) The entry
|
2325
2440
|
## @param formatted (Bool) Return human readable time (default seconds)
|
2326
2441
|
##
|
2327
|
-
def get_interval(item, formatted
|
2442
|
+
def get_interval(item, formatted: true, record: true)
|
2328
2443
|
done = nil
|
2329
2444
|
start = nil
|
2330
2445
|
|
2331
2446
|
if @interval_cache.keys.include? item['title']
|
2332
2447
|
seconds = @interval_cache[item['title']]
|
2448
|
+
record_tag_times(item, seconds) if record
|
2333
2449
|
return seconds > 0 ? '%02d:%02d:%02d' % fmt_time(seconds) : false
|
2334
2450
|
end
|
2335
2451
|
|
2336
2452
|
if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/
|
2337
2453
|
done = Time.parse(Regexp.last_match(1))
|
2338
2454
|
else
|
2339
|
-
return
|
2455
|
+
return false
|
2340
2456
|
end
|
2341
2457
|
|
2342
2458
|
start = if item['title'] =~ /@start\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/
|
@@ -2347,20 +2463,34 @@ EOS
|
|
2347
2463
|
|
2348
2464
|
seconds = (done - start).to_i
|
2349
2465
|
|
2466
|
+
if record
|
2467
|
+
record_tag_times(item, seconds)
|
2468
|
+
end
|
2469
|
+
|
2470
|
+
@interval_cache[item['title']] = seconds
|
2471
|
+
|
2472
|
+
return seconds > 0 ? seconds : false unless formatted
|
2473
|
+
|
2474
|
+
seconds > 0 ? '%02d:%02d:%02d' % fmt_time(seconds) : false
|
2475
|
+
end
|
2476
|
+
|
2477
|
+
##
|
2478
|
+
## @brief Record times for item tags
|
2479
|
+
##
|
2480
|
+
## @param item The item
|
2481
|
+
##
|
2482
|
+
def record_tag_times(item, seconds)
|
2483
|
+
return if @recorded_items.include?(item)
|
2484
|
+
|
2350
2485
|
item['title'].scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
|
2351
2486
|
k = m[0] == 'done' ? 'All' : m[0].downcase
|
2352
|
-
if @timers.
|
2487
|
+
if @timers.key?(k)
|
2353
2488
|
@timers[k] += seconds
|
2354
2489
|
else
|
2355
2490
|
@timers[k] = seconds
|
2356
2491
|
end
|
2492
|
+
@recorded_items.push(item)
|
2357
2493
|
end
|
2358
|
-
|
2359
|
-
@interval_cache[item['title']] = seconds
|
2360
|
-
|
2361
|
-
return seconds unless formatted
|
2362
|
-
|
2363
|
-
seconds > 0 ? '%02d:%02d:%02d' % fmt_time(seconds) : false
|
2364
2494
|
end
|
2365
2495
|
|
2366
2496
|
##
|
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: 1.0.
|
4
|
+
version: 1.0.85
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brett Terpstra
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|