doing 1.0.79 → 1.0.83
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 +40 -11
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +148 -117
- data/lib/doing.rb +3 -2
- 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: fb85f27f305f246d4ab182268c9e5188da67f22fde46d3ee4ae716996c78b2c3
|
4
|
+
data.tar.gz: 7b302748d9837511adc77679fd619c6daf85b2f7e15f541d530c8cc211e0aac3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbf5e04626a97e5287c2417cf5afffd6516860a1fa5f3ed64e93fe15bdc62b85f045d0a54adc08c774b4d711add29f02799cf3d4a2c22853e1273a691d0a9f00
|
7
|
+
data.tar.gz: 87bf6eac2e770b161c8ae4fd2fee4adea5ed261b828f2142997858393a627e0d36788981fe52a4c66486f442da2de349ce1ac157732d824b0ef633e4dcbdc703
|
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.82<!--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
@@ -1,4 +1,4 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
1
|
+
#!/usr/bin/env ruby -W1
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
@@ -301,6 +301,9 @@ command :select do |c|
|
|
301
301
|
c.desc 'Add flag to selected item(s)'
|
302
302
|
c.switch %i[flag], negatable: false, default_value: false
|
303
303
|
|
304
|
+
c.desc 'Perform action without confirmation'
|
305
|
+
c.switch %i[force], negatable: false, default_value: false
|
306
|
+
|
304
307
|
c.desc 'Save selected entries to file using --output format'
|
305
308
|
c.arg_name 'FILE'
|
306
309
|
c.flag %i[save_to]
|
@@ -1306,7 +1309,8 @@ desc 'Select a section to display from a menu'
|
|
1306
1309
|
command :choose do |c|
|
1307
1310
|
c.action do |_global_options, _options, _args|
|
1308
1311
|
section = wwid.choose_section
|
1309
|
-
|
1312
|
+
|
1313
|
+
puts wwid.list_section({ section: section.cap_first, count: 0 }) if section
|
1310
1314
|
end
|
1311
1315
|
end
|
1312
1316
|
|
@@ -1340,13 +1344,14 @@ command :colors do |c|
|
|
1340
1344
|
end
|
1341
1345
|
|
1342
1346
|
desc 'Display a user-created view'
|
1347
|
+
long_desc 'Command line options override associated view settings'
|
1343
1348
|
arg_name 'VIEW_NAME'
|
1344
1349
|
command :view do |c|
|
1345
|
-
c.desc 'Section
|
1350
|
+
c.desc 'Section'
|
1346
1351
|
c.arg_name 'NAME'
|
1347
1352
|
c.flag %i[s section]
|
1348
1353
|
|
1349
|
-
c.desc 'Count to display
|
1354
|
+
c.desc 'Count to display'
|
1350
1355
|
c.arg_name 'COUNT'
|
1351
1356
|
c.flag %i[c count], must_match: /^\d+$/, type: Integer
|
1352
1357
|
|
@@ -1364,12 +1369,14 @@ command :view do |c|
|
|
1364
1369
|
c.switch [:color], default_value: true, negatable: true
|
1365
1370
|
|
1366
1371
|
c.desc 'Sort tags by (name|time)'
|
1367
|
-
default = 'time'
|
1368
|
-
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1369
1372
|
c.arg_name 'KEY'
|
1370
|
-
c.flag [:tag_sort], must_match: /^(?:name|time)$/i
|
1373
|
+
c.flag [:tag_sort], must_match: /^(?:name|time)$/i
|
1371
1374
|
|
1372
|
-
c.desc '
|
1375
|
+
c.desc 'Tag sort direction (asc|desc)'
|
1376
|
+
c.arg_name 'DIRECTION'
|
1377
|
+
c.flag [:tag_order], must_match: /^(?:a(?:sc)?|d(?:esc)?)$/i
|
1378
|
+
|
1379
|
+
c.desc 'Only show items with recorded time intervals (override view settings)'
|
1373
1380
|
c.switch [:only_timed], default_value: false, negatable: false
|
1374
1381
|
|
1375
1382
|
c.action do |_global_options, options, args|
|
@@ -1418,10 +1425,31 @@ command :view do |c|
|
|
1418
1425
|
end
|
1419
1426
|
order = view.key?('order') ? view['order'] : 'asc'
|
1420
1427
|
|
1421
|
-
|
1428
|
+
totals = if options[:totals]
|
1429
|
+
true
|
1430
|
+
else
|
1431
|
+
view.key?('totals') ? view['totals'] : false
|
1432
|
+
end
|
1433
|
+
|
1434
|
+
options[:t] = true if totals
|
1422
1435
|
options[:output]&.downcase!
|
1423
|
-
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1424
1436
|
|
1437
|
+
options[:sort_tags] = if options[:tag_sort]
|
1438
|
+
options[:tag_sort] =~ /^n/i ? true : false
|
1439
|
+
elsif view.key?('tag_sort')
|
1440
|
+
view['tag_sort'] =~ /^n/i ? true : false
|
1441
|
+
else
|
1442
|
+
false
|
1443
|
+
end
|
1444
|
+
|
1445
|
+
tag_order = if options[:tag_order]
|
1446
|
+
options[:tag_order] =~ /^d/i ? 'desc' : 'asc'
|
1447
|
+
elsif view.key?('tag_order')
|
1448
|
+
view['tag_order'] =~ /^d/i ? 'desc' : 'asc'
|
1449
|
+
else
|
1450
|
+
'asc'
|
1451
|
+
end
|
1452
|
+
warn "TAG ORDER: #{options[:tag_order]}"
|
1425
1453
|
opts = {
|
1426
1454
|
count: count,
|
1427
1455
|
format: format,
|
@@ -1432,10 +1460,11 @@ command :view do |c|
|
|
1432
1460
|
section: section,
|
1433
1461
|
sort_tags: options[:sort_tags],
|
1434
1462
|
tag_filter: tag_filter,
|
1463
|
+
tag_order: tag_order,
|
1435
1464
|
tags_color: tags_color,
|
1436
1465
|
template: template,
|
1437
1466
|
times: options[:t],
|
1438
|
-
totals:
|
1467
|
+
totals: totals
|
1439
1468
|
}
|
1440
1469
|
|
1441
1470
|
puts wwid.list_section(opts)
|
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)
|
@@ -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
|
@@ -757,7 +758,7 @@ class WWID
|
|
757
758
|
all_items.concat(@content[section]['items'].dup) if @content.key?(section)
|
758
759
|
end
|
759
760
|
|
760
|
-
if opt[:tag]
|
761
|
+
if opt[:tag]&.length
|
761
762
|
all_items.select! { |item| item.has_tags?(opt[:tag], opt[:tag_bool]) }
|
762
763
|
elsif opt[:search]&.length
|
763
764
|
all_items.select! { |item| item.matches_search?(opt[:search]) }
|
@@ -771,16 +772,17 @@ class WWID
|
|
771
772
|
##
|
772
773
|
## @return (String) The selected option
|
773
774
|
##
|
774
|
-
def choose_from(options, prompt)
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
775
|
+
def choose_from(options, prompt: 'Make a selection: ', multiple: false, fzf_args: [])
|
776
|
+
fzf = File.join(File.dirname(__FILE__), '../helpers/fuzzyfilefinder')
|
777
|
+
fzf_args << '-1'
|
778
|
+
fzf_args << %(--prompt "#{prompt}")
|
779
|
+
fzf_args << '--multi' if multiple
|
780
|
+
header = "esc: cancel,#{multiple ? ' tab: multi-select, ctrl-a: select all,' : ''} return: confirm"
|
781
|
+
fzf_args << %(--header "#{header}")
|
782
|
+
res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} #{fzf_args.join(' ')}`
|
783
|
+
return false if res.strip.size.zero?
|
784
|
+
|
785
|
+
res
|
784
786
|
end
|
785
787
|
|
786
788
|
##
|
@@ -811,7 +813,7 @@ class WWID
|
|
811
813
|
') ',
|
812
814
|
item['date'],
|
813
815
|
' | ',
|
814
|
-
item['title']
|
816
|
+
item['title']
|
815
817
|
]
|
816
818
|
if opt[:section] =~ /^all/i
|
817
819
|
out.concat([
|
@@ -822,8 +824,15 @@ class WWID
|
|
822
824
|
end
|
823
825
|
out.join('')
|
824
826
|
end
|
825
|
-
|
826
|
-
|
827
|
+
fzf_args = [
|
828
|
+
%(--header="Arrows: navigate, tab: mark for selection, ctrl-a: select all, enter: commit"),
|
829
|
+
%(--prompt="Select entries to act on > "),
|
830
|
+
'-1',
|
831
|
+
'-m',
|
832
|
+
'--bind ctrl-a:select-all',
|
833
|
+
%(-q "#{opt[:query]}")
|
834
|
+
]
|
835
|
+
res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} #{fzf_args.join(' ')}`
|
827
836
|
selected = []
|
828
837
|
res.split(/\n/).each do |item|
|
829
838
|
idx = item.match(/^(\d+)\)/)[1].to_i
|
@@ -845,62 +854,71 @@ class WWID
|
|
845
854
|
end
|
846
855
|
|
847
856
|
unless has_action
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
852
|
-
|
853
|
-
|
854
|
-
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
opt[:
|
857
|
+
choice = choose_from([
|
858
|
+
'add tag',
|
859
|
+
'remove tag',
|
860
|
+
'cancel',
|
861
|
+
'delete',
|
862
|
+
'finish',
|
863
|
+
'flag',
|
864
|
+
'archive',
|
865
|
+
'move',
|
866
|
+
'edit',
|
867
|
+
'output formatted'
|
868
|
+
],
|
869
|
+
prompt: 'What do you want to do with the selected items? > ',
|
870
|
+
multiple: true,
|
871
|
+
fzf_args: ['--height=60%', '--tac', '--no-sort'])
|
872
|
+
return unless choice
|
873
|
+
|
874
|
+
to_do = choice.strip.split(/\n/)
|
875
|
+
to_do.each do |action|
|
876
|
+
case action
|
877
|
+
when /(add|remove) tag/
|
878
|
+
type = action =~ /^add/ ? 'add' : 'remove'
|
879
|
+
if opt[:tag]
|
880
|
+
warn "'add tag' and 'remove tag' can not be used together"
|
881
|
+
Process.exit 1
|
882
|
+
end
|
883
|
+
print "#{colors['yellow']}Tag to #{type}: #{colors['reset']}"
|
884
|
+
tag = STDIN.gets
|
885
|
+
return if tag =~ /^ *$/
|
886
|
+
opt[:tag] = tag.strip.sub(/^@/, '')
|
887
|
+
opt[:remove] = true if type == 'remove'
|
888
|
+
when /output formatted/
|
889
|
+
output_format = choose_from(%w[doing taskpaper json timeline html csv].sort, prompt: 'Which output format? > ', fzf_args: ['--height=60%', '--tac', '--no-sort'])
|
890
|
+
return if tag =~ /^ *$/
|
891
|
+
opt[:output] = output_format.strip
|
892
|
+
res = opt[:force] ? false : yn('Save to file?', default_response: 'n')
|
893
|
+
if res
|
894
|
+
print "#{colors['yellow']}File path/name: #{colors['reset']}"
|
895
|
+
filename = STDIN.gets.strip
|
896
|
+
return if filename.empty?
|
897
|
+
opt[:save_to] = filename
|
898
|
+
end
|
899
|
+
when /archive/
|
900
|
+
opt[:archive] = true
|
901
|
+
when /delete/
|
902
|
+
opt[:delete] = true
|
903
|
+
when /edit/
|
904
|
+
opt[:editor] = true
|
905
|
+
when /finish/
|
906
|
+
opt[:finish] = true
|
907
|
+
when /cancel/
|
908
|
+
opt[:cancel] = true
|
909
|
+
when /move/
|
910
|
+
section = choose_section.strip
|
911
|
+
opt[:move] = section.strip unless section =~ /^ *$/
|
912
|
+
when /flag/
|
913
|
+
opt[:flag] = true
|
879
914
|
end
|
880
|
-
when /archive/
|
881
|
-
opt[:archive] = true
|
882
|
-
when /delete/
|
883
|
-
opt[:delete] = true
|
884
|
-
when /edit/
|
885
|
-
opt[:editor] = true
|
886
|
-
when /finish/
|
887
|
-
opt[:finish] = true
|
888
|
-
when /cancel/
|
889
|
-
opt[:cancel] = true
|
890
|
-
when /move/
|
891
|
-
section = choose_section.strip
|
892
|
-
return if section =~ /^ *$/
|
893
|
-
opt[:move] = section.strip
|
894
|
-
when /flag/
|
895
|
-
opt[:flag] = true
|
896
915
|
end
|
897
916
|
end
|
898
917
|
|
899
|
-
|
900
918
|
if opt[:delete]
|
901
|
-
res = yn("Delete #{selected.size} items?", default_response: 'y')
|
919
|
+
res = opt[:force] ? true : yn("Delete #{selected.size} items?", default_response: 'y')
|
902
920
|
if res
|
903
|
-
selected.each {|item| delete_item(item) }
|
921
|
+
selected.each { |item| delete_item(item) }
|
904
922
|
write(@doing_file)
|
905
923
|
end
|
906
924
|
return
|
@@ -994,13 +1012,16 @@ class WWID
|
|
994
1012
|
item
|
995
1013
|
end
|
996
1014
|
|
997
|
-
@content = {'Export' => {'original' => 'Export:', 'items' => selected}}
|
998
|
-
options = {section: 'Export'}
|
1015
|
+
@content = { 'Export' => { 'original' => 'Export:', 'items' => selected } }
|
1016
|
+
options = { section: 'Export' }
|
999
1017
|
|
1000
|
-
|
1001
|
-
|
1002
|
-
else
|
1018
|
+
case opt[:output]
|
1019
|
+
when /doing/
|
1003
1020
|
options[:template] = '- %date | %title%note'
|
1021
|
+
when /taskpaper/
|
1022
|
+
options[:template] = '- %title @date(%date)%note'
|
1023
|
+
else
|
1024
|
+
options[:output] = opt[:output]
|
1004
1025
|
end
|
1005
1026
|
|
1006
1027
|
output = list_section(options)
|
@@ -1082,7 +1103,6 @@ class WWID
|
|
1082
1103
|
count = (opt[:count]).zero? ? items.length : opt[:count]
|
1083
1104
|
items.map! do |item|
|
1084
1105
|
break if idx == count
|
1085
|
-
|
1086
1106
|
finished = opt[:unfinished] && item.has_tags?('done', :and)
|
1087
1107
|
tag_match = opt[:tag].nil? || opt[:tag].empty? ? true : item.has_tags?(opt[:tag], opt[:tag_bool])
|
1088
1108
|
search_match = opt[:search].nil? || opt[:search].empty? ? true : item.matches_search?(opt[:search])
|
@@ -1507,14 +1527,8 @@ class WWID
|
|
1507
1527
|
## @return (String) The selected section name
|
1508
1528
|
##
|
1509
1529
|
def choose_section
|
1510
|
-
sections.
|
1511
|
-
|
1512
|
-
end
|
1513
|
-
print "#{colors['green']}> #{colors['default']}"
|
1514
|
-
num = STDIN.gets
|
1515
|
-
return false if num =~ /^[a-z ]*$/i
|
1516
|
-
|
1517
|
-
sections[num.to_i - 1]
|
1530
|
+
choice = choose_from(sections.sort, prompt: 'Choose a section > ', fzf_args: ['--height=60%'])
|
1531
|
+
choice ? choice.strip : choice
|
1518
1532
|
end
|
1519
1533
|
|
1520
1534
|
##
|
@@ -1532,14 +1546,8 @@ class WWID
|
|
1532
1546
|
## @return (String) The selected view name
|
1533
1547
|
##
|
1534
1548
|
def choose_view
|
1535
|
-
views.
|
1536
|
-
|
1537
|
-
end
|
1538
|
-
print '> '
|
1539
|
-
num = STDIN.gets
|
1540
|
-
return false if num =~ /^[a-z ]*$/i
|
1541
|
-
|
1542
|
-
views[num.to_i - 1]
|
1549
|
+
choice = choose_from(views.sort, prompt: 'Choose a view > ', fzf_args: ['--height=60%'])
|
1550
|
+
choice ? choice.strip : choice
|
1543
1551
|
end
|
1544
1552
|
|
1545
1553
|
##
|
@@ -1562,20 +1570,21 @@ class WWID
|
|
1562
1570
|
def list_section(opt = {})
|
1563
1571
|
opt[:count] ||= 0
|
1564
1572
|
count = opt[:count] - 1
|
1565
|
-
opt[:section] ||= nil
|
1566
|
-
opt[:format] ||= @default_date_format
|
1567
|
-
opt[:template] ||= @default_template
|
1568
1573
|
opt[:age] ||= 'newest'
|
1574
|
+
opt[:date_filter] ||= []
|
1575
|
+
opt[:format] ||= @default_date_format
|
1576
|
+
opt[:only_timed] ||= false
|
1569
1577
|
opt[:order] ||= 'desc'
|
1570
|
-
opt[:
|
1578
|
+
opt[:search] ||= false
|
1579
|
+
opt[:section] ||= nil
|
1580
|
+
opt[:sort_tags] ||= false
|
1571
1581
|
opt[:tag_filter] ||= false
|
1582
|
+
opt[:tag_order] ||= 'asc'
|
1572
1583
|
opt[:tags_color] ||= false
|
1584
|
+
opt[:template] ||= @default_template
|
1573
1585
|
opt[:times] ||= false
|
1586
|
+
opt[:today] ||= false
|
1574
1587
|
opt[:totals] ||= false
|
1575
|
-
opt[:sort_tags] ||= false
|
1576
|
-
opt[:search] ||= false
|
1577
|
-
opt[:only_timed] ||= false
|
1578
|
-
opt[:date_filter] ||= []
|
1579
1588
|
|
1580
1589
|
# opt[:highlight] ||= true
|
1581
1590
|
section = ''
|
@@ -1628,7 +1637,7 @@ class WWID
|
|
1628
1637
|
|
1629
1638
|
if opt[:only_timed]
|
1630
1639
|
items.delete_if do |item|
|
1631
|
-
get_interval(item) == false
|
1640
|
+
get_interval(item, record: false) == false
|
1632
1641
|
end
|
1633
1642
|
end
|
1634
1643
|
|
@@ -1663,7 +1672,7 @@ class WWID
|
|
1663
1672
|
arr = i['note'].map { |line| line.strip }.delete_if { |e| e =~ /^\s*$/ }
|
1664
1673
|
note = arr.join("\n") unless arr.nil?
|
1665
1674
|
end
|
1666
|
-
interval = get_interval(i, false) if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1675
|
+
interval = get_interval(i, formatted: false) if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1667
1676
|
interval ||= 0
|
1668
1677
|
output.push(CSV.generate_line([i['date'], i['title'], note, interval, i['section']]))
|
1669
1678
|
end
|
@@ -1682,7 +1691,7 @@ class WWID
|
|
1682
1691
|
end
|
1683
1692
|
if i['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1684
1693
|
end_date = Time.parse(Regexp.last_match(1))
|
1685
|
-
interval = get_interval(i, false)
|
1694
|
+
interval = get_interval(i, formatted: false)
|
1686
1695
|
end
|
1687
1696
|
end_date ||= ''
|
1688
1697
|
interval ||= 0
|
@@ -1731,7 +1740,7 @@ class WWID
|
|
1731
1740
|
out = {
|
1732
1741
|
'section' => section,
|
1733
1742
|
'items' => items_out,
|
1734
|
-
'timers' => tag_times('json', opt[:sort_tags])
|
1743
|
+
'timers' => tag_times(format: 'json', sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order])
|
1735
1744
|
}.to_json
|
1736
1745
|
elsif opt[:output] == 'timeline'
|
1737
1746
|
template = <<~EOTEMPLATE
|
@@ -1812,7 +1821,7 @@ class WWID
|
|
1812
1821
|
css_template
|
1813
1822
|
end
|
1814
1823
|
|
1815
|
-
totals = opt[:totals] ? tag_times('html', opt[:sort_tags]) : ''
|
1824
|
+
totals = opt[:totals] ? tag_times(format: 'html', sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
1816
1825
|
engine = Haml::Engine.new(template)
|
1817
1826
|
out = engine.render(Object.new,
|
1818
1827
|
{ :@items => items_out, :@page_title => page_title, :@style => style, :@totals => totals })
|
@@ -1853,7 +1862,7 @@ class WWID
|
|
1853
1862
|
|
1854
1863
|
output.sub!(/%date/, item['date'].strftime(opt[:format]))
|
1855
1864
|
|
1856
|
-
interval = get_interval(item) if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1865
|
+
interval = get_interval(item, record: true) if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/ && opt[:times]
|
1857
1866
|
interval ||= ''
|
1858
1867
|
output.sub!(/%interval/, interval)
|
1859
1868
|
|
@@ -1903,7 +1912,8 @@ class WWID
|
|
1903
1912
|
|
1904
1913
|
out += "#{output}\n"
|
1905
1914
|
end
|
1906
|
-
|
1915
|
+
|
1916
|
+
out += tag_times(format: 'text', sort_by_name: opt[:sort_tags], sort_order: opt[:tag_order]) if opt[:totals]
|
1907
1917
|
end
|
1908
1918
|
out
|
1909
1919
|
end
|
@@ -2166,11 +2176,15 @@ class WWID
|
|
2166
2176
|
end
|
2167
2177
|
|
2168
2178
|
##
|
2169
|
-
## @brief Get total elapsed time for all tags in
|
2179
|
+
## @brief Get total elapsed time for all tags in
|
2180
|
+
## selection
|
2170
2181
|
##
|
2171
|
-
## @param format
|
2182
|
+
## @param format (String) return format (html,
|
2183
|
+
## json, or text)
|
2184
|
+
## @param sort_by_name (Boolean) Sort by name if true, otherwise by time
|
2185
|
+
## @param sort_order (String) The sort order (asc or desc)
|
2172
2186
|
##
|
2173
|
-
def tag_times(format
|
2187
|
+
def tag_times(format: 'text', sort_by_name: false, sort_order: 'asc')
|
2174
2188
|
return '' if @timers.empty?
|
2175
2189
|
|
2176
2190
|
max = @timers.keys.sort_by { |k| k.length }.reverse[0].length + 1
|
@@ -2179,11 +2193,13 @@ class WWID
|
|
2179
2193
|
|
2180
2194
|
tags_data = @timers.delete_if { |_k, v| v == 0 }
|
2181
2195
|
sorted_tags_data = if sort_by_name
|
2182
|
-
tags_data.sort_by { |k, _v| k }
|
2196
|
+
tags_data.sort_by { |k, _v| k }
|
2183
2197
|
else
|
2184
2198
|
tags_data.sort_by { |_k, v| v }
|
2185
2199
|
end
|
2186
2200
|
|
2201
|
+
sorted_tags_data.reverse! if sort_order =~ /^asc/i
|
2202
|
+
|
2187
2203
|
if format == 'html'
|
2188
2204
|
output = <<EOS
|
2189
2205
|
<table>
|
@@ -2317,19 +2333,20 @@ EOS
|
|
2317
2333
|
## @param item (Hash) The entry
|
2318
2334
|
## @param formatted (Bool) Return human readable time (default seconds)
|
2319
2335
|
##
|
2320
|
-
def get_interval(item, formatted
|
2336
|
+
def get_interval(item, formatted: true, record: true)
|
2321
2337
|
done = nil
|
2322
2338
|
start = nil
|
2323
2339
|
|
2324
2340
|
if @interval_cache.keys.include? item['title']
|
2325
2341
|
seconds = @interval_cache[item['title']]
|
2342
|
+
record_tag_times(item, seconds) if record
|
2326
2343
|
return seconds > 0 ? '%02d:%02d:%02d' % fmt_time(seconds) : false
|
2327
2344
|
end
|
2328
2345
|
|
2329
2346
|
if item['title'] =~ /@done\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/
|
2330
2347
|
done = Time.parse(Regexp.last_match(1))
|
2331
2348
|
else
|
2332
|
-
return
|
2349
|
+
return false
|
2333
2350
|
end
|
2334
2351
|
|
2335
2352
|
start = if item['title'] =~ /@start\((\d{4}-\d\d-\d\d \d\d:\d\d.*?)\)/
|
@@ -2340,20 +2357,34 @@ EOS
|
|
2340
2357
|
|
2341
2358
|
seconds = (done - start).to_i
|
2342
2359
|
|
2360
|
+
if record
|
2361
|
+
record_tag_times(item, seconds)
|
2362
|
+
end
|
2363
|
+
|
2364
|
+
@interval_cache[item['title']] = seconds
|
2365
|
+
|
2366
|
+
return seconds > 0 ? seconds : false unless formatted
|
2367
|
+
|
2368
|
+
seconds > 0 ? '%02d:%02d:%02d' % fmt_time(seconds) : false
|
2369
|
+
end
|
2370
|
+
|
2371
|
+
##
|
2372
|
+
## @brief Record times for item tags
|
2373
|
+
##
|
2374
|
+
## @param item The item
|
2375
|
+
##
|
2376
|
+
def record_tag_times(item, seconds)
|
2377
|
+
return if @recorded_items.include?(item)
|
2378
|
+
|
2343
2379
|
item['title'].scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
|
2344
2380
|
k = m[0] == 'done' ? 'All' : m[0].downcase
|
2345
|
-
if @timers.
|
2381
|
+
if @timers.key?(k)
|
2346
2382
|
@timers[k] += seconds
|
2347
2383
|
else
|
2348
2384
|
@timers[k] = seconds
|
2349
2385
|
end
|
2386
|
+
@recorded_items.push(item)
|
2350
2387
|
end
|
2351
|
-
|
2352
|
-
@interval_cache[item['title']] = seconds
|
2353
|
-
|
2354
|
-
return seconds unless formatted
|
2355
|
-
|
2356
|
-
seconds > 0 ? '%02d:%02d:%02d' % fmt_time(seconds) : false
|
2357
2388
|
end
|
2358
2389
|
|
2359
2390
|
##
|
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.83
|
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-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|