示例#1
0
class ProcessViewSet(
    ResolweCreateModelMixin,
    mixins.RetrieveModelMixin,
    mixins.ListModelMixin,
    ResolwePermissionsMixin,
    ResolweCheckSlugMixin,
    viewsets.GenericViewSet,
):
    """API view for :class:`Process` objects."""

    qs_permission_model = PermissionModel.objects.select_related("user", "group")
    queryset = Process.objects.all().select_related("contributor")
    serializer_class = ProcessSerializer
    permission_classes = (get_permissions_class(),)
    filterset_class = ProcessFilter
    ordering_fields = ("id", "created", "modified", "name", "version")
    ordering = ("id",)

    def create(self, request, *args, **kwargs):
        """Only superusers can create new processes."""
        if not request.user.is_superuser:
            raise exceptions.NotFound

        return super().create(request, *args, **kwargs)

    def get_queryset(self):
        """Prefetch permissions for current user."""
        return self.prefetch_current_user_permissions(self.queryset)
示例#2
0
class DescriptorSchemaViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin,
                              ResolwePermissionsMixin,
                              viewsets.GenericViewSet):
    """API view for :class:`DescriptorSchema` objects."""

    queryset = DescriptorSchema.objects.all().prefetch_related('contributor')
    serializer_class = DescriptorSchemaSerializer
    permission_classes = (get_permissions_class(), )
    filter_class = DescriptorSchemaFilter
    ordering_fields = ('id', 'created', 'modified', 'name', 'version')
    ordering = ('id', )
示例#3
0
class DescriptorSchemaViewSet(
    mixins.RetrieveModelMixin,
    mixins.ListModelMixin,
    ResolwePermissionsMixin,
    viewsets.GenericViewSet,
):
    """API view for :class:`DescriptorSchema` objects."""

    queryset = DescriptorSchema.objects.all().select_related("contributor")
    serializer_class = DescriptorSchemaSerializer
    permission_classes = (get_permissions_class(),)
    filterset_class = DescriptorSchemaFilter
    ordering_fields = ("id", "created", "modified", "name", "version")
    ordering = ("id",)
示例#4
0
文件: process.py 项目: tjanez/resolwe
class ProcessViewSet(ResolweCreateModelMixin, mixins.RetrieveModelMixin,
                     mixins.ListModelMixin, ResolwePermissionsMixin,
                     ResolweCheckSlugMixin, viewsets.GenericViewSet):
    """API view for :class:`Process` objects."""

    queryset = Process.objects.all().prefetch_related('contributor')
    serializer_class = ProcessSerializer
    permission_classes = (get_permissions_class(), )
    filter_class = ProcessFilter
    ordering_fields = ('id', 'created', 'modified', 'name', 'version')
    ordering = ('id', )

    def create(self, request, *args, **kwargs):
        """Only superusers can create new processes."""
        if not request.user.is_superuser:
            raise exceptions.NotFound

        return super().create(request, *args, **kwargs)
示例#5
0
文件: data.py 项目: tjanez/resolwe
class DataViewSet(ElasticSearchCombinedViewSet, ResolweCreateModelMixin,
                  mixins.RetrieveModelMixin, ResolweUpdateModelMixin,
                  mixins.DestroyModelMixin, ResolwePermissionsMixin,
                  ResolweCheckSlugMixin, viewsets.GenericViewSet):
    """API view for :class:`Data` objects."""

    queryset = Data.objects.all().prefetch_related('process',
                                                   'descriptor_schema',
                                                   'contributor')
    serializer_class = DataSerializer
    permission_classes = (get_permissions_class(), )
    document_class = DataDocument

    filtering_fields = ('id', 'slug', 'version', 'name', 'created', 'modified',
                        'contributor', 'owners', 'status', 'process',
                        'process_type', 'type', 'process_name', 'tags',
                        'collection', 'parents', 'children', 'entity',
                        'started', 'finished', 'text')
    filtering_map = {
        'name': 'name.ngrams',
        'contributor': 'contributor_id',
        'owners': 'owner_ids',
        'process_name': 'process_name.ngrams',
    }
    ordering_fields = ('id', 'created', 'modified', 'started', 'finished',
                       'name', 'contributor', 'process_name', 'process_type',
                       'type')
    ordering_map = {
        'name': 'name.raw',
        'process_type': 'process_type.raw',
        'type': 'type.raw',
        'process_name': 'process_name.raw',
        'contributor': 'contributor_sort',
    }
    ordering = '-created'

    def get_always_allowed_arguments(self):
        """Return query arguments which are always allowed."""
        return super().get_always_allowed_arguments() + [
            'hydrate_data',
            'hydrate_collections',
            'hydrate_entities',
        ]

    def custom_filter_tags(self, value, search):
        """Support tags query."""
        if not isinstance(value, list):
            value = value.split(',')

        filters = [Q('match', **{'tags': item}) for item in value]
        search = search.query('bool', must=filters)

        return search

    def custom_filter_text(self, value, search):
        """Support general query using the 'text' attribute."""
        if isinstance(value, list):
            value = ' '.join(value)

        should = [
            Q('match', slug={
                'query': value,
                'operator': 'and',
                'boost': 10.0
            }),
            Q(
                'match', **{
                    'slug.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 5.0
                    }
                }),
            Q('match', name={
                'query': value,
                'operator': 'and',
                'boost': 10.0
            }),
            Q(
                'match', **{
                    'name.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 5.0
                    }
                }),
            Q('match',
              contributor_name={
                  'query': value,
                  'operator': 'and',
                  'boost': 5.0
              }),
            Q(
                'match', **{
                    'contributor_name.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 2.0
                    }
                }),
            Q('match',
              owner_names={
                  'query': value,
                  'operator': 'and',
                  'boost': 5.0
              }),
            Q(
                'match', **{
                    'owner_names.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 2.0
                    }
                }),
            Q('match',
              process_name={
                  'query': value,
                  'operator': 'and',
                  'boost': 5.0
              }),
            Q(
                'match', **{
                    'process_name.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 2.0
                    }
                }),
            Q('match',
              status={
                  'query': value,
                  'operator': 'and',
                  'boost': 2.0
              }),
            Q('match', type={
                'query': value,
                'operator': 'and',
                'boost': 2.0
            }),
        ]

        # Add registered text extensions.
        for extension in composer.get_extensions(self):
            if hasattr(extension, 'text_filter'):
                should += extension.text_filter(value)

        search = search.query('bool', should=should)

        return search

    def create(self, request, *args, **kwargs):
        """Create a resource."""
        collections = request.data.get('collections', [])

        # check that user has permissions on all collections that Data
        # object will be added to
        for collection_id in collections:
            try:
                collection = Collection.objects.get(pk=collection_id)
            except Collection.DoesNotExist:
                return Response(
                    {
                        'collections': [
                            'Invalid pk "{}" - object does not exist.'.format(
                                collection_id)
                        ]
                    },
                    status=status.HTTP_400_BAD_REQUEST)

            if not request.user.has_perm('add_collection', obj=collection):
                if request.user.has_perm('view_collection', obj=collection):
                    raise exceptions.PermissionDenied(
                        "You don't have `ADD` permission on collection (id: {})."
                        .format(collection_id))
                else:
                    raise exceptions.NotFound(
                        "Collection not found (id: {}).".format(collection_id))

        self.define_contributor(request)

        if kwargs.pop('get_or_create', False):
            response = self.perform_get_or_create(request, *args, **kwargs)
            if response:
                return response

        return super().create(request, *args, **kwargs)

    @list_route(methods=['post'])
    def get_or_create(self, request, *args, **kwargs):
        """Get ``Data`` object if similar already exists, otherwise create it."""
        kwargs['get_or_create'] = True
        return self.create(request, *args, **kwargs)

    def perform_get_or_create(self, request, *args, **kwargs):
        """Perform "get_or_create" - return existing object if found."""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        process = serializer.validated_data.get('process')
        process_input = request.data.get('input', {})

        fill_with_defaults(process_input, process.input_schema)

        checksum = get_data_checksum(process_input, process.slug,
                                     process.version)
        data_qs = Data.objects.filter(
            checksum=checksum,
            process__persistence__in=[
                Process.PERSISTENCE_CACHED, Process.PERSISTENCE_TEMP
            ],
        )
        data_qs = get_objects_for_user(request.user, 'view_data', data_qs)
        if data_qs.exists():
            data = data_qs.order_by('created').last()
            serializer = self.get_serializer(data)
            return Response(serializer.data)

    def perform_create(self, serializer):
        """Create a resource."""
        process = serializer.validated_data.get('process')
        if not process.is_active:
            raise exceptions.ParseError(
                'Process retired (id: {}, slug: {}/{}).'.format(
                    process.id, process.slug, process.version))

        with transaction.atomic():
            instance = serializer.save()

            assign_contributor_permissions(instance)

            # Entity is added to the collection only when it is
            # created - when it only contains 1 Data object.
            entities = Entity.objects.annotate(num_data=Count('data')).filter(
                data=instance, num_data=1)

            # Assign data object to all specified collections.
            collection_pks = self.request.data.get('collections', [])
            for collection in Collection.objects.filter(pk__in=collection_pks):
                collection.data.add(instance)
                copy_permissions(collection, instance)

                # Add entities to which data belongs to the collection.
                for entity in entities:
                    entity.collections.add(collection)
                    copy_permissions(collection, entity)
示例#6
0
class DataViewSet(ResolweCreateModelMixin, mixins.RetrieveModelMixin,
                  ResolweUpdateModelMixin, mixins.DestroyModelMixin,
                  mixins.ListModelMixin, ResolwePermissionsMixin,
                  ResolweCheckSlugMixin, viewsets.GenericViewSet):
    """API view for :class:`Data` objects."""

    queryset = Data.objects.all().prefetch_related('process',
                                                   'descriptor_schema',
                                                   'contributor')
    serializer_class = DataSerializer
    permission_classes = (get_permissions_class(), )
    filter_class = DataFilter
    ordering_fields = ('id', 'created', 'modified', 'started', 'finished',
                       'name')
    ordering = ('id', )

    def create(self, request, *args, **kwargs):
        """Create a resource."""
        collections = request.data.get('collections', [])

        # check that user has permissions on all collections that Data
        # object will be added to
        for collection_id in collections:
            try:
                collection = Collection.objects.get(pk=collection_id)
            except Collection.DoesNotExist:
                return Response(
                    {
                        'collections': [
                            'Invalid pk "{}" - object does not exist.'.format(
                                collection_id)
                        ]
                    },
                    status=status.HTTP_400_BAD_REQUEST)

            if not request.user.has_perm('add_collection', obj=collection):
                if request.user.has_perm('view_collection', obj=collection):
                    raise exceptions.PermissionDenied(
                        "You don't have `ADD` permission on collection (id: {})."
                        .format(collection_id))
                else:
                    raise exceptions.NotFound(
                        "Collection not found (id: {}).".format(collection_id))

        # translate processe's slug to id
        process_slug = request.data.get('process', None)
        process_query = Process.objects.filter(slug=process_slug)
        process_query = get_objects_for_user(request.user, 'view_process',
                                             process_query)
        try:
            process = process_query.latest()
        except Process.DoesNotExist:
            return Response(
                {
                    'process': [
                        'Invalid process slug "{}" - object does not exist.'.
                        format(process_slug)
                    ]
                },
                status=status.HTTP_400_BAD_REQUEST)
        request.data['process'] = process.pk

        # perform "get_or_create" if requested - return existing object
        # if found
        if kwargs.pop('get_or_create', False):
            process_input = request.data.get('input', {})

            # use default values if they are not given
            for field_schema, fields, path in iterate_schema(
                    process_input, process.input_schema):
                if 'default' in field_schema and field_schema[
                        'name'] not in fields:
                    dict_dot(process_input, path, field_schema['default'])

            checksum = get_data_checksum(process_input, process.slug,
                                         process.version)
            data_qs = Data.objects.filter(
                checksum=checksum,
                process__persistence__in=[
                    Process.PERSISTENCE_CACHED, Process.PERSISTENCE_TEMP
                ],
            )
            data_qs = get_objects_for_user(request.user, 'view_data', data_qs)
            if data_qs.exists():
                data = data_qs.order_by('created').last()
                serializer = self.get_serializer(data)
                return Response(serializer.data)

        # create the objects
        resp = super(DataViewSet, self).create(request, *args, **kwargs)

        # run manager
        manager.communicate()

        return resp

    @list_route(methods=[u'post'])
    def get_or_create(self, request, *args, **kwargs):
        """Get ``Data`` object if similar already exists, otherwise create it."""
        kwargs['get_or_create'] = True
        return self.create(request, *args, **kwargs)

    def perform_create(self, serializer):
        """Create a resource."""
        with transaction.atomic():
            instance = serializer.save()

            assign_contributor_permissions(instance)

            # Entity is added to the collection only when it is
            # created - when it only contains 1 Data object.
            entities = Entity.objects.annotate(num_data=Count('data')).filter(
                data=instance, num_data=1)

            # Assign data object to all specified collections.
            collection_pks = self.request.data.get('collections', [])
            for collection in Collection.objects.filter(pk__in=collection_pks):
                collection.data.add(instance)
                copy_permissions(collection, instance)

                # Add entities to which data belongs to the collection.
                for entity in entities:
                    entity.collections.add(collection)
                    copy_permissions(collection, entity)
示例#7
0
class DataViewSet(ElasticSearchCombinedViewSet, ResolweCreateModelMixin,
                  mixins.RetrieveModelMixin, ResolweUpdateModelMixin,
                  mixins.DestroyModelMixin, ResolwePermissionsMixin,
                  ResolweCheckSlugMixin, ParametersMixin,
                  viewsets.GenericViewSet):
    """API view for :class:`Data` objects."""

    queryset = Data.objects.all().prefetch_related('process',
                                                   'descriptor_schema',
                                                   'contributor', 'collection',
                                                   'entity')
    serializer_class = DataSerializer
    permission_classes = (get_permissions_class(), )
    document_class = DataDocument

    filtering_fields = ('id', 'slug', 'version', 'name', 'created', 'modified',
                        'contributor', 'owners', 'status', 'process',
                        'process_type', 'type', 'process_name', 'tags',
                        'collection', 'entity', 'started', 'finished', 'text')
    filtering_map = {
        'name': 'name.raw',
        'contributor': 'contributor_id',
        'owners': 'owner_ids',
        'process_name': 'process_name.ngrams',
    }
    ordering_fields = ('id', 'created', 'modified', 'started', 'finished',
                       'name', 'contributor', 'process_name', 'process_type',
                       'type')
    ordering_map = {
        'name': 'name.raw',
        'process_type': 'process_type.raw',
        'type': 'type.raw',
        'process_name': 'process_name.raw',
        'contributor': 'contributor_sort',
    }
    ordering = '-created'

    def custom_filter_tags(self, value, search):
        """Support tags query."""
        if not isinstance(value, list):
            value = value.split(',')

        filters = [Q('match', **{'tags': item}) for item in value]
        search = search.query('bool', must=filters)

        return search

    def custom_filter_text(self, value, search):
        """Support general query using the 'text' attribute."""
        if isinstance(value, list):
            value = ' '.join(value)

        should = [
            Q('match', slug={
                'query': value,
                'operator': 'and',
                'boost': 10.0
            }),
            Q(
                'match', **{
                    'slug.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 5.0
                    }
                }),
            Q('match', name={
                'query': value,
                'operator': 'and',
                'boost': 10.0
            }),
            Q(
                'match', **{
                    'name.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 5.0
                    }
                }),
            Q('match',
              contributor_name={
                  'query': value,
                  'operator': 'and',
                  'boost': 5.0
              }),
            Q(
                'match', **{
                    'contributor_name.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 2.0
                    }
                }),
            Q('match',
              owner_names={
                  'query': value,
                  'operator': 'and',
                  'boost': 5.0
              }),
            Q(
                'match', **{
                    'owner_names.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 2.0
                    }
                }),
            Q('match',
              process_name={
                  'query': value,
                  'operator': 'and',
                  'boost': 5.0
              }),
            Q(
                'match', **{
                    'process_name.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 2.0
                    }
                }),
            Q('match',
              status={
                  'query': value,
                  'operator': 'and',
                  'boost': 2.0
              }),
            Q('match', type={
                'query': value,
                'operator': 'and',
                'boost': 2.0
            }),
        ]

        # Add registered text extensions.
        for extension in composer.get_extensions(self):
            if hasattr(extension, 'text_filter'):
                should += extension.text_filter(value)

        search = search.query('bool', should=should)

        return search

    @action(detail=False, methods=['post'])
    def duplicate(self, request, *args, **kwargs):
        """Duplicate (make copy of) ``Data`` objects."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        ids = self.get_ids(request.data)
        queryset = get_objects_for_user(request.user, 'view_data',
                                        Data.objects.filter(id__in=ids))
        actual_ids = queryset.values_list('id', flat=True)
        missing_ids = list(set(ids) - set(actual_ids))
        if missing_ids:
            raise exceptions.ParseError(
                "Data objects with the following ids not found: {}".format(
                    ', '.join(map(str, missing_ids))))

        # TODO support ``inherit_collection``
        duplicated = queryset.duplicate(contributor=request.user)

        serializer = self.get_serializer(duplicated, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=['post'])
    def get_or_create(self, request, *args, **kwargs):
        """Get ``Data`` object if similar already exists, otherwise create it."""
        response = self.perform_get_or_create(request, *args, **kwargs)
        if response:
            return response

        return super().create(request, *args, **kwargs)

    def perform_get_or_create(self, request, *args, **kwargs):
        """Perform "get_or_create" - return existing object if found."""
        self.define_contributor(request)
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        process = serializer.validated_data.get('process')
        process_input = request.data.get('input', {})

        fill_with_defaults(process_input, process.input_schema)

        checksum = get_data_checksum(process_input, process.slug,
                                     process.version)
        data_qs = Data.objects.filter(
            checksum=checksum,
            process__persistence__in=[
                Process.PERSISTENCE_CACHED, Process.PERSISTENCE_TEMP
            ],
        )
        data_qs = get_objects_for_user(request.user, 'view_data', data_qs)
        if data_qs.exists():
            data = data_qs.order_by('created').last()
            serializer = self.get_serializer(data)
            return Response(serializer.data)

    def _parents_children(self, request, queryset):
        """Process given queryset and return serialized objects."""
        queryset = get_objects_for_user(request.user, 'view_data', queryset)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @action(detail=True)
    def parents(self, request, pk=None):
        """Return parents of the current data object."""
        return self._parents_children(request, self.get_object().parents)

    @action(detail=True)
    def children(self, request, pk=None):
        """Return children of the current data object."""
        return self._parents_children(request, self.get_object().children)
示例#8
0
class CollectionViewSet(ResolweCreateModelMixin, mixins.RetrieveModelMixin,
                        ResolweUpdateModelMixin, mixins.DestroyModelMixin,
                        mixins.ListModelMixin, ResolwePermissionsMixin,
                        ResolweCheckSlugMixin, viewsets.GenericViewSet):
    """API view for :class:`Collection` objects."""

    queryset = Collection.objects.all().prefetch_related(
        'descriptor_schema', 'contributor',
        Prefetch('data', queryset=Data.objects.all().order_by('id')))
    serializer_class = CollectionSerializer
    permission_classes = (get_permissions_class(), )
    filter_class = CollectionFilter
    ordering_fields = ('id', 'created', 'modified', 'name')
    ordering = ('id', )

    def set_content_permissions(self, user, obj, payload):
        """Apply permissions to data objects and entities in ``Collection``."""
        for entity in obj.entity_set.all():
            if user.has_perm('share_entity', entity):
                update_permission(entity, payload)

        # Data doesn't have "ADD" permission, so it has to be removed
        payload = remove_permission(payload, 'add')

        for data in obj.data.all():
            if user.has_perm('share_data', data):
                update_permission(data, payload)

    def create(self, request, *args, **kwargs):
        """Only authenticated usesr can create new collections."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        return super(CollectionViewSet, self).create(request, *args, **kwargs)

    def destroy(self, request, *args, **kwargs):
        """Destroy a model instance.

        If ``delete_content`` flag is set in query parameters, also all
        Data objects and Entities, on which user has ``EDIT``
        permission, contained in collection will be deleted.
        """
        obj = self.get_object()
        user = request.user

        if strtobool(request.query_params.get('delete_content', 'false')):
            for entity in obj.entity_set.all():
                if user.has_perm('edit_entity', entity):
                    entity.delete()

            for data in obj.data.all():
                if user.has_perm('edit_data', data):
                    data.delete()

        return super(CollectionViewSet, self).destroy(request, *args, **kwargs)  # pylint: disable=no-member

    @detail_route(methods=[u'post'])
    def add_data(self, request, pk=None):
        """Add data to collection."""
        collection = self.get_object()

        if 'ids' not in request.data:
            return Response({"error": "`ids`parameter is required"},
                            status=status.HTTP_400_BAD_REQUEST)

        missing = []
        for data_id in request.data['ids']:
            if not Data.objects.filter(pk=data_id).exists():
                missing.append(data_id)

        if missing:
            return Response(
                {
                    "error":
                    "Data objects with following ids are missing: {}".format(
                        ', '.join(missing))
                },
                status=status.HTTP_400_BAD_REQUEST)

        for data_id in request.data['ids']:
            collection.data.add(data_id)

        return Response()

    @detail_route(methods=[u'post'])
    def remove_data(self, request, pk=None):
        """Remove data from collection."""
        collection = self.get_object()

        if 'ids' not in request.data:
            return Response({"error": "`ids`parameter is required"},
                            status=status.HTTP_400_BAD_REQUEST)

        for data_id in request.data['ids']:
            collection.data.remove(data_id)

        return Response()
