Source code for org.forms.resource
from wtforms.fields import BooleanField
from wtforms.fields import DecimalField
from wtforms.fields import IntegerField
from wtforms.fields import RadioField
from wtforms.fields import StringField
from wtforms.fields import TextAreaField
from wtforms.validators import InputRequired
from wtforms.validators import NumberRange
from wtforms.validators import Optional
from wtforms.validators import ValidationError
from onegov.form import Form, merge_forms, parse_formcode
from onegov.form.fields import MultiCheckboxField
from onegov.form.filters import as_float
from onegov.form.validators import ValidFormDefinition
from onegov.org import _
from onegov.org.forms.fields import HtmlField
from onegov.org.forms.generic import DateRangeForm
from onegov.org.forms.generic import ExportForm
from onegov.org.forms.generic import PaymentForm
from onegov.org.forms.reservation import (
RESERVED_FIELDS, ExportToExcelWorksheets)
from onegov.org.forms.util import WEEKDAYS
from typing import Any, Literal, TYPE_CHECKING
if TYPE_CHECKING:
from onegov.reservation import Resource
[docs]
class ResourceBaseForm(Form):
""" Defines the form for all resources. """
[docs]
lead = TextAreaField(
label=_('Lead'),
description=_('Describes what this reservation resource is about'),
render_kw={'rows': 4})
[docs]
group = StringField(
label=_('Group'),
description=_('Used to group the resource in the overview')
)
[docs]
pick_up = TextAreaField(
label=_('Pick-Up'),
description=_('Describes how this resource can be picked up. '
'This text is used on the ticket status page to '
'inform the user')
)
[docs]
definition = TextAreaField(
label=_('Extra Fields Definition'),
validators=[
Optional(),
ValidFormDefinition(
require_email_field=False,
reserved_fields=RESERVED_FIELDS
)
],
render_kw={'rows': 32, 'data-editor': 'form'}
)
[docs]
deadline_unit = RadioField(
label=_('Closing date for the public'),
fieldset=_('Closing date'),
default='n',
validators=[InputRequired()],
choices=(
('n', _(
'No closing date')),
('d', _(
'Stop accepting reservations days before the allocation')),
('h', _(
'Stop accepting reservations hours before the allocation')),
)
)
[docs]
deadline_hours = IntegerField(
label=_('Hours'),
fieldset=_('Closing date'),
depends_on=('deadline_unit', 'h'),
default=1,
validators=[
InputRequired(),
NumberRange(min=1)
]
)
[docs]
deadline_days = IntegerField(
label=_('Days'),
fieldset=_('Closing date'),
depends_on=('deadline_unit', 'd'),
default=1,
validators=[
InputRequired(),
NumberRange(min=1)
]
)
[docs]
zipcode_block_use = BooleanField(
label=_('Limit reservations to certain zip-codes'),
fieldset=_('Zip-code limit'),
default=False,
)
[docs]
zipcode_field = TextAreaField(
label=_('Zip-code field'),
fieldset=_('Zip-code limit'),
depends_on=('zipcode_block_use', 'y'),
validators=[InputRequired()],
render_kw={
'class_': 'formcode-select',
'data-fields-include': 'text',
'data-type': 'radio',
})
[docs]
zipcode_list = TextAreaField(
label=_('Allowed zip-codes (one per line)'),
fieldset=_('Zip-code limit'),
depends_on=('zipcode_block_use', 'y'),
validators=[InputRequired()],
render_kw={
'rows': 4
}
)
[docs]
zipcode_days = IntegerField(
label=_('Days before an allocation may be reserved by anyone'),
fieldset=_('Zip-code limit'),
depends_on=('zipcode_block_use', 'y'),
validators=[
InputRequired(),
NumberRange(min=0)
],
)
# only used for rooms, not day-passes
[docs]
default_view = RadioField(
label=_('Default view'),
fieldset=_('View'),
default='agendaWeek',
validators=[InputRequired()],
choices=(
('agendaWeek', _('Week view')),
('month', _('Month view')),
))
[docs]
pricing_method = RadioField(
label=_('Price'),
fieldset=_('Payments'),
default='free',
validators=[InputRequired()],
choices=(
('free', _('Free of charge')),
('per_item', _('Per item')),
('per_hour', _('Per hour'))
)
)
[docs]
price_per_item = DecimalField(
label=_('Price per item'),
filters=(as_float,),
fieldset=_('Payments'),
validators=[InputRequired()],
depends_on=('pricing_method', 'per_item')
)
[docs]
price_per_hour = DecimalField(
label=_('Price per hour'),
filters=(as_float,),
fieldset=_('Payments'),
validators=[InputRequired()],
depends_on=('pricing_method', 'per_hour')
)
[docs]
currency = StringField(
label=_('Currency'),
default='CHF',
fieldset=_('Payments'),
depends_on=('pricing_method', '!free'),
validators=[InputRequired()],
)
[docs]
def on_request(self) -> None:
if hasattr(self.model, 'type'):
if self.model.type == 'daypass':
self.delete_field('default_view')
else:
if self.request.view_name.endswith('new-daypass'):
self.delete_field('default_view')
@property
[docs]
def zipcodes(self) -> list[int]:
assert self.zipcode_list.data is not None
return [int(z) for z in self.zipcode_list.data.split()]
[docs]
def validate_zipcode_field(self, field: TextAreaField) -> None:
if not self.zipcode_block_use.data:
return
if not self.zipcode_field.data:
raise ValidationError(
_('Please select the form field that holds the zip-code'))
for fieldset in parse_formcode(self.definition.data):
for parsed_field in fieldset.fields:
if parsed_field.human_id == self.zipcode_field.data:
return
raise ValidationError(
_('Please select the form field that holds the zip-code'))
[docs]
def validate_zipcode_list(self, field: TextAreaField) -> None:
if not self.zipcode_block_use.data:
return
if not self.zipcode_list.data:
raise ValidationError(
_('Please enter at least one zip-code'))
try:
self.zipcodes # noqa: B018
except ValueError as exception:
raise ValidationError(
_(
'Please enter one zip-code per line, '
'without spaces or commas'
)
) from exception
[docs]
def ensure_valid_price(self) -> bool | None:
if self.pricing_method.data == 'per_item':
if not float(self.price_per_item.data or 0) > 0:
assert isinstance(self.price_per_item.errors, list)
self.price_per_item.errors.append(_(
'The price must be larger than zero'
))
return False
if self.pricing_method.data == 'per_hour':
if not float(self.price_per_hour.data or 0) > 0:
assert isinstance(self.price_per_hour.errors, list)
self.price_per_hour.errors.append(_(
'The price must be larger than zero'
))
return False
return None
@property
[docs]
def deadline(self) -> tuple[int, Literal['d', 'h']] | None:
if self.deadline_unit.data == 'h':
assert self.deadline_hours.data is not None
return (self.deadline_hours.data, 'h')
if self.deadline_unit.data == 'd':
assert self.deadline_days.data is not None
return (self.deadline_days.data, 'd')
return None
@deadline.setter
def deadline(self, value: tuple[int, Literal['d', 'h']] | None) -> None:
self.deadline_unit.data = 'n'
if not value:
return
amount, unit = value
self.deadline_unit.data = unit
if unit == 'h':
self.deadline_hours.data = amount
elif unit == 'd':
self.deadline_days.data = amount
else:
raise NotImplementedError()
# FIXME: Use TypedDict?
@property
[docs]
def zipcode_block(self) -> dict[str, Any] | None:
if not self.zipcode_block_use.data:
return None
assert self.zipcode_days.data is not None
return {
'zipcode_field': self.zipcode_field.data,
'zipcode_list': self.zipcodes,
'zipcode_days': int(self.zipcode_days.data),
}
@zipcode_block.setter
def zipcode_block(self, value: dict[str, Any] | None) -> None:
if not value:
self.zipcode_block_use.data = False
return
self.zipcode_block_use.data = True
self.zipcode_field.data = value['zipcode_field']
self.zipcode_days.data = value['zipcode_days']
self.zipcode_list.data = '\n'.join(
str(i) for i in sorted(value['zipcode_list']))
[docs]
def populate_obj(self, obj: 'Resource') -> None: # type:ignore
super().populate_obj(obj, exclude=('deadline', 'zipcode_block'))
obj.deadline = self.deadline
obj.zipcode_block = self.zipcode_block
[docs]
def process_obj(self, obj: 'Resource') -> None: # type:ignore
super().process_obj(obj)
self.deadline = obj.deadline
self.zipcode_block = obj.zipcode_block
if TYPE_CHECKING:
else:
class ResourceForm(
merge_forms(ResourceBaseForm, PaymentForm)
):
pass
[docs]
class ResourceCleanupForm(DateRangeForm):
""" Defines the form to remove multiple allocations. """
[docs]
weekdays = MultiCheckboxField(
label=_('Weekdays'),
choices=WEEKDAYS,
coerce=int,
validators=[InputRequired()],
render_kw={
'prefix_label': False,
'class_': 'oneline-checkboxes'
})
if TYPE_CHECKING:
class AllResourcesExportForm(DateRangeForm, ExportToExcelWorksheets):
pass
else:
class ResourceExportForm(
merge_forms(DateRangeForm, ExportForm)
):
""" Resource export form with start/end date. """
class AllResourcesExportForm(
merge_forms(DateRangeForm, ExportToExcelWorksheets)
):
""" Resource export all resources, with start/end date. """