Пример #1
0
class DSSContainerSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer,
                             EntityMetadataSerializerMixin):
    """ REST API Serializer for DSS Containers """

    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    envelopes = DSSEnvelopeSerializer(
        many=True,
        required=False,
        read_only=True,
        source='dss_envelopes',
    )

    class Meta:
        model = DSSContainer
        fields = (
            'pk',
            'name',
            'path',
            'read_write_setting',
            'import_option',
            'is_mounted',
            'projects',
            'envelopes',
            'created_by',
            'created_at',
            'last_modified_by',
            'last_modified_at',
            'url',
            'deleted',
        )
        read_only_fields = ()
Пример #2
0
class ContactSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer,
                        EntityMetadataSerializerMixin):
    """ REST API Serializer for Contacts """

    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    class Meta:
        model = Contact
        fields = ('academic_title', 'first_name', 'last_name', 'email',
                  'phone', 'company', 'projects', 'notes', 'created_by',
                  'created_at', 'last_modified_by', 'last_modified_at',
                  'version_number', 'url', 'metadata', 'is_favourite')

    @transaction.atomic
    def create(self, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        instance = super(ContactSerializer, self).create(validated_data)
        self.create_metadata(metadata_list, instance)
        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        self.update_metadata(metadata_list, instance)
        return super(ContactSerializer, self).update(instance, validated_data)
Пример #3
0
class LabBookSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer, EntityMetadataSerializerMixin):
    """ Serializer for LabBook """
    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    class Meta:
        model = LabBook
        fields = (
            'title', 'description', 'is_template', 'projects',
            'url',
            'created_by', 'created_at', 'last_modified_by', 'last_modified_at', 'version_number',
            'metadata', 'is_favourite'
        )

    @transaction.atomic
    def create(self, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        instance = super().create(validated_data)
        self.create_metadata(metadata_list, instance)
        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        self.update_metadata(metadata_list, instance)
        return super().update(instance, validated_data)
Пример #4
0
class LabbookSectionSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer):
    """ Serializer for LabbookSections """
    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)
    child_elements = LabBookChildElementPrimaryKeyRelatedField(many=True, required=False)

    class Meta:
        model = LabbookSection
        fields = (
            'title', 'date', 'projects', 'child_elements',
            'created_by', 'created_at', 'last_modified_by', 'last_modified_at', 'version_number', 'url'
        )