示例#9
0
class CollectionViewSet(
    ElasticSearchCombinedViewSet,
    ResolweCreateModelMixin,
    mixins.RetrieveModelMixin,
    ResolweUpdateModelMixin,
    mixins.DestroyModelMixin,
    mixins.ListModelMixin,
    ResolwePermissionsMixin,
    ResolweCheckSlugMixin,
    ParametersMixin,
    viewsets.GenericViewSet,
):
    """API view for :class:`Collection` objects."""

    queryset = Collection.objects.all().prefetch_related(
        "descriptor_schema", "contributor"
    )
    serializer_class = CollectionSerializer
    permission_classes = (get_permissions_class(),)
    document_class = CollectionDocument

    filtering_fields = (
        "id",
        "slug",
        "name",
        "name_contains",
        "created",
        "modified",
        "contributor",
        "contributor_name",
        "owners",
        "owners_name",
        "text",
        "tags",
    )
    filtering_map = {
        "name": "name.raw",
        "name_contains": "name.ngrams",
        "contributor": "contributor_id",
        "contributor_name": "contributor_name.ngrams",
        "owners": "owner_ids",
        "owners_name": "owner_names.ngrams",
    }
    ordering_fields = ("id", "created", "modified", "name", "contributor")
    ordering_map = {
        "name": "name.raw",
        "contributor": "contributor_sort",
    }
    ordering = "id"

    def custom_filter_tags(self, value, search):
        """Support tags query."""
        if not isinstance(value, list):
            value = value.split(",")

        filters = [Q("match", **{"tags": item}) for item in value]
        search = search.query("bool", must=filters)

        return search

    def custom_filter_text(self, value, search):
        """Support general query using the 'text' attribute."""
        if isinstance(value, list):
            value = " ".join(value)

        should = [
            Q("match", slug={"query": value, "operator": "and", "boost": 10.0}),
            Q(
                "match",
                **{"slug.ngrams": {"query": value, "operator": "and", "boost": 5.0}},
            ),
            Q("match", name={"query": value, "operator": "and", "boost": 10.0}),
            Q(
                "match",
                **{"name.ngrams": {"query": value, "operator": "and", "boost": 5.0}},
            ),
            Q(
                "match",
                contributor_name={"query": value, "operator": "and", "boost": 5.0},
            ),
            Q(
                "match",
                **{
                    "contributor_name.ngrams": {
                        "query": value,
                        "operator": "and",
                        "boost": 2.0,
                    }
                },
            ),
            Q("match", owner_names={"query": value, "operator": "and", "boost": 5.0}),
            Q(
                "match",
                **{
                    "owner_names.ngrams": {
                        "query": value,
                        "operator": "and",
                        "boost": 2.0,
                    }
                },
            ),
            Q("match", descriptor_data={"query": value, "operator": "and"}),
            Q("match", description={"query": value, "operator": "and"}),
        ]

        # Add registered text extensions.
        for extension in composer.get_extensions(self):
            if hasattr(extension, "text_filter"):
                should += extension.text_filter(value)

        search = search.query("bool", should=should)

        return search

    def set_content_permissions(self, user, obj, payload):
        """Apply permissions to data objects and entities in ``Collection``."""
        for entity in obj.entity_set.all():
            if user.has_perm("share_entity", entity):
                update_permission(entity, payload)

        for data in obj.data.all():
            if user.has_perm("share_data", data):
                update_permission(data, payload)

    def create(self, request, *args, **kwargs):
        """Only authenticated users can create new collections."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        return super().create(request, *args, **kwargs)

    @action(detail=False, methods=["post"])
    def duplicate(self, request, *args, **kwargs):
        """Duplicate (make copy of) ``Collection`` models."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        ids = self.get_ids(request.data)
        queryset = get_objects_for_user(
            request.user, "view_collection", Collection.objects.filter(id__in=ids)
        )
        actual_ids = queryset.values_list("id", flat=True)
        missing_ids = list(set(ids) - set(actual_ids))
        if missing_ids:
            raise exceptions.ParseError(
                "Collections with the following ids not found: {}".format(
                    ", ".join(map(str, missing_ids))
                )
            )

        duplicated = queryset.duplicate(contributor=request.user)

        serializer = self.get_serializer(duplicated, many=True)
        return Response(serializer.data)
