2023-01-27 17:09:18 +01:00
|
|
|
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) + ";" +
|
2023-02-02 12:48:05 +01:00
|
|
|
tools.DurationToHHMMSS(workday.Pause.Duration) + ";" +
|
|
|
|
tools.DurationToHHMMSS(workday.Balance)
|
2023-01-27 17:09:18 +01:00
|
|
|
}
|
|
|
|
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),
|
|
|
|
}
|
2023-01-30 10:31:47 +01:00
|
|
|
workdayJson.Open = workday.isOpen()
|
2023-01-27 17:09:18 +01:00
|
|
|
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
|
|
|
|
}
|
2023-02-02 12:48:05 +01:00
|
|
|
if !workday.isOpen() {
|
|
|
|
workdayJson.Balance = tools.DurationToHHMMSS(workday.Balance)
|
|
|
|
}
|
2023-01-27 17:09:18 +01:00
|
|
|
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),
|
|
|
|
}
|
2023-01-30 10:31:47 +01:00
|
|
|
workdayJson.Open = workday.isOpen()
|
2023-01-27 17:09:18 +01:00
|
|
|
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
|
|
|
|
}
|
2023-02-02 12:48:05 +01:00
|
|
|
if !workday.isOpen() {
|
|
|
|
workdayJson.Balance = tools.DurationToHHMMSS(workday.Balance)
|
|
|
|
}
|
2023-01-27 17:09:18 +01:00
|
|
|
bytes, error := json.Marshal(workdayJson)
|
|
|
|
return string(bytes), error
|
|
|
|
}
|
|
|
|
|
2023-02-02 12:48:05 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-01-27 17:09:18 +01:00
|
|
|
func GetToday(user string) (Workday, error) {
|
|
|
|
var workday Workday
|
2023-01-30 10:31:47 +01:00
|
|
|
firstTimestamp := database.GetFirstTimestamp(user, time.Now())
|
|
|
|
if firstTimestamp.ID == 0 {
|
2023-01-27 17:09:18 +01:00
|
|
|
return workday, errors.New("no workday for user '" + user + "' started today")
|
|
|
|
}
|
2023-01-30 10:31:47 +01:00
|
|
|
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
|
2023-01-27 17:09:18 +01:00
|
|
|
if workday.Stop == tools.ZeroDate {
|
2023-01-30 10:31:47 +01:00
|
|
|
workday.Duration = time.Since(workday.Start)
|
|
|
|
} else {
|
|
|
|
workday.Duration = workday.Stop.Sub(workday.Start)
|
2023-01-27 17:09:18 +01:00
|
|
|
}
|
2023-02-02 12:48:05 +01:00
|
|
|
workday.Balance = workday.Duration - time.Hour*8
|
2023-01-27 17:09:18 +01:00
|
|
|
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,
|
2023-02-02 12:48:05 +01:00
|
|
|
Balance: duration - time.Hour*8,
|
2023-01-27 17:09:18 +01:00
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-01-30 10:31:47 +01:00
|
|
|
func (workday *Workday) isOpen() bool {
|
|
|
|
return workday.Stop == tools.ZeroDate
|
|
|
|
}
|
|
|
|
|
2023-02-02 12:48:05 +01:00
|
|
|
func (workday *Workday) calculateBalance() {
|
|
|
|
workday.Balance = workday.Duration - time.Hour*8
|
|
|
|
}
|
|
|
|
|
2023-01-27 17:09:18 +01:00
|
|
|
func (workday *Workday) insertFakePause() {
|
|
|
|
var pause Pause
|
2023-02-02 12:48:05 +01:00
|
|
|
pauseLength := time.Minute * 30
|
|
|
|
if workday.Duration.Hours() >= 10 {
|
|
|
|
pauseLength = time.Minute * 60
|
|
|
|
}
|
2023-01-27 17:09:18 +01:00
|
|
|
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)
|
2023-02-02 12:48:05 +01:00
|
|
|
pause.Stop = pause.Start.Add(pauseLength).Add(time.Second * time.Duration(deviationEnd))
|
2023-01-27 17:09:18 +01:00
|
|
|
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
|
2023-02-02 12:48:05 +01:00
|
|
|
Balance time.Duration
|
2023-01-27 17:09:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2023-01-30 10:31:47 +01:00
|
|
|
Open bool
|
2023-01-27 17:09:18 +01:00
|
|
|
Pause pauseJson
|
2023-02-02 12:48:05 +01:00
|
|
|
Balance string
|
2023-01-27 17:09:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type pauseJson struct {
|
|
|
|
Start string
|
|
|
|
Stop string
|
|
|
|
Duration string
|
|
|
|
}
|