diff --git a/README.md b/README.md
index 3611614..3b7c584 100644
--- a/README.md
+++ b/README.md
@@ -7,9 +7,10 @@ simple workday recording with a basic rest api
| method | url | arguments | description |
| ------ | --- | --------- | ----------- |
-| `get` | /csv| from=DD.MM.YYYY
to=DD.MM.YYYY | get recordings (in range) as csv |
-| `get` | /json| from=DD.MM.YYYY
to=DD.MM.YYYY | get recordings (in range) as json |
+| `get` | /csv| from=DD.MM.YYYY
to=DD.MM.YYYY | get recordings (for specified range) as csv |
+| `get` | /json| from=DD.MM.YYYY
to=DD.MM.YYYY | get recordings (for specified range) as json |
| `get` | /today | | get aggregated recordings for today as json (if any) |
+| `get` | /overtime | | get aggregated overtime (for specified range) |
| `post` | /start | | start a new recording |
| `post` | /stop | | stop the current recording (if any) |
diff --git a/config.yml b/config.yml
index 8e7db46..328f14a 100644
--- a/config.yml
+++ b/config.yml
@@ -13,6 +13,7 @@ report:
- "Pause-Beginn"
- "Pause-Ende"
- "Pause-Dauer"
+ - "Saldo"
users:
exampleuser:
diff --git a/internal/api/server.go b/internal/api/server.go
index 9dc29ae..7aed1f7 100644
--- a/internal/api/server.go
+++ b/internal/api/server.go
@@ -30,7 +30,7 @@ func handleRequests(writer http.ResponseWriter, request *http.Request) {
requestMethod := strings.ToLower(request.Method)
user, authorized := isAuthorized(request)
if !authorized {
- log.Info("denying unauthorized '"+requestMethod+"' request", "path: "+requestPath)
+ log.Info("denying unauthorized '"+requestMethod+"' request", "path: '"+requestPath+"'")
writer.WriteHeader(404)
writer.Write([]byte("error: basic authorization failed\n"))
return
@@ -43,10 +43,10 @@ func handleRequests(writer http.ResponseWriter, request *http.Request) {
handled = handlePost(user, writer, request)
}
if handled {
- log.Debug("handled '"+requestMethod+"' request", "path: "+requestPath, "user: "+user)
+ log.Debug("handled '"+requestMethod+"' request", "path: '"+requestPath+"'", "user: '"+user+"'")
return
}
- log.Debug("ignoring '"+requestMethod+"' request", "path: "+requestPath, "user: "+user)
+ log.Debug("ignoring '"+requestMethod+"' request", "path: '"+requestPath+"'", "user: '"+user+"'")
writer.WriteHeader(501)
writer.Write([]byte("error: endpoint '" + requestPath + "' not implemented\n"))
}
@@ -78,6 +78,15 @@ func handleGet(user string, writer http.ResponseWriter, request *http.Request) b
}
writer.WriteHeader(200)
writer.Write([]byte(json + "\n"))
+ case "/overtime":
+ from, to := getTimespan(request.URL.Query())
+ overtime := workday.GetOvertime(from, to, user)
+ if len(overtime) == 0 {
+ writer.WriteHeader(404)
+ break
+ }
+ writer.WriteHeader(200)
+ writer.Write([]byte(overtime + "\n"))
case "/json":
from, to := getTimespan(request.URL.Query())
json, error := workday.ToJSON(from, to, user)
diff --git a/internal/tools/constants.go b/internal/tools/constants.go
index afaecce..f42dfdc 100644
--- a/internal/tools/constants.go
+++ b/internal/tools/constants.go
@@ -3,4 +3,4 @@ package tools
import "time"
var ZeroDate = time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
-var ReportHeaders = []string{"date", "workday start", "workday end", "workday duration", "pause start", "pause end", "pause duration"}
+var ReportHeaders = []string{"date", "workday start", "workday end", "workday duration", "pause start", "pause end", "pause duration", "balance"}
diff --git a/internal/tools/datetime.go b/internal/tools/datetime.go
index e7131f4..cc62909 100644
--- a/internal/tools/datetime.go
+++ b/internal/tools/datetime.go
@@ -48,6 +48,11 @@ func TimeToHHMMSS(time time.Time) string {
}
func DurationToHHMMSS(duration time.Duration) string {
+ negative := false
+ if duration < 0 {
+ duration = duration * -1
+ negative = true
+ }
durationSeconds := duration.Seconds()
durationHours := int(durationSeconds / 3600)
if durationHours > 0 {
@@ -71,6 +76,9 @@ func DurationToHHMMSS(duration time.Duration) string {
for len(tmpDuration) < 8 {
tmpDuration = "0" + tmpDuration
}
+ if negative {
+ tmpDuration = "-" + tmpDuration
+ }
return tmpDuration
}
diff --git a/internal/workday/workday.go b/internal/workday/workday.go
index b978616..ff56676 100644
--- a/internal/workday/workday.go
+++ b/internal/workday/workday.go
@@ -22,7 +22,8 @@ func ToCSV(from time.Time, to time.Time, user string) string {
tools.DurationToHHMMSS(workday.Duration) + ";" +
tools.TimeToHHMMSS(workday.Pause.Start) + ";" +
tools.TimeToHHMMSS(workday.Pause.Stop) + ";" +
- tools.DurationToHHMMSS(workday.Pause.Duration)
+ tools.DurationToHHMMSS(workday.Pause.Duration) + ";" +
+ tools.DurationToHHMMSS(workday.Balance)
}
return report
}
@@ -50,6 +51,9 @@ func ToJSON(from time.Time, to time.Time, user string) (string, error) {
}
workdayJson.Pause = pauseJson
}
+ if !workday.isOpen() {
+ workdayJson.Balance = tools.DurationToHHMMSS(workday.Balance)
+ }
jsonWorkdays = append(jsonWorkdays, workdayJson)
}
bytes, error := json.Marshal(jsonWorkdays)
@@ -73,10 +77,25 @@ func (workday *Workday) ToJSON(user string) (string, error) {
}
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())
@@ -94,6 +113,7 @@ func GetToday(user string) (Workday, error) {
} else {
workday.Duration = workday.Stop.Sub(workday.Start)
}
+ workday.Balance = workday.Duration - time.Hour*8
return workday, nil
}
@@ -135,6 +155,7 @@ func aggregatedTimestampsToWorkdays(aggregatedTimestamps map[string][]database.T
Start: tmpStart,
Stop: tmpEnd,
Duration: duration,
+ Balance: duration - time.Hour*8,
}
workday.insertFakePause()
workdays = append(workdays, workday)
@@ -154,13 +175,21 @@ 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(time.Minute * 30).Add(time.Second * time.Duration(deviationEnd))
+ 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)
@@ -173,6 +202,7 @@ type Workday struct {
Stop time.Time
Duration time.Duration
Pause Pause
+ Balance time.Duration
}
type Pause struct {
@@ -189,6 +219,7 @@ type workdayJson struct {
Duration string
Open bool
Pause pauseJson
+ Balance string
}
type pauseJson struct {