doing 2.1.40 → 2.1.41

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 (192) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +22 -0
  4. data/Gemfile.lock +1 -1
  5. data/Rakefile +4 -4
  6. data/bin/commands/changes.rb +1 -1
  7. data/bin/commands/tag_dir.rb +49 -15
  8. data/{Dockerfile → docker/Dockerfile} +3 -1
  9. data/{Dockerfile-2.6 → docker/Dockerfile-2.6} +2 -2
  10. data/{Dockerfile-2.7 → docker/Dockerfile-2.7} +2 -2
  11. data/{Dockerfile-3.0 → docker/Dockerfile-3.0} +2 -2
  12. data/{bash_profile → docker/bash_profile} +0 -0
  13. data/{inputrc → docker/inputrc} +0 -0
  14. data/docs/doc/Array.html +84 -2
  15. data/docs/doc/BooleanTermParser/Clause.html +1 -1
  16. data/docs/doc/BooleanTermParser/Operator.html +1 -1
  17. data/docs/doc/BooleanTermParser/Query.html +1 -1
  18. data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
  19. data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
  20. data/docs/doc/BooleanTermParser.html +1 -1
  21. data/docs/doc/Doing/ArrayNestedHash.html +198 -0
  22. data/docs/doc/Doing/ArrayTags.html +424 -0
  23. data/docs/doc/Doing/CSVExport.html +266 -0
  24. data/docs/doc/Doing/CalendarImport.html +232 -0
  25. data/docs/doc/Doing/Change.html +617 -0
  26. data/docs/doc/Doing/Changes.html +468 -0
  27. data/docs/doc/Doing/ChronifyArray.html +347 -0
  28. data/docs/doc/Doing/ChronifyNumeric.html +271 -0
  29. data/docs/doc/Doing/ChronifyString.html +682 -0
  30. data/docs/doc/Doing/Color.html +2 -2
  31. data/docs/doc/Doing/Completion/BashCompletions.html +445 -0
  32. data/docs/doc/Doing/Completion/FishCompletions.html +445 -0
  33. data/docs/doc/Doing/Completion/StringUtils.html +229 -0
  34. data/docs/doc/Doing/Completion/ZshCompletions.html +445 -0
  35. data/docs/doc/Doing/Completion.html +17 -3
  36. data/docs/doc/Doing/Configuration.html +1 -1
  37. data/docs/doc/Doing/DayOneRenderer.html +383 -0
  38. data/docs/doc/Doing/DayoneExport.html +290 -0
  39. data/docs/doc/Doing/DoingImport.html +391 -0
  40. data/docs/doc/Doing/Entry.html +381 -0
  41. data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
  42. data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
  43. data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
  44. data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
  45. data/docs/doc/Doing/Errors/HistoryLimitError.html +1 -1
  46. data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
  47. data/docs/doc/Doing/Errors/MissingBackupFile.html +1 -1
  48. data/docs/doc/Doing/Errors/NoResults.html +1 -1
  49. data/docs/doc/Doing/Errors/PluginException.html +1 -1
  50. data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
  51. data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
  52. data/docs/doc/Doing/Errors.html +1 -1
  53. data/docs/doc/Doing/HTMLExport.html +256 -0
  54. data/docs/doc/Doing/Hooks.html +1 -1
  55. data/docs/doc/Doing/Item.html +47 -3
  56. data/docs/doc/Doing/ItemDates.html +564 -0
  57. data/docs/doc/Doing/ItemQuery.html +614 -0
  58. data/docs/doc/Doing/ItemState.html +387 -0
  59. data/docs/doc/Doing/ItemTags.html +498 -0
  60. data/docs/doc/Doing/Items.html +460 -11
  61. data/docs/doc/Doing/JSONExport.html +222 -0
  62. data/docs/doc/Doing/Logger.html +1 -1
  63. data/docs/doc/Doing/MarkdownExport.html +266 -0
  64. data/docs/doc/Doing/MarkdownRenderer.html +383 -0
  65. data/docs/doc/Doing/Note.html +16 -3
  66. data/docs/doc/Doing/Pager.html +1 -1
  67. data/docs/doc/Doing/Plugins.html +1 -1
  68. data/docs/doc/Doing/Prompt.html +31 -682
  69. data/docs/doc/Doing/PromptChoose.html +484 -0
  70. data/docs/doc/Doing/PromptFZF.html +391 -0
  71. data/docs/doc/Doing/PromptInput.html +572 -0
  72. data/docs/doc/Doing/PromptSTD.html +293 -0
  73. data/docs/doc/Doing/PromptYN.html +237 -0
  74. data/docs/doc/Doing/Section.html +58 -2
  75. data/docs/doc/Doing/StringHighlight.html +533 -0
  76. data/docs/doc/Doing/StringNormalize.html +929 -0
  77. data/docs/doc/Doing/StringQuery.html +725 -0
  78. data/docs/doc/Doing/StringTags.html +884 -0
  79. data/docs/doc/Doing/StringTransform.html +565 -0
  80. data/docs/doc/Doing/StringTruncate.html +448 -0
  81. data/docs/doc/Doing/StringURL.html +409 -0
  82. data/docs/doc/Doing/SymbolNormalize.html +341 -0
  83. data/docs/doc/Doing/TaskPaperExport.html +222 -0
  84. data/docs/doc/Doing/TemplateExport.html +249 -0
  85. data/docs/doc/Doing/TemplateString.html +101 -2
  86. data/docs/doc/Doing/TimingImport.html +285 -0
  87. data/docs/doc/Doing/Types.html +1 -1
  88. data/docs/doc/Doing/Util/Backup.html +9 -7
  89. data/docs/doc/Doing/Util.html +2 -2
  90. data/docs/doc/Doing/Version.html +523 -0
  91. data/docs/doc/Doing/WWID/WWIDUtil.html +510 -0
  92. data/docs/doc/Doing/WWID.html +4377 -217
  93. data/docs/doc/Doing/WWIDDisplay.html +865 -0
  94. data/docs/doc/Doing/WWIDEditor.html +466 -0
  95. data/docs/doc/Doing/WWIDFileTools.html +359 -0
  96. data/docs/doc/Doing/WWIDFilter.html +466 -0
  97. data/docs/doc/Doing/WWIDGuess.html +299 -0
  98. data/docs/doc/Doing/WWIDInteractive.html +752 -0
  99. data/docs/doc/Doing/WWIDModify.html +1078 -0
  100. data/docs/doc/Doing/WWIDTags.html +302 -0
  101. data/docs/doc/Doing/WWIDTimers.html +359 -0
  102. data/docs/doc/Doing/WWIDUtil.html +510 -0
  103. data/docs/doc/Doing.html +9 -6
  104. data/docs/doc/FalseClass.html +1 -1
  105. data/docs/doc/GLI/Commands/Help.html +1 -1
  106. data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
  107. data/docs/doc/GLI/Commands.html +1 -1
  108. data/docs/doc/GLI.html +1 -1
  109. data/docs/doc/Hash.html +1 -1
  110. data/docs/doc/Numeric.html +23 -78
  111. data/docs/doc/Object.html +1 -1
  112. data/docs/doc/PhraseParser/Operator.html +1 -1
  113. data/docs/doc/PhraseParser/PhraseClause.html +1 -1
  114. data/docs/doc/PhraseParser/Query.html +1 -1
  115. data/docs/doc/PhraseParser/QueryParser.html +1 -1
  116. data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
  117. data/docs/doc/PhraseParser/TermClause.html +1 -1
  118. data/docs/doc/PhraseParser.html +1 -1
  119. data/docs/doc/Status.html +1 -1
  120. data/docs/doc/String.html +58 -633
  121. data/docs/doc/Symbol.html +9 -224
  122. data/docs/doc/Time.html +119 -13
  123. data/docs/doc/TrueClass.html +1 -1
  124. data/docs/doc/_index.html +324 -8
  125. data/docs/doc/class_list.html +1 -1
  126. data/docs/doc/file.README.html +1 -1
  127. data/docs/doc/index.html +1 -1
  128. data/docs/doc/method_list.html +2326 -542
  129. data/docs/doc/top-level-namespace.html +2 -2
  130. data/doing.rdoc +13 -3
  131. data/lib/completion/_doing.zsh +1 -1
  132. data/lib/completion/doing.bash +2 -2
  133. data/lib/completion/doing.fish +3 -1
  134. data/lib/doing/array/array.rb +16 -12
  135. data/lib/doing/array/nested_hash.rb +1 -1
  136. data/lib/doing/array/tags.rb +6 -5
  137. data/lib/doing/changelog/changelog.rb +6 -0
  138. data/lib/doing/chronify/array.rb +1 -3
  139. data/lib/doing/chronify/chronify.rb +12 -0
  140. data/lib/doing/chronify/numeric.rb +3 -2
  141. data/lib/doing/chronify/string.rb +1 -1
  142. data/lib/doing/completion/completion_string.rb +25 -0
  143. data/lib/doing/completion.rb +1 -1
  144. data/lib/doing/good.rb +8 -0
  145. data/lib/doing/item/dates.rb +1 -1
  146. data/lib/doing/{item.rb → item/item.rb} +10 -5
  147. data/lib/doing/item/query.rb +1 -1
  148. data/lib/doing/item/state.rb +1 -1
  149. data/lib/doing/item/tags.rb +1 -1
  150. data/lib/doing/items/filter.rb +67 -0
  151. data/lib/doing/items/items.rb +57 -0
  152. data/lib/doing/items/modify.rb +36 -0
  153. data/lib/doing/items/sections.rb +83 -0
  154. data/lib/doing/items/util.rb +74 -0
  155. data/lib/doing/normalize.rb +10 -2
  156. data/lib/doing/plugins/export/markdown_export.rb +4 -2
  157. data/lib/doing/plugins/import/doing_import.rb +1 -1
  158. data/lib/doing/prompt/choose.rb +118 -0
  159. data/lib/doing/prompt/fzf.rb +84 -0
  160. data/lib/doing/prompt/input.rb +129 -0
  161. data/lib/doing/prompt/prompt.rb +41 -0
  162. data/lib/doing/prompt/std.rb +32 -0
  163. data/lib/doing/prompt/yn.rb +64 -0
  164. data/lib/doing/section.rb +4 -0
  165. data/lib/doing/string/highlight.rb +1 -1
  166. data/lib/doing/string/query.rb +1 -1
  167. data/lib/doing/string/string.rb +18 -7
  168. data/lib/doing/string/tags.rb +14 -3
  169. data/lib/doing/string/transform.rb +1 -1
  170. data/lib/doing/string/truncate.rb +1 -1
  171. data/lib/doing/string/url.rb +1 -1
  172. data/lib/doing/time.rb +19 -1
  173. data/lib/doing/util_backup.rb +2 -2
  174. data/lib/doing/version.rb +1 -1
  175. data/lib/doing/wwid/display.rb +357 -360
  176. data/lib/doing/wwid/editor.rb +173 -176
  177. data/lib/doing/wwid/filetools.rb +156 -159
  178. data/lib/doing/wwid/filter.rb +191 -183
  179. data/lib/doing/wwid/guess.rb +58 -60
  180. data/lib/doing/wwid/interactive.rb +332 -330
  181. data/lib/doing/wwid/modify.rb +509 -512
  182. data/lib/doing/wwid/tags.rb +38 -41
  183. data/lib/doing/wwid/timers.rb +293 -296
  184. data/lib/doing/{wwid.rb → wwid/wwid.rb} +32 -23
  185. data/lib/doing/wwid/wwidutil.rb +79 -82
  186. data/lib/doing.rb +5 -5
  187. data/lib/helpers/threaded_tests.rb +1 -0
  188. metadata +76 -14
  189. data/lib/doing/changelog.rb +0 -6
  190. data/lib/doing/completion/string.rb +0 -17
  191. data/lib/doing/items.rb +0 -221
  192. data/lib/doing/prompt.rb +0 -330
