Ejemplo n.º 1
0
    def update(self, request, force_request_data=None, *args, **kwargs):
        """ Updates an existing element. """

        # force_request_data:
        #   JSONField doesn't parse JSON data sent via API, therefore it can be necessary
        #   to fake HTML input by passing a MultiValueDict as data to the serializer.
        #   See also JSONField.get_value (.../rest_framework/fields.py)

        request_data = request.data if force_request_data is None else force_request_data

        # the following code is from MOdelViewSet.update
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request_data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        # temporarily disable revision model mixin for the response -> performance boost
        RevisionModelMixin.set_enabled(False)
        response = Response(serializer.data)
        RevisionModelMixin.set_enabled(True)

        return response
def migrate_idtags_out_of_existing_dir_metadata_json_forward(
        apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias

    dir_metadata_files = File.objects.using(db_alias).filter(
        name="dir_metadata.json",
        imported=True,
    )

    with disable_permission_checks(File):
        for dir_metadata_file in dir_metadata_files:
            file_path = get_upload_to_path(dir_metadata_file,
                                           dir_metadata_file.name)
            file_path = os.path.join(dss_storage.location, file_path)
            if os.path.isfile(file_path):
                with open(file_path, 'r') as infile:
                    dir_metadata_file_content = json.loads(infile.read())
                for entry in dir_metadata_file_content:
                    if "idtag" in entry.keys():
                        idtag_id = entry["idtag"]
                        idtag = f"<p>idtag: {idtag_id}</p>"
            if idtag:
                File.objects.using(db_alias).filter(
                    directory=dir_metadata_file.directory,
                    imported=True,
                ).update(description=idtag)

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 3
0
def remove_calendaraccess_and_full_access(apps, schema_editor):
    """
    Remove all entity permission assignments
    :param apps:
    :param schema_editor:
    :return:
    """
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias
    CalendarAccess = apps.get_model('shared_elements', 'CalendarAccess')
    ContentType = apps.get_model('contenttypes', "ContentType")
    ModelPrivilege = apps.get_model('model_privileges', 'ModelPrivilege')

    # get content type
    calendaraccess_content_type = ContentType.objects.using(db_alias).get(
        app_label='shared_elements', model='calendaraccess')

    with DisableSignals():
        # remove all CalendarAccess objects
        CalendarAccess.objects.using(db_alias).all().delete()

        # remove all ModelPrivilege objects where the content_type is that of the CalendarAccess
        ModelPrivilege.objects.using(db_alias).all().filter(
            content_type=calendaraccess_content_type, ).delete()

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 4
0
def rename_auth_permission(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias

    Permission = apps.get_model("auth", "Permission")

    permissions = Permission.objects.using(db_alias).filter(
        codename__contains='_change_project')

    for permission in permissions:
        if permission.codename.endswith('_change_project'):
            modelname = permission.codename[0:permission.codename.
                                            index('_change_project')]
            new_perm_name = "%s_%s" % ('change_project', modelname)

            print("Renaming permission", permission.codename, "to",
                  new_perm_name)

            permission.codename = new_perm_name
            try:
                permission.save()
            except:
                print('failed to rename, ignoring...')

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 5
0
def migrate_task_priority_choices_backward(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias
    # Task = apps.get_model('shared_elements', 'Task')

    with DisableSignals():  # avoid permission/lock checks
        Task.objects.using(db_alias).filter(
            priority=Task.TASK_PRIORITY_VERY_HIGH).update(
                priority=OLD_TASK_PRIORITY_VERY_HIGH)

        Task.objects.using(db_alias).filter(
            priority=Task.TASK_PRIORITY_HIGH).update(
                priority=OLD_TASK_PRIORITY_HIGH)

        Task.objects.using(db_alias).filter(
            priority=Task.TASK_PRIORITY_NORMAL).update(
                priority=OLD_TASK_PRIORITY_NORMAL)

        Task.objects.using(db_alias).filter(
            priority=Task.TASK_PRIORITY_LOW).update(
                priority=OLD_TASK_PRIORITY_LOW)

        Task.objects.using(db_alias).filter(
            priority=Task.TASK_PRIORITY_VERY_LOW).update(
                priority=OLD_TASK_PRIORITY_VERY_LOW)

    RevisionModelMixin.set_enabled(True)
def create_virtual_root_directory(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias

    Directory = apps.get_model("drives", "Directory")
    Drive = apps.get_model("drives", "Drive")

    with disable_permission_checks(Directory):
        with disable_permission_checks(Drive):
            # iterate over all drives
            for drive in Drive.objects.all():
                # get all root directories of this drive
                root_directories = Directory.objects.filter(drive=drive,
                                                            directory=None)

                if len(root_directories) == 1:
                    # if there is only one root directory, we dedicate this as the new virtual root
                    root_directories.update(is_virtual_root=True, name="/")
                else:
                    # create a new virtual root and move all the others
                    root_directory = Directory.objects.create(
                        drive=drive,
                        directory=None,
                        is_virtual_root=True,
                        name="/")

                    root_directories.exclude(pk=root_directory.pk).update(
                        directory=root_directory)

    RevisionModelMixin.set_enabled(True)
def fix_changeset_for_file_entries(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias

    ChangeSet = apps.get_model('django_changeset', "ChangeSet")

    ContentType = apps.get_model('contenttypes', "ContentType")
    User = apps.get_model('auth', 'User')

    File = apps.get_model('projects', 'File')
    UploadedFileEntry = apps.get_model('projects', 'UploadedFileEntry')

    # get the first superuser as the alternative user
    alternative_user = User.objects.filter(is_superuser=True).first()

    file_content_type = ContentType.objects.using(db_alias).get(
        app_label='shared_elements', model='file')

    uploaded_file_entry_content_type = ContentType.objects.using(db_alias).get(
        app_label='shared_elements', model='uploadedfileentry')

    # iterate over all file entries
    for entry in UploadedFileEntry.objects.using(db_alias).all():
        # check if the file entry has an insert changeset

        insert_changeset = ChangeSet.objects.using(db_alias).filter(
            object_uuid=entry.pk,
            object_type=uploaded_file_entry_content_type,
            changeset_type='I').first()

        # if there is no insert changeset, create one
        if not insert_changeset:
            # get insert_changest of file
            file_insert_changeset = ChangeSet.objects.using(db_alias).filter(
                object_uuid=entry.file.pk,
                object_type=file_content_type,
                changeset_type='I').first()

            # also, if there is no insert changeset, we need to create one
            if not file_insert_changeset:
                file_insert_changeset = ChangeSet.objects.using(
                    db_alias).create(object_uuid=entry.file.pk,
                                     object_type=file_content_type,
                                     changeset_type='I',
                                     user=alternative_user)

            # get user of file_insert_changeset
            file_created_by = file_insert_changeset.user

            # create a new insert changeset for the file entry
            insert_changeset = ChangeSet.objects.using(db_alias).create(
                user=file_created_by,
                object_uuid=entry.pk,
                object_type=uploaded_file_entry_content_type,
                changeset_type='I')

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 8
0
def fix_privileges_up(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    with disable_permission_checks(ModelPrivilege):
        privileges = ModelPrivilege.objects.all()
        for privilege in privileges:
            fix_privileges(privilege)

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 9
0
def change_project_state_from_deleted_to_cancel(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)
    db_alias = schema_editor.connection.alias

    Project = apps.get_model('projects', 'Project')

    Project.objects.filter(project_state=DELETED).update(project_state=CANCEL,
                                                         deleted=True)

    RevisionModelMixin.set_enabled(True)
def fill_rolepermissionassignment_uuid(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias
    RolePermissionAssignment = apps.get_model('projects',
                                              'rolepermissionassignment')
    for obj in RolePermissionAssignment.objects.using(db_alias).all():
        obj.uuid = uuid.uuid4()
        obj.save()

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 11
0
def convert_task_state_from_new_to_old(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias

    Task = apps.get_model("shared_elements", "Task")

    with disable_permission_checks(Task):
        for task in Task.objects.all():
            task.state = TASK_STATE_MAPPING_NEW_TO_OLD[task.state]
            task.save()

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 12
0
def clear_entity_permission_assignment(apps, schema_editor):
    """
    Remove all entity permission assignments
    :param apps:
    :param schema_editor:
    :return:
    """
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias

    ModelPrivilege = apps.get_model('projects', 'ModelPrivilege')

    ModelPrivilege.objects.all().delete()

    RevisionModelMixin.set_enabled(True)
def migrate_resource_usage_settings_fields_backward(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias
    # Resource = apps.get_model('shared_elements', 'Resource')

    with DisableSignals():  # avoid permission/lock checks
        Resource.objects.using(db_alias).filter(
            general_usage_setting=Resource.SELECTED_GROUPS).update(
                general_usage_setting=OLD_SELECTED_GROUPS)

        Resource.objects.using(db_alias).filter(
            general_usage_setting=Resource.GLOBAL).update(
                general_usage_setting=OLD_GLOBAL)

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 14
0
def create_entity_permission_assignment(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)
    db_alias = schema_editor.connection.alias

    ChangeSet = apps.get_model('django_changeset', "ChangeSet")
    ContentType = apps.get_model('contenttypes', "ContentType")
    User = apps.get_model('auth', 'User')
    Dmp = apps.get_model('dmp', 'dmp')
    ModelPrivilege = apps.get_model('model_privileges', 'ModelPrivilege')

    # get the first superuser as the alternative user
    alternative_user = User.objects.filter(is_superuser=True).first()

    # get all content types
    dmp_content_type = ContentType.objects.using(db_alias).get(app_label='dmp',
                                                               model='dmp')

    # iterate over all tasks and create an entity permission assignment
    for dmp in Dmp.objects.using(db_alias).all():
        # get the insert changeset of this object
        insert_changeset = ChangeSet.objects.using(db_alias).filter(
            object_uuid=dmp.pk,
            object_type=dmp_content_type,
            changeset_type='I').first()

        # if there is no insert changeset, create one
        if not insert_changeset:
            insert_changeset = ChangeSet.objects.using(db_alias).create(
                user=alternative_user,
                object_uuid=dmp.pk,
                object_type=dmp_content_type,
                changeset_type='I')

        created_by = insert_changeset.user

        if not created_by:
            print(
                "DMP with ID {id} does not have created_by, assuming user={user}"
                .format(id=dmp.id, user=alternative_user))
            created_by = alternative_user

        ModelPrivilege.objects.create(user_id=created_by.id,
                                      content_type=dmp_content_type,
                                      object_id=dmp.id,
                                      is_owner=True)

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 15
0
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data,
                                         many=isinstance(request.data, list))
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)

        # temporarily disable revision model mixin for the response -> performance boost
        RevisionModelMixin.set_enabled(False)
        # return only the count of added paths here for performance
        count = len(serializer.data)
        data = {'count': count}
        response = Response(data,
                            status=status.HTTP_201_CREATED,
                            headers=headers)
        RevisionModelMixin.set_enabled(True)

        return response
Ejemplo n.º 16
0
def clear_entity_permission_assignment(apps, schema_editor):
    """
    Remove all entity permission assignments
    :param apps:
    :param schema_editor:
    :return:
    """
    RevisionModelMixin.set_enabled(False)
    db_alias = schema_editor.connection.alias
    ContentType = apps.get_model('contenttypes', "ContentType")

    # get all content types
    dmp_content_type = ContentType.objects.using(db_alias).get(app_label='dmp',
                                                               model='dmp')

    ModelPrivilege = apps.get_model('model_privileges', 'ModelPrivilege')
    ModelPrivilege.objects.filter(content_type=dmp_content_type).delete()

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 17
0
    def create(self, request, force_request_data=None, *args, **kwargs):
        """ Creates a new element. """

        # force_request_data:
        #   JSONField doesn't parse JSON data sent via API, therefore it can be necessary
        #   to fake HTML input by passing a MultiValueDict as data to the serializer.
        #   See also JSONField.get_value (.../rest_framework/fields.py)

        request_data = request.data if force_request_data is None else force_request_data

        # the following code is from ModelViewSet.create
        serializer = self.get_serializer(data=request_data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)

        # temporarily disable revision model mixin for the response -> performance boost
        RevisionModelMixin.set_enabled(False)
        response = Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
        RevisionModelMixin.set_enabled(True)

        return response
Ejemplo n.º 18
0
def create_calendaraccess_and_full_access(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias
    ContentType = apps.get_model('contenttypes', "ContentType")
    User = apps.get_model('auth', 'User')
    CalendarAccess = apps.get_model('shared_elements', 'CalendarAccess')
    ModelPrivilege = apps.get_model('model_privileges', 'ModelPrivilege')

    # get content type
    calendaraccess_content_type = ContentType.objects.get_for_model(
        CalendarAccess)

    # iterate over all Users, create a CalendarAccess and give the user full_access
    # If the user never logged in we don't do anything here.
    # There is still a handler that does the same on authentication for "new" users.
    with DisableSignals():  # avoid permission/lock checks
        for user in User.objects.using(db_alias).filter(
                last_login__isnull=False):
            # check if a CalendarAccess already exists for this user
            privilege_exists = CalendarAccess.objects.using(
                db_alias).all().filter(created_by=user, )

            if not privilege_exists:
                new_privilege = CalendarAccess.objects.using(db_alias).create()
                # set created_by and last_modified_by to the user, so the CalendarAccess is his/hers
                new_privilege.created_by = user
                new_privilege.last_modified_by = user
                new_privilege.save()

                # now give the user full_access to his calendar
                perm = ModelPrivilege(user=user,
                                      full_access_privilege="AL",
                                      content_type=calendaraccess_content_type,
                                      object_id=new_privilege.pk)
                perm.save()

    RevisionModelMixin.set_enabled(True)
def remove_virtual_root_directory(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias

    Directory = apps.get_model("drives", "Directory")
    Drive = apps.get_model("drives", "Drive")

    with disable_permission_checks(Directory):
        with disable_permission_checks(Drive):
            # iterate over all drives
            for drive in Drive.objects.all():
                # get all virtual root directories of this drive
                virtual_root_directories = Directory.objects.filter(
                    drive=drive, directory=None, is_virtual_root=True)
                # get all directories that have one of those virtual root directories as parent
                sub_root_directories = Directory.objects.filter(
                    directory__in=virtual_root_directories)
                # update their parents
                sub_root_directories.update(directory=None)
                # and delete virtual root directories
                virtual_root_directories.delete()

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 20
0
def create_entity_permission_assignment(apps, schema_editor):
    RevisionModelMixin.set_enabled(False)

    db_alias = schema_editor.connection.alias

    ChangeSet = apps.get_model('django_changeset', "ChangeSet")

    ContentType = apps.get_model('contenttypes', "ContentType")
    User = apps.get_model('auth', 'User')

    Task = apps.get_model('projects', 'Task')
    Note = apps.get_model('projects', 'Note')
    Meeting = apps.get_model('projects', 'Meeting')
    Contact = apps.get_model('projects', 'Contact')
    File = apps.get_model('projects', 'File')

    # get the first superuser as the alternative user
    alternative_user = User.objects.filter(is_superuser=True).first()

    ModelPrivilege = apps.get_model('projects', 'ModelPrivilege')

    # get all content types
    task_content_type = ContentType.objects.using(db_alias).get(
        app_label='shared_elements', model='task')

    note_content_type = ContentType.objects.using(db_alias).get(
        app_label='shared_elements', model='note')

    meeting_content_type = ContentType.objects.using(db_alias).get(
        app_label='shared_elements', model='meeting')

    contact_content_type = ContentType.objects.using(db_alias).get(
        app_label='shared_elements', model='contact')

    file_content_type = ContentType.objects.using(db_alias).get(
        app_label='shared_elements', model='file')

    # iterate over all tasks and create an entity permission assignment
    for task in Task.objects.using(db_alias).all():
        # get the insert changeset of this object
        insert_changeset = ChangeSet.objects.using(db_alias).filter(
            object_uuid=task.pk,
            object_type=task_content_type,
            changeset_type='I').first()

        # if there is no insert changeset, create one
        if not insert_changeset:
            insert_changeset = ChangeSet.objects.using(db_alias).create(
                user=alternative_user,
                object_uuid=task.pk,
                object_type=task_content_type,
                changeset_type='I')

        created_by = insert_changeset.user

        if not created_by:
            print(
                "Task with ID {id} does not have created_by, assuming user={user}"
                .format(id=task.id, user=alternative_user))
            created_by = alternative_user

        ModelPrivilege.objects.create(user_id=created_by.id,
                                      content_type=task_content_type,
                                      object_id=task.id,
                                      is_owner=True)

    # iterate over all notes and create an entity permission assignment
    for note in Note.objects.using(db_alias).all():
        insert_changeset = ChangeSet.objects.using(db_alias).filter(
            object_uuid=note.pk,
            object_type=note_content_type,
            changeset_type='I').first()

        # if there is no insert changeset, create one
        if not insert_changeset:
            insert_changeset = ChangeSet.objects.using(db_alias).create(
                user=alternative_user,
                object_uuid=note.pk,
                object_type=note_content_type,
                changeset_type='I')

        created_by = insert_changeset.user

        if not created_by:
            print(
                "Note with ID {id} does not have created_by, assuming user={user}"
                .format(id=note.id, user=alternative_user.username))
            created_by = alternative_user

        ModelPrivilege.objects.create(user_id=created_by.id,
                                      content_type=note_content_type,
                                      object_id=note.id,
                                      is_owner=True)

    # iterate over all meetings and create an entity permission assignment
    for meeting in Meeting.objects.using(db_alias).all():
        insert_changeset = ChangeSet.objects.using(db_alias).filter(
            object_uuid=meeting.pk,
            object_type=meeting_content_type,
            changeset_type='I').first()

        # if there is no insert changeset, create one
        if not insert_changeset:
            insert_changeset = ChangeSet.objects.using(db_alias).create(
                user=alternative_user,
                object_uuid=meeting.pk,
                object_type=meeting_content_type,
                changeset_type='I')

        created_by = insert_changeset.user

        if not created_by:
            print(
                "Meeting with ID {id} does not have created_by, assuming user={user}"
                .format(id=meeting.id, user=alternative_user.username))
            created_by = alternative_user

        ModelPrivilege.objects.create(user_id=created_by.id,
                                      content_type=meeting_content_type,
                                      object_id=meeting.id,
                                      is_owner=True)

    # iterate over all contacts and create an entity permission assignment
    for contact in Contact.objects.using(db_alias).all():
        insert_changeset = ChangeSet.objects.using(db_alias).filter(
            object_uuid=contact.pk,
            object_type=contact_content_type,
            changeset_type='I').first()

        # if there is no insert changeset, create one
        if not insert_changeset:
            insert_changeset = ChangeSet.objects.using(db_alias).create(
                user=alternative_user,
                object_uuid=contact.pk,
                object_type=contact_content_type,
                changeset_type='I')

        created_by = insert_changeset.user

        if not created_by:
            print(
                "Contact with ID {id} does not have created_by, assuming user={user}"
                .format(id=contact.id, user=alternative_user.username))
            created_by = alternative_user

        ModelPrivilege.objects.create(user_id=created_by.id,
                                      content_type=contact_content_type,
                                      object_id=contact.id,
                                      is_owner=True)

    # iterate over all files and create an entity permission assignment
    for file in File.objects.using(db_alias).all():
        insert_changeset = ChangeSet.objects.using(db_alias).filter(
            object_uuid=file.pk,
            object_type=file_content_type,
            changeset_type='I').first()

        # if there is no insert changeset, create one
        if not insert_changeset:
            insert_changeset = ChangeSet.objects.using(db_alias).create(
                user=alternative_user,
                object_uuid=file.pk,
                object_type=file_content_type,
                changeset_type='I')

        created_by = insert_changeset.user

        if not created_by:
            print(
                "File with ID {id} does not have created_by, assuming user={user}"
                .format(id=file.id, user=alternative_user.username))
            created_by = alternative_user

        ModelPrivilege.objects.create(user_id=created_by.id,
                                      content_type=file_content_type,
                                      object_id=file.id,
                                      is_owner=True)

    RevisionModelMixin.set_enabled(True)
Ejemplo n.º 21
0
    def update_all(self, *args, **kwargs):
        """
        Change positioning, width, height, and order of labbook child elements
        """
        # verify that we are getting an array of data
        assert isinstance(self.request.data, list), "Expected array"

        request_pk_list = []

        # loop over request.data and collect PKs of child element
        for child_element in self.request.data:
            if 'pk' in child_element:
                request_pk_list.append(child_element['pk'])
            else:
                # we do not support creating a new element here
                raise ValidationError(
                    _("Element with no primary key supplied, but this endpoint does not support creating new elements"
                      ))

        # query all editable LabBookChildElement with all primary keys from the request
        child_elements = LabBookChildElement.objects.filter(
            lab_book=self.parent_object, pk__in=request_pk_list).editable()

        # fetch them and put them into a dictionary
        child_elements = {
            str(obj._get_pk_val()): obj
            for obj in child_elements
        }

        # make sure the elements have the same length (else something is wrong with the pk, assignment or permissions)
        if len(child_elements) != len(request_pk_list):
            raise ValidationError(
                _("Invalid primary key suplied - element does not belong to the labbook"
                  ))

        # the remainder of this can be done without permission checks (we already know the element is editable)
        with disable_permission_checks(LabBookChildElement):
            for child_element in self.request.data:
                pk = child_element['pk']

                # get item from existing child_elements
                real_item = child_elements[pk]

                # check if anything has changed -> only fire a database query if the element really needs to be changed
                if real_item.position_x != child_element['position_x'] \
                        or real_item.position_y != child_element['position_y'] \
                        or real_item.width != child_element['width'] \
                        or real_item.height != child_element['height']:
                    # something has changed -> update it in database
                    real_item.position_x = child_element['position_x']
                    real_item.position_y = child_element['position_y']
                    real_item.width = child_element['width']
                    real_item.height = child_element['height']

                    real_item.save()

        # call save method of parent object, such that the Django ChangeSet stores the changes on the "related_many"
        # self.parent_object.save()
        # commented out on purpose - this causes an issue with moving elements - see ticket-259775

        # temporarily disable revision model to improve performance for providing the list endpoint here
        RevisionModelMixin.set_enabled(False)
        # ToDo: It is not necessary to return the whole list of elements here (needs frontend adaptation too)
        response = Response(request_pk_list)
        RevisionModelMixin.set_enabled(True)

        return response
Ejemplo n.º 22
0
    def move_assignment(self, *args, **kwargs):
        """ Moves a task from one column to another, also updates the ordering if necessary. """
        from timeit import default_timer as timer

        start = timer()

        to_column = self.request.data['to_column']
        to_index = self.request.data['to_index']
        assignment_pk = self.request.data['assignment_pk']

        # Check if to_column is in the same kanban board (if not, this will raise an exception)
        real_column = self.parent_object.kanban_board_columns.filter(
            pk=to_column).first()

        if not real_column:
            raise NotFound

        # get the assignment that we need to change (no need to call viewable here; if the user is allowed to access
        # the parent object, the user is also allowed to access the column)
        assignment = KanbanBoardColumnTaskAssignment.objects.filter(
            pk=assignment_pk).select_related('task').first()

        if not assignment:
            raise NotFound

        # store current ordering (this is the index where the assignment is moved FROM)
        from_index = assignment.ordering
        # store current column of assignment
        from_column = str(assignment.kanban_board_column_id)

        if from_index == to_index and from_column == to_column:
            # no changes
            return self.list(self.request, *args, **kwargs)

        if from_column != to_column:
            # easy: if from_column != to_column, we just need to make space in to_column and fill the gap in from_column
            # step 1: make space in the to_column: Shift everything in the to_column that is below to_index down by 1
            task_assignments = KanbanBoardColumnTaskAssignment.objects.filter(
                kanban_board_column_id=to_column,
                ordering__gte=to_index).exclude(pk=assignment_pk)

            task_assignments.update(ordering=F('ordering') + 1)

            # step 2: now that we have made some room, we can set the ordering of our assignment
            assignment.ordering = to_index
            assignment.kanban_board_column_id = to_column

            assignment.save()

            # step 3: now that the original element was removed, close the gap in from_column by decreasing ordering
            # of the assignments in from_column
            task_assignments = KanbanBoardColumnTaskAssignment.objects.filter(
                kanban_board_column_id=from_column,
                ordering__gte=from_index).exclude(pk=assignment_pk)

            task_assignments.update(ordering=F('ordering') - 1)

        else:  # from_column == to_column
            # move within the same column
            logger.debug(
                "KanbanBoardColumnTaskAssignmentViewSet.move_assignment(board_id={}): Moving assignment "
                "within the same column ".format(self.parent_object.pk))

            ########################################################################################################
            # step 1: make space in the to_column: Shift everything in the to_column after to_index down by 1
            ########################################################################################################
            task_assignments = KanbanBoardColumnTaskAssignment.objects.viewable(
            ).filter(kanban_board_column_id=to_column).exclude(
                pk=assignment_pk)

            if from_index < to_index:
                # as we are moving down, we need to decrease to_index - else we move it too far
                to_index -= 1
                # move down within the same column
                task_assignments = task_assignments.filter(
                    ordering__gte=from_index, ordering__lte=to_index)

                # just mass-decrease the ordering by 1
                task_assignments.update(ordering=F('ordering') - 1)
            else:
                # move up within the same column
                task_assignments = task_assignments.filter(
                    ordering__gte=to_index,
                    ordering__lte=from_index,
                )

                # just mass-increase the ordering by 1
                task_assignments.update(ordering=F('ordering') + 1)

            ########################################################################################################
            # step 2: now that we have made some room, we can set the ordering of the current assignment without
            #         causing a DB/consistency conflict (uniqueness of ordering + kanban_board_column)
            ########################################################################################################
            assignment.ordering = to_index
            assignment.kanban_board_column_id = to_column

            assignment.save()

            ########################################################################################################
            # step 3: Consistency check - make sure the from_column has a consistent ordering again
            #         in an optimal world, this should not never happen - so worst case it's just a READ QUERY, and
            #         not an UPDATE QUERY
            ########################################################################################################
            task_assignments = KanbanBoardColumnTaskAssignment.objects.viewable(
            ).filter(kanban_board_column_id=from_column).order_by('ordering')

            # iterate over all task assignments of the current column and fix ordering (in case it needs to be fixed)
            i = 0

            with disable_permission_checks(KanbanBoardColumnTaskAssignment):
                for a in task_assignments:
                    # for this assignment we expect the ordering to be i
                    if a.ordering != i:
                        # wrong ordering detected, fix it
                        logger.debug(
                            "KanbanBoardColumnTaskAssignmentViewSet.move_assignment():  "
                            "Detected ordering of assignment is {}, but expected it to be {}"
                            .format(a.ordering, i))

                        a.ordering = i
                        a.save(update_fields=['ordering'])

                    i = i + 1

        end = timer()

        logger.debug(
            "KanbanBoardColumnTaskAssignmentViewSet.move_assignment(board_id={}): "
            "Moving took {} seconds".format(self.parent_object.pk,
                                            (end - start)))

        # return the QUERY/LIST api call
        from django_changeset.models import RevisionModelMixin

        # temporarily disable revision model mixin for the list api call
        RevisionModelMixin.set_enabled(False)
        response = self.list(self.request, *args, **kwargs)
        RevisionModelMixin.set_enabled(True)

        return response
Ejemplo n.º 23
0
    def create_many(self, *args, **kwargs):
        """ Assigns multiple tasks to a board. """

        # verify that we are getting an array of data
        assert isinstance(self.request.data, list), "Expected array"

        # collect task_ids and column_ids of assignment
        task_ids = []
        column_ids = []

        for assignment in self.request.data:
            task_ids.append(assignment['task_id'])
            column_ids.append(assignment['kanban_board_column'])

        # the task ids should be unique, lets verify that we actually have access on those tasks
        if Task.objects.viewable().filter(
                pk__in=task_ids).count() != len(task_ids):
            # the number of tasks in task_ids differs from the number of tasks that the current user is allowed to
            # view
            raise NotFound

        # since Django 1.11, there is a weird behaviour of QueryDicts that are immutable
        if isinstance(
                self.request.data, QueryDict
        ):  # however, some request.data objects are normal dictionaries...
            self.request.data._mutable = True

        # for each column we need to determine the currently highest ordering, and the expected next ordering
        max_ordering_qs = KanbanBoardColumnTaskAssignment.objects.filter(
            kanban_board_column__kanban_board=self.parent_object).order_by(
                'kanban_board_column').values('kanban_board_column').annotate(
                    max_ordering=Max('ordering', output_field=FloatField()))

        # convert max_ordering_qs into a dict, where the kanban_board_column is the primary key and the index field
        max_ordering = {}

        for result in max_ordering_qs:
            max_ordering[str(
                result['kanban_board_column'])] = result['max_ordering']

        # now fill in the max ordering
        for assignment in self.request.data:
            column_id = assignment['kanban_board_column']

            if column_id not in max_ordering:
                # column_id not found in our max_ordering dict, setting value to 0
                max_ordering[column_id] = 0
            else:
                # found! increase by 1 for the new assignment
                max_ordering[column_id] += 1

            assignment['ordering'] = max_ordering[column_id]

        serializer = self.get_serializer(data=self.request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)

        # return the QUERY/LIST api call
        from django_changeset.models import RevisionModelMixin

        # temporarily disable revision model mixin for the list api call
        RevisionModelMixin.set_enabled(False)
        response = self.list(self.request, *args, **kwargs)
        RevisionModelMixin.set_enabled(True)

        return response