Source code for org.forms.political_business

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 ChosenSelectMultipleField
from onegov.form.fields import TranslatedSelectField
from onegov.form.utils import coerce_uuid
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 SelectChoice
from wtforms.fields import StringField
from wtforms.fields import TextAreaField
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.core import _Filter
    from wtforms.meta import _MultiDictLikeWithGetlist


[docs] class ParticipantForm(Form):
[docs] parliamentarian_id = SelectField( label='', choices=(), coerce=coerce_uuid, 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=[ SelectChoice(''), SelectChoice('First signatory', _('First signatory')), SelectChoice('Co-signatory', _('Co-signatory')), ] )
if TYPE_CHECKING:
[docs] FieldBase = FieldList[FormField[ParticipantForm]]
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] title = StringField( label=_('Title'), validators=[InputRequired()], )
[docs] number = StringField( label=_('Number'), validators=[Optional()], )
[docs] political_business_type = TranslatedSelectField( label=_('Type'), choices=[SelectChoice('', '-'), *( SelectChoice(value, label) for value, label in POLITICAL_BUSINESS_TYPE.items() )], validators=[InputRequired()], choices_sorted=True, )
[docs] status = TranslatedSelectField( label=_('Business Status'), choices=[SelectChoice('', '-'), *( SelectChoice(value, label) for value, label in POLITICAL_BUSINESS_STATUS.items() )], validators=[InputRequired()], choices_sorted=True, default='-', )
[docs] entry_date = DateField( label=_('Submission/publication date'), validators=[InputRequired()], default=date.today, )
[docs] chronology = TextAreaField( label=_('Chronology'), validators=[Optional()], description='Schriftlich beantwortet am ...', )
[docs] parliamentary_groups = ChosenSelectMultipleField( label=_('Parliamentary Group(s)'), validators=[Optional()], choices=lambda form, field: [ SelectChoice(group.id.hex, group.name) for group in form.request.session.query(RISParliamentaryGroup) .filter(RISParliamentaryGroup.end == None) .order_by(RISParliamentaryGroup.name) ] or [SelectChoice('', _('No active parliamentary groups'))], coerce=coerce_uuid )
[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 = [ SelectChoice( participant.id.hex, participant.title, {'data-role': selected.get(participant.id) or ''} ) for participant in selectable_participants ] choices.insert(0, SelectChoice('')) # 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 for choice in field.form.participant_type.choices: if choice.label: choice.label = self.request.translate(choice.label) else: self.delete_field('participants')
[docs] def process_obj(self, obj: PoliticalBusiness) -> None: # type:ignore[override] super().process_obj(obj) # only populate from model when the form did not receive formdata if self.parliamentary_groups.raw_data is None: self.parliamentary_groups.data = [ group.id for group in obj.parliamentary_groups ]
[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_groups', *(exclude or ()) }, include=include ) # convert multi-select ids into model instances data = self.parliamentary_groups.data or [] if data and data != [None]: obj.parliamentary_groups = ( self.request.session.query(RISParliamentaryGroup) .filter(RISParliamentaryGroup.id.in_(data)) .all() ) else: obj.parliamentary_groups = []