doing 2.1.103 → 2.1.105

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.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +3 -1
  5. data/bin/commands/view.rb +5 -0
  6. data/bin/doing +5 -0
  7. data/docs/doc/Array.html +1 -1
  8. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  9. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  10. data/docs/doc/BooleanTermParser/Query.html +1 -1
  11. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  12. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  13. data/docs/doc/BooleanTermParser.html +1 -1
  14. data/docs/doc/Doing/ArrayCleanup.html +1 -1
  15. data/docs/doc/Doing/ArrayNestedHash.html +1 -1
  16. data/docs/doc/Doing/ArrayTags.html +1 -1
  17. data/docs/doc/Doing/ByDayExport.html +1 -1
  18. data/docs/doc/Doing/CSVExport.html +1 -1
  19. data/docs/doc/Doing/CalendarImport.html +1 -1
  20. data/docs/doc/Doing/Change.html +1 -1
  21. data/docs/doc/Doing/Changes.html +1 -1
  22. data/docs/doc/Doing/ChronifyArray.html +1 -1
  23. data/docs/doc/Doing/ChronifyNumeric.html +1 -1
  24. data/docs/doc/Doing/ChronifyString.html +1 -1
  25. data/docs/doc/Doing/Color.html +1 -1
  26. data/docs/doc/Doing/Completion/BashCompletions.html +1 -1
  27. data/docs/doc/Doing/Completion/FigCompletions.html +1 -1
  28. data/docs/doc/Doing/Completion/FishCompletions.html +1 -1
  29. data/docs/doc/Doing/Completion/StringUtils.html +1 -1
  30. data/docs/doc/Doing/Completion/ZshCompletions.html +1 -1
  31. data/docs/doc/Doing/Completion.html +1 -1
  32. data/docs/doc/Doing/Configuration.html +2 -1
  33. data/docs/doc/Doing/DayOneRenderer.html +1 -1
  34. data/docs/doc/Doing/DayoneExport.html +1 -1
  35. data/docs/doc/Doing/DoingExport.html +1 -1
  36. data/docs/doc/Doing/DoingImport.html +1 -1
  37. data/docs/doc/Doing/Entry.html +1 -1
  38. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  39. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  40. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  41. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  42. data/docs/doc/Doing/Errors/HistoryLimitError.html +1 -1
  43. data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
  44. data/docs/doc/Doing/Errors/MissingBackupFile.html +1 -1
  45. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  46. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  47. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  48. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  49. data/docs/doc/Doing/Errors.html +1 -1
  50. data/docs/doc/Doing/HTMLExport.html +1 -1
  51. data/docs/doc/Doing/Hooks.html +1 -1
  52. data/docs/doc/Doing/Item.html +1 -1
  53. data/docs/doc/Doing/ItemDates.html +1 -1
  54. data/docs/doc/Doing/ItemQuery.html +1 -1
  55. data/docs/doc/Doing/ItemState.html +1 -1
  56. data/docs/doc/Doing/ItemTags.html +1 -1
  57. data/docs/doc/Doing/Items.html +1 -1
  58. data/docs/doc/Doing/JSONExport.html +1 -1
  59. data/docs/doc/Doing/JSONImport.html +1 -1
  60. data/docs/doc/Doing/Logger.html +1 -1
  61. data/docs/doc/Doing/MarkdownExport.html +1 -1
  62. data/docs/doc/Doing/Note.html +1 -1
  63. data/docs/doc/Doing/Pager.html +1 -1
  64. data/docs/doc/Doing/Plugins.html +1 -1
  65. data/docs/doc/Doing/Prompt.html +1 -1
  66. data/docs/doc/Doing/PromptChoose.html +1 -1
  67. data/docs/doc/Doing/PromptFZF.html +1 -1
  68. data/docs/doc/Doing/PromptInput.html +1 -1
  69. data/docs/doc/Doing/PromptSTD.html +1 -1
  70. data/docs/doc/Doing/PromptYN.html +1 -1
  71. data/docs/doc/Doing/Section.html +1 -1
  72. data/docs/doc/Doing/StringHighlight.html +1 -1
  73. data/docs/doc/Doing/StringNormalize.html +1 -1
  74. data/docs/doc/Doing/StringQuery.html +1 -1
  75. data/docs/doc/Doing/StringTags.html +1 -1
  76. data/docs/doc/Doing/StringTransform.html +1 -1
  77. data/docs/doc/Doing/StringTruncate.html +1 -1
  78. data/docs/doc/Doing/StringURL.html +1 -1
  79. data/docs/doc/Doing/SymbolNormalize.html +1 -1
  80. data/docs/doc/Doing/TaskPaperExport.html +1 -1
  81. data/docs/doc/Doing/TemplateExport.html +1 -1
  82. data/docs/doc/Doing/TemplateString.html +1 -1
  83. data/docs/doc/Doing/TimingImport.html +1 -1
  84. data/docs/doc/Doing/Types.html +11 -1
  85. data/docs/doc/Doing/Util/Backup.html +1 -1
  86. data/docs/doc/Doing/Util.html +1 -1
  87. data/docs/doc/Doing/Version.html +1 -1
  88. data/docs/doc/Doing/WWID.html +3 -3
  89. data/docs/doc/Doing.html +2 -2
  90. data/docs/doc/FalseClass.html +1 -1
  91. data/docs/doc/GLI/Commands/Help.html +1 -1
  92. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  93. data/docs/doc/GLI/Commands.html +1 -1
  94. data/docs/doc/GLI.html +1 -1
  95. data/docs/doc/Hash.html +1 -1
  96. data/docs/doc/Numeric.html +1 -1
  97. data/docs/doc/Object.html +1 -1
  98. data/docs/doc/PhraseParser/Operator.html +1 -1
  99. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  100. data/docs/doc/PhraseParser/Query.html +1 -1
  101. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  102. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  103. data/docs/doc/PhraseParser/TermClause.html +1 -1
  104. data/docs/doc/PhraseParser.html +1 -1
  105. data/docs/doc/Status.html +1 -1
  106. data/docs/doc/String.html +1 -1
  107. data/docs/doc/Symbol.html +1 -1
  108. data/docs/doc/Time.html +1 -1
  109. data/docs/doc/TrueClass.html +1 -1
  110. data/docs/doc/_index.html +1 -1
  111. data/docs/doc/file.README.html +4 -2
  112. data/docs/doc/index.html +4 -2
  113. data/docs/doc/top-level-namespace.html +1 -1
  114. data/doing.rdoc +65 -1
  115. data/lib/completion/_doing.zsh +9 -9
  116. data/lib/completion/doing.bash +16 -16
  117. data/lib/completion/doing.fish +8 -0
  118. data/lib/completion/doing.ts +90 -0
  119. data/lib/doing/add_options.rb +4 -0
  120. data/lib/doing/configuration.rb +1 -0
  121. data/lib/doing/plugins/export/template_export.rb +3 -1
  122. data/lib/doing/types.rb +2 -0
  123. data/lib/doing/version.rb +1 -1
  124. data/lib/doing/wwid/timers.rb +46 -8
  125. metadata +1 -1
@@ -1518,6 +1518,16 @@ const completionSpec: Fig.Spec = {
1518
1518
 
1519
1519
  },
1520
1520
 
1521
+ {
1522
+ name: ["--totals_format"],
1523
+ description: "Time format for totals output",
1524
+ args: {
1525
+ name: "FORMAT",
1526
+ description: "FORMAT",
1527
+ },
1528
+
1529
+ },
1530
+
1521
1531
  {
1522
1532
  name: ["--val"],
1523
1533
  description: "Perform a tag value query",
@@ -1746,6 +1756,16 @@ const completionSpec: Fig.Spec = {
1746
1756
 
1747
1757
  },
1748
1758
 
1759
+ {
1760
+ name: ["--totals_format"],
1761
+ description: "Time format for totals output",
1762
+ args: {
1763
+ name: "FORMAT",
1764
+ description: "FORMAT",
1765
+ },
1766
+
1767
+ },
1768
+
1749
1769
  {
1750
1770
  name: ["--val"],
1751
1771
  description: "Perform a tag value query",
@@ -2801,6 +2821,16 @@ const completionSpec: Fig.Spec = {
2801
2821
 
2802
2822
  },
2803
2823
 
2824
+ {
2825
+ name: ["--totals_format"],
2826
+ description: "Time format for totals output",
2827
+ args: {
2828
+ name: "FORMAT",
2829
+ description: "FORMAT",
2830
+ },
2831
+
2832
+ },
2833
+
2804
2834
  {
2805
2835
  name: ["--val"],
2806
2836
  description: "Perform a tag value query",
@@ -3007,6 +3037,16 @@ const completionSpec: Fig.Spec = {
3007
3037
 
3008
3038
  },
3009
3039
 
3040
+ {
3041
+ name: ["--totals_format"],
3042
+ description: "Time format for totals output",
3043
+ args: {
3044
+ name: "FORMAT",
3045
+ description: "FORMAT",
3046
+ },
3047
+
3048
+ },
3049
+
3010
3050
  ],
3011
3051
 
3012
3052
  },
@@ -3813,6 +3853,16 @@ const completionSpec: Fig.Spec = {
3813
3853
 
3814
3854
  },
3815
3855
 
3856
+ {
3857
+ name: ["--totals_format"],
3858
+ description: "Time format for totals output",
3859
+ args: {
3860
+ name: "FORMAT",
3861
+ description: "FORMAT",
3862
+ },
3863
+
3864
+ },
3865
+
3816
3866
  {
3817
3867
  name: ["--val"],
3818
3868
  description: "Perform a tag value query",
@@ -3997,6 +4047,16 @@ const completionSpec: Fig.Spec = {
3997
4047
 
3998
4048
  },
3999
4049
 
4050
+ {
4051
+ name: ["--totals_format"],
4052
+ description: "Time format for totals output",
4053
+ args: {
4054
+ name: "FORMAT",
4055
+ description: "FORMAT",
4056
+ },
4057
+
4058
+ },
4059
+
4000
4060
  {
4001
4061
  name: ["--val"],
4002
4062
  description: "Perform a tag value query",
@@ -4503,6 +4563,16 @@ const completionSpec: Fig.Spec = {
4503
4563
 
4504
4564
  },
4505
4565
 
4566
+ {
4567
+ name: ["--totals_format"],
4568
+ description: "Time format for totals output",
4569
+ args: {
4570
+ name: "FORMAT",
4571
+ description: "FORMAT",
4572
+ },
4573
+
4574
+ },
4575
+
4506
4576
  ],
4507
4577
 
4508
4578
  },
@@ -4803,6 +4873,16 @@ const completionSpec: Fig.Spec = {
4803
4873
 
4804
4874
  },
4805
4875
 
4876
+ {
4877
+ name: ["--totals_format"],
4878
+ description: "Time format for totals output",
4879
+ args: {
4880
+ name: "FORMAT",
4881
+ description: "FORMAT",
4882
+ },
4883
+
4884
+ },
4885
+
4806
4886
  {
4807
4887
  name: ["--val"],
4808
4888
  description: "Perform a tag value query",
@@ -5091,6 +5171,16 @@ const completionSpec: Fig.Spec = {
5091
5171
 
5092
5172
  },
5093
5173
 
5174
+ {
5175
+ name: ["--totals_format"],
5176
+ description: "Time format for totals output",
5177
+ args: {
5178
+ name: "FORMAT",
5179
+ description: "FORMAT",
5180
+ },
5181
+
5182
+ },
5183
+
5094
5184
  ],
5095
5185
 
5096
5186
  },
