Skip to content

Commit

Permalink
Add warning about time entries in the future
Browse files Browse the repository at this point in the history
  • Loading branch information
jotaen committed Feb 14, 2021
1 parent 6f5de24 commit f84c915
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 89 deletions.
26 changes: 14 additions & 12 deletions src/app/cli/cmd_now.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,28 @@ func (opt *Now) Run(ctx app.Context) error {
if err != nil {
return err
}
recent, err := getTodayOrYesterday(ctx.Now(), records)
recents, err := getTodayOrYesterday(ctx.Now(), records)
if err != nil {
ctx.Print(err.Error())
return nil
}
// Headline:
label := " Today"
if !recent.Date().IsEqualTo(NewDateFromTime(ctx.Now())) {
if !recents[0].Date().IsEqualTo(NewDateFromTime(ctx.Now())) {
label = " Yesterday"
}
ctx.Print(" " + label + " " + "Overall\n")
// Total:
ctx.Print("Total ")
total, _ := service.HypotheticalTotal(ctx.Now(), recent)
total, _ := service.HypotheticalTotal(ctx.Now(), recents...)
grandTotal, _ := service.HypotheticalTotal(ctx.Now(), records...)
ctx.Print(pad(10-len(total.ToString())) + styler.Duration(total, false))
ctx.Print(pad(11-len(grandTotal.ToString())) + styler.Duration(grandTotal, false))
ctx.Print("\n")
if opt.Diff {
// Should:
ctx.Print("Should ")
shouldTotal := service.ShouldTotalSum(recent)
shouldTotal := service.ShouldTotalSum(recents...)
grandShouldTotal := service.ShouldTotalSum(records...)
ctx.Print(pad(9-len(shouldTotal.ToString())) + styler.ShouldTotal(shouldTotal))
ctx.Print(pad(11-len(grandShouldTotal.ToString())) + styler.ShouldTotal(grandShouldTotal))
Expand Down Expand Up @@ -83,15 +83,17 @@ func (opt *Now) Run(ctx app.Context) error {
return handle()
}

func getTodayOrYesterday(now gotime.Time, records []Record) (Record, error) {
rs := service.Filter(records, service.FilterQry{
Dates: []Date{NewDateFromTime(now), NewDateFromTime(now).PlusDays(-1)},
})
rs = service.Sort(rs, false)
if len(rs) == 0 {
return nil, errors.New("No record found for today\n")
func getTodayOrYesterday(now gotime.Time, records []Record) ([]Record, error) {
rs := service.Sort(records, false)
for i := 0; i <= 1; i++ {
rs = service.Filter(records, service.FilterQry{
Dates: []Date{NewDateFromTime(now).PlusDays(-i)},
})
if len(rs) > 0 {
return rs, nil
}
}
return rs[0], nil
return nil, errors.New("No record found for today\n")
}

func withRepeat(ctx app.Context, fn func() error) error {
Expand Down
5 changes: 4 additions & 1 deletion src/app/cli/cmd_now_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ func TestPrintsTodaysEvalutaion(t *testing.T) {
12h5m
1999-03-14
4h
1h
1999-03-14
3h
13:15 - ?
`)._Run((&Now{}).Run)
require.Nil(t, err)
Expand Down
2 changes: 1 addition & 1 deletion src/app/cli/cmd_report_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestReportOfRecords(t *testing.T) {
2021-01-19
5m
`)._Run((&Report{}).Run)
`)._SetNow(2021, 3, 4, 0, 0)._Run((&Report{}).Run)
require.Nil(t, err)
assert.Equal(t, `
Total
Expand Down
2 changes: 1 addition & 1 deletion src/app/cli/cmd_total_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func TestTotalOfInput(t *testing.T) {
2150-11-10
Open ranges are not considered
16:00 - ?
`)._Run((&Total{}).Run)
`)._Run((&Total{WarnArgs: WarnArgs{NoWarn: true}}).Run)
require.Nil(t, err)
assert.Equal(t, "\nTotal: 2h\n(In 3 records)\n", out)
}
Expand Down
25 changes: 0 additions & 25 deletions src/service/check.go

This file was deleted.

49 changes: 0 additions & 49 deletions src/service/check_test.go

This file was deleted.

73 changes: 73 additions & 0 deletions src/service/sanitycheck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package service

import (
. "klog"
gotime "time"
)

type Warning struct {
Date Date
Message string
}

type checker interface {
Warn(Record) *Warning
}

func SanityCheck(reference gotime.Time, rs []Record) []Warning {
today := NewDateFromTime(reference)
sortedRs := Sort(rs, false)
var ws []Warning
checkers := []checker{
&unclosedOpenRangeChecker{today: today},
&futureEntriesChecker{today: today},
}
for _, r := range sortedRs {
for _, c := range checkers {
w := c.Warn(r)
if w != nil {
ws = append(ws, *w)
}
}
}
return ws
}

type unclosedOpenRangeChecker struct {
today Date
encounteredRecordAtToday bool
}

func (c *unclosedOpenRangeChecker) Warn(r Record) *Warning {
if r.Date().IsEqualTo(c.today) {
// Open ranges at today’s date are always okay
c.encounteredRecordAtToday = true
return nil
}
if !c.encounteredRecordAtToday && c.today.PlusDays(-1).IsEqualTo(r.Date()) {
// Open ranges at yesterday’s date are only okay if there is no entry today today
return nil
}
if r.OpenRange() != nil {
// Any other case is most likely a mistake
return &Warning{
Date: r.Date(),
Message: "Unclosed open range",
}
}
return nil
}

type futureEntriesChecker struct {
today Date
}

func (c *futureEntriesChecker) Warn(r Record) *Warning {
if r.Date().IsAfterOrEqual(c.today.PlusDays(1)) && len(r.Entries()) > 0 {
return &Warning{
Date: r.Date(),
Message: "Entry in future record",
}
}
return nil
}
96 changes: 96 additions & 0 deletions src/service/sanitycheck_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package service

import (
"github.com/stretchr/testify/require"
. "klog"
"testing"
gotime "time"
)

func TestNoWarningWhenAllGood(t *testing.T) {
timestamp := gotime.Now()
today := NewDateFromTime(timestamp)
now := NewTimeFromTime(timestamp)
rs := []Record{
func() Record {
// OK: Record in the future but without entries
r := NewRecord(today.PlusDays(1))
return r
}(), func() Record {
// OK: Open range today
r := NewRecord(today)
r.StartOpenRange(now, "")
return r
}(), func() Record {
// OK: Just a regular record in the past
r := NewRecord(today.PlusDays(-1))
r.AddDuration(NewDuration(1, 2), "")
return r
}(),
}
ws := SanityCheck(timestamp, rs)
require.Nil(t, ws)
}

func TestNoOpenRangeWarningWhenYesterdayAndNoRecordToday(t *testing.T) {
timestamp := gotime.Now()
today := NewDateFromTime(timestamp)
now := NewTimeFromTime(timestamp)
rs := []Record{
func() Record {
// This open range is okay, because there is no record at today’s date
r := NewRecord(today.PlusDays(-1))
r.StartOpenRange(now, "")
return r
}(), func() Record {
r := NewRecord(today.PlusDays(2))
return r
}(),
}
ws := SanityCheck(timestamp, rs)
require.Nil(t, ws)
}

func TestOpenRangeWarningWhenUnclosedOpenRangeBeforeTodayRegardlessOfOrder(t *testing.T) {
timestamp := gotime.Now()
today := NewDateFromTime(timestamp)
now := NewTimeFromTime(timestamp)
// The warnings must work reliably even when the records are not ordered by date initially
rs := []Record{
func() Record {
// NOT OK: There is a record at today’s date
r := NewRecord(today.PlusDays(-1))
r.StartOpenRange(now, "")
return r
}(), func() Record {
r := NewRecord(today)
return r
}(), func() Record {
// NOT OK: There is a record at today’s date
r := NewRecord(today.PlusDays(-2))
r.StartOpenRange(now, "")
return r
}(),
}
ws := SanityCheck(timestamp, rs)
require.NotNil(t, ws)
require.Len(t, ws, 2)
}

func TestFutureEntriesWarning(t *testing.T) {
timestamp := gotime.Now()
today := NewDateFromTime(timestamp)
rs := []Record{
func() Record {
r := NewRecord(today.PlusDays(1))
r.AddDuration(NewDuration(2, 0), "")
return r
}(), func() Record {
r := NewRecord(today)
return r
}(),
}
ws := SanityCheck(timestamp, rs)
require.NotNil(t, ws)
require.Len(t, ws, 1)
}

0 comments on commit f84c915

Please sign in to comment.