Source code for org.forms.political_business
from __future__ import annotations
from datetime import date
from itertools import zip_longest
from markupsafe import Markup
from onegov.core.templates import render_macro
from onegov.form import Form
from onegov.form.fields import ChosenSelectField
from onegov.form.fields import TranslatedSelectField
from onegov.org import _
from onegov.org.models import PoliticalBusiness
from onegov.org.models import PoliticalBusinessParticipation
from onegov.org.models import PoliticalBusinessParticipationCollection
from onegov.org.models import RISParliamentarian
from onegov.org.models import RISParliamentaryGroup
from onegov.org.models.political_business import (
POLITICAL_BUSINESS_TYPE,
POLITICAL_BUSINESS_STATUS
)
from wtforms.fields import DateField
from wtforms.fields import FormField
from wtforms.fields import FieldList
from wtforms.fields import StringField
from wtforms.fields import SelectField
from wtforms.utils import unset_value
from wtforms.validators import InputRequired
from wtforms.validators import Optional
from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Collection
from collections.abc import Sequence
from wtforms.fields.choices import _Choice
from wtforms.fields.core import _Filter
from wtforms.meta import _MultiDictLikeWithGetlist
[docs]
class ParticipantForm(Form):
[docs]
parliamentarian_id = SelectField(
label='',
choices=(),
render_kw={
'class_': 'participant-select',
'data-placeholder': _('Select additional participant'),
'data-no_results_text': _('No results match'),
}
)
[docs]
participant_type = SelectField(
label=_('Role'),
depends_on=('parliamentarian_id', '!'),
render_kw={'class_': 'indent-form-field'},
choices=[
('', ''),
('First signatory', _('First signatory')),
('Co-signatory', _('Co-signatory')),
]
)
if TYPE_CHECKING:
else:
FieldBase = FieldList
[docs]
def participant_widget(field: FieldBase, **kwargs: Any) -> Markup:
field.meta.request.include('participant-select')
return Markup('<br>').join(
Markup('<div id="{}">{}</div>').format(f.id, f())
for f in field
)
[docs]
class BusinessParticipationField(FieldBase):
[docs]
def process(
self,
formdata: _MultiDictLikeWithGetlist | None,
data: Any = unset_value,
extra_filters: Sequence[_Filter] | None = None
) -> None:
# FIXME: I'm not quite sure why we need to do this
# but it looks like the last_index gets updated
# to 0 by something, so we start counting at 1
# instead of 0, which breaks the field
self.last_index = -1
super().process(formdata, data, extra_filters)
# always have an empty extra entry
if (
formdata is None
and self[-1].form.parliamentarian_id.data is not None
):
self.append_entry()
[docs]
def populate_obj(self, obj: PoliticalBusiness, name: str) -> None: # type: ignore[override]
assert name == 'participants'
collection = PoliticalBusinessParticipationCollection(
self.meta.request.session
)
participants = obj.participants
output: list[PoliticalBusinessParticipation] = []
for field, participant in zip_longest(self.entries, participants):
if field is None:
# this generally shouldn't happen, but we should
# guard against it anyways, since it can happen
# if people manually call pop_entry()
break
participant_id = field.form.parliamentarian_id.data
participant_type = field.form.participant_type.data
if not participant_id:
if participant is not None:
collection.delete(participant)
continue
elif participant is None:
participant = collection.add(
political_business_id=obj.id,
parliamentarian_id=participant_id,
participant_type=participant_type
)
elif str(participant.id) != participant_id:
collection.delete(participant)
participant = collection.add(
political_business_id=obj.id,
parliamentarian_id=participant_id,
participant_type=participant_type
)
else:
participant.participant_type = participant_type
output.append(participant)
setattr(obj, name, output)
[docs]
class PoliticalBusinessForm(Form):
[docs]
political_business_type = TranslatedSelectField(
label=_('Type'),
choices=sorted(POLITICAL_BUSINESS_TYPE.items()),
validators=[InputRequired()],
)
[docs]
status = TranslatedSelectField(
label=_('Business Status'),
choices=sorted(POLITICAL_BUSINESS_STATUS.items()),
validators=[InputRequired()],
default='-',
)
[docs]
entry_date = DateField(
label=_('Entry Date'),
validators=[InputRequired()],
default=date.today,
)
# FIXME : make multiple groups possible ChosenSelectMultipleField
[docs]
parliamentary_group_id = ChosenSelectField(
label=_('Parliamentary Group'),
validators=[Optional()],
choices=[],
)
[docs]
participants = BusinessParticipationField(
FormField(
ParticipantForm,
widget=lambda field, **kw: Markup('').join(
Markup('<div><label>{}</label></div>').format(render_macro(
field.meta.request.template_loader.macros['field'],
field.meta.request,
{
'field': f,
# FIXME: only used for rendering descriptions
# we should probably move this logic
# into a template macro or a method on
# CoreRequest, this doesn't really need
# to be part of Form, we could also move
# it to the form meta and access it
# through the field instead
'form': field.meta.request.get_form(
Form, csrf_support=False
)
}
)) for f in field
)
),
label=_('Participations'),
fieldset=_('Participations'),
# we always have at least one empty entry
min_entries=1,
widget=participant_widget,
)
[docs]
def on_request(self) -> None:
selectable_participants = (
self.request.session.query(RISParliamentarian)
.filter(RISParliamentarian.active)
.order_by(
RISParliamentarian.last_name,
RISParliamentarian.first_name
)
)
if selectable_participants:
selected = {
participant.parliamentarian_id: participant.participant_type
for participant in self.model.participants
} if isinstance(self.model, PoliticalBusiness) else {}
choices: list[_Choice] = [
(
str(participant.id),
participant.display_name,
{'data-role': selected.get(participant.id) or ''}
)
for participant in selectable_participants
]
choices.insert(0, ('', ''))
# NOTE: Ensures translations work in FormField
for field in self.participants:
field.form.meta = self.meta
field.form.parliamentarian_id.meta = self.meta
field.form.parliamentarian_id.choices = choices
render_kw = field.form.parliamentarian_id.render_kw
render_kw['data-placeholder'] = self.request.translate(
render_kw['data-placeholder'])
render_kw['data-no_results_text'] = self.request.translate(
render_kw['data-no_results_text'])
field.form.participant_type.meta = self.meta
field.form.participant_type.choices = [ # type: ignore[misc]
(value, self.request.translate(label) if label else label)
for value, label in field.form.participant_type.choices
]
else:
self.delete_field('participants')
self.political_business_type.choices.insert(0, ('', '-')) # type:ignore[union-attr]
self.status.choices.insert(0, ('', '-')) # type:ignore[union-attr]
groups = (
self.request.session.query(RISParliamentaryGroup)
.filter(RISParliamentaryGroup.end == None)
.order_by(RISParliamentaryGroup.name)
.all()
)
self.parliamentary_group_id.choices = [
(str(g.id.hex), g.name) for g in groups
]
self.parliamentary_group_id.choices.insert(0, ('', '-'))
[docs]
def get_useful_data(self) -> dict[str, Any]: # type:ignore[override]
result = super().get_useful_data()
result.pop('participants', None)
result['parliamentary_group_id'] = (
result.get('parliamentary_group_id') or None)
return result
[docs]
def populate_obj( # type: ignore[override]
self,
obj: PoliticalBusiness, # type: ignore[override]
exclude: Collection[str] | None = None,
include: Collection[str] | None = None
) -> None:
super().populate_obj(
obj,
exclude={
'parliamentary_group_id',
*(exclude or ())
},
include=include
)
obj.parliamentary_group_id = self.parliamentary_group_id.data or None