class NodeContributorsSerializer(JSONAPISerializer): """ Separate from UserSerializer due to necessity to override almost every field as read only """ non_anonymized_fields = ['bibliographic', 'permission'] filterable_fields = frozenset( ['id', 'bibliographic', 'permission', 'index']) id = IDField(source='_id', read_only=True) type = TypeField() index = ser.IntegerField(required=False, read_only=True, source='_order') bibliographic = ser.BooleanField( help_text= 'Whether the user will be included in citations for this node or not.', default=True) permission = ser.ChoiceField( choices=osf_permissions.PERMISSIONS, required=False, allow_null=True, default=osf_permissions.reduce_permissions( osf_permissions.DEFAULT_CONTRIBUTOR_PERMISSIONS), help_text= 'User permission level. Must be "read", "write", or "admin". Defaults to "write".' ) unregistered_contributor = ser.SerializerMethodField() links = LinksField({'self': 'get_absolute_url'}) users = RelationshipField(related_view='users:user-detail', related_view_kwargs={'user_id': '<user._id>'}, always_embed=True) node = RelationshipField(related_view='nodes:node-detail', related_view_kwargs={'node_id': '<node._id>'}) class Meta: type_ = 'contributors' def get_absolute_url(self, obj): return absolute_reverse( 'nodes:node-contributor-detail', kwargs={ 'user_id': obj.user._id, 'node_id': self.context['request'].parser_context['kwargs']['node_id'], 'version': self.context['request'].parser_context['kwargs']['version'] }) def get_unregistered_contributor(self, obj): unclaimed_records = obj.user.unclaimed_records.get(obj.node._id, None) if unclaimed_records: return unclaimed_records.get('name', None)
class PreprintContributorsCreateSerializer(NodeContributorsCreateSerializer, PreprintContributorsSerializer): """ Overrides PreprintContributorsSerializer to add email, full_name, send_email, and non-required index and users field. id and index redefined because of the two serializers we've inherited """ id = IDField(source='_id', required=False, allow_null=True) index = ser.IntegerField(required=False, source='_order') email_preferences = ['preprint', 'false']
class RegistrationDetailSerializer(RegistrationSerializer): """ Overrides RegistrationSerializer make _id required and other fields writeable """ id = IDField(source='_id', required=True) pending_withdrawal = HideIfWithdrawal(ser.BooleanField( source='is_pending_retraction', required=False, help_text='The registration is awaiting withdrawal approval by project admins.', )) withdrawal_justification = ser.CharField(required=False)
class WaterbutlerMetadataSerializer(ser.Serializer): source = ser.CharField(write_only=True) destination = DestinationSerializer(write_only=True) id = IDField(source='_id', read_only=True) kind = ser.CharField(read_only=True) name = ser.CharField(read_only=True, help_text='Display name used in the general user interface') created = ser.CharField(read_only=True) modified = ser.CharField(read_only=True) path = ser.CharField(read_only=True) checkout = ser.SerializerMethodField(read_only=True) version = ser.IntegerField(help_text='Latest file version', read_only=True, source='current_version_number') downloads = ser.SerializerMethodField() sha256 = ser.SerializerMethodField() md5 = ser.SerializerMethodField() size = ser.SerializerMethodField() def get_checkout(self, obj): return obj.checkout._id if obj.checkout else None def get_downloads(self, obj): return obj.get_download_count() def get_sha256(self, obj): return obj.versions.first().metadata.get('sha256', None) if obj.versions.exists() else None def get_md5(self, obj): return obj.versions.first().metadata.get('md5', None) if obj.versions.exists() else None def get_size(self, obj): if obj.versions.exists(): self.size = obj.versions.first().size return self.size return None def create(self, validated_data): source = validated_data.pop('source') destination = validated_data.pop('destination') name = validated_data.pop('name') try: return self.context['view'].perform_file_action(source, destination, name) except IntegrityError: raise exceptions.ValidationError('File already exists with this name.') except file_exceptions.FileNodeCheckedOutError: raise exceptions.ValidationError('Cannot move file as it is checked out.') except file_exceptions.FileNodeIsPrimaryFile: raise exceptions.ValidationError('Cannot move file as it is the primary file of preprint.') class Meta: type_ = 'file_metadata'
class NodeViewOnlyLinkSerializer(JSONAPISerializer): filterable_fields = frozenset(['anonymous', 'name', 'date_created']) key = ser.CharField(read_only=True) id = IDField(source='_id', read_only=True) date_created = DateByVersion(source='created', read_only=True) anonymous = ser.BooleanField(required=False, default=False) name = ser.CharField(required=False, default='Shared project link') links = LinksField({'self': 'get_absolute_url'}) creator = RelationshipField( related_view='users:user-detail', related_view_kwargs={'user_id': '<creator._id>'}, ) nodes = RelationshipField( related_view='view-only-links:view-only-link-nodes', related_view_kwargs={'link_id': '<_id>'}, self_view='view-only-links:view-only-link-nodes-relationships', self_view_kwargs={'link_id': '<_id>'}) def create(self, validated_data): name = validated_data.pop('name') user = get_user_auth(self.context['request']).user anonymous = validated_data.pop('anonymous') node = self.context['view'].get_node() try: view_only_link = new_private_link(name=name, user=user, nodes=[node], anonymous=anonymous) except ValidationError: raise exceptions.ValidationError('Invalid link name.') return view_only_link def get_absolute_url(self, obj): return absolute_reverse( 'nodes:node-view-only-link-detail', kwargs={ 'link_id': obj._id, 'node_id': self.context['request'].parser_context['kwargs']['node_id'], 'version': self.context['request'].parser_context['kwargs']['version'] }) class Meta: type_ = 'view_only_links'
class UserSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'full_name', 'given_name', 'middle_names', 'family_name', 'id' ]) id = IDField(source='_id', read_only=True) type = TypeField() full_name = ser.CharField(source='fullname', required=True, label='Full name', help_text='Display name used in the general user interface') given_name = ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations') middle_names = ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations') family_name = ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations') suffix = ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations') date_registered = ser.DateTimeField(read_only=True) # Social Fields are broken out to get around DRF complex object bug and to make API updating more user friendly. gitHub = DevOnly(ser.CharField(required=False, label='GitHub', source='social.github', allow_blank=True, help_text='GitHub Handle')) scholar = DevOnly(ser.CharField(required=False, source='social.scholar', allow_blank=True, help_text='Google Scholar Account')) personal_website = DevOnly(ser.URLField(required=False, source='social.personal', allow_blank=True, help_text='Personal Website')) twitter = DevOnly(ser.CharField(required=False, source='social.twitter', allow_blank=True, help_text='Twitter Handle')) linkedIn = DevOnly(ser.CharField(required=False, source='social.linkedIn', allow_blank=True, help_text='LinkedIn Account')) impactStory = DevOnly(ser.CharField(required=False, source='social.impactStory', allow_blank=True, help_text='ImpactStory Account')) orcid = DevOnly(ser.CharField(required=False, label='ORCID', source='social.orcid', allow_blank=True, help_text='ORCID')) researcherId = DevOnly(ser.CharField(required=False, label='ResearcherID', source='social.researcherId', allow_blank=True, help_text='ResearcherId Account')) profile_image_url = DevOnly(ser.SerializerMethodField(required=False, read_only=True)) def get_profile_image_url(self, user): size = self.context['request'].query_params.get('profile_image_size') return user.profile_image_url(size=size) links = LinksField({'html': 'absolute_url'}) nodes = JSONAPIHyperlinkedIdentityField(view_name='users:user-nodes', lookup_field='pk', lookup_url_kwarg='user_id', link_type='related') class Meta: type_ = 'users' def absolute_url(self, obj): return obj.absolute_url def update(self, instance, validated_data): assert isinstance(instance, User), 'instance must be a User' for attr, value in validated_data.items(): if 'social' == attr: for key, val in value.items(): instance.social[key] = val else: setattr(instance, attr, value) instance.save() return instance
class NodeAlternativeCitationSerializer(JSONAPISerializer): id = IDField(source='_id', read_only=True) type = TypeField() name = ser.CharField(required=True) text = ser.CharField(required=True) class Meta: type_ = 'citations' def create(self, validated_data): errors = self.error_checker(validated_data) if len(errors) > 0: raise exceptions.ValidationError(detail=errors) node = self.context['view'].get_node() auth = Auth(self.context['request']._user) citation = node.add_citation(auth, save=True, **validated_data) return citation def update(self, instance, validated_data): errors = self.error_checker(validated_data) if len(errors) > 0: raise exceptions.ValidationError(detail=errors) node = self.context['view'].get_node() auth = Auth(self.context['request']._user) instance = node.edit_citation(auth, instance, save=True, **validated_data) return instance def error_checker(self, data): errors = [] name = data.get('name', None) text = data.get('text', None) citations = self.context['view'].get_node().alternative_citations if not (self.instance and self.instance.name == name) and citations.find(Q('name', 'eq', name)).count() > 0: errors.append( "There is already a citation named '{}'".format(name)) if not (self.instance and self.instance.text == text): matching_citations = citations.find(Q('text', 'eq', text)) if matching_citations.count() > 0: names = "', '".join( [str(citation.name) for citation in matching_citations]) errors.append("Citation matches '{}'".format(names)) return errors def get_absolute_url(self, obj): # Citations don't have urls raise NotImplementedError
class NodeLinksSerializer(JSONAPISerializer): id = IDField(source='_id', read_only=True) type = TypeField() target_node_id = ser.CharField( write_only=True, source='node._id', help_text='The ID of the node that this Node Link points to') # TODO: We don't show the title because the current user may not have access to this node. We may want to conditionally # include this field in the future. # title = ser.CharField(read_only=True, source='node.title', help_text='The title of the node that this Node Link ' # 'points to') target_node = JSONAPIHyperlinkedIdentityField( view_name='nodes:node-detail', lookup_field='pk', link_type='related', lookup_url_kwarg='node_id') class Meta: type_ = 'node_links' links = LinksField({'self': 'get_absolute_url'}) def get_absolute_url(self, obj): node_id = self.context['request'].parser_context['kwargs']['node_id'] return absolute_reverse('nodes:node-pointer-detail', kwargs={ 'node_id': node_id, 'node_link_id': obj._id }) def create(self, validated_data): request = self.context['request'] user = request.user auth = Auth(user) node = self.context['view'].get_node() pointer_node = Node.load(validated_data['node']['_id']) if not pointer_node: raise exceptions.NotFound('Node not found.') try: pointer = node.add_pointer(pointer_node, auth, save=True) return pointer except ValueError: raise exceptions.ValidationError( 'Node link to node {} already in list'.format( pointer_node._id)) def update(self, instance, validated_data): pass
class NodeLinksSerializer(JSONAPISerializer): id = IDField(source='_id') type = TypeField() target_type = TargetTypeField(target_type='nodes') # TODO: We don't show the title because the current user may not have access to this node. We may want to conditionally # include this field in the future. # title = ser.CharField(read_only=True, source='node.title', help_text='The title of the node that this Node Link ' # 'points to') target_node = RelationshipField(related_view='nodes:node-detail', related_view_kwargs={'node_id': '<pk>'}, always_embed=True) class Meta: type_ = 'node_links' links = LinksField({'self': 'get_absolute_url'}) def get_absolute_url(self, obj): node_id = self.context['request'].parser_context['kwargs']['node_id'] return absolute_reverse('nodes:node-pointer-detail', kwargs={ 'node_id': node_id, 'node_link_id': obj._id }) def create(self, validated_data): request = self.context['request'] user = request.user auth = Auth(user) node = self.context['view'].get_node() target_node_id = validated_data['_id'] pointer_node = Node.load(target_node_id) if not pointer_node or pointer_node.is_collection: raise InvalidModelValueError( source={'pointer': '/data/relationships/node_links/data/id'}, detail='Target Node \'{}\' not found.'.format(target_node_id)) try: pointer = node.add_pointer(pointer_node, auth, save=True) return pointer except ValueError: raise InvalidModelValueError( source={'pointer': '/data/relationships/node_links/data/id'}, detail='Target Node \'{}\' already pointed to by \'{}\'.'. format(target_node_id, node._id)) def update(self, instance, validated_data): pass
class PreprintContributorsCreateSerializer(NodeContributorsCreateSerializer, PreprintContributorsSerializer): """ Overrides PreprintContributorsSerializer to add email, full_name, send_email, and non-required index and users field. id and index redefined because of the two serializers we've inherited """ id = IDField(source='_id', required=False, allow_null=True) index = ser.IntegerField(required=False, source='_order') email_preferences = ['preprint', 'false'] def get_proposed_permissions(self, validated_data): return validated_data.get('permission') or osf_permissions.WRITE
class ApiOAuthApplicationBaseSerializer(JSONAPISerializer): """Base serializer class for OAuth2 applications """ id = IDField( source='client_id', read_only=True, help_text='The client ID for this application (automatically generated)' ) type = TypeField() client_id = ser.CharField( help_text= 'The client ID for this application (automatically generated)', read_only=True, ) client_secret = ser.CharField( help_text= 'The client secret for this application (automatically generated)', read_only=True, ) # TODO: May change this later links = LinksField({ 'html': 'absolute_url', 'reset': 'reset_url', }) def absolute_url(self, obj): return obj.absolute_url def get_absolute_url(self, obj): return obj.get_absolute_url() def reset_url(self, obj): if StrictVersion( self.context['request'].version) < StrictVersion('2.15'): return absolute_reverse( 'applications:application-reset', kwargs={ 'client_id': obj.client_id, 'version': self.context['request'].parser_context['kwargs'] ['version'], }, ) class Meta: type_ = 'applications'
class GuidSerializer(JSONAPISerializer): class Meta: type_ = 'guids' filterable_fields = tuple() id = IDField(source='_id', read_only=True) type = TypeField() referent = RelationshipField( related_view=get_related_view, related_view_kwargs=get_related_view_kwargs, related_meta={ 'type': 'get_type', }, ) links = LinksField({ 'self': 'get_absolute_url', 'html': 'get_absolute_html_url', }) def get_type(self, guid): return get_type(guid.referent) def get_absolute_url(self, obj): return absolute_reverse( 'guids:guid-detail', kwargs={ 'guids': obj._id, 'version': self.context['request'].parser_context['kwargs']['version'], }, ) def get_absolute_html_url(self, obj): if not isinstance(obj.referent, BaseFileNode): return obj.referent.absolute_url return urlparse.urljoin(website_settings.DOMAIN, '/{}/'.format(obj._id)) def to_representation(self, obj): if self.context['view'].kwargs.get('is_embedded'): # Force the referent to serialize instead. obj = obj.referent ser = resolve(reverse( get_related_view(obj), kwargs={'node_id': obj._id, 'version': self.context['view'].kwargs.get('version', '2')}, )).func.cls.serializer_class(context=self.context) [ser.context.update({k: v}) for k, v in self.context.items()] return ser.to_representation(obj) return super(GuidSerializer, self).to_representation(obj)
class CommentReportSerializer(JSONAPISerializer): id = IDField(source='_id', read_only=True) type = TypeField() category = ser.ChoiceField(choices=[('spam', 'Spam or advertising'), ('hate', 'Hate speech'), ('violence', 'Violence or harmful behavior')], required=True) message = ser.CharField(source='text', required=False, allow_blank=True) links = LinksField({'self': 'get_absolute_url'}) class Meta: type_ = 'comment_reports' def get_absolute_url(self, obj): return absolute_reverse( 'comments:report-detail', kwargs={ 'user_id': obj._id, 'comment_id': self.context['request'].parser_context['kwargs']['comment_id'], 'version': self.context['request'].parser_context['kwargs']['version'] }) def create(self, validated_data): user = self.context['request'].user comment = self.context['view'].get_comment() if user._id in comment.reports and not comment.reports[user._id].get( 'retracted', True): raise ValidationError('Comment already reported.') try: comment.report_abuse(user, save=True, **validated_data) except ValueError: raise ValidationError('You cannot report your own comment.') return CommentReport(user._id, **validated_data) def update(self, comment_report, validated_data): user = self.context['request'].user comment = self.context['view'].get_comment() if user._id != comment_report._id: raise ValidationError( 'You cannot report a comment on behalf of another user.') try: comment.report_abuse(user, save=True, **validated_data) except ValueError: raise ValidationError('You cannot report your own comment.') return CommentReport(user._id, **validated_data)
class NodeCitationSerializer(JSONAPISerializer): id = IDField(read_only=True) title = ser.CharField(allow_blank=True, read_only=True) author = ser.ListField(read_only=True) publisher = ser.CharField(allow_blank=True, read_only=True) type = ser.CharField(allow_blank=True, read_only=True) doi = ser.CharField(allow_blank=True, read_only=True) links = LinksField({'self': 'get_absolute_url'}) def get_absolute_url(self, obj): return obj['URL'] class Meta: type_ = 'node-citation'
class UserEmailsSerializer(JSONAPISerializer): id = IDField(read_only=True) type = TypeField() email_address = ser.CharField(source='address') confirmed = ser.BooleanField(read_only=True) primary = ser.BooleanField(required=False) links = LinksField({ 'self': 'get_absolute_url', }) def get_absolute_url(self, obj): user = self.context['request'].user return absolute_reverse( 'users:user-email-detail', kwargs={ 'user_id': user._id, 'email_id': obj.id, 'version': self.context['request'].parser_context['kwargs']['version'], }, ) class Meta: type_ = 'user_emails' def create(self, validated_data): user = self.context['request'].user address = validated_data['address'] if address in user.unconfirmed_emails or address in user.emails.all().values_list('address', flat=True): raise Conflict('This user already has registered with the email address {}'.format(address)) try: token = user.add_unconfirmed_email(address) user.save() if CONFIRM_REGISTRATIONS_BY_EMAIL: send_confirm_email(user, email=address) except ValidationError as e: raise exceptions.ValidationError(e.args[0]) return UserEmail(email_id=token, address=address, confirmed=False, primary=False) def update(self, instance, validated_data): user = self.context['request'].user primary = validated_data.get('primary', None) if primary and instance.confirmed: user.username = instance.address user.save() elif primary and not instance.confirmed: raise exceptions.ValidationError('You cannot set an unconfirmed email address as your primary email address.') return instance
class PreprintCreateSerializer(PreprintSerializer): # Overrides PreprintSerializer to make id nullable, adds `create` id = IDField(source='_id', required=False, allow_null=True) def create(self, validated_data): creator = self.context['request'].user provider = validated_data.pop('provider', None) if not provider: raise exceptions.ValidationError(detail='You must specify a valid provider to create a preprint.') title = validated_data.pop('title') description = validated_data.pop('description', '') preprint = Preprint(provider=provider, title=title, creator=creator, description=description) preprint.save() return self.update(preprint, validated_data)
class MetaSchemaSerializer(JSONAPISerializer): id = IDField(source='_id', read_only=True) type = TypeField() name = ser.CharField(read_only=True) schema_version = ser.IntegerField(read_only=True) schema = ser.DictField(read_only=True) links = LinksField({'self': 'get_absolute_url'}) def get_absolute_url(self, obj): return obj.absolute_api_v2_url class Meta: type_ = 'metaschemas'
class LicenseSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'name', 'id', ]) non_anonymized_fields = ['type'] id = IDField(source='_id', read_only=True) type = TypeField() name = ser.CharField(required=True, help_text='License name') text = ser.CharField(required=True, help_text='Full text of the license') links = LinksField({'self': 'get_absolute_url'}) class Meta: type_ = 'licenses' def get_absolute_url(self, obj): return absolute_reverse('licenses:license-detail', kwargs={'license_id': obj._id})
class PreprintCreateSerializer(PreprintSerializer): # Overrides PreprintSerializer to make id nullable, adds `create` id = IDField(source='_id', required=False, allow_null=True) def create(self, validated_data): node = Node.load(validated_data.pop('node', None)) if not node: raise exceptions.NotFound('Unable to find Node with specified id.') elif node.is_deleted: raise exceptions.ValidationError( 'Cannot create a preprint from a deleted node.') auth = get_user_auth(self.context['request']) if not node.has_permission(auth.user, permissions.ADMIN): raise exceptions.PermissionDenied primary_file = validated_data.pop('primary_file', None) if not primary_file: raise exceptions.ValidationError( detail= 'You must specify a valid primary_file to create a preprint.') provider = validated_data.pop('provider', None) if not provider: raise exceptions.ValidationError( detail='You must specify a valid provider to create a preprint.' ) if PreprintService.find( Q('node', 'eq', node) & Q('provider', 'eq', provider)).count(): conflict = PreprintService.find_one( Q('node', 'eq', node) & Q('provider', 'eq', provider)) raise Conflict( 'Only one preprint per provider can be submitted for a node. Check `meta[existing_resource_id]`.', meta={'existing_resource_id': conflict._id}) preprint = PreprintService(node=node, provider=provider) self.set_field(preprint.set_primary_file, primary_file, auth, save=True) preprint.node._has_abandoned_preprint = True preprint.node.save() return self.update(preprint, validated_data)
class RegistrationIdentifierSerializer(JSONAPISerializer): category = ser.SerializerMethodField() filterable_fields = frozenset(['category']) value = ser.CharField(read_only=True) referent = RelationshipField( related_view='registrations:registration-detail', related_view_kwargs={'node_id': '<referent._id>'}, ) id = IDField(source='_id', read_only=True) links = LinksField({'self': 'self_url'}) class Meta: type_ = 'identifiers' def get_category(self, obj): if obj.category == 'legacy_doi': return 'doi' return obj.category def get_absolute_url(self, obj): return obj.absolute_api_v2_url def get_id(self, obj): return obj._id def get_detail_url(self, obj): return '{}/identifiers/{}'.format(obj.absolute_api_v2_url, obj._id) def self_url(self, obj): return absolute_reverse( 'identifiers:identifier-detail', kwargs={ 'identifier_id': obj._id, 'version': self.context['request'].parser_context['kwargs']['version'], }, )
class NodeContributorsSerializer(JSONAPISerializer): """ Separate from UserSerializer due to necessity to override almost every field as read only """ filterable_fields = frozenset([ 'id', 'bibliographic', 'permission' ]) id = IDField(source='_id', required=True) type = TypeField() bibliographic = ser.BooleanField(help_text='Whether the user will be included in citations for this node or not.', default=True) permission = ser.ChoiceField(choices=osf_permissions.PERMISSIONS, required=False, allow_null=True, default=osf_permissions.reduce_permissions(osf_permissions.DEFAULT_CONTRIBUTOR_PERMISSIONS), help_text='User permission level. Must be "read", "write", or "admin". Defaults to "write".') links = LinksField({ 'self': 'get_absolute_url' }) users = RelationshipField( related_view='users:user-detail', related_view_kwargs={'user_id': '<pk>'}, always_embed=True ) class Meta: type_ = 'contributors' def absolute_url(self, obj): return obj.absolute_url def get_absolute_url(self, obj): node_id = self.context['request'].parser_context['kwargs']['node_id'] return absolute_reverse( 'nodes:node-contributor-detail', kwargs={ 'node_id': node_id, 'user_id': obj._id } )
class GroupSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'name', ]) non_anonymized_fields = [ 'type', ] id = IDField(source='_id', read_only=True) type = TypeField() name = ser.CharField(required=True) date_created = VersionedDateTimeField(source='created', read_only=True) date_modified = VersionedDateTimeField(source='modified', read_only=True) links = LinksField({ 'self': 'get_absolute_url', }) def get_absolute_url(self, obj): return obj.get_absolute_url() members = RelationshipField( related_view='groups:group-members', related_view_kwargs={'group_id': '<_id>'}, ) class Meta: type_ = 'groups' def create(self, validated_data): group = OSFGroup(creator=validated_data['creator'], name=validated_data['name']) group.save() return group def update(self, instance, validated_data): if 'name' in validated_data: instance.set_group_name(validated_data.get('name')) instance.save() return instance
class ApiOAuth2ApplicationSerializer(ApiOAuthApplicationBaseSerializer): """Serialize data about a registered OAuth2 application""" id = IDField(source='client_id', read_only=True, help_text='The client ID for this application (automatically generated)') type = TypeField() name = ser.CharField(help_text='A short, descriptive name for this application', required=True) description = ser.CharField(help_text='An optional description displayed to all users of this application', required=False, allow_blank=True) home_url = ser.CharField(help_text="The full URL to this application's homepage.", required=True, validators=[URLValidator()], label="Home URL") callback_url = ser.CharField(help_text='The callback URL for this application (refer to OAuth documentation)', required=True, validators=[URLValidator()], label="Callback URL") owner = ser.CharField(help_text='The id of the user who owns this application', read_only=True, # Don't let user register an application in someone else's name source='owner._id') date_created = ser.DateTimeField(help_text='The date this application was generated (automatically filled in)', read_only=True) def create(self, validated_data): instance = ApiOAuth2Application(**validated_data) instance.save() return instance def update(self, instance, validated_data): assert isinstance(instance, ApiOAuth2Application), 'instance must be an ApiOAuth2Application' for attr, value in validated_data.iteritems(): setattr(instance, attr, value) instance.save() return instance
class InstitutionUserMetricsSerializer(JSONAPISerializer): class Meta: type_ = 'institution-users' id = IDField(source='user_id', read_only=True) user_name = ser.CharField(read_only=True) public_projects = ser.IntegerField(source='public_project_count', read_only=True) private_projects = ser.IntegerField(source='private_project_count', read_only=True) department = ser.CharField(read_only=True) user = RelationshipField( related_view='users:user-detail', related_view_kwargs={'user_id': '<user_id>'}, ) links = LinksField({ 'self': 'get_absolute_url', }) filterable_fields = frozenset([ 'id', 'user_name', 'public_projects', 'private_projects', 'department', ]) def get_absolute_url(self, obj): return absolute_reverse( 'institutions:institution-user-metrics', kwargs={ 'institution_id': self.context['request'].parser_context['kwargs'] ['institution_id'], 'version': 'v2', }, )
class UserIdentitiesSerializer(JSONAPISerializer): id = IDField(source='_id', read_only=True) type = TypeField() external_id = ser.CharField(read_only=True) status = ser.CharField(read_only=True) links = LinksField({ 'self': 'get_absolute_url', }) def get_absolute_url(self, obj): return absolute_reverse( 'users:user-identities-detail', kwargs={ 'user_id': self.context['request'].parser_context['kwargs']['user_id'], 'version': self.context['request'].parser_context['kwargs']['version'], 'identity_id': obj['_id'], }, ) class Meta: type_ = 'external-identities'
class LicenseSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'name', 'id', ]) non_anonymized_fields = ['type'] id = IDField(source='_id', read_only=True) type = TypeField() name = ser.CharField(required=True, help_text='License name') text = ser.CharField(required=True, help_text='Full text of the license') url = ser.URLField(required=False, help_text='URL for the license') required_fields = ser.ListField(source='properties', read_only=True, help_text='Fields required for this license (provided to help front-end validators)') links = LinksField({'self': 'get_absolute_url'}) class Meta: type_ = 'licenses' def get_absolute_url(self, obj): return absolute_reverse('licenses:license-detail', kwargs={ 'license_id': obj._id, 'version': self.context['request'].parser_context['kwargs']['version'] })
class UserSettingsSerializer(JSONAPISerializer): id = IDField(source='_id', read_only=True) type = TypeField() two_factor_enabled = ser.SerializerMethodField() subscribe_osf_general_email = ser.SerializerMethodField() subscribe_osf_help_email = ser.SerializerMethodField() def get_two_factor_enabled(self, obj): try: two_factor = TwoFactorUserSettings.objects.get(owner_id=obj.id) return not two_factor.deleted except TwoFactorUserSettings.DoesNotExist: return False def get_subscribe_osf_general_email(self, obj): return obj.mailchimp_mailing_lists.get(MAILCHIMP_GENERAL_LIST, False) def get_subscribe_osf_help_email(self, obj): return obj.osf_mailing_lists.get(OSF_HELP_LIST, False) links = LinksField({ 'self': 'get_absolute_url', }) def get_absolute_url(self, obj): return absolute_reverse( 'users:user_settings', kwargs={ 'user_id': obj._id, 'version': self.context['request'].parser_context['kwargs']['version'], }, ) class Meta: type_ = 'user_settings'
class ViewOnlyLinkDetailSerializer(JSONAPISerializer): key = ser.CharField(read_only=True) id = IDField(source='_id', read_only=True) date_created = ser.DateTimeField(read_only=True) anonymous = ser.BooleanField(required=False) name = ser.CharField(required=False) nodes = RelationshipField( related_view='view-only-links:view-only-link-nodes', related_view_kwargs={'link_id': '<_id>'}, self_view='view-only-links:view-only-link-nodes-relationships', self_view_kwargs={'link_id': '<_id>'}) creator = RelationshipField( related_view='users:user-detail', related_view_kwargs={'user_id': '<creator._id>'}, ) def get_absolute_url(self, obj): return absolute_reverse('nodes:node-view-only-link-detail', kwargs={'link_id': obj._id}) class Meta: type_ = 'view-only-links'
class InstitutionSummaryMetricSerializer(JSONAPISerializer): class Meta: type_ = 'institution-summary-metrics' id = IDField(source='institution_id', read_only=True) public_project_count = ser.IntegerField(read_only=True) private_project_count = ser.IntegerField(read_only=True) user_count = ser.IntegerField(read_only=True) links = LinksField({ 'self': 'get_absolute_url', }) def get_absolute_url(self, obj): return absolute_reverse( 'institutions:institution-summary-metrics', kwargs={ 'institution_id': self.context['request'].parser_context['kwargs'] ['institution_id'], 'version': 'v2', }, )
class PreprintCreateSerializer(PreprintSerializer): # Overrides PreprintSerializer to make id nullable, adds `create` id = IDField(source='_id', required=False, allow_null=True) def create(self, validated_data): node = validated_data.pop('node', {}) if isinstance(node, dict): node = Node.objects.create(creator=self.context['request'].user, **node) if node.is_deleted: raise exceptions.ValidationError( 'Cannot create a preprint from a deleted node.') auth = get_user_auth(self.context['request']) if not node.has_permission(auth.user, permissions.ADMIN): raise exceptions.PermissionDenied provider = validated_data.pop('provider', None) if not provider: raise exceptions.ValidationError( detail='You must specify a valid provider to create a preprint.' ) node_preprints = node.preprints.filter(provider=provider) if node_preprints.exists(): raise Conflict( 'Only one preprint per provider can be submitted for a node. Check `meta[existing_resource_id]`.', meta={'existing_resource_id': node_preprints.first()._id}) preprint = PreprintService(node=node, provider=provider) preprint.save() preprint.node._has_abandoned_preprint = True preprint.node.save() return self.update(preprint, validated_data)