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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24a207b31d2f64fc81c00b1484f62a5ab2609ded162fc9733cb54463c652d63e
4
- data.tar.gz: dfc767b0d8cec0229e9ad3823f5853d81c343261198c6638e364331920dc7989
3
+ metadata.gz: fb85f27f305f246d4ab182268c9e5188da67f22fde46d3ee4ae716996c78b2c3
4
+ data.tar.gz: 7b302748d9837511adc77679fd619c6daf85b2f7e15f541d530c8cc211e0aac3
5
5
  SHA512:
6
- metadata.gz: 1f64972ed2d157a0b52afa882d257190d236ebb5805d0ff8049d3ab65ca92974a1cbe2e11ba2ee1ea9cd04b751bd4cc6874f409d442cdfb9347c37bacb6fc47c
7
- data.tar.gz: 9d3432a35cd14d44474ef32e90df40943a2a9ed7eb7c232a2361816aba255b7ed39992dc7b4cfa000afb93abbe4cea3036c9eec244a2fe141f7dca70ce400095
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.78<!--END VER-->.
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: Old
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
  ![](http://ckyp.us/XKpj+)
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 file.
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
- puts wwid.list_section({ section: section.cap_first, count: 0 })
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 (override view settings)'
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 (override view settings)'
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, default_value: default
1373
+ c.flag [:tag_sort], must_match: /^(?:name|time)$/i
1371
1374
 
1372
- c.desc 'Only show items with recorded time intervals'
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
- options[:t] = true if options[:totals]
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: options[:totals]
1467
+ totals: totals
1439
1468
  }
1440
1469
 
1441
1470
  puts wwid.list_section(opts)
data/lib/doing/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Doing
2
- VERSION = '1.0.79'
2
+ VERSION = '1.0.83'
3
3
  end
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] && opt[:tag].length.positive?
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
- puts prompt
776
- options.each_with_index do |section, i|
777
- puts format('% 3d: %s', i + 1, section)
778
- end
779
- print "#{colors['green']}> #{colors['default']}"
780
- num = STDIN.gets
781
- return false if num =~ /^[a-z ]*$/i
782
-
783
- options[num.to_i - 1]
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
- res = `echo #{Shellwords.escape(options.join("\n"))}|#{fzf} --header="Arrows to navigate, tab to mark for selection, enter to perform action" --prompt="Select entries to act on> " -m --bind ctrl-a:select-all -q "#{opt[:query]}"`
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
- action = choose_from(
849
- [
850
- "add tag",
851
- "remove tag",
852
- "archive",
853
- "cancel",
854
- "delete",
855
- "edit",
856
- "finish",
857
- "flag",
858
- "move",
859
- "output format"
860
- ],
861
- 'What do you want to do with the selected items?')
862
- case action
863
- when /(add|remove) tag/
864
- print "Enter tag: "
865
- tag = STDIN.gets
866
- return if tag =~ /^ *$/
867
- opt[:tag] = tag.strip
868
- opt[:remove] = true if action =~ /remove tag/
869
- when /output format/
870
- output_format = choose_from(%w[doing taskpaper json timeline html csv], 'Which output format?')
871
- return if tag =~ /^ *$/
872
- opt[:output] = output_format.strip
873
- res = yn("Save to file?", default_response: 'n')
874
- if res
875
- print "File path/name: "
876
- filename = STDIN.gets.strip
877
- return if filename.empty?
878
- opt[:save_to] = filename
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
- if opt[:output] !~ /(doing|taskpaper)/
1001
- options[:output] = opt[:output]
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.each_with_index do |section, i|
1511
- puts format('% 3d: %s', i + 1, section)
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.each_with_index do |view, i|
1536
- puts format('% 3d: %s', i + 1, view)
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[:today] ||= false
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
- out += tag_times('text', opt[:sort_tags]) if opt[:totals]
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 selection
2179
+ ## @brief Get total elapsed time for all tags in
2180
+ ## selection
2170
2181
  ##
2171
- ## @param format (String) return format (html, json, or text)
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 = 'text', sort_by_name = false)
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 }.reverse
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 = true)
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 nil
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.has_key?(k)
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
@@ -8,5 +8,6 @@ require 'tempfile'
8
8
  require 'chronic'
9
9
  require 'haml'
10
10
  require 'json'
11
- require 'doing/helpers.rb'
12
- require 'doing/wwid.rb'
11
+ require 'doing/helpers'
12
+ require 'doing/wwid'
13
+ # require 'doing/markdown_document_listener'
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.79
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-09-25 00:00:00.000000000 Z
11
+ date: 2021-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake