class PrivateCategorySerializer(HALSerializer): serializer_url_field = PrivateCategoryHyperlinkedIdentityField _display = DisplayField() handling_message = serializers.SerializerMethodField() sla = serializers.SerializerMethodField() new_sla = PrivateCategorySLASerializer(write_only=True) class Meta: model = Category fields = ( '_links', '_display', 'id', 'name', 'slug', 'is_active', 'description', 'handling_message', 'sla', 'new_sla', ) read_only_fields = ( 'id', 'slug', 'handling_message', 'sla', ) def get_handling_message(self, obj): return ALL_AFHANDELING_TEXT[obj.handling] def get_sla(self, obj): return PrivateCategorySLASerializer( obj.slo.all().order_by('-created_at').first()).data def update(self, instance, validated_data): new_sla = validated_data.pop( 'new_sla') if 'new_sla' in validated_data else None if new_sla: ServiceLevelObjective.objects.create(category=instance, **new_sla) instance.refresh_from_db() return super(PrivateCategorySerializer, self).update(instance, validated_data)
class ParentCategoryHALSerializer(HALSerializer): serializer_url_field = ParentCategoryHyperlinkedIdentityField _display = DisplayField() sub_categories = CategoryHALSerializer(many=True, source='children') class Meta: model = Category fields = ( '_links', '_display', 'name', 'slug', 'sub_categories', 'category_level_name1', 'category_level_name2', 'category_level_name3', 'category_level_name4', 'filter_label', )
class PublicQuestionSerializer(HALSerializer): serializer_url_field = QuestionHyperlinkedIdentityField next_rules = serializers.SerializerMethodField() _display = DisplayField() key = serializers.CharField(source='retrieval_key') class Meta: model = Question fields = ( '_links', '_display', 'key', 'retrieval_key', 'analysis_key', 'uuid', 'label', 'short_label', 'field_type', 'next_rules', 'required', ) read_only_fields = fields # No create or update allowed def get_next_rules(self, obj): # For backwards compatibility with earlier REST API version, this is # candidate for removal. This also only makes sense for questions seen # as part of a QuestionGraph, as the next_rules are no longer on the # Question object --- graph structure is now explicitly modelled in the # QuestionGraph and Edge objects. next_rules = None if graph := self.context.get('graph', None): outgoing_edges = Edge.objects.filter(graph=graph, question=obj) next_rules = [] for edge in outgoing_edges: payload = edge.choice.payload if edge.choice else None next_rules.append({ 'key': edge.next_question.ref, 'payload': payload }) return next_rules
class SignalAttachmentSerializer(HALSerializer): _display = DisplayField() location = serializers.FileField(source='file', required=False) class Meta: model = Attachment fields = ( '_display', '_links', 'location', 'is_image', 'created_at', 'file', 'is_issue_finish_image', ) read_only = ( '_display', '_links', 'location', 'is_image', 'created_at', ) extra_kwargs = {'file': {'write_only': True}, 'is_issue_finish_image': {'required': False}} def create(self, validated_data): is_issue_finish_image = validated_data['is_issue_finish_image'] if 'is_issue_finish_image' in validated_data else False attachment = Signal.actions.add_attachment(validated_data['file'], self.context['view'].get_object(), issue_finish=is_issue_finish_image) if self.context['request'].user: attachment.created_by = self.context['request'].user.email attachment.save() return attachment def validate_file(self, file): if file.size > SIGNALS_API_MAX_UPLOAD_SIZE: msg = f'Bestand mag maximaal {SIGNALS_API_MAX_UPLOAD_SIZE} bytes groot zijn.' raise ValidationError(msg) return file
class PrivateDepartmentSerializerList(HALSerializer): _display = DisplayField() category_names = serializers.SerializerMethodField() class Meta: model = Department fields = ( '_links', '_display', 'id', 'name', 'code', 'is_intern', 'category_names', ) def get_category_names(self, obj): return list( obj.category_set.filter(is_active=True).values_list('name', flat=True))
class PublicSessionSerializer(HALSerializer): serializer_url_field = SessionPublicHyperlinkedIdentityField _display = DisplayField() class Meta: model = Session fields = ( '_links', '_display', 'uuid', 'started_at', 'submit_before', 'duration', 'created_at', ) read_only_fields = ( 'id', 'uuid', 'created_at', )
class CategoryHALSerializer(HALSerializer): serializer_url_field = CategoryHyperlinkedIdentityField _display = DisplayField() departments = _NestedDepartmentSerializer(many=True) handling_message = serializers.SerializerMethodField() class Meta: model = Category fields = ( '_links', '_display', 'name', 'slug', 'handling', 'departments', 'is_active', 'handling_message', ) def get_handling_message(self, obj): return ALL_AFHANDELING_TEXT[obj.handling]
class ContainerSerializer(FlexFieldsModelSerializer, HALSerializer): _display = DisplayField() container_type = ContainerTypeSerializer() address = serializers.SerializerMethodField() # well = WellModelSerializer() # geometrie = serializers.SerializerMethodField() class Meta(object): model = Container fields = [ "_links", "_display", "id", "id_number", "owner", "active", "waste_type", "waste_name", "container_type", "warranty_date", "operational_date", "placing_date", "well", "address", ] expandable_fields = { 'well': (WellModelSerializer, { 'source': 'well', 'fields': ['id', 'id_number', 'geometrie', 'site', 'buurt_code'] }), } def get_address(self, obj): if obj.well: return obj.well.address.get('summary')
class SignalCityObjectSerializerDetail(HALSerializer): _display = DisplayField() signal_id = serializers.CharField() city_obj_id = serializers.CharField() signal = serializers.SerializerMethodField() city_object = serializers.SerializerMethodField() class Meta: model = SignalCityObject fields = ('_display', 'id', 'signal_id', 'signal', 'city_object', 'city_obj_id', 'complainID', 'complainIDall', 'reportCount', 'is_Orac') def get_city_object(self, obj): if obj.city_obj_id: return CityObjectSerializer( CityObject.objects.get(id=obj.city_obj_id)).data return None def get_signal(self, obj): if obj.signal_id: return PublicSignalSerializerDetail( Signal.objects.get(id=obj.signal_id)).data return None def create(self, validated_data): signal_id = validated_data.pop('signal_id') city_obj_id = validated_data.pop('city_obj_id') signal = Signal.objects.get(id=signal_id) city_obj = CityObject.objects.get(id=city_obj_id) instance = super(SignalCityObjectSerializerDetail, self).create(validated_data) instance.signal = signal instance.city_obj = city_obj instance.save() return instance
class SignalCityObjectSerializerList(HALSerializer): _display = DisplayField() city_obj = serializers.SerializerMethodField() signal = serializers.SerializerMethodField() class Meta: model = SignalCityObject fields = ('_display', 'id', 'complainID', 'complainIDall', 'reportCount', 'is_Orac', 'city_obj', 'signal') def get_city_obj(self, obj): if obj.city_obj_id: return CityObjectSerializer( CityObject.objects.get(id=obj.city_obj_id)).data return None def get_signal(self, obj): if obj.signal: return PublicSignalSerializerDetail( Signal.objects.get(id=obj.signal.id)).data return None
class CategoryHALSerializer(HALSerializer): serializer_url_field = CategoryHyperlinkedIdentityField _display = DisplayField() departments = serializers.SerializerMethodField() class Meta: model = Category fields = ( '_links', '_display', 'name', 'slug', 'handling', 'departments', 'is_active', 'description', 'handling_message', ) def get_departments(self, obj): return _NestedPublicDepartmentSerializer( obj.departments.filter(categorydepartment__is_responsible=True), many=True).data
class WellSerializer(HALSerializer): _display = DisplayField() containers = RelatedSummaryField() class Meta(object): model = Well fields = [ "_links", "_display", "id", "id_number", "serial_number", "buurt_code", "stadsdeel", "geometrie", "created_at", "warranty_date", "operational_date", "containers", "address", "site", ]
class PrivateCategorySerializer(HALSerializer): serializer_url_field = PrivateCategoryHyperlinkedIdentityField _display = DisplayField() sla = serializers.SerializerMethodField() new_sla = PrivateCategorySLASerializer(write_only=True) departments = _NestedPrivateCategoryDepartmentSerializer(source='categorydepartment_set', many=True, read_only=True) class Meta: model = Category fields = ( '_links', '_display', 'id', 'name', 'slug', 'is_active', 'description', 'handling_message', 'sla', 'new_sla', 'departments', ) read_only_fields = ( 'slug', ) def get_sla(self, obj): return PrivateCategorySLASerializer(obj.slo.first()).data def update(self, instance, validated_data): new_sla = validated_data.pop('new_sla') if 'new_sla' in validated_data else None if new_sla: ServiceLevelObjective.objects.create(category=instance, **new_sla) instance.refresh_from_db() return super(PrivateCategorySerializer, self).update(instance, validated_data)
class PriorityHALSerializer(AddExtrasMixin, HALSerializer): _display = DisplayField() _signal = serializers.PrimaryKeyRelatedField(queryset=Signal.objects.all()) serializer_url_field = PriorityLinksField class Meta: model = Priority fields = ( '_links', '_display', 'id', '_signal', 'priority', 'created_at', 'created_by', ) def create(self, validated_data): validated_data = self.add_user(validated_data) validated_data['created_by'] = validated_data.pop('user') signal = validated_data.pop('_signal') priority = Signal.actions.update_priority(validated_data, signal) return priority
class SignalStatusOnlyHALSerializer(HALSerializer): _display = DisplayField() signal_id = serializers.CharField(label='SIGNAL_ID', read_only=True) status = _NestedStatusUnauthenticatedModelSerializer(read_only=True) _links = SignalUnauthenticatedLinksField('signal-detail') class Meta(object): model = Signal fields = ( '_links', '_display', 'signal_id', 'status', 'created_at', 'updated_at', 'incident_date_start', 'incident_date_end', 'operational_date', ) read_only_fields = ( 'created_at', 'updated_at', )
class PrivateQuestionSerializer(HALSerializer): serializer_url_field = QuestionHyperlinkedIdentityField _display = DisplayField() class Meta: model = Question fields = ( '_links', '_display', 'id', 'key', 'uuid', 'label', 'short_label', 'field_type', 'next_rules', 'required', 'created_at', ) read_only_fields = ( 'id', 'uuid', 'created_at', )
class PrivateDepartmentSerializerDetail(HALSerializer): _display = DisplayField() categories = CategoryDepartmentSerializer( source='active_categorydepartment_set', many=True, required=False) country = CountrySerializer(required=False) city = CitySerializer(required=False) class Meta: model = Department fields = ( '_links', '_display', 'id', 'name', 'code', 'is_intern', 'categories', 'country', 'city', 'app', ) def _save_category_department(self, instance, validated_data): instance.category_set.clear() for category_department_validated_data in validated_data: category_department_validated_data['department'] = instance category_department = CategoryDepartment( **category_department_validated_data) category_department.save() def create(self, validated_data): country_data = validated_data.pop('country', None) city_data = validated_data.pop('city', None) categorydepartment_set_validated_data = None if 'active_categorydepartment_set' in validated_data: categorydepartment_set_validated_data = validated_data.pop( 'active_categorydepartment_set') instance = super(PrivateDepartmentSerializerDetail, self).create(validated_data) # get country object if exists else create new if country_data and country_data["country_name"]: if Country.objects.filter( country_name__iexact=country_data["country_name"]).exists( ): instance.country = Country.objects.get( country_name__iexact=country_data["country_name"]) else: country = Country.objects.create( country_name=country_data["country_name"]) instance.country = country if city_data and city_data["city_name"]: if City.objects.filter( city_name__iexact=city_data["city_name"]).exists(): instance.city = City.objects.get( city_name__iexact=city_data["city_name"]) else: city = City.objects.create(city_name=city_data["city_name"]) instance.city = city if categorydepartment_set_validated_data: self._save_category_department( instance=instance, validated_data=categorydepartment_set_validated_data) instance.save() instance.refresh_from_db() return instance def update(self, instance, validated_data): if 'active_categorydepartment_set' in validated_data: self._save_category_department( instance=instance, validated_data=validated_data.pop( 'active_categorydepartment_set')) instance = super(PrivateDepartmentSerializerDetail, self).update(instance, validated_data) instance.refresh_from_db() return instance
class CategoryHALSerializer(AddExtrasMixin, HALSerializer): serializer_url_field = CategoryLinksField _display = DisplayField() _signal = serializers.PrimaryKeyRelatedField(queryset=Signal.objects.all()) # Should be required, but to make it work with the backwards compatibility fix it's not required # at the moment.. sub_category = CategoryHyperlinkedRelatedField(write_only=True, required=False, source='category') sub = serializers.CharField(source='category.name', read_only=True) sub_slug = serializers.CharField(source='category.slug', read_only=True) main = serializers.CharField(source='category.parent.name', read_only=True) main_slug = serializers.CharField(source='category.parent.slug', read_only=True) # Backwards compatibility fix for departments, should be retrieved from category terms resource. department = serializers.SerializerMethodField(source='category.departments', read_only=True) class Meta(object): model = CategoryAssignment fields = ( '_links', '_display', '_signal', 'sub_category', 'sub', 'sub_slug', 'main', 'main_slug', 'department', 'created_by', 'created_at', ) def get_department(self, obj): return ', '.join(obj.category.departments.values_list('code', flat=True)) def to_internal_value(self, data): internal_data = super().to_internal_value(data) if 'sub_category' in data: # Fix for renaming sub_category to category internally data['category'] = data['sub_category'] del data['sub_category'] # Backwards compatibility fix to let this endpoint work with `sub` as key. is_main_name_posted = 'main' in data is_sub_name_posted = 'sub' in data is_category_not_posted = 'category' not in data if is_main_name_posted and is_sub_name_posted and is_category_not_posted: try: category = Category.objects.get(parent__name__iexact=data['main'], name__iexact=data['sub']) except Category.DoesNotExist: internal_data['category'] = Category.objects.get(id=76) # Overig else: internal_data['category'] = category return internal_data def validate(self, attrs): if 'category' in attrs: if attrs['_signal'].category_assignment.category.id == attrs['category'].id: raise ValidationError('Cannot assign the same category twice') return super(CategoryHALSerializer, self).validate(attrs=attrs) def create(self, validated_data): validated_data = self.add_user(validated_data) validated_data['created_by'] = validated_data.pop('user') signal = validated_data.pop('_signal') category = Signal.actions.update_category_assignment(validated_data, signal) return category
class PublicSessionSerializer(HALSerializer): serializer_url_field = SessionPublicHyperlinkedIdentityField _display = DisplayField() can_freeze = serializers.SerializerMethodField() path_questions = serializers.SerializerMethodField() path_answered_question_uuids = serializers.SerializerMethodField() path_unanswered_question_uuids = serializers.SerializerMethodField() path_validation_errors_by_uuid = serializers.SerializerMethodField() class Meta: model = Session fields = ( '_links', '_display', 'uuid', 'started_at', 'submit_before', 'duration', 'created_at', # generated using the SessionService in serializer context: 'can_freeze', 'path_questions', 'path_answered_question_uuids', 'path_unanswered_question_uuids', 'path_validation_errors_by_uuid') read_only_fields = ( 'id', 'uuid', 'created_at', # generated using the SessionService in serializer context: 'can_freeze', 'path_questions', 'path_path_answered_question_uuids', 'path_unanswered_question_uuids', 'path_validation_errors_by_uuid') def get_can_freeze(self, obj): session_service = self.context.get('session_service') return session_service.can_freeze def get_path_questions(self, obj): session_service = self.context.get('session_service') serializer = PublicQuestionSerializer(session_service.path_questions, many=True, context=self.context) return serializer.data def get_path_answered_question_uuids(self, obj): session_service = self.context.get('session_service') return session_service.path_answered_question_uuids def get_path_unanswered_question_uuids(self, obj): session_service = self.context.get('session_service') return session_service.path_unanswered_question_uuids def get_path_validation_errors_by_uuid(self, obj): session_service = self.context.get('session_service') # Possibly turn all UUIDs into str(UUID)s in SessionService. return { str(k): v for k, v in session_service.path_validation_errors_by_uuid.items() } def create(self, validated_data): """ This serializer cannot be used to create Session instances """ raise NotImplementedError def update(self, instance, validated_data): """ This serializer cannot be used to update Session instances """ raise NotImplementedError
class PrivateSignalSerializerDetail(HALSerializer, AddressValidationMixin): """ This serializer is used for the detail endpoint and when updating the instance """ serializer_url_field = PrivateSignalLinksFieldWithArchives _display = DisplayField() location = _NestedLocationModelSerializer( required=False, permission_classes=(SIAPermissions, )) status = _NestedStatusModelSerializer( required=False, permission_classes=(SignalChangeStatusPermission, )) category = _NestedCategoryModelSerializer( source='category_assignment', required=False, permission_classes=(SignalChangeCategoryPermission, )) reporter = _NestedReporterModelSerializer( required=False, permission_classes=(SIAPermissions, )) priority = _NestedPriorityModelSerializer( required=False, permission_classes=(SIAPermissions, )) notes = _NestedNoteModelSerializer( many=True, required=False, permission_classes=(SignalCreateNotePermission, )) has_attachments = serializers.SerializerMethodField() extra_properties = SignalExtraPropertiesField( required=False, validators=[ ExtraPropertiesValidator( filename=os.path.join(os.path.dirname(__file__), '..', 'json_schema', 'extra_properties.json')) ]) # noqa class Meta: model = Signal fields = ( '_links', '_display', 'category', 'id', 'has_attachments', 'location', 'status', 'reporter', 'priority', 'notes', 'source', 'text', 'text_extra', 'extra_properties', 'created_at', 'updated_at', 'incident_date_start', 'incident_date_end', ) read_only_fields = ( 'id', 'has_attachments', ) def get_has_attachments(self, obj): return obj.attachments.exists() def update(self, instance, validated_data): """ Perform update on nested models. Note: - Reporter cannot be updated via the API. - Atomic update (all fail/succeed), django signals on full success (see underlying update_multiple method of actions SignalManager). """ user_email = self.context['request'].user.email for _property in [ 'location', 'status', 'category_assignment', 'priority' ]: if _property in validated_data: data = validated_data[_property] data['created_by'] = user_email if 'notes' in validated_data and validated_data['notes']: note_data = validated_data['notes'][0] note_data['created_by'] = user_email signal = Signal.actions.update_multiple(validated_data, instance) return signal
class PrivateSignalSerializerDetail(HALSerializer, AddressValidationMixin): """ This serializer is used for the detail endpoint and when updating the instance """ serializer_url_field = PrivateSignalLinksFieldWithArchives _display = DisplayField() country = CountrySerializer(required=False, permission_classes=(SIAPermissions, )) city = CitySerializer(required=False, permission_classes=(SIAPermissions, )) city_object = CityObjectSerializer(many=True, required=False) location = _NestedLocationModelSerializer( required=False, permission_classes=(SIAPermissions, )) status = _NestedStatusModelSerializer( required=False, permission_classes=(SignalChangeStatusPermission, )) category = _NestedCategoryModelSerializer( source='category_assignment', required=False, permission_classes=(SignalChangeCategoryPermission, )) reporter = _NestedReporterModelSerializer( required=False, permission_classes=(SIAPermissions, )) priority = _NestedPriorityModelSerializer( required=False, permission_classes=(SIAPermissions, )) notes = _NestedNoteModelSerializer( many=True, required=False, permission_classes=(SignalCreateNotePermission, )) type = _NestedTypeModelSerializer( required=False, permission_classes=(SIAPermissions, ), source='type_assignment', ) directing_departments = _NestedDepartmentModelSerializer( source='directing_departments_assignment.departments', many=True, required=False, permission_classes=(SIAPermissions, ), ) updates = SignalPlanUpdateSerializer(many=True, required=False) mb_mapping = serializers.SerializerMethodField(required=False) has_attachments = serializers.SerializerMethodField() image_category = ImageCategorySerializer(required=False) extra_properties = SignalExtraPropertiesField( required=False, validators=[ ExtraPropertiesValidator( filename=os.path.join(os.path.dirname(__file__), '..', 'json_schema', 'extra_properties.json')) ]) # noqa class Meta: model = Signal fields = ( '_links', '_display', 'category', 'id', 'has_attachments', 'location', 'status', 'reporter', 'priority', 'notes', 'type', 'source', 'text', 'text_extra', 'extra_properties', 'created_at', 'updated_at', 'incident_date_start', 'incident_date_end', 'finished_by', 'directing_departments', 'country', 'city', 'city_object', 'webform_kenmark', 'mb_report_id', 'facilitator_report_id', 'report_days', 'forman_emp_name', 'urgency', 'plan_time', 'updates', 'mb_mapping', 'updated_by', 'image_category', ) read_only_fields = ( 'id', 'has_attachments', ) def get_mb_mapping(self, obj): if IDMapping.objects.filter(seda_signal_id=obj.signal_id).exists(): return IDMappingSerializer( IDMapping.objects.get(seda_signal_id=obj.signal_id)).data return None def get_has_attachments(self, obj): return obj.attachments.exists() def update(self, instance, validated_data): """ Perform update on nested models. Note: - Reporter cannot be updated via the API. - Atomic update (all fail/succeed), django signals on full success (see underlying update_multiple method of actions SignalManager). """ if not instance.is_parent() and validated_data.get( 'directing_departments_assignment') is not None: raise serializers.ValidationError( 'Directing departments can only be set on a parent Signal') user_email = self.context['request'].user.email for _property in [ 'location', 'status', 'category_assignment', 'priority' ]: if _property in validated_data: data = validated_data[_property] data['created_by'] = user_email if 'type_assignment' in validated_data: type_data = validated_data.pop('type_assignment') type_data['created_by'] = user_email validated_data['type'] = type_data if 'notes' in validated_data and validated_data['notes']: note_data = validated_data['notes'][0] note_data['created_by'] = user_email if 'directing_departments_assignment' in validated_data and validated_data[ 'directing_departments_assignment']: validated_data['directing_departments_assignment'][ 'created_by'] = user_email signal = Signal.actions.update_multiple(validated_data, instance) return signal
class PrivateSignalSerializerList(HALSerializer, AddressValidationMixin): """ This serializer is used for the list endpoint and when creating a new instance """ serializer_url_field = PrivateSignalLinksField _display = DisplayField() country = CountrySerializer(required=False, permission_classes=(SIAPermissions, )) city = CitySerializer(required=False, permission_classes=(SIAPermissions, )) city_object = CityObjectSerializer(many=True, required=False) location = _NestedLocationModelSerializer( permission_classes=(SIAPermissions, )) status = _NestedStatusModelSerializer( required=False, permission_classes=(SignalCreateInitialPermission, )) category = _NestedCategoryModelSerializer( source='category_assignment', permission_classes=(SignalCreateInitialPermission, )) reporter = _NestedReporterModelSerializer( permission_classes=(SIAPermissions, )) priority = _NestedPriorityModelSerializer( required=False, permission_classes=(SIAPermissions, )) notes = _NestedNoteModelSerializer( many=True, required=False, permission_classes=(SignalCreateInitialPermission, )) type = _NestedTypeModelSerializer( required=False, permission_classes=(SIAPermissions, ), source='type_assignment', ) updates = SignalPlanUpdateSerializer(many=True, required=False) directing_departments = _NestedDepartmentModelSerializer( source='directing_departments_assignment.departments', many=True, required=False, permission_classes=(SIAPermissions, ), ) has_attachments = serializers.SerializerMethodField() extra_properties = SignalExtraPropertiesField( required=False, allow_null=True, validators=[ ExtraPropertiesValidator( filename=os.path.join(os.path.dirname(__file__), '..', 'json_schema', 'extra_properties.json')) ]) class Meta: model = Signal fields = ( '_links', '_display', 'id', 'signal_id', 'source', 'text', 'text_extra', 'status', 'location', 'category', 'reporter', 'priority', 'type', 'created_at', 'updated_at', 'incident_date_start', 'incident_date_end', 'operational_date', 'has_attachments', 'extra_properties', 'notes', 'directing_departments', 'finished_by', 'country', 'city', 'city_object', 'webform_kenmark', 'mb_report_id', 'facilitator_report_id', 'report_days', 'forman_emp_name', 'urgency', 'plan_time', 'updates', 'updated_by', ) read_only_fields = ( 'created_at', 'updated_at', 'has_attachments', ) extra_kwargs = { 'source': { 'validators': [SignalSourceValidator()] }, } def get_has_attachments(self, obj): return obj.attachments.exists() def create(self, validated_data): if validated_data.get('directing_departments_assignment') is not None: raise serializers.ValidationError( 'Directing departments cannot be set on initial creation') if validated_data.get('status') is not None: raise serializers.ValidationError( "Status cannot be set on initial creation") # Set default status logged_in_user = self.context['request'].user INITIAL_STATUS = { 'state': workflow.GEMELD, # see models.py is already default 'text': None, 'user': logged_in_user.email, } # We require location and reporter to be set and to be valid. reporter_data = validated_data.pop('reporter') location_data = validated_data.pop('location') location_data['created_by'] = logged_in_user.email category_assignment_data = validated_data.pop('category_assignment') category_assignment_data['created_by'] = logged_in_user.email # We will use the priority and signal type on the incoming message if present. priority_data = validated_data.pop( 'priority', {'priority': Priority.PRIORITY_NORMAL}) priority_data['created_by'] = logged_in_user.email type_data = validated_data.pop('type_assignment', {}) type_data['created_by'] = logged_in_user.email country_data = validated_data.pop('country', None) city_data = validated_data.pop('city', None) city_object_data = validated_data.pop('city_object', None) signal = Signal.actions.create_initial(validated_data, location_data, INITIAL_STATUS, category_assignment_data, reporter_data, country_data, city_data, city_object_data, priority_data, type_data) return signal
class PrivateCategorySerializer(HALSerializer): serializer_url_field = PrivateCategoryHyperlinkedIdentityField _display = DisplayField() sla = serializers.SerializerMethodField() new_sla = PrivateCategorySLASerializer(write_only=True) departments = serializers.SerializerMethodField() country = serializers.SerializerMethodField() city = serializers.SerializerMethodField() class Meta: model = Category fields = ( '_links', '_display', 'id', 'name', 'slug', 'is_active', 'description', 'handling_message', 'sla', 'new_sla', 'departments', 'filter_label', 'category_level_name1', 'category_level_name2', 'category_level_name3', 'category_level_name4', 'country', 'city', ) read_only_fields = ( 'id', 'slug', 'sla', 'filter_label' 'departments', # noqa Is read-only by default because we use the SerializerMethodField but also added here for readability ) def get_sla(self, obj): return PrivateCategorySLASerializer(obj.slo.all().order_by('-created_at').first()).data def get_departments(self, obj): return _NestedPrivateCategoryDepartmentSerializer( CategoryDepartment.objects.filter(category_id=obj.pk).order_by('department__code'), many=True ).data def get_country(self, obj): if obj.country: return CountrySerializer( Country.objects.get(id=obj.country.id) ).data return None def get_city(self, obj): if obj.city: return CitySerializer( City.objects.get(id=obj.city.id) ).data return None def update(self, instance, validated_data): new_sla = validated_data.pop('new_sla') if 'new_sla' in validated_data else None if new_sla: ServiceLevelObjective.objects.create(category=instance, **new_sla) instance.refresh_from_db() return super(PrivateCategorySerializer, self).update(instance, validated_data)
class PrivateSignalSerializerList(SignalValidationMixin, HALSerializer): """ This serializer is used for the list endpoint and when creating a new instance """ serializer_url_field = PrivateSignalLinksField _display = DisplayField() signal_id = serializers.UUIDField(source='uuid', required=False, read_only=True) location = _NestedLocationModelSerializer( permission_classes=(SIAPermissions,) ) status = _NestedStatusModelSerializer( required=False, permission_classes=(SignalCreateInitialPermission,) ) category = _NestedCategoryModelSerializer( source='category_assignment', permission_classes=(SignalCreateInitialPermission,) ) reporter = _NestedReporterModelSerializer( permission_classes=(SIAPermissions,) ) priority = _NestedPriorityModelSerializer( required=False, permission_classes=(SIAPermissions,) ) notes = _NestedNoteModelSerializer( many=True, required=False, permission_classes=(SignalCreateInitialPermission,) ) type = _NestedTypeModelSerializer( required=False, permission_classes=(SIAPermissions,), source='type_assignment', ) directing_departments = _NestedDepartmentModelSerializer( source='directing_departments_assignment.departments', many=True, required=False, permission_classes=(SIAPermissions,), ) routing_departments = _NestedDepartmentModelSerializer( source='routing_assignment.departments', many=True, required=False, allow_null=True, permission_classes=(SIAPermissions,), ) has_attachments = serializers.SerializerMethodField() assigned_user_email = serializers.EmailField(source='user_assignment.user.email', required=False, allow_null=True) extra_properties = serializers.JSONField( required=False, allow_null=True, validators=[ ExtraPropertiesValidator( filename=os.path.join( os.path.dirname(__file__), '..', 'json_schema', 'extra_properties.json') ) ] ) parent = serializers.PrimaryKeyRelatedField( required=False, read_only=False, write_only=True, queryset=Signal.objects.all() ) has_parent = serializers.SerializerMethodField() has_children = serializers.SerializerMethodField() attachments = PrivateSignalAttachmentRelatedField(view_name='private-signals-attachments-detail', many=True, required=False, read_only=False, write_only=True, queryset=Attachment.objects.all()) # The Session containing the given answers of a Questionnaire session = serializers.UUIDField(default=None, write_only=True, required=False) id_display = serializers.CharField(source='get_id_display', read_only=True) class Meta: model = Signal list_serializer_class = _SignalListSerializer fields = ( '_links', '_display', 'id', 'id_display', 'signal_id', 'source', 'text', 'text_extra', 'status', 'location', 'category', 'reporter', 'priority', 'type', 'created_at', 'updated_at', 'incident_date_start', 'incident_date_end', 'operational_date', 'has_attachments', 'extra_properties', 'notes', 'directing_departments', 'routing_departments', 'attachments', 'parent', 'has_parent', 'has_children', 'assigned_user_email', 'session' ) read_only_fields = ( 'created_at', 'updated_at', 'has_attachments', 'has_parent', 'has_children', ) extra_kwargs = { 'source': {'validators': [PrivateSignalSourceValidator()]}, } def get_has_attachments(self, obj): return obj.attachments.exists() def get_has_parent(self, obj): return obj.parent_id is not None # True is a parent_id is set, False if not def get_has_children(self, obj): return obj.children.exists() def validate(self, attrs): # noqa C901 errors = {} if attrs.get('directing_departments_assignment') is not None: errors.update( {'directing_departments_assignment': ['Directing departments cannot be set on initial creation']} ) if attrs.get('routing_assignment') is not None: errors.update( {'routing_assignment': ['Signal departments relation cannot be set on initial creation']} ) if attrs.get('status') is not None: errors.update( {'status': ['Status cannot be set on initial creation']} ) attachments = attrs.get('attachments') parent = attrs.get('parent') if attachments and parent is None: errors.update({'attachments': ['Attachments can only be copied when creating a child Signal']}) if attachments and parent: attachments_belong_to_parent = all([parent.pk == attachment._signal_id for attachment in attachments]) if not attachments_belong_to_parent: errors.update({'attachments': ['Attachments can only be copied from the parent Signal']}) if 'session' in attrs and attrs['session']: """ If a Session UUID is given the following checks must be valid before it can be connected to the Signal that is going to be created created. - The Session with given UUID must exists - The Session cannot be connected to a Signal - The Session cannot be expired - The Session must be frozen """ try: session = Session.objects.get(uuid=attrs['session']) except Session.DoesNotExist as e: errors.update({'session': [f'{e}']}) else: if session._signal_id: errors.update({'session': ['Session already used']}) elif not session.frozen and not session.is_expired: errors.update({'session': ['Session not frozen']}) elif session.too_late: errors.update({'session': ['Session expired']}) else: attrs['session'] = session # SIG-4382 additional check on the extra_properties if the category is lantaarpaal-straatverlichting # # Disabled additional check. This check prevents the creation of a "child" signal in the # lantaarnpaal-straatverlichting category because there is no way to provide the streetlight # # if attrs.get('category_assignment').get('category').slug == 'lantaarnpaal-straatverlichting': # validator = ExtraPropertiesValidator(filename=os.path.join(os.path.dirname(__file__), '..', 'json_schema', # 'extra_properties_streetlights.json')) # try: # validator(attrs.get('extra_properties')) # except ValidationError: # errors.update({'extra_properties': [ # 'Extra properties not valid for category "lantaarnpaal-straatverlichting"' # ]}) if errors: raise serializers.ValidationError(errors) return super().validate(attrs=attrs) def create(self, validated_data): # Set default status logged_in_user = self.context['request'].user INITIAL_STATUS = { 'state': workflow.GEMELD, # see models.py is already default 'text': None, 'user': logged_in_user.email, } # We require location and reporter to be set and to be valid. reporter_data = validated_data.pop('reporter') location_data = validated_data.pop('location') location_data['created_by'] = logged_in_user.email category_assignment_data = validated_data.pop('category_assignment') category_assignment_data['created_by'] = logged_in_user.email # We will use the priority and signal type on the incoming message if present. priority_data = validated_data.pop('priority', { 'priority': Priority.PRIORITY_NORMAL }) priority_data['created_by'] = logged_in_user.email type_data = validated_data.pop('type_assignment', {}) type_data['created_by'] = logged_in_user.email attachments = validated_data.pop('attachments') if 'attachments' in validated_data else None session = validated_data.pop('session') signal = Signal.actions.create_initial( validated_data, location_data, INITIAL_STATUS, category_assignment_data, reporter_data, priority_data, type_data, session=session, ) if attachments: Signal.actions.copy_attachments(data=attachments, signal=signal, created_by=logged_in_user.email) # Add history entries for every attachment that was copied. Only photos allowed for now. for attachment in Attachment.objects.filter(_signal=signal).order_by('created_at'): filename = os.path.basename(attachment.file.name) msg = f'Bijlage gekopieerd van hoofdmelding: {filename}' Signal.actions.create_note(data={'text': msg}, signal=signal) signal.refresh_from_db() return signal
class PrivateSignalSerializerDetail(HALSerializer, AddressValidationMixin): """ This serializer is used for the detail endpoint and when updating the instance """ serializer_url_field = PrivateSignalLinksFieldWithArchives _display = DisplayField() location = _NestedLocationModelSerializer( required=False, permission_classes=(SIAPermissions, )) status = _NestedStatusModelSerializer( required=False, permission_classes=(SignalChangeStatusPermission, )) category = _NestedCategoryModelSerializer( source='category_assignment', required=False, permission_classes=(SignalChangeCategoryPermission, )) reporter = _NestedReporterModelSerializer( required=False, permission_classes=(SIAPermissions, )) priority = _NestedPriorityModelSerializer( required=False, permission_classes=(SIAPermissions, )) notes = _NestedNoteModelSerializer( many=True, required=False, permission_classes=(SignalCreateNotePermission, )) type = _NestedTypeModelSerializer( required=False, permission_classes=(SIAPermissions, ), source='type_assignment', ) directing_departments = _NestedDepartmentModelSerializer( source='directing_departments_assignment.departments', many=True, required=False, permission_classes=(SIAPermissions, ), ) routing_departments = _NestedDepartmentModelSerializer( source='routing_assignment.departments', many=True, required=False, allow_null=True, permission_classes=(SIAPermissions, ), ) has_attachments = serializers.SerializerMethodField() assigned_user_id = serializers.IntegerField( source='user_assignment.user.id', required=False, allow_null=True) extra_properties = SignalExtraPropertiesField( required=False, validators=[ ExtraPropertiesValidator( filename=os.path.join(os.path.dirname(__file__), '..', 'json_schema', 'extra_properties.json')) ]) attachments = PrivateSignalAttachmentRelatedField( view_name='private-signals-attachments-detail', many=True, required=False, read_only=True) class Meta: model = Signal fields = ( '_links', '_display', 'category', 'id', 'has_attachments', 'location', 'status', 'reporter', 'priority', 'notes', 'type', 'source', 'text', 'text_extra', 'extra_properties', 'created_at', 'updated_at', 'incident_date_start', 'incident_date_end', 'directing_departments', 'routing_departments', 'attachments', 'assigned_user_id', ) read_only_fields = ( 'id', 'has_attachments', ) def get_has_attachments(self, obj): return obj.attachments.exists() def update(self, instance, validated_data): # noqa """ Perform update on nested models. Note: - Reporter cannot be updated via the API. - Atomic update (all fail/succeed), django signals on full success (see underlying update_multiple method of actions SignalManager). """ if not instance.is_parent() and validated_data.get( 'directing_departments_assignment') is not None: raise serializers.ValidationError( 'Directing departments can only be set on a parent Signal') user_email = self.context['request'].user.email for _property in [ 'location', 'status', 'category_assignment', 'priority' ]: if _property in validated_data: data = validated_data[_property] data['created_by'] = user_email if 'type_assignment' in validated_data: type_data = validated_data.pop('type_assignment') type_data['created_by'] = user_email validated_data['type'] = type_data if 'notes' in validated_data and validated_data['notes']: note_data = validated_data['notes'][0] note_data['created_by'] = user_email if 'directing_departments_assignment' in validated_data and validated_data[ 'directing_departments_assignment']: validated_data['directing_departments_assignment'][ 'created_by'] = user_email if 'routing_assignment' in validated_data and validated_data[ 'routing_assignment']: validated_data['routing_assignment']['created_by'] = user_email if 'user_assignment' in validated_data and validated_data[ 'user_assignment']: validated_data['created_by'] = user_email signal = Signal.actions.update_multiple(validated_data, instance) return signal
class PrivateSignalSerializerList(SignalValidationMixin, HALSerializer): """ This serializer is used for the list endpoint and when creating a new instance """ serializer_url_field = PrivateSignalLinksField _display = DisplayField() location = _NestedLocationModelSerializer( permission_classes=(SIAPermissions, )) status = _NestedStatusModelSerializer( required=False, permission_classes=(SignalCreateInitialPermission, )) category = _NestedCategoryModelSerializer( source='category_assignment', permission_classes=(SignalCreateInitialPermission, )) reporter = _NestedReporterModelSerializer( permission_classes=(SIAPermissions, )) priority = _NestedPriorityModelSerializer( required=False, permission_classes=(SIAPermissions, )) notes = _NestedNoteModelSerializer( many=True, required=False, permission_classes=(SignalCreateInitialPermission, )) type = _NestedTypeModelSerializer( required=False, permission_classes=(SIAPermissions, ), source='type_assignment', ) directing_departments = _NestedDepartmentModelSerializer( source='directing_departments_assignment.departments', many=True, required=False, permission_classes=(SIAPermissions, ), ) routing_departments = _NestedDepartmentModelSerializer( source='routing_assignment.departments', many=True, required=False, allow_null=True, permission_classes=(SIAPermissions, ), ) has_attachments = serializers.SerializerMethodField() assigned_user_id = serializers.IntegerField( source='user_assignment.user.id', required=False, allow_null=True) extra_properties = SignalExtraPropertiesField( required=False, allow_null=True, validators=[ ExtraPropertiesValidator( filename=os.path.join(os.path.dirname(__file__), '..', 'json_schema', 'extra_properties.json')) ]) parent = serializers.PrimaryKeyRelatedField(required=False, read_only=False, write_only=True, queryset=Signal.objects.all()) has_parent = serializers.SerializerMethodField() has_children = serializers.SerializerMethodField() attachments = PrivateSignalAttachmentRelatedField( view_name='private-signals-attachments-detail', many=True, required=False, read_only=False, write_only=True, queryset=Attachment.objects.all()) class Meta: model = Signal list_serializer_class = _SignalListSerializer fields = ('_links', '_display', 'id', 'signal_id', 'source', 'text', 'text_extra', 'status', 'location', 'category', 'reporter', 'priority', 'type', 'created_at', 'updated_at', 'incident_date_start', 'incident_date_end', 'operational_date', 'has_attachments', 'extra_properties', 'notes', 'directing_departments', 'routing_departments', 'attachments', 'parent', 'has_parent', 'has_children', 'assigned_user_id') read_only_fields = ( 'created_at', 'updated_at', 'has_attachments', 'has_parent', 'has_children', ) extra_kwargs = { 'source': { 'validators': [SignalSourceValidator()] }, } def get_has_attachments(self, obj): return obj.attachments.exists() def get_has_parent(self, obj): return obj.parent_id is not None # True is a parent_id is set, False if not def get_has_children(self, obj): return obj.children.exists() def validate(self, attrs): errors = {} if attrs.get('directing_departments_assignment') is not None: errors.update({ 'directing_departments_assignment': ['Directing departments cannot be set on initial creation'] }) if attrs.get('routing_assignment') is not None: errors.update({ 'routing_assignment': [ 'Signal departments relation cannot be set on initial creation' ] }) if attrs.get('status') is not None: errors.update( {'status': ['Status cannot be set on initial creation']}) attachments = attrs.get('attachments') parent = attrs.get('parent') if attachments and parent is None: errors.update({ 'attachments': [ 'Attachments can only be copied when creating a child Signal' ] }) if attachments and parent: attachments_belong_to_parent = all([ parent.pk == attachment._signal_id for attachment in attachments ]) if not attachments_belong_to_parent: errors.update({ 'attachments': ['Attachments can only be copied from the parent Signal'] }) if errors: raise serializers.ValidationError(errors) return super(PrivateSignalSerializerList, self).validate(attrs=attrs) def create(self, validated_data): # Set default status logged_in_user = self.context['request'].user INITIAL_STATUS = { 'state': workflow.GEMELD, # see models.py is already default 'text': None, 'user': logged_in_user.email, } # We require location and reporter to be set and to be valid. reporter_data = validated_data.pop('reporter') location_data = validated_data.pop('location') location_data['created_by'] = logged_in_user.email category_assignment_data = validated_data.pop('category_assignment') category_assignment_data['created_by'] = logged_in_user.email # We will use the priority and signal type on the incoming message if present. priority_data = validated_data.pop( 'priority', {'priority': Priority.PRIORITY_NORMAL}) priority_data['created_by'] = logged_in_user.email type_data = validated_data.pop('type_assignment', {}) type_data['created_by'] = logged_in_user.email attachments = validated_data.pop( 'attachments') if 'attachments' in validated_data else None signal = Signal.actions.create_initial(validated_data, location_data, INITIAL_STATUS, category_assignment_data, reporter_data, priority_data, type_data) if attachments: Signal.actions.copy_attachments(data=attachments, signal=signal) signal.refresh_from_db() return signal