class OrganizationInfoSchema(MappingSchema): """Data structure for organizational information.""" name = SingleLine(missing=drop) validator = OneOf([ 'registered_nonprofit', 'planned_nonprofit', 'support_needed', 'other', ]) city = SingleLine(missing=drop) country = ISOCountryCode(missing=drop) help_request = Text(validator=Length(max=300)) registration_date = DateTime(missing=drop, default=None) website = URL(missing=drop) status = OrganizationStatusEnum(missing=required) status_other = Text(validator=Length(max=300)) def validator(self, node, value): """Extra validation depending on the status of the organisation. Make `status_other` required if `status` == `other` and `help_request` required if `status` == `support_needed`. """ status = value.get('status', None) if status == 'support_needed': if not value.get('help_request', None): help_request = node['help_request'] raise Invalid(help_request, msg='Required iff status == support_needed') elif status == 'other': if not value.get('status_other', None): status_other = node['status_other'] raise Invalid(status_other, msg='Required iff status == other')
class CaptchaSchema(MappingSchema): """Wraps user-submitted captcha data. This sheet may be required when creating a new user (if captchas are turned on), but the data is discarded after validation. `id`: captcha ID (generated by Thentos) `solution`: solution to the captcha (entered by a human user) """ id = SingleLine(missing=required) solution = SingleLine(missing=required) def validator(self, node, value): """ Validate the captcha. If 'adhocracy.captcha_enabled' is true, we ask the thentos-captcha service whether the given solution is correct. If captchas are not enabled, this validator will always pass. """ request = node.bindings['request'] settings = request.registry['config'] if not self._captcha_is_correct(settings, value): err = Invalid(node) err['solution'] = 'Captcha solution is wrong' raise err def _captcha_is_correct(self, settings, value) -> bool: """Ask the captcha service whether the captcha was solved correctly.""" captcha_service = settings.adhocracy.captcha_backend_url resp = requests.post(urljoin(captcha_service, 'solve_captcha'), json=value) return resp.json()['data']
class TransitionMeta(MappingSchema): """Workflow transition to state.""" callback = WorkflowCallback() from_state = SingleLine(missing=required) to_state = SingleLine(missing=required) permission = SingleLine(missing='do_transition', default='do_transition')
class ProcessSettingsSchema(colander.MappingSchema): """Settings for the B-Plan process.""" office_worker = Reference(reftype=OfficeWorkerUserReference) plan_number = SingleLine(missing=colander.required) participation_kind = SingleLine(missing=colander.required) participation_start_date = DateTime(default=None) participation_end_date = DateTime(default=None)
class LocationSchema(colander.MappingSchema): """Data structure for for proposal location.""" location_is_specific = Boolean() location_specific_1 = SingleLine() location_specific_2 = SingleLine() location_specific_3 = SingleLine() location_is_online = Boolean() location_is_linked_to_ruhr = Boolean()
class ProposalSchema(MappingSchema): """Data structure for plan stellungsname information.""" name = SingleLine(missing=required) street_number = SingleLine(missing=required) postal_code_city = SingleLine(missing=required) email = Email() statement = Text(missing=required, validator=Length(max=17500))
class AssetMetadataSchema(MappingSchema): """Data structure storing asset metadata.""" mime_type = SingleLine(readonly=True) size = Integer(readonly=True) filename = SingleLine(readonly=True) attached_to = UniqueReferences(readonly=True, backref=True, reftype=AssetReference)
class WorkflowMeta(MappingSchema): """Data structure to define a workflow (finite state machine).""" initial_state = SingleLine(missing=drop) defaults = SingleLine(missing=drop) add_local_role_participant_to_default_group = Boolean(missing=drop, default=False) auto_transition = Boolean(missing=drop) states = SchemaNode(MappingType(unknown='preserve'), missing=drop) transitions = SchemaNode(MappingType(unknown='preserve'), missing=drop)
class ActivitySchema(MappingSchema): """Activity entry.""" subject = Reference(reftype=SubjectReference) type = SingleLine(validator=OneOf( [activity_type.value for activity_type in ActivityType])) object = Reference(reftype=ObjectReference) target = Reference(reftype=TargetReference) name = SingleLine() published = DateTime()
class PartnersSchema(MappingSchema): has_partners = Boolean() partner1_name = SingleLine() partner1_website = URL() partner1_country = ISOCountryCode() partner2_name = SingleLine() partner2_website = URL() partner2_country = ISOCountryCode() partner3_name = SingleLine() partner3_website = URL() partner3_country = ISOCountryCode() other_partners = Text()
class TitleSchema(colander.MappingSchema): """Title sheet data structure. `title`: a human readable title """ title = SingleLine(validator=colander.Length(min=3, max=100))
class OrganizationInfoSchema(colander.MappingSchema): """Data structure for organizational information.""" name = SingleLine() country = ISOCountryCode() status = StatusEnum() status_other = Text(validator=colander.Length(max=500)) """Custom description for status == other.""" website = URL() planned_date = DateTime(missing=colander.drop, default=None) help_request = Text(validator=colander.Length(max=500)) def validator(self, node, value): """Make `status_other` required if `status` == `other`.""" status = value.get('status', None) if status == 'other': if not value.get('status_other', None): status_other = node['status_other'] raise colander.Invalid(status_other, msg='Required iff status == other') else: # TODO: Allow multiple errors at the same time name = node['name'] if not value.get('name', None): raise colander.Invalid(name, msg='Required iff status != other') country = node['country'] if not value.get('country', None): raise colander.Invalid(country, msg='Required iff status != other')
class ProposalSchema(colander.MappingSchema): """Data structure for the Burgerhaushalt information.""" budget = CurrencyAmount(missing=colander.drop, default=None, validator=colander.Range(min=0)) location_text = SingleLine(validator=colander.Length(max=100))
class POSTMessageUserViewRequestSchema(colander.Schema): """Schema for messages to a user.""" recipient = Reference(missing=colander.required, reftype=MessageUserReference) title = SingleLine(missing=colander.required) text = Text(missing=colander.required)
class BlockExplanationResponseSchema(colander.Schema): """Data structure explaining a 410 Gone response.""" reason = SingleLine() modified_by = Reference() modification_date = DateTime(default=colander.null)
class ImageMetadataSchema(AssetMetadataSchema): """Data structure storing image asset metadata.""" mime_type = SingleLine(missing=required, validator=image_mime_type_validator) detail = Resource(dimensions=Dimensions(width=800, height=800)) thumbnail = Resource(dimensions=Dimensions(width=100, height=100))
class StateMeta(MappingSchema): """Workflow state.""" title = SingleLine(missing='') description = Text(missing='') acm = ACM() display_only_to_roles = Roles(missing=[]) """Hint for the fronted, this is not security related."""
class WorkflowMeta(MappingSchema): """Data structure to define a workflow (finite state machine).""" initial_state = SingleLine(missing=required) states = SchemaNode(Mapping(unknown='preserve'), missing=required) transitions = SchemaNode(Mapping(unknown='preserve'), missing=required)
class FinanceSchema(MappingSchema): """Data structure for financial aspects.""" budget = CurrencyAmount(missing=required) requested_funding = CurrencyAmount(missing=required, validator=Range(min=0, max=50000)) other_sources = SingleLine() granted = Boolean()
class ProposalSchema(MappingSchema): """Data structure for organizational information.""" # TODO: check exact length restrictions budget = CurrencyAmount(missing=required, validator=Range(min=0, max=50000)) creator_participate = Boolean() location_text = SingleLine(validator=Length(max=100))
class ImageReferenceSchema(MappingSchema): """Data structure for the image reference sheet.""" picture = Reference(reftype=ImageReference, choices_getter=get_asset_choices) picture_description = SingleLine() external_picture_url = URL(validator=All( URL.validator, picture_url_validator, ))
def _is_reference_filter(name: str, registry: Registry) -> bool: """ Check whether a name refers to a reference node in a sheet. Raises an error if `name` contains a colon but is not a reference node. """ if ':' not in name: return False resolve = registry.content.resolve_isheet_field_from_dotted_string try: isheet, field, node = resolve(name) except ValueError: dummy_node = SingleLine(name=name) raise Invalid(dummy_node, 'No such sheet or field') if isinstance(node, (Reference, References)): return True else: dummy_node = SingleLine(name=name) raise Invalid(dummy_node, 'Not a reference node')
class UserBasicSchema(colander.MappingSchema): """Basic user sheet data structure. This sheet must only store public information, as everyone can see it. `name`: visible name """ name = SingleLine(missing=colander.required, validator=deferred_validate_user_name)
class PointSchema(colander.MappingSchema): """A geographical Point object. GeoJSON like geometry object fields: `type`: 'Point' (geometry object type) `coordinates`: tuple of points with (longitude, latitude). """ type = SingleLine(default='Point', readonly=True) coordinates = Point()
class POSTLoginServiceKontoSchema(MappingSchema): """Schema for login requests via service konto token.""" token = SingleLine(missing=required) @deferred def validator(node: SchemaNode, kw: dict) -> All: request = kw['request'] context = kw['context'] registry = kw['registry'] return All( create_validate_service_konto_auth(context, request, registry), )
class POSTLoginUsernameRequestSchema(MappingSchema): """Schema for login requests via username and password.""" name = SingleLine(missing=required) password = Password(missing=required) @deferred def validator(node: SchemaNode, kw: dict) -> All: request = kw['request'] context = kw['context'] registry = kw['registry'] return All( create_validate_login(context, request, registry, 'name'), create_validate_login_password(request, registry), create_validate_account_active(request, 'name'), )
class LocationSchema(MappingSchema): location = SingleLine() is_online = Boolean() has_link_to_ruhr = Boolean(missing=required, default=False) link_to_ruhr = Text() def validator(self, node, value): """Extra validation depending on the status of the location. Make `link_to_ruhr` required if `has_link_to_ruhr` == `True`. """ has_link_to_ruhr = value.get('has_link_to_ruhr', None) if has_link_to_ruhr: if not value.get('link_to_ruhr', None): link_to_ruhr = node['link_to_ruhr'] raise Invalid(link_to_ruhr, msg='Required iff has_link_to_ruhr == True')
class MultiPolygonSchema(colander.MappingSchema): """A geographical MultiPolygon object. GeoJSON like geometry object fields: `type`: 'MultiPolygon' (geometry object type) `coordinates`: list of list of list of points with (longitude, latitude). Metadata property fields: `administrative_level`: administrative division level `administrative_division`: administrative division name `part_of`: surrounding geographical object """ type = SingleLine(default='MultiPolygon', readonly=True) coordinates = MultiPolygon() administrative_division = AdministrativeDivisionName() part_of = Reference(reftype=PartOfReference)
class WorkflowAssignmentSchema(MappingSchema): """Workflow assignment sheet data structure.""" workflow = Workflow(missing=drop) """Workflow assigned to the sheet context resource. Available workflows are defined in :mod:`adhocracy_core.workflows`. """ workflow_state = SingleLine( missing=drop, default=deferred_state_default, validator=deferred_state_validator, widget=deferred_state_widget, ) """Workflow state of the sheet context resource. Setting this executes a transition to the new state value. """ state_data = StateDataList(missing=drop) """Optional List of :class:`StateData`.
class TitleSchema(colander.MappingSchema): """Data structure for the proposal title.""" title = SingleLine(validator=colander.Length(min=1, max=100))