Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

print more info when created record collides with others #174

Merged
merged 11 commits into from
Sep 12, 2021
1 change: 0 additions & 1 deletion cli/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ func createRecordCommand(t *core.Timetrace) *cobra.Command {
return
}
if collides {
out.Err("Record collides with other record!")
return
}

Expand Down
53 changes: 48 additions & 5 deletions core/timetrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
"io"
"os"
"path/filepath"
"strconv"
"time"

"github.com/dominikbraun/timetrace/config"
"github.com/dominikbraun/timetrace/out"
)

const (
Expand Down Expand Up @@ -273,6 +275,37 @@ func (t *Timetrace) latestNonEmptyDir(dirs []string) (string, error) {
return "", ErrAllDirectoriesEmpty
}

func printCollisions(t *Timetrace, records []*Record) {
out.Err("collides with these records :")

rows := make([][]string, len(records))

for i, record := range records {
end := "still running"
if record.End != nil {
end = t.Formatter().TimeString(*record.End)
}

billable := "no"

if record.IsBillable {
billable = "yes"
}

rows[i] = make([]string, 6)
rows[i][0] = strconv.Itoa(i + 1)
rows[i][1] = t.Formatter().RecordKey(record)
rows[i][2] = record.Project.Key
rows[i][3] = t.Formatter().TimeString(record.Start)
rows[i][4] = end
rows[i][5] = billable
}

out.Table([]string{"#", "Key", "Project", "Start", "End", "Billable"}, rows, []string{})

out.Warn(" start and end of the record should not overlap with others")
}

// RecordCollides checks if the time of a record collides
// with other records of the same day and returns a bool
func (t *Timetrace) RecordCollides(toCheck Record) (bool, error) {
Expand All @@ -291,21 +324,31 @@ func (t *Timetrace) RecordCollides(toCheck Record) (bool, error) {
}
}

return collides(toCheck, allRecords), nil
collide, collidingRecords := collides(toCheck, allRecords)
if collide {
printCollisions(t, collidingRecords)
}

return collide, nil
}

func collides(toCheck Record, allRecords []*Record) bool {
func collides(toCheck Record, allRecords []*Record) (bool, []*Record) {
collide := false
collidingRecords := make([]*Record, 0)
for _, rec := range allRecords {

if rec.End != nil && rec.Start.Before(*toCheck.End) && rec.End.After(toCheck.Start) {
return true
collidingRecords = append(collidingRecords, rec)
collide = true
}

if rec.End == nil && toCheck.End.After(rec.Start) {
return true
collidingRecords = append(collidingRecords, rec)
collide = true
}
}

return false
return collide, collidingRecords
}

// isBackFile checks if a given filename is a backup-file
Expand Down
63 changes: 52 additions & 11 deletions core/timetrace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,31 @@ func newTestRecTracked(s int) Record {
return Record{Start: start}
}

func checkConsistent(t *testing.T, expect, result []*Record) {
sameLen := len(result) == len(expect)
sameContent := true

if sameLen {
for i := range result {
if expect[i] != result[i] {
sameContent = false
}
}
}

if !(sameLen && sameContent) {
t.Errorf("should collide with :\n")
for _, r := range expect {
t.Errorf("%v\n", r)
}
t.Errorf("while collides return :\n")
for _, r := range result {
t.Errorf("%v\n", r)
}
}

}

func TestCollides(t *testing.T) {
savedRec := newTestRecord(-60, -1)
allRecs := []*Record{&savedRec}
Expand All @@ -25,77 +50,93 @@ func TestCollides(t *testing.T) {
// rec1 starts and end after savedRec
rec1 := newTestRecord(-1, 0)

if collides(rec1, allRecs) {
if collide, collidingRecs := collides(rec1, allRecs); collide && len(collidingRecs) == 0 {
t.Error("records should not collide")
}

// rec2 starts in savedRec, ends after
rec2 := newTestRecord(-30, 1)

if !collides(rec2, allRecs) {
if collide, collidingRecs := collides(rec2, allRecs); !collide {
t.Error("records should collide")
} else {
checkConsistent(t, allRecs, collidingRecs)
}

// rec3 start before savedRec, ends inside
rec3 := newTestRecord(-75, -30)

if !collides(rec3, allRecs) {
if collide, collidingRecs := collides(rec3, allRecs); !collide {
t.Error("records should collide")
} else {
checkConsistent(t, allRecs, collidingRecs)
}

// rec4 starts and ends before savedRec
rec4 := newTestRecord(-75, -70)

if collides(rec4, allRecs) {
if collide, collidingRecs := collides(rec4, allRecs); collide && len(collidingRecs) == 0 {
t.Error("records should not collide")
}

// rec5 starts and ends inside savedRec
rec5 := newTestRecord(-40, -20)

if !collides(rec5, allRecs) {
if collide, collidingRecs := collides(rec5, allRecs); !collide {
t.Error("records should collide")
} else {
checkConsistent(t, allRecs, collidingRecs)
}

// rec6 starts before and ends after savedRec
rec6 := newTestRecord(-70, 10)

if !collides(rec6, allRecs) {
if collide, collidingRecs := collides(rec6, allRecs); !collide {
t.Error("records should collide")
} else {
checkConsistent(t, allRecs, collidingRecs)
}

// rec7 starts and ends at the same time as savedRec
rec7 := newTestRecord(-60, -1)

if !collides(rec7, allRecs) {
if collide, collidingRecs := collides(rec7, allRecs); !collide {
t.Error("records should collide")
} else {
checkConsistent(t, allRecs, collidingRecs)
}

// rec7 starts at the same time as savedRecTracked
rec8 := newTestRecord(-60, -1)

if !collides(rec8, allRecsTracked) {
if collide, collidingRecs := collides(rec8, allRecsTracked); !collide {
t.Error("records should collide")
} else {
checkConsistent(t, allRecsTracked, collidingRecs)
}

// rec9 ends at the time savedRecTracked ends
rec9 := newTestRecord(-80, -60)

if !collides(rec9, allRecsTracked) {
if collide, collidingRecs := collides(rec9, allRecsTracked); !collide {
t.Error("records should collide")
} else {
checkConsistent(t, allRecsTracked, collidingRecs)
}

// rec10 ends after savedRecTracked starts
rec10 := newTestRecord(-80, -50)

if !collides(rec10, allRecsTracked) {
if collide, collidingRecs := collides(rec10, allRecsTracked); !collide {
t.Error("records should collide")
} else {
checkConsistent(t, allRecsTracked, collidingRecs)
}

// rec11 ends before savedRecTracked starts
rec11 := newTestRecord(-80, -70)

if collides(rec11, allRecsTracked) {
if collide, collidingRecs := collides(rec11, allRecsTracked); collide && len(collidingRecs) == 0 {
t.Error("records should not collide")
}
}
Expand Down