From 14d605e728d6bb1a37b6488cf2fcd82cfd8f1273 Mon Sep 17 00:00:00 2001 From: rknizzle Date: Mon, 31 May 2021 23:24:04 -0400 Subject: [PATCH 1/5] Add BreakTimeToday to the Report and calculate it on Status --- core/timetrace.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/timetrace.go b/core/timetrace.go index 8f71bf4..393ba76 100644 --- a/core/timetrace.go +++ b/core/timetrace.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "os" + "sort" "time" "github.com/dominikbraun/timetrace/config" @@ -20,6 +21,7 @@ type Report struct { Current *Record TrackedTimeCurrent *time.Duration TrackedTimeToday time.Duration + BreakTimeToday time.Duration } // Filesystem represents a filesystem used for storing and loading resources. @@ -109,8 +111,14 @@ func (t *Timetrace) Status() (*Report, error) { return nil, err } + breakTimeToday, err := t.breakTime(now) + if err != nil { + return nil, err + } + report := &Report{ TrackedTimeToday: trackedTimeToday, + BreakTimeToday: breakTimeToday, } // If the latest record has been stopped, there is no active time tracking. @@ -129,6 +137,26 @@ func (t *Timetrace) Status() (*Report, error) { return report, nil } +func (t *Timetrace) breakTime(date time.Time) (time.Duration, error) { + records, err := t.loadAllRecords(date) + if err != nil { + return 0, err + } + + // sort the records by start time + sort.Slice(records, func(i, j int) bool { + return records[i].Start.Before(records[j].Start) + }) + + // add up the time between records + var breakTime time.Duration + for i := 0; i < len(records)-1; i++ { + breakTime += records[i+1].Start.Sub(*records[i].End) + } + + return breakTime, nil +} + // Stop stops the time tracking and marks the current record as ended. func (t *Timetrace) Stop() error { latestRecord, err := t.LoadLatestRecord() From 289529fa12890e48aba89c99e9158c196ce8bbba Mon Sep 17 00:00:00 2001 From: rknizzle Date: Mon, 31 May 2021 23:25:08 -0400 Subject: [PATCH 2/5] Add fxn to format the break time and output it for the status command --- cli/status.go | 3 ++- core/timetrace.go | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cli/status.go b/cli/status.go index 0bba9f7..0fe1dba 100644 --- a/cli/status.go +++ b/cli/status.go @@ -45,9 +45,10 @@ func statusCommand(t *core.Timetrace) *cobra.Command { project, trackedTimeCurrent, report.FormatTodayTime(), + report.FormatBreakTime(), }, } - out.Table([]string{"Current project", "Worked since start", "Worked today"}, rows, nil) + out.Table([]string{"Current project", "Worked since start", "Worked today", "Breaks"}, rows, nil) }, } diff --git a/core/timetrace.go b/core/timetrace.go index 393ba76..2cdfa8f 100644 --- a/core/timetrace.go +++ b/core/timetrace.go @@ -220,6 +220,12 @@ func (report *Report) FormatCurrentTime() string { return formatDuration(*report.TrackedTimeCurrent) } +// FormatBreakTime returns the formated string of the total time +// taking breaks today following the format convention +func (report *Report) FormatBreakTime() string { + return formatDuration(report.BreakTimeToday) +} + // formatDuration formats the passed duration into a string. // The format will be "8h 24min". If the duration is less then 60 secods // the format will be "0h 0min 12sec". From 9b537a45604a0163a9e2441418ed839226cafe6f Mon Sep 17 00:00:00 2001 From: rknizzle Date: Tue, 1 Jun 2021 17:10:30 -0400 Subject: [PATCH 3/5] Add function to load all records sorted in ascending order - It uses the less argument to RecordFilepaths to do the sorting --- core/record.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/core/record.go b/core/record.go index ca0d93b..9320c1d 100644 --- a/core/record.go +++ b/core/record.go @@ -158,6 +158,31 @@ func (t *Timetrace) loadAllRecords(date time.Time) ([]*Record, error) { return records, nil } +func (t *Timetrace) loadAllRecordsSortedAscending(date time.Time) ([]*Record, error) { + dir := t.fs.RecordDirFromDate(date) + + recordFilepaths, err := t.fs.RecordFilepaths(dir, func(a, b string) bool { + timeA, _ := time.Parse(recordLayout, a) + timeB, _ := time.Parse(recordLayout, b) + return timeA.Before(timeB) + }) + if err != nil { + return nil, err + } + + var records []*Record + + for _, recordFilepath := range recordFilepaths { + record, err := t.loadRecord(recordFilepath) + if err != nil { + return nil, err + } + records = append(records, record) + } + + return records, nil +} + // LoadLatestRecord loads the youngest record. This may also be a record from // another day. If there is no latest record, nil and no error will be returned. func (t *Timetrace) LoadLatestRecord() (*Record, error) { From 6a3d20351b62a9fab7e15fcc8397c25f3461b749 Mon Sep 17 00:00:00 2001 From: rknizzle Date: Tue, 1 Jun 2021 17:11:26 -0400 Subject: [PATCH 4/5] Replace sorting in breakTime with loadAllRecordsSortedAscending - I realized that fs.RecordFilepaths already has a mechanism for sorting the record filenames by start time so I think its better to take advantage of that and use loadAllRecordsSortedAscending instead of sorting in breakTime() --- core/timetrace.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/core/timetrace.go b/core/timetrace.go index 2cdfa8f..784a578 100644 --- a/core/timetrace.go +++ b/core/timetrace.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "os" - "sort" "time" "github.com/dominikbraun/timetrace/config" @@ -138,16 +137,11 @@ func (t *Timetrace) Status() (*Report, error) { } func (t *Timetrace) breakTime(date time.Time) (time.Duration, error) { - records, err := t.loadAllRecords(date) + records, err := t.loadAllRecordsSortedAscending(date) if err != nil { return 0, err } - // sort the records by start time - sort.Slice(records, func(i, j int) bool { - return records[i].Start.Before(records[j].Start) - }) - // add up the time between records var breakTime time.Duration for i := 0; i < len(records)-1; i++ { From 3536a41d79f31e7944c966f4e77a4d8e6677b4b9 Mon Sep 17 00:00:00 2001 From: rknizzle Date: Wed, 2 Jun 2021 21:54:46 -0400 Subject: [PATCH 5/5] Move Format*Time functions to the formatter package.. fixes #101 --- cli/status.go | 6 +++--- core/formatter.go | 36 ++++++++++++++++++++++++++++++++++++ core/timetrace.go | 36 ------------------------------------ 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/cli/status.go b/cli/status.go index 0fe1dba..5a03420 100644 --- a/cli/status.go +++ b/cli/status.go @@ -37,15 +37,15 @@ func statusCommand(t *core.Timetrace) *cobra.Command { trackedTimeCurrent := defaultString if report.TrackedTimeCurrent != nil { - trackedTimeCurrent = report.FormatCurrentTime() + trackedTimeCurrent = t.Formatter().FormatCurrentTime(report) } rows := [][]string{ { project, trackedTimeCurrent, - report.FormatTodayTime(), - report.FormatBreakTime(), + t.Formatter().FormatTodayTime(report), + t.Formatter().FormatBreakTime(report), }, } out.Table([]string{"Current project", "Worked since start", "Worked today", "Breaks"}, rows, nil) diff --git a/core/formatter.go b/core/formatter.go index 6357601..d5b273e 100644 --- a/core/formatter.go +++ b/core/formatter.go @@ -1,6 +1,7 @@ package core import ( + "fmt" "time" ) @@ -67,3 +68,38 @@ func (f *Formatter) ParseRecordKey(key string) (time.Time, error) { func (f *Formatter) RecordKey(record *Record) string { return record.Start.Format(f.RecordKeyLayout()) } + +// FormatTodayTime returns the formated string of the total +// time of today follwoing the format convention +func (f *Formatter) FormatTodayTime(report *Report) string { + return f.formatDuration(report.TrackedTimeToday) +} + +// FormatCurrentTime returns the formated string of the current +// report time follwoing the format convention +func (f *Formatter) FormatCurrentTime(report *Report) string { + return f.formatDuration(*report.TrackedTimeCurrent) +} + +// FormatBreakTime returns the formated string of the total time +// taking breaks today following the format convention +func (f *Formatter) FormatBreakTime(report *Report) string { + return f.formatDuration(report.BreakTimeToday) +} + +// formatDuration formats the passed duration into a string. +// The format will be "8h 24min". If the duration is less then 60 secods +// the format will be "0h 0min 12sec". +func (f *Formatter) formatDuration(duration time.Duration) string { + + hours := int64(duration.Hours()) % 60 + minutes := int64(duration.Minutes()) % 60 + secods := int64(duration.Seconds()) % 60 + + // as by convention if the duarion is < then 60 secods + // return "0h 0min Xsec" + if hours == 0 && minutes == 0 { + return fmt.Sprintf("0h 0min %dsec", secods) + } + return fmt.Sprintf("%dh %dmin", hours, minutes) +} diff --git a/core/timetrace.go b/core/timetrace.go index 784a578..09f1686 100644 --- a/core/timetrace.go +++ b/core/timetrace.go @@ -2,7 +2,6 @@ package core import ( "errors" - "fmt" "io" "os" "time" @@ -202,41 +201,6 @@ func (t *Timetrace) trackedTime(date time.Time) (time.Duration, error) { return trackedTime, nil } -// FormatTodayTime returns the formated string of the total -// time of today follwoing the format convention -func (report *Report) FormatTodayTime() string { - return formatDuration(report.TrackedTimeToday) -} - -// FormatCurrentTime returns the formated string of the current -// report time follwoing the format convention -func (report *Report) FormatCurrentTime() string { - return formatDuration(*report.TrackedTimeCurrent) -} - -// FormatBreakTime returns the formated string of the total time -// taking breaks today following the format convention -func (report *Report) FormatBreakTime() string { - return formatDuration(report.BreakTimeToday) -} - -// formatDuration formats the passed duration into a string. -// The format will be "8h 24min". If the duration is less then 60 secods -// the format will be "0h 0min 12sec". -func formatDuration(duration time.Duration) string { - - hours := int64(duration.Hours()) % 60 - minutes := int64(duration.Minutes()) % 60 - secods := int64(duration.Seconds()) % 60 - - // as by convention if the duarion is < then 60 secods - // return "0h 0min Xsec" - if hours == 0 && minutes == 0 { - return fmt.Sprintf("0h 0min %dsec", secods) - } - return fmt.Sprintf("%dh %dmin", hours, minutes) -} - func (t *Timetrace) isDirEmpty(dir string) (bool, error) { openedDir, err := os.Open(dir) if err != nil {