@@ -138,6 +138,10 @@ def add_options(type, cmd, default_template: 'default')
138
138
  cmd.desc 'Totals grouping (tags|section). Can be repeated, e.g. --by tags --by section'
139
139
  cmd.arg_name 'GROUP'
140
140
  cmd.flag [:by], must_match: REGEX_TOTALS_BY, multiple: true, type: TotalsBySymbol
141
+
142
+ cmd.desc 'Time format for totals output (clock|hmclock|hm|dhm|ydhm|tight|natural|speech|m|averages)'
143
+ cmd.arg_name 'FORMAT'
144
+ cmd.flag [:totals_format], must_match: REGEX_TOTALS_FORMAT, type: TotalsFormatSymbol
141
145
  when :tag_filter
142
146
  cmd.desc 'Filter entries by tag. Combine multiple tags with a comma. Wildcards allowed (*, ?)'
143
147
  cmd.arg_name 'TAG'
@@ -44,6 +44,7 @@ module Doing
44
44
  'budgets' => {},
45
45
 
46
46
  'timer_format' => 'text',
47
+ 'totals_format' => 'clock',
47
48
  'interval_format' => 'text',
48
49
 
49
50
  'order' => 'asc',
@@ -141,7 +141,9 @@ module Doing
141
141
  out += wwid.tag_times(format: Doing.setting('timer_format').to_sym,
142
142
  sort_by: opt[:sort_tags],
143
143
  sort_order: opt[:tag_order],
144
- by: opt[:by])
144
+ by: opt[:by],
145
+ totals_format: opt[:totals_format],
146
+ items: items)
145
147
  end