示例#10
0
class DataViewSet(
        ElasticSearchCombinedViewSet,
        ResolweCreateModelMixin,
        mixins.RetrieveModelMixin,
        ResolweUpdateModelMixin,
        mixins.DestroyModelMixin,
        ResolwePermissionsMixin,
        ResolweCheckSlugMixin,
        ParametersMixin,
        viewsets.GenericViewSet,
):
    """API view for :class:`Data` objects."""

    queryset = Data.objects.all().prefetch_related("process",
                                                   "descriptor_schema",
                                                   "contributor", "collection",
                                                   "entity")
    serializer_class = DataSerializer
    permission_classes = (get_permissions_class(), )
    document_class = DataDocument

    filtering_fields = (
        "id",
        "slug",
        "version",
        "name",
        "created",
        "modified",
        "contributor",
        "owners",
        "status",
        "process",
        "process_type",
        "type",
        "process_name",
        "tags",
        "collection",
        "entity",
        "started",
        "finished",
        "text",
        "process_slug",
    )
    filtering_map = {
        "name": "name.raw",
        "contributor": "contributor_id",
        "owners": "owner_ids",
        "process_name": "process_name.ngrams",
    }
    ordering_fields = (
        "id",
        "created",
        "modified",
        "started",
        "finished",
        "name",
        "contributor",
        "process_name",
        "process_type",
        "type",
    )
    ordering_map = {
        "name": "name.raw",
        "process_type": "process_type.raw",
        "type": "type.raw",
        "process_name": "process_name.raw",
        "contributor": "contributor_sort",
    }
    ordering = "-created"

    def custom_filter_tags(self, value, search):
        """Support tags query."""
        if not isinstance(value, list):
            value = value.split(",")

        filters = [Q("match", **{"tags": item}) for item in value]
        search = search.query("bool", must=filters)

        return search

    def custom_filter_text(self, value, search):
        """Support general query using the 'text' attribute."""
        if isinstance(value, list):
            value = " ".join(value)

        should = [
            Q("match", slug={
                "query": value,
                "operator": "and",
                "boost": 10.0
            }),
            Q(
                "match",
                **{
                    "slug.ngrams": {
                        "query": value,
                        "operator": "and",
                        "boost": 5.0
                    }
                },
            ),
            Q("match", name={
                "query": value,
                "operator": "and",
                "boost": 10.0
            }),
            Q(
                "match",
                **{
                    "name.ngrams": {
                        "query": value,
                        "operator": "and",
                        "boost": 5.0
                    }
                },
            ),
            Q(
                "match",
                contributor_name={
                    "query": value,
                    "operator": "and",
                    "boost": 5.0
                },
            ),
            Q(
                "match",
                **{
                    "contributor_name.ngrams": {
                        "query": value,
                        "operator": "and",
                        "boost": 2.0,
                    }
                },
            ),
            Q("match",
              owner_names={
                  "query": value,
                  "operator": "and",
                  "boost": 5.0
              }),
            Q(
                "match",
                **{
                    "owner_names.ngrams": {
                        "query": value,
                        "operator": "and",
                        "boost": 2.0,
                    }
                },
            ),
            Q("match",
              process_name={
                  "query": value,
                  "operator": "and",
                  "boost": 5.0
              }),
            Q(
                "match",
                **{
                    "process_name.ngrams": {
                        "query": value,
                        "operator": "and",
                        "boost": 2.0,
                    }
                },
            ),
            Q("match",
              status={
                  "query": value,
                  "operator": "and",
                  "boost": 2.0
              }),
            Q("match", type={
                "query": value,
                "operator": "and",
                "boost": 2.0
            }),
        ]

        # Add registered text extensions.
        for extension in composer.get_extensions(self):
            if hasattr(extension, "text_filter"):
                should += extension.text_filter(value)

        search = search.query("bool", should=should)

        return search

    @action(detail=False, methods=["post"])
    def duplicate(self, request, *args, **kwargs):
        """Duplicate (make copy of) ``Data`` objects."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        ids = self.get_ids(request.data)
        queryset = get_objects_for_user(request.user, "view_data",
                                        Data.objects.filter(id__in=ids))
        actual_ids = queryset.values_list("id", flat=True)
        missing_ids = list(set(ids) - set(actual_ids))
        if missing_ids:
            raise exceptions.ParseError(
                "Data objects with the following ids not found: {}".format(
                    ", ".join(map(str, missing_ids))))

        # TODO support ``inherit_collection``
        duplicated = queryset.duplicate(contributor=request.user)

        serializer = self.get_serializer(duplicated, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=["post"])
    def get_or_create(self, request, *args, **kwargs):
        """Get ``Data`` object if similar already exists, otherwise create it."""
        response = self.perform_get_or_create(request, *args, **kwargs)
        if response:
            return response

        return super().create(request, *args, **kwargs)

    def perform_get_or_create(self, request, *args, **kwargs):
        """Perform "get_or_create" - return existing object if found."""
        self.define_contributor(request)
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        process = serializer.validated_data.get("process")
        process_input = request.data.get("input", {})

        fill_with_defaults(process_input, process.input_schema)

        checksum = get_data_checksum(process_input, process.slug,
                                     process.version)
        data_qs = Data.objects.filter(
            checksum=checksum,
            process__persistence__in=[
                Process.PERSISTENCE_CACHED,
                Process.PERSISTENCE_TEMP,
            ],
        )
        data_qs = get_objects_for_user(request.user, "view_data", data_qs)
        if data_qs.exists():
            data = data_qs.order_by("created").last()
            serializer = self.get_serializer(data)
            return Response(serializer.data)

    def _parents_children(self, request, queryset):
        """Process given queryset and return serialized objects."""
        queryset = get_objects_for_user(request.user, "view_data", queryset)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @action(detail=True)
    def parents(self, request, pk=None):
        """Return parents of the current data object."""
        return self._parents_children(request, self.get_object().parents)

    @action(detail=True)
    def children(self, request, pk=None):
        """Return children of the current data object."""
        return self._parents_children(request, self.get_object().children)
示例#11
0
class CollectionViewSet(ElasticSearchCombinedViewSet,
                        ResolweCreateModelMixin,
                        mixins.RetrieveModelMixin,
                        ResolweUpdateModelMixin,
                        mixins.DestroyModelMixin,
                        mixins.ListModelMixin,
                        ResolwePermissionsMixin,
                        ResolweCheckSlugMixin,
                        ParametersMixin,
                        viewsets.GenericViewSet):
    """API view for :class:`Collection` objects."""

    queryset = Collection.objects.all().prefetch_related('descriptor_schema', 'contributor')
    serializer_class = CollectionSerializer
    permission_classes = (get_permissions_class(),)
    document_class = CollectionDocument

    filtering_fields = (
        'id', 'slug', 'name', 'created', 'modified', 'contributor', 'owners', 'text', 'tags',
    )
    filtering_map = {
        'name': 'name.raw',
        'contributor': 'contributor_id',
        'owners': 'owner_ids',
    }
    ordering_fields = ('id', 'created', 'modified', 'name', 'contributor')
    ordering_map = {
        'name': 'name.raw',
        'contributor': 'contributor_sort',
    }
    ordering = 'id'

    def custom_filter_tags(self, value, search):
        """Support tags query."""
        if not isinstance(value, list):
            value = value.split(',')

        filters = [Q('match', **{'tags': item}) for item in value]
        search = search.query('bool', must=filters)

        return search

    def custom_filter_text(self, value, search):
        """Support general query using the 'text' attribute."""
        if isinstance(value, list):
            value = ' '.join(value)

        should = [
            Q('match', slug={'query': value, 'operator': 'and', 'boost': 10.0}),
            Q('match', **{'slug.ngrams': {'query': value, 'operator': 'and', 'boost': 5.0}}),
            Q('match', name={'query': value, 'operator': 'and', 'boost': 10.0}),
            Q('match', **{'name.ngrams': {'query': value, 'operator': 'and', 'boost': 5.0}}),
            Q('match', contributor_name={'query': value, 'operator': 'and', 'boost': 5.0}),
            Q('match', **{'contributor_name.ngrams': {'query': value, 'operator': 'and', 'boost': 2.0}}),
            Q('match', owner_names={'query': value, 'operator': 'and', 'boost': 5.0}),
            Q('match', **{'owner_names.ngrams': {'query': value, 'operator': 'and', 'boost': 2.0}}),
            Q('match', descriptor_data={'query': value, 'operator': 'and'}),
        ]

        # Add registered text extensions.
        for extension in composer.get_extensions(self):
            if hasattr(extension, 'text_filter'):
                should += extension.text_filter(value)

        search = search.query('bool', should=should)

        return search

    def set_content_permissions(self, user, obj, payload):
        """Apply permissions to data objects and entities in ``Collection``."""
        for entity in obj.entity_set.all():
            if user.has_perm('share_entity', entity):
                update_permission(entity, payload)

        # Data doesn't have "ADD" permission, so it has to be removed
        payload = remove_permission(payload, 'add')

        for data in obj.data.all():
            if user.has_perm('share_data', data):
                update_permission(data, payload)

    def create(self, request, *args, **kwargs):
        """Only authenticated users can create new collections."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        return super().create(request, *args, **kwargs)

    @action(detail=False, methods=['post'])
    def duplicate(self, request, *args, **kwargs):
        """Duplicate (make copy of) ``Collection`` models."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        ids = self.get_ids(request.data)
        queryset = get_objects_for_user(request.user, 'view_collection', Collection.objects.filter(id__in=ids))
        actual_ids = queryset.values_list('id', flat=True)
        missing_ids = list(set(ids) - set(actual_ids))
        if missing_ids:
            raise exceptions.ParseError(
                "Collections with the following ids not found: {}".format(', '.join(map(str, missing_ids)))
            )

        duplicated = queryset.duplicate(contributor=request.user)

        serializer = self.get_serializer(duplicated, many=True)
        return Response(serializer.data)
示例#12
0
class DataViewSet(
        ResolweCreateModelMixin,
        mixins.ListModelMixin,
        mixins.RetrieveModelMixin,
        ResolweUpdateModelMixin,
        mixins.DestroyModelMixin,
        ResolwePermissionsMixin,
        ResolweCheckSlugMixin,
        ParametersMixin,
        viewsets.GenericViewSet,
):
    """API view for :class:`Data` objects."""

    qs_collection_ds = DescriptorSchema.objects.select_related("contributor")
    qs_collection = Collection.objects.select_related("contributor")
    qs_collection = qs_collection.prefetch_related(
        "data",
        "entity_set",
        Prefetch("descriptor_schema", queryset=qs_collection_ds),
    )

    qs_descriptor_schema = DescriptorSchema.objects.select_related(
        "contributor")

    qs_entity_col_ds = DescriptorSchema.objects.select_related("contributor")
    qs_entity_col = Collection.objects.select_related("contributor")
    qs_entity_col = qs_entity_col.prefetch_related(
        "data",
        "entity_set",
        Prefetch("descriptor_schema", queryset=qs_entity_col_ds),
    )
    qs_entity_ds = DescriptorSchema.objects.select_related("contributor")
    qs_entity = Entity.objects.select_related("contributor")
    qs_entity = qs_entity.prefetch_related(
        "data",
        Prefetch("collection", queryset=qs_entity_col),
        Prefetch("descriptor_schema", queryset=qs_entity_ds),
    )

    qs_process = Process.objects.select_related("contributor")

    queryset = Data.objects.select_related("contributor").prefetch_related(
        Prefetch("collection", queryset=qs_collection),
        Prefetch("descriptor_schema", queryset=qs_descriptor_schema),
        Prefetch("entity", queryset=qs_entity),
        Prefetch("process", queryset=qs_process),
    )
    serializer_class = DataSerializer
    filter_class = DataFilter
    permission_classes = (get_permissions_class(), )

    ordering_fields = (
        "contributor",
        "contributor__first_name",
        "contributor__last_name",
        "created",
        "finished",
        "id",
        "modified",
        "name",
        "process__name",
        "process__type",
        "started",
    )
    ordering = "-created"

    @action(detail=False, methods=["post"])
    def duplicate(self, request, *args, **kwargs):
        """Duplicate (make copy of) ``Data`` objects."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        inherit_collection = request.data.get("inherit_collection", False)
        ids = self.get_ids(request.data)
        queryset = get_objects_for_user(request.user, "view_data",
                                        Data.objects.filter(id__in=ids))
        actual_ids = queryset.values_list("id", flat=True)
        missing_ids = list(set(ids) - set(actual_ids))
        if missing_ids:
            raise exceptions.ParseError(
                "Data objects with the following ids not found: {}".format(
                    ", ".join(map(str, missing_ids))))

        duplicated = queryset.duplicate(
            contributor=request.user,
            inherit_collection=inherit_collection,
        )

        serializer = self.get_serializer(duplicated, many=True)
        return Response(serializer.data)

    @action(detail=False, methods=["post"])
    def get_or_create(self, request, *args, **kwargs):
        """Get ``Data`` object if similar already exists, otherwise create it."""
        response = self.perform_get_or_create(request, *args, **kwargs)
        if response:
            return response

        return super().create(request, *args, **kwargs)

    def perform_get_or_create(self, request, *args, **kwargs):
        """Perform "get_or_create" - return existing object if found."""
        self.define_contributor(request)
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        process = serializer.validated_data.get("process")
        process_input = request.data.get("input", {})

        fill_with_defaults(process_input, process.input_schema)

        checksum = get_data_checksum(process_input, process.slug,
                                     process.version)
        data_qs = Data.objects.filter(
            checksum=checksum,
            process__persistence__in=[
                Process.PERSISTENCE_CACHED,
                Process.PERSISTENCE_TEMP,
            ],
        )
        data_qs = get_objects_for_user(request.user, "view_data", data_qs)
        if data_qs.exists():
            data = data_qs.order_by("created").last()
            serializer = self.get_serializer(data)
            return Response(serializer.data)

    def _parents_children(self, request, queryset):
        """Process given queryset and return serialized objects."""
        queryset = get_objects_for_user(request.user, "view_data", queryset)

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    @action(detail=True)
    def parents(self, request, pk=None):
        """Return parents of the current data object."""
        return self._parents_children(request, self.get_object().parents)

    @action(detail=True)
    def children(self, request, pk=None):
        """Return children of the current data object."""
        return self._parents_children(request, self.get_object().children)

    @action(detail=False, methods=["post"])
    def move_to_collection(self, request, *args, **kwargs):
        """Move data objects to destination collection."""
        ids = self.get_ids(request.data)
        dst_collection_id = self.get_id(request.data, "destination_collection")

        dst_collection = get_collection_for_user(dst_collection_id,
                                                 request.user)

        queryset = self._get_data(request.user, ids)
        queryset.move_to_collection(dst_collection)

        return Response()

    def _get_data(self, user, ids):
        """Return data objects queryset based on provided ids."""
        queryset = get_objects_for_user(user, "view_data",
                                        Data.objects.filter(id__in=ids))
        actual_ids = queryset.values_list("id", flat=True)
        missing_ids = list(set(ids) - set(actual_ids))
        if missing_ids:
            raise exceptions.ParseError(
                "Data objects with the following ids not found: {}".format(
                    ", ".join(map(str, missing_ids))))

        for data in queryset:
            collection = data.collection
            if collection and not user.has_perm("edit_collection",
                                                obj=collection):
                if user.is_authenticated:
                    raise exceptions.PermissionDenied()
                else:
                    raise exceptions.NotFound()

        return queryset
