Source code for agency.forms.agency

from __future__ import annotations

from cgi import FieldStorage
from io import BytesIO

from markupsafe import Markup
from wtforms import EmailField, TextAreaField

from onegov.agency import _
from onegov.agency.collections import ExtendedAgencyCollection
from onegov.agency.models import ExtendedAgency
from onegov.agency.utils import handle_empty_p_tags
from onegov.core.security import Private
from onegov.core.utils import linkify, ensure_scheme
from onegov.form import Form
from onegov.form.fields import ChosenSelectField, HtmlField
from onegov.form.fields import MultiCheckboxField
from onegov.form.fields import UploadField
from onegov.form.validators import FileSizeLimit, MIME_TYPES_IMAGE
from onegov.gis import CoordinatesField
from sqlalchemy import func
from wtforms.fields import StringField
from wtforms.validators import InputRequired


from typing import cast
from typing import Any
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from webob.request import _FieldStorageWithFile


[docs] class ExtendedAgencyForm(Form): """ Form to edit agencies. """
[docs] title = StringField( label=_('Title'), validators=[ InputRequired() ], )
[docs] portrait = HtmlField( label=_('Portrait'), render_kw={'rows': 10} )
[docs] location_address = TextAreaField( label=_('Location address'), render_kw={'rows': 2}, )
[docs] location_code_city = StringField( label=_('Location Code and City') )
[docs] postal_address = TextAreaField( label=_('Postal address'), render_kw={'rows': 2}, )
[docs] postal_code_city = StringField(label=_('Postal Code and City'))
[docs] phone = StringField(label=_('Phone'))
[docs] phone_direct = StringField(label=_('Alternate Phone Number / Fax'))
[docs] email = EmailField(label=_('E-Mail'))
[docs] website = StringField(label=_('Website'), filters=(ensure_scheme, ))
[docs] opening_hours = TextAreaField( label=_('Opening hours'), render_kw={'rows': 5}, )
[docs] organigram = UploadField( label=_('Organigram'), validators=[ FileSizeLimit(1 * 1024 * 1024) ], allowed_mimetypes=MIME_TYPES_IMAGE, )
[docs] coordinates = CoordinatesField( label=_('Location'), description=_( 'Search for the exact address to set a marker. The zoom of ' 'the map will be saved as well.' ), fieldset=_('Map'), render_kw={'data-map-type': 'marker'}, )
[docs] export_fields = MultiCheckboxField( label=_('Fields to include for each membership'), choices=[ ('membership.title', _('Membership: Title')), ('membership.since', _('Membership: Since')), ('membership.addition', _('Membership: Addition')), ('person.title', _('Person: Title')), ('person.function', _('Person: Function')), ('person.last_name', _('Person: Last Name')), ('person.first_name', _('Person: First Name')), ('person.born', _('Person: Born')), ('person.academic_title', _('Person: Academic Title')), ('person.profession', _('Person: Profession')), ('person.location_address', _('Person: Location address')), ('person.location_code_city', _('Person: Location Code and City')), ('person.postal_address', _('Person: Postal address')), ('person.postal_code_city', _('Person: Postal Code and City')), ('person.political_party', _('Person: Political Party')), ('person.parliamentary_group', _('Person: Parliamentary Group')), ('person.phone', _('Person: Phone')), ('person.phone_direct', _('Person: Direct Phone')), ], default=['membership.title', 'person.title'], fieldset=_('PDF Export'), render_kw={'class_': 'sortable-multi-checkbox'} )
[docs] def on_request(self) -> None: self.request.include('sortable-multi-checkbox')
[docs] def get_useful_data(self) -> dict[str, Any]: # type:ignore[override] result = super().get_useful_data(exclude={'csrf_token', 'organigram'}) if self.organigram.data: result['organigram_file'] = self.organigram.file if self.portrait.data: result['portrait'] = linkify(self.portrait.data) return result
[docs] def update_model(self, model: ExtendedAgency) -> None: assert self.title.data is not None model.title = self.title.data model.portrait = handle_empty_p_tags(linkify(self.portrait.data)) model.location_address = self.location_address.data model.location_code_city = self.location_code_city.data model.postal_address = self.postal_address.data model.postal_code_city = self.postal_code_city.data model.phone = self.phone.data model.phone_direct = self.phone_direct.data model.email = self.email.data model.website = self.website.data model.opening_hours = self.opening_hours.data model.export_fields = self.export_fields.data or [] if self.organigram.action == 'delete': del model.organigram elif self.organigram.action == 'replace' and self.organigram.data: model.organigram_file = self.organigram.file # type:ignore model.coordinates = self.coordinates.data if hasattr(self, 'access'): model.access = self.access.data if hasattr(self, 'publication_start'): model.publication_start = self.publication_start.data if hasattr(self, 'publication_end'): model.publication_end = self.publication_end.data
[docs] def reorder_export_fields(self) -> None: titles: dict[str, str] = dict( self.export_fields.choices # type:ignore[arg-type] ) chosen = self.export_fields.data or [] self.export_fields.choices = [ (choice, titles[choice]) for choice in chosen ] + [ choice for choice in self.export_fields.choices if choice[0] not in chosen ]
[docs] def apply_model(self, model: ExtendedAgency) -> None: self.title.data = model.title self.portrait.data = model.portrait or Markup('') self.location_address.data = model.location_address self.location_code_city.data = model.location_code_city self.postal_address.data = model.postal_address self.postal_code_city.data = model.postal_code_city self.phone.data = model.phone self.phone_direct.data = model.phone_direct self.email.data = model.email self.website.data = model.website self.opening_hours.data = model.opening_hours self.export_fields.data = model.export_fields if model.organigram_file: fs = cast('_FieldStorageWithFile', FieldStorage()) fs.file = BytesIO(model.organigram_file.read()) fs.type = model.organigram_file.content_type fs.filename = model.organigram_file.filename self.organigram.data = self.organigram.process_fieldstorage(fs) self.coordinates.data = model.coordinates if hasattr(self, 'access'): self.access.data = model.access if hasattr(self, 'publication_start'): self.publication_start.process_data(model.publication_start) if hasattr(self, 'publication_end'): self.publication_end.process_data(model.publication_end) self.reorder_export_fields()
[docs] class MoveAgencyForm(Form): """ Form to move an agency. """
[docs] parent_id = ChosenSelectField( label=_('Destination'), choices=[], validators=[ InputRequired() ] )
[docs] def on_request(self) -> None: self.request.include('common') self.request.include('chosen') agencies = ExtendedAgencyCollection(self.request.session) self.parent_id.choices = [ (str(agency.id), agency.title) for agency in agencies.query().order_by(None).order_by( func.unaccent(ExtendedAgency.title) ) if self.request.has_permission(agency, Private) ] if self.request.has_permission(agencies, Private): self.parent_id.choices.insert( 0, ('root', self.request.translate(_('- Root -'))) )
[docs] def ensure_valid_parent(self) -> bool: """ As a new destination (parent page) every menu item is valid except yourself. You cannot assign yourself as the new destination :return: bool """ if self.parent_id.data and self.parent_id.data.isdigit(): new_parent_id = int(self.parent_id.data) # prevent selecting yourself as new parent if self.model.id == new_parent_id: assert isinstance(self.parent_id.errors, list) self.parent_id.errors.append( _('Invalid destination selected')) return False return True
[docs] def update_model(self, model: ExtendedAgency) -> None: session = self.request.session agencies = ExtendedAgencyCollection(session) new_parent_id = None new_parent = None if self.parent_id.data and self.parent_id.data.isdigit(): new_parent_id = int(self.parent_id.data) new_parent = agencies.by_id(new_parent_id) model.name = agencies.get_unique_child_name(model.title, new_parent) model.parent_id = new_parent_id
[docs] def apply_model(self, model: ExtendedAgency) -> None: def remove(item: ExtendedAgency) -> None: choice = (str(item.id), item.title) if choice in self.parent_id.choices: assert isinstance(self.parent_id.choices, list) self.parent_id.choices.remove(choice) def remove_with_children(item: ExtendedAgency) -> None: remove(item) for child in item.children: remove_with_children(child) if model.parent: remove(model.parent) else: assert isinstance(self.parent_id.choices, list) self.parent_id.choices.pop(0) remove_with_children(model)