Пример #5
0
class CommentSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer,
                        EntityMetadataSerializerMixin):
    """ Serializer for Comments """
    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    class Meta:
        model = Comment

        fields = ('content', 'projects', 'created_by', 'created_at',
                  'last_modified_by', 'last_modified_at', 'version_number',
                  'url', 'metadata', 'is_favourite')

    @transaction.atomic
    def create(self, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        instance = super(CommentSerializer, self).create(validated_data)
        self.create_metadata(metadata_list, instance)

        # read the request data and add the values of relates_to_content_type_id, relates_to_pk and private to the
        # instance so they can be used to create a relation within the viewset
        request = self.context['request']
        instance.relates_to_content_type_id = request.data.get(
            'relates_to_content_type_id')
        instance.relates_to_pk = request.data.get('relates_to_pk')
        instance.private = request.data.get('private')

        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        self.update_metadata(metadata_list, instance)
        return super(CommentSerializer, self).update(instance, validated_data)
Пример #6
0
class DriveSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer,
                      EntityMetadataSerializerMixin):
    """ REST API Serializer for Drive """

    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    sub_directories = DirectorySerializer(many=True,
                                          required=False,
                                          read_only=True)

    # url for file-list
    sub_directories_url = HyperlinkedToListField(
        view_name="drive-sub_directories-list", lookup_field_name='drive_pk')

    dss_envelope_id = serializers.PrimaryKeyRelatedField(
        queryset=DSSEnvelope.objects.all(),
        source='envelope',
        many=False,
        required=False,
        allow_null=True)

    envelope_path = serializers.StringRelatedField(source='envelope',
                                                   many=False,
                                                   required=False,
                                                   allow_null=True)

    container_id = serializers.PrimaryKeyRelatedField(
        queryset=DSSContainer.objects.all(),
        source='envelope.container',
        many=False,
        required=False,
        allow_null=True)

    webdav_url = serializers.SerializerMethodField()

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    class Meta:
        model = Drive
        fields = ('title', 'projects', 'sub_directories',
                  'sub_directories_url', 'container_id', 'location',
                  'is_dss_drive', 'envelope_path', 'dss_envelope_id',
                  'created_by', 'created_at', 'last_modified_by',
                  'last_modified_at', 'version_number', 'url', 'webdav_url',
                  'metadata', 'imported', 'is_favourite')

    def get_webdav_url(self, obj):
        """
        Returns the webdav URL for the current drive
        :param obj:
        :return:
        """
        request = get_current_request()

        # strip all non-confirming characters from drive title
        pat = re.compile(r'[\W \-]+')

        stripped_title = re.sub(pat, ' ', obj.title)

        # temporarily remove webdav_url for DSS drives
        if not obj.is_dss_drive:
            return reverse('webdav-drive',
                           request=request,
                           kwargs={
                               'drive': obj.pk,
                               'path': '/',
                               'drive_title': stripped_title
                           })
        return None

    @transaction.atomic
    def create(self, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        instance = super(DriveSerializer, self).create(validated_data)
        self.create_metadata(metadata_list, instance)
        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        self.update_metadata(metadata_list, instance)
        return super(DriveSerializer, self).update(instance, validated_data)
Пример #7
0
class KanbanBoardSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer):
    """
    Serializer for Kanban Boards
    Includes Kanban Board Columns
    """
    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    kanban_board_columns = InternalKanbanBoardColumnSerializer(read_only=False,
                                                               many=True,
                                                               required=False)

    # provide a download link for the background image
    download_background_image = serializers.SerializerMethodField(
        read_only=True)

    # provide a download link for the background image
    download_background_image_thumbnail = serializers.SerializerMethodField(
        read_only=True)

    # write only for the background image
    background_image = serializers.FileField(write_only=True, required=False)

    class Meta:
        model = KanbanBoard
        fields = ('title', 'description', 'projects', 'kanban_board_columns',
                  'background_image', 'download_background_image',
                  'background_image_thumbnail',
                  'download_background_image_thumbnail', 'background_color',
                  'url', 'created_by', 'created_at', 'last_modified_by',
                  'last_modified_at', 'version_number', 'is_favourite')

    def get_download_background_image(self, obj):
        if not obj.background_image:
            return None

        request = get_current_request()
        path = reverse('kanbanboard-background-image.png',
                       kwargs={'pk': obj.pk})

        return build_expiring_jwt_url(request, path)

    def get_download_background_image_thumbnail(self, obj):
        if not obj.background_image_thumbnail:
            return None

        request = get_current_request()
        path = reverse('kanbanboard-background-image-thumbnail.png',
                       kwargs={'pk': obj.pk})

        return build_expiring_jwt_url(request, path)

    def create(self, validated_data):
        """
        Override create method 0f KanbanBoardSerializer such that we can handle sub-serializers for
        - kanban_board_columns
        If a kanban board is created without columns, we add the default columns to the board
        :param validated_data: validated data of the serializer
        :return: KanbanBoard
        """
        kanban_board_columns = None

        # get kanban board columns from validated data (if it is available)
        if 'kanban_board_columns' in validated_data:
            kanban_board_columns = validated_data.pop('kanban_board_columns')

        # create the kanban board using KanbanBoardSerializer (hence we needed to remove kanban_board_columns)
        instance = super(KanbanBoardSerializer, self).create(validated_data)

        # now create columns (if set)
        if kanban_board_columns:
            for item in kanban_board_columns:
                KanbanBoardColumn.objects.create(kanban_board=instance,
                                                 title=item['title'],
                                                 ordering=item['ordering'],
                                                 color=item['color'],
                                                 icon=item['icon'])
        else:
            # no kanban board columns set, create a default column "new"
            ordering = 0

            KanbanBoardColumn.objects.create(ordering=0,
                                             title=_("New"),
                                             kanban_board=instance)

        # save the instance again so we trigger the changeset
        instance.save()

        return instance

    def update(self, instance, validated_data):
        """
        Override update method of KanbanBoardSerializer such that we can handle sub-serializers for
        - kanban_board_columns
        :param instance: the instance that is being updated
        :param validated_data: validated data of the serializer
        :return: KanbanBoard
        """
        kanban_board_columns = None

        # get kanban board columns from validated data (if it is available)
        if 'kanban_board_columns' in validated_data:
            kanban_board_columns = validated_data.pop('kanban_board_columns')

        # start a transaction which handles updating assigned_users, kanban_board_columns and updating the task itself
        with transaction.atomic():
            # update kanban_board_columns
            if kanban_board_columns is not None:
                current_kanban_board_columns_pk = list(
                    instance.kanban_board_columns.values_list('pk', flat=True))

                for item in kanban_board_columns:
                    if 'pk' in item:
                        # update existing item
                        real_item = KanbanBoardColumn.objects.filter(
                            kanban_board=instance, pk=item['pk']).first()

                        item_updated = False

                        if item['title'] != real_item.title:
                            real_item.title = item['title']
                            item_updated = True

                        if item['color'] != real_item.color:
                            real_item.color = item['color']
                            item_updated = True

                        if item['ordering'] != real_item.ordering:
                            real_item.ordering = item['ordering']
                            item_updated = True

                        if item.get('icon', '') != real_item.icon:
                            real_item.icon = item.get('icon', '')
                            item_updated = True

                        if item_updated:
                            real_item.save()

                        # remove pk from current_kanban_board_columns_pk
                        try:
                            current_kanban_board_columns_pk.remove(item['pk'])
                        except ValueError as e:
                            logger.error(e)
                    else:
                        # create new item
                        KanbanBoardColumn.objects.create(
                            kanban_board=instance,
                            title=item['title'],
                            icon=item.get('icon', ''),
                            ordering=item['ordering'],
                            color=item['color'])

                # finally, everything that is still in current_kanban_board_columns_pk needs to be removed
                KanbanBoardColumn.objects.filter(
                    pk__in=current_kanban_board_columns_pk).delete()

            # update kanban board instance
            instance = super(KanbanBoardSerializer,
                             self).update(instance, validated_data)

        return instance