示例#13
0
class BaseCollectionViewSet(
        ResolweCreateModelMixin,
        mixins.RetrieveModelMixin,
        ResolweUpdateModelMixin,
        mixins.DestroyModelMixin,
        mixins.ListModelMixin,
        ResolwePermissionsMixin,
        ResolweCheckSlugMixin,
        ParametersMixin,
        viewsets.GenericViewSet,
):
    """Base API view for :class:`Collection` objects."""

    qs_descriptor_schema = DescriptorSchema.objects.select_related(
        "contributor")
    qs_permission_model = PermissionModel.objects.select_related(
        "user", "group")

    queryset = Collection.objects.select_related(
        "contributor").prefetch_related(
            "data", Prefetch("descriptor_schema",
                             queryset=qs_descriptor_schema))

    filter_class = CollectionFilter
    permission_classes = (get_permissions_class(), )

    ordering_fields = (
        "contributor",
        "contributor__first_name",
        "contributor__last_name",
        "created",
        "id",
        "modified",
        "name",
    )
    ordering = "id"

    def get_queryset(self):
        """Prefetch permissions for current user."""
        return self.prefetch_current_user_permissions(self.queryset)

    def create(self, request, *args, **kwargs):
        """Only authenticated users can create new collections."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        return super().create(request, *args, **kwargs)

    @action(detail=False, methods=["post"])
    def duplicate(self, request, *args, **kwargs):
        """Duplicate (make copy of) ``Collection`` models."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        ids = self.get_ids(request.data)
        queryset = Collection.objects.filter(id__in=ids).filter_for_user(
            request.user, Permission.VIEW)
        actual_ids = queryset.values_list("id", flat=True)
        missing_ids = list(set(ids) - set(actual_ids))
        if missing_ids:
            raise exceptions.ParseError(
                "Collections with the following ids not found: {}".format(
                    ", ".join(map(str, missing_ids))))

        duplicated = queryset.duplicate(contributor=request.user)

        serializer = self.get_serializer(duplicated, many=True)
        return Response(serializer.data)
