Skip to content

Commit 68e77c0

Browse files
authored
Merge pull request #12 from jotaen/templates
Add experimental support for templates and appending
2 parents 2b8eca9 + cebd59c commit 68e77c0

File tree

13 files changed

+302
-107
lines changed

13 files changed

+302
-107
lines changed

src/app/cli/cmd_append.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package cli
2+
3+
import (
4+
"klog/app"
5+
)
6+
7+
type Append struct {
8+
File string `arg optional type:"existingfile" name:"file" help:".klg source file (if empty the bookmark is used)"`
9+
From string `required name:"from" help:"The name of the template to instantiate"`
10+
}
11+
12+
func (args *Append) Run(ctx app.Context) error {
13+
target := args.File
14+
if target == "" {
15+
target = ctx.Bookmark().Path
16+
}
17+
return ctx.AppendTemplateToFile(target, args.From)
18+
}

src/app/cli/common_args.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
type InputFilesArgs struct {
10-
File []string `arg optional type:"existingfile" name:"file" help:".klg source file(s) (falls back to bookmark if not specified)"`
10+
File []string `arg optional type:"existingfile" name:"file" help:".klg source file(s) (if empty the bookmark is used)"`
1111
}
1212

1313
type DiffArg struct {

src/app/cli/exec.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type cli struct {
1818
Eval Eval `cmd hidden`
1919
Report Report `cmd help:"Print a calendar report summarising all days"`
2020
Tags Tags `cmd help:"Print total times aggregated by tags"`
21+
Append Append `cmd hidden help:"Appends a new record to a file (based on templates)"`
2122
Bookmark Bookmark `cmd help:"Set a default file that klog reads from"`
2223
Widget Widget `cmd help:"Start menu bar widget (MacOS only)"`
2324
Version Version `cmd help:"Print version info and check for updates"`

src/app/cli/testutil_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cli
22

33
import (
4+
"errors"
45
"klog"
56
"klog/app"
67
"klog/parser"
@@ -34,10 +35,14 @@ func (m *TestContext) Print(s string) {
3435
m.printBuffer += s
3536
}
3637

37-
func (m *TestContext) HomeDir() string {
38+
func (m *TestContext) HomeFolder() string {
3839
return "~"
3940
}
4041

42+
func (m *TestContext) KlogFolder() string {
43+
return m.HomeFolder() + "/.klog/"
44+
}
45+
4146
func (m *TestContext) MetaInfo() struct {
4247
Version string
4348
BuildHash string
@@ -67,3 +72,7 @@ func (m *TestContext) Bookmark() *app.File {
6772
func (m *TestContext) OpenInFileBrowser(_ string) error {
6873
return nil
6974
}
75+
76+
func (m *TestContext) AppendTemplateToFile(string, string) error {
77+
return errors.New("No such template")
78+
}

src/app/context.go

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,22 @@ import (
66
"io/ioutil"
77
"klog"
88
"klog/parser"
9+
"klog/service"
910
"os"
1011
"os/exec"
1112
"os/user"
1213
"path/filepath"
1314
"strings"
15+
"time"
1416
)
1517

1618
var BinaryVersion string // will be set during build
1719
var BinaryBuildHash string // will be set during build
1820

1921
type Context interface {
2022
Print(string)
21-
HomeDir() string
23+
KlogFolder() string
24+
HomeFolder() string
2225
MetaInfo() struct {
2326
Version string
2427
BuildHash string
@@ -27,6 +30,7 @@ type Context interface {
2730
SetBookmark(string) error
2831
Bookmark() *File
2932
OpenInFileBrowser(string) error
33+
AppendTemplateToFile(string, string) error
3034
}
3135

3236
type context struct {
@@ -51,10 +55,14 @@ func (c *context) Print(text string) {
5155
fmt.Print(text)
5256
}
5357

54-
func (c *context) HomeDir() string {
58+
func (c *context) HomeFolder() string {
5559
return c.homeDir
5660
}
5761

62+
func (c *context) KlogFolder() string {
63+
return c.homeDir + "/.klog/"
64+
}
65+
5866
func (c *context) MetaInfo() struct {
5967
Version string
6068
BuildHash string
@@ -95,9 +103,9 @@ func (c *context) RetrieveRecords(paths ...string) ([]klog.Record, error) {
95103
if err != nil {
96104
return nil, err
97105
}
98-
rs, errs := parser.Parse(content)
99-
if errs != nil {
100-
return nil, errs
106+
rs, parserErrors := parser.Parse(content)
107+
if parserErrors != nil {
108+
return nil, parserErrors
101109
}
102110
records = append(records, rs...)
103111
}
@@ -111,7 +119,7 @@ type File struct {
111119
}
112120

113121
func (c *context) Bookmark() *File {
114-
bookmarkPath := c.HomeDir() + "/.klog/bookmark.klg"
122+
bookmarkPath := c.KlogFolder() + "bookmark.klg"
115123
dest, err := os.Readlink(bookmarkPath)
116124
if err != nil {
117125
return nil
@@ -130,30 +138,64 @@ func (c *context) Bookmark() *File {
130138
func (c *context) SetBookmark(path string) error {
131139
bookmark, err := filepath.Abs(path)
132140
if err != nil {
133-
return err
141+
return errors.New("Target file does not exist")
134142
}
135143
if !strings.HasSuffix(bookmark, ".klg") {
136144
return errors.New("File name must have .klg extension")
137145
}
138-
klogFolder := c.HomeDir() + "/.klog"
146+
klogFolder := c.KlogFolder()
139147
err = os.MkdirAll(klogFolder, 0700)
140148
if err != nil {
141-
return err
149+
return errors.New("Unable to initialise ~/.klog folder")
142150
}
143151
symlink := klogFolder + "/bookmark.klg"
144152
_ = os.Remove(symlink)
145-
return os.Symlink(bookmark, symlink)
153+
err = os.Symlink(bookmark, symlink)
154+
if err != nil {
155+
return errors.New("Failed to create bookmark")
156+
}
157+
return nil
146158
}
147159

148160
func (c *context) OpenInFileBrowser(path string) error {
149161
cmd := exec.Command("open", path)
150162
return cmd.Run()
151163
}
152164

165+
func (c *context) AppendTemplateToFile(filePath string, templateName string) error {
166+
location := c.KlogFolder() + templateName + ".template.klg"
167+
template, err := readFile(location)
168+
if err != nil {
169+
return errors.New("No such template: " + location)
170+
}
171+
instance, err := service.RenderTemplate(template, time.Now())
172+
if err != nil {
173+
return err
174+
}
175+
contents, err := readFile(filePath)
176+
if err != nil {
177+
return err
178+
}
179+
err = appendToFile(filePath, service.AppendableText(contents, instance))
180+
return err
181+
}
182+
153183
func readFile(path string) (string, error) {
154184
contents, err := ioutil.ReadFile(path)
155185
if err != nil {
156-
return "", err
186+
return "", errors.New("Cannot read file: " + path)
157187
}
158188
return string(contents), nil
159189
}
190+
191+
func appendToFile(path string, textToAppend string) error {
192+
file, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0644)
193+
if err != nil {
194+
return errors.New("Cannot write to file: " + path)
195+
}
196+
defer file.Close()
197+
if _, err := file.WriteString(textToAppend); err != nil {
198+
return errors.New("Cannot write to file: " + path)
199+
}
200+
return nil
201+
}

src/app/mac_widget/render_darwin.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ func render(ctx app.Context, agent *launchAgent) []menuet.MenuItem {
2020
if err == nil {
2121
return renderRecords(ctx, rs, file)
2222
}
23+
return []menuet.MenuItem{{
24+
Text: "Bookmarked file invalid",
25+
FontWeight: menuet.WeightBold,
26+
}, {
27+
Text: "Please fix the syntax errors",
28+
}}
2329
}
2430
return []menuet.MenuItem{{
2531
Text: "No bookmark specified",

src/app/mac_widget/run_darwin.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ func Run(forceRunThroughLaunchAgent bool) {
1616
os.Exit(1)
1717
}
1818
binPath, _ := os.Executable()
19-
launchAgent := NewLaunchAgent(ctx.HomeDir(), binPath)
19+
launchAgent := NewLaunchAgent(ctx.HomeFolder(), binPath)
2020

2121
if forceRunThroughLaunchAgent {
2222
if !launchAgent.isActive() {

src/service/evaluate.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package service
2+
3+
import (
4+
. "klog"
5+
"time"
6+
)
7+
8+
func Total(rs ...Record) Duration {
9+
total, _ := HypotheticalTotal(time.Time{}, rs...)
10+
return total
11+
}
12+
13+
func TotalEntries(es ...Entry) Duration {
14+
total := NewDuration(0, 0)
15+
for _, e := range es {
16+
total = total.Plus(e.Duration())
17+
}
18+
return total
19+
}
20+
21+
func HypotheticalTotal(until time.Time, rs ...Record) (Duration, bool) {
22+
total := NewDuration(0, 0)
23+
isCurrent := false
24+
void := time.Time{}
25+
thisDay := NewDateFromTime(until)
26+
theDayBefore := thisDay.PlusDays(-1)
27+
for _, r := range rs {
28+
for _, e := range r.Entries() {
29+
t := (e.Unbox(
30+
func(r Range) interface{} { return r.Duration() },
31+
func(d Duration) interface{} { return d },
32+
func(o OpenRange) interface{} {
33+
if until != void && (r.Date().IsEqualTo(thisDay) || r.Date().IsEqualTo(theDayBefore)) {
34+
end := NewTimeFromTime(until)
35+
if r.Date().IsEqualTo(theDayBefore) {
36+
end, _ = NewTimeTomorrow(end.Hour(), end.Minute())
37+
}
38+
tr, err := NewRange(o.Start(), end)
39+
if err == nil {
40+
isCurrent = true
41+
return tr.Duration()
42+
}
43+
}
44+
return NewDuration(0, 0)
45+
})).(Duration)
46+
total = total.Plus(t)
47+
}
48+
}
49+
return total, isCurrent
50+
}
51+
52+
func ShouldTotalSum(rs ...Record) ShouldTotal {
53+
total := NewDuration(0, 0)
54+
for _, r := range rs {
55+
total = total.Plus(r.ShouldTotal())
56+
}
57+
return NewShouldTotal(0, total.InMinutes())
58+
}

src/service/evaluate_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package service
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
. "klog"
6+
"testing"
7+
gotime "time"
8+
)
9+
10+
func TestTotalSumUpZeroIfNoTimesSpecified(t *testing.T) {
11+
r := NewRecord(Ɀ_Date_(2020, 1, 1))
12+
assert.Equal(t, NewDuration(0, 0), Total(r))
13+
}
14+
15+
func TestTotalSumsUpTimesAndRangesButNotOpenRanges(t *testing.T) {
16+
r1 := NewRecord(Ɀ_Date_(2020, 1, 1))
17+
r1.AddDuration(NewDuration(3, 0), "")
18+
r1.AddDuration(NewDuration(1, 33), "")
19+
r1.AddRange(Ɀ_Range_(Ɀ_TimeYesterday_(8, 0), Ɀ_TimeTomorrow_(12, 0)), "")
20+
r1.AddRange(Ɀ_Range_(Ɀ_Time_(13, 49), Ɀ_Time_(17, 12)), "")
21+
_ = r1.StartOpenRange(Ɀ_Time_(1, 2), "")
22+
r2 := NewRecord(Ɀ_Date_(2020, 1, 2))
23+
r2.AddDuration(NewDuration(7, 55), "")
24+
assert.Equal(t, NewDuration(3+1+(16+24+12)+3+7, 33+11+12+55), Total(r1, r2))
25+
}
26+
27+
func TestSumUpHypotheticalTotalAtGivenTime(t *testing.T) {
28+
r := NewRecord(Ɀ_Date_(2020, 1, 1))
29+
r.AddDuration(NewDuration(2, 14), "")
30+
r.AddRange(Ɀ_Range_(Ɀ_TimeYesterday_(23, 0), Ɀ_Time_(4, 0)), "")
31+
_ = r.StartOpenRange(Ɀ_Time_(5, 7), "")
32+
33+
time1, _ := gotime.Parse("2006-01-02T15:04:05-0700", "2020-01-01T05:06:59-0000")
34+
ht1, isOngoing1 := HypotheticalTotal(time1, r)
35+
assert.False(t, isOngoing1)
36+
assert.Equal(t, NewDuration(2+(1+4), 14), ht1)
37+
38+
time2, _ := gotime.Parse("2006-01-02T15:04:05-0700", "2020-01-01T10:48:13-0000")
39+
ht2, isOngoing2 := HypotheticalTotal(time2, r)
40+
assert.True(t, isOngoing2)
41+
assert.Equal(t, NewDuration(2+(1+4)+4, 14+53+48), ht2)
42+
43+
time3, _ := gotime.Parse("2006-01-02T15:04:05-0700", "2020-01-02T03:01:29-0000")
44+
ht3, isOngoing3 := HypotheticalTotal(time3, r)
45+
assert.True(t, isOngoing3)
46+
assert.Equal(t, NewDuration(2+(1+4)+18+3, 14+53+1), ht3)
47+
}
Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,61 +3,8 @@ package service
33
import (
44
. "klog"
55
"sort"
6-
"time"
76
)
87

9-
func Total(rs ...Record) Duration {
10-
total, _ := HypotheticalTotal(time.Time{}, rs...)
11-
return total
12-
}
13-
14-
func TotalEntries(es ...Entry) Duration {
15-
total := NewDuration(0, 0)
16-
for _, e := range es {
17-
total = total.Plus(e.Duration())
18-
}
19-
return total
20-
}
21-
22-
func HypotheticalTotal(until time.Time, rs ...Record) (Duration, bool) {
23-
total := NewDuration(0, 0)
24-
isCurrent := false
25-
void := time.Time{}
26-
thisDay := NewDateFromTime(until)
27-
theDayBefore := thisDay.PlusDays(-1)
28-
for _, r := range rs {
29-
for _, e := range r.Entries() {
30-
t := (e.Unbox(
31-
func(r Range) interface{} { return r.Duration() },
32-
func(d Duration) interface{} { return d },
33-
func(o OpenRange) interface{} {
34-
if until != void && (r.Date().IsEqualTo(thisDay) || r.Date().IsEqualTo(theDayBefore)) {
35-
end := NewTimeFromTime(until)
36-
if r.Date().IsEqualTo(theDayBefore) {
37-
end, _ = NewTimeTomorrow(end.Hour(), end.Minute())
38-
}
39-
tr, err := NewRange(o.Start(), end)
40-
if err == nil {
41-
isCurrent = true
42-
return tr.Duration()
43-
}
44-
}
45-
return NewDuration(0, 0)
46-
})).(Duration)
47-
total = total.Plus(t)
48-
}
49-
}
50-
return total, isCurrent
51-
}
52-
53-
func ShouldTotalSum(rs ...Record) ShouldTotal {
54-
total := NewDuration(0, 0)
55-
for _, r := range rs {
56-
total = total.Plus(r.ShouldTotal())
57-
}
58-
return NewShouldTotal(0, total.InMinutes())
59-
}
60-
618
type Filter struct {
629
Tags []string
6310
BeforeEq Date

0 commit comments

Comments
 (0)