From eb45a3ef751216e2458fffc507f9f888ad0f9701 Mon Sep 17 00:00:00 2001 From: tom Date: Fri, 28 Mar 2025 08:23:52 +0100 Subject: [PATCH] added empty days in /team view --- Backend/helper/system.go | 36 ++++++++++++ Backend/models/booking.go | 4 ++ Backend/models/workDay.go | 102 ++++++++++++++++++---------------- Docker/docker-compose.dev.yml | 1 + 4 files changed, 96 insertions(+), 47 deletions(-) diff --git a/Backend/helper/system.go b/Backend/helper/system.go index 4ebffb2..b1c28b5 100644 --- a/Backend/helper/system.go +++ b/Backend/helper/system.go @@ -2,11 +2,13 @@ package helper import ( "os" + "time" ) // Returns env with default fallback value. // // Params: +// // key - enviroment var name // fallback - default value func GetEnv(key, fallback string) string { @@ -15,3 +17,37 @@ func GetEnv(key, fallback string) string { } 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 +} diff --git a/Backend/models/booking.go b/Backend/models/booking.go index 14d231e..ba55cf0 100644 --- a/Backend/models/booking.go +++ b/Backend/models/booking.go @@ -227,3 +227,7 @@ func (b *Booking) UpdateTime(newTime time.Time) { b.Update(newBooking) 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) +} diff --git a/Backend/models/workDay.go b/Backend/models/workDay.go index 388b4ff..56a1a28 100644 --- a/Backend/models/workDay.go +++ b/Backend/models/workDay.go @@ -1,6 +1,7 @@ package models import ( + "encoding/json" "fmt" "log" "time" @@ -19,53 +20,53 @@ func (d *WorkDay) GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay var workDays []WorkDay var workSec, pauseSec float64 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 - timestamp::DATE AS work_date, -- Extract date for grouping - timestamp, - check_in_out, - LAG(timestamp) OVER ( - PARTITION BY card_uid, timestamp::DATE -- Reset for each day - 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 -- Replace with actual card_uid - AND timestamp::DATE >= $2 -- Set date range - AND timestamp::DATE <= $3 - ) - SELECT - work_date, - MIN(timestamp) AS time_from, - MAX(timestamp) AS time_to, - -- Total work time per day - COALESCE( - EXTRACT(EPOCH FROM SUM( - CASE - WHEN prev_check IN (1, 3) AND check_in_out IN (2, 4, 254) - THEN timestamp - prev_timestamp - ELSE INTERVAL '0' - END - )), 0 - ) AS total_work, - - -- 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;`) + d.work_date, + COALESCE(MIN(b.timestamp), NOW()) AS time_from, + COALESCE(MAX(b.timestamp), NOW()) AS time_to, + COALESCE( + EXTRACT(EPOCH FROM SUM( + CASE + WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 255) + THEN b.timestamp - b.prev_timestamp + ELSE INTERVAL '0' + END + )), 0 + ) AS total_work_seconds, + COALESCE( + EXTRACT(EPOCH FROM SUM( + CASE + WHEN b.prev_check IN (2, 4, 255) AND b.check_in_out IN (1, 3) + THEN b.timestamp - b.prev_timestamp + ELSE INTERVAL '0' + END + )), 0 + ) AS total_pause_seconds, + COALESCE(jsonb_agg(jsonb_build_object( + 'check_in_out', b.check_in_out, + 'timestamp', b.timestamp, + '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 { 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() for rows.Next() { 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) return workDays } workDay.workTime = time.Duration(workSec * float64(time.Second)) workDay.pauseTime = time.Duration(pauseSec * float64(time.Second)) workDay.calcPauseTime() + + err = json.Unmarshal(bookings, &workDay.Bookings) + if err != nil { + log.Println("Error parsing bookings JSON!", err) + return nil + } workDays = append(workDays, workDay) } if err = rows.Err(); err != nil { diff --git a/Docker/docker-compose.dev.yml b/Docker/docker-compose.dev.yml index e0919b1..2ece6b9 100644 --- a/Docker/docker-compose.dev.yml +++ b/Docker/docker-compose.dev.yml @@ -21,6 +21,7 @@ services: backend: build: ../Backend image: git.letsstein.de/tom/arbeitszeit-backend:0.0.1 + restart: unless-stopped env_file: - .env environment: