from __future__ import annotations
from collections import OrderedDict
from onegov.election_day.models.election import Election
from onegov.election_day.models.election_compound import ElectionCompound
from onegov.election_day.models.election_compound import ElectionCompoundPart
from onegov.election_day.models.vote import Vote
from onegov.core.orm import Base
from onegov.core.orm.mixins import ContentMixin
from onegov.core.orm.mixins import TimestampMixin
from sqlalchemy import ForeignKey
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import object_session
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Mapped
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from datetime import datetime
[docs]
class ScreenType:
[docs]
types = (
'simple_vote',
'complex_vote',
'majorz_election',
'proporz_election',
'election_compound',
'election_compound_part'
)
def __init__(self, type_: str):
assert type_ in self.types
@property
[docs]
def categories(self) -> tuple[str, ...]:
if self.type == 'simple_vote':
return ('generic', 'vote')
if self.type == 'complex_vote':
return ('generic', 'vote', 'complex_vote')
if self.type == 'majorz_election':
return ('generic', 'election', 'majorz_election')
if self.type == 'proporz_election':
return ('generic', 'election', 'proporz_election')
if self.type == 'election_compound':
return ('generic', 'election_compound')
if self.type == 'election_compound_part':
return ('generic', 'election_compound')
raise NotImplementedError()
[docs]
class Screen(Base, ContentMixin, TimestampMixin):
[docs]
__tablename__ = 'election_day_screens'
#: Identifies the screen
[docs]
id: Mapped[int] = mapped_column(primary_key=True)
#: A unique number for the path
[docs]
number: Mapped[int] = mapped_column(unique=True)
#: The vote
[docs]
vote_id: Mapped[str | None] = mapped_column(
ForeignKey(Vote.id, onupdate='CASCADE')
)
[docs]
vote: Mapped[Vote | None] = relationship(
back_populates='screens'
)
#: The election
[docs]
election_id: Mapped[str | None] = mapped_column(
ForeignKey(Election.id, onupdate='CASCADE')
)
[docs]
election: Mapped[Election | None] = relationship(
back_populates='screens'
)
#: The election compound
[docs]
election_compound_id: Mapped[str | None] = mapped_column(
ForeignKey(ElectionCompound.id, onupdate='CASCADE')
)
[docs]
election_compound: Mapped[ElectionCompound | None] = relationship(
back_populates='screens'
)
#: The domain of the election compound part.
[docs]
domain: Mapped[str | None]
#: The domain segment of the election compound part.
[docs]
domain_segment: Mapped[str | None]
@property
[docs]
def election_compound_part(self) -> ElectionCompoundPart | None:
if self.election_compound and self.domain and self.domain_segment:
return ElectionCompoundPart(
self.election_compound, self.domain, self.domain_segment
)
return None
#: The title
[docs]
description: Mapped[str | None]
#: The type
#: The content of the screen
#: Additional CSS
[docs]
css: Mapped[str | None]
#: The group this screen belongs to, used for cycling
[docs]
group: Mapped[str | None]
#: The duration this screen is presented if cycling
[docs]
duration: Mapped[int | None]
@property
[docs]
def model(
self
) -> Election | ElectionCompound | ElectionCompoundPart | Vote | None:
if self.type in ('simple_vote', 'complex_vote'):
return self.vote
if self.type in ('majorz_election', 'proporz_election'):
return self.election
if self.type == 'election_compound':
return self.election_compound
if self.type == 'election_compound_part':
return self.election_compound_part
raise NotImplementedError()
@property
[docs]
def screen_type(self) -> ScreenType:
return ScreenType(self.type)
@property
[docs]
def last_modified(self) -> datetime | None:
model = self.model
assert model is not None
changes = [self.last_change, model.last_change]
set_changes = [change for change in changes if change]
return max(set_changes) if set_changes else None
@property
[docs]
def next(self) -> Screen | None:
if self.group:
session = object_session(self)
assert session is not None
query = session.query(Screen.number, Screen)
query = query.filter_by(group=self.group).order_by(Screen.number)
screens = OrderedDict(query.tuples().all())
if len(screens) > 1:
keys = tuple(screens.keys())
index = (keys.index(self.number) + 1) % len(screens)
return screens[keys[index]]
return None