from collections import OrderedDict
from typing import Any
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Collection
from decimal import Decimal
from onegov.election_day.models import ElectionCompound
from onegov.election_day.models import ProporzElection
[docs]
def export_parties_internal(
item: 'ElectionCompound | ProporzElection',
locales: 'Collection[str]',
default_locale: str,
json_serializable: bool = False
) -> list[dict[str, Any]]:
""" Returns all party results with the panachage as list with dicts.
This is meant as a base for json/csv/excel exports. The result is
therefore a flat list of dictionaries with repeating values to avoid
the nesting of values. Each record in the resulting list is a single
candidate result for each political entity. Party results are not
included in the export (since they are not really connected with the
lists).
If `json_serializable` is True, decimals are converted to floats. This
might be a lossy conversation!
"""
result = []
parts = {(r.domain, r.domain_segment) for r in item.party_results}
for domain, domain_segment in sorted(parts):
result.extend(
_export_parties(
item,
locales, default_locale,
json_serializable,
domain, domain_segment
)
)
return result
[docs]
def _export_parties(
item: 'ElectionCompound | ProporzElection',
locales: 'Collection[str]',
default_locale: str,
json_serializable: bool = False,
domain: str | None = None,
domain_segment: str | None = None
) -> list[dict[str, Any]]:
""" Returns all party results with the panachage as list with dicts.
This is meant as a base for json/csv/excel exports. The result is
therefore a flat list of dictionaries with repeating values to avoid
the nesting of values. Each record in the resulting list is a single
candidate result for each political entity. Party results are not
included in the export (since they are not really connected with the
lists).
If `json_serializable` is True, decimals are converted to floats. This
might be a lossy conversation!
"""
def convert_decimal(value: 'Decimal | None') -> float | str | None:
if value is None:
return value
if json_serializable:
return float(value)
return str(value)
results: dict[int, dict[str, dict[str, Any]]] = {}
# get the party results
for party_result in item.party_results:
if party_result.domain != (domain or item.domain):
continue
if domain_segment and party_result.domain_segment != domain_segment:
continue
results_per_year = results.setdefault(party_result.year, {})
results_per_year[party_result.party_id] = {
'domain': party_result.domain,
'domain_segment': party_result.domain_segment,
'name_translations': party_result.name_translations,
'total_votes': party_result.total_votes,
'mandates': party_result.number_of_mandates,
'votes': party_result.votes,
'voters_count': party_result.voters_count,
'voters_count_percentage': party_result.voters_count_percentage
}
# get the panachage results
if domain == item.domain:
for panachage_result in item.party_panachage_results:
results_per_year = results.setdefault(item.date.year, {})
target = results_per_year.setdefault(panachage_result.target, {})
target[panachage_result.source] = panachage_result.votes
rows: list[dict[str, Any]] = []
parties = sorted({key for r in results.values() for key in r.keys()})
for year in sorted(results.keys(), reverse=True):
for party_id in parties:
if party_id not in results[year]:
continue
result = results[year][party_id]
default_name = result['name_translations'].get(default_locale)
default_color = item.colors.get(default_name)
fallback_color = None
# add the party results
row = OrderedDict()
row['domain'] = result['domain'] or item.domain
row['domain_segment'] = (
result['domain_segment']
or getattr(item, 'domain_segment', None)
or None
)
row['year'] = year
row['id'] = party_id
row['name'] = default_name
for locale in locales:
name = result['name_translations'].get(locale)
fallback_color = fallback_color or item.colors.get(name)
row[f'name_{locale}'] = name
row['total_votes'] = result['total_votes']
row['color'] = default_color or fallback_color
row['mandates'] = result['mandates']
row['votes'] = result['votes']
row['voters_count'] = convert_decimal(result['voters_count'])
row['voters_count_percentage'] = convert_decimal(
result['voters_count_percentage']
)
# add the panachage results
if item.party_panachage_results:
for source in parties:
column = f'panachage_votes_from_{source}'
row[column] = result.get(source, None)
row['panachage_votes_from_999'] = result.get('', None)
rows.append(row)
return rows