146
148
  out
147
149
  end
data/lib/doing/types.rb CHANGED
@@ -7,6 +7,7 @@ module Doing
7
7
  REGEX_BOOL = /^(?:and|all|any|or|not|none|p(?:at(?:tern)?)?)$/i.freeze
8
8
  REGEX_SORT_ORDER = /^(?:a(?:sc)?|d(?:esc)?)$/i.freeze
9
9
  REGEX_TOTALS_BY = /^(?:t(?:ag(?:s)?)?|s(?:ec(?:tion|tions)?)?|p(?:roj(?:ect|ects)?)?)$/i.freeze
10
+ REGEX_TOTALS_FORMAT = /^(?:averages|clock|hmclock|hm|dhm|ydhm|tight|natural|speech|m)$/i.freeze
10
11
  REGEX_VALUE_QUERY = /^(?:!)?@?(?:\S+) +(?:!?[<>=][=*]?|[$*^]=) +(?:.*?)$/.freeze
11
12
  REGEX_CLOCK = '(?:\d{1,2}+(?::\d{1,2}+)?(?: *(?:am|pm))?|midnight|noon)'
12
13
  REGEX_TIME = /^#{REGEX_CLOCK}$/i.freeze
@@ -32,6 +33,7 @@ module Doing
32
33
  TagArray = Class.new(Array)
33
34
  TagSortSymbol = Class.new(Symbol)
34
35
  TotalsBySymbol = Class.new(Symbol)
36
+ TotalsFormatSymbol = Class.new(String)
35
37
  TemplateName = Class.new(String)
36
38
  end
37
39
  end
data/lib/doing/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Doing
4
- VERSION = '2.1.103'
4
+ VERSION = '2.1.105'
5
5
  end
@@ -11,10 +11,11 @@ module Doing
11
11
  ## @param sort_by [Symbol] Sort by :name or :time
12
12
  ## @param sort_order [Symbol] The sort order (:asc or :desc)
13
13
  ##
14
- def tag_times(format: :text, sort_by: :time, sort_order: :asc, by: nil)
14
+ def tag_times(format: :text, sort_by: :time, sort_order: :asc, by: nil, totals_format: nil, items: nil)
15
15
  groupings = normalize_totals_groupings(by)
16
16
  totals = collect_totals(groupings)
17
17
  return '' if totals.empty?
18
+ totals_format = normalize_totals_format(totals_format)
18
19
 
19
20
  timers_snapshot = totals[:tags] || {}
20
21
 
@@ -54,6 +55,8 @@ module Doing
54
55
  timer_data: timer_data,
55
56
  sort_by: sort_by,
56
57
  sort_order: sort_order,
58
+ totals_format: totals_format,
59
+ items: items,
57
60
  remaining_map: remaining_map,
58
61
  budgets: budgets,
59
62
  budgets_total: budgets_total,
@@ -144,7 +147,7 @@ module Doing
144
147
  sorted
145
148
  end
146
149
 
147
- def render_totals_group(format:, group:, timer_data:, sort_by:, sort_order:, remaining_map:, budgets:, budgets_total:, budget_fmt:)
150
+ def render_totals_group(format:, group:, timer_data:, sort_by:, sort_order:, totals_format:, items:, remaining_map:, budgets:, budgets_total:, budget_fmt:)
148
151
  timer_data = timer_data.dup
149
152
  timer_data.delete('meanwhile')
150
153
 
@@ -155,6 +158,7 @@ module Doing
155
158
 
156
159
  title = group == :section ? 'Section Totals' : 'Tag Totals'
157
160
  label = group == :section ? 'section' : 'tag'
161
+ line_format = totals_format == :averages ? :hmclock : totals_format
158
162
 
159
163
  case format
160
164
  when :html
@@ -180,7 +184,7 @@ EOHEAD
180
184
  if group == :tags && remaining_map.key?(k) && remaining_map[k].positive?
181
185
  budget_str = " (budget left #{budget_fmt.call(remaining_map[k])})"