Пример #8
0
class PictureSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer,
                        EntityMetadataSerializerMixin):
    """ REST API Serializer for Picture """

    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    download_shapes = serializers.SerializerMethodField(read_only=True)
    download_background_image = serializers.SerializerMethodField(
        read_only=True)
    download_rendered_image = serializers.SerializerMethodField(read_only=True)

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    def get_download_shapes(self, picture):
        return self.build_download_url(picture, 'picture-shapes-json')

    def get_download_background_image(self, picture):
        return self.build_download_url_with_token(picture,
                                                  'picture-background-image')

    def get_download_rendered_image(self, picture):
        if not picture.rendered_image:
            return self.get_download_background_image(picture)

        return self.build_download_url_with_token(picture,
                                                  'picture-rendered-image')

    @staticmethod
    def build_download_url_with_token(picture, reverse_url_name):
        request = get_current_request()

        if picture.uploaded_picture_entry is None:
            raise IntegrityError("uploaded_picture_entry is not referenced")

        path = reverse(reverse_url_name,
                       kwargs={'pk': picture.uploaded_picture_entry.pk})

        return build_expiring_jwt_url(request, path)

    @staticmethod
    def build_download_url(picture, reverse_url_name):
        request = get_current_request()
        path = reverse(reverse_url_name,
                       kwargs={'pk': picture.uploaded_picture_entry.pk})
        return request.build_absolute_uri(path)

    # all files should be write only - we have the download links as alternative

    # write only for the shapes
    shapes_image = serializers.FileField(write_only=True, required=False)

    # write only for the background image
    background_image = serializers.FileField(write_only=True, required=False)

    # write only for the rendered image
    rendered_image = serializers.FileField(write_only=True, required=False)

    class Meta:
        model = Picture
        fields = ('title', 'shapes_image', 'background_image',
                  'rendered_image', 'projects', 'width', 'height',
                  'created_by', 'created_at', 'last_modified_by',
                  'last_modified_at', 'version_number', 'download_shapes',
                  'download_background_image', 'download_rendered_image',
                  'url', 'metadata', 'is_favourite')

    @transaction.atomic
    def create(self, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        instance = super().create(validated_data)
        self.create_metadata(metadata_list, instance)
        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        self.update_metadata(metadata_list, instance)
        return super().update(instance, validated_data)
Пример #9
0
class FileSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer,
                     EntityMetadataSerializerMixin):
    """ REST API Serializer for Files """
    from eric.drives.models import Directory

    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    download = serializers.SerializerMethodField(read_only=True)

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    def get_download(self, file):
        request = get_current_request()
        path = reverse("file-download",
                       kwargs={'pk': file.uploaded_file_entry.pk})
        return request.build_absolute_uri(path)

    directory_id = serializers.PrimaryKeyRelatedField(
        # ToDo: We should also provide Directory.objects.viewable() here
        queryset=Directory.objects.all(),
        source='directory',
        many=False,
        required=False,
        allow_null=True)

    envelope_id = serializers.PrimaryKeyRelatedField(
        source='directory.drive.envelope',
        many=False,
        required=False,
        allow_null=True,
        read_only=True)

    container_id = serializers.PrimaryKeyRelatedField(
        source='directory.drive.envelope.container',
        many=False,
        required=False,
        allow_null=True,
        read_only=True)

    path = serializers.FileField(write_only=True, allow_empty_file=True)

    class Meta:
        model = File
        fields = ('title', 'name', 'description', 'path', 'original_filename',
                  'mime_type', 'projects', 'url', 'download', 'file_size',
                  'directory_id', 'envelope_id', 'container_id', 'is_dss_file',
                  'location', 'created_by', 'created_at', 'last_modified_by',
                  'last_modified_at', 'version_number', 'metadata', 'imported',
                  'is_favourite')
        read_only_fields = ('original_filename', 'mime_type', 'file_size',
                            'mime_type')

    @transaction.atomic
    def create(self, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        instance = super().create(validated_data)
        self.create_metadata(metadata_list, instance)
        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        self.update_metadata(metadata_list, instance)
        return super().update(instance, validated_data)
Пример #10
0
class TaskSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer,
                     EntityMetadataSerializerMixin):
    """ REST API Serializer for Tasks """

    assigned_users = PublicUserSerializer(read_only=True, many=True)

    assigned_users_pk = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all(),
        source='assigned_users',
        many=True,
        required=False)

    checklist_items = TaskCheckListItemSerializer(read_only=False,
                                                  many=True,
                                                  required=False)

    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    labels = ElementLabelPrimaryKeyRelatedField(many=True, required=False)

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    class Meta:
        model = Task
        fields = (
            'title',
            'start_date',
            'due_date',
            'priority',
            'state',
            'description',
            'projects',
            'task_id',
            'assigned_users',
            'assigned_users_pk',
            'checklist_items',
            'labels',
            'created_by',
            'created_at',
            'last_modified_by',
            'last_modified_at',
            'version_number',
            'url',
            'metadata',
            'full_day',
            'is_favourite',
            'remind_assignees',
            'reminder_datetime',
        )
        read_only_fields = ('assigned_users', )

    @transaction.atomic
    def create(self, validated_data):
        """
        Override create method of TaskSerializer such that we can handle sub-serializers for
         - assigned_users
         - checklist_items
         - labels
         - metadata
        :param validated_data: validated data of the serializer
        :return: Task
        """
        assigned_users = None
        checklist_items = None
        metadata_list = self.pop_metadata(validated_data)

        # get assigned users from validated data (if it is available)
        if 'assigned_users' in validated_data:
            assigned_users = validated_data.pop('assigned_users')

        # get checklist items from validated data (if it is available)
        if 'checklist_items' in validated_data:
            checklist_items = validated_data.pop('checklist_items')

        # create the task using TaskSerializer (hence we needed to remove assigned_users and checklist_items)
        instance = super(TaskSerializer, self).create(validated_data)

        # now create assigned users
        if assigned_users:
            for user in assigned_users:
                # instance.assigned_users.add(user)
                TaskAssignedUser.objects.create(task=instance,
                                                assigned_user=user)

        # and checklist items
        if checklist_items:
            for item in checklist_items:
                TaskCheckList.objects.create(task=instance,
                                             title=item['title'],
                                             checked=item['checked'],
                                             ordering=item['ordering'])

        self.create_metadata(metadata_list, instance)

        # save instance again so we can trigger the changeset
        instance.save()

        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        """
        Override update method of TaskSerializer such that we can handle sub-serializers for
        - assigned_users
         - checklist_items
        :param instance: the instance that is being updated
        :param validated_data: validated data of the serializer
        :return: Task
        """
        assigned_users = None
        checklist_items = None
        metadata_list = self.pop_metadata(validated_data)

        # get assigned users from validated data (if it is available)
        if 'assigned_users' in validated_data:
            assigned_users = validated_data.pop('assigned_users')

        # get checklist items from validated data (if it is available)
        if 'checklist_items' in validated_data:
            checklist_items = validated_data.pop('checklist_items')

        # update assigned_users
        if assigned_users is not None:
            currently_assigned_users = instance.assigned_users.all()

            users_to_remove = set(currently_assigned_users).difference(
                assigned_users)

            users_to_add = set(assigned_users).difference(
                set(currently_assigned_users))

            # handle assigned users that need to be removed
            for user in users_to_remove:
                # The following is equal to: instance.assigned_users.remove(user)
                TaskAssignedUser.objects.filter(task=instance,
                                                assigned_user=user).delete()

            # handle assigned users that need to be added
            for user in users_to_add:
                # The following is equal to: instance.assigned_users.add(user)
                TaskAssignedUser.objects.create(task=instance,
                                                assigned_user=user)

        # update checklist_items
        if checklist_items is not None:
            current_checklist_items_pk = list(
                instance.checklist_items.values_list('pk', flat=True))

            for item in checklist_items:
                if 'pk' in item:
                    # update existing item
                    real_item = TaskCheckList.objects.filter(
                        task=instance, pk=item['pk']).first()
                    real_item.checked = item['checked']
                    real_item.title = item['title']
                    real_item.ordering = item['ordering']
                    real_item.save()
                    # remove pk from current_checklist_items_pk
                    current_checklist_items_pk.remove(item['pk'])
                else:
                    # create new item
                    TaskCheckList.objects.create(
                        task=instance,
                        title=item['title'],
                        checked=item['checked'],
                        ordering=item['ordering'],
                    )

            # finally, everything that is still in current_checklist_items_pk needs to be removed
            TaskCheckList.objects.filter(
                pk__in=current_checklist_items_pk).delete()

        self.update_metadata(metadata_list, instance)

        # update task instance
        instance = super(TaskSerializer, self).update(instance, validated_data)

        return instance
