added empty days in /team view

This commit is contained in:
2025-03-28 08:23:52 +01:00
parent b43783356f
commit eb45a3ef75
4 changed files with 96 additions and 47 deletions

View File

@@ -2,11 +2,13 @@ package helper
import ( import (
"os" "os"
"time"
) )
// Returns env with default fallback value. // Returns env with default fallback value.
// //
// Params: // Params:
//
// key - enviroment var name // key - enviroment var name
// fallback - default value // fallback - default value
func GetEnv(key, fallback string) string { func GetEnv(key, fallback string) string {
@@ -15,3 +17,37 @@ func GetEnv(key, fallback string) string {
} }
return fallback return fallback
} }
type CacheItem struct {
value any
expiration time.Time
}
type Cache struct {
data map[string]CacheItem
ttl time.Duration
fetch func(key string) (any, error)
}
func NewCache(ttl time.Duration, fetchFunc func(key string) (any, error)) *Cache {
return &Cache{
data: make(map[string]CacheItem),
ttl: ttl,
fetch: fetchFunc,
}
}
func (c *Cache) Get(key string) (any, error) {
if item, found := c.data[key]; found {
if time.Now().Before(item.expiration) {
return item.value, nil
}
}
value, err := c.fetch(key)
if err != nil {
return nil, err
}
c.data[key] = CacheItem{value: value, expiration: time.Now().Add(c.ttl)}
return value, nil
}

View File

@@ -227,3 +227,7 @@ func (b *Booking) UpdateTime(newTime time.Time) {
b.Update(newBooking) b.Update(newBooking)
b.Save() b.Save()
} }
func (b *Booking) ToString() string {
return fmt.Sprintf("Booking %d: at: %s, as type: %d", b.CounterId, b.Timestamp.Format("15:04"), b.CheckInOut)
}

View File

@@ -1,6 +1,7 @@
package models package models
import ( import (
"encoding/json"
"fmt" "fmt"
"log" "log"
"time" "time"
@@ -19,53 +20,53 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay
var workDays []WorkDay var workDays []WorkDay
var workSec, pauseSec float64 var workSec, pauseSec float64
qStr, err := DB.Prepare(` qStr, err := DB.Prepare(`
WITH ordered_bookings AS ( WITH all_days AS (
SELECT generate_series($2::DATE, $3::DATE - INTERVAL '1 day', INTERVAL '1 day')::DATE AS work_date
),
ordered_bookings AS (
SELECT
timestamp::DATE AS work_date,
timestamp,
check_in_out,
counter_id,
LAG(timestamp) OVER (PARTITION BY card_uid, timestamp::DATE ORDER BY timestamp) AS prev_timestamp,
LAG(check_in_out) OVER (PARTITION BY card_uid, timestamp::DATE ORDER BY timestamp) AS prev_check
FROM anwesenheit
WHERE card_uid = $1
AND timestamp::DATE >= $2
AND timestamp::DATE <= $3
)
SELECT SELECT
timestamp::DATE AS work_date, -- Extract date for grouping d.work_date,
timestamp, COALESCE(MIN(b.timestamp), NOW()) AS time_from,
check_in_out, COALESCE(MAX(b.timestamp), NOW()) AS time_to,
LAG(timestamp) OVER ( COALESCE(
PARTITION BY card_uid, timestamp::DATE -- Reset for each day EXTRACT(EPOCH FROM SUM(
ORDER BY timestamp CASE
) AS prev_timestamp, WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 255)
LAG(check_in_out) OVER ( THEN b.timestamp - b.prev_timestamp
PARTITION BY card_uid, timestamp::DATE ELSE INTERVAL '0'
ORDER BY timestamp END
) AS prev_check )), 0
FROM anwesenheit ) AS total_work_seconds,
WHERE card_uid = $1 -- Replace with actual card_uid COALESCE(
AND timestamp::DATE >= $2 -- Set date range EXTRACT(EPOCH FROM SUM(
AND timestamp::DATE <= $3 CASE
) WHEN b.prev_check IN (2, 4, 255) AND b.check_in_out IN (1, 3)
SELECT THEN b.timestamp - b.prev_timestamp
work_date, ELSE INTERVAL '0'
MIN(timestamp) AS time_from, END
MAX(timestamp) AS time_to, )), 0
-- Total work time per day ) AS total_pause_seconds,
COALESCE( COALESCE(jsonb_agg(jsonb_build_object(
EXTRACT(EPOCH FROM SUM( 'check_in_out', b.check_in_out,
CASE 'timestamp', b.timestamp,
WHEN prev_check IN (1, 3) AND check_in_out IN (2, 4, 254) 'counter_id', b.counter_id
THEN timestamp - prev_timestamp ) ORDER BY b.timestamp), '[]'::jsonb) AS bookings
ELSE INTERVAL '0' FROM all_days d
END LEFT JOIN ordered_bookings b ON d.work_date = b.work_date
)), 0 GROUP BY d.work_date
) AS total_work, ORDER BY d.work_date;`)
-- Extract total pause time in seconds
COALESCE(
EXTRACT(EPOCH FROM SUM(
CASE
WHEN prev_check IN (2, 4, 254) AND check_in_out IN (1, 3)
THEN timestamp - prev_timestamp
ELSE INTERVAL '0'
END
)), 0
) AS total_pause
FROM ordered_bookings
GROUP BY work_date
ORDER BY work_date;`)
if err != nil { if err != nil {
log.Println("Error preparing SQL statement", err) log.Println("Error preparing SQL statement", err)
@@ -81,13 +82,20 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay
defer rows.Close() defer rows.Close()
for rows.Next() { for rows.Next() {
var workDay WorkDay var workDay WorkDay
if err := rows.Scan(&workDay.Day, &workDay.TimeFrom, &workDay.TimeTo, &workSec, &pauseSec); err != nil { var bookings []byte
if err := rows.Scan(&workDay.Day, &workDay.TimeFrom, &workDay.TimeTo, &workSec, &pauseSec, &bookings); err != nil {
log.Println("Error scanning row!", err) log.Println("Error scanning row!", err)
return workDays return workDays
} }
workDay.workTime = time.Duration(workSec * float64(time.Second)) workDay.workTime = time.Duration(workSec * float64(time.Second))
workDay.pauseTime = time.Duration(pauseSec * float64(time.Second)) workDay.pauseTime = time.Duration(pauseSec * float64(time.Second))
workDay.calcPauseTime() workDay.calcPauseTime()
err = json.Unmarshal(bookings, &workDay.Bookings)
if err != nil {
log.Println("Error parsing bookings JSON!", err)
return nil
}
workDays = append(workDays, workDay) workDays = append(workDays, workDay)
} }
if err = rows.Err(); err != nil { if err = rows.Err(); err != nil {

View File

@@ -21,6 +21,7 @@ services:
backend: backend:
build: ../Backend build: ../Backend
image: git.letsstein.de/tom/arbeitszeit-backend:0.0.1 image: git.letsstein.de/tom/arbeitszeit-backend:0.0.1
restart: unless-stopped
env_file: env_file:
- .env - .env
environment: environment: