Skip to content

Commit

Permalink
Merge pull request #12 from vshn/format
Browse files Browse the repository at this point in the history
Better formatted overtime hours
  • Loading branch information
ccremer authored Nov 9, 2021
2 parents 8981059 + 3660423 commit e8b5417
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 2 deletions.
20 changes: 18 additions & 2 deletions pkg/web/html/overtimereport_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (v *OvertimeReportView) formatDailySummary(daily *timesheet.DailySummary) V
basic := Values{
"Weekday": daily.Date.Weekday(),
"Date": daily.Date.Format(odoo.DateFormat),
"OvertimeHours": strconv.FormatFloat(daily.CalculateOvertime().Hours(), 'f', 2, 64),
"OvertimeHours": formatDurationInHours(daily.CalculateOvertime()),
"LeaveType": "",
}
if daily.HasAbsences() {
Expand All @@ -35,9 +35,25 @@ func (v *OvertimeReportView) formatDailySummary(daily *timesheet.DailySummary) V
return basic
}

// formatDurationInHours returns a human friendly "0:00"-formatted duration.
// Seconds within a minute are rounded up or down to the nearest full minute.
// A sign ("-") is prefixed if duration is negative.
func formatDurationInHours(d time.Duration) string {
sign := ""
if d.Seconds() < 0 {
sign = "-"
d = time.Duration(d.Nanoseconds() * -1)
}
d = d.Round(time.Minute)
h := d / time.Hour
d -= h * time.Hour
m := d / time.Minute
return fmt.Sprintf("%s%d:%02d", sign, h, m)
}

func (v *OvertimeReportView) formatSummary(s timesheet.Summary) Values {
return Values{
"TotalOvertime": s.TotalOvertime.Truncate(time.Minute),
"TotalOvertime": formatDurationInHours(s.TotalOvertime.Truncate(time.Minute)),
// TODO: Might not be accurate for days before 2021
"TotalLeaves": fmt.Sprintf("%sd", strconv.FormatFloat(s.TotalLeaveDays.Hours()/8, 'f', 0, 64)),
}
Expand Down
85 changes: 85 additions & 0 deletions pkg/web/html/overtimereport_view_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package html

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestOvertimeReportView_formatDurationHumanFriendly(t *testing.T) {
tests := map[string]struct {
givenDuration time.Duration
expectedOutcome string
}{
"GivenNoDuration_ThenReturnZero": {
givenDuration: time.Duration(0),
expectedOutcome: "0:00",
},
"GivenPositiveDuration_WhenDurationMoreThan30s_ThenRoundUp": {
givenDuration: parseDuration(t, "30s"),
expectedOutcome: "0:01",
},
"GivenPositiveDuration_WhenDurationMoreThan30s_ThenRoundUpEdgeCase": {
givenDuration: parseDuration(t, "1m59s"),
expectedOutcome: "0:02",
},
"GivenPositiveDuration_WhenDurationLessThan30s_ThenRoundDown": {
givenDuration: parseDuration(t, "29s"),
expectedOutcome: "0:00",
},
"GivenPositiveDuration_WhenUnder1Hour_ThenReturnMinutesOnly": {
givenDuration: parseDuration(t, "38m"),
expectedOutcome: "0:38",
},
"GivenPositiveDuration_WhenOver1Hour_ThenReturnHoursAndMinutes": {
givenDuration: parseDuration(t, "1h38m"),
expectedOutcome: "1:38",
},
"GivenPositiveDuration_WhenOver10Hour_ThenReturnHoursAndMinutes": {
givenDuration: parseDuration(t, "10h38m"),
expectedOutcome: "10:38",
},
"GivenPositiveDuration_WhenOver100Hour_ThenReturnHoursAndMinutes": {
givenDuration: parseDuration(t, "100h38m"),
expectedOutcome: "100:38",
},
"GivenNegativeDuration_WhenDurationMoreThan30s_ThenRoundUp": {
givenDuration: parseDuration(t, "-30s"),
expectedOutcome: "-0:01",
},
"GivenNegativeDuration_WhenDurationLessThan30s_ThenRoundDown": {
givenDuration: parseDuration(t, "-29s"),
expectedOutcome: "-0:00",
},
"GivenNegativeDuration_WhenUnder1Hour_ThenReturnMinutesOnly": {
givenDuration: parseDuration(t, "-38m"),
expectedOutcome: "-0:38",
},
"GivenNegativeDuration_WhenOver1Hour_ThenReturnHoursAndMinutes": {
givenDuration: parseDuration(t, "-1h38m"),
expectedOutcome: "-1:38",
},
"GivenNegativeDuration_WhenOver10Hour_ThenReturnHoursAndMinutes": {
givenDuration: parseDuration(t, "-10h38m"),
expectedOutcome: "-10:38",
},
"GivenNegativeDuration_WhenOver100Hour_ThenReturnHoursAndMinutes": {
givenDuration: parseDuration(t, "-100h38m"),
expectedOutcome: "-100:38",
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
result := formatDurationInHours(tt.givenDuration)
assert.Equal(t, tt.expectedOutcome, result)
})
}
}

func parseDuration(t *testing.T, format string) time.Duration {
d, err := time.ParseDuration(format)
require.NoError(t, err)
return d
}

0 comments on commit e8b5417

Please sign in to comment.