Skip to content

Commit

Permalink
extracted duration
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp Galichkin committed Aug 23, 2024
1 parent a2f2d6e commit 2628563
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 40 deletions.
Binary file modified bin/osx/jtl-osx-x64
Binary file not shown.
37 changes: 26 additions & 11 deletions cmd/duration/duration.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,29 @@ import (
"unicode/utf8"
)

const EIGHT_HOURS_IN_MIN = 8 * 60
type Time struct {
time.Time
}

const EightHoursInMin = 8 * 60

func MinutesToDurationString(minutes int) string {
func ToString(minutes int) string {
if minutes <= 0 {
return "0m"
}
durationString := (time.Duration(minutes) * time.Minute).String()
return strings.TrimSuffix(strings.TrimSuffix(strings.TrimSuffix(durationString, "0s"), "0S"), "0m")
}

// durationToMinutes converts string duration d "2D", "4h", "2H 30m", "1d 7h 40m", etc, to minutes.
// ToMinutes converts string duration d "2D", "4h", "2H 30m", "1d 7h 40m", etc, to minutes.
// if it fails to process a duration, it returns (-1, error)
func DurationToMinutes(d string) int {
func ToMinutes(d string) int {
duration := strings.ToLower(d)

sub := strings.SplitN(duration, " ", 2)
if len(sub) > 1 {
v0 := DurationToMinutes(sub[0])
v1 := DurationToMinutes(sub[1])
v0 := ToMinutes(sub[0])
v1 := ToMinutes(sub[1])
return v0 + v1
}
//TODO add restrictions for 1h = 60m, ...
Expand All @@ -45,12 +49,23 @@ func DurationToMinutes(d string) int {
}
}

func DateTimeToTime(date string) time.Time {
// ParseTime converts a string date into time.Time using custom datetime pattern from the config
func ParseTime(date string) Time {
t, _ := time.Parse(config.DefaultDateTimePattern, date)
return t
return Time{t}
}

// ParseTimeTruncatedToDate converts a string date into time.Time using custom datetime pattern from the config, then trims the time part to zeroes
func ParseTimeTruncatedToDate(date string) Time {
return ParseTime(date).TruncateToDate()
}

func DateTimeToDate(date string) time.Time {
d := DateTimeToTime(date).Truncate(24 * time.Hour)
return d
func (t Time) TruncateToDate() Time {
return Time{t.Truncate(24 * time.Hour)}
}

// Overrides

func (t Time) Compare(other Time) int {
return t.Time.Compare(other.Time)
}
5 changes: 2 additions & 3 deletions cmd/duration/duration_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package duration

import (
"reflect"
"testing"
"time"
)
Expand All @@ -22,8 +21,8 @@ func TestDateTimeToDate(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := DateTimeToDate(tt.args.date); !reflect.DeepEqual(got, tt.want) {
t.Errorf("DateTimeToDate() = %v, want %v", got, tt.want)
if got := ParseTimeTruncatedToDate(tt.args.date); got.Compare(Time{tt.want}) != 0 {
t.Errorf("ParseTimeTruncatedToDate() = %v, want %v", got, tt.want)
}
})
}
Expand Down
6 changes: 3 additions & 3 deletions cmd/internal/report/dailyReport.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ func (r *DailyReport) Print() {
"today: " + time.Now().Format(config.DefaultDatePattern),
"", //ticket
fmt.Sprintf("%v (%v)",
duration.MinutesToDurationString(r.timeSpentInMinutes),
duration.MinutesToDurationString(r.timeSpentInMinutesToday)), //time tracked
duration.ToString(r.timeSpentInMinutes),
duration.ToString(r.timeSpentInMinutesToday)), //time tracked
"", //comment
fmt.Sprintf("%v/%v", totalPushed, r.tasksToday), //pushed to jira
})
t.Render()
}

func addTimeSpent(r csv.CsvRec, timeSpentInMinutes int) int {
tsm := duration.DurationToMinutes(r.TimeSpent)
tsm := duration.ToMinutes(r.TimeSpent)
return timeSpentInMinutes + tsm
}
6 changes: 3 additions & 3 deletions cmd/internal/report/monthlyReport.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func NewMonthlyReport(csvRecords csv.CsvRecords) *MonthlyReport {
if r.IsPushed() {
wr.pushedTasks++
}
wr.totalMinutes += duration.DurationToMinutes(r.TimeSpent)
wr.totalMinutes += duration.ToMinutes(r.TimeSpent)
}
//Summarize totals from weekly reports
for _, wr := range mr.weeklyReports {
Expand All @@ -56,13 +56,13 @@ func (r *MonthlyReport) Print() {
t.AppendRow([]interface{}{
fmt.Sprintf("%v - %v", wr.weekStart, wr.weekEnd),
fmt.Sprintf("%v (%v)", wr.totalTasks, wr.pushedTasks),
duration.MinutesToDurationString(wr.totalMinutes),
duration.ToString(wr.totalMinutes),
})
}
t.AppendFooter(table.Row{
"Total for: " + config.GetCurrentDataFileName(),
fmt.Sprintf("%v (%v)", r.totalTasks, r.totalTasksPushed),
duration.MinutesToDurationString(r.totalMinutes),
duration.ToString(r.totalMinutes),
})
t.Render()
}
Expand Down
14 changes: 5 additions & 9 deletions cmd/internal/report/monthlyReport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package report
import (
"testing"

"github.com/philgal/jtl/cmd/duration"
"github.com/philgal/jtl/cmd/internal/csv"
"github.com/stretchr/testify/assert"
)
Expand All @@ -27,19 +28,14 @@ func Test_timeSpentToMinutes(t *testing.T) {
name string
timeSpent string
want int
wantErr bool
}{
{"Should parse 1h", "1h", 60, false},
{"Should parse 1d 2h", "1d 2h", (8 + 2) * 60, false},
{"Should parse 2d 3h 25m", "2d 3h 25m", (16+3)*60 + 25, false},
{"Should parse 1h", "1h", 60},
{"Should parse 1d 2h", "1d 2h", (8 + 2) * 60},
{"Should parse 2d 3h 25m", "2d 3h 25m", (16+3)*60 + 25},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := durationToMinutes(tt.timeSpent)
if (err != nil) != tt.wantErr {
t.Errorf("timeSpentToMinutes() error = %v, wantErr %v", err, tt.wantErr)
return
}
got := duration.ToMinutes(tt.timeSpent)
if got != tt.want {
t.Errorf("timeSpentToMinutes() = %v, want %v", got, tt.want)
}
Expand Down
22 changes: 11 additions & 11 deletions cmd/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func runLogCommand(cmd *cobra.Command, args []string) {

// calculate how much is left of the startedTs
slices.SortFunc(fcsv.Records, func(a csv.CsvRec, b csv.CsvRec) int {
return duration.DateTimeToDate(a.StartedTs).Compare(duration.DateTimeToDate(b.StartedTs))
return duration.ParseTimeTruncatedToDate(a.StartedTs).Compare(duration.ParseTimeTruncatedToDate(b.StartedTs))
})

logDate, _ := time.Parse(config.DefaultDateTimePattern, startedTs)
Expand All @@ -112,24 +112,24 @@ func runLogCommand(cmd *cobra.Command, args []string) {
timeSpentToDate := timeSpentToDateInMin(&sameDateRecs, logDate)
// for example 500 > 480 -> 20m to log
// fixme: make max daily duration configurable
if timeSpentToDate >= duration.EIGHT_HOURS_IN_MIN {
fmt.Printf("You have already logged %s, will not log more\n", duration.MinutesToDurationString(timeSpentToDate))
if timeSpentToDate >= duration.EightHoursInMin {
fmt.Printf("You have already logged %s, will not log more\n", duration.ToString(timeSpentToDate))
return
}

fmt.Printf("Same date records: %v\n", sameDateRecs)

if duration.DurationToMinutes(timeSpent)+timeSpentToDate <= duration.EIGHT_HOURS_IN_MIN {
timeSpentMin := int(math.Min(float64(duration.EIGHT_HOURS_IN_MIN-timeSpentToDate), duration.EIGHT_HOURS_IN_MIN/2))
timeSpent = duration.MinutesToDurationString(timeSpentMin)
if duration.ToMinutes(timeSpent)+timeSpentToDate <= duration.EightHoursInMin {
timeSpentMin := int(math.Min(float64(duration.EightHoursInMin-timeSpentToDate), duration.EightHoursInMin/2))
timeSpent = duration.ToString(timeSpentMin)
fmt.Printf("Time spent will is trimmed to %s, to not to exceed %s\n",
timeSpent,
duration.MinutesToDurationString(duration.EIGHT_HOURS_IN_MIN))
duration.ToString(duration.EightHoursInMin))
// adjust startedTs to the last record: last rec.StartedTs + calculated time spent = new startedTs
if reclen := len(sameDateRecs); reclen > 0 {
lastRec := sameDateRecs[reclen-1]
lastRecStaredAt := duration.DateTimeToTime(lastRec.StartedTs)
startedTs = lastRecStaredAt.Add(time.Minute * time.Duration(duration.DurationToMinutes(lastRec.TimeSpent))).Format(config.DefaultDateTimePattern)
lastRecStaredAt := duration.ParseTime(lastRec.StartedTs)
startedTs = lastRecStaredAt.Add(time.Minute * time.Duration(duration.ToMinutes(lastRec.TimeSpent))).Format(config.DefaultDateTimePattern)
}
}

Expand All @@ -154,7 +154,7 @@ func timeSpentToDateInMin(sameDateRecs *csv.CsvRecords, logDate time.Time) int {
recDate, _ := time.Parse(config.DefaultDateTimePattern, rec.StartedTs)
// logs files are already collected by months of the year, so it's enough to compare days
if recDate.Day() == logDate.Day() {
totalTimeSpentOnDate += duration.DurationToMinutes(rec.TimeSpent)
totalTimeSpentOnDate += duration.ToMinutes(rec.TimeSpent)
} else {
// items are sorted, so first occurrence of mismatched date means there will no more matches.
return totalTimeSpentOnDate
Expand All @@ -165,6 +165,6 @@ func timeSpentToDateInMin(sameDateRecs *csv.CsvRecords, logDate time.Time) int {

func sameDateRecords(recs *csv.CsvRecords, logDate time.Time) csv.CsvRecords {
return (*recs).Filter(func(rec csv.CsvRec) bool {
return duration.DateTimeToDate(rec.StartedTs).Equal(logDate.Truncate(24 * time.Hour))
return duration.ParseTimeTruncatedToDate(rec.StartedTs).Equal(logDate.Truncate(24 * time.Hour))
})
}

0 comments on commit 2628563

Please sign in to comment.