182
186
  end
183
- output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: :clock)}#{budget_str}</td></tr>\n"
187
+ output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: line_format)}#{budget_str}</td></tr>\n"
184
188
  end
185
189
  output += <<EOTAIL
186
190
  <tr>
@@ -190,7 +194,7 @@ EOHEAD
190
194
  <tfoot>
191
195
  <tr>
192
196
  <td style="text-align:left;"><strong>Total</strong></td>
193
- <td style="text-align:left;">#{total.time_string(format: :clock)}#{group == :tags && budgets_total.positive? ? " (total budgets left #{budget_fmt.call(budgets_total)})" : ''}</td>
197
+ <td style="text-align:left;">#{total.time_string(format: line_format)}#{group == :tags && budgets_total.positive? ? " (total budgets left #{budget_fmt.call(budgets_total)})" : ''}</td>
194
198
  </tr>
195
199
  </tfoot>
196
200
  </table>
@@ -210,7 +214,7 @@ EOTAIL
210
214
  if group == :tags && remaining_map.key?(k) && remaining_map[k].positive?
211
215
  budget_str = " (budget left #{budget_fmt.call(remaining_map[k])})"
212
216
  end
213
- output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)}#{budget_str} |\n"
217
+ output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: line_format)}#{budget_str} |\n"
214
218
  end
215
219
  output + "[#{title}]"
216
220
  when :json
@@ -219,7 +223,7 @@ EOTAIL
219
223
  row = {
220
224
  label => k,
221
225
  'seconds' => v,
222
- 'formatted' => v.time_string(format: :clock)
226
+ 'formatted' => v.time_string(format: line_format)
223
227
  }
224
228
  if group == :tags
225
229
  row['budget'] = budgets[k]
@@ -287,7 +291,7 @@ EOTAIL
287
291
  (max - k.length).times do
288
292
  spacer += ' '
289
293
  end
290
- line = "#{k}:#{spacer}#{v.time_string(format: :clock)}"
294
+ line = "#{k}:#{spacer}#{v.time_string(format: line_format)}"
291
295
  if group == :tags && remaining_map.key?(k) && remaining_map[k].positive?
292
296
  line += " (budget left #{budget_fmt.call(remaining_map[k])})"
293
297
  end
@@ -295,11 +299,45 @@ EOTAIL
295
299
  end
296
300
 
297
301
  output = output.empty? ? '' : "\n--- #{title} ---\n#{output.join("\n")}"
298
- output += "\n\nTotal tracked: #{total.time_string(format: :clock)}"
302
+ output += "\n\nTotal tracked: #{total.time_string(format: line_format)}"
303
+ output += totals_average_suffix(total, items) if totals_format == :averages
299
304
  output += " (total budgets left #{budget_fmt.call(budgets_total)})" if group == :tags && budgets_total.positive?
300
305
  output += "\n"
301
306
  output
302
307
  end
303
308
  end
309
+
310
+ def normalize_totals_format(value)
311
+ format = (value || Doing.setting('totals_format') || 'clock').to_s.downcase
312
+ format = 'clock' if format.empty?
313
+ return :averages if format == 'averages'
314
+
315
+ format.to_sym
316
+ end
317
+
318
+ def totals_average_suffix(total_seconds, items)
319
+ return '' unless items.respond_to?(:empty?) && !items.empty?
320
+
321
+ total_hours = total_seconds.to_f / 3600
322
+ total_hours_floor = total_hours.floor
323
+ total_minutes = ((total_hours - total_hours_floor) * 60).round
324
+ if total_minutes == 60
325
+ total_hours_floor += 1
326
+ total_minutes = 0
327
+ end
328
+
329
+ dates = items.map { |item| item.date.to_date }.compact
330
+ return '' if dates.empty?
331
+
332
+ start_date = dates.min
333
+ end_date = dates.max
334
+ time_span_in_days = [(end_date - start_date).to_i + 1, 1].max
335
+ avg_hours_per_day = total_hours / time_span_in_days
336
+
337
+ format(' (%<hours>dh %<minutes>d min, %<avg>.2fh/day)',
338
+ hours: total_hours_floor,
339
+ minutes: total_minutes,
340
+ avg: avg_hours_per_day)
341
+ end
304
342
  end
305
343
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doing
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.103
4
+ version: 2.1.105
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra