Add functionality to identify elections needing candidates: Introduced a new SQL query to select elections without candidates, including both newly registered and manually created elections. Updated the PoliticsWorker to schedule elections and insert candidates accordingly, ensuring all relevant elections are processed.
This commit is contained in:
@@ -213,6 +213,27 @@ const QUERY_INSERT_CANDIDATES: &str = r#"
|
|||||||
) AS sub(id);
|
) AS sub(id);
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
/// Wahlen finden, für die noch keine Kandidaten existieren.
|
||||||
|
/// Das umfasst sowohl frisch eingetragene Wahlen als auch manuell
|
||||||
|
/// angelegte Wahlen, solange:
|
||||||
|
/// - das Wahl-Datum heute oder in der Zukunft liegt und
|
||||||
|
/// - noch kein Eintrag in `falukant_data.candidate` existiert.
|
||||||
|
const QUERY_SELECT_ELECTIONS_NEEDING_CANDIDATES: &str = r#"
|
||||||
|
SELECT
|
||||||
|
e.id AS election_id,
|
||||||
|
e.region_id AS region_id,
|
||||||
|
e.posts_to_fill
|
||||||
|
FROM falukant_data.election AS e
|
||||||
|
WHERE e.region_id IS NOT NULL
|
||||||
|
AND e.posts_to_fill > 0
|
||||||
|
AND e.date::date >= CURRENT_DATE
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM falukant_data.candidate AS c
|
||||||
|
WHERE c.election_id = e.id
|
||||||
|
);
|
||||||
|
"#;
|
||||||
|
|
||||||
const QUERY_PROCESS_EXPIRED_AND_FILL: &str = r#"
|
const QUERY_PROCESS_EXPIRED_AND_FILL: &str = r#"
|
||||||
WITH
|
WITH
|
||||||
expired_offices AS (
|
expired_offices AS (
|
||||||
@@ -536,26 +557,39 @@ impl PoliticsWorker {
|
|||||||
Self::notify_office_filled(pool, broker, &new_offices_direct)?;
|
Self::notify_office_filled(pool, broker, &new_offices_direct)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5) Neue Wahlen planen und Kandidaten eintragen
|
// 5) Neue Wahlen planen
|
||||||
let elections = Self::schedule_elections(pool)?;
|
let new_elections = Self::schedule_elections(pool)?;
|
||||||
if !elections.is_empty() {
|
|
||||||
Self::insert_candidates_for_elections(pool, &elections)?;
|
// 6) Für alle Wahlen ohne Kandidaten (inkl. manuell
|
||||||
|
// angelegter) Kandidaten eintragen.
|
||||||
|
let mut elections_to_fill = Self::find_elections_needing_candidates(pool)?;
|
||||||
|
// Die gerade neu angelegten Wahlen sind typischerweise auch
|
||||||
|
// in obiger Liste enthalten. Falls nicht, fügen wir sie
|
||||||
|
// sicherheitshalber hinzu.
|
||||||
|
for e in new_elections.iter() {
|
||||||
|
if !elections_to_fill.iter().any(|x| x.election_id == e.election_id) {
|
||||||
|
elections_to_fill.push(e.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !elections_to_fill.is_empty() {
|
||||||
|
Self::insert_candidates_for_elections(pool, &elections_to_fill)?;
|
||||||
|
|
||||||
// Benachrichtige User in betroffenen Regionen
|
// Benachrichtige User in betroffenen Regionen
|
||||||
let region_ids: HashSet<i32> =
|
let region_ids: HashSet<i32> =
|
||||||
elections.iter().map(|e| e.region_id).collect();
|
elections_to_fill.iter().map(|e| e.region_id).collect();
|
||||||
let user_ids =
|
let user_ids =
|
||||||
Self::get_user_ids_in_cities_of_regions(pool, ®ion_ids)?;
|
Self::get_user_ids_in_cities_of_regions(pool, ®ion_ids)?;
|
||||||
Self::notify_election_created(pool, broker, &user_ids)?;
|
Self::notify_election_created(pool, broker, &user_ids)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6) Wahlen auswerten und neu besetzte Ämter melden
|
// 7) Wahlen auswerten und neu besetzte Ämter melden
|
||||||
let new_offices_from_elections = Self::process_elections(pool)?;
|
let new_offices_from_elections = Self::process_elections(pool)?;
|
||||||
if !new_offices_from_elections.is_empty() {
|
if !new_offices_from_elections.is_empty() {
|
||||||
Self::notify_office_filled(pool, broker, &new_offices_from_elections)?;
|
Self::notify_office_filled(pool, broker, &new_offices_from_elections)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7) Als letzter Schritt sicherstellen, dass es für keinen
|
// 8) Als letzter Schritt sicherstellen, dass es für keinen
|
||||||
// Amtstyp/Region-Kombi mehr besetzte Ämter gibt als laut
|
// Amtstyp/Region-Kombi mehr besetzte Ämter gibt als laut
|
||||||
// `seats_per_region` erlaubt. Dieser Abgleich wird nach allen
|
// `seats_per_region` erlaubt. Dieser Abgleich wird nach allen
|
||||||
// Lösch- und Besetzungsvorgängen ausgeführt.
|
// Lösch- und Besetzungsvorgängen ausgeführt.
|
||||||
@@ -741,6 +775,36 @@ impl PoliticsWorker {
|
|||||||
Ok(elections)
|
Ok(elections)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Findet alle existierenden Wahlen (inkl. manuell angelegter),
|
||||||
|
/// für die noch keine Kandidaten eingetragen wurden.
|
||||||
|
fn find_elections_needing_candidates(pool: &ConnectionPool) -> Result<Vec<Election>, DbError> {
|
||||||
|
let mut conn = pool
|
||||||
|
.get()
|
||||||
|
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||||
|
|
||||||
|
conn.prepare(
|
||||||
|
"select_elections_needing_candidates",
|
||||||
|
QUERY_SELECT_ELECTIONS_NEEDING_CANDIDATES,
|
||||||
|
)?;
|
||||||
|
let rows = conn.execute("select_elections_needing_candidates", &[])?;
|
||||||
|
|
||||||
|
let mut elections = Vec::with_capacity(rows.len());
|
||||||
|
for row in rows {
|
||||||
|
let election_id = parse_i32(&row, "election_id", -1);
|
||||||
|
let region_id = parse_i32(&row, "region_id", -1);
|
||||||
|
let posts_to_fill = parse_i32(&row, "posts_to_fill", 0);
|
||||||
|
if election_id >= 0 && region_id >= 0 && posts_to_fill > 0 {
|
||||||
|
elections.push(Election {
|
||||||
|
election_id,
|
||||||
|
region_id,
|
||||||
|
posts_to_fill,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(elections)
|
||||||
|
}
|
||||||
|
|
||||||
fn insert_candidates_for_elections(
|
fn insert_candidates_for_elections(
|
||||||
pool: &ConnectionPool,
|
pool: &ConnectionPool,
|
||||||
elections: &[Election],
|
elections: &[Election],
|
||||||
|
|||||||
Reference in New Issue
Block a user