dev/main #19

Merged
tom_trgr merged 24 commits from dev/main into main 2025-06-20 22:17:21 +02:00
8 changed files with 116 additions and 1172 deletions
Showing only changes of commit 71e65e9b17 - Show all commits

View File

@@ -1,9 +1,11 @@
package models
import (
"arbeitszeitmessung/helper"
"encoding/json"
"fmt"
"log"
"strconv"
"time"
)
@@ -19,6 +21,7 @@ type WorkDay struct {
func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay {
var workDays []WorkDay
var workSec, pauseSec float64
qStr, err := DB.Prepare(`
WITH all_days AS (
SELECT generate_series($2::DATE, $3::DATE - INTERVAL '1 day', INTERVAL '1 day')::DATE AS work_date
@@ -43,7 +46,7 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay
COALESCE(
EXTRACT(EPOCH FROM SUM(
CASE
WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 255)
WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 254)
THEN b.timestamp - b.prev_timestamp
ELSE INTERVAL '0'
END
@@ -52,7 +55,7 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay
COALESCE(
EXTRACT(EPOCH FROM SUM(
CASE
WHEN b.prev_check IN (2, 4, 255) AND b.check_in_out IN (1, 3)
WHEN b.prev_check IN (2, 4, 254) AND b.check_in_out IN (1, 3)
THEN b.timestamp - b.prev_timestamp
ELSE INTERVAL '0'
END
@@ -80,6 +83,7 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay
return workDays
}
defer rows.Close()
emptyDays, _ := strconv.ParseBool(helper.GetEnv("EMPTY_DAYS", "false"))
for rows.Next() {
var workDay WorkDay
var bookings []byte
@@ -100,8 +104,11 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay
} else {
workDay.calcPauseTime()
}
if emptyDays || workDay.Bookings[0].CounterId != 0 {
workDays = append(workDays, workDay)
} else {
log.Println("no booking on day", workDay.Day.Format("02.01.2006"))
}
}
if err = rows.Err(); err != nil {
return workDays
@@ -172,7 +179,7 @@ func (d *WorkDay) GetWorkTimeString() (string, string) {
// returns bool wheter the workday was ended with an automatic logout
func (d *WorkDay) RequiresAction() bool {
return d.Bookings[len(d.Bookings)-1].CheckInOut == 255
return d.Bookings[len(d.Bookings)-1].CheckInOut == 254
}
// returns a integer percentage of how much day has been worked of

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,7 @@
package templates
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"fmt"
"net/url"
@@ -63,14 +64,16 @@ templ dayComponent(workDay models.WorkDay) {
<div class="time-component flex flex-row md:col-span-3 gap-2 w-full grid-cell">
@lineComponent()
<form id={ "time-" + workDay.Day.Format("2006-01-02") } class="flex flex-col gap-2 group w-full justify-between" style={ justify } method="post">
if len(workDay.Bookings) <= 1 && workDay.Bookings[len(workDay.Bookings)-1].CounterId == 0 {
if len(workDay.Bookings) <= 1 && workDay.Bookings[len(workDay.Bookings)-1].CounterId == 0 && helper.GetEnv("GO_ENV", "production") == "debug" {
@noBookingComponent(workDay)
} else {
for _, booking := range workDay.Bookings {
@bookingComponent(booking)
}
if helper.GetEnv("GO_ENV", "production") == "debug" {
@newBookingComponent(workDay)
}
}
</form>
</div>
<div class="grid-cell">

View File

@@ -9,6 +9,7 @@ import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
import (
"arbeitszeitmessung/helper"
"arbeitszeitmessung/models"
"fmt"
"net/url"
@@ -47,7 +48,7 @@ func inputForm() templ.Component {
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(user.Vorname + " " + user.Name)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 18, Col: 66}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 19, Col: 66}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
@@ -68,7 +69,7 @@ func inputForm() templ.Component {
var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_from"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 27, Col: 57}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 28, Col: 57}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
@@ -81,7 +82,7 @@ func inputForm() templ.Component {
var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(urlParams.Get("time_to"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 28, Col: 55}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 29, Col: 55}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
@@ -137,7 +138,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Day.Format("Mon"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 51, Col: 94}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 52, Col: 94}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
@@ -150,7 +151,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(workDay.Day.Format("02.01.2006"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 51, Col: 139}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 52, Col: 139}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
@@ -178,7 +179,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(work)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 57, Col: 36}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 58, Col: 36}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
@@ -196,7 +197,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(pause)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 59, Col: 40}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 60, Col: 40}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
@@ -222,7 +223,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs("time-" + workDay.Day.Format("2006-01-02"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 65, Col: 56}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 66, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
@@ -235,7 +236,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(justify)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 65, Col: 131}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 66, Col: 131}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
@@ -245,7 +246,7 @@ func dayComponent(workDay models.WorkDay) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if len(workDay.Bookings) <= 1 && workDay.Bookings[len(workDay.Bookings)-1].CounterId == 0 {
if len(workDay.Bookings) <= 1 && workDay.Bookings[len(workDay.Bookings)-1].CounterId == 0 && helper.GetEnv("GO_ENV", "production") == "debug" {
templ_7745c5c3_Err = noBookingComponent(workDay).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
@@ -261,11 +262,13 @@ func dayComponent(workDay models.WorkDay) templ.Component {
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if helper.GetEnv("GO_ENV", "production") == "debug" {
templ_7745c5c3_Err = newBookingComponent(workDay).Render(ctx, templ_7745c5c3_Buffer)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "</form></div><div class=\"grid-cell\">")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
@@ -394,7 +397,7 @@ func timeGaugeComponent(progress uint8, today bool, warning bool) templ.Componen
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues(fmt.Sprintf("height: %d%%", int(progress)))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 116, Col: 149}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 119, Col: 149}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
if templ_7745c5c3_Err != nil {
@@ -526,7 +529,7 @@ func newBookingComponent(d models.WorkDay) templ.Component {
var templ_7745c5c3_Var23 string
templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(time.Now().Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 151, Col: 55}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 154, Col: 55}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
if templ_7745c5c3_Err != nil {
@@ -588,7 +591,7 @@ func bookingComponent(booking models.Booking) templ.Component {
var templ_7745c5c3_Var25 string
templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 164, Col: 97}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 167, Col: 97}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
if templ_7745c5c3_Err != nil {
@@ -601,7 +604,7 @@ func bookingComponent(booking models.Booking) templ.Component {
var templ_7745c5c3_Var26 string
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs("booking_" + strconv.Itoa(booking.CounterId))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 165, Col: 70}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 168, Col: 70}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
if templ_7745c5c3_Err != nil {
@@ -614,7 +617,7 @@ func bookingComponent(booking models.Booking) templ.Component {
var templ_7745c5c3_Var27 string
templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(booking.Timestamp.Format("15:04"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 165, Col: 126}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 168, Col: 126}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
if templ_7745c5c3_Err != nil {
@@ -627,7 +630,7 @@ func bookingComponent(booking models.Booking) templ.Component {
var templ_7745c5c3_Var28 string
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(booking.GetBookingType())
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 166, Col: 29}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `templates/timeComponents.templ`, Line: 169, Col: 29}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
if templ_7745c5c3_Err != nil {

View File

@@ -8,3 +8,4 @@ EXPOSED_PORT=8000
TZ=Europe/Berlin
PGTZ=Europe/Berlin
API_TOKEN=dont_access
EMPTY_DAYS=false

View File

@@ -28,9 +28,7 @@
"paths": {
"/time": {
"get": {
"tags": [
"booking"
],
"tags": ["booking"],
"summary": "Gets all the bookings from one card_uid",
"description": "Returns all the bookings optionally filtered with cardID",
"operationId": "getBooking",
@@ -100,11 +98,7 @@
"check_in_out": {
"type": "integer",
"example": 1,
"enum": [
1,
2,
255
]
"enum": [1, 2, 254]
},
"timestamp": {
"type": "string",
@@ -131,9 +125,7 @@
},
"/time/new": {
"put": {
"tags": [
"booking"
],
"tags": ["booking"],
"summary": "Create new Booking",
"description": "Creates a new booking with the supplied parameters",
"operationId": "pcreateBooking",
@@ -171,11 +163,7 @@
"required": true,
"schema": {
"type": "integer",
"enum": [
1,
2,
255
]
"enum": [1, 2, 254]
}
}
],
@@ -203,11 +191,7 @@
"check_in_out": {
"type": "integer",
"example": 1,
"enum": [
1,
2,
255
]
"enum": [1, 2, 254]
},
"timestamp": {
"type": "string",
@@ -228,9 +212,7 @@
}
},
"get": {
"tags": [
"booking"
],
"tags": ["booking"],
"summary": "Create new Booking",
"description": "Creates a new booking with the supplied parameters",
"operationId": "gcreateBooking",
@@ -277,11 +259,7 @@
"required": true,
"schema": {
"type": "integer",
"enum": [
1,
2,
255
]
"enum": [1, 2, 254]
}
}
],
@@ -309,11 +287,7 @@
"check_in_out": {
"type": "integer",
"example": 1,
"enum": [
1,
2,
255
]
"enum": [1, 2, 254]
},
"timestamp": {
"type": "string",
@@ -339,11 +313,9 @@
},
"/logout": {
"get": {
"tags": [
"booking"
],
"tags": ["booking"],
"summary": "Logs out all logged in users",
"description": "With this call all actively logged in users (last booking today has check_in_out=1) will be logged out automaticly (check_in_out=255)",
"description": "With this call all actively logged in users (last booking today has check_in_out=1) will be logged out automaticly (check_in_out=254)",
"operationId": "autoLogout",
"responses": {
"200": {
@@ -412,11 +384,7 @@
"check_in_out": {
"type": "integer",
"example": 1,
"enum": [
1,
2,
255
]
"enum": [1, 2, 254]
},
"timestamp": {
"type": "string",
@@ -450,11 +418,7 @@
"check_in_out": {
"type": "integer",
"example": 1,
"enum": [
1,
2,
255
]
"enum": [1, 2, 254]
},
"timestamp": {
"type": "string",

View File

@@ -88,7 +88,7 @@ paths:
enum:
- 1
- 2
- 255
- 254
responses:
"200":
description: successfully created booking
@@ -137,7 +137,7 @@ paths:
enum:
- 1
- 2
- 255
- 254
responses:
"200":
description: successfully created booking
@@ -154,7 +154,7 @@ paths:
tags:
- booking
summary: Logs out all logged in users
description: With this call all actively logged in users (last booking today has check_in_out=1) will be logged out automaticly (check_in_out=255)
description: With this call all actively logged in users (last booking today has check_in_out=1) will be logged out automaticly (check_in_out=254)
operationId: autoLogout
responses:
"200":
@@ -196,7 +196,7 @@ components:
enum:
- 1
- 2
- 255
- 254
timestamp:
type: string
format: date-time

View File

@@ -26,15 +26,26 @@ Nutzeransicht (/user):
![user](docs/images/user.png)
## Buchungstypen
1 - Kommen
2 - Gehen
3 - Kommen Manuell
4 - Gehen Manuell
254 - Automatisch abgemeldet
## API
Nutzung der API
wenn die `dev-docker-compose.yml` Datei gestartet wird, ist direkt ein SwaggerUI Server mit entsprechender Datei inbegriffen.
### Buchungen [/time]
#### [GET] Anfrage
Parameter: cardID (string)
Antwort: `200`
```json
[
{
@@ -50,25 +61,30 @@ Antwort: `200`
"bookingTyp": 1,
"loggedTime": "2024-09-05T08:51:12.670827Z",
"id": 6
},
}
]
```
Antwort `500`
Serverfehler
#### [PUT] Anfrage
Parameter: id (int)
Body: (veränderte Parameter)
```json
{
"cradID": "test_card",
"readerID": "mytest",
"bookingTyp": 1,
"loggedTime": "2024-09-05T08:51:12.670827Z",
"loggedTime": "2024-09-05T08:51:12.670827Z"
}
```
Antwort `200`
```json
Antwort `200`
```json
{
"cradID": "test_card",
"readerID": "mytest",
@@ -76,16 +92,20 @@ Body: (veränderte Parameter)
"loggedTime": "2024-09-05T08:51:12.670827Z",
"id": 6
}
```
```
### Neue Buchung [/time/new]
#### [PUT] Anfrage
Parameter:
- cardID (string)
- readerID (string)
- bookingType (string)
Antwort `202` Akzeptiert und eingefügt
```json
{
"cradID": "test_card",