Skip to content

Commit

Permalink
Add copy_set function to ProjectValueViewSet
Browse files Browse the repository at this point in the history
  • Loading branch information
jochenklar committed Aug 22, 2024
1 parent 1e97f24 commit c0f8cec
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 41 deletions.
25 changes: 14 additions & 11 deletions rdmo/projects/assets/js/interview/actions/interviewActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ export function deleteSetError(errors) {
return {type: DELETE_SET_ERROR, errors}
}

export function copySet(currentSet, attrs) {
export function copySet(currentSet, currentSetValue, attrs) {
const pendingId = `copySet/${currentSet.set_prefix}/${currentSet.set_index}`

return (dispatch, getState) => {
Expand Down Expand Up @@ -592,18 +592,19 @@ export function copySet(currentSet, attrs) {
return dispatch({type: COPY_SET_SUCCESS, values, sets})
}

let promise
if (isNil(value)) {
// gather all values for the currentSet and it's descendants
const currentValues = getDescendants(getState().interview.values, currentSet)

// store each value in currentSet with the new set_index
return Promise.all(
promise = Promise.all(
currentValues.filter((currentValue) => !isEmptyValue(currentValue)).map((currentValue) => {
const value = {...currentValue}
const setPrefixLength = set.set_prefix.split('|').length

if (value.set_prefix == set.set_prefix) {
value.set_index == set.set_index
value.set_index = set.set_index
} else {
value.set_prefix = value.set_prefix.split('|').reduce((acc, cur, idx) => {
return [...acc, (idx == setPrefixLength - 1) ? set.set_index : cur]
Expand All @@ -613,16 +614,18 @@ export function copySet(currentSet, attrs) {
delete value.id
return ValueApi.storeValue(projectId, value)
})
).then((values) => {
dispatch(removeFromPending(pendingId))
dispatch(copySetCallback(values))
}).catch((errors) => {
dispatch(removeFromPending(pendingId))
dispatch(copySetError(errors))
})
)
} else {
console.log(value)
promise = ValueApi.copySet(projectId, currentSetValue, value)
}

return promise.then((values) => {
dispatch(removeFromPending(pendingId))
dispatch(copySetCallback(values))
}).catch((errors) => {
dispatch(removeFromPending(pendingId))
dispatch(copySetError(errors))
})
}
}

Expand Down
8 changes: 6 additions & 2 deletions rdmo/projects/assets/js/interview/api/ValueApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ class ValueApi extends BaseApi {
}
}

static deleteSet(projectId, value) {
return this.delete(`/api/v1/projects/projects/${projectId}/values/${value.id}/set/`)
static copySet(projectId, currentSetValue, setValue) {
return this.post(`/api/v1/projects/projects/${projectId}/values/${currentSetValue.id}/set/`, setValue)
}

static deleteSet(projectId, setValue) {
return this.delete(`/api/v1/projects/projects/${projectId}/values/${setValue.id}/set/`)
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const PageHead = ({ templates, page, sets, values, currentSet,
}

const handleCopySet = (text) => {
copySet(currentSet, {
copySet(currentSet, currentSetValue, {
attribute: page.attribute,
set_index: last(sets) ? last(sets).set_index + 1 : 0,
set_collection: page.is_collection,
Expand Down
23 changes: 23 additions & 0 deletions rdmo/projects/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,29 @@ def exclude_empty(self):
def distinct_list(self):
return self.order_by('attribute').values_list('attribute', 'set_prefix', 'set_index').distinct()

def filter_set(self, set_value):
# get the catalog and prefetch most elements of the catalog
catalog = set_value.project.catalog
catalog.prefetch_elements()

# collect the attributes of all questions of all pages or questionsets
# of this catalog, which have the attribute of this value
attributes = set()
elements = catalog.pages + catalog.questions
for element in elements:
if element.attribute == set_value.attribute:
attributes.update([descendant.attribute for descendant in element.descendants])

# construct the set_prefix for decendants for this set
decendants_set_prefix = \
f'{set_value.set_prefix}|{set_value.set_index}' if set_value.set_prefix else str(set_value.set_index)

# collect all values for this set and all decendants
return self.filter(attribute__in=attributes).filter(
Q(set_prefix=set_value.set_prefix, set_index=set_value.set_index) |
Q(set_prefix__startswith=decendants_set_prefix)
)


class ProjectManager(CurrentSiteManagerMixin, TreeManager):

Expand Down
80 changes: 53 additions & 27 deletions rdmo/projects/viewsets.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.http import Http404, HttpResponseRedirect
from django.utils.translation import gettext_lazy as _

from rest_framework import serializers
from rest_framework import serializers, status
from rest_framework.decorators import action
from rest_framework.exceptions import NotFound
from rest_framework.exceptions import MethodNotAllowed, NotFound
from rest_framework.mixins import CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from rest_framework.response import Response
from rest_framework.reverse import reverse
Expand Down Expand Up @@ -376,34 +375,61 @@ def get_queryset(self):
# this is needed for the swagger ui
return Value.objects.none()

@action(detail=True, methods=['DELETE'],
@action(detail=True, methods=['POST', 'DELETE'], url_path='set',
permission_classes=(HasModelPermission | HasProjectPermission, ))
def set(self, request, parent_lookup_project, pk=None):
if request.method == 'POST':
return self.copy_set(request, parent_lookup_project, pk)
elif request.method == 'DELETE':
return self.delete_set(request, parent_lookup_project, pk)
else:
raise MethodNotAllowed

def copy_set(self, request, parent_lookup_project, pk=None):
# copy all values for questions in questionset collections with the attribute
# for this value and the same set_prefix and set_index
currentValue = self.get_object()

# collect all values for this set and all decendants
currentValues = self.get_queryset().filter_set(currentValue)

# de-serialize the posted new set value and save it, use the ValueSerializer
# instead of ProjectValueSerializer, since the latter does not include project
set_value_serializer = ValueSerializer(data=request.data)
set_value_serializer.is_valid(raise_exception=True)
set_value = set_value_serializer.save()
set_value_data = set_value_serializer.data

# create new values for the new set
values = []
set_prefix_length = len(set_value.set_prefix.split('|'))
for value in currentValues:
value.id = None
if value.set_prefix == set_value.set_prefix:
value.set_index = set_value.set_index
else:
value.set_prefix = '|'.join([
str(set_value.set_index) if (index == set_prefix_length - 1) else value
for index, value in enumerate(value.set_prefix.split('|'))
])
values.append(value)

# bulk create the new values
values = Value.objects.bulk_create(values)
values_data = [ValueSerializer(instance=value).data for value in values]

# return all new values
headers = self.get_success_headers(set_value_serializer.data)
return Response([set_value_data, *values_data], status=status.HTTP_201_CREATED, headers=headers)

def delete_set(self, request, parent_lookup_project, pk=None):
# delete all values for questions in questionset collections with the attribute
# for this value and the same set_prefix and set_index
value = self.get_object()
value.delete()

# prefetch most elements of the catalog
self.project.catalog.prefetch_elements()

# collect the attributes of all questions of all pages or questionsets
# of this catalog, which have the attribute of this value
attributes = set()
elements = self.project.catalog.pages + self.project.catalog.questions
for element in elements:
if element.attribute == value.attribute:
attributes.update([descendant.attribute for descendant in element.descendants])

# construct the set_prefix for decendants for this set
decendants_set_prefix = f'{value.set_prefix}|{value.set_index}' if value.set_prefix else str(value.set_index)

# delete all values for this set and all decendants
values = self.get_queryset().filter(attribute__in=attributes) \
.filter(
Q(set_prefix=value.set_prefix, set_index=value.set_index) |
Q(set_prefix__startswith=decendants_set_prefix)
)
set_value = self.get_object()
set_value.delete()

# collect all values for this set and all decendants and delete them
values = self.get_queryset().filter_set(set_value)
values.delete()

return Response(status=204)
Expand Down

0 comments on commit c0f8cec

Please sign in to comment.