class TopLevelMeta(schemas.ExtSchema): language = fields.String() params = fields.Raw() path = fields.String() count = fields.Integer() relative_uri = fields.String() aggregations = fields.Raw() server_time = fields.DateTime(default=now)
class ErrorsSchema(schemas.ExtSchema): jsonapi = fields.Raw(default={'version': '1.0'}) errors = fields.Nested(ErrorSchema, many=True) class Meta: strict = True ordered = True
class UpdateSubscriptionAttrs(core_schemas.ExtSchema): name = core_fields.String(description='Subscription name', example='my query 1', required=False) enable_notifications = core_fields.Boolean(description='Enable notifications', example=True, required=False) customfields = core_fields.Raw() class Meta: strict = True ordered = True
class TopLevelMeta(schemas.ExtSchema): language = fields.String() params = fields.Raw() path = fields.String() count = fields.Integer() relative_uri = fields.String() aggregations = fields.Raw() subscription_url = fields.String() server_time = fields.DateTime(default=now) notifications = fields.Raw() @pre_dump def do_something(self, data, **kwargs): request = self.context['request'] user = getattr(request, 'user', None) if user and user.is_authenticated: data['notifications'] = user.get_unread_notifications() return data
class UpdateSubscriptionAttrs(ObjectAttrs): name = core_fields.String(description='Subscription name', example='my query 1', required=False) customfields = core_fields.Raw() class Meta: strict = True ordered = True object_type = 'subscription'
class ChartApiAttrs(ObjectAttrs): chart = fields.Raw() is_default = fields.Boolean() name = fields.Str() def get_chart(self, obj): return json.dumps(obj.chart) class Meta: relationships_schema = ChartApiRelationships object_type = 'chart' api_path = 'chart' model = 'resources.Chart' url_template = '{api_url}/resources/{data.resource.ident}/charts/{ident}'
class ChartAttrs(ObjectAttrs): resource_id = fields.Int(dump_only=True) chart = fields.Raw(required=True) is_default = fields.Bool() name = fields.Str(required=True, validate=validate.Length(min=1, max=200)) class Meta: object_type = 'chart' strict = True ordered = True @pre_load def prepare_data(self, data, **kwargs): data.setdefault('is_default', False) return data @validates_schema def validate_schema(self, data, **kwargs): chart = self.context.get('chart') resource = self.context['resource'] user = self.context['user'] if resource.is_chart_creation_blocked and not any( [user.is_staff, user.is_superuser]): raise ValidationError( _('Chart creation for this resource is blocked!')) if data['is_default'] and not any([ user.is_superuser, user.is_editor_of_organization(resource.institution) ]): raise ValidationError(_('No permission to define chart')) if chart and chart.is_default != data['is_default']: raise ValidationError(_('You cannot change type of chart!')) private_charts = resource.charts.filter(is_default=False, created_by=user) if chart: private_charts = private_charts.exclude(id=chart.id) if not data['is_default'] and private_charts.exists(): raise ValidationError(_('You cannot add another private chart!')) charts_with_same_name = resource.charts.filter(is_default=True, name=data['name']) if chart: charts_with_same_name = charts_with_same_name.exclude(id=chart.id) if charts_with_same_name.exists() and data['is_default']: raise ValidationError( _('You cannot put changes into chart defined by Data Provider. Please provide new chart name.' ))
class CreateSubscriptionAttrs(core_schemas.ExtSchema): object_name = core_fields.String(description='Object name', example='resource', required=True, validate=validate.OneOf(choices=ALLOWED_OBJECT_NAMES, error=_('Unsupported object name'))) object_ident = core_fields.String(description='Object ID or query url.', example='12342', required=True) name = core_fields.String(description='Subscription name', example='my query 1', required=False) enable_notifications = core_fields.Boolean(description='Enable notifications', example=True, required=False) customfields = core_fields.Raw() class Meta: strict = True ordered = True @pre_load(pass_many=False) def ident_to_string(self, data): data['object_ident'] = str(data['object_ident']) return data
class ShowcaseProposalCSVSerializer(CSVSerializer): id = fields.Int(data_key='id', required=True, example=77) category_name = fields.Str(data_key=_('Category'), example='Aplikacja') title = fields.Str(data_key=_('Name'), example='Propozycja aplikacji') notes = fields.Str(data_key=_('Notes'), default='', example='opis...') url = fields.Str(data_key=_('App URL'), default='', example='http://example.com') author = fields.Str(data_key=_('Author'), default='', example='Jan Kowalski') applicant_email = fields.Email(data_key=_('applicant email'), default='', required=False, example='*****@*****.**') keywords = fields.Str(data_key=_('keywords'), attribute='keywords_as_str', default='', example='tag1,tag2,tag3') report_date = fields.Date(data_key=_('report date')) decision_date = fields.Date(data_key=_('decision date'), default=None) comment = fields.Str(data_key=_('comment'), example='komentarz...', default='') datasets = fields.Method('get_datasets', data_key=_('datasets'), example='998,999', default='') external_datasets = fields.Raw(data_key=_('external datasets'), example='[]') showcase = fields.Int(data_key=_('Showcase'), attribute='showcase.id', default=None) class Meta: ordered = True model = 'showcases.ShowcaseProposal' def get_datasets(self, obj): return ','.join(str(x.id) for x in obj.datasets.order_by('id'))
class CreateSubscriptionAttrs(ObjectAttrs): object_name = core_fields.String(description='Object name', example='resource', required=True, validate=validate.OneOf( choices=ALLOWED_OBJECT_NAMES, error=_('Unsupported object name'))) object_ident = core_fields.String(description='Object ID or query url.', example='12342', required=True) name = core_fields.String(description='Subscription name', example='my query 1', required=False) customfields = core_fields.Raw() @pre_load def prepare_data(self, data, **kwargs): object_ident = data.get('object_ident') data['object_ident'] = str(object_ident) if object_ident else None object_name = data.get('object_name') if object_name and isinstance(object_name, str): data['object_name'] = object_name.lower() return data @validates_schema def validate_url(self, data, **kwargs): if data['object_name'] == 'query': url_split = urlsplit(data['object_ident']) api_split = urlsplit(settings.API_URL) if url_split.scheme != api_split.scheme or url_split.netloc != api_split.netloc: raise ValidationError(_('Invalid url address'), field_name='object_ident') class Meta: object_type = 'subscription' strict = True ordered = True
class SubscriptionIncludes(jss.BaseData): attributes = fields.Raw()
class NotificationIncludes(jss.BaseData): attributes = fields.Raw()
class DummyAttributes(schemas.ExtSchema): dummy = fields.Raw()
class Aggregation(schemas.ExtSchema): key = fields.Raw() title = fields.String() doc_count = fields.Integer()
class GeoApiMeta(TopLevelMeta): data_schema = fields.Raw() headers_map = fields.Raw() aggregations = fields.Nested(GeoAggregations)
class TopLevel(schemas.ExtSchema): included = fields.Raw() jsonapi = fields.Raw(default={'version': '1.0'}) errors = fields.Raw() OPTIONS_CLASS = TopLevelOpts def __init__( self, only=None, exclude=(), many=False, context=None, load_only=(), dump_only=(), partial=False, unknown=None, ): data_cls = self.opts.data_schema or type( '{}Data'.format(self.__class__.__name__), (Object,), {} ) if self.opts.attrs_schema: setattr(data_cls.opts, 'attrs_schema', self.opts.attrs_schema) self._declared_fields['data'] = fields.Nested(data_cls, name='data', many=many, allow_none=True) if self.opts.meta_schema: if self.opts.aggs_schema: self.opts.meta_schema._declared_fields['aggregations'] = fields.Nested(self.opts.aggs_schema, many=False) self._declared_fields['meta'] = fields.Nested( self.opts.meta_schema, name='meta', many=False ) if self.opts.links_schema: self._declared_fields['links'] = fields.Nested( self.opts.links_schema, name='links', many=False ) context = context or {} context['is_listing'] = many super().__init__(only=only, exclude=exclude, many=False, context=context, load_only=load_only, dump_only=dump_only, partial=partial, unknown=unknown) @pre_dump def prepare_top_level(self, c, **kwargs): def _get_page_link(page_number): cleaned_data['page'] = page_number return '{}{}?{}'.format(settings.API_URL, request.path, builder.build(cleaned_data)) c.data = c.data if hasattr(c, 'data') else None if not c.data and self.context['is_listing']: if isinstance(c.data, es_response.Response): c.data.hits = [] else: c.data = [] request = self.context['request'] c.meta = getattr(c, 'meta', {}) c.links = getattr(c, 'links', {}) cleaned_data = dict(getattr(request.context, 'cleaned_data', {})) c.meta.update({ 'language': request.language, 'params': request.params, 'path': request.path, 'relative_uri': request.relative_uri, }) c.links['self'] = request.uri.replace(request.forwarded_prefix, settings.API_URL) if self.context['is_listing']: data = getattr(c, 'data', {}) c.meta['aggregations'] = self.get_aggregations(data) items_count = self._get_items_count(data) c.meta['count'] = items_count page, per_page = cleaned_data.get('page', 1), cleaned_data.get('per_page', 20) c.links['self'] = _get_page_link(page) if page > 1: c.links['first'] = _get_page_link(1) c.links['prev'] = _get_page_link(page - 1) if items_count: max_count = min(items_count, self.opts.max_items_num) off = 1 if max_count % per_page else 0 last_page = max_count // per_page + off if last_page > 1: c.links['last'] = _get_page_link(last_page) if page * per_page < max_count: c.links['next'] = _get_page_link(page + 1) return c @staticmethod def _get_items_count(data): if isinstance(data, paginator.Page): return data.paginator.count elif isinstance(data, list): return len(data) return data.hits.total if hasattr(data, 'hits') else 0 def get_aggregations(self, data): return getattr(data, 'aggregations', {}) or {}
class TableApiMeta(TopLevelMeta): data_schema = fields.Raw() headers_map = fields.Raw()
class ResponseSchema(schemas.ExtSchema): jsonapi = fields.Raw(missing=json_api_object, default=json_api_object) errors = fields.Raw(required=False) def __init__( self, only=None, exclude=(), many=False, context=None, load_only=(), dump_only=(), partial=False, unknown=None, ): super().__init__(only=only, exclude=exclude, many=many, context=context, load_only=load_only, dump_only=dump_only, partial=partial, unknown=unknown) # Top level links links_cls = ResponseLinksMany if self.many else ResponseLinksSingle self.fields['links'] = Nested(links_cls, name='links', required=False, many=False, context={'many': self.many}) # Top level meta _meta = getattr(self, 'Meta', None) meta_cls = getattr(_meta, 'meta_cls', ResponseMeta) if not issubclass(meta_cls, ResponseMeta): raise Exception("{} must be a subclass of TopLevelMeta".format(meta_cls.__class__)) self.fields['meta'] = fields.Nested(meta_cls, name='meta', many=False, context={'many': self.many}, required=False) # Top level data data_cls = getattr(_meta, 'data_cls', ResponseData) if not issubclass(data_cls, ResponseData): raise Exception("{} must be a subclass of ResponseData".format(data_cls.__class__)) self.fields['data'] = fields.Nested(data_cls, name='data', many=self.many, data_key='data') def dump(self, obj, many=None): # noqa:C901 marshaller = marshalling.Marshaller() errors = {} many = self.many if many is None else bool(many) processed_obj = obj result = None if self._has_processors(PRE_DUMP): try: processed_obj = self._invoke_dump_processors( PRE_DUMP, obj, many, original_data=obj, ) except ValidationError as error: errors = error.normalized_messages() if not errors: try: result = marshaller.serialize( processed_obj, self._fields, many=False, accessor=self.get_attribute, dict_class=self.dict_class, index_errors=self.opts.index_errors, ) except ValidationError as error: errors = marshaller.errors result = error.data if not errors and self._has_processors(POST_DUMP): try: result = self._invoke_dump_processors( POST_DUMP, result, many, original_data=obj, ) except ValidationError as error: errors = error.normalized_messages() if errors: error_field_names = getattr(marshaller, 'error_field_names', []) exc = ValidationError( errors, field_names=error_field_names, data=obj, valid_data=result, **marshaller.error_kwargs ) # User-defined error handler self.handle_error(exc, obj) raise exc return result def _do_load( # noqa:C901 self, data, many=None, partial=None, unknown=None, postprocess=True, ): errors = {} result = None processed_data = None many = self.many if many is None else bool(many) unknown = unknown or self.unknown if partial is None: partial = self.partial if self._has_processors(PRE_LOAD): try: processed_data = self._invoke_load_processors( PRE_LOAD, data, many, original_data=data, ) except ValidationError as err: errors = err.normalized_messages() else: processed_data = data unmarshaller = marshalling.Unmarshaller() if not errors: result = unmarshaller.deserialize( processed_data, self._fields, many=False, partial=partial, unknown=unknown, dict_class=self.dict_class, index_errors=self.opts.index_errors, ) self._invoke_field_validators(unmarshaller, data=result, many=False) if self._has_processors(VALIDATES_SCHEMA): field_errors = bool(unmarshaller.errors) self._invoke_schema_validators( unmarshaller, pass_many=True, data=result, original_data=data, many=many, field_errors=field_errors, ) self._invoke_schema_validators( unmarshaller, pass_many=False, data=result, original_data=data, many=many, field_errors=field_errors, ) errors = unmarshaller.errors if not errors and postprocess and self._has_processors(POST_LOAD): try: result = self._invoke_load_processors( POST_LOAD, result, many, original_data=data, ) except ValidationError as err: errors = err.normalized_messages() if errors: error_field_names = getattr(unmarshaller, 'error_field_names', []) exc = ValidationError( errors, field_names=error_field_names, data=data, valid_data=result, **unmarshaller.error_kwargs ) self.handle_error(exc, data) raise exc return result @property def _many(self): return False
class Geometry(ma.Schema): type = fields.String(required=True) coordinates = fields.Raw(required=True)