package models import ( "fmt" "log" "time" ) type WorkDay struct { Day time.Time Bookings []Booking workTime time.Duration pauseTime time.Duration } 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 ( 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, -- 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;`) if err != nil { log.Println("Error preparing SQL statement", err) return workDays } defer qStr.Close() rows, err := qStr.Query(card_uid, tsFrom, tsTo) if err != nil { log.Println("Error getting rows!") return workDays } defer rows.Close() for rows.Next() { var workDay WorkDay if err := rows.Scan(&workDay.Day, &workSec, &pauseSec); 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() workDays = append(workDays, workDay) } if err = rows.Err(); err != nil { return workDays } return workDays } func (d *WorkDay) calcPauseTime() { if d.workTime > 6*time.Hour && d.pauseTime < 45*time.Minute { if d.workTime < 9*time.Hour && d.pauseTime < 30*time.Minute { diff := 30*time.Minute - d.pauseTime d.workTime -= diff d.pauseTime += diff } else if d.pauseTime < 45*time.Minute { diff := 45*time.Minute - d.pauseTime d.workTime -= diff d.pauseTime += diff } } } // Gets the duration someone worked that day func (d *WorkDay) GetWorkTime() { var workTime, pauseTime time.Duration var lastBooking Booking for _, booking := range d.Bookings { if booking.CheckInOut%2 == 1 { if !lastBooking.Timestamp.IsZero() { pauseTime += booking.Timestamp.Sub(lastBooking.Timestamp) } } else { workTime += booking.Timestamp.Sub(lastBooking.Timestamp) } lastBooking = booking } // checks if booking is today and has no gehen yet, so the time since last kommen booking is added to workTime if d.Day.Day() == time.Now().Day() && len(d.Bookings)%2 == 1 { workTime += time.Since(lastBooking.Timestamp.Local()) } d.workTime = workTime d.pauseTime = pauseTime d.calcPauseTime() } func formatDuration(d time.Duration) string { hours := int(d.Abs().Hours()) minutes := int(d.Abs().Minutes()) % 60 switch { case hours > 0: return fmt.Sprintf("%dh %dmin", hours, minutes) case minutes > 0: return fmt.Sprintf("%dmin", minutes) default: return "" } } // Converts duration to string and replaces 0s with in // // -> output xhxmin func (d *WorkDay) GetWorkTimeString() (string, string) { workString := formatDuration(d.workTime) pauseString := formatDuration(d.pauseTime) return workString, pauseString } // 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 } // returns a integer percentage of how much day has been worked of func (d *WorkDay) GetWorkDayProgress(user User) uint8 { defaultWorkTime := time.Duration(user.Arbeitszeit * float32(time.Hour)) progress := (d.workTime.Seconds() / defaultWorkTime.Seconds()) * 100 return uint8(progress) }