- Fix 'string(d)' int-to-rune conversion in rpg_test.go by using fmt.Sprintf - Fix ComputeDailyTotals dayStart logic: initial state should be StateWorking when dayStart > 0, correctly counting work from dayStart to first out event - Update README project structure to reflect new domain/handler/repo layout - Implement missing achievements: rpg_pioneer (on RPG enable) and salary_setter (on first rate set) - Add tryUnlockAchievement helper to Handler
69 lines
1.8 KiB
Go
69 lines
1.8 KiB
Go
package domain
|
|
|
|
import "sort"
|
|
|
|
// ComputeDailyTotals calculates the total work and break time from a list of events.
|
|
func ComputeDailyTotals(events []Event, minBreakThreshold int64, dayStart, dayEnd int64) DailyTotals {
|
|
if len(events) == 0 {
|
|
return DailyTotals{}
|
|
}
|
|
|
|
sorted := make([]Event, len(events))
|
|
copy(sorted, events)
|
|
sort.Slice(sorted, func(i, j int) bool {
|
|
return sorted[i].OccurredAt < sorted[j].OccurredAt
|
|
})
|
|
|
|
var totalWork, totalBreak int64
|
|
state := StateIdle
|
|
var workStart, breakStart int64
|
|
|
|
if dayStart > 0 {
|
|
state = StateWorking
|
|
workStart = dayStart
|
|
} else if sorted[0].EventType == "out" {
|
|
state = StateOnBreak
|
|
}
|
|
|
|
for _, e := range sorted {
|
|
switch {
|
|
case e.EventType == "in" && state == StateIdle:
|
|
state = StateWorking
|
|
workStart = e.OccurredAt
|
|
case e.EventType == "in" && state == StateOnBreak:
|
|
breakElapsed := e.OccurredAt - breakStart
|
|
if breakElapsed >= minBreakThreshold {
|
|
totalBreak += breakElapsed
|
|
} else {
|
|
totalWork += breakElapsed
|
|
}
|
|
state = StateWorking
|
|
workStart = e.OccurredAt
|
|
case e.EventType == "in" && state == StateWorking:
|
|
case e.EventType == "out" && state == StateWorking:
|
|
totalWork += e.OccurredAt - workStart
|
|
state = StateOnBreak
|
|
breakStart = e.OccurredAt
|
|
case e.EventType == "out" && state == StateOnBreak:
|
|
breakStart = e.OccurredAt
|
|
case e.EventType == "out" && state == StateIdle:
|
|
state = StateOnBreak
|
|
breakStart = e.OccurredAt
|
|
}
|
|
}
|
|
|
|
if state == StateWorking && dayEnd > 0 {
|
|
totalWork += dayEnd - workStart
|
|
}
|
|
if state == StateOnBreak && dayEnd > 0 {
|
|
breakElapsed := dayEnd - breakStart
|
|
if breakElapsed >= minBreakThreshold {
|
|
totalBreak += breakElapsed
|
|
} else {
|
|
totalWork += breakElapsed
|
|
}
|
|
}
|
|
|
|
return DailyTotals{TotalSeconds: totalWork, BreakSeconds: totalBreak}
|
|
}
|