fix: refactored sql request for get days + highlight invalid bookings and ignore them for calculation
Some checks failed
Tests / Run Go Tests (push) Failing after 1m14s
Some checks failed
Tests / Run Go Tests (push) Failing after 1m14s
fixed #77
This commit is contained in:
@@ -36,6 +36,7 @@ type Booking struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
CounterId int `json:"counter_id"`
|
||||
BookingType BookingType `json:"anwesenheit_typ"`
|
||||
Valid bool `json:"valid"`
|
||||
}
|
||||
|
||||
type IDatabase interface {
|
||||
@@ -252,12 +253,13 @@ func (b *Booking) Update(nb Booking) {
|
||||
func checkLastBooking(b Booking) bool {
|
||||
var check_in_out int
|
||||
slog.Info("Checking with timestamp:", "timestamp", b.Timestamp.String())
|
||||
stmt, err := DB.Prepare((`SELECT check_in_out FROM "anwesenheit" WHERE "card_uid" = $1 AND "timestamp"::DATE <= $2::DATE ORDER BY "timestamp" DESC LIMIT 1;`))
|
||||
stmt, err := DB.Prepare((`SELECT check_in_out FROM "anwesenheit" WHERE "card_uid" = $1 AND "timestamp" <= $2 ORDER BY "timestamp" DESC LIMIT 1;`))
|
||||
if err != nil {
|
||||
log.Fatalf("Error preparing query: %v", err)
|
||||
return false
|
||||
}
|
||||
err = stmt.QueryRow(b.CardUID, b.Timestamp).Scan(&check_in_out)
|
||||
slog.Info("Checking last bookings check_in_out", "Check", check_in_out)
|
||||
if err == sql.ErrNoRows {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -186,97 +186,148 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []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),
|
||||
normalized_bookings AS (
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT
|
||||
a.card_uid,
|
||||
a.timestamp,
|
||||
a.timestamp::DATE AS work_date,
|
||||
a.check_in_out,
|
||||
a.counter_id,
|
||||
a.anwesenheit_typ,
|
||||
sat.anwesenheit_name AS anwesenheit_typ_name,
|
||||
LAG(a.check_in_out) OVER (
|
||||
PARTITION BY a.card_uid, a.timestamp::DATE
|
||||
ORDER BY a.timestamp
|
||||
) AS prev_check
|
||||
FROM anwesenheit a
|
||||
LEFT JOIN s_anwesenheit_typen sat
|
||||
ON a.anwesenheit_typ = sat.anwesenheit_id
|
||||
WHERE a.card_uid = $1
|
||||
AND a.timestamp::DATE >= $2
|
||||
AND a.timestamp::DATE <= $3
|
||||
) t
|
||||
WHERE prev_check IS NULL OR prev_check <> check_in_out
|
||||
),
|
||||
ordered_bookings AS (
|
||||
SELECT
|
||||
*,
|
||||
LAG(timestamp) OVER (
|
||||
PARTITION BY card_uid, work_date
|
||||
ORDER BY timestamp
|
||||
) AS prev_timestamp
|
||||
FROM normalized_bookings
|
||||
)
|
||||
SELECT
|
||||
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, 254)
|
||||
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, 254) 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,
|
||||
'anwesenheit_typ', b.anwesenheit_typ,
|
||||
'anwesenheit_typ', jsonb_build_object(
|
||||
'anwesenheit_id', b.anwesenheit_typ,
|
||||
'anwesenheit_name', b.anwesenheit_typ_name
|
||||
)
|
||||
) 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 ASC;`)
|
||||
WITH
|
||||
all_days AS (
|
||||
SELECT
|
||||
generate_series(
|
||||
$2 ::DATE,
|
||||
$3 ::DATE - INTERVAL '1 day',
|
||||
INTERVAL '1 day'
|
||||
)::DATE AS work_date
|
||||
),
|
||||
all_bookings AS (
|
||||
SELECT
|
||||
a.card_uid,
|
||||
a.timestamp,
|
||||
a.timestamp::DATE AS work_date,
|
||||
a.check_in_out,
|
||||
a.counter_id,
|
||||
a.anwesenheit_typ,
|
||||
sat.anwesenheit_name AS anwesenheit_typ_name,
|
||||
LAG(a.check_in_out) OVER (
|
||||
PARTITION BY
|
||||
a.card_uid,
|
||||
a.timestamp::DATE
|
||||
ORDER BY
|
||||
a.timestamp
|
||||
) AS prev_check,
|
||||
LAG(a.timestamp) OVER (
|
||||
PARTITION BY
|
||||
a.card_uid,
|
||||
a.timestamp::DATE
|
||||
ORDER BY
|
||||
a.timestamp
|
||||
) AS prev_timestamp
|
||||
FROM
|
||||
anwesenheit a
|
||||
LEFT JOIN s_anwesenheit_typen sat ON a.anwesenheit_typ = sat.anwesenheit_id
|
||||
WHERE
|
||||
a.card_uid = $1
|
||||
AND a.timestamp::DATE >= $2::DATE
|
||||
AND a.timestamp::DATE <= $3::DATE
|
||||
),
|
||||
normalized_bookings AS (
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
all_bookings
|
||||
WHERE
|
||||
prev_check IS NULL
|
||||
OR prev_check <> check_in_out
|
||||
)
|
||||
SELECT
|
||||
d.work_date,
|
||||
COALESCE(MIN(b.timestamp), NOW()) AS time_from,
|
||||
COALESCE(MAX(b.timestamp), NOW()) AS time_to,
|
||||
EXTRACT(
|
||||
EPOCH
|
||||
FROM
|
||||
SUM(
|
||||
CASE
|
||||
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
|
||||
)
|
||||
) AS total_work_seconds,
|
||||
EXTRACT(
|
||||
EPOCH
|
||||
FROM
|
||||
SUM(
|
||||
CASE
|
||||
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
|
||||
)
|
||||
) AS total_pause_seconds,
|
||||
jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'check_in_out',
|
||||
b.check_in_out,
|
||||
'valid',
|
||||
coalesce(b.check_in_out != b.prev_check, true),
|
||||
'timestamp',
|
||||
b.timestamp,
|
||||
'counter_id',
|
||||
b.counter_id,
|
||||
'anwesenheit_typ',
|
||||
jsonb_build_object(
|
||||
'anwesenheit_id',
|
||||
b.anwesenheit_typ,
|
||||
'anwesenheit_name',
|
||||
b.anwesenheit_typ_name
|
||||
)
|
||||
)
|
||||
ORDER BY
|
||||
b.timestamp
|
||||
) FILTER (
|
||||
WHERE
|
||||
b.card_uid IS NOT NULL
|
||||
) AS bookings
|
||||
FROM
|
||||
all_days d
|
||||
LEFT JOIN all_bookings b ON b.work_date = d.work_date
|
||||
GROUP BY
|
||||
d.work_date;
|
||||
`)
|
||||
|
||||
// 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),
|
||||
// ordered_bookings AS (
|
||||
// SELECT
|
||||
// a.timestamp::DATE AS work_date,
|
||||
// a.timestamp,
|
||||
// a.check_in_out,
|
||||
// a.counter_id,
|
||||
// a.anwesenheit_typ,
|
||||
// sat.anwesenheit_name AS anwesenheit_typ_name,
|
||||
// LAG(a.timestamp) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_timestamp,
|
||||
// LAG(a.check_in_out) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_check
|
||||
// FROM anwesenheit a
|
||||
// LEFT JOIN s_anwesenheit_typen sat ON a.anwesenheit_typ = sat.anwesenheit_id
|
||||
// WHERE a.card_uid = $1
|
||||
// AND a.timestamp::DATE >= $2
|
||||
// AND a.timestamp::DATE <= $3
|
||||
// )
|
||||
// normalized_bookings AS (
|
||||
// SELECT *
|
||||
// FROM (
|
||||
// SELECT
|
||||
// a.card_uid,
|
||||
// a.timestamp,
|
||||
// a.timestamp::DATE AS work_date,
|
||||
// a.check_in_out,
|
||||
// a.counter_id,
|
||||
// a.anwesenheit_typ,
|
||||
// sat.anwesenheit_name AS anwesenheit_typ_name,
|
||||
// LAG(a.check_in_out) OVER (
|
||||
// PARTITION BY a.card_uid, a.timestamp::DATE
|
||||
// ORDER BY a.timestamp
|
||||
// ) AS prev_check
|
||||
// FROM anwesenheit a
|
||||
// LEFT JOIN s_anwesenheit_typen sat
|
||||
// ON a.anwesenheit_typ = sat.anwesenheit_id
|
||||
// WHERE a.card_uid = $1
|
||||
// AND a.timestamp::DATE >= $2
|
||||
// AND a.timestamp::DATE <= $3
|
||||
// ) t
|
||||
// WHERE prev_check IS NULL OR prev_check <> check_in_out
|
||||
// ),
|
||||
// ordered_bookings AS (
|
||||
// SELECT
|
||||
// *,
|
||||
// LAG(timestamp) OVER (
|
||||
// PARTITION BY card_uid, work_date
|
||||
// ORDER BY timestamp
|
||||
// ) AS prev_timestamp
|
||||
// FROM normalized_bookings
|
||||
// )
|
||||
// SELECT
|
||||
// d.work_date,
|
||||
// COALESCE(MIN(b.timestamp), NOW()) AS time_from,
|
||||
@@ -330,26 +381,29 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
||||
var workDay WorkDay
|
||||
var bookings []byte
|
||||
if err := rows.Scan(&workDay.Day, &workDay.TimeFrom, &workDay.TimeTo, &workSec, &pauseSec, &bookings); err != nil {
|
||||
log.Println("Error scanning row!", err)
|
||||
slog.Error("Error scanning row!", "Error", err)
|
||||
return workDays
|
||||
}
|
||||
workDay.workTime = time.Duration(workSec * float64(time.Second))
|
||||
workDay.pauseTime = time.Duration(pauseSec * float64(time.Second))
|
||||
err = json.Unmarshal(bookings, &workDay.Bookings)
|
||||
if err != nil {
|
||||
log.Println("Error parsing bookings JSON!", err)
|
||||
return nil
|
||||
if bookings != nil {
|
||||
err = json.Unmarshal(bookings, &workDay.Bookings)
|
||||
if err != nil {
|
||||
slog.Error("Error parsing bookings JSON!", "Error", err, "Json", bookings)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// better empty day handling
|
||||
if len(workDay.Bookings) == 1 && workDay.Bookings[0].CounterId == 0 {
|
||||
workDay.Bookings = []Booking{}
|
||||
}
|
||||
// if len(workDay.Bookings) == 1 && workDay.Bookings[0].CounterId == 0 {
|
||||
// workDay.Bookings = []Booking{}
|
||||
// }
|
||||
if len(workDay.Bookings) >= 1 || !helper.IsWeekend(workDay.Date()) {
|
||||
workDays = append(workDays, workDay)
|
||||
}
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
log.Println("Error in workday rows!", err)
|
||||
slog.Error("Error in workday rows!", "Error", err)
|
||||
return workDays
|
||||
}
|
||||
return workDays
|
||||
|
||||
Reference in New Issue
Block a user