from __future__ import annotations
from onegov.core.orm import Base
from onegov.core.orm.mixins import ContentMixin
from onegov.core.orm.mixins import dict_markup_property
from onegov.core.orm.mixins import TimestampMixin
from onegov.core.orm.types import UUID
from onegov.file import AssociatedFiles
from onegov.landsgemeinde import _
from onegov.landsgemeinde.models.mixins import TimestampedVideoMixin
from onegov.landsgemeinde.observer import observes
from onegov.search import ORMSearchable
from sqlalchemy import Column
from sqlalchemy import Enum
from sqlalchemy import ForeignKey
from sqlalchemy import Integer
from sqlalchemy import Text
from sqlalchemy.orm import object_session
from sqlalchemy.orm import relationship
from sqlalchemy.orm.attributes import flag_modified
from sqlalchemy.orm.attributes import set_committed_value
from uuid import uuid4
from typing import TYPE_CHECKING
if TYPE_CHECKING:
import uuid
from datetime import date as date_t, datetime
from onegov.file import File
from onegov.landsgemeinde.models import AgendaItem
from onegov.landsgemeinde.models import Assembly
from translationstring import TranslationString
from typing import Literal
from typing import TypeAlias
[docs]
VotumState: TypeAlias = Literal[
'draft', 'scheduled', 'ongoing', 'completed']
[docs]
STATES: dict[VotumState, TranslationString] = {
'draft': _('draft'),
'scheduled': _('scheduled'),
'ongoing': _('ongoing'),
'completed': _('completed')
}
[docs]
class Votum(
Base, ContentMixin, TimestampMixin, AssociatedFiles, ORMSearchable,
TimestampedVideoMixin
):
[docs]
__tablename__ = 'landsgemeinde_vota'
[docs]
fts_type_title = _('Vota')
[docs]
fts_title_property = None
[docs]
fts_properties = {
'text': {'type': 'localized', 'weight': 'A'},
'motion': {'type': 'localized', 'weight': 'A'},
'statement_of_reasons': {'type': 'localized', 'weight': 'C'},
'person_name': {'type': 'text', 'weight': 'A'},
'person_function': {'type': 'text', 'weight': 'B'},
'person_place': {'type': 'text', 'weight': 'D'},
'person_political_affiliation': {'type': 'text', 'weight': 'C'},
}
@property
[docs]
def fts_last_change(self) -> datetime:
self._fetch_if_necessary()
return self.agenda_item.fts_last_change
@property
[docs]
def fts_suggestion(self) -> tuple[str, ...]:
return ()
[docs]
def _fetch_if_necessary(self) -> None:
session = object_session(self)
if session is None:
return
if self.agenda_item_id is not None and self.agenda_item is None:
# retrieve the assembly
from onegov.landsgemeinde.models import AgendaItem # type: ignore[unreachable]
set_committed_value(
self,
'agenda_item',
session.get(AgendaItem, self.agenda_item_id)
)
#: the internal id of the votum
[docs]
id: Column[uuid.UUID] = Column(
UUID, # type:ignore[arg-type]
primary_key=True,
default=uuid4
)
#: the state of the votum
[docs]
state: Column[VotumState] = Column(
Enum(*STATES.keys(), name='votum_item_state'), # type:ignore[arg-type]
nullable=False
)
#: the external id of the agenda item
[docs]
number: Column[int] = Column(Integer, nullable=False)
#: The main text of the votum
[docs]
text = dict_markup_property('content')
#: Motion of the votum
[docs]
motion = dict_markup_property('content')
#: Statement of reasons of the votum
[docs]
statement_of_reasons = dict_markup_property('content')
#: The name of the person
[docs]
person_name: Column[str | None] = Column(Text, nullable=True)
#: The function of the person
[docs]
person_function: Column[str | None] = Column(Text, nullable=True)
#: The place of the person
[docs]
person_place: Column[str | None] = Column(Text, nullable=True)
#: The political affiliation of the person (party or parliamentary group)
[docs]
person_political_affiliation: Column[str | None] = Column(
Text,
nullable=True
)
#: A picture of the person
[docs]
person_picture: Column[str | None] = Column(Text, nullable=True)
#: the agenda this votum belongs to
[docs]
agenda_item_id: Column[uuid.UUID] = Column(
UUID, # type:ignore[arg-type]
ForeignKey(
'landsgemeinde_agenda_items.id',
onupdate='CASCADE',
ondelete='CASCADE'
),
nullable=False
)
[docs]
agenda_item: relationship[AgendaItem] = relationship(
'AgendaItem',
back_populates='vota',
)
@property
[docs]
def date(self) -> date_t:
return self.agenda_item.date
@property
[docs]
def agenda_item_number(self) -> int:
return self.agenda_item.number
@property
[docs]
def assembly(self) -> Assembly: # type:ignore[override]
return self.agenda_item.assembly
@property
[docs]
def person_details(self) -> str:
details = (
self.person_function,
self.person_political_affiliation,
self.person_place
)
return ', '.join(d for d in details if d)
@observes('files', 'agenda_item.assembly.date')
[docs]
def update_assembly_date(self, files: list[File], date: date_t) -> None:
# NOTE: Makes sure we will get reindexed, it doesn't really
# matter what we flag as modified, we just pick something.
flag_modified(self, 'number')
if not files or date is None:
# nothing else to do
return
for file in files:
if file.meta.get('assembly_date') != date:
file.meta['assembly_date'] = date
flag_modified(file, 'meta')