Source code for election_day.utils.election_compound.candidates

from onegov.core.utils import groupbylist
from onegov.election_day.models import Candidate
from onegov.election_day.models import Election
from onegov.election_day.models import List
from operator import itemgetter
from sqlalchemy.orm import object_session
from statistics import mean


from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from collections.abc import Iterable
    from onegov.election_day.models import ElectionCompound
    from onegov.election_day.models import ElectionCompoundPart
    from onegov.election_day.types import Gender
    from sqlalchemy.orm import Query
    from sqlalchemy.orm import Session
    from typing import NamedTuple
    from typing import TypedDict

[docs] class ElectedCandidateRow(NamedTuple):
[docs] family_name: str
[docs] first_name: str
[docs] party: str | None
[docs] gender: Gender | None
[docs] year_of_birth: int | None
[docs] list: str | None
[docs] list_id: str | None
[docs] election_id: str | None
class CandidateStatistics(TypedDict): count: int age: int | None
[docs] def get_elected_candidates( election_compound: 'ElectionCompound | ElectionCompoundPart', session: 'Session' ) -> 'Query[ElectedCandidateRow]': """ Returns the elected candidates of an election compound. """ election_ids = [election.id for election in election_compound.elections] elected = session.query( Candidate.family_name, Candidate.first_name, Candidate.party, Candidate.gender, Candidate.year_of_birth, List.name.label('list'), List.list_id, Election.id.label('election_id') ) elected = elected.outerjoin(List, Candidate.list_id == List.id) elected = elected.outerjoin(Election) elected = elected.order_by( Election.shortcode, List.list_id, Candidate.family_name, Candidate.first_name ) elected = elected.filter(Candidate.election_id.in_(election_ids)) elected = elected.filter(Candidate.elected.is_(True)) return elected
[docs] def get_candidate_statistics( election_compound: 'ElectionCompound | ElectionCompoundPart', elected_candidates: 'Iterable[ElectedCandidateRow] | None' = None ) -> dict[str, 'CandidateStatistics']: if elected_candidates is None: session = object_session(election_compound) elected_candidates = get_elected_candidates(election_compound, session) year = election_compound.date.year def statistics( values: list[tuple['Gender', int | None]] ) -> 'CandidateStatistics': birth_years = [ birth_year for _, birth_year in values if birth_year is not None ] age = mean(year - y for y in birth_years) if birth_years else None return { 'count': len(values), 'age': round(age) if age is not None else age } all_values = sorted( ( (candidate.gender or 'undetermined', candidate.year_of_birth) for candidate in elected_candidates ), key=itemgetter(0) ) result = { gender: statistics(values) for gender, values in groupbylist(all_values, key=itemgetter(0)) } result['total'] = statistics(all_values) if set(result) - {'total', 'undetermined'}: return result return {}