doing 2.1.99 → 2.1.103
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/CHANGELOG.md +48 -0
- data/Gemfile.lock +1 -1
- data/README.md +8 -2
- data/bin/commands/view.rb +5 -0
- data/bin/doing +92 -2
- data/docs/doc/Array.html +3 -3
- data/docs/doc/BooleanTermParser/Clause.html +3 -3
- data/docs/doc/BooleanTermParser/Operator.html +3 -3
- data/docs/doc/BooleanTermParser/Query.html +3 -3
- data/docs/doc/BooleanTermParser/QueryParser.html +3 -3
- data/docs/doc/BooleanTermParser/QueryTransformer.html +3 -3
- data/docs/doc/BooleanTermParser.html +3 -3
- data/docs/doc/Doing/ArrayCleanup.html +3 -3
- data/docs/doc/Doing/ArrayNestedHash.html +3 -3
- data/docs/doc/Doing/ArrayTags.html +9 -9
- data/docs/doc/Doing/ByDayExport.html +3 -3
- data/docs/doc/Doing/CSVExport.html +3 -3
- data/docs/doc/Doing/CalendarImport.html +3 -3
- data/docs/doc/Doing/Change.html +3 -3
- data/docs/doc/Doing/Changes.html +3 -3
- data/docs/doc/Doing/ChronifyArray.html +3 -3
- data/docs/doc/Doing/ChronifyNumeric.html +3 -3
- data/docs/doc/Doing/ChronifyString.html +6 -6
- data/docs/doc/Doing/Color.html +16 -16
- data/docs/doc/Doing/Completion/BashCompletions.html +3 -3
- data/docs/doc/Doing/Completion/FigCompletions.html +3 -3
- data/docs/doc/Doing/Completion/FishCompletions.html +3 -3
- data/docs/doc/Doing/Completion/StringUtils.html +3 -3
- data/docs/doc/Doing/Completion/ZshCompletions.html +3 -3
- data/docs/doc/Doing/Completion.html +3 -3
- data/docs/doc/Doing/Configuration.html +3 -3
- data/docs/doc/Doing/DayOneRenderer.html +3 -3
- data/docs/doc/Doing/DayoneExport.html +3 -3
- data/docs/doc/Doing/DoingExport.html +3 -3
- data/docs/doc/Doing/DoingImport.html +3 -3
- data/docs/doc/Doing/Entry.html +3 -3
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +3 -3
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +3 -3
- data/docs/doc/Doing/Errors/DoingStandardError.html +3 -3
- data/docs/doc/Doing/Errors/EmptyInput.html +3 -3
- data/docs/doc/Doing/Errors/HistoryLimitError.html +3 -3
- data/docs/doc/Doing/Errors/InvalidPlugin.html +3 -3
- data/docs/doc/Doing/Errors/MissingBackupFile.html +3 -3
- data/docs/doc/Doing/Errors/NoResults.html +3 -3
- data/docs/doc/Doing/Errors/PluginException.html +3 -3
- data/docs/doc/Doing/Errors/UserCancelled.html +3 -3
- data/docs/doc/Doing/Errors/WrongCommand.html +3 -3
- data/docs/doc/Doing/Errors.html +3 -3
- data/docs/doc/Doing/HTMLExport.html +3 -3
- data/docs/doc/Doing/Hooks.html +3 -3
- data/docs/doc/Doing/Item.html +3 -3
- data/docs/doc/Doing/ItemDates.html +3 -3
- data/docs/doc/Doing/ItemQuery.html +3 -3
- data/docs/doc/Doing/ItemState.html +3 -3
- data/docs/doc/Doing/ItemTags.html +3 -3
- data/docs/doc/Doing/Items.html +3 -3
- data/docs/doc/Doing/JSONExport.html +3 -3
- data/docs/doc/Doing/JSONImport.html +3 -3
- data/docs/doc/Doing/Logger.html +3 -3
- data/docs/doc/Doing/MarkdownExport.html +3 -3
- data/docs/doc/Doing/Note.html +3 -3
- data/docs/doc/Doing/Pager.html +3 -3
- data/docs/doc/Doing/Plugins.html +3 -3
- data/docs/doc/Doing/Prompt.html +3 -3
- data/docs/doc/Doing/PromptChoose.html +3 -3
- data/docs/doc/Doing/PromptFZF.html +3 -3
- data/docs/doc/Doing/PromptInput.html +3 -3
- data/docs/doc/Doing/PromptSTD.html +3 -3
- data/docs/doc/Doing/PromptYN.html +3 -3
- data/docs/doc/Doing/Section.html +3 -3
- data/docs/doc/Doing/StringHighlight.html +3 -3
- data/docs/doc/Doing/StringNormalize.html +115 -3
- data/docs/doc/Doing/StringQuery.html +4 -4
- data/docs/doc/Doing/StringTags.html +3 -3
- data/docs/doc/Doing/StringTransform.html +3 -3
- data/docs/doc/Doing/StringTruncate.html +3 -3
- data/docs/doc/Doing/StringURL.html +3 -3
- data/docs/doc/Doing/SymbolNormalize.html +37 -3
- data/docs/doc/Doing/TaskPaperExport.html +3 -3
- data/docs/doc/Doing/TemplateExport.html +3 -3
- data/docs/doc/Doing/TemplateString.html +4 -4
- data/docs/doc/Doing/TimingImport.html +3 -3
- data/docs/doc/Doing/Types.html +13 -3
- data/docs/doc/Doing/Util/Backup.html +3 -3
- data/docs/doc/Doing/Util.html +4 -4
- data/docs/doc/Doing/Version.html +3 -3
- data/docs/doc/Doing/WWID.html +5 -5
- data/docs/doc/Doing.html +4 -4
- data/docs/doc/FalseClass.html +3 -3
- data/docs/doc/GLI/Commands/Help.html +3 -3
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +3 -3
- data/docs/doc/GLI/Commands.html +3 -3
- data/docs/doc/GLI.html +3 -3
- data/docs/doc/Hash.html +4 -4
- data/docs/doc/Numeric.html +3 -3
- data/docs/doc/Object.html +3 -3
- data/docs/doc/PhraseParser/Operator.html +3 -3
- data/docs/doc/PhraseParser/PhraseClause.html +3 -3
- data/docs/doc/PhraseParser/Query.html +3 -3
- data/docs/doc/PhraseParser/QueryParser.html +3 -3
- data/docs/doc/PhraseParser/QueryTransformer.html +3 -3
- data/docs/doc/PhraseParser/TermClause.html +3 -3
- data/docs/doc/PhraseParser.html +3 -3
- data/docs/doc/Status.html +3 -3
- data/docs/doc/String.html +4 -4
- data/docs/doc/Symbol.html +4 -4
- data/docs/doc/Time.html +3 -3
- data/docs/doc/TrueClass.html +3 -3
- data/docs/doc/_index.html +4 -4
- data/docs/doc/class_list.html +3 -6
- data/docs/doc/css/full_list.css +3 -3
- data/docs/doc/css/style.css +15 -8
- data/docs/doc/file.README.html +13 -5
- data/docs/doc/file_list.html +2 -5
- data/docs/doc/frames.html +1 -1
- data/docs/doc/index.html +13 -5
- data/docs/doc/js/app.js +267 -348
- data/docs/doc/js/full_list.js +24 -52
- data/docs/doc/method_list.html +403 -382
- data/docs/doc/top-level-namespace.html +3 -3
- data/doing.rdoc +65 -1
- data/lib/completion/_doing.zsh +9 -9
- data/lib/completion/doing.bash +16 -16
- data/lib/completion/doing.fish +8 -0
- data/lib/completion/doing.ts +90 -0
- data/lib/doing/add_options.rb +4 -0
- data/lib/doing/normalize.rb +25 -0
- data/lib/doing/plugins/export/dayone_export.rb +2 -1
- data/lib/doing/plugins/export/html_export.rb +1 -1
- data/lib/doing/plugins/export/json_export.rb +2 -1
- data/lib/doing/plugins/export/markdown_export.rb +2 -1
- data/lib/doing/plugins/export/template_export.rb +2 -1
- data/lib/doing/types.rb +2 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid/timers.rb +146 -94
- data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
- metadata +2 -2
data/lib/doing/normalize.rb
CHANGED
|
@@ -161,6 +161,27 @@ module Doing
|
|
|
161
161
|
replace normalize_bool(default)
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
+
##
|
|
165
|
+
## Convert totals-by string to a symbol
|
|
166
|
+
##
|
|
167
|
+
## @return Symbol :tags or :section
|
|
168
|
+
##
|
|
169
|
+
def normalize_totals_by(default = :tags)
|
|
170
|
+
case self
|
|
171
|
+
when /^t/i
|
|
172
|
+
:tags
|
|
173
|
+
when /^[sp]/i
|
|
174
|
+
:section
|
|
175
|
+
else
|
|
176
|
+
default.is_a?(Symbol) ? default : default.normalize_totals_by
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
## @see #normalize_totals_by
|
|
181
|
+
def normalize_totals_by!(default = :tags)
|
|
182
|
+
replace normalize_totals_by(default)
|
|
183
|
+
end
|
|
184
|
+
|
|
164
185
|
##
|
|
165
186
|
## Adds ?: to any parentheticals in a regular expression
|
|
166
187
|
## to avoid match groups
|
|
@@ -217,6 +238,10 @@ module Doing
|
|
|
217
238
|
def normalize_matching(default = :pattern)
|
|
218
239
|
to_s.normalize_matching(default)
|
|
219
240
|
end
|
|
241
|
+
|
|
242
|
+
def normalize_totals_by(default = :tags)
|
|
243
|
+
to_s.normalize_totals_by(default)
|
|
244
|
+
end
|
|
220
245
|
end
|
|
221
246
|
end
|
|
222
247
|
|
|
@@ -70,7 +70,7 @@ module Doing
|
|
|
70
70
|
self.template('css')
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
|
73
|
+
totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by: opt[:sort_tags], sort_order: opt[:tag_order], by: opt[:by]) : ''
|
|
74
74
|
engine = Haml::Engine.new(template)
|
|
75
75
|
Doing.logger.debug('HTML Export:', "#{items_out.count} items output to HTML")
|
|
76
76
|
@out = engine.render(Object.new,
|
|
@@ -104,7 +104,8 @@ module Doing
|
|
|
104
104
|
'items' => items_out,
|
|
105
105
|
'timers' => wwid.tag_times(format: :json,
|
|
106
106
|
sort_by: opt[:sort_tags],
|
|
107
|
-
sort_order: opt[:tag_order]
|
|
107
|
+
sort_order: opt[:tag_order],
|
|
108
|
+
by: opt[:by])
|
|
108
109
|
})
|
|
109
110
|
when 'timeline'
|
|
110
111
|
template = <<~EOTEMPLATE
|
data/lib/doing/types.rb
CHANGED
|
@@ -6,6 +6,7 @@ module Doing
|
|
|
6
6
|
REGEX_TAG_SORT = /^(?:n(?:ame)?|t(?:ime)?)$/i.freeze
|
|
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
|
+
REGEX_TOTALS_BY = /^(?:t(?:ag(?:s)?)?|s(?:ec(?:tion|tions)?)?|p(?:roj(?:ect|ects)?)?)$/i.freeze
|
|
9
10
|
REGEX_VALUE_QUERY = /^(?:!)?@?(?:\S+) +(?:!?[<>=][=*]?|[$*^]=) +(?:.*?)$/.freeze
|
|
10
11
|
REGEX_CLOCK = '(?:\d{1,2}+(?::\d{1,2}+)?(?: *(?:am|pm))?|midnight|noon)'
|
|
11
12
|
REGEX_TIME = /^#{REGEX_CLOCK}$/i.freeze
|
|
@@ -30,6 +31,7 @@ module Doing
|
|
|
30
31
|
OrderSymbol = Class.new(Symbol)
|
|
31
32
|
TagArray = Class.new(Array)
|
|
32
33
|
TagSortSymbol = Class.new(Symbol)
|
|
34
|
+
TotalsBySymbol = Class.new(Symbol)
|
|
33
35
|
TemplateName = Class.new(String)
|
|
34
36
|
end
|
|
35
37
|
end
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid/timers.rb
CHANGED
|
@@ -11,12 +11,12 @@ 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)
|
|
15
|
-
|
|
14
|
+
def tag_times(format: :text, sort_by: :time, sort_order: :asc, by: nil)
|
|
15
|
+
groupings = normalize_totals_groupings(by)
|
|
16
|
+
totals = collect_totals(groupings)
|
|
17
|
+
return '' if totals.empty?
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
timers_snapshot = @timers.dup
|
|
19
|
+
timers_snapshot = totals[:tags] || {}
|
|
20
20
|
|
|
21
21
|
budgets = Doing.setting('budgets', {}) || {}
|
|
22
22
|
budgets = budgets.transform_keys { |k| k.to_s.downcase }
|
|
@@ -44,24 +44,123 @@ module Doing
|
|
|
44
44
|
budgets_total += remaining
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
outputs = groupings.map do |group|
|
|
48
|
+
timer_data = totals[group]
|
|
49
|
+
next nil unless timer_data
|
|
50
|
+
|
|
51
|
+
render_totals_group(
|
|
52
|
+
format: format,
|
|
53
|
+
group: group,
|
|
54
|
+
timer_data: timer_data,
|
|
55
|
+
sort_by: sort_by,
|
|
56
|
+
sort_order: sort_order,
|
|
57
|
+
remaining_map: remaining_map,
|
|
58
|
+
budgets: budgets,
|
|
59
|
+
budgets_total: budgets_total,
|
|
60
|
+
budget_fmt: budget_fmt
|
|
61
|
+
)
|
|
62
|
+
end.compact
|
|
63
|
+
|
|
64
|
+
return '' if outputs.empty?
|
|
65
|
+
return outputs.first if format == :json && outputs.length == 1
|
|
66
|
+
|
|
67
|
+
format == :json ? outputs.to_h : outputs.join(format == :human ? "\n" : '')
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
##
|
|
71
|
+
## Gets the interval between entry's start
|
|
72
|
+
## date and @done date
|
|
73
|
+
##
|
|
74
|
+
## @param item [Item] The entry
|
|
75
|
+
## @param formatted [Boolean] Return human readable
|
|
76
|
+
## time (default seconds)
|
|
77
|
+
## @param record [Boolean] Add the interval to the
|
|
78
|
+
## total for each tag
|
|
79
|
+
##
|
|
80
|
+
## @return Interval in seconds, or [d, h, m] array if
|
|
81
|
+
## formatted is true. False if no end date or
|
|
82
|
+
## interval is 0
|
|
83
|
+
##
|
|
84
|
+
def get_interval(item, formatted: true, record: true)
|
|
85
|
+
if item.interval
|
|
86
|
+
seconds = item.interval
|
|
87
|
+
record_tag_times(item, seconds) if record
|
|
88
|
+
return seconds.positive? ? seconds : false unless formatted
|
|
89
|
+
|
|
90
|
+
return seconds.positive? ? seconds.time_string(format: :clock) : false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
false
|
|
94
|
+
end
|
|
48
95
|
|
|
49
|
-
|
|
96
|
+
private
|
|
50
97
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
98
|
+
##
|
|
99
|
+
## Record times for item tags
|
|
100
|
+
##
|
|
101
|
+
## @param item [Item] The item to record
|
|
102
|
+
##
|
|
103
|
+
def record_tag_times(item, seconds)
|
|
104
|
+
item_hash = "#{item.date.strftime('%s')}#{item.title}#{item.section}"
|
|
105
|
+
return if @recorded_items.include?(item_hash)
|
|
106
|
+
|
|
107
|
+
@section_timers ||= {}
|
|
108
|
+
section = item.section.to_s.strip
|
|
109
|
+
section = 'Unknown' if section.empty?
|
|
110
|
+
@section_timers['All'] = @section_timers.fetch('All', 0) + seconds
|
|
111
|
+
@section_timers[section] = @section_timers.fetch(section, 0) + seconds
|
|
112
|
+
|
|
113
|
+
item.title.scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
|
|
114
|
+
k = m[0] == 'done' ? 'All' : m[0].downcase
|
|
115
|
+
if @timers.key?(k)
|
|
116
|
+
@timers[k] += seconds
|
|
117
|
+
else
|
|
118
|
+
@timers[k] = seconds
|
|
119
|
+
end
|
|
120
|
+
@recorded_items.push(item_hash)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def normalize_totals_groupings(by)
|
|
125
|
+
return [:tags] if by.nil? || (by.respond_to?(:empty?) && by.empty?)
|
|
126
|
+
|
|
127
|
+
Array(by).map { |v| v.normalize_totals_by(:tags) }.uniq
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def collect_totals(groupings)
|
|
131
|
+
totals = {}
|
|
132
|
+
totals[:tags] = @timers.dup if groupings.include?(:tags) && @timers.good?
|
|
133
|
+
totals[:section] = (@section_timers || {}).dup if groupings.include?(:section) && @section_timers.good?
|
|
134
|
+
totals
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def sort_totals_data(data, sort_by:, sort_order:)
|
|
138
|
+
sorted = if sort_by.normalize_tag_sort == :name
|
|
139
|
+
data.sort_by { |k, _v| k }
|
|
140
|
+
else
|
|
141
|
+
data.sort_by { |_k, v| v }
|
|
142
|
+
end
|
|
143
|
+
sorted.reverse! if sort_order.normalize_order == :asc
|
|
144
|
+
sorted
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def render_totals_group(format:, group:, timer_data:, sort_by:, sort_order:, remaining_map:, budgets:, budgets_total:, budget_fmt:)
|
|
148
|
+
timer_data = timer_data.dup
|
|
149
|
+
timer_data.delete('meanwhile')
|
|
150
|
+
|
|
151
|
+
max = timer_data.keys.sort_by(&:length).reverse[0].length + 1
|
|
152
|
+
total = timer_data.delete('All').to_i
|
|
153
|
+
group_data = timer_data.delete_if { |_k, v| v.zero? }
|
|
154
|
+
sorted_data = sort_totals_data(group_data, sort_by: sort_by, sort_order: sort_order)
|
|
155
|
+
|
|
156
|
+
title = group == :section ? 'Section Totals' : 'Tag Totals'
|
|
157
|
+
label = group == :section ? 'section' : 'tag'
|
|
57
158
|
|
|
58
|
-
sorted_tags_data.reverse! if sort_order.normalize_order == :asc
|
|
59
159
|
case format
|
|
60
160
|
when :html
|
|
61
|
-
|
|
62
161
|
output = <<EOHEAD
|
|
63
162
|
<table>
|
|
64
|
-
<caption id="
|
|
163
|
+
<caption id="#{group}totals">#{title}</caption>
|
|
65
164
|
<colgroup>
|
|
66
165
|
<col style="text-align:left;"/>
|
|
67
166
|
<col style="text-align:left;"/>
|
|
@@ -74,16 +173,16 @@ module Doing
|
|
|
74
173
|
</thead>
|
|
75
174
|
<tbody>
|
|
76
175
|
EOHEAD
|
|
77
|
-
|
|
176
|
+
sorted_data.reverse.each do |k, v|
|
|
78
177
|
next unless v.positive?
|
|
79
178
|
|
|
80
179
|
budget_str = ''
|
|
81
|
-
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
180
|
+
if group == :tags && remaining_map.key?(k) && remaining_map[k].positive?
|
|
82
181
|
budget_str = " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
83
182
|
end
|
|
84
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"
|
|
85
184
|
end
|
|
86
|
-
|
|
185
|
+
output += <<EOTAIL
|
|
87
186
|
<tr>
|
|
88
187
|
<td style="text-align:left;" colspan="2"></td>
|
|
89
188
|
</tr>
|
|
@@ -91,52 +190,54 @@ EOHEAD
|
|
|
91
190
|
<tfoot>
|
|
92
191
|
<tr>
|
|
93
192
|
<td style="text-align:left;"><strong>Total</strong></td>
|
|
94
|
-
<td style="text-align:left;">#{total.time_string(format: :clock)}#{" (total budgets left #{budget_fmt.call(budgets_total)})"
|
|
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>
|
|
95
194
|
</tr>
|
|
96
195
|
</tfoot>
|
|
97
196
|
</table>
|
|
98
197
|
EOTAIL
|
|
99
|
-
output
|
|
198
|
+
output
|
|
100
199
|
when :markdown
|
|
101
|
-
pad =
|
|
200
|
+
pad = sorted_data.map { |k, _| k }.group_by(&:size).max.last[0].length
|
|
102
201
|
pad = 7 if pad < 7
|
|
103
202
|
output = <<~EOHEADER
|
|
104
203
|
| #{' ' * (pad - 7)}project | time |
|
|
105
204
|
| #{'-' * (pad - 1)}: | :------- |
|
|
106
205
|
EOHEADER
|
|
107
|
-
|
|
206
|
+
sorted_data.reverse.each do |k, v|
|
|
108
207
|
next unless v.positive?
|
|
109
208
|
|
|
110
209
|
budget_str = ''
|
|
111
|
-
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
210
|
+
if group == :tags && remaining_map.key?(k) && remaining_map[k].positive?
|
|
112
211
|
budget_str = " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
113
212
|
end
|
|
114
213
|
output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)}#{budget_str} |\n"
|
|
115
214
|
end
|
|
116
|
-
|
|
117
|
-
output + tail
|
|
215
|
+
output + "[#{title}]"
|
|
118
216
|
when :json
|
|
119
217
|
output = []
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
218
|
+
sorted_data.reverse.each do |k, v|
|
|
219
|
+
row = {
|
|
220
|
+
label => k,
|
|
123
221
|
'seconds' => v,
|
|
124
|
-
'formatted' => v.time_string(format: :clock)
|
|
125
|
-
'budget' => budgets[k],
|
|
126
|
-
'remaining' => remaining_map[k],
|
|
127
|
-
'remaining_formatted' => (remaining_map[k] && remaining_map[k].positive? ? budget_fmt.call(remaining_map[k]) : nil)
|
|
222
|
+
'formatted' => v.time_string(format: :clock)
|
|
128
223
|
}
|
|
224
|
+
if group == :tags
|
|
225
|
+
row['budget'] = budgets[k]
|
|
226
|
+
row['remaining'] = remaining_map[k]
|
|
227
|
+
row['remaining_formatted'] = (remaining_map[k] && remaining_map[k].positive? ? budget_fmt.call(remaining_map[k]) : nil)
|
|
228
|
+
end
|
|
229
|
+
output << row
|
|
129
230
|
end
|
|
130
|
-
output
|
|
231
|
+
[group, output]
|
|
131
232
|
when :human
|
|
132
233
|
output = []
|
|
133
|
-
|
|
234
|
+
sorted_data.reverse.each do |k, v|
|
|
134
235
|
spacer = ''
|
|
135
236
|
(max - k.length).times do
|
|
136
237
|
spacer += ' '
|
|
137
238
|
end
|
|
138
239
|
line = "┃ #{spacer}#{k}:#{v.time_string(format: :hm)}"
|
|
139
|
-
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
240
|
+
if group == :tags && remaining_map.key?(k) && remaining_map[k].positive?
|
|
140
241
|
line += " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
141
242
|
end
|
|
142
243
|
line += ' ┃'
|
|
@@ -144,7 +245,7 @@ EOTAIL
|
|
|
144
245
|
end
|
|
145
246
|
|
|
146
247
|
total_content = "┃ #{' ' * (max - 6)}total: #{total.time_string(format: :hm)}"
|
|
147
|
-
total_content += " (total budgets left #{budget_fmt.call(budgets_total)})" if budgets_total.positive?
|
|
248
|
+
total_content += " (total budgets left #{budget_fmt.call(budgets_total)})" if group == :tags && budgets_total.positive?
|
|
148
249
|
total_content += ' ┃'
|
|
149
250
|
max_line_len = (output + [total_content]).map(&:length).max
|
|
150
251
|
|
|
@@ -154,8 +255,9 @@ EOTAIL
|
|
|
154
255
|
end
|
|
155
256
|
output = output.map { |l| pad_line.call(l) }
|
|
156
257
|
|
|
157
|
-
header =
|
|
158
|
-
|
|
258
|
+
header = "┏━━ #{title} "
|
|
259
|
+
# Keep top border width aligned with body/footer width.
|
|
260
|
+
[(max_line_len - title.length - 6), 0].max.times { header += '━' }
|
|
159
261
|
header += '┓'
|
|
160
262
|
footer = '┗'
|
|
161
263
|
[(max_line_len - 2), 0].max.times { footer += '━' }
|
|
@@ -172,7 +274,7 @@ EOTAIL
|
|
|
172
274
|
total_time = total.time_string(format: :hm)
|
|
173
275
|
total_line = "┃ #{spacer}total: "
|
|
174
276
|
total_line += total_time
|
|
175
|
-
total_line += " (total budgets left #{budget_fmt.call(budgets_total)})" if budgets_total.positive?
|
|
277
|
+
total_line += " (total budgets left #{budget_fmt.call(budgets_total)})" if group == :tags && budgets_total.positive?
|
|
176
278
|
total_line += ' ┃'
|
|
177
279
|
total_line = pad_line.call(total_line)
|
|
178
280
|
output += "\n#{total_line}"
|
|
@@ -180,74 +282,24 @@ EOTAIL
|
|
|
180
282
|
output
|
|
181
283
|
else
|
|
182
284
|
output = []
|
|
183
|
-
|
|
285
|
+
sorted_data.reverse.each do |k, v|
|
|
184
286
|
spacer = ''
|
|
185
287
|
(max - k.length).times do
|
|
186
288
|
spacer += ' '
|
|
187
289
|
end
|
|
188
290
|
line = "#{k}:#{spacer}#{v.time_string(format: :clock)}"
|
|
189
|
-
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
291
|
+
if group == :tags && remaining_map.key?(k) && remaining_map[k].positive?
|
|
190
292
|
line += " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
191
293
|
end
|
|
192
294
|
output.push(line)
|
|
193
295
|
end
|
|
194
296
|
|
|
195
|
-
output = output.empty? ? '' : "\n---
|
|
297
|
+
output = output.empty? ? '' : "\n--- #{title} ---\n#{output.join("\n")}"
|
|
196
298
|
output += "\n\nTotal tracked: #{total.time_string(format: :clock)}"
|
|
197
|
-
if budgets_total.positive?
|
|
198
|
-
output += " (total budgets left #{budget_fmt.call(budgets_total)})"
|
|
199
|
-
end
|
|
299
|
+
output += " (total budgets left #{budget_fmt.call(budgets_total)})" if group == :tags && budgets_total.positive?
|
|
200
300
|
output += "\n"
|
|
201
301
|
output
|
|
202
302
|
end
|
|
203
303
|
end
|
|
204
|
-
|
|
205
|
-
##
|
|
206
|
-
## Gets the interval between entry's start
|
|
207
|
-
## date and @done date
|
|
208
|
-
##
|
|
209
|
-
## @param item [Item] The entry
|
|
210
|
-
## @param formatted [Boolean] Return human readable
|
|
211
|
-
## time (default seconds)
|
|
212
|
-
## @param record [Boolean] Add the interval to the
|
|
213
|
-
## total for each tag
|
|
214
|
-
##
|
|
215
|
-
## @return Interval in seconds, or [d, h, m] array if
|
|
216
|
-
## formatted is true. False if no end date or
|
|
217
|
-
## interval is 0
|
|
218
|
-
##
|
|
219
|
-
def get_interval(item, formatted: true, record: true)
|
|
220
|
-
if item.interval
|
|
221
|
-
seconds = item.interval
|
|
222
|
-
record_tag_times(item, seconds) if record
|
|
223
|
-
return seconds.positive? ? seconds : false unless formatted
|
|
224
|
-
|
|
225
|
-
return seconds.positive? ? seconds.time_string(format: :clock) : false
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
false
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
private
|
|
232
|
-
|
|
233
|
-
##
|
|
234
|
-
## Record times for item tags
|
|
235
|
-
##
|
|
236
|
-
## @param item [Item] The item to record
|
|
237
|
-
##
|
|
238
|
-
def record_tag_times(item, seconds)
|
|
239
|
-
item_hash = "#{item.date.strftime('%s')}#{item.title}#{item.section}"
|
|
240
|
-
return if @recorded_items.include?(item_hash)
|
|
241
|
-
|
|
242
|
-
item.title.scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
|
|
243
|
-
k = m[0] == 'done' ? 'All' : m[0].downcase
|
|
244
|
-
if @timers.key?(k)
|
|
245
|
-
@timers[k] += seconds
|
|
246
|
-
else
|
|
247
|
-
@timers[k] = seconds
|
|
248
|
-
end
|
|
249
|
-
@recorded_items.push(item_hash)
|
|
250
|
-
end
|
|
251
|
-
end
|
|
252
304
|
end
|
|
253
305
|
end
|
|
@@ -73,7 +73,7 @@ module Doing
|
|
|
73
73
|
self.template('wiki_css')
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
-
totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by: opt[:sort_tags], sort_order: opt[:tag_order]) : ''
|
|
76
|
+
totals = opt[:totals] ? wwid.tag_times(format: :html, sort_by: opt[:sort_tags], sort_order: opt[:tag_order], by: opt[:by]) : ''
|
|
77
77
|
engine = Haml::Engine.new(template)
|
|
78
78
|
Doing.logger.debug('Wiki Export:', "#{items_out.count} items output to #{variables[:page_title]} wiki page")
|
|
79
79
|
@out = engine.render(Object.new,
|
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.
|
|
4
|
+
version: 2.1.103
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brett Terpstra
|
|
@@ -944,7 +944,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
944
944
|
- !ruby/object:Gem::Version
|
|
945
945
|
version: '0'
|
|
946
946
|
requirements: []
|
|
947
|
-
rubygems_version: 4.0.
|
|
947
|
+
rubygems_version: 4.0.6
|
|
948
948
|
specification_version: 4
|
|
949
949
|
summary: A command line tool for managing What Was I Doing reminders
|
|
950
950
|
test_files: []
|