def test_invalid_items(self): """`parse_pks` strip non-int values""" self.assertItemsEqual(parse_pks('1,2,a,b'), [1, 2]) self.assertItemsEqual(parse_pks('a,b,1,2'), [1, 2]) self.assertItemsEqual(parse_pks('1,a,b,2'), [1, 2]) self.assertItemsEqual(parse_pks('a,1,b,2'), [1, 2]) self.assertEqual(parse_pks('a,b'), [])
def test_items(self): """`parse_pks` will retrun in list all values that are integers. Extra spaces are allowed """ self.assertItemsEqual(parse_pks('1'), [1]) self.assertItemsEqual(parse_pks('1,2'), [1, 2]) self.assertItemsEqual(parse_pks('1, 2, 3,'), [1, 2, 3]) self.assertItemsEqual(parse_pks('-1, 1'), [-1, 1]) self.assertItemsEqual(parse_pks('1, 1'), [1, 1])
def get_collections(self): """For given `request.POST.pks` get list of research project collections that will be assigned to classification project""" collection_pks = parse_pks(pks=self.request.POST.get('pks', '')) collections = ResearchProjectCollection.objects.get_accessible( user=self.request.user).filter(pk__in=collection_pks) return collections
def get(self, request, *args, **kwargs): """ """ project_pk = kwargs['project_pk'] collection_pk = kwargs['collection_pk'] collection = self.get_collection(project_pk=project_pk, collection_pk=collection_pk) deployments = request.GET.get('deployments', None) if deployments: params = {'resource__deployment__pk__in': parse_pks(deployments)} else: params = {} classification = collection.classifications.filter( **params).order_by('resource__date_recorded').first() if not classification: messages.error( request=self.request, message=('Selected collection contains no resources. ' 'Nothing to classify.')) return redirect( reverse('media_classification:project_detail', kwargs={'pk': project_pk})) url = reverse('media_classification:classify', kwargs={'pk': classification.pk}) if deployments: return redirect('{url}?deployments={deployments_pks}'.format( url=url, deployments_pks=deployments)) else: return redirect(url)
def filter(self, qs, value): if value: pks = parse_pks(value) if pks: return qs.filter( **{"{model_field}__in".format(model_field=self.name): pks}) return qs
def get_collections(self): """Get list of collection pks's from `request.POST.pks` and return all collections that are accessible for user""" collection_pks = parse_pks(self.request.POST.get('pks', '')) collections = Collection.objects.get_accessible( user=self.request.user).filter(pk__in=collection_pks) return collections
def post(self, request, *args, **kwargs): """ `request.POST` method is used to remove multiple resources from collection in a single request using AJAX. """ user = request.user data = request.POST.get('pks', None) try: collection = Collection.objects.get(pk=kwargs.get('pk', None)) except Collection.DoesNotExist: collection = None changed = [] if data and collection and collection.can_update(user=user): values = parse_pks(pks=data) status = True msg = '' collection.resources.through.objects.filter( resource__in=values, collection=collection.pk).delete() else: status = False msg = 'Invlid request' context = {'status': status, 'msg': msg} return self.render_json_response(context)
def clean_locations_pks(self): locations_pks = self.cleaned_data.pop('locations_pks', None) if locations_pks: pks_list = parse_pks(locations_pks) locations = Location.objects.get_available( editable_only=True).filter(pk__in=pks_list) if locations: self.cleaned_data['locations'] = locations
def clean_records_pks(self): records_pks = self.cleaned_data.pop('records_pks', None) if records_pks: pks_list = parse_pks(records_pks) records = self.filter_editable() records = records.filter(pk__in=pks_list) if records: self.cleaned_data['records'] = records
def clean_resources_pks(self): resources_pks = self.cleaned_data.pop('resources_pks', None) if resources_pks: pks_list = parse_pks(resources_pks) resources = Resource.objects.get_accessible().filter( pk__in=pks_list) if resources: self.cleaned_data['resources'] = resources
def get_resources(self, collection, resource_pks): """For given collection and resource pk return :class:`apps.storage.model.Resource` objects but only those that are accessible for currently logged in user""" base_queryset = collection.collection.collection.resources.filter( pk__in=parse_pks(resource_pks)) return Resource.objects.get_accessible(base_queryset=base_queryset, user=self.request.user, basic=True)
def clean_collections_pks(self): collections_pks = self.cleaned_data.pop('collections_pks', None) if collections_pks: pks_list = parse_pks(collections_pks) collections = ClassificationProjectCollection.objects.get_accessible( role_levels=[ ClassificationProjectRoleLevels.ADMIN, ]).filter(pk__in=pks_list) if collections: self.cleaned_data['project_collections'] = collections
def clean_selected_resources(self): error_msg = u"Classify multiple resources form has been tempered with." collection = self.initial.get('collection', None) if not collection: raise forms.ValidationError(error_msg) selected_resources = self.cleaned_data.get('selected_resources', None) resources = Resource.objects.filter( pk__in=parse_pks(pks=selected_resources), collection=collection.collection.collection) if not resources: raise forms.ValidationError(error_msg) return resources
def post(self, request, *args, **kwargs): """ """ user = self.request.user status = False msg = 'No resources to update or you have no permission \ to update selected resources.' data = request.POST.copy() if 'classifications_pks' in data: classifications_pks = data['classifications_pks'] or '' del data['classifications_pks'] classifications_pks = parse_pks(classifications_pks) classifications = Classification.objects.filter( Q(resource__owner=user) | Q(resource__managers=user)).filter( pk__in=classifications_pks).select_related('resource') tag_keys = data.keys() if classifications: params = { 'data': { 'classifications': list(classifications), 'tag_keys': tag_keys }, 'user': user } if settings.CELERY_ENABLED: task = celery_create_tags.delay(**params) user_task = UserTask(user=user, task_id=task.task_id) user_task.save() msg = ( 'You have successfully run a celery task. Tags for ' 'selected resources are being created now.') else: msg = celery_create_tags(**params) status = True return self.render_json_response({ 'status': status, 'msg': msg, })
def post(self, request, *args, **kwargs): """Delete multiple objects based on POST request. Object can be deleted only when request user has permission to do that. """ user = request.user data = request.POST.get('pks', None) model = self.get_model() if data: values = parse_pks(pks=data) status = True msg = u'' deleted = 0 candidates = model.objects.filter(pk__in=values) candidates = self.filter_editable(candidates, user) total = len(candidates) if not total: status = False msg = ( u'No items to delete (most probably you have no permission ' u'to do that).') else: try: self.bulk_delete(candidates) msg = str( total) + ' record(s) have been successfully deleted.' except ProtectedError: status = False msg = ( u'Some of selected items can not be deleted because ' u'they are referenced through a protected foreign key. ' u'Unselect them and re-run the action.') else: status = False msg = u'Invalid request' context = {'status': status, 'msg': msg} return self.render_json_response(context)
def post(self, request, *args, **kwargs): """ `request.POST` method is used to alter multiple resources in single request using AJAX. List of resources pks is passed in `pks` key as list of integers separated by comma. Only owned resources can be updated. Response contains status of removal and list of resource pks that were changed. """ user = request.user data = request.POST.get('pks', None) custom_prefix = request.POST.get('custom_prefix', None) inherit_prefix = request.POST.get('append', False) == 'true' changed = [] status = False msg = '' if data: values = parse_pks(pks=data) candidates = Resource.objects.filter(pk__in=values).filter( (Q(owner=user) | Q(managers=user))) if candidates: if custom_prefix: candidates.update(custom_prefix=custom_prefix) if inherit_prefix: candidates = candidates.filter( deployment__location__isnull=False) if candidates: candidates.update(inherit_prefix=inherit_prefix, ) status = True else: msg = 'No resources to update' else: msg = 'Invalid request' context = {'status': status, 'msg': msg} return self.render_json_response(context)
def clean(self): """ When collection and its resources are saved and the status of this collection is set to "Public" or "OnDemand" check if this collection can be published or shared. This should be only possible when a user has editing rights to all resources that are part of this collection. """ cleaned_data = super(CollectionForm, self).clean() status = cleaned_data.get('status') resources_pks = cleaned_data.pop('resources_pks', None) app = cleaned_data.pop('app', None) if resources_pks: pks_list = parse_pks(resources_pks) if app == 'media_classification': resources = Resource.objects.get_accessible().filter( classifications__pk__in=pks_list) else: resources = Resource.objects.get_accessible().filter( pk__in=pks_list) if resources: cleaned_data['resources'] = resources if status != 'Private': if self.instance.pk: resources = self.instance.resources.all() else: resources = cleaned_data.get('resources', None) if not resources: return cleaned_data user = get_current_user() n = resources.exclude(Q(owner=user) | Q(managers=user)).count() if n != 0: errors = mark_safe( 'You are not allowed to set the status of this collection ' 'to <strong>{status}</strong> as you are not the owner of all resources ' 'that are part of this collection.'.format(status=status)) raise forms.ValidationError(errors) return cleaned_data
def clean_resources(self): """Convert resources from string of comma separated integers into list of integers""" resources = self.data.get('resources', None) return parse_pks(pks=resources)
def format_pks(self, pks): """Prepare list of pk from string""" return parse_pks(pks=pks)
def filter_deployments(self, qs, value): if value.strip() == "": return qs ids = parse_pks(pks=value) qs = qs.filter(deployments__pk__in=ids) return qs
def _format_value(self, value): return parse_pks(pks=value)
def test_invalid_separator(self): """`parse_pks` can handle invalid separator by returning empty list""" self.assertEqual(parse_pks('1:2'), []) self.assertEqual(parse_pks('1;2'), [])
def test_empty(self): """`parse_pks` can handle empty string by returning empty list""" self.assertEqual(parse_pks(''), []) self.assertEqual(parse_pks(u''), [])
def test_invalid_type(self): """`parse_pks` can handle invalid types by returning empty list""" self.assertEqual(parse_pks(None), []) self.assertEqual(parse_pks([]), []) self.assertEqual(parse_pks(()), []) self.assertEqual(parse_pks(b'test'), [])
def post(self, request, *args, **kwargs): user = request.user timestamp = now() data = request.POST.get('pks', None) if data: values = parse_pks(pks=data) user_classifications = UserClassification.objects.filter( classification__project=self.project, pk__in=values).select_related( 'classification', 'owner').prefetch_related('dynamic_attrs') total = len(user_classifications) if not total: status = False msg = 'No items to delete (most probably you have no permission to do that).' context = {'status': status, 'msg': msg} return self.render_json_response(context) users = [k.owner for k in user_classifications] if len(set(users)) != 1: status = False msg = ( 'You can run this action only for a set of classifications ' 'that all belong to a one user.') context = {'status': status, 'msg': msg} return self.render_json_response(context) # bulk update classification objects classifications_to_update = [] dynamic_attrs_objects = [] for uc in user_classifications: classification = uc.classification classification.status = True classification.approved_by_id = user.pk classification.approved_at = str(timestamp) classification.approved_source_id = uc.pk classification.static_attrs = uc.static_attrs classifications_to_update.append(classification) for dynamic_row in uc.dynamic_attrs.all(): dynamic_attrs_objects.append( ClassificationDynamicAttrs( classification_id=classification.pk, attrs=dynamic_row.attrs)) bulk_update(classifications_to_update, update_fields=[ 'status', 'approved_by_id', 'approved_at', 'approved_source_id', 'static_attrs' ]) # bulk delete ClassificationDynamicAttrs objects ClassificationDynamicAttrs.objects.filter( classification__in=classifications_to_update).delete() # bulk create ClassificationDynamicAttrs objects ClassificationDynamicAttrs.objects.bulk_create( dynamic_attrs_objects) else: status = False msg = 'Invalid request' status = True msg = ('You have successfully approved <strong>{n}</strong> user ' 'classifications.'.format(n=total)) context = {'status': status, 'msg': msg} return self.render_json_response(context)
def post(self, request, *args, **kwargs): """ `request.POST` method is used to append multiple resources to collection in single request. List of resources pks is passed in `pks` key as list of integers separated by comma. Before append, all resources pks are validated if user can view them and only those, which user has enough permissions for, are added to collection. Response contains status of update and list of resource pks that were added. After that, user is redirected to collection details. """ user = request.user collection_pk = request.POST.get('collection', None) resources_pk = request.POST.get('resources', None) app = request.POST.get('app', None) resources_url = reverse('storage:resource_list') resources = None if resources_pk: resources_pk = parse_pks(pks=resources_pk) if app == 'media_classification': base_queryset = Resource.objects.filter( classifications__pk__in=resources_pk) else: base_queryset = Resource.objects.filter(pk__in=resources_pk) resources = Resource.objects.get_accessible( base_queryset=base_queryset) if not resources: messages.error( request, 'You have no permissions to add any of selected resources.') return redirect(resources_url) collection = get_object_or_404(Collection, pk=collection_pk) collection_url = reverse('storage:collection_detail', kwargs={'pk': collection.pk}) if not collection.can_update(user=user): messages.error( request, 'You have no permissions to modify this collection.') return redirect(resources_url) if not collection.status == 'Private': n = resources.exclude(Q(owner=user) | Q(managers=user)).count() if n != 0: messages.error( request, 'You have no permission to add some of selected resources ' 'to this "{ctype}" collection'.format( ctype=collection.status.lower())) return redirect(resources_url) new_resources = resources.exclude( pk__in=collection.resources.values_list('pk', flat=True)) if not new_resources: messages.warning( request, 'The selected resources already are in this collection.') return redirect(collection_url) messages.success( request, 'You have successfully added <strong>{len}</strong> new ' 'resources to this collection.'.format(len=new_resources.count())) collection.resources.add(*new_resources) return redirect(collection_url)