Skip to content

Commit

Permalink
Merge pull request #174 from RedemptionC/issue-173
Browse files Browse the repository at this point in the history
  • Loading branch information
dominikbraun authored Sep 12, 2021
2 parents 8006a3d + 0c08e97 commit a4dfe6e
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 17 deletions.
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

0 comments on commit a4dfe6e

Please sign in to comment.