from collections import OrderedDict
from onegov.election_day import _
from onegov.election_day.models import ElectionResult
from onegov.election_day.models import List
from onegov.election_day.models import ListResult
from sqlalchemy import desc
from sqlalchemy import func
from sqlalchemy.orm import object_session
from sqlalchemy.sql.expression import case
from typing import cast
from typing import Any
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Collection
from onegov.core.types import JSONObject
from onegov.core.types import JSONObject_ro
from onegov.election_day.models import Election
from onegov.election_day.models import ProporzElection
from onegov.election_day.request import ElectionDayRequest
from sqlalchemy.orm import Query
from sqlalchemy.sql import ColumnElement
from typing import NamedTuple
from uuid import UUID
[docs]
class ListResultRow(NamedTuple):
[docs]
number_of_mandates: int
[docs]
def get_list_results(
election: 'Election',
limit: int | None = None,
names: 'Collection[str] | None' = None,
sort_by_names: bool = False,
entities: 'Collection[str] | None' = None
) -> 'Query[ListResultRow]':
""" Returns the aggregated list results as list. """
session = object_session(election)
result = session.query(
func.sum(ListResult.votes).label('votes'),
List.name,
List.number_of_mandates
)
result = result.join(ListResult.list)
result = result.filter(List.election_id == election.id)
if names:
result = result.filter(List.name.in_(names))
if entities:
election_result_id = session.query(ElectionResult.id).filter(
ElectionResult.election_id == election.id,
ElectionResult.name.in_(entities)
)
election_result_id = [result.id for result in election_result_id]
result = result.filter(
ListResult.election_result_id.in_(election_result_id)
)
result = result.group_by(
List.name,
List.number_of_mandates
)
order: list[ColumnElement[Any]] = [desc('votes')]
if names and sort_by_names:
order.insert(0, case(
[
(List.name == name, index)
for index, name in enumerate(names, 1)
],
else_=0
))
result = result.order_by(*order)
if limit and limit > 0:
result = result.limit(limit)
return result
[docs]
def get_lists_data(
election: 'Election',
limit: int | None = None,
names: 'Collection[str] | None' = None,
mandates_only: bool = False,
sort_by_names: bool = False,
entities: 'Collection[str] | None' = None
) -> 'JSONObject_ro':
"""" View the lists as JSON. Used to for the lists bar chart. """
allocated_mandates = election.allocated_mandates
colors = election.colors
if election.type == 'majorz':
return {
'results': [],
'majority': None,
'title': election.title
}
return {
'results': [
{
'text': list_.name,
'value': list_.votes,
'value2': list_.number_of_mandates,
'class': (
'active'
if list_.number_of_mandates or not allocated_mandates
else 'inactive'
),
'color': colors.get(list_.name)
}
for list_ in get_list_results(
election,
limit=limit,
names=names,
sort_by_names=sort_by_names,
entities=entities
)
],
'majority': None,
'title': election.title
}
[docs]
def get_lists_panachage_data(
election: 'Election',
request: 'ElectionDayRequest | None'
) -> 'JSONObject_ro':
"""" Get the panachage data as JSON. Used to for the panachage sankey
chart.
"""
if election.type == 'majorz':
return {}
election = cast('ProporzElection', election)
if not election.has_lists_panachage_data:
return {}
blank = request.translate(_('Blank list')) if request else '-'
nodes: dict[str, JSONObject] = OrderedDict()
nodes['left.999'] = {'name': blank}
for list_ in sorted(election.lists, key=lambda l: l.name):
nodes[f'left.{list_.list_id}'] = {
'name': list_.name,
'color': election.colors.get(list_.name),
'active': list_.number_of_mandates > 0
}
for list_ in election.lists:
nodes[f'right.{list_.list_id}'] = {
'name': list_.name,
'color': election.colors.get(list_.name),
'active': list_.number_of_mandates > 0
}
node_keys = list(nodes.keys())
links: list[JSONObject_ro] = []
list_ids: dict[UUID | None, str] = {
list.id: list.list_id for list in election.lists
}
list_ids[None] = '999'
for list_target in election.lists:
target_index = node_keys.index(f'right.{list_target.list_id}')
for result in list_target.panachage_results:
source_list_id = list_ids[result.source_id]
source_key = f'left.{source_list_id}'
source_item = nodes.get(source_key, {})
source_index = node_keys.index(source_key)
votes = result.votes
if list_target.list_id == source_list_id:
continue
links.append({
'source': source_index,
'target': target_index,
'value': votes,
'color': source_item.get('color'),
'active': source_item.get('active')
})
count = 0
for key in nodes.keys():
count = count + 1
nodes[key]['id'] = count
return {
'nodes': list(nodes.values()),
'links': links,
'title': election.title
}