@@ -2,343 +2,340 @@
2
2
 
3
3
  module Doing
4
4
  class WWID
5
- # Timer methods for WWID class
6
- module Timers
7
- ##
8
- ## Get total elapsed time for all tags in
9
- ## selection
10
- ##
11
- ## @param format [String] return format (html,
12
- ## json, or text)
13
- ## @param sort_by [Symbol] Sort by :name or :time
14
- ## @param sort_order [Symbol] The sort order (:asc or :desc)
15
- ##
16
- def tag_times(format: :text, sort_by: :time, sort_order: :asc)
17
- return '' if @timers.empty?
5
+ ##
6
+ ## Get total elapsed time for all tags in
7
+ ## selection
8
+ ##
9
+ ## @param format [String] return format (html,
10
+ ## json, or text)
11
+ ## @param sort_by [Symbol] Sort by :name or :time
12
+ ## @param sort_order [Symbol] The sort order (:asc or :desc)
13
+ ##
14
+ def tag_times(format: :text, sort_by: :time, sort_order: :asc)
15
+ return '' if @timers.empty?
18
16
 
19
- max = @timers.keys.sort_by(&:length).reverse[0].length + 1
17
+ max = @timers.keys.sort_by(&:length).reverse[0].length + 1
20
18
 
21
- total = @timers.delete('All')
19
+ total = @timers.delete('All')
22
20
 
23
- tags_data = @timers.delete_if { |_k, v| v.zero? }
24
- sorted_tags_data = if sort_by.normalize_tag_sort == :name
25
- tags_data.sort_by { |k, _v| k }
26
- else
27
- tags_data.sort_by { |_k, v| v }
28
- end
21
+ tags_data = @timers.delete_if { |_k, v| v.zero? }
22
+ sorted_tags_data = if sort_by.normalize_tag_sort == :name
23
+ tags_data.sort_by { |k, _v| k }
24
+ else
25
+ tags_data.sort_by { |_k, v| v }
26
+ end
29
27
 
30
- sorted_tags_data.reverse! if sort_order.normalize_order == :asc
31
- case format
32
- when :html
28
+ sorted_tags_data.reverse! if sort_order.normalize_order == :asc
29
+ case format
30
+ when :html
33
31
 
34
- output = <<EOHEAD
35
- <table>
36
- <caption id="tagtotals">Tag Totals</caption>
37
- <colgroup>
38
- <col style="text-align:left;"/>
39
- <col style="text-align:left;"/>
40
- </colgroup>
41
- <thead>
42
- <tr>
43
- <th style="text-align:left;">project</th>
44
- <th style="text-align:left;">time</th>
45
- </tr>
46
- </thead>
47
- <tbody>
48
- EOHEAD
49
- sorted_tags_data.reverse.each do |k, v|
50
- if v.positive?
51
- output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: :clock)}</td></tr>\n"
52
- end
53
- end
54
- tail = <<EOTAIL
55
- <tr>
56
- <td style="text-align:left;" colspan="2"></td>
57
- </tr>
58
- </tbody>
59
- <tfoot>
32
+ output = <<EOHEAD
33
+ <table>
34
+ <caption id="tagtotals">Tag Totals</caption>
35
+ <colgroup>
36
+ <col style="text-align:left;"/>
37
+ <col style="text-align:left;"/>
38
+ </colgroup>
39
+ <thead>
60
40
  <tr>
61
- <td style="text-align:left;"><strong>Total</strong></td>
62
- <td style="text-align:left;">#{total.time_string(format: :clock)}</td>
41
+ <th style="text-align:left;">project</th>
42
+ <th style="text-align:left;">time</th>
63
43
  </tr>
64
- </tfoot>
65
- </table>
66
- EOTAIL
67
- output + tail
68
- when :markdown
69
- pad = sorted_tags_data.map { |k, _| k }.group_by(&:size).max.last[0].length
70
- pad = 7 if pad < 7
71
- output = <<~EOHEADER
72
- | #{' ' * (pad - 7)}project | time |
73
- | #{'-' * (pad - 1)}: | :------- |
74
- EOHEADER
75
- sorted_tags_data.reverse.each do |k, v|
76
- if v.positive?
77
- output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)} |\n"
78
- end
44
+ </thead>
45
+ <tbody>
46
+ EOHEAD
47
+ sorted_tags_data.reverse.each do |k, v|
48
+ if v.positive?
49
+ output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: :clock)}</td></tr>\n"
79
50
  end
80
- tail = '[Tag Totals]'
81
- output + tail
82
- when :json
83
- output = []
84
- sorted_tags_data.reverse.each do |k, v|
85
- output << {
86
- 'tag' => k,
87
- 'seconds' => v,
88
- 'formatted' => v.time_string(format: :clock)
89
- }
51
+ end
52
+ tail = <<EOTAIL
53
+ <tr>
54
+ <td style="text-align:left;" colspan="2"></td>
55
+ </tr>
56
+ </tbody>
57
+ <tfoot>
58
+ <tr>
59
+ <td style="text-align:left;"><strong>Total</strong></td>
60
+ <td style="text-align:left;">#{total.time_string(format: :clock)}</td>
61
+ </tr>
62
+ </tfoot>
63
+ </table>
64
+ EOTAIL
65
+ output + tail
66
+ when :markdown
67
+ pad = sorted_tags_data.map { |k, _| k }.group_by(&:size).max.last[0].length
68
+ pad = 7 if pad < 7
69
+ output = <<~EOHEADER
70
+ | #{' ' * (pad - 7)}project | time |
71
+ | #{'-' * (pad - 1)}: | :------- |
72
+ EOHEADER
73
+ sorted_tags_data.reverse.each do |k, v|
74
+ if v.positive?
75
+ output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)} |\n"
90
76
  end