Пример #11
0
class DmpSerializerExtended(BaseModelWithCreatedByAndSoftDeleteSerializer,
                            EntityMetadataSerializerMixin):
    """ Serializer for DMPs """
    dmp_form_title = serializers.SerializerMethodField(read_only=True)

    dmp_form_data = DmpFormDataSerializerExtended(many=True,
                                                  required=False,
                                                  read_only=True)

    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    class Meta:
        model = Dmp
        fields = ('url', 'title', 'status', 'dmp_form', 'dmp_form_title',
                  'dmp_form_data', 'projects', 'created_by', 'created_at',
                  'last_modified_by', 'last_modified_at', 'version_number',
                  'metadata', 'is_favourite')

    def get_dmp_form_title(self, dmp):
        try:
            return dmp.dmp_form.title
        except Dmp.dmp_form.RelatedObjectDoesNotExist:
            return None

    @transaction.atomic
    def create(self, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        instance = super().create(validated_data)
        self.create_metadata(metadata_list, instance)
        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        metadata_list = self.pop_metadata(validated_data)
        self.update_metadata(metadata_list, instance)

        # list of changed dmp_form_data values
        initial_dmp_form_data = self.initial_data.get('dmp_form_data', [])

        # validated_data.pop('dmp_form_data')

        # iterate through list of changed dmp_form_data values
        for dmp_form_data in initial_dmp_form_data:
            # get pk and value from changed dmp_form_data
            data_pk = dmp_form_data.get('pk', None)
            data_value = dmp_form_data.get('value', None)

            # check if pk and value are set
            if data_pk and data_value is not None:
                # get the DB entry for dmp form data
                dmp_form_data_queryset = DmpFormData.objects.filter(
                    dmp=instance, pk=data_pk)
                # check if an object with the pk exists
                if len(dmp_form_data_queryset) == 1:
                    form_data_db_instance = dmp_form_data_queryset.first()
                    # update form data value
                    form_data_db_instance.value = data_value
                    try:
                        form_data_db_instance.save()
                    except ValidationError as err:
                        if 'value' in err.message_dict:
                            raise ValidationError(
                                {data_pk: err.message_dict['value']})
                        else:
                            raise err
                else:
                    raise NotFound
            else:
                raise ValidationError({
                    'dmp_form_data':
                    ValidationError(_('Not all fields are provided'),
                                    params={'assignment': self},
                                    code='invalid')
                })

        # last but not least, update DMP title and status
        # instance.title = validated_data.get('title', instance.title)
        # instance.status = validated_data.get('status', instance.status)
        # instance.project = validated_data.get('project', instance.project)
        # instance.dmp_form = validated_data.get('dmp_form', instance.dmp_form)
        # instance.save()

        return super(DmpSerializerExtended,
                     self).update(instance, validated_data)
Пример #12
0
class MeetingSerializer(BaseModelWithCreatedByAndSoftDeleteSerializer,
                        EntityMetadataSerializerMixin,
                        ScheduledNotificationSerializer):
    """ Serializer for Meetings """

    projects = ProjectPrimaryKeyRelatedField(many=True, required=False)

    resource = MinimalisticResourceSerializer(read_only=True)

    resource_pk = serializers.PrimaryKeyRelatedField(
        queryset=Resource.objects.all(),
        source='resource',
        many=False,
        required=False,
        allow_null=True)

    attending_users = PublicUserSerializer(read_only=True, many=True)

    attending_users_pk = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all(),
        source='attending_users',
        many=True,
        required=False)

    attending_contacts = MinimalisticContactSerializer(read_only=True,
                                                       many=True)

    attending_contacts_pk = ContactPrimaryKeyRelatedField(
        source='attending_contacts', many=True, required=False)

    metadata = EntityMetadataSerializer(
        read_only=False,
        many=True,
        required=False,
    )

    scheduled_notification = SerializerMethodField()

    def get_scheduled_notification(self, instance):
        return ScheduledNotificationSerializer.get_scheduled_notification(
            instance)

    # a separate named field is required for writing to ScheduledNotification
    scheduled_notification_writable = ScheduledNotificationSerializer(
        write_only=True, required=False)

    class Meta:
        model = Meeting
        fields = ('title', 'location', 'date_time_start', 'date_time_end',
                  'text', 'projects', 'url', 'resource', 'resource_pk',
                  'attending_users', 'attending_contacts',
                  'attending_contacts_pk', 'attending_users_pk', 'created_by',
                  'created_at', 'last_modified_by', 'last_modified_at',
                  'version_number', 'metadata', 'scheduled_notification',
                  'scheduled_notification_writable', 'full_day',
                  'is_favourite')

    @transaction.atomic
    def create(self, validated_data):
        """
        Create a meeting with attending users and attending contacts
        :param validated_data:
        :return:
        """
        attending_users = None
        attending_contacts = None
        scheduled_notification_writable = None

        if 'attending_users' in validated_data:
            attending_users = validated_data.pop('attending_users')
        if 'attending_contacts' in validated_data:
            attending_contacts = validated_data.pop('attending_contacts')

        if 'scheduled_notification_writable' in validated_data:
            scheduled_notification_writable = validated_data.pop(
                'scheduled_notification_writable')

        metadata_list = self.pop_metadata(validated_data)

        # delegate creating the meeting to the current serializer
        instance = super(MeetingSerializer, self).create(validated_data)

        # read the request data and add the value of create_for to the instance, which is the pk of a user
        # in the MeetingViewSet we will use it to change attending_users accordingly and to give full access privilege
        request = self.context['request']
        instance.create_for = request.data.get('create_for')

        # create attending users
        if attending_users:
            for user in attending_users:
                UserAttendsMeeting.objects.create(user=user, meeting=instance)

        # create attending contacts
        if attending_contacts:
            for contact in attending_contacts:
                ContactAttendsMeeting.objects.create(contact=contact,
                                                     meeting=instance)

        self.create_metadata(metadata_list, instance)

        if scheduled_notification_writable:
            self.update_or_create_schedulednotification(
                scheduled_notification_writable, instance)

        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        """
        Update a meeting with attending users and attending contacts
        :param instance:
        :param validated_data:
        :return:
        """
        attending_contacts = None
        attending_users = None
        scheduled_notification_writable = None

        if 'attending_contacts' in validated_data:
            attending_contacts = validated_data.pop('attending_contacts')

        if 'attending_users' in validated_data:
            attending_users = validated_data.pop('attending_users')

        if 'scheduled_notification_writable' in validated_data:
            scheduled_notification_writable = validated_data.pop(
                'scheduled_notification_writable')

        metadata_list = self.pop_metadata(validated_data)
        self.update_metadata(metadata_list, instance)

        if attending_users is not None:
            currently_attending_users = instance.attending_users.all()
            users_to_remove = set(currently_attending_users).difference(
                attending_users)
            users_to_add = set(attending_users).difference(
                set(currently_attending_users))

            # handle attending users that need to be removed
            for user in users_to_remove:
                # The following is equal to: instance.attending_users.remove(user)
                UserAttendsMeeting.objects.filter(meeting=instance,
                                                  user=user).delete()

            # handle attending users that need to be added
            for user in users_to_add:
                # The following is equal to: instance.attending_users.add(user)
                UserAttendsMeeting.objects.create(meeting=instance, user=user)

        if attending_contacts is not None:
            currently_attending_contacts = instance.attending_contacts.all()
            contacts_to_remove = set(currently_attending_contacts).difference(
                attending_contacts)
            contacts_to_add = set(attending_contacts).difference(
                set(currently_attending_contacts))

            # handle attending contacts that need to be removed
            for contact in contacts_to_remove:
                # The following is equal to: instance.attending_contacts.remove(contact)
                ContactAttendsMeeting.objects.filter(meeting=instance,
                                                     contact=contact).delete()

            # handle attending contacts that need to be added
            for contact in contacts_to_add:
                # The following is equal to: instance.attending_contacts.add(contact)
                ContactAttendsMeeting.objects.create(meeting=instance,
                                                     contact=contact)

        instance = super(MeetingSerializer,
                         self).update(instance, validated_data)

        if scheduled_notification_writable:
            self.update_or_create_schedulednotification(
                scheduled_notification_writable, instance)

        return instance