doing 2.1.93 → 2.1.97
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 +29 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/bin/commands/budget.rb +85 -0
- data/bin/doing +1 -1
- data/docs/doc/Array.html +1 -1
- data/docs/doc/BooleanTermParser/Clause.html +1 -1
- data/docs/doc/BooleanTermParser/Operator.html +1 -1
- data/docs/doc/BooleanTermParser/Query.html +1 -1
- data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
- data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/ArrayCleanup.html +1 -1
- data/docs/doc/Doing/ArrayNestedHash.html +1 -1
- data/docs/doc/Doing/ArrayTags.html +1 -1
- data/docs/doc/Doing/ByDayExport.html +1 -1
- data/docs/doc/Doing/CSVExport.html +1 -1
- data/docs/doc/Doing/CalendarImport.html +1 -1
- data/docs/doc/Doing/Change.html +1 -1
- data/docs/doc/Doing/Changes.html +1 -1
- data/docs/doc/Doing/ChronifyArray.html +1 -1
- data/docs/doc/Doing/ChronifyNumeric.html +1 -1
- data/docs/doc/Doing/ChronifyString.html +1 -1
- data/docs/doc/Doing/Color.html +1 -1
- data/docs/doc/Doing/Completion/BashCompletions.html +1 -1
- data/docs/doc/Doing/Completion/FigCompletions.html +1 -1
- data/docs/doc/Doing/Completion/FishCompletions.html +1 -1
- data/docs/doc/Doing/Completion/StringUtils.html +1 -1
- data/docs/doc/Doing/Completion/ZshCompletions.html +1 -1
- data/docs/doc/Doing/Completion.html +1 -1
- data/docs/doc/Doing/Configuration.html +3 -1
- data/docs/doc/Doing/DayOneRenderer.html +1 -1
- data/docs/doc/Doing/DayoneExport.html +1 -1
- data/docs/doc/Doing/DoingExport.html +1 -1
- data/docs/doc/Doing/DoingImport.html +1 -1
- data/docs/doc/Doing/Entry.html +1 -1
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
- data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
- data/docs/doc/Doing/Errors/HistoryLimitError.html +1 -1
- data/docs/doc/Doing/Errors/InvalidPlugin.html +1 -1
- data/docs/doc/Doing/Errors/MissingBackupFile.html +1 -1
- data/docs/doc/Doing/Errors/NoResults.html +1 -1
- data/docs/doc/Doing/Errors/PluginException.html +1 -1
- data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
- data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/HTMLExport.html +1 -1
- data/docs/doc/Doing/Hooks.html +1 -1
- data/docs/doc/Doing/Item.html +1 -1
- data/docs/doc/Doing/ItemDates.html +1 -1
- data/docs/doc/Doing/ItemQuery.html +1 -1
- data/docs/doc/Doing/ItemState.html +1 -1
- data/docs/doc/Doing/ItemTags.html +1 -1
- data/docs/doc/Doing/Items.html +1 -1
- data/docs/doc/Doing/JSONExport.html +1 -1
- data/docs/doc/Doing/JSONImport.html +1 -1
- data/docs/doc/Doing/Logger.html +1 -1
- data/docs/doc/Doing/MarkdownExport.html +1 -1
- data/docs/doc/Doing/Note.html +1 -1
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +1 -1
- data/docs/doc/Doing/Prompt.html +1 -1
- data/docs/doc/Doing/PromptChoose.html +1 -1
- data/docs/doc/Doing/PromptFZF.html +1 -1
- data/docs/doc/Doing/PromptInput.html +1 -1
- data/docs/doc/Doing/PromptSTD.html +1 -1
- data/docs/doc/Doing/PromptYN.html +1 -1
- data/docs/doc/Doing/Section.html +1 -1
- data/docs/doc/Doing/StringHighlight.html +1 -1
- data/docs/doc/Doing/StringNormalize.html +1 -1
- data/docs/doc/Doing/StringQuery.html +1 -1
- data/docs/doc/Doing/StringTags.html +1 -1
- data/docs/doc/Doing/StringTransform.html +1 -1
- data/docs/doc/Doing/StringTruncate.html +1 -1
- data/docs/doc/Doing/StringURL.html +1 -1
- data/docs/doc/Doing/SymbolNormalize.html +1 -1
- data/docs/doc/Doing/TaskPaperExport.html +1 -1
- data/docs/doc/Doing/TemplateExport.html +1 -1
- data/docs/doc/Doing/TemplateString.html +1 -1
- data/docs/doc/Doing/TimingImport.html +1 -1
- data/docs/doc/Doing/Types.html +1 -1
- data/docs/doc/Doing/Util/Backup.html +1 -1
- data/docs/doc/Doing/Util.html +1 -1
- data/docs/doc/Doing/Version.html +1 -1
- data/docs/doc/Doing/WWID.html +1 -1
- data/docs/doc/Doing.html +2 -2
- data/docs/doc/FalseClass.html +1 -1
- data/docs/doc/GLI/Commands/Help.html +1 -1
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/docs/doc/GLI/Commands.html +1 -1
- data/docs/doc/GLI.html +1 -1
- data/docs/doc/Hash.html +1 -1
- data/docs/doc/Numeric.html +1 -1
- data/docs/doc/Object.html +1 -1
- data/docs/doc/PhraseParser/Operator.html +1 -1
- data/docs/doc/PhraseParser/PhraseClause.html +1 -1
- data/docs/doc/PhraseParser/Query.html +1 -1
- data/docs/doc/PhraseParser/QueryParser.html +1 -1
- data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
- data/docs/doc/PhraseParser/TermClause.html +1 -1
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +1 -1
- data/docs/doc/String.html +1 -1
- data/docs/doc/Symbol.html +1 -1
- data/docs/doc/Time.html +1 -1
- data/docs/doc/TrueClass.html +1 -1
- data/docs/doc/_index.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/top-level-namespace.html +1 -1
- data/doing.rdoc +17 -1
- data/lib/completion/_doing.zsh +4 -0
- data/lib/completion/doing.bash +11 -0
- data/lib/completion/doing.fish +2 -0
- data/lib/completion/doing.ts +14 -0
- data/lib/doing/configuration.rb +4 -2
- data/lib/doing/plugins/export/byday.rb +39 -0
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid/timers.rb +82 -15
- metadata +2 -1
data/docs/doc/_index.html
CHANGED
|
@@ -961,7 +961,7 @@
|
|
|
961
961
|
</div>
|
|
962
962
|
|
|
963
963
|
<div id="footer">
|
|
964
|
-
Generated on
|
|
964
|
+
Generated on Sat Feb 14 06:59:18 2026 by
|
|
965
965
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
966
966
|
0.9.38 (ruby-3.4.4).
|
|
967
967
|
</div>
|
data/docs/doc/file.README.html
CHANGED
|
@@ -71,7 +71,7 @@ you've done.</strong></p>
|
|
|
71
71
|
|
|
72
72
|
<!--README-->
|
|
73
73
|
|
|
74
|
-
<p>The current version of <code>doing</code> is <!--VER-->2.1.
|
|
74
|
+
<p>The current version of <code>doing</code> is <!--VER-->2.1.94<!--END VER-->.</p>
|
|
75
75
|
|
|
76
76
|
<p>Find all of the documentation in the <a href="https://github.com/ttscoff/doing/wiki">doing wiki</a>.</p>
|
|
77
77
|
|
|
@@ -153,7 +153,7 @@ with changes. Please target the <code>develop</code> branch with pull requests.<
|
|
|
153
153
|
</div></div>
|
|
154
154
|
|
|
155
155
|
<div id="footer">
|
|
156
|
-
Generated on
|
|
156
|
+
Generated on Sat Feb 14 06:59:18 2026 by
|
|
157
157
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
158
158
|
0.9.38 (ruby-3.4.4).
|
|
159
159
|
</div>
|
data/docs/doc/index.html
CHANGED
|
@@ -71,7 +71,7 @@ you've done.</strong></p>
|
|
|
71
71
|
|
|
72
72
|
<!--README-->
|
|
73
73
|
|
|
74
|
-
<p>The current version of <code>doing</code> is <!--VER-->2.1.
|
|
74
|
+
<p>The current version of <code>doing</code> is <!--VER-->2.1.94<!--END VER-->.</p>
|
|
75
75
|
|
|
76
76
|
<p>Find all of the documentation in the <a href="https://github.com/ttscoff/doing/wiki">doing wiki</a>.</p>
|
|
77
77
|
|
|
@@ -153,7 +153,7 @@ with changes. Please target the <code>develop</code> branch with pull requests.<
|
|
|
153
153
|
</div></div>
|
|
154
154
|
|
|
155
155
|
<div id="footer">
|
|
156
|
-
Generated on
|
|
156
|
+
Generated on Sat Feb 14 06:59:18 2026 by
|
|
157
157
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
158
158
|
0.9.38 (ruby-3.4.4).
|
|
159
159
|
</div>
|
|
@@ -216,7 +216,7 @@
|
|
|
216
216
|
</div>
|
|
217
217
|
|
|
218
218
|
<div id="footer">
|
|
219
|
-
Generated on
|
|
219
|
+
Generated on Sat Feb 14 06:59:18 2026 by
|
|
220
220
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
221
221
|
0.9.38 (ruby-3.4.4).
|
|
222
222
|
</div>
|
data/doing.rdoc
CHANGED
|
@@ -5,7 +5,7 @@ record of what you've been doing, complete with tag-based time tracking. The
|
|
|
5
5
|
command line tool allows you to add entries, annotate with tags and notes, and
|
|
6
6
|
view your entries with myriad options, with a focus on a "natural" language syntax.
|
|
7
7
|
|
|
8
|
-
v2.1.
|
|
8
|
+
v2.1.97
|
|
9
9
|
|
|
10
10
|
=== Global Options
|
|
11
11
|
=== --config_file arg
|
|
@@ -346,6 +346,22 @@ Autotag last entry (or entries) not marked @done
|
|
|
346
346
|
|
|
347
347
|
|
|
348
348
|
|
|
349
|
+
==== Command: <tt>budget [TAG [AMOUNT]]</tt>
|
|
350
|
+
Set, list, and remove tag time budgets
|
|
351
|
+
|
|
352
|
+
Manage simple time budgets for tags.
|
|
353
|
+
|
|
354
|
+
Run without arguments to list configured budgets.
|
|
355
|
+
|
|
356
|
+
Use `doing budget TAG AMOUNT` to set a budget (e.g. `doing budget dev 100h`).
|
|
357
|
+
|
|
358
|
+
Use `doing budget TAG --remove` to delete a budget.
|
|
359
|
+
===== Options
|
|
360
|
+
===== -r|--remove
|
|
361
|
+
Delete specified tag budget
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
|
|
349
365
|
==== Command: <tt>cancel COUNT</tt>
|
|
350
366
|
End last X entries with no time tracked
|
|
351
367
|
|
data/lib/completion/_doing.zsh
CHANGED
|
@@ -12,6 +12,7 @@ function _doing() {
|
|
|
12
12
|
'archive:Move entries between sections'
|
|
13
13
|
'move:Move entries between sections'
|
|
14
14
|
'autotag:Autotag last entry or filtered entries'
|
|
15
|
+
'budget:Set'
|
|
15
16
|
'cancel:End last X entries with no time tracked'
|
|
16
17
|
'changes:List recent changes in Doing'
|
|
17
18
|
'changelog:List recent changes in Doing'
|
|
@@ -82,6 +83,9 @@ function _doing() {
|
|
|
82
83
|
autotag)
|
|
83
84
|
args=( "--bool[Boolean]:BOOLEAN:" {'(--count)-c','(-c)--count'}"[How many recent entries to autotag]:COUNT:" "--force[Don't ask permission to autotag all entries when count is 0]" {'(--interactive)-i','(-i)--interactive'}"[Select item(s) to tag from a menu of matching entries]" {'(--section)-s','(-s)--section'}"[Section]:SECTION_NAME:" "--search[Autotag entries matching search filter]:QUERY:" "--tag[Autotag the last X entries containing TAG]:TAG:" {'(--unfinished)-u','(-u)--unfinished'}"[Autotag last entry]" )
|
|
84
85
|
;;
|
|
86
|
+
budget)
|
|
87
|
+
args=( {'(--remove)-r','(-r)--remove'}"[Delete specified tag budget]" )
|
|
88
|
+
;;
|
|
85
89
|
cancel)
|
|
86
90
|
args=( {'(--archive)-a','(-a)--archive'}"[Archive entries]" "--bool[Boolean used to combine multiple tags]:BOOLEAN:" "--case[Case sensitivity for search string matching [(c)ase-sensitive]:TYPE:" {'(--interactive)-i','(-i)--interactive'}"[Select item(s) to cancel from a menu of matching entries]" "--not[Cancel items that *don't* match search/tag filters]" {'(--section)-s','(-s)--section'}"[Section]:NAME:" "--search[Filter entries using a search query]:QUERY:" "--tag[Filter entries by tag]:TAG:" {'(--unfinished)-u','(-u)--unfinished'}"[Cancel last entry]" "--val[Perform a tag value query]:QUERY:" {'(--exact)-x','(-x)--exact'}"[Force exact search string matching]" )
|
|
87
91
|
;;
|
data/lib/completion/doing.bash
CHANGED
|
@@ -48,6 +48,16 @@ _doing_autotag() {
|
|
|
48
48
|
fi
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
_doing_budget() {
|
|
52
|
+
|
|
53
|
+
if [[ "$token" == --* ]]; then
|
|
54
|
+
COMPREPLY=( $( compgen -W '--remove' -- $token ) )
|
|
55
|
+
elif [[ "$token" == -* ]]; then
|
|
56
|
+
COMPREPLY=( $( compgen -W '-r --remove' -- $token ) )
|
|
57
|
+
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
_doing_cancel() {
|
|
52
62
|
|
|
53
63
|
if [[ "$token" == --* ]]; then
|
|
@@ -456,6 +466,7 @@ _doing()
|
|
|
456
466
|
if [[ $last =~ (again|resume) ]]; then _doing_again
|
|
457
467
|
elif [[ $last =~ (archive|move) ]]; then _doing_archive
|
|
458
468
|
elif [[ $last =~ (autotag) ]]; then _doing_autotag
|
|
469
|
+
elif [[ $last =~ (budget) ]]; then _doing_budget
|
|
459
470
|
elif [[ $last =~ (cancel) ]]; then _doing_cancel
|
|
460
471
|
elif [[ $last =~ (changes|changelog) ]]; then _doing_changes
|
|
461
472
|
elif [[ $last =~ (completion) ]]; then _doing_completion
|
data/lib/completion/doing.fish
CHANGED
|
@@ -138,6 +138,7 @@ __fish_doing_complete_args tag
|
|
|
138
138
|
complete -xc doing -n '__fish_doing_needs_command' -a 'again resume' -d Repeat\ last\ entry\ as\ new\ entry
|
|
139
139
|
complete -xc doing -n '__fish_doing_needs_command' -a 'archive move' -d Move\ entries\ between\ sections
|
|
140
140
|
complete -xc doing -n '__fish_doing_needs_command' -a 'autotag' -d Autotag\ last\ entry\ or\ filtered\ entries
|
|
141
|
+
complete -xc doing -n '__fish_doing_needs_command' -a 'budget' -d Set
|
|
141
142
|
complete -xc doing -n '__fish_doing_needs_command' -a 'cancel' -d End\ last\ X\ entries\ with\ no\ time\ tracked
|
|
142
143
|
complete -xc doing -n '__fish_doing_needs_command' -a 'changes changelog' -d List\ recent\ changes\ in\ Doing
|
|
143
144
|
complete -xc doing -n '__fish_doing_needs_command' -a 'colors' -d List\ available\ color\ variables\ for\ configuration\ templates\ and\ views
|
|
@@ -214,6 +215,7 @@ complete -c doing -l section -s s -f -r -n '__fish_doing_using_command autotag'
|
|
|
214
215
|
complete -c doing -l search -f -r -n '__fish_doing_using_command autotag' -d Autotag\ entries\ matching\ search\ filter
|
|
215
216
|
complete -c doing -l tag -f -r -n '__fish_doing_using_command autotag' -d Autotag\ the\ last\ X\ entries\ containing\ TAG
|
|
216
217
|
complete -c doing -l unfinished -s u -f -n '__fish_doing_using_command autotag' -d Autotag\ last\ entry
|
|
218
|
+
complete -c doing -l remove -s r -f -n '__fish_doing_using_command budget' -d Delete\ specified\ tag\ budget
|
|
217
219
|
complete -c doing -l archive -s a -f -n '__fish_doing_using_command cancel' -d Archive\ entries
|
|
218
220
|
complete -c doing -l bool -f -r -n '__fish_doing_using_command cancel' -d Boolean\ used\ to\ combine\ multiple\ tags
|
|
219
221
|
complete -c doing -l case -f -r -n '__fish_doing_using_command cancel' -d Case\ sensitivity\ for\ search\ string\ matching\ \[\(c\)ase-sensitive
|
data/lib/completion/doing.ts
CHANGED
|
@@ -598,6 +598,20 @@ const completionSpec: Fig.Spec = {
|
|
|
598
598
|
|
|
599
599
|
},
|
|
600
600
|
|
|
601
|
+
{
|
|
602
|
+
name: "budget",
|
|
603
|
+
description: "Set",
|
|
604
|
+
options: [
|
|
605
|
+
{
|
|
606
|
+
name: ["-r", "--remove"],
|
|
607
|
+
description: "Delete specified tag budget",
|
|
608
|
+
|
|
609
|
+
},
|
|
610
|
+
|
|
611
|
+
],
|
|
612
|
+
|
|
613
|
+
},
|
|
614
|
+
|
|
601
615
|
{
|
|
602
616
|
name: "cancel",
|
|
603
617
|
description: "End last X entries with no time tracked",
|
data/lib/doing/configuration.rb
CHANGED
|
@@ -41,6 +41,8 @@ module Doing
|
|
|
41
41
|
'never_finish' => [],
|
|
42
42
|
'date_tags' => ['done', 'defer(?:red)?', 'waiting'],
|
|
43
43
|
|
|
44
|
+
'budgets' => {},
|
|
45
|
+
|
|
44
46
|
'timer_format' => 'text',
|
|
45
47
|
'interval_format' => 'text',
|
|
46
48
|
|
|
@@ -175,8 +177,8 @@ module Doing
|
|
|
175
177
|
|
|
176
178
|
return @config_file if @force_answer
|
|
177
179
|
|
|
178
|
-
if
|
|
179
|
-
choices = [@config_file].concat(
|
|
180
|
+
if additional_configs&.count&.positive? || create
|
|
181
|
+
choices = [@config_file].concat(additional_configs)
|
|
180
182
|
choices.push('Create a new .doingrc in the current directory') if create && !File.exist?('.doingrc')
|
|
181
183
|
res = Doing::Prompt.choose_from(choices.uniq.sort.reverse,
|
|
182
184
|
sorted: false,
|
|
@@ -28,6 +28,7 @@ module Doing
|
|
|
28
28
|
|
|
29
29
|
totals = {}
|
|
30
30
|
total = 0
|
|
31
|
+
tag_totals = Hash.new(0)
|
|
31
32
|
|
|
32
33
|
days.each do |day, day_items|
|
|
33
34
|
day_items.each do |item|
|
|
@@ -35,8 +36,40 @@ module Doing
|
|
|
35
36
|
duration = item.interval || 0
|
|
36
37
|
totals[day] += duration
|
|
37
38
|
total += duration
|
|
39
|
+
|
|
40
|
+
item.title.scan(/(?mi)@(\S+?)(\(.*\))?(?=\s|$)/).each do |m|
|
|
41
|
+
tag = m[0].downcase
|
|
42
|
+
next if tag == 'done'
|
|
43
|
+
|
|
44
|
+
tag_totals[tag] += duration
|
|
45
|
+
end
|
|
38
46
|
end
|
|
39
47
|
end
|
|
48
|
+
|
|
49
|
+
budgets = Doing.setting('budgets', {}) || {}
|
|
50
|
+
budgets = budgets.transform_keys { |k| k.to_s.downcase }
|
|
51
|
+
budgets_total = 0
|
|
52
|
+
|
|
53
|
+
budget_fmt = lambda do |secs|
|
|
54
|
+
secs = secs.to_i
|
|
55
|
+
return '0h' if secs <= 0
|
|
56
|
+
|
|
57
|
+
minutes = (secs / 60).to_i
|
|
58
|
+
hours = (minutes / 60).to_i
|
|
59
|
+
mins = (minutes % 60).to_i
|
|
60
|
+
return format('%<h>dh', h: hours) if mins.zero?
|
|
61
|
+
return format('%<m>dm', m: mins) if hours.zero?
|
|
62
|
+
|
|
63
|
+
format('%<h>dh%<m>dm', h: hours, m: mins)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
budgets.each do |tag, budget_secs|
|
|
67
|
+
used = tag_totals[tag].to_i
|
|
68
|
+
remaining = budget_secs.to_i - used
|
|
69
|
+
remaining = 0 if remaining.negative?
|
|
70
|
+
budgets_total += remaining
|
|
71
|
+
end
|
|
72
|
+
|
|
40
73
|
width = wwid.config['plugins']['byday']['item_width'].to_i || 60
|
|
41
74
|
divider = "{wd}+{xk}#{'-' * 10}{wd}+{xk}#{'-' * width}{wd}+{xk}#{'-' * 8}{wd}+{x}"
|
|
42
75
|
out = []
|
|
@@ -54,11 +87,17 @@ module Doing
|
|
|
54
87
|
out << "{wd}| |{xbw}#{title}{wd}|{xy}#{interval}{wd}|{x}"
|
|
55
88
|
end
|
|
56
89
|
day_total = "Total: #{totals[day].time_string(format: :clock)}"
|
|
90
|
+
if budgets_total.positive?
|
|
91
|
+
day_total += " (total budgets left #{budget_fmt.call(budgets_total)})"
|
|
92
|
+
end
|
|
57
93
|
out << divider
|
|
58
94
|
out << "{wd}|{xg}#{day_total.rjust(width + 20)}{wd}|{x}"
|
|
59
95
|
out << divider
|
|
60
96
|
end
|
|
61
97
|
all_total = "Grand Total: #{total.time_string(format: :clock)}"
|
|
98
|
+
if budgets_total.positive?
|
|
99
|
+
all_total += " (total budgets left #{budget_fmt.call(budgets_total)})"
|
|
100
|
+
end
|
|
62
101
|
out << "{wd}|{xrb}#{all_total.rjust(width + 20)}{wd}|{x}"
|
|
63
102
|
out << divider
|
|
64
103
|
Doing::Color.template(out.join("\n"))
|
data/lib/doing/version.rb
CHANGED
data/lib/doing/wwid/timers.rb
CHANGED
|
@@ -16,6 +16,34 @@ module Doing
|
|
|
16
16
|
|
|
17
17
|
@timers.delete('meanwhile')
|
|
18
18
|
|
|
19
|
+
timers_snapshot = @timers.dup
|
|
20
|
+
|
|
21
|
+
budgets = Doing.setting('budgets', {}) || {}
|
|
22
|
+
budgets = budgets.transform_keys { |k| k.to_s.downcase }
|
|
23
|
+
remaining_map = {}
|
|
24
|
+
budgets_total = 0
|
|
25
|
+
|
|
26
|
+
budget_fmt = lambda do |secs|
|
|
27
|
+
secs = secs.to_i
|
|
28
|
+
return '0h' if secs <= 0
|
|
29
|
+
|
|
30
|
+
minutes = (secs / 60).to_i
|
|
31
|
+
hours = (minutes / 60).to_i
|
|
32
|
+
mins = (minutes % 60).to_i
|
|
33
|
+
return format('%dh', hours) if mins.zero?
|
|
34
|
+
return format('%dm', mins) if hours.zero?
|
|
35
|
+
|
|
36
|
+
format('%dh%dm', hours, mins)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
budgets.each do |tag, budget_secs|
|
|
40
|
+
used = timers_snapshot[tag].to_i
|
|
41
|
+
remaining = budget_secs.to_i - used
|
|
42
|
+
remaining = 0 if remaining.negative?
|
|
43
|
+
remaining_map[tag] = remaining
|
|
44
|
+
budgets_total += remaining
|
|
45
|
+
end
|
|
46
|
+
|
|
19
47
|
max = @timers.keys.sort_by(&:length).reverse[0].length + 1
|
|
20
48
|
|
|
21
49
|
total = @timers.delete('All')
|
|
@@ -47,9 +75,13 @@ module Doing
|
|
|
47
75
|
<tbody>
|
|
48
76
|
EOHEAD
|
|
49
77
|
sorted_tags_data.reverse.each do |k, v|
|
|
50
|
-
|
|
51
|
-
|
|
78
|
+
next unless v.positive?
|
|
79
|
+
|
|
80
|
+
budget_str = ''
|
|
81
|
+
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
82
|
+
budget_str = " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
52
83
|
end
|
|
84
|
+
output += "<tr><td style='text-align:left;'>#{k}</td><td style='text-align:left;'>#{v.time_string(format: :clock)}#{budget_str}</td></tr>\n"
|
|
53
85
|
end
|
|
54
86
|
tail = <<EOTAIL
|
|
55
87
|
<tr>
|
|
@@ -59,7 +91,7 @@ EOHEAD
|
|
|
59
91
|
<tfoot>
|
|
60
92
|
<tr>
|
|
61
93
|
<td style="text-align:left;"><strong>Total</strong></td>
|
|
62
|
-
<td style="text-align:left;">#{total.time_string(format: :clock)}</td>
|
|
94
|
+
<td style="text-align:left;">#{total.time_string(format: :clock)}#{" (total budgets left #{budget_fmt.call(budgets_total)})" if budgets_total.positive?}</td>
|
|
63
95
|
</tr>
|
|
64
96
|
</tfoot>
|
|
65
97
|
</table>
|
|
@@ -73,7 +105,13 @@ EOTAIL
|
|
|
73
105
|
| #{'-' * (pad - 1)}: | :------- |
|
|
74
106
|
EOHEADER
|
|
75
107
|
sorted_tags_data.reverse.each do |k, v|
|
|
76
|
-
|
|
108
|
+
next unless v.positive?
|
|
109
|
+
|
|
110
|
+
budget_str = ''
|
|
111
|
+
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
112
|
+
budget_str = " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
113
|
+
end
|
|
114
|
+
output += "| #{' ' * (pad - k.length)}#{k} | #{v.time_string(format: :clock)}#{budget_str} |\n"
|
|
77
115
|
end
|
|
78
116
|
tail = '[Tag Totals]'
|
|
79
117
|
output + tail
|
|
@@ -83,7 +121,10 @@ EOTAIL
|
|
|
83
121
|
output << {
|
|
84
122
|
'tag' => k,
|
|
85
123
|
'seconds' => v,
|
|
86
|
-
'formatted' => v.time_string(format: :clock)
|
|
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)
|
|
87
128
|
}
|
|
88
129
|
end
|
|
89
130
|
output
|
|
@@ -94,17 +135,33 @@ EOTAIL
|
|
|
94
135
|
(max - k.length).times do
|
|
95
136
|
spacer += ' '
|
|
96
137
|
end
|
|
97
|
-
|
|
138
|
+
line = "┃ #{spacer}#{k}:#{v.time_string(format: :hm)}"
|
|
139
|
+
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
140
|
+
line += " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
141
|
+
end
|
|
142
|
+
line += ' ┃'
|
|
143
|
+
output.push(line)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
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?
|
|
148
|
+
total_content += ' ┃'
|
|
149
|
+
max_line_len = (output + [total_content]).map(&:length).max
|
|
150
|
+
|
|
151
|
+
pad_line = lambda do |line|
|
|
152
|
+
pad = max_line_len - line.length
|
|
153
|
+
pad.positive? ? "#{line[0..-3]} #{' ' * pad}┃" : line
|
|
98
154
|
end
|
|
155
|
+
output = output.map { |l| pad_line.call(l) }
|
|
99
156
|
|
|
100
157
|
header = '┏━━ Tag Totals '
|
|
101
|
-
(
|
|
158
|
+
[(max_line_len - 16), 0].max.times { header += '━' }
|
|
102
159
|
header += '┓'
|
|
103
160
|
footer = '┗'
|
|
104
|
-
(
|
|
161
|
+
[(max_line_len - 2), 0].max.times { footer += '━' }
|
|
105
162
|
footer += '┛'
|
|
106
163
|
divider = '┣'
|
|
107
|
-
(
|
|
164
|
+
[(max_line_len - 2), 0].max.times { divider += '━' }
|
|
108
165
|
divider += '┫'
|
|
109
166
|
output = output.empty? ? '' : "\n#{header}\n#{output.join("\n")}"
|
|
110
167
|
output += "\n#{divider}"
|
|
@@ -113,10 +170,12 @@ EOTAIL
|
|
|
113
170
|
spacer += ' '
|
|
114
171
|
end
|
|
115
172
|
total_time = total.time_string(format: :hm)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
173
|
+
total_line = "┃ #{spacer}total: "
|
|
174
|
+
total_line += total_time
|
|
175
|
+
total_line += " (total budgets left #{budget_fmt.call(budgets_total)})" if budgets_total.positive?
|
|
176
|
+
total_line += ' ┃'
|
|
177
|
+
total_line = pad_line.call(total_line)
|
|
178
|
+
output += "\n#{total_line}"
|
|
120
179
|
output += "\n#{footer}"
|
|
121
180
|
output
|
|
122
181
|
else
|
|
@@ -126,11 +185,19 @@ EOTAIL
|
|
|
126
185
|
(max - k.length).times do
|
|
127
186
|
spacer += ' '
|
|
128
187
|
end
|
|
129
|
-
|
|
188
|
+
line = "#{k}:#{spacer}#{v.time_string(format: :clock)}"
|
|
189
|
+
if remaining_map.key?(k) && remaining_map[k].positive?
|
|
190
|
+
line += " (budget left #{budget_fmt.call(remaining_map[k])})"
|
|
191
|
+
end
|
|
192
|
+
output.push(line)
|
|
130
193
|
end
|
|
131
194
|
|
|
132
195
|
output = output.empty? ? '' : "\n--- Tag Totals ---\n#{output.join("\n")}"
|
|
133
|
-
output += "\n\nTotal tracked: #{total.time_string(format: :clock)}
|
|
196
|
+
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
|
|
200
|
+
output += "\n"
|
|
134
201
|
output
|
|
135
202
|
end
|
|
136
203
|
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.
|
|
4
|
+
version: 2.1.97
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brett Terpstra
|
|
@@ -514,6 +514,7 @@ files:
|
|
|
514
514
|
- _config.yml
|
|
515
515
|
- bin/commands/again.rb
|
|
516
516
|
- bin/commands/archive.rb
|
|
517
|
+
- bin/commands/budget.rb
|
|
517
518
|
- bin/commands/cancel.rb
|
|
518
519
|
- bin/commands/changes.rb
|
|
519
520
|
- bin/commands/choose.rb
|