feat(politics): update election scheduling logic to include all unoccupied positions and improve query efficiency
This commit is contained in:
@@ -80,72 +80,98 @@ private:
|
||||
)";
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// STEP 1: Erzeuge nur diejenigen Wahlen, bei denen noch keine Election
|
||||
// für denselben Termin (NOW()+2 Tage) existiert.
|
||||
// STEP 1: Erzeuge Wahlen für *alle* unbesetzten Soll-Stellen.
|
||||
// Dabei zählen auch bereits angelegte Wahlen als Reservierung,
|
||||
// damit der tägliche Lauf keine doppelten Ausschreibungen erzeugt.
|
||||
// ------------------------------------------------------------
|
||||
static constexpr const char* QUERY_SELECT_NEEDED_ELECTIONS = R"(
|
||||
WITH
|
||||
-- 1) Definiere das heutige Datum einmal als Referenz
|
||||
-- 1) Definiere den tatsächlichen Wahltermin einmal als Referenz.
|
||||
-- Die Bewerbungsphase dauert drei Tage (vgl. process_elections()).
|
||||
target_date AS (
|
||||
SELECT NOW()::date AS election_date
|
||||
SELECT NOW() + INTERVAL '3 days' AS election_date
|
||||
),
|
||||
|
||||
-- 2) Lösche nur diejenigen Ämter, deren Ablaufdatum heute erreicht ist,
|
||||
-- und merke deren (office_type_id, region_id)
|
||||
expired_today AS (
|
||||
-- 2) Abgelaufene Ämter entfernen. Ein <= statt = heilt auch Lücken,
|
||||
-- falls der Worker an einem Tag nicht gelaufen ist.
|
||||
expired_offices AS (
|
||||
DELETE FROM falukant_data.political_office AS po
|
||||
USING falukant_type.political_office_type AS pot
|
||||
WHERE po.office_type_id = pot.id
|
||||
AND (po.created_at + (pot.term_length * INTERVAL '1 day'))::date = (SELECT election_date FROM target_date)
|
||||
AND po.created_at + (pot.term_length * INTERVAL '1 day') <= NOW()
|
||||
RETURNING
|
||||
pot.id AS office_type_id,
|
||||
po.region_id AS region_id
|
||||
),
|
||||
|
||||
-- 3) Gruppiere nach Typ+Region und zähle, wie viele Sitze heute frei geworden sind
|
||||
gaps_per_region AS (
|
||||
-- 3) Alle Soll-Stellen pro politischem Amt und echter Spielregion.
|
||||
-- Bisher wurden nur gerade abgelaufene Ämter betrachtet. Deshalb
|
||||
-- erhielten nie besetzte (z. B. neu hinzugefügte) Amtstypen nie eine
|
||||
-- Ausschreibung.
|
||||
required_seats AS (
|
||||
SELECT
|
||||
office_type_id,
|
||||
region_id,
|
||||
COUNT(*) AS gaps
|
||||
FROM expired_today
|
||||
pot.id AS office_type_id,
|
||||
r.id AS region_id,
|
||||
pot.seats_per_region AS seats_total
|
||||
FROM falukant_type.political_office_type AS pot
|
||||
JOIN falukant_data.region AS r
|
||||
ON TRUE
|
||||
JOIN falukant_type.region AS rt
|
||||
ON rt.id = r.region_type_id
|
||||
AND rt.label_tr = pot.region_type
|
||||
),
|
||||
|
||||
-- 4) Aktuell besetzte und durch laufende Wahlen bereits reservierte
|
||||
-- Sitze zählen.
|
||||
occupied_seats AS (
|
||||
SELECT po.office_type_id, po.region_id, COUNT(*) AS seats_occupied
|
||||
FROM falukant_data.political_office AS po
|
||||
JOIN falukant_type.political_office_type AS pot
|
||||
ON pot.id = po.office_type_id
|
||||
-- Datenändernde CTEs teilen sich denselben Statement-Snapshot.
|
||||
-- Deshalb abgelaufene Zeilen hier ausdrücklich nicht mitzählen.
|
||||
WHERE po.created_at + (pot.term_length * INTERVAL '1 day') > NOW()
|
||||
GROUP BY po.office_type_id, po.region_id
|
||||
),
|
||||
reserved_seats AS (
|
||||
SELECT office_type_id, region_id, SUM(posts_to_fill) AS seats_reserved
|
||||
FROM falukant_data.election
|
||||
GROUP BY office_type_id, region_id
|
||||
),
|
||||
|
||||
-- 4) Filtere nur diejenigen Typ+Region‐Kombinationen, für die noch **keine** Election
|
||||
-- mit genau demselben Datum angelegt wurde
|
||||
to_schedule AS (
|
||||
gaps_per_region AS (
|
||||
SELECT
|
||||
g.office_type_id,
|
||||
g.region_id,
|
||||
g.gaps,
|
||||
rs.office_type_id,
|
||||
rs.region_id,
|
||||
rs.seats_total
|
||||
- COALESCE(os.seats_occupied, 0)
|
||||
- COALESCE(es.seats_reserved, 0) AS gaps,
|
||||
td.election_date
|
||||
FROM
|
||||
gaps_per_region AS g
|
||||
CROSS JOIN
|
||||
target_date AS td
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM falukant_data.election AS e
|
||||
WHERE e.office_type_id = g.office_type_id
|
||||
AND e.region_id = g.region_id
|
||||
AND e."date"::date = td.election_date
|
||||
)
|
||||
FROM required_seats AS rs
|
||||
LEFT JOIN occupied_seats AS os
|
||||
ON os.office_type_id = rs.office_type_id
|
||||
AND os.region_id = rs.region_id
|
||||
LEFT JOIN reserved_seats AS es
|
||||
ON es.office_type_id = rs.office_type_id
|
||||
AND es.region_id = rs.region_id
|
||||
CROSS JOIN target_date AS td
|
||||
WHERE rs.seats_total
|
||||
- COALESCE(os.seats_occupied, 0)
|
||||
- COALESCE(es.seats_reserved, 0) > 0
|
||||
),
|
||||
|
||||
-- 5) Lege für jede so gefilterte Kombination genau eine Election an
|
||||
-- 5) Lege für jede vakante Kombination genau eine Election an.
|
||||
new_elections AS (
|
||||
INSERT INTO falukant_data.election
|
||||
(office_type_id, "date", posts_to_fill, created_at, updated_at, region_id)
|
||||
SELECT
|
||||
ts.office_type_id,
|
||||
ts.election_date AS "date",
|
||||
ts.election_date AS "date",
|
||||
ts.gaps AS posts_to_fill,
|
||||
NOW() AS created_at,
|
||||
NOW() AS updated_at,
|
||||
ts.region_id
|
||||
FROM
|
||||
to_schedule AS ts
|
||||
gaps_per_region AS ts
|
||||
RETURNING
|
||||
id AS election_id,
|
||||
region_id,
|
||||
|
||||
Reference in New Issue
Block a user