package workday import ( "encoding/json" "errors" "math/rand" "sort" "time" "velvettear/worklog/internal/config" "velvettear/worklog/internal/database" "velvettear/worklog/internal/tools" ) // exported function(s) func ToCSV(from time.Time, to time.Time, user string) string { report := getReportHeader() for _, workday := range getInRange(from, to, user) { report += "\n" + workday.Date + ";" + tools.TimeToHHMMSS(workday.Start) + ";" + tools.TimeToHHMMSS(workday.Stop) + ";" + tools.DurationToHHMMSS(workday.Duration) + ";" + tools.TimeToHHMMSS(workday.Pause.Start) + ";" + tools.TimeToHHMMSS(workday.Pause.Stop) + ";" + tools.DurationToHHMMSS(workday.Pause.Duration) + ";" + tools.DurationToHHMMSS(workday.Balance) } return report } func ToJSON(from time.Time, to time.Time, user string) (string, error) { workdays := getInRange(from, to, user) if len(workdays) == 0 { return "", nil } var jsonWorkdays []workdayJson for _, workday := range workdays { workdayJson := workdayJson{ User: user, Date: workday.Date, Start: tools.TimeToHHMMSS(workday.Start), Stop: tools.TimeToHHMMSS(workday.Stop), Duration: tools.DurationToHHMMSS(workday.Duration), } workdayJson.Open = workday.isOpen() if workday.Pause.Duration > 0 { pauseJson := pauseJson{ Start: tools.TimeToHHMMSS(workday.Pause.Start), Stop: tools.TimeToHHMMSS(workday.Pause.Stop), Duration: tools.DurationToHHMMSS(workday.Pause.Duration), } workdayJson.Pause = pauseJson } if !workday.isOpen() { workdayJson.Balance = tools.DurationToHHMMSS(workday.Balance) } jsonWorkdays = append(jsonWorkdays, workdayJson) } bytes, error := json.Marshal(jsonWorkdays) return string(bytes), error } func (workday *Workday) ToJSON(user string) (string, error) { workdayJson := workdayJson{ User: user, Date: workday.Date, Start: tools.TimeToHHMMSS(workday.Start), Stop: tools.TimeToHHMMSS(workday.Stop), Duration: tools.DurationToHHMMSS(workday.Duration), } workdayJson.Open = workday.isOpen() if workday.Pause.Duration > 0 { pauseJson := pauseJson{ Start: tools.TimeToHHMMSS(workday.Pause.Start), Stop: tools.TimeToHHMMSS(workday.Pause.Stop), Duration: tools.DurationToHHMMSS(workday.Pause.Duration), } workdayJson.Pause = pauseJson } if !workday.isOpen() { workdayJson.Balance = tools.DurationToHHMMSS(workday.Balance) } bytes, error := json.Marshal(workdayJson) return string(bytes), error } func GetOvertime(from time.Time, to time.Time, user string) string { workdays := getInRange(from, to, user) if len(workdays) == 0 { return "" } var overtime time.Duration for _, workday := range workdays { overtime += workday.Balance } return tools.DurationToHHMMSS(overtime) } func GetToday(user string) (Workday, error) { var workday Workday firstTimestamp := database.GetFirstTimestamp(user, time.Now()) if firstTimestamp.ID == 0 { return workday, errors.New("no workday for user '" + user + "' started today") } workday.Date = tools.TimeToDDMMYYYY(firstTimestamp.Start) workday.Start = firstTimestamp.Start workday.Stop = firstTimestamp.End workday.Duration = workday.Stop.Sub(workday.Start) currentTimestamp := database.GetLastTimestamp(user, time.Now()) workday.Stop = currentTimestamp.End if workday.Stop == tools.ZeroDate { workday.Duration = time.Since(workday.Start) } else { workday.Duration = workday.Stop.Sub(workday.Start) } workday.Balance = workday.Duration - time.Hour*8 return workday, nil } // unexported function(s) func getReportHeader() string { var reportHeader string for index, header := range config.ReportHeaders() { if index > 0 { reportHeader += ";" } reportHeader += header } return reportHeader } func getInRange(from time.Time, to time.Time, user string) []Workday { return aggregatedTimestampsToWorkdays(database.GetAggregatedTimestamps(from, to, user)) } func aggregatedTimestampsToWorkdays(aggregatedTimestamps map[string][]database.Timestamp) []Workday { var workdays []Workday for key, value := range aggregatedTimestamps { var tmpStart time.Time var tmpEnd time.Time var duration time.Duration for _, timestamp := range value { start := timestamp.Start end := timestamp.End if tmpStart == tools.ZeroDate || start.Before(tmpStart) { tmpStart = start } if tmpEnd == tools.ZeroDate || end.After(tmpEnd) { tmpEnd = end } duration = tmpEnd.Sub(tmpStart) } workday := Workday{ Date: key, Start: tmpStart, Stop: tmpEnd, Duration: duration, Balance: duration - time.Hour*8, } workday.insertFakePause() workdays = append(workdays, workday) tmpStart = time.Time{} tmpEnd = time.Time{} duration = 0 } sort.Slice(workdays, func(index, comparisonIndex int) bool { date, _ := tools.DDMMYYToDate(workdays[index].Date) comparisonDate, _ := tools.DDMMYYToDate(workdays[comparisonIndex].Date) return date.After(comparisonDate) }) return workdays } func (workday *Workday) isOpen() bool { return workday.Stop == tools.ZeroDate } func (workday *Workday) calculateBalance() { workday.Balance = workday.Duration - time.Hour*8 } func (workday *Workday) insertFakePause() { var pause Pause pauseLength := time.Minute * 30 if workday.Duration.Hours() >= 10 { pauseLength = time.Minute * 60 } deviationStart := rand.Intn(15-(-15)) + (-15) pause.Start = workday.Start.Add(workday.Duration / 2) pause.Start = pause.Start.Add(time.Minute * time.Duration(deviationStart)) deviationEnd := rand.Intn(300) pause.Stop = pause.Start.Add(pauseLength).Add(time.Second * time.Duration(deviationEnd)) pause.Duration = pause.Stop.Sub(pause.Start) workday.Pause = pause workday.Stop = workday.Stop.Add(workday.Pause.Duration) } // struct(s) type Workday struct { Date string Start time.Time Stop time.Time Duration time.Duration Pause Pause Balance time.Duration } type Pause struct { Start time.Time Stop time.Time Duration time.Duration } type workdayJson struct { User string Date string Start string Stop string Duration string Open bool Pause pauseJson Balance string } type pauseJson struct { Start string Stop string Duration string }