def get_meta_information(self, meta_data, value): """ For retrieving meta values, otherwise returns {} """ meta = {} for key in meta_data or {}: if key == 'count': show_related_counts = self.context['request'].query_params.get( 'related_counts', False) if utils.is_truthy(show_related_counts): meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue if not utils.is_truthy(show_related_counts): raise InvalidQueryStringError( detail= "Acceptable values for the related_counts query param are 'true' or 'false'; got '{0}'" .format(show_related_counts), parameter='related_counts') else: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) return meta
def _library_list(auth, node_addon, **kwargs): """ Returns a list of group libraries - for use with Zotero addon """ limit = request.args.get('limit') start = request.args.get('start') return_count = is_truthy(request.args.get('return_count', False)) append_personal = is_truthy(request.args.get('append_personal', True)) return node_addon.get_folders(limit=limit, start=start, return_count=return_count, append_personal=append_personal)
def get_meta_information(self, meta_data, value): """ For retrieving meta values, otherwise returns {} """ meta = {} for key in meta_data or {}: if key == 'count' or key == 'unread': show_related_counts = self.context['request'].query_params.get( 'related_counts', False) field_counts_requested = self.process_related_counts_parameters( show_related_counts, value) if utils.is_truthy(show_related_counts): meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue elif self.field_name in field_counts_requested: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) else: continue else: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) return meta
def process_related_counts_parameters(self, params, value): """ Processes related_counts parameter. Can either be a True/False value for fetching counts on all fields, or a comma-separated list for specifying individual fields. Ensures field for which we are requesting counts is a relationship field. """ if utils.is_truthy(params) or utils.is_falsy(params): return params field_counts_requested = [val for val in params.split(',')] countable_fields = {field for field in self.parent.fields if getattr(self.parent.fields[field], 'json_api_link', False) or getattr(getattr(self.parent.fields[field], 'field', None), 'json_api_link', None)} for count_field in field_counts_requested: # Some fields will hide relationships, e.g. HideIfWithdrawal # Ignore related_counts for these fields fetched_field = self.parent.fields.get(count_field) hidden = fetched_field and isinstance(fetched_field, HideIfWithdrawal) and getattr(value, 'is_retracted', False) if not hidden and count_field not in countable_fields: raise InvalidQueryStringError( detail="Acceptable values for the related_counts query param are 'true', 'false', or any of the relationship fields; got '{0}'".format( params), parameter='related_counts' ) return field_counts_requested
def convert_value(self, value, field): """Used to convert incoming values from query params to the appropriate types for filter comparisons :param basestring value: value to be resolved :param rest_framework.fields.Field field: Field instance """ if isinstance(field, ser.BooleanField): if utils.is_truthy(value): return True elif utils.is_falsy(value): return False else: raise InvalidFilterValue(value=value, field_type="bool") elif isinstance(field, self.DATE_FIELDS): try: return date_parser.parse(value) except ValueError: raise InvalidFilterValue(value=value, field_type="date") elif isinstance(field, (self.LIST_FIELDS, self.RELATIONSHIP_FIELDS, ser.SerializerMethodField)) or isinstance( (getattr(field, "field", None)), self.LIST_FIELDS ): if value == "null": value = None return value else: try: return field.to_internal_value(value) except ValidationError: raise InvalidFilterValue(value=value)
def to_representation(self, value): """ Returns nested dictionary in format {'links': {'self.link_type': ... } If no meta information, self.link_type is equal to a string containing link's URL. Otherwise, the link is represented as a links object with 'href' and 'meta' members. """ url = super(JSONAPIHyperlinkedIdentityField, self).to_representation(value) meta = {} for key in self.meta or {}: if key == 'count': show_related_counts = self.context['request'].query_params.get( 'related_counts', False) if utils.is_truthy(show_related_counts): meta[key] = _rapply(self.meta[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue else: raise InvalidQueryStringError( detail= "Acceptable values for the related_counts query param are 'true' or 'false'; got '{0}'" .format(show_related_counts), parameter='related_counts') return {'links': {self.link_type: {'href': url, 'meta': meta}}}
def convert_value(self, value, field): """Used to convert incoming values from query params to the appropriate types for filter comparisons :param basestring value: value to be resolved :param rest_framework.fields.Field field: Field instance """ if isinstance(field, ser.BooleanField): if utils.is_truthy(value): return True elif utils.is_falsy(value): return False else: raise InvalidFilterValue( value=value, field_type='bool' ) elif isinstance(field, self.DATE_FIELDS): try: return date_parser.parse(value) except ValueError: raise InvalidFilterValue( value=value, field_type='date' ) elif isinstance(field, self.LIST_FIELDS): return value else: try: return field.to_internal_value(value) except ValidationError: raise InvalidFilterValue( value=value, )
def get_queryset(self): # For bulk requests, queryset is formed from request body. if is_bulk_request(self.request): auth = get_user_auth(self.request) registrations = Registration.objects.filter(guids___id__in=[ registration['id'] for registration in self.request.data ]) # If skip_uneditable=True in query_params, skip nodes for which the user # does not have EDIT permissions. if is_truthy( self.request.query_params.get('skip_uneditable', False)): has_permission = registrations.filter( contributor__user_id=auth.user.id, contributor__write=True).values_list('guids___id', flat=True) return Registration.objects.filter( guids___id__in=has_permission) for registration in registrations: if not registration.can_edit(auth): raise PermissionDenied return registrations blacklisted = self.is_blacklisted() registrations = self.get_queryset_from_request() # If attempting to filter on a blacklisted field, exclude withdrawals. if blacklisted: return registrations.exclude(retraction__isnull=False) return registrations
def create(self, validated_data): request = self.context['request'] user = request.user if 'template_from' in validated_data: template_from = validated_data.pop('template_from') template_node = Node.load(key=template_from) if template_node is None: raise exceptions.NotFound if not template_node.has_permission(user, 'read', check_parent=False): raise exceptions.PermissionDenied validated_data.pop('creator') changed_data = {template_from: validated_data} node = template_node.use_as_template(auth=get_user_auth(request), changes=changed_data) else: node = Node(**validated_data) try: node.save() except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) if is_truthy(request.GET.get('inherit_contributors')) and validated_data['parent'].has_permission(user, 'write'): auth = get_user_auth(request) parent = validated_data['parent'] contributors = [] for contributor in parent.contributors: if contributor is not user: contributors.append({ 'user': contributor, 'permissions': parent.get_permissions(contributor), 'visible': parent.get_visible(contributor) }) node.add_contributors(contributors, auth=auth, log=True, save=True) return node
def get_queryset(self): # For bulk requests, queryset is formed from request body. if is_bulk_request(self.request): auth = get_user_auth(self.request) registrations = Registration.objects.filter(guids___id__in=[ registration['id'] for registration in self.request.data ]) # If skip_uneditable=True in query_params, skip nodes for which the user # does not have EDIT permissions. if is_truthy( self.request.query_params.get('skip_uneditable', False)): return Registration.objects.get_nodes_for_user( auth.user, WRITE_NODE, registrations) for registration in registrations: if not registration.can_edit(auth): raise PermissionDenied return registrations blacklisted = self.is_blacklisted() registrations = self.get_queryset_from_request() # If attempting to filter on a blacklisted field, exclude withdrawals. if blacklisted: registrations = registrations.exclude(retraction__isnull=False) return registrations.select_related( 'root', 'root__embargo', 'root__embargo_termination_approval', 'root__retraction', 'root__registration_approval', )
def update(self, registration, validated_data): user = self.context['request'].user auth = Auth(user) user_is_admin = registration.has_permission(user, permissions.ADMIN) # Update tags if 'tags' in validated_data: new_tags = validated_data.pop('tags', []) try: registration.update_tags(new_tags, auth=auth) except NodeStateError as err: raise Conflict(str(err)) if 'custom_citation' in validated_data: if user_is_admin: registration.update_custom_citation( validated_data.pop('custom_citation'), auth) else: raise exceptions.PermissionDenied() is_public = validated_data.get('is_public', None) if is_public is not None: if is_public: if user_is_admin: try: registration.update(validated_data, auth=auth) except NodeUpdateError as err: raise exceptions.ValidationError(err.reason) except NodeStateError as err: raise exceptions.ValidationError(str(err)) else: raise exceptions.PermissionDenied() else: raise exceptions.ValidationError( 'Registrations can only be turned from private to public.') if 'withdrawal_justification' in validated_data or 'is_pending_retraction' in validated_data: if user_is_admin: is_pending_retraction = validated_data.get( 'is_pending_retraction', None) withdrawal_justification = validated_data.get( 'withdrawal_justification', None) if withdrawal_justification and not is_pending_retraction: raise exceptions.ValidationError( 'You cannot provide a withdrawal_justification without a concurrent withdrawal request.', ) if is_truthy(is_pending_retraction): if registration.is_pending_retraction: raise exceptions.ValidationError( 'This registration is already pending withdrawal') try: retraction = registration.retract_registration( user, withdrawal_justification, save=True) except NodeStateError as err: raise exceptions.ValidationError(str(err)) retraction.ask( registration.get_active_contributors_recursive( unique_users=True)) elif is_pending_retraction is not None: raise exceptions.ValidationError( 'You cannot set is_pending_withdrawal to False.') else: raise exceptions.PermissionDenied() return registration
def get_meta_information(self, meta_data, value): """ For retrieving meta values, otherwise returns {} """ meta = {} for key in meta_data or {}: if key == 'count' or key == 'unread': show_related_counts = self.context['request'].query_params.get('related_counts', False) if self.context['request'].parser_context.get('kwargs'): if self.context['request'].parser_context['kwargs'].get('is_embedded'): show_related_counts = False field_counts_requested = self.process_related_counts_parameters(show_related_counts, value) if utils.is_truthy(show_related_counts): meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue elif self.field_name in field_counts_requested: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) else: continue elif key == 'projects_in_common': if not get_user_auth(self.context['request']).user: continue if not self.context['request'].query_params.get('show_projects_in_common', False): continue meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) else: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) return meta
def update(self, instance, validated_data): # avoiding circular import from api.nodes.serializers import ContributorIDField # if PATCH request, the child serializer's partial attribute needs to be True if self.context['request'].method == 'PATCH': self.child.partial = True bulk_skip_uneditable = utils.is_truthy(self.context['request'].query_params.get('skip_uneditable', False)) if not bulk_skip_uneditable: if len(instance) != len(validated_data): raise exceptions.ValidationError({'non_field_errors': 'Could not find all objects to update.'}) id_lookup = self.child.fields['id'].source data_mapping = {item.get(id_lookup): item for item in validated_data} if isinstance(self.child.fields['id'], ContributorIDField): instance_mapping = {self.child.fields['id'].get_id(item): item for item in instance} else: instance_mapping = {getattr(item, id_lookup): item for item in instance} ret = {'data': []} for resource_id, resource in instance_mapping.items(): data = data_mapping.pop(resource_id, None) ret['data'].append(self.child.update(resource, data)) # If skip_uneditable in request, add validated_data for nodes in which the user did not have edit permissions to errors if data_mapping and bulk_skip_uneditable: ret.update({'errors': data_mapping.values()}) return ret
def to_representation(self, data): enable_esi = self.context.get('enable_esi', False) envelope = self.context.update({'envelope': None}) # Don't envelope when serializing collection errors = {} bulk_skip_uneditable = utils.is_truthy( self.context['request'].query_params.get('skip_uneditable', False)) if isinstance(data, collections.Mapping): errors = data.get('errors', None) data = data.get('data', None) if enable_esi: ret = [ self.child.to_esi_representation(item, envelope=None) for item in data ] else: ret = [ self.child.to_representation(item, envelope=envelope) for item in data ] if errors and bulk_skip_uneditable: ret.append({'errors': errors}) return ret
def to_representation(self, value): """ Returns nested dictionary in format {'links': {'self.link_type': ... } If no meta information, self.link_type is equal to a string containing link's URL. Otherwise, the link is represented as a links object with 'href' and 'meta' members. """ url = super(JSONAPIHyperlinkedIdentityField, self).to_representation(value) meta = {} for key in self.meta or {}: if key == 'count': show_related_counts = self.context['request'].query_params.get('related_counts', False) if utils.is_truthy(show_related_counts): meta[key] = _rapply(self.meta[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue else: raise InvalidQueryStringError( detail="Acceptable values for the related_counts query param are 'true' or 'false'; got '{0}'".format(show_related_counts), parameter='related_counts' ) else: meta[key] = _rapply(self.meta[key], _url_val, obj=value, serializer=self.parent) return {'links': {self.link_type: {'href': url, 'meta': meta}}}
def update(self, instance, validated_data): # if PATCH request, the child serializer's partial attribute needs to be True if self.context['request'].method == 'PATCH': self.child.partial = True bulk_skip_uneditable = utils.is_truthy( self.context['request'].query_params.get('skip_uneditable', False)) if not bulk_skip_uneditable: if len(instance) != len(validated_data): raise exceptions.ValidationError({ 'non_field_errors': 'Could not find all objects to update.' }) id_lookup = self.child.fields['id'].source instance_mapping = { getattr(item, id_lookup): item for item in instance } data_mapping = {item.get(id_lookup): item for item in validated_data} ret = {'data': []} for resource_id, resource in instance_mapping.items(): data = data_mapping.pop(resource_id, None) ret['data'].append(self.child.update(resource, data)) # If skip_uneditable in request, add validated_data for nodes in which the user did not have edit permissions to errors if data_mapping and bulk_skip_uneditable: ret.update({'errors': data_mapping.values()}) return ret
def convert_value(self, value, field): """Used to convert incoming values from query params to the appropriate types for filter comparisons :param basestring value: value to be resolved :param rest_framework.fields.Field field: Field instance """ if isinstance(field, ser.BooleanField): if utils.is_truthy(value): return True elif utils.is_falsy(value): return False else: raise InvalidFilterValue(value=value, field_type='bool') elif isinstance(field, self.DATE_FIELDS): try: return date_parser.parse(value) except ValueError: raise InvalidFilterValue(value=value, field_type='date') elif isinstance(field, (self.LIST_FIELDS, self.RELATIONSHIP_FIELDS, ser.SerializerMethodField)) \ or isinstance((getattr(field, 'field', None)), self.LIST_FIELDS): if value == 'null': value = None return value else: try: return field.to_internal_value(value) except ValidationError: raise InvalidFilterValue(value=value, )
def get_queryset(self): # For bulk requests, queryset is formed from request body. if is_bulk_request(self.request): auth = get_user_auth(self.request) registrations = Registration.objects.filter(guids___id__in=[registration['id'] for registration in self.request.data]) # If skip_uneditable=True in query_params, skip nodes for which the user # does not have EDIT permissions. if is_truthy(self.request.query_params.get('skip_uneditable', False)): has_permission = registrations.filter(contributor__user_id=auth.user.id, contributor__write=True).values_list('guids___id', flat=True) return Registration.objects.filter(guids___id__in=has_permission) for registration in registrations: if not registration.can_edit(auth): raise PermissionDenied return registrations blacklisted = self.is_blacklisted() registrations = self.get_queryset_from_request() # If attempting to filter on a blacklisted field, exclude withdrawals. if blacklisted: registrations = registrations.exclude(retraction__isnull=False) return registrations.select_related( 'root', 'root__embargo', 'root__embargo_termination_approval', 'root__retraction', 'root__registration_approval', )
def get(self, request, *args, **kwargs): response = super(UserEmailsDetail, self).get(request, *args, **kwargs) if is_truthy(self.request.query_params.get('resend_confirmation')): user = self.get_user() email_id = kwargs.get('email_id') if user.unconfirmed_emails and user.email_verifications.get(email_id): response.status = response.status_code = status.HTTP_202_ACCEPTED return response
def get_renderer_context(self): context = super().get_renderer_context() if is_truthy(self.request.query_params.get('meta[reviews_state_counts]', False)): provider = self.get_provider() context['meta'] = { 'reviews_state_counts': provider.get_reviewable_state_counts(), } return context
def get_renderer_context(self): context = super(RegistrationDetail, self).get_renderer_context() show_counts = is_truthy(self.request.query_params.get('related_counts', False)) if show_counts: registration = self.get_object() context['meta'] = { 'templated_by_count': registration.templated_list.count(), } return context
def create(self, validated_data): request = self.context['request'] user = request.user Node = apps.get_model('osf.Node') tag_instances = [] if 'tags' in validated_data: tags = validated_data.pop('tags') for tag in tags: tag_instance, created = Tag.objects.get_or_create( name=tag, defaults=dict(system=False)) tag_instances.append(tag_instance) if 'template_from' in validated_data: template_from = validated_data.pop('template_from') template_node = Node.load(template_from) if template_node is None: raise exceptions.NotFound if not template_node.has_permission( user, 'read', check_parent=False): raise exceptions.PermissionDenied validated_data.pop('creator') changed_data = {template_from: validated_data} node = template_node.use_as_template(auth=get_user_auth(request), changes=changed_data) else: node = Node(**validated_data) try: node.save() except ValidationError as e: raise InvalidModelValueError(detail=e.messages[0]) if len(tag_instances): for tag in tag_instances: node.tags.add(tag) if is_truthy(request.GET.get('inherit_contributors') ) and validated_data['parent'].has_permission( user, 'write'): auth = get_user_auth(request) parent = validated_data['parent'] contributors = [] for contributor in parent.contributor_set.exclude(user=user): contributors.append({ 'user': contributor.user, 'permissions': parent.get_permissions(contributor.user), 'visible': contributor.visible }) if not contributor.user.is_registered: node.add_unregistered_contributor( fullname=contributor.user.fullname, email=contributor.user.email, auth=auth, permissions=parent.get_permissions(contributor.user), existing_user=contributor.user) node.add_contributors(contributors, auth=auth, log=True, save=True) return node
def get_renderer_context(self): context = super(PreprintProviderWithdrawRequestList, self).get_renderer_context() if is_truthy(self.request.query_params.get('meta[requests_state_counts]', False)): auth = get_user_auth(self.request) auth_user = getattr(auth, 'user', None) provider = self.get_provider() if auth_user and auth_user.has_perm('view_submissions', provider): context['meta'] = { 'requests_state_counts': provider.get_request_state_counts(), } return context
def get_meta_information(self, meta_data, value): """ For retrieving meta values, otherwise returns {} """ meta = {} for key in meta_data or {}: if key == 'count' or key == 'unread': show_related_counts = self.context['request'].query_params.get('related_counts', False) if utils.is_truthy(show_related_counts): meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue if not utils.is_truthy(show_related_counts): raise InvalidQueryStringError( detail="Acceptable values for the related_counts query param are 'true' or 'false'; got '{0}'".format(show_related_counts), parameter='related_counts' ) else: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) return meta
def get_serializer_context(self): """Inject request into the serializer context. Additionally, inject partial functions (request, object -> embed items) if the query string contains embeds. Allows multiple levels of nesting. """ context = super(JSONAPIBaseView, self).get_serializer_context() if self.kwargs.get('is_embedded'): embeds = [] else: embeds = self.request.query_params.getlist( 'embed') or self.request.query_params.getlist('embed[]') fields_check = self.get_serializer_class()._declared_fields.copy() serializer_class_type = get_meta_type(self.serializer_class, self.request) if 'fields[{}]'.format( serializer_class_type) in self.request.query_params: # Check only requested and mandatory fields sparse_fields = self.request.query_params['fields[{}]'.format( serializer_class_type)] for field in fields_check.copy().keys(): if field not in ('type', 'id', 'links') and field not in sparse_fields: fields_check.pop(field) for field in fields_check: if getattr(fields_check[field], 'field', None): fields_check[field] = fields_check[field].field for field in fields_check: if getattr(fields_check[field], 'always_embed', False) and field not in embeds: embeds.append(unicode(field)) if getattr(fields_check[field], 'never_embed', False) and field in embeds: embeds.remove(field) embeds_partials = {} for embed in embeds: embed_field = fields_check.get(embed) embeds_partials[embed] = self._get_embed_partial( embed, embed_field) context.update({ 'enable_esi': (utils.is_truthy( self.request.query_params.get('esi', django_settings.ENABLE_ESI)) and self.request.accepted_renderer.media_type in django_settings.ESI_MEDIA_TYPES), 'embed': embeds_partials, 'envelope': self.request.query_params.get('envelope', 'data'), }) return context
def convert_value(self, value, field): field_type = type(self.serializer_class._declared_fields[field]) value = value.strip() if field_type == ser.BooleanField: if utils.is_truthy(value): return True elif utils.is_falsy(value): return False # TODO Should we handle if the value is neither TRUTHY nor FALSY (first add test for how we'd expect it to # work, then ensure that it works that way). else: return value
def create(self, validated_data): request = self.context['request'] user = request.user if 'template_from' in validated_data: template_from = validated_data.pop('template_from') template_node = Node.load(key=template_from) if template_node is None: raise exceptions.NotFound if not template_node.has_permission( user, 'read', check_parent=False): raise exceptions.PermissionDenied validated_data.pop('creator') changed_data = {template_from: validated_data} node = template_node.use_as_template(auth=get_user_auth(request), changes=changed_data) else: node = Node(**validated_data) try: node.save() except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) if is_truthy(request.GET.get('inherit_contributors') ) and validated_data['parent'].has_permission( user, 'write'): auth = get_user_auth(request) parent = validated_data['parent'] contributors = [] for contributor in parent.contributors: if contributor is not user: contributors.append({ 'user': contributor, 'permissions': parent.get_permissions(contributor), 'visible': parent.get_visible(contributor) }) if not contributor.is_registered: node.add_unregistered_contributor( fullname=contributor.fullname, email=contributor.email, auth=auth, permissions=parent.get_permissions(contributor), existing_user=contributor) node.add_contributors(contributors, auth=auth, log=True, save=True) return node
def get_renderer_context(self): context = super(PreprintProviderPreprintList, self).get_renderer_context() show_counts = is_truthy(self.request.query_params.get('meta[reviews_state_counts]', False)) if show_counts: # TODO don't duplicate the above auth = get_user_auth(self.request) auth_user = getattr(auth, 'user', None) provider = get_object_or_error(PreprintProvider, self.kwargs['provider_id'], self.request, display_name='PreprintProvider') if auth_user and auth_user.has_perm('view_submissions', provider): context['meta'] = { 'reviews_state_counts': provider.get_reviewable_state_counts(), } return context
def build_query_from_field(self, field_name, operation): if field_name == 'parent': if operation['op'] == 'eq': if operation['value']: # filter[parent]=<nid> parent = utils.get_object_or_error(AbstractNode, operation['value'], self.request, display_name='parent') node_ids = NodeRelation.objects.filter( parent=parent, is_node_link=False).values_list('child_id', flat=True) return Q(id__in=node_ids) elif operation['op'] == 'ne': if not operation['value']: # filter[parent][ne]=null child_ids = (NodeRelation.objects.filter( is_node_link=False, child___contributors=self.get_user(), ).exclude(parent__type='osf.collection').exclude( child__is_deleted=True).values_list('child_id', flat=True)) return Q(id__in=set(child_ids)) # TODO: support this case in the future: # else filter[parent][ne]=<nid> raise InvalidFilterValue( detail= 'Only "null" is accepted as valid input to "filter[parent][ne]"' ) else: # filter[parent][gte]='' raise InvalidFilterOperator(value=operation['op'], valid_operators=['eq', 'ne']) if field_name == 'root': if None in operation['value']: raise InvalidFilterValue(value=operation['value']) with_as_root_query = Q(root__guids___id__in=operation['value']) return ~with_as_root_query if operation[ 'op'] == 'ne' else with_as_root_query if field_name == 'preprint': not_preprint_query = (Q(preprint_file=None) | Q(_is_preprint_orphan=True) | Q(_has_abandoned_preprint=True)) return ~not_preprint_query if utils.is_truthy( operation['value']) else not_preprint_query return super(NodesFilterMixin, self).build_query_from_field(field_name, operation)
def get_object(self): email_id = self.kwargs['email_id'] user = self.get_user() email = None # check to see if it's a confirmed email with hashed id decoded_id = hashids.decode(email_id) if decoded_id: try: email = user.emails.get(id=decoded_id[0]) except Email.DoesNotExist: email = None else: primary = email.address == user.username address = email.address confirmed = True verified = True is_merge = False # check to see if it's an unconfirmed email with a token elif user.unconfirmed_emails: try: email = user.email_verifications[email_id] address = email['email'] confirmed = email['confirmed'] verified = False primary = False is_merge = Email.objects.filter(address=address).exists() except KeyError: email = None if not email: raise NotFound # check for resend confirmation email query parameter in a GET request if self.request.method == 'GET' and is_truthy( self.request.query_params.get('resend_confirmation')): if not confirmed and settings.CONFIRM_REGISTRATIONS_BY_EMAIL: if throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): send_confirm_email(user, email=address, renew=True) user.email_last_sent = timezone.now() user.save() return UserEmail(email_id=email_id, address=address, confirmed=confirmed, verified=verified, primary=primary, is_merge=is_merge)
def create(self, validated_data): request = self.context['request'] user = request.user Node = apps.get_model('osf.Node') tag_instances = [] if 'tags' in validated_data: tags = validated_data.pop('tags') for tag in tags: tag_instance, created = Tag.objects.get_or_create(name=tag, defaults=dict(system=False)) tag_instances.append(tag_instance) if 'template_from' in validated_data: template_from = validated_data.pop('template_from') template_node = Node.load(template_from) if template_node is None: raise exceptions.NotFound if not template_node.has_permission(user, 'read', check_parent=False): raise exceptions.PermissionDenied validated_data.pop('creator') changed_data = {template_from: validated_data} node = template_node.use_as_template(auth=get_user_auth(request), changes=changed_data) else: node = Node(**validated_data) try: node.save() except ValidationError as e: raise InvalidModelValueError(detail=e.messages[0]) if len(tag_instances): for tag in tag_instances: node.tags.add(tag) if is_truthy(request.GET.get('inherit_contributors')) and validated_data['parent'].has_permission(user, 'write'): auth = get_user_auth(request) parent = validated_data['parent'] contributors = [] for contributor in parent.contributor_set.exclude(user=user): contributors.append({ 'user': contributor.user, 'permissions': parent.get_permissions(contributor.user), 'visible': contributor.visible }) if not contributor.user.is_registered: node.add_unregistered_contributor( fullname=contributor.user.fullname, email=contributor.user.email, auth=auth, permissions=parent.get_permissions(contributor.user), existing_user=contributor.user ) node.add_contributors(contributors, auth=auth, log=True, save=True) return node
def retract_registration(self, registration, validated_data, user): is_pending_retraction = validated_data.pop('is_pending_retraction', None) withdrawal_justification = validated_data.pop('withdrawal_justification', None) if withdrawal_justification and not is_pending_retraction: raise exceptions.ValidationError( 'You cannot provide a withdrawal_justification without a concurrent withdrawal request.', ) if is_truthy(is_pending_retraction): if registration.is_pending_retraction: raise exceptions.ValidationError('This registration is already pending withdrawal.') try: retraction = registration.retract_registration(user, withdrawal_justification, save=True) except NodeStateError as err: raise exceptions.ValidationError(str(err)) retraction.ask(registration.get_active_contributors_recursive(unique_users=True)) elif is_pending_retraction is not None: raise exceptions.ValidationError('You cannot set is_pending_withdrawal to False.')
def to_representation(self, data): # Don't envelope when serializing collection errors = {} bulk_skip_uneditable = utils.is_truthy(self.context['request'].query_params.get('skip_uneditable', False)) if isinstance(data, collections.Mapping): errors = data.get('errors', None) data = data.get('data', None) ret = [ self.child.to_representation(item, envelope=None) for item in data ] if errors and bulk_skip_uneditable: ret.append({'errors': errors}) return ret
def convert_value(self, value, field): """Used to convert incoming values from query params to the appropriate types for filter comparisons :param basestring value: value to be resolved :param rest_framework.fields.Field field: Field instance """ field = utils.decompose_field(field) if isinstance(field, ShowIfVersion): field = field.field if isinstance(field, ser.BooleanField): if utils.is_truthy(value): return True elif utils.is_falsy(value): return False else: raise InvalidFilterValue( value=value, field_type='bool', ) elif isinstance(field, self.DATE_FIELDS): try: ret = date_parser.parse(value, ignoretz=False) if not ret.tzinfo: ret = ret.replace(tzinfo=pytz.utc) return ret except ValueError: raise InvalidFilterValue( value=value, field_type='date', ) elif isinstance(field, (self.RELATIONSHIP_FIELDS, ser.SerializerMethodField, ser.ManyRelatedField)): if value == 'null': value = None return value elif isinstance(field, self.LIST_FIELDS) or isinstance( (getattr(field, 'field', None)), self.LIST_FIELDS): if value == 'null': value = [] return value else: try: return field.to_internal_value(value) except ValidationError: raise InvalidFilterValue(value=value, )
def parse_special_query_params(self, field_name, key, value, query): op = 'ne' if utils.is_truthy(value) else 'eq' query.get(key).update({ field_name: [{ 'op': op, 'value': None, 'source_field_name': 'preprint_file' }, { 'op': op, 'value': True, 'source_field_name': '_is_preprint_orphan' }, { 'op': op, 'value': True, 'source_field_name': '_has_abandoned_preprint' }] }) return query
def convert_value(self, value, field): """Used to convert incoming values from query params to the appropriate types for filter comparisons :param basestring value: value to be resolved :param rest_framework.fields.Field field: Field instance """ field = utils.decompose_field(field) if isinstance(field, ShowIfVersion): field = field.field if isinstance(field, ser.BooleanField): if utils.is_truthy(value): return True elif utils.is_falsy(value): return False else: raise InvalidFilterValue( value=value, field_type='bool', ) elif isinstance(field, self.DATE_FIELDS): try: ret = date_parser.parse(value, ignoretz=False) if not ret.tzinfo: ret = ret.replace(tzinfo=pytz.utc) return ret except ValueError: raise InvalidFilterValue( value=value, field_type='date', ) elif isinstance(field, (self.RELATIONSHIP_FIELDS, ser.SerializerMethodField)): if value == 'null': value = None return value elif isinstance(field, self.LIST_FIELDS) or isinstance((getattr(field, 'field', None)), self.LIST_FIELDS): if value == 'null': value = [] return value else: try: return field.to_internal_value(value) except ValidationError: raise InvalidFilterValue( value=value, )
def build_query_from_field(self, field_name, operation): if field_name == 'parent': if operation['op'] == 'eq': if operation['value']: # filter[parent]=<nid> parent = utils.get_object_or_error(AbstractNode, operation['value'], self.request, display_name='parent') node_ids = NodeRelation.objects.filter(parent=parent, is_node_link=False).values_list('child_id', flat=True) return Q(id__in=node_ids) elif operation['op'] == 'ne': if not operation['value']: # filter[parent][ne]=null child_ids = ( NodeRelation.objects.filter( is_node_link=False, child___contributors=self.get_user() ) .exclude(parent__type='osf.collection') .exclude(child__is_deleted=True) .values_list('child_id', flat=True) ) return Q(id__in=set(child_ids)) # TODO: support this case in the future: # else filter[parent][ne]=<nid> raise InvalidFilterValue(detail='Only "null" is accepted as valid input to "filter[parent][ne]"') else: # filter[parent][gte]='' raise InvalidFilterOperator(value=operation['op'], valid_operators=['eq', 'ne']) if field_name == 'root': if None in operation['value']: raise InvalidFilterValue(value=operation['value']) with_as_root_query = Q(root__guids___id__in=operation['value']) return ~with_as_root_query if operation['op'] == 'ne' else with_as_root_query if field_name == 'preprint': not_preprint_query = ( Q(preprint_file=None) | Q(_is_preprint_orphan=True) | Q(_has_abandoned_preprint=True) ) return ~not_preprint_query if utils.is_truthy(operation['value']) else not_preprint_query return super(NodesFilterMixin, self).build_query_from_field(field_name, operation)
def get_serializer_context(self): """Inject request into the serializer context. Additionally, inject partial functions (request, object -> embed items) if the query string contains embeds. Allows multiple levels of nesting. """ context = super(JSONAPIBaseView, self).get_serializer_context() if self.kwargs.get('is_embedded'): embeds = [] else: embeds = self.request.query_params.getlist('embed') or self.request.query_params.getlist('embed[]') fields_check = self.get_serializer_class()._declared_fields.copy() serializer_class_type = get_meta_type(self.serializer_class, self.request) if 'fields[{}]'.format(serializer_class_type) in self.request.query_params: # Check only requested and mandatory fields sparse_fields = self.request.query_params['fields[{}]'.format(serializer_class_type)] for field in fields_check.copy().keys(): if field not in ('type', 'id', 'links') and field not in sparse_fields: fields_check.pop(field) for field in fields_check: if getattr(fields_check[field], 'field', None): fields_check[field] = fields_check[field].field for field in fields_check: if getattr(fields_check[field], 'always_embed', False) and field not in embeds: embeds.append(unicode(field)) if getattr(fields_check[field], 'never_embed', False) and field in embeds: embeds.remove(field) embeds_partials = {} for embed in embeds: embed_field = fields_check.get(embed) embeds_partials[embed] = self._get_embed_partial(embed, embed_field) context.update({ 'enable_esi': ( utils.is_truthy(self.request.query_params.get('esi', django_settings.ENABLE_ESI)) and self.request.accepted_renderer.media_type in django_settings.ESI_MEDIA_TYPES ), 'embed': embeds_partials, 'envelope': self.request.query_params.get('envelope', 'data'), }) return context
def get_object(self): email_id = self.kwargs['email_id'] user = self.get_user() email = None # check to see if it's a confirmed email with hashed id decoded_id = hashids.decode(email_id) if decoded_id: try: email = user.emails.get(id=decoded_id[0]) except Email.DoesNotExist: email = None else: primary = email.address == user.username address = email.address confirmed = True verified = True is_merge = False # check to see if it's an unconfirmed email with a token elif user.unconfirmed_emails: try: email = user.email_verifications[email_id] address = email['email'] confirmed = email['confirmed'] verified = False primary = False is_merge = Email.objects.filter(address=address).exists() except KeyError: email = None if not email: raise NotFound # check for resend confirmation email query parameter in a GET request if self.request.method == 'GET' and is_truthy(self.request.query_params.get('resend_confirmation')): if not confirmed and settings.CONFIRM_REGISTRATIONS_BY_EMAIL: if throttle_period_expired(user.email_last_sent, settings.SEND_EMAIL_THROTTLE): send_confirm_email(user, email=address, renew=True) user.email_last_sent = timezone.now() user.save() return UserEmail(email_id=email_id, address=address, confirmed=confirmed, verified=verified, primary=primary, is_merge=is_merge)
def get_meta_information(self, metadata, provider): # Clone metadata because its mutability is questionable metadata = dict(metadata or {}) # Make counts opt-in show_counts = utils.is_truthy(self.context['request'].query_params.get('related_counts', False)) # Only include counts on detail routes is_detail = self.context.get('view') and not isinstance(self.context['view'], generics.ListAPIView) # Weird hack to avoid being called twice # get_meta_information is called with both self.related_meta and self.self_meta. # `is` could probably be used here but this seems more comprehensive. is_related_meta = metadata.pop('include_state_counts', False) if show_counts and is_detail and is_related_meta: # Finally, require users to have view_actions permissions auth = utils.get_user_auth(self.context['request']) if auth and auth.logged_in and auth.user.has_perm('view_actions', provider): metadata.update(provider.get_reviewable_state_counts()) return super(ReviewableCountsRelationshipField, self).get_meta_information(metadata, provider)
def get_meta_information(self, meta_data, value): """ For retrieving meta values, otherwise returns {} """ meta = {} for key in meta_data or {}: if key == 'count' or key == 'unread': show_related_counts = self.context['request'].query_params.get('related_counts', False) field_counts_requested = self.process_related_counts_parameters(show_related_counts, value) if utils.is_truthy(show_related_counts): meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) elif utils.is_falsy(show_related_counts): continue elif self.field_name in field_counts_requested: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) else: continue else: meta[key] = website_utils.rapply(meta_data[key], _url_val, obj=value, serializer=self.parent) return meta
def get_serializer_context(self): """Inject request into the serializer context. Additionally, inject partial functions (request, object -> embed items) if the query string contains embeds. Allows multiple levels of nesting. """ context = super(JSONAPIBaseView, self).get_serializer_context() if self.kwargs.get("is_embedded"): embeds = [] else: embeds = self.request.query_params.getlist("embed") fields_check = self.serializer_class._declared_fields.copy() for field in fields_check: if getattr(fields_check[field], "field", None): fields_check[field] = fields_check[field].field for field in fields_check: if getattr(fields_check[field], "always_embed", False) and field not in embeds: embeds.append(unicode(field)) if getattr(fields_check[field], "never_embed", False) and field in embeds: embeds.remove(field) embeds_partials = {} for embed in embeds: embed_field = fields_check.get(embed) embeds_partials[embed] = self._get_embed_partial(embed, embed_field) context.update( { "enable_esi": ( utils.is_truthy(self.request.query_params.get("esi", django_settings.ENABLE_ESI)) and self.request.accepted_renderer.media_type in django_settings.ESI_MEDIA_TYPES ), "embed": embeds_partials, "envelope": self.request.query_params.get("envelope", "data"), } ) return context
def parse_query_params(self, query_params): """Maps query params to a dict usable for filtering :param dict query_params: :return dict: of the format { <resolved_field_name>: { 'op': <comparison_operator>, 'value': <resolved_value>, 'source_field_name': <model_field_source_of_serializer_field> } } """ query = {} for key, value in query_params.iteritems(): match = self.QUERY_PATTERN.match(key) if match: match_dict = match.groupdict() fields = match_dict['fields'] field_names = re.findall(self.FILTER_FIELDS, fields.strip()) query.update({key: {}}) for field_name in field_names: field = self._get_field_or_error(field_name) op = match_dict.get('op') or self._get_default_operator( field) self._validate_operator(field, field_name, op) source_field_name = field_name if not isinstance(field, ser.SerializerMethodField): source_field_name = self.convert_key(field_name, field) # Special case date(time)s to allow for ambiguous date matches if isinstance(field, self.DATE_FIELDS): query.get(key).update({ field_name: self._parse_date_param(field, source_field_name, op, value) }) elif not isinstance( value, int) and (source_field_name == '_id' or source_field_name == 'root'): query.get(key).update({ field_name: { 'op': 'in', 'value': self.bulk_get_values(value, field), 'source_field_name': source_field_name } }) elif source_field_name == 'is_preprint': # TODO: Make this also include _is_preprint_orphan when value is false [#PREP-129] op = 'ne' if utils.is_truthy(value) else 'eq' query.get(key).update({ field_name: { 'op': op, 'value': None, 'source_field_name': 'preprint_file' } }) if utils.is_truthy(value): query['_is_preprint_orphan'] = { field_name: { 'op': 'ne', 'value': True, 'source_field_name': '_is_preprint_orphan' } } else: query.get(key).update({ field_name: { 'op': op, 'value': self.convert_value(value, field), 'source_field_name': source_field_name } }) return query
def should_resolve(request): query_params = getattr(request, 'query_params', request.GET) resolve = query_params.get('resolve') return resolve is None or is_truthy(resolve)
def parse_query_params(self, query_params): """Maps query params to a dict usable for filtering :param dict query_params: :return dict: of the format { <resolved_field_name>: { 'op': <comparison_operator>, 'value': <resolved_value>, 'source_field_name': <model_field_source_of_serializer_field> } } """ query = {} for key, value in query_params.iteritems(): match = self.QUERY_PATTERN.match(key) if match: match_dict = match.groupdict() fields = match_dict['fields'] field_names = re.findall(self.FILTER_FIELDS, fields.strip()) query.update({key: {}}) for field_name in field_names: field = self._get_field_or_error(field_name) op = match_dict.get('op') or self._get_default_operator(field) self._validate_operator(field, field_name, op) source_field_name = field_name if not isinstance(field, ser.SerializerMethodField): source_field_name = self.convert_key(field_name, field) # Special case date(time)s to allow for ambiguous date matches if isinstance(field, self.DATE_FIELDS): query.get(key).update({ field_name: self._parse_date_param(field, source_field_name, op, value) }) elif not isinstance(value, int) and (source_field_name == '_id' or source_field_name == 'root'): query.get(key).update({ field_name: { 'op': 'in', 'value': self.bulk_get_values(value, field), 'source_field_name': source_field_name } }) elif source_field_name == 'is_preprint': # TODO: Make this also include _is_preprint_orphan when value is false [#PREP-129] op = 'ne' if utils.is_truthy(value) else 'eq' query.get(key).update({ field_name: { 'op': op, 'value': None, 'source_field_name': 'preprint_file' } }) if utils.is_truthy(value): query['_is_preprint_orphan'] = { field_name: { 'op': 'ne', 'value': True, 'source_field_name': '_is_preprint_orphan' } } else: query.get(key).update({ field_name: { 'op': op, 'value': self.convert_value(value, field), 'source_field_name': source_field_name } }) return query