示例#14
0
class CollectionViewSet(ElasticSearchCombinedViewSet, ResolweCreateModelMixin,
                        mixins.RetrieveModelMixin, ResolweUpdateModelMixin,
                        mixins.DestroyModelMixin, mixins.ListModelMixin,
                        ResolwePermissionsMixin, ResolweCheckSlugMixin,
                        viewsets.GenericViewSet):
    """API view for :class:`Collection` objects."""

    queryset = Collection.objects.all().prefetch_related(
        'descriptor_schema', 'contributor',
        Prefetch('data', queryset=Data.objects.all().order_by('id')))
    serializer_class = CollectionSerializer
    permission_classes = (get_permissions_class(), )
    document_class = CollectionDocument

    filtering_fields = (
        'id',
        'slug',
        'name',
        'created',
        'modified',
        'contributor',
        'owners',
        'text',
        'tags',
    )
    filtering_map = {
        'name': 'name.ngrams',
        'contributor': 'contributor_id',
        'owners': 'owner_ids',
    }
    ordering_fields = ('id', 'created', 'modified', 'name', 'contributor')
    ordering_map = {
        'name': 'name.raw',
        'contributor': 'contributor_sort',
    }
    ordering = 'id'

    def custom_filter_tags(self, value, search):
        """Support tags query."""
        if not isinstance(value, list):
            value = value.split(',')

        filters = [Q('match', **{'tags': item}) for item in value]
        search = search.query('bool', must=filters)

        return search

    def get_always_allowed_arguments(self):
        """Return query arguments which are always allowed."""
        return super().get_always_allowed_arguments() + [
            'hydrate_data',
        ]

    def custom_filter_text(self, value, search):
        """Support general query using the 'text' attribute."""
        if isinstance(value, list):
            value = ' '.join(value)

        should = [
            Q('match', slug={
                'query': value,
                'operator': 'and',
                'boost': 10.0
            }),
            Q(
                'match', **{
                    'slug.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 5.0
                    }
                }),
            Q('match', name={
                'query': value,
                'operator': 'and',
                'boost': 10.0
            }),
            Q(
                'match', **{
                    'name.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 5.0
                    }
                }),
            Q('match',
              contributor_name={
                  'query': value,
                  'operator': 'and',
                  'boost': 5.0
              }),
            Q(
                'match', **{
                    'contributor_name.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 2.0
                    }
                }),
            Q('match',
              owner_names={
                  'query': value,
                  'operator': 'and',
                  'boost': 5.0
              }),
            Q(
                'match', **{
                    'owner_names.ngrams': {
                        'query': value,
                        'operator': 'and',
                        'boost': 2.0
                    }
                }),
            Q('match', descriptor_data={
                'query': value,
                'operator': 'and'
            }),
        ]

        # Add registered text extensions.
        for extension in composer.get_extensions(self):
            if hasattr(extension, 'text_filter'):
                should += extension.text_filter(value)

        search = search.query('bool', should=should)

        return search

    def set_content_permissions(self, user, obj, payload):
        """Apply permissions to data objects and entities in ``Collection``."""
        for entity in obj.entity_set.all():
            if user.has_perm('share_entity', entity):
                update_permission(entity, payload)

        # Data doesn't have "ADD" permission, so it has to be removed
        payload = remove_permission(payload, 'add')

        for data in obj.data.all():
            if user.has_perm('share_data', data):
                update_permission(data, payload)

    def create(self, request, *args, **kwargs):
        """Only authenticated usesr can create new collections."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        return super().create(request, *args, **kwargs)

    def destroy(self, request, *args, **kwargs):
        """Destroy a model instance.

        If ``delete_content`` flag is set in query parameters, also all
        Data objects and Entities, on which user has ``EDIT``
        permission, contained in collection will be deleted.
        """
        obj = self.get_object()
        user = request.user

        if strtobool(request.query_params.get('delete_content', 'false')):
            for entity in obj.entity_set.all():
                if user.has_perm('edit_entity', entity):
                    entity.delete()

            for data in obj.data.all():
                if user.has_perm('edit_data', data):
                    data.delete()

        return super().destroy(request, *args, **kwargs)  # pylint: disable=no-member

    @detail_route(methods=['post'])
    def add_data(self, request, pk=None):
        """Add data to collection."""
        collection = self.get_object()

        if 'ids' not in request.data:
            return Response({"error": "`ids`parameter is required"},
                            status=status.HTTP_400_BAD_REQUEST)

        missing = []
        for data_id in request.data['ids']:
            if not Data.objects.filter(pk=data_id).exists():
                missing.append(data_id)

        if missing:
            return Response(
                {
                    "error":
                    "Data objects with following ids are missing: {}".format(
                        ', '.join(missing))
                },
                status=status.HTTP_400_BAD_REQUEST)

        for data_id in request.data['ids']:
            collection.data.add(data_id)

        return Response()

    @detail_route(methods=['post'])
    def remove_data(self, request, pk=None):
        """Remove data from collection."""
        collection = self.get_object()

        if 'ids' not in request.data:
            return Response({"error": "`ids`parameter is required"},
                            status=status.HTTP_400_BAD_REQUEST)

        for data_id in request.data['ids']:
            collection.data.remove(data_id)

        return Response()
示例#15
0
class BaseCollectionViewSet(
    ResolweCreateModelMixin,
    mixins.RetrieveModelMixin,
    ResolweUpdateModelMixin,
    mixins.DestroyModelMixin,
    mixins.ListModelMixin,
    ResolwePermissionsMixin,
    ResolweCheckSlugMixin,
    ParametersMixin,
    viewsets.GenericViewSet,
):
    """Base API view for :class:`Collection` objects."""

    qs_descriptor_schema = DescriptorSchema.objects.select_related("contributor")

    queryset = Collection.objects.select_related("contributor").prefetch_related(
        "data",
        Prefetch("descriptor_schema", queryset=qs_descriptor_schema),
    )

    filter_class = CollectionFilter
    permission_classes = (get_permissions_class(),)

    ordering_fields = (
        "contributor",
        "contributor__first_name",
        "contributor__last_name",
        "created",
        "id",
        "modified",
        "name",
    )
    ordering = "id"

    def set_content_permissions(self, user, obj, payload):
        """Apply permissions to data objects and entities in ``Collection``."""
        for entity in obj.entity_set.all():
            if user.has_perm("share_entity", entity):
                update_permission(entity, payload)

        for data in obj.data.all():
            if user.has_perm("share_data", data):
                update_permission(data, payload)

    def create(self, request, *args, **kwargs):
        """Only authenticated users can create new collections."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        return super().create(request, *args, **kwargs)

    @action(detail=False, methods=["post"])
    def duplicate(self, request, *args, **kwargs):
        """Duplicate (make copy of) ``Collection`` models."""
        if not request.user.is_authenticated:
            raise exceptions.NotFound

        ids = self.get_ids(request.data)
        queryset = get_objects_for_user(
            request.user, "view_collection", Collection.objects.filter(id__in=ids)
        )
        actual_ids = queryset.values_list("id", flat=True)
        missing_ids = list(set(ids) - set(actual_ids))
        if missing_ids:
            raise exceptions.ParseError(
                "Collections with the following ids not found: {}".format(
                    ", ".join(map(str, missing_ids))
                )
            )

        duplicated = queryset.duplicate(contributor=request.user)

        serializer = self.get_serializer(duplicated, many=True)
        return Response(serializer.data)