Skip to content

Commit c437702

Browse files
committed
Optionally include untagged entries in klog tags
1 parent 3a78b06 commit c437702

File tree

6 files changed

+293
-90
lines changed

6 files changed

+293
-90
lines changed

klog/app/cli/tags.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import (
99
)
1010

1111
type Tags struct {
12-
Values bool `name:"values" short:"v" help:"Display breakdown of tag values (if the data contains any; e.g.: '#tag=value')."`
13-
Count bool `name:"count" short:"c" help:"Display the number of matching entries per tag."`
12+
Values bool `name:"values" short:"v" help:"Display breakdown of tag values (if the data contains any; e.g.: '#tag=value')."`
13+
Count bool `name:"count" short:"c" help:"Display the number of matching entries per tag."`
14+
WithUntagged bool `name:"with-untagged" short:"u" help:"Display remainder of any untagged entries"`
1415
util.FilterArgs
1516
util.NowArgs
1617
util.DecimalArgs
@@ -46,39 +47,48 @@ func (opt *Tags) Run(ctx app.Context) app.Error {
4647
if nErr != nil {
4748
return nErr
4849
}
49-
totalByTag := service.AggregateTotalsByTags(records...)
50-
if len(totalByTag) == 0 {
51-
return nil
52-
}
50+
tagStats, untagged := service.AggregateTotalsByTags(records...)
5351
numberOfColumns := 2
5452
if opt.Values {
5553
numberOfColumns++
5654
}
5755
if opt.Count {
5856
numberOfColumns++
5957
}
58+
countString := func(c int) string {
59+
return styler.Props(tf.StyleProps{Color: tf.TEXT_SUBDUED}).Format(fmt.Sprintf(" (%d)", c))
60+
}
6061
table := tf.NewTable(numberOfColumns, " ")
61-
for _, t := range totalByTag {
62+
for _, t := range tagStats {
6263
totalString := serialiser.Duration(t.Total)
63-
countString := styler.Props(tf.StyleProps{Color: tf.TEXT_SUBDUED}).Format(fmt.Sprintf(" (%d)", t.Count))
6464
if t.Tag.Value() == "" {
6565
table.CellL("#" + t.Tag.Name())
6666
table.CellL(totalString)
6767
if opt.Values {
6868
table.Skip(1)
6969
}
7070
if opt.Count {
71-
table.CellL(countString)
71+
table.CellL(countString(t.Count))
7272
}
7373
} else if opt.Values {
7474
table.CellL(" " + styler.Props(tf.StyleProps{Color: tf.TEXT_SUBDUED}).Format(t.Tag.Value()))
7575
table.Skip(1)
7676
table.CellL(totalString)
7777
if opt.Count {
78-
table.CellL(countString)
78+
table.CellL(countString(t.Count))
7979
}
8080
}
8181
}
82+
if opt.WithUntagged {
83+
table.CellL("(untagged)")
84+
table.CellL(serialiser.Duration(untagged.Total))
85+
if opt.Values {
86+
table.Skip(1)
87+
}
88+
if opt.Count {
89+
table.CellL(countString(untagged.Count))
90+
}
91+
}
8292
table.Collect(ctx.Print)
8393
opt.WarnArgs.PrintWarnings(ctx, records, opt.GetNowWarnings())
8494
return nil

klog/app/cli/tags_test.go

Lines changed: 182 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ func TestPrintTagsOverview(t *testing.T) {
1919
- Sort output alphabetically
2020
- Print in tabular manner
2121
*/
22-
state, err := NewTestingContext()._SetRecords(`
22+
ctx := NewTestingContext()._SetRecords(`
2323
1995-03-17
2424
#sports
2525
3h #badminton
26-
1h #running
27-
1h #running
26+
1h #running=home-trail
27+
1h #running=river-route
2828
2929
1995-03-28
3030
Was #sick, need to compensate later
@@ -38,59 +38,206 @@ Was #sick, need to compensate later
3838
#sports #running (Don’t count that twice!)
3939
14:00 - 17:00 #sports #running
4040
41-
`)._Run((&Tags{}).Run)
42-
require.Nil(t, err)
43-
assert.Equal(t, `
41+
`)
42+
43+
t.Run("Without argument", func(t *testing.T) {
44+
state, err := ctx._Run((&Tags{}).Run)
45+
require.Nil(t, err)
46+
assert.Equal(t, `
47+
#badminton 3h45m
48+
#running 4h30m
49+
#sick -30m
50+
#sports 8h
51+
`, state.printBuffer)
52+
})
53+
54+
t.Run("With count", func(t *testing.T) {
55+
state, err := ctx._Run((&Tags{
56+
Count: true,
57+
}).Run)
58+
require.Nil(t, err)
59+
assert.Equal(t, `
60+
#badminton 3h45m (2)
61+
#running 4h30m (4)
62+
#sick -30m (1)
63+
#sports 8h (4)
64+
`, state.printBuffer)
65+
})
66+
67+
t.Run("With values", func(t *testing.T) {
68+
state, err := ctx._Run((&Tags{
69+
Values: true,
70+
}).Run)
71+
require.Nil(t, err)
72+
assert.Equal(t, `
73+
#badminton 3h45m
74+
#running 4h30m
75+
home-trail 1h
76+
river-route 1h
77+
#sick -30m
78+
#sports 8h
79+
`, state.printBuffer)
80+
})
81+
82+
t.Run("With values and count", func(t *testing.T) {
83+
state, err := ctx._Run((&Tags{
84+
Values: true,
85+
Count: true,
86+
}).Run)
87+
require.Nil(t, err)
88+
assert.Equal(t, `
89+
#badminton 3h45m (2)
90+
#running 4h30m (4)
91+
home-trail 1h (1)
92+
river-route 1h (1)
93+
#sick -30m (1)
94+
#sports 8h (4)
95+
`, state.printBuffer)
96+
})
97+
98+
t.Run("With untagged", func(t *testing.T) {
99+
state, err := ctx._Run((&Tags{
100+
WithUntagged: true,
101+
}).Run)
102+
require.Nil(t, err)
103+
assert.Equal(t, `
44104
#badminton 3h45m
45105
#running 4h30m
46106
#sick -30m
47107
#sports 8h
108+
(untagged) 9h
48109
`, state.printBuffer)
110+
})
111+
112+
t.Run("With untagged and count", func(t *testing.T) {
113+
state, err := ctx._Run((&Tags{
114+
WithUntagged: true,
115+
Count: true,
116+
}).Run)
117+
require.Nil(t, err)
118+
assert.Equal(t, `
119+
#badminton 3h45m (2)
120+
#running 4h30m (4)
121+
#sick -30m (1)
122+
#sports 8h (4)
123+
(untagged) 9h (1)
124+
`, state.printBuffer)
125+
})
126+
127+
t.Run("With values and untagged", func(t *testing.T) {
128+
state, err := ctx._Run((&Tags{
129+
Values: true,
130+
WithUntagged: true,
131+
}).Run)
132+
require.Nil(t, err)
133+
assert.Equal(t, `
134+
#badminton 3h45m
135+
#running 4h30m
136+
home-trail 1h
137+
river-route 1h
138+
#sick -30m
139+
#sports 8h
140+
(untagged) 9h
141+
`, state.printBuffer)
142+
})
143+
144+
t.Run("With values and untagged and count", func(t *testing.T) {
145+
state, err := ctx._Run((&Tags{
146+
Values: true,
147+
WithUntagged: true,
148+
Count: true,
149+
}).Run)
150+
require.Nil(t, err)
151+
assert.Equal(t, `
152+
#badminton 3h45m (2)
153+
#running 4h30m (4)
154+
home-trail 1h (1)
155+
river-route 1h (1)
156+
#sick -30m (1)
157+
#sports 8h (4)
158+
(untagged) 9h (1)
159+
`, state.printBuffer)
160+
})
161+
}
162+
163+
func TestPrintUntaggedIfNoTags(t *testing.T) {
164+
t.Run("No tags present", func(t *testing.T) {
165+
state, err := NewTestingContext()._SetRecords(`
166+
1995-03-17
167+
1h
168+
`)._Run((&Tags{
169+
WithUntagged: true,
170+
}).Run)
171+
require.Nil(t, err)
172+
assert.Equal(t, `
173+
(untagged) 1h
174+
`, state.printBuffer)
175+
})
176+
177+
t.Run("Empty file", func(t *testing.T) {
178+
state, err := NewTestingContext()._SetRecords(`
179+
`)._Run((&Tags{
180+
WithUntagged: true,
181+
}).Run)
182+
require.Nil(t, err)
183+
assert.Equal(t, `
184+
(untagged) 0m
185+
`, state.printBuffer)
186+
})
187+
188+
t.Run("Empty file (with count)", func(t *testing.T) {
189+
state, err := NewTestingContext()._SetRecords(`
190+
`)._Run((&Tags{
191+
WithUntagged: true,
192+
Count: true,
193+
}).Run)
194+
require.Nil(t, err)
195+
assert.Equal(t, `
196+
(untagged) 0m (0)
197+
`, state.printBuffer)
198+
})
49199
}
50200

51201
func TestPrintTagsWithUnicodeCharacters(t *testing.T) {
52202
state, err := NewTestingContext()._SetRecords(`
53203
1995-03-17
54204
1h #ascii
55-
2h #üñïčödę
205+
2h #üñïčöδę
56206
`)._Run((&Tags{}).Run)
57207
require.Nil(t, err)
58208
assert.Equal(t, `
59209
#ascii 1h
60-
#üñïčödę 2h
210+
#üñïčöδę 2h
61211
`, state.printBuffer)
62212
}
63213

64-
func TestPrintTagsWithCount(t *testing.T) {
65-
state, err := NewTestingContext()._SetRecords(`
214+
func TestPrintTagsOverviewWithUntaggedEmptyStates(t *testing.T) {
215+
ctx := NewTestingContext()._SetRecords(`
66216
1995-03-17
67-
#sports
68-
3h #badminton
69-
1h #running
70-
1h #running
71-
72-
1995-03-28
73-
Was #sick, need to compensate later
74-
-30m #running
75-
76-
1995-04-02
77-
9h something untagged
78-
45m #badminton
217+
3h #ticket
218+
`)
219+
t.Run("Include 0 line", func(t *testing.T) {
220+
state, err := ctx._Run((&Tags{
221+
WithUntagged: true,
222+
}).Run)
223+
require.Nil(t, err)
224+
assert.Equal(t, `
225+
#ticket 3h
226+
(untagged) 0m
227+
`, state.printBuffer)
228+
})
79229

80-
1995-04-19
81-
#sports #running (Don’t count that twice!)
82-
14:00 - 17:00 #sports #running
83-
84-
`)._Run((&Tags{
85-
Count: true,
86-
}).Run)
87-
require.Nil(t, err)
88-
assert.Equal(t, `
89-
#badminton 3h45m (2)
90-
#running 4h30m (4)
91-
#sick -30m (1)
92-
#sports 8h (4)
230+
t.Run("Include 0 count", func(t *testing.T) {
231+
state, err := ctx._Run((&Tags{
232+
WithUntagged: true,
233+
Count: true,
234+
}).Run)
235+
require.Nil(t, err)
236+
assert.Equal(t, `
237+
#ticket 3h (1)
238+
(untagged) 0m (0)
93239
`, state.printBuffer)
240+
})
94241
}
95242

96243
func TestPrintTagsOverviewWithValueGrouping(t *testing.T) {

klog/app/cli/terminalformat/table.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,5 +103,7 @@ func (t *Table) Collect(fn func(string)) {
103103
}
104104
}
105105
}
106-
fn("\n")
106+
if len(t.cells) > 0 {
107+
fn("\n")
108+
}
107109
}

klog/app/cli/terminalformat/table_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ long-text `+"\x1b[0m\x1b[4m"+`asdf`+"\x1b[0m"+` -----
2929
`, result)
3030
}
3131

32+
func TestPrintEmptyTable(t *testing.T) {
33+
// If the table is empty, it shouldn’t print a trailing newline.
34+
result := ""
35+
table := NewTable(3, " ")
36+
table.Collect(func(x string) { result += x })
37+
assert.Equal(t, ``, result)
38+
}
39+
3240
func TestPrintTableWithUnicode(t *testing.T) {
3341
result := ""
3442
table := NewTable(3, " ")

0 commit comments

Comments
 (0)