91
- output
92
- when :human
93
- output = []
94
- sorted_tags_data.reverse.each do |k, v|
95
- spacer = ''
96
- (max - k.length).times do
97
- spacer += ' '
98
- end
99
- output.push("┃ #{spacer}#{k}:#{v.time_string(format: :hm)} ┃")
77
+ end
78
+ tail = '[Tag Totals]'
79
+ output + tail
80
+ when :json
81
+ output = []
82
+ sorted_tags_data.reverse.each do |k, v|
83
+ output << {
84
+ 'tag' => k,
85
+ 'seconds' => v,
86
+ 'formatted' => v.time_string(format: :clock)
87
+ }
88
+ end
89
+ output
90
+ when :human
91
+ output = []
92
+ sorted_tags_data.reverse.each do |k, v|
93
+ spacer = ''
94
+ (max - k.length).times do
95
+ spacer += ' '
100
96
  end
97
+ output.push("┃ #{spacer}#{k}:#{v.time_string(format: :hm)} ┃")
98
+ end
101
99
 
102
- header = '┏━━ Tag Totals '
103
- (max - 2).times { header += '━' }
104
- header += '┓'
105
- footer = '┗'
106
- (max + 12).times { footer += '━' }
107
- footer += '┛'
108
- divider = '┣'
109
- (max + 12).times { divider += '━' }
110
- divider += '┫'
111
- output = output.empty? ? '' : "\n#{header}\n#{output.join("\n")}"
112
- output += "\n#{divider}"
100
+ header = '┏━━ Tag Totals '
101
+ (max - 2).times { header += '━' }
102
+ header += '┓'
103
+ footer = '┗'
104
+ (max + 12).times { footer += '━' }
105
+ footer += '┛'
106
+ divider = '┣'
107
+ (max + 12).times { divider += '━' }
108
+ divider += '┫'
109
+ output = output.empty? ? '' : "\n#{header}\n#{output.join("\n")}"
110
+ output += "\n#{divider}"
111
+ spacer = ''
112
+ (max - 6).times do
113
+ spacer += ' '
114
+ end
115
+ total_time = total.time_string(format: :hm)
116
+ total = "┃ #{spacer}total: "
117
+ total += total_time
118
+ total += ' ┃'
119
+ output += "\n#{total}"
120
+ output += "\n#{footer}"
121
+ output
122
+ else
123
+ output = []
124
+ sorted_tags_data.reverse.each do |k, v|
113
125
  spacer = ''
114
- (max - 6).times do
126
+ (max - k.length).times do
115
127
  spacer += ' '
116
128
  end
117
- total_time = total.time_string(format: :hm)
118
- total = "┃ #{spacer}total: "
119
- total += total_time
120
- total += ' ┃'
121
- output += "\n#{total}"
122
- output += "\n#{footer}"
123
- output
124
- else
125
- output = []
126
- sorted_tags_data.reverse.each do |k, v|
127
- spacer = ''
128
- (max - k.length).times do
129
- spacer += ' '
130
- end
131
- output.push("#{k}:#{spacer}#{v.time_string(format: :clock)}")
132
- end
133
-
134
- output = output.empty? ? '' : "\n--- Tag Totals ---\n#{output.join("\n")}"
135
- output += "\n\nTotal tracked: #{total.time_string(format: :clock)}\n"
136
- output
129
+ output.push("#{k}:#{spacer}#{v.time_string(format: :clock)}")
137
130
  end
131
+
132
+ output = output.empty? ? '' : "\n--- Tag Totals ---\n#{output.join("\n")}"
133
+ output += "\n\nTotal tracked: #{total.time_string(format: :clock)}\n"
134
+ output
138
135
  end
136
+ end
139
137
 
140
- ##
141
- ## Gets the interval between entry's start
142
- ## date and @done date
143
- ##
144
- ## @param item [Item] The entry
145
- ## @param formatted [Boolean] Return human readable
146
- ## time (default seconds)
147
- ## @param record [Boolean] Add the interval to the
148
- ## total for each tag
149
- ##
150
- ## @return Interval in seconds, or [d, h, m] array if
151
- ## formatted is true. False if no end date or
152
- ## interval is 0
153
- ##
154
- def get_interval(item, formatted: true, record: true)
155
- if item.interval
156
- seconds = item.interval
157
- record_tag_times(item, seconds) if record
158
- return seconds.positive? ? seconds : false unless formatted
138
+ ##
139
+ ## Gets the interval between entry's start
140
+ ## date and @done date
141
+ ##
142
+ ## @param item [Item] The entry
143
+ ## @param formatted [Boolean] Return human readable
144
+ ## time (default seconds)
145
+ ## @param record [Boolean] Add the interval to the
146
+ ## total for each tag
147
+ ##
148
+ ## @return Interval in seconds, or [d, h, m] array if
149
+ ## formatted is true. False if no end date or
150
+ ## interval is 0
151
+ ##
152
+ def get_interval(item, formatted: true, record: true)
153
+ if item.interval
154
+ seconds = item.interval
155
+ record_tag_times(item, seconds) if record
156
+ return seconds.positive? ? seconds : false unless formatted
159
157
 
160
- return seconds.positive? ? seconds.time_string(format: :clock) : false
161
- end
158
+ return seconds.positive? ? seconds.time_string(format: :clock) : false
159
+ end
162
160
 
163
- false
164
- end##
165
- ## Get total elapsed time for all tags in
166
- ## selection
167
- ##
168
- ## @param format [String] return format (html,
169
- ## json, or text)
170
- ## @param sort_by [Symbol] Sort by :name or :time
171
- ## @param sort_order [Symbol] The sort order (:asc or :desc)
172
- ##
173
- def tag_times(format: :text, sort_by: :time, sort_order: :asc)
174
- return '' if @timers.empty?
161
+ false
162
+ end##
163
+ ## Get total elapsed time for all tags in
164
+ ## selection
165
+ ##
166
+ ## @param format [String] return format (html,
167
+ ## json, or text)
168
+ ## @param sort_by [Symbol] Sort by :name or :time
169
+ ## @param sort_order [Symbol] The sort order (:asc or :desc)
170
+ ##
171
+ def tag_times(format: :text, sort_by: :time, sort_order: :asc)
172
+ return '' if @timers.empty?
175
173
 
176
- max = @timers.keys.sort_by(&:length).reverse[0].length + 1
174
+ max = @timers.keys.sort_by(&:length).reverse[0].length + 1
177
175
 
178
- total = @timers.delete('All')
176
+ total = @timers.delete('All')
179
177
 
180
- tags_data = @timers.delete_if { |_k, v| v.zero? }
181
- sorted_tags_data = if sort_by.normalize_tag_sort == :name
182
- tags_data.sort_by { |k, _v| k }
183
- else
184
- tags_data.sort_by { |_k, v| v }
185
- end
178
+ tags_data = @timers.delete_if { |_k, v| v.zero? }
179
+ sorted_tags_data = if sort_by.normalize_tag_sort == :name
180
+ tags_data.sort_by { |k, _v| k }
181
+ else
182
+ tags_data.sort_by { |_k, v| v }
183
+ end
186
184
 
187
- sorted_tags_data.reverse! if sort_order.normalize_order == :asc
188
- case format
189
- when :html
185
+ sorted_tags_data.reverse! if sort_order.normalize_order == :asc
186
+ case format
187
+ when :html
190
188
 
191
- output = <<EOHEAD
192
- <table>
193
- <caption id="tagtotals">Tag Totals</caption>
194
- <colgroup>
195
- <col style="text-align:left;"/>
196
- <col style="text-align:left;"/>
197
- </colgroup>
198
- <thead>
199
- <tr>
200
- <th style="text-align:left;">project</th>
201
- <th style="text-align:left;">time</th>
202
- </tr>
203
- </thead>
204
- <tbody>
205
- EOHEAD
206
- sorted_tags_data.reverse.each do |k, v|
207
- if v.positive?
208
- output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: :clock)}</td></tr>\n"
209
- end
210
- end
211
- tail = <<EOTAIL
189
+ output = <<EOHEAD
190
+ <table>
191
+ <caption id="tagtotals">Tag Totals</caption>
192
+ <colgroup>
193
+ <col style="text-align:left;"/>
194
+ <col style="text-align:left;"/>
195
+ </colgroup>
196
+ <thead>
212
197
  <tr>
213
- <td style="text-align:left;" colspan="2"></td>
198
+ <th style="text-align:left;">project</th>
199
+ <th style="text-align:left;">time</th>
214
200
  </tr>
215
- </tbody>
216
- <tfoot>
217
- <tr>
218
- <td style="text-align:left;"><strong>Total</strong></td>
219
- <td style="text-align:left;">#{total.time_string(format: :clock)}</td>
220
- </tr>
221
- </tfoot>
222
- </table>
223
- EOTAIL
224
- output + tail
225
- when :markdown
226
- pad = sorted_tags_data.map { |k, _| k }.group_by(&:size).max.last[0].length
227
- pad = 7 if pad < 7
228
- output = <<~EOHEADER
229
- | #{' ' * (pad - 7)}project | time |
230
- | #{'-' * (pad - 1)}: | :------- |
231
- EOHEADER
232
- sorted_tags_data.reverse.each do |k, v|
233
- if v.positive?
234
- output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)} |\n"
235
- end
201
+ </thead>
202
+ <tbody>
203
+ EOHEAD
204
+ sorted_tags_data.reverse.each do |k, v|
205
+ if v.positive?
206
+ output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: :clock)}</td></tr>\n"
236
207
  end
237
- tail = '[Tag Totals]'
238
- output + tail
239
- when :json
240
- output = []
241
- sorted_tags_data.reverse.each do |k, v|
242
- output << {
243
- 'tag' => k,
244
- 'seconds' => v,
245
- 'formatted' => v.time_string(format: :clock)
246
- }
208
+ end
209
+ tail = <<EOTAIL
210
+ <tr>
211
+ <td style="text-align:left;" colspan="2"></td>
212
+ </tr>
213
+ </tbody>
214
+ <tfoot>
215
+ <tr>
216
+ <td style="text-align:left;"><strong>Total</strong></td>
217
+ <td style="text-align:left;">#{total.time_string(format: :clock)}</td>
218
+ </tr>
219
+ </tfoot>
220
+ </table>
221
+ EOTAIL
222
+ output + tail
223
+ when :markdown
224
+ pad = sorted_tags_data.map { |k, _| k }.group_by(&:size).max.last[0].length
225
+ pad = 7 if pad < 7
226
+ output = <<~EOHEADER
227
+ | #{' ' * (pad - 7)}project | time |
228
+ | #{'-' * (pad - 1)}: | :------- |
229
+ EOHEADER
230
+ sorted_tags_data.reverse.each do |k, v|
231
+ if v.positive?
232
+ output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)} |\n"
247
233
  end
248
- output
249
- when :human
250
- output = []
251
- sorted_tags_data.reverse.each do |k, v|
252
- spacer = ''
253
- (max - k.length).times do
254
- spacer += ' '
255
- end
256
- output.push("┃ #{spacer}#{k}:#{v.time_string(format: :hm)} ┃")
234
+ end
235
+ tail = '[Tag Totals]'
236
+ output + tail
237
+ when :json
238
+ output = []
239
+ sorted_tags_data.reverse.each do |k, v|
240
+ output << {
241
+ 'tag' => k,
242
+ 'seconds' => v,
243
+ 'formatted' => v.time_string(format: :clock)
244
+ }
245
+ end
246
+ output
247
+ when :human
248
+ output = []
249
+ sorted_tags_data.reverse.each do |k, v|
250
+ spacer = ''
251
+ (max - k.length).times do
252
+ spacer += ' '
257
253
  end
254
+ output.push("┃ #{spacer}#{k}:#{v.time_string(format: :hm)} ┃")
255
+ end
258
256
 
259
- header = '┏━━ Tag Totals '
260
- (max - 2).times { header += '━' }
261
- header += '┓'
262
- footer = '┗'
263
- (max + 12).times { footer += '━' }
264
- footer += '┛'
265
- divider = '┣'
266
- (max + 12).times { divider += '━' }
267
- divider += '┫'
268
- output = output.empty? ? '' : "\n#{header}\n#{output.join("\n")}"
269
- output += "\n#{divider}"
257
+ header = '┏━━ Tag Totals '
258
+ (max - 2).times { header += '━' }
259
+ header += '┓'
260
+ footer = '┗'
261
+ (max + 12).times { footer += '━' }
262
+ footer += '┛'
263
+ divider = '┣'
264
+ (max + 12).times { divider += '━' }
265
+ divider += '┫'
266
+ output = output.empty? ? '' : "\n#{header}\n#{output.join("\n")}"
267
+ output += "\n#{divider}"
268
+ spacer = ''
269
+ (max - 6).times do
270
+ spacer += ' '
271
+ end
272
+ total_time = total.time_string(format: :hm)
273
+ total = "┃ #{spacer}total: "
274
+ total += total_time
275
+ total += ' ┃'
276
+ output += "\n#{total}"
277
+ output += "\n#{footer}"
278
+ output
279
+ else
280
+ output = []
281
+ sorted_tags_data.reverse.each do |k, v|
270
282
  spacer = ''
271
- (max - 6).times do
283
+ (max - k.length).times do
272
284
  spacer += ' '
273
285
  end
274
- total_time = total.time_string(format: :hm)
275
- total = "┃ #{spacer}total: "
276
- total += total_time
277
- total += ' ┃'
278
- output += "\n#{total}"
279
- output += "\n#{footer}"
280
- output
281
- else
282
- output = []
283
- sorted_tags_data.reverse.each do |k, v|
284
- spacer = ''
285
- (max - k.length).times do
286
- spacer += ' '
287
- end
288
- output.push("#{k}:#{spacer}#{v.time_string(format: :clock)}")
289
- end
290
-
291
- output = output.empty? ? '' : "\n--- Tag Totals ---\n#{output.join("\n")}"
292
- output += "\n\nTotal tracked: #{total.time_string(format: :clock)}\n"
293
- output
286
+ output.push("#{k}:#{spacer}#{v.time_string(format: :clock)}")
294
287
  end
288
+
289
+ output = output.empty? ? '' : "\n--- Tag Totals ---\n#{output.join("\n")}"
290
+ output += "\n\nTotal tracked: #{total.time_string(format: :clock)}\n"
291
+ output
295
292
  end
293
+ end
296
294
 
297
- ##
298
- ## Gets the interval between entry's start
299
- ## date and @done date
300
- ##
301
- ## @param item [Item] The entry
302
- ## @param formatted [Boolean] Return human readable
303
- ## time (default seconds)
304
- ## @param record [Boolean] Add the interval to the
305
- ## total for each tag
306
- ##
307
- ## @return Interval in seconds, or [d, h, m] array if
308
- ## formatted is true. False if no end date or
309
- ## interval is 0
310
- ##
311
- def get_interval(item, formatted: true, record: true)
312
- if item.interval
313
- seconds = item.interval
314
- record_tag_times(item, seconds) if record
315
- return seconds.positive? ? seconds : false unless formatted
295
+ ##
296
+ ## Gets the interval between entry's start
297
+ ## date and @done date
298
+ ##
299
+ ## @param item [Item] The entry
300
+ ## @param formatted [Boolean] Return human readable
301
+ ## time (default seconds)
302
+ ## @param record [Boolean] Add the interval to the
303
+ ## total for each tag
304
+ ##
305
+ ## @return Interval in seconds, or [d, h, m] array if
306
+ ## formatted is true. False if no end date or
307
+ ## interval is 0
308
+ ##
309
+ def get_interval(item, formatted: true, record: true)
310
+ if item.interval
311
+ seconds = item.interval
312
+ record_tag_times(item, seconds) if record
313
+ return seconds.positive? ? seconds : false unless formatted
316
314
 
317
- return seconds.positive? ? seconds.time_string(format: :clock) : false
318
- end
319
-
320
- false
315
+ return seconds.positive? ? seconds.time_string(format: :clock) : false
321
316
  end
322
317
 
323
- private
318
+ false
319
+ end
324
320
 
325
- ##
326
- ## Record times for item tags
327
- ##
328
- ## @param item [Item] The item to record
329
- ##
330
- def record_tag_times(item, seconds)
331
- item_hash = "#{item.date.strftime('%s')}#{item.title}#{item.section}"
332
- return if @recorded_items.include?(item_hash)
333
- item.title.scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
334
- k = m[0] == 'done' ? 'All' : m[0].downcase
335
- if @timers.key?(k)
336
- @timers[k] += seconds
337
- else
338
- @timers[k] = seconds
339
- end
340
- @recorded_items.push(item_hash)
321
+ private
322
+
323
+ ##
324
+ ## Record times for item tags
325
+ ##
326
+ ## @param item [Item] The item to record
327
+ ##
328
+ def record_tag_times(item, seconds)
329
+ item_hash = "#{item.date.strftime('%s')}#{item.title}#{item.section}"
330
+ return if @recorded_items.include?(item_hash)
331
+ item.title.scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
332
+ k = m[0] == 'done' ? 'All' : m[0].downcase
333
+ if @timers.key?(k)
334
+ @timers[k] += seconds
335
+ else
336
+ @timers[k] = seconds
341
337
  end
338
+ @recorded_items.push(item_hash)
342
339
  end
343
340
  end
344
341
  end