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 SELECT
timestamp::DATE AS work_date, -- Extract date for grouping timestamp::DATE AS work_date,
timestamp, timestamp,
check_in_out, check_in_out,
LAG(timestamp) OVER ( counter_id,
PARTITION BY card_uid, timestamp::DATE -- Reset for each day LAG(timestamp) OVER (PARTITION BY card_uid, timestamp::DATE ORDER BY timestamp) AS prev_timestamp,
ORDER BY timestamp LAG(check_in_out) OVER (PARTITION BY card_uid, timestamp::DATE ORDER BY timestamp) AS prev_check
) AS prev_timestamp,
LAG(check_in_out) OVER (
PARTITION BY card_uid, timestamp::DATE
ORDER BY timestamp
) AS prev_check
FROM anwesenheit FROM anwesenheit
WHERE card_uid = $1 -- Replace with actual card_uid WHERE card_uid = $1
AND timestamp::DATE >= $2 -- Set date range AND timestamp::DATE >= $2
AND timestamp::DATE <= $3 AND timestamp::DATE <= $3
) )
SELECT SELECT
work_date, d.work_date,
MIN(timestamp) AS time_from, COALESCE(MIN(b.timestamp), NOW()) AS time_from,
MAX(timestamp) AS time_to, COALESCE(MAX(b.timestamp), NOW()) AS time_to,
-- Total work time per day
COALESCE( COALESCE(
EXTRACT(EPOCH FROM SUM( EXTRACT(EPOCH FROM SUM(
CASE CASE
WHEN prev_check IN (1, 3) AND check_in_out IN (2, 4, 254) WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 255)
THEN timestamp - prev_timestamp THEN b.timestamp - b.prev_timestamp
ELSE INTERVAL '0' ELSE INTERVAL '0'
END END
)), 0 )), 0
) AS total_work, ) AS total_work_seconds,
-- Extract total pause time in seconds
COALESCE( COALESCE(
EXTRACT(EPOCH FROM SUM( EXTRACT(EPOCH FROM SUM(
CASE CASE
WHEN prev_check IN (2, 4, 254) AND check_in_out IN (1, 3) WHEN b.prev_check IN (2, 4, 255) AND b.check_in_out IN (1, 3)
THEN timestamp - prev_timestamp THEN b.timestamp - b.prev_timestamp
ELSE INTERVAL '0' ELSE INTERVAL '0'
END END
)), 0 )), 0
) AS total_pause ) AS total_pause_seconds,
COALESCE(jsonb_agg(jsonb_build_object(
FROM ordered_bookings 'check_in_out', b.check_in_out,
GROUP BY work_date 'timestamp', b.timestamp,
ORDER BY work_date;`) 'counter_id', b.counter_id
) ORDER BY b.timestamp), '[]'::jsonb) AS bookings
FROM all_days d
LEFT JOIN ordered_bookings b ON d.work_date = b.work_date
GROUP BY d.work_date
ORDER BY d.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: