Compare commits
No commits in common. "056e749085d48403325a7f4cd591edc1ee0d93a4" and "4998e24dfc5f222de44081aa71f0736577444b5a" have entirely different histories.
056e749085
...
4998e24dfc
12 changed files with 32 additions and 195 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
__debug_bin
|
|
||||||
worklog
|
|
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
|
@ -6,11 +6,7 @@
|
||||||
"type": "go",
|
"type": "go",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"mode": "auto",
|
"mode": "auto",
|
||||||
"program": "${workspaceFolder}/main.go",
|
"program": "${workspaceFolder}/main.go"
|
||||||
"args": [
|
|
||||||
"-c",
|
|
||||||
"/home/velvettear/worklog/config.yml"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
38
README.md
38
README.md
|
@ -1,39 +1,3 @@
|
||||||
# worklog
|
# worklog
|
||||||
|
|
||||||
simple working day time tracking with a basic rest api
|
simple workday recording with a basic rest api
|
||||||
|
|
||||||
## endpoints
|
|
||||||
|
|
||||||
| method | url | arguments | description |
|
|
||||||
| ------ | --- | --------- | ----------- |
|
|
||||||
| `get` | /csv| from=DD.MM.YYYY<br>to=DD.MM.YYYY | get recordings (for specified range) as csv |
|
|
||||||
| `get` | /json| from=DD.MM.YYYY<br>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) |
|
|
||||||
|
|
||||||
**note:**
|
|
||||||
the api uses basic authentication so each request has to include an `Authorization` request header.
|
|
||||||
|
|
||||||
## examples
|
|
||||||
|
|
||||||
**start a new recording:**
|
|
||||||
curl -X POST -u username:password http://localhost:3333/start
|
|
||||||
|
|
||||||
**stop the current recording:**
|
|
||||||
curl -X POST -u username:password http://localhost:3333/stop
|
|
||||||
|
|
||||||
**get all recordings as json:**
|
|
||||||
curl -u username:password http://localhost:3333/json
|
|
||||||
|
|
||||||
**get all recordings for january 2023 as csv:**
|
|
||||||
curl -u username:password http://localhost:3333/csv?from=01.01.2023&to=31.01.2023
|
|
||||||
|
|
||||||
## configuration
|
|
||||||
|
|
||||||
configuration is entirely done inside the file `config.yml`.
|
|
||||||
you can specify the location of the config file with the optional argument `--config` or `-c` when starting the server.
|
|
||||||
otherwise the program will try to find a config file at the following locations:
|
|
||||||
- `$HOME/.config/worklog/config.yml`
|
|
||||||
- `$PWD/config.yml`
|
|
|
@ -1,4 +1,4 @@
|
||||||
database: /opt/worklog/worklog.sqlite
|
database: /home/velvettear/worklog/worklog.sqlite
|
||||||
|
|
||||||
server:
|
server:
|
||||||
listen: "0.0.0.0"
|
listen: "0.0.0.0"
|
||||||
|
@ -13,10 +13,9 @@ report:
|
||||||
- "Pause-Beginn"
|
- "Pause-Beginn"
|
||||||
- "Pause-Ende"
|
- "Pause-Ende"
|
||||||
- "Pause-Dauer"
|
- "Pause-Dauer"
|
||||||
- "Saldo"
|
|
||||||
|
|
||||||
users:
|
users:
|
||||||
exampleuser:
|
dsommer:
|
||||||
password: "examplepassword"
|
password: "$Velvet90"
|
||||||
|
|
||||||
debug: true
|
debug: true
|
|
@ -1,12 +0,0 @@
|
||||||
version: "3"
|
|
||||||
|
|
||||||
services:
|
|
||||||
worklog:
|
|
||||||
build: ./docker
|
|
||||||
container_name: worklog
|
|
||||||
restart: unless-stopped
|
|
||||||
volumes:
|
|
||||||
- ./config:/root/.config/worklog
|
|
||||||
- ./data:/opt/worklog
|
|
||||||
ports:
|
|
||||||
- 5000:5000
|
|
|
@ -1,29 +0,0 @@
|
||||||
FROM alpine:3.17
|
|
||||||
|
|
||||||
LABEL version="1.0.0" \
|
|
||||||
author="Daniel Sommer <daniel.sommer@nux.de>" \
|
|
||||||
license="MIT"
|
|
||||||
|
|
||||||
MAINTAINER Daniel Sommer <daniel.sommer@nux.de>
|
|
||||||
|
|
||||||
ENV LANG=C.UTF-8
|
|
||||||
|
|
||||||
RUN apk upgrade --no-cache --progress \
|
|
||||||
&& apk add --no-cache --progress \
|
|
||||||
tzdata \
|
|
||||||
git \
|
|
||||||
go \
|
|
||||||
&& ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime \
|
|
||||||
&& echo "Europe/Berlin" > /etc/timezone \
|
|
||||||
&& git clone https://git.velvettear.de/velvettear/worklog.git /tmp/worklog \
|
|
||||||
&& cd /tmp/worklog \
|
|
||||||
&& go build \
|
|
||||||
&& mv /tmp/worklog/worklog /usr/bin/worklog \
|
|
||||||
&& rm -rf /tmp/worklog \
|
|
||||||
&& apk del --no-cache --progress \
|
|
||||||
git \
|
|
||||||
go
|
|
||||||
|
|
||||||
EXPOSE 5000
|
|
||||||
|
|
||||||
ENTRYPOINT ["worklog"]
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"velvettear/worklog/internal/config"
|
"velvettear/worklog/internal/config"
|
||||||
"velvettear/worklog/internal/database"
|
"velvettear/worklog/internal/database"
|
||||||
"velvettear/worklog/internal/log"
|
"velvettear/worklog/internal/log"
|
||||||
"velvettear/worklog/internal/tools"
|
|
||||||
"velvettear/worklog/internal/workday"
|
"velvettear/worklog/internal/workday"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,7 +29,7 @@ func handleRequests(writer http.ResponseWriter, request *http.Request) {
|
||||||
requestMethod := strings.ToLower(request.Method)
|
requestMethod := strings.ToLower(request.Method)
|
||||||
user, authorized := isAuthorized(request)
|
user, authorized := isAuthorized(request)
|
||||||
if !authorized {
|
if !authorized {
|
||||||
log.Info("denying unauthorized '"+requestMethod+"' request", "path: '"+requestPath+"'")
|
log.Debug("denying unauthorized '" + requestMethod + "' request for path '" + requestPath + "'")
|
||||||
writer.WriteHeader(404)
|
writer.WriteHeader(404)
|
||||||
writer.Write([]byte("error: basic authorization failed\n"))
|
writer.Write([]byte("error: basic authorization failed\n"))
|
||||||
return
|
return
|
||||||
|
@ -38,15 +37,16 @@ func handleRequests(writer http.ResponseWriter, request *http.Request) {
|
||||||
handled := false
|
handled := false
|
||||||
switch requestMethod {
|
switch requestMethod {
|
||||||
case "get":
|
case "get":
|
||||||
|
log.Debug("handling '" + requestMethod + "' request for path '" + requestPath + "'...")
|
||||||
handled = handleGet(user, writer, request)
|
handled = handleGet(user, writer, request)
|
||||||
case "post":
|
case "post":
|
||||||
|
log.Debug("handling '" + requestMethod + "' request for path '" + requestPath + "'...")
|
||||||
handled = handlePost(user, writer, request)
|
handled = handlePost(user, writer, request)
|
||||||
}
|
}
|
||||||
if handled {
|
if handled {
|
||||||
log.Debug("handled '"+requestMethod+"' request", "path: '"+requestPath+"'", "user: '"+user+"'")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debug("ignoring '"+requestMethod+"' request", "path: '"+requestPath+"'", "user: '"+user+"'")
|
log.Debug("ignoring '" + requestMethod + "' request for path '" + requestPath + "'\n")
|
||||||
writer.WriteHeader(501)
|
writer.WriteHeader(501)
|
||||||
writer.Write([]byte("error: endpoint '" + requestPath + "' not implemented\n"))
|
writer.Write([]byte("error: endpoint '" + requestPath + "' not implemented\n"))
|
||||||
}
|
}
|
||||||
|
@ -78,15 +78,6 @@ func handleGet(user string, writer http.ResponseWriter, request *http.Request) b
|
||||||
}
|
}
|
||||||
writer.WriteHeader(200)
|
writer.WriteHeader(200)
|
||||||
writer.Write([]byte(json + "\n"))
|
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":
|
case "/json":
|
||||||
from, to := getTimespan(request.URL.Query())
|
from, to := getTimespan(request.URL.Query())
|
||||||
json, error := workday.ToJSON(from, to, user)
|
json, error := workday.ToJSON(from, to, user)
|
||||||
|
@ -118,7 +109,7 @@ func handlePost(user string, writer http.ResponseWriter, request *http.Request)
|
||||||
if !success {
|
if !success {
|
||||||
if workday.ID > 0 {
|
if workday.ID > 0 {
|
||||||
writer.WriteHeader(400)
|
writer.WriteHeader(400)
|
||||||
writer.Write([]byte("workday for today has already been started at " + tools.TimeToHHMMSS(workday.Start) + "\n"))
|
writer.Write([]byte("workday for today has already been started at " + strconv.Itoa(workday.Start.Hour()) + ":" + strconv.Itoa(workday.Start.Minute()) + ":" + strconv.Itoa(workday.Start.Second()) + "\n"))
|
||||||
} else {
|
} else {
|
||||||
writer.WriteHeader(500)
|
writer.WriteHeader(500)
|
||||||
writer.Write([]byte("encountered an error starting a new workday\n"))
|
writer.Write([]byte("encountered an error starting a new workday\n"))
|
||||||
|
@ -126,21 +117,21 @@ func handlePost(user string, writer http.ResponseWriter, request *http.Request)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
writer.WriteHeader(200)
|
writer.WriteHeader(200)
|
||||||
writer.Write([]byte("started new workday for user '" + workday.User + "' at " + tools.TimeToHHMMSS(workday.Start) + "\n"))
|
writer.Write([]byte("started new workday for user '" + workday.User + "' at " + strconv.Itoa(workday.Start.Hour()) + ":" + strconv.Itoa(workday.Start.Minute()) + ":" + strconv.Itoa(workday.Start.Second()) + "\n"))
|
||||||
case "/stop":
|
case "/stop":
|
||||||
success, workday := database.StopTimestamp(user)
|
success, workday := database.StopTimestamp()
|
||||||
if !success {
|
if !success {
|
||||||
if workday.ID > 0 {
|
if workday.ID > 0 {
|
||||||
writer.WriteHeader(500)
|
writer.WriteHeader(500)
|
||||||
writer.Write([]byte("encountered an error stopping workday for user '" + user + "'\n"))
|
writer.Write([]byte("encountered an error stopping workday for user '" + workday.User + "'\n"))
|
||||||
} else {
|
} else {
|
||||||
writer.WriteHeader(400)
|
writer.WriteHeader(400)
|
||||||
writer.Write([]byte("there is no open workday for user '" + user + "' to stop\n"))
|
writer.Write([]byte("there is no open workday to stop\n"))
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
writer.WriteHeader(200)
|
writer.WriteHeader(200)
|
||||||
writer.Write([]byte("stopped workday for user '" + workday.User + "' started at " + tools.TimeToHHMMSS(workday.Start) + "\n"))
|
writer.Write([]byte("stopped workday for user '" + workday.User + "' started at " + strconv.Itoa(workday.Start.Hour()) + ":" + strconv.Itoa(workday.Start.Minute()) + ":" + strconv.Itoa(workday.Start.Second()) + "\n"))
|
||||||
default:
|
default:
|
||||||
handled = false
|
handled = false
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ func Initialize() {
|
||||||
viper.SetConfigName("config")
|
viper.SetConfigName("config")
|
||||||
viper.SetConfigType("yaml")
|
viper.SetConfigType("yaml")
|
||||||
viper.AddConfigPath("$HOME/.config/worklog/")
|
viper.AddConfigPath("$HOME/.config/worklog/")
|
||||||
|
viper.AddConfigPath("$HOME/.config/")
|
||||||
workingDirectory, error := os.Getwd()
|
workingDirectory, error := os.Getwd()
|
||||||
for error == nil && path.Base(workingDirectory) != "worklog" {
|
for error == nil && path.Base(workingDirectory) != "worklog" {
|
||||||
workingDirectory = path.Dir(workingDirectory)
|
workingDirectory = path.Dir(workingDirectory)
|
||||||
|
|
|
@ -32,15 +32,15 @@ func StartTimestamp(user string) (bool, Timestamp) {
|
||||||
return success, workday
|
return success, workday
|
||||||
}
|
}
|
||||||
|
|
||||||
func StopTimestamp(user string) (bool, Timestamp) {
|
func StopTimestamp() (bool, Timestamp) {
|
||||||
var timestamp Timestamp
|
var timestamp Timestamp
|
||||||
result := connection.Where("end = ? and user = ?", tools.ZeroDate, user).Last(×tamp)
|
result := connection.Last(×tamp)
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
log.Error("encountered an error selecting the last workday for user '"+user+"'", result.Error.Error())
|
log.Error("encountered an error selecting the last workday", result.Error.Error())
|
||||||
return false, timestamp
|
return false, timestamp
|
||||||
}
|
}
|
||||||
if timestamp.ID == 0 || !timestamp.End.Equal(tools.ZeroDate) {
|
if timestamp.ID == 0 || !timestamp.End.Equal(tools.ZeroDate) {
|
||||||
log.Debug("there is no open workday for user '" + user + "' to stop")
|
log.Debug("there is no open workday to stop")
|
||||||
timestamp.ID = 0
|
timestamp.ID = 0
|
||||||
return false, timestamp
|
return false, timestamp
|
||||||
}
|
}
|
||||||
|
@ -58,27 +58,9 @@ func StopTimestamp(user string) (bool, Timestamp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFirstTimestamp(user string, date time.Time) Timestamp {
|
func GetFirstTimestamp(user string, date time.Time) Timestamp {
|
||||||
return GetTodaysTimestamp(user, date, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLastTimestamp(user string, date time.Time) Timestamp {
|
|
||||||
return GetTodaysTimestamp(user, date, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetTodaysTimestamp(user string, date time.Time, first bool) Timestamp {
|
|
||||||
var timestamp Timestamp
|
var timestamp Timestamp
|
||||||
var where string
|
date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, time.UTC)
|
||||||
var order string
|
connection.Where("start > ? and user = ?", date, user).First(×tamp)
|
||||||
if first {
|
|
||||||
date = time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, time.UTC)
|
|
||||||
order = "asc"
|
|
||||||
where = "start > ?"
|
|
||||||
} else {
|
|
||||||
date = time.Date(date.Year(), date.Month(), date.Day(), 23, 59, 59, 0, time.UTC)
|
|
||||||
order = "desc"
|
|
||||||
where = "start < ?"
|
|
||||||
}
|
|
||||||
connection.Where(where+" and user = ?", date, user).Order("start " + order).First(×tamp)
|
|
||||||
return timestamp
|
return timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,4 @@ package tools
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
var ZeroDate = time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC)
|
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", "balance"}
|
var ReportHeaders = []string{"date", "workday start", "workday end", "workday duration", "pause start", "pause end", "pause duration"}
|
||||||
|
|
|
@ -27,9 +27,6 @@ func TimeToDDMMYYYY(time time.Time) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TimeToHHMMSS(time time.Time) string {
|
func TimeToHHMMSS(time time.Time) string {
|
||||||
if time == ZeroDate {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
tmp := strconv.Itoa(time.Second())
|
tmp := strconv.Itoa(time.Second())
|
||||||
if len(tmp) < 2 {
|
if len(tmp) < 2 {
|
||||||
tmp = "0" + tmp
|
tmp = "0" + tmp
|
||||||
|
@ -48,11 +45,6 @@ func TimeToHHMMSS(time time.Time) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func DurationToHHMMSS(duration time.Duration) string {
|
func DurationToHHMMSS(duration time.Duration) string {
|
||||||
negative := false
|
|
||||||
if duration < 0 {
|
|
||||||
duration = duration * -1
|
|
||||||
negative = true
|
|
||||||
}
|
|
||||||
durationSeconds := duration.Seconds()
|
durationSeconds := duration.Seconds()
|
||||||
durationHours := int(durationSeconds / 3600)
|
durationHours := int(durationSeconds / 3600)
|
||||||
if durationHours > 0 {
|
if durationHours > 0 {
|
||||||
|
@ -76,9 +68,6 @@ func DurationToHHMMSS(duration time.Duration) string {
|
||||||
for len(tmpDuration) < 8 {
|
for len(tmpDuration) < 8 {
|
||||||
tmpDuration = "0" + tmpDuration
|
tmpDuration = "0" + tmpDuration
|
||||||
}
|
}
|
||||||
if negative {
|
|
||||||
tmpDuration = "-" + tmpDuration
|
|
||||||
}
|
|
||||||
return tmpDuration
|
return tmpDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,7 @@ func ToCSV(from time.Time, to time.Time, user string) string {
|
||||||
tools.DurationToHHMMSS(workday.Duration) + ";" +
|
tools.DurationToHHMMSS(workday.Duration) + ";" +
|
||||||
tools.TimeToHHMMSS(workday.Pause.Start) + ";" +
|
tools.TimeToHHMMSS(workday.Pause.Start) + ";" +
|
||||||
tools.TimeToHHMMSS(workday.Pause.Stop) + ";" +
|
tools.TimeToHHMMSS(workday.Pause.Stop) + ";" +
|
||||||
tools.DurationToHHMMSS(workday.Pause.Duration) + ";" +
|
tools.DurationToHHMMSS(workday.Pause.Duration)
|
||||||
tools.DurationToHHMMSS(workday.Balance)
|
|
||||||
}
|
}
|
||||||
return report
|
return report
|
||||||
}
|
}
|
||||||
|
@ -42,7 +41,6 @@ func ToJSON(from time.Time, to time.Time, user string) (string, error) {
|
||||||
Stop: tools.TimeToHHMMSS(workday.Stop),
|
Stop: tools.TimeToHHMMSS(workday.Stop),
|
||||||
Duration: tools.DurationToHHMMSS(workday.Duration),
|
Duration: tools.DurationToHHMMSS(workday.Duration),
|
||||||
}
|
}
|
||||||
workdayJson.Open = workday.isOpen()
|
|
||||||
if workday.Pause.Duration > 0 {
|
if workday.Pause.Duration > 0 {
|
||||||
pauseJson := pauseJson{
|
pauseJson := pauseJson{
|
||||||
Start: tools.TimeToHHMMSS(workday.Pause.Start),
|
Start: tools.TimeToHHMMSS(workday.Pause.Start),
|
||||||
|
@ -51,9 +49,6 @@ func ToJSON(from time.Time, to time.Time, user string) (string, error) {
|
||||||
}
|
}
|
||||||
workdayJson.Pause = pauseJson
|
workdayJson.Pause = pauseJson
|
||||||
}
|
}
|
||||||
if !workday.isOpen() {
|
|
||||||
workdayJson.Balance = tools.DurationToHHMMSS(workday.Balance)
|
|
||||||
}
|
|
||||||
jsonWorkdays = append(jsonWorkdays, workdayJson)
|
jsonWorkdays = append(jsonWorkdays, workdayJson)
|
||||||
}
|
}
|
||||||
bytes, error := json.Marshal(jsonWorkdays)
|
bytes, error := json.Marshal(jsonWorkdays)
|
||||||
|
@ -68,7 +63,6 @@ func (workday *Workday) ToJSON(user string) (string, error) {
|
||||||
Stop: tools.TimeToHHMMSS(workday.Stop),
|
Stop: tools.TimeToHHMMSS(workday.Stop),
|
||||||
Duration: tools.DurationToHHMMSS(workday.Duration),
|
Duration: tools.DurationToHHMMSS(workday.Duration),
|
||||||
}
|
}
|
||||||
workdayJson.Open = workday.isOpen()
|
|
||||||
if workday.Pause.Duration > 0 {
|
if workday.Pause.Duration > 0 {
|
||||||
pauseJson := pauseJson{
|
pauseJson := pauseJson{
|
||||||
Start: tools.TimeToHHMMSS(workday.Pause.Start),
|
Start: tools.TimeToHHMMSS(workday.Pause.Start),
|
||||||
|
@ -77,43 +71,23 @@ func (workday *Workday) ToJSON(user string) (string, error) {
|
||||||
}
|
}
|
||||||
workdayJson.Pause = pauseJson
|
workdayJson.Pause = pauseJson
|
||||||
}
|
}
|
||||||
if !workday.isOpen() {
|
|
||||||
workdayJson.Balance = tools.DurationToHHMMSS(workday.Balance)
|
|
||||||
}
|
|
||||||
bytes, error := json.Marshal(workdayJson)
|
bytes, error := json.Marshal(workdayJson)
|
||||||
return string(bytes), error
|
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) {
|
func GetToday(user string) (Workday, error) {
|
||||||
var workday Workday
|
var workday Workday
|
||||||
firstTimestamp := database.GetFirstTimestamp(user, time.Now())
|
timestamp := database.GetFirstTimestamp(user, time.Now())
|
||||||
if firstTimestamp.ID == 0 {
|
if timestamp.ID == 0 {
|
||||||
return workday, errors.New("no workday for user '" + user + "' started today")
|
return workday, errors.New("no workday for user '" + user + "' started today")
|
||||||
}
|
}
|
||||||
workday.Date = tools.TimeToDDMMYYYY(firstTimestamp.Start)
|
workday.Date = tools.TimeToDDMMYYYY(timestamp.Start)
|
||||||
workday.Start = firstTimestamp.Start
|
workday.Start = timestamp.Start
|
||||||
workday.Stop = firstTimestamp.End
|
workday.Stop = timestamp.End
|
||||||
workday.Duration = workday.Stop.Sub(workday.Start)
|
|
||||||
currentTimestamp := database.GetLastTimestamp(user, time.Now())
|
|
||||||
workday.Stop = currentTimestamp.End
|
|
||||||
if workday.Stop == tools.ZeroDate {
|
if workday.Stop == tools.ZeroDate {
|
||||||
workday.Duration = time.Since(workday.Start)
|
workday.Stop = time.Now()
|
||||||
} else {
|
|
||||||
workday.Duration = workday.Stop.Sub(workday.Start)
|
|
||||||
}
|
}
|
||||||
workday.Balance = workday.Duration - time.Hour*8
|
workday.Duration = workday.Stop.Sub(workday.Start)
|
||||||
return workday, nil
|
return workday, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +129,6 @@ func aggregatedTimestampsToWorkdays(aggregatedTimestamps map[string][]database.T
|
||||||
Start: tmpStart,
|
Start: tmpStart,
|
||||||
Stop: tmpEnd,
|
Stop: tmpEnd,
|
||||||
Duration: duration,
|
Duration: duration,
|
||||||
Balance: duration - time.Hour*8,
|
|
||||||
}
|
}
|
||||||
workday.insertFakePause()
|
workday.insertFakePause()
|
||||||
workdays = append(workdays, workday)
|
workdays = append(workdays, workday)
|
||||||
|
@ -171,25 +144,13 @@ func aggregatedTimestampsToWorkdays(aggregatedTimestamps map[string][]database.T
|
||||||
return workdays
|
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() {
|
func (workday *Workday) insertFakePause() {
|
||||||
var pause Pause
|
var pause Pause
|
||||||
pauseLength := time.Minute * 30
|
|
||||||
if workday.Duration.Hours() >= 10 {
|
|
||||||
pauseLength = time.Minute * 60
|
|
||||||
}
|
|
||||||
deviationStart := rand.Intn(15-(-15)) + (-15)
|
deviationStart := rand.Intn(15-(-15)) + (-15)
|
||||||
pause.Start = workday.Start.Add(workday.Duration / 2)
|
pause.Start = workday.Start.Add(workday.Duration / 2)
|
||||||
pause.Start = pause.Start.Add(time.Minute * time.Duration(deviationStart))
|
pause.Start = pause.Start.Add(time.Minute * time.Duration(deviationStart))
|
||||||
deviationEnd := rand.Intn(300)
|
deviationEnd := rand.Intn(300)
|
||||||
pause.Stop = pause.Start.Add(pauseLength).Add(time.Second * time.Duration(deviationEnd))
|
pause.Stop = pause.Start.Add(time.Minute * 30).Add(time.Second * time.Duration(deviationEnd))
|
||||||
pause.Duration = pause.Stop.Sub(pause.Start)
|
pause.Duration = pause.Stop.Sub(pause.Start)
|
||||||
workday.Pause = pause
|
workday.Pause = pause
|
||||||
workday.Stop = workday.Stop.Add(workday.Pause.Duration)
|
workday.Stop = workday.Stop.Add(workday.Pause.Duration)
|
||||||
|
@ -202,7 +163,6 @@ type Workday struct {
|
||||||
Stop time.Time
|
Stop time.Time
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
Pause Pause
|
Pause Pause
|
||||||
Balance time.Duration
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Pause struct {
|
type Pause struct {
|
||||||
|
@ -217,9 +177,7 @@ type workdayJson struct {
|
||||||
Start string
|
Start string
|
||||||
Stop string
|
Stop string
|
||||||
Duration string
|
Duration string
|
||||||
Open bool
|
|
||||||
Pause pauseJson
|
Pause pauseJson
|
||||||
Balance string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type pauseJson struct {
|
type pauseJson struct {
|
||||||
|
|
Loading…
Reference in a new issue