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 NodeSerializer(JSONAPISerializer): # TODO: If we have to redo this implementation in any of the other serializers, subclass ChoiceField and make it # handle blank choices properly. Currently DRF ChoiceFields ignore blank options, which is incorrect in this # instance filterable_fields = frozenset([ 'title', 'description', 'public', 'tags', 'category', 'date_created', 'date_modified', 'registration' ]) id = IDField(source='_id', read_only=True) type = TypeField() category_choices = Node.CATEGORY_MAP.keys() category_choices_string = ', '.join(["'{}'".format(choice) for choice in category_choices]) title = ser.CharField(required=True) description = ser.CharField(required=False, allow_blank=True, allow_null=True) category = ser.ChoiceField(choices=category_choices, help_text="Choices: " + category_choices_string) date_created = ser.DateTimeField(read_only=True) date_modified = ser.DateTimeField(read_only=True) registration = ser.BooleanField(read_only=True, source='is_registration') fork = ser.BooleanField(read_only=True, source='is_fork') collection = DevOnly(ser.BooleanField(read_only=True, source='is_folder')) dashboard = ser.BooleanField(read_only=True, source='is_dashboard') tags = JSONAPIListField(child=NodeTagField(), required=False) # Public is only write-able by admins--see update method public = ser.BooleanField(source='is_public', required=False, help_text='Nodes that are made public will give read-only access ' 'to everyone. Private nodes require explicit read ' 'permission. Write and admin access are the same for ' 'public and private nodes. Administrators on a parent ' 'node have implicit read permissions for all child nodes') links = LinksField({'html': 'get_absolute_url'}) # TODO: When we have osf_permissions.ADMIN permissions, make this writable for admins children = RelationshipField( related_view='nodes:node-children', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_node_count'}, ) comments = RelationshipField( related_view='nodes:node-comments', related_view_kwargs={'node_id': '<pk>'}, related_meta={'unread': 'get_unread_comments_count'}) contributors = RelationshipField( related_view='nodes:node-contributors', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_contrib_count'}, ) files = RelationshipField( related_view='nodes:node-providers', related_view_kwargs={'node_id': '<pk>'} ) forked_from = RelationshipField( related_view='nodes:node-detail', related_view_kwargs={'node_id': '<forked_from_id>'} ) node_links = DevOnly(RelationshipField( related_view='nodes:node-pointers', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_pointers_count'}, )) parent = RelationshipField( related_view='nodes:node-detail', related_view_kwargs={'node_id': '<parent_id>'} ) registrations = DevOnly(HideIfRegistration(RelationshipField( related_view='nodes:node-registrations', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_registration_count'} ))) logs = RelationshipField( related_view='nodes:node-logs', related_view_kwargs={'node_id': '<pk>'}, ) class Meta: type_ = 'nodes' def get_absolute_url(self, obj): return obj.absolute_url # TODO: See if we can get the count filters into the filter rather than the serializer. def get_user_auth(self, request): user = request.user if user.is_anonymous(): auth = Auth(None) else: auth = Auth(user) return auth def get_node_count(self, obj): auth = self.get_user_auth(self.context['request']) nodes = [node for node in obj.nodes if node.can_view(auth) and node.primary and not node.is_deleted] return len(nodes) def get_contrib_count(self, obj): return len(obj.contributors) def get_registration_count(self, obj): auth = self.get_user_auth(self.context['request']) registrations = [node for node in obj.node__registrations if node.can_view(auth)] return len(registrations) def get_pointers_count(self, obj): return len(obj.nodes_pointer) def get_unread_comments_count(self, obj): auth = self.get_user_auth(self.context['request']) user = auth.user return Comment.find_unread(user=user, node=obj) def create(self, validated_data): node = Node(**validated_data) try: node.save() except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) return node def update(self, node, validated_data): """Update instance with the validated data. Requires the request to be in the serializer context. """ assert isinstance(node, Node), 'node must be a Node' auth = self.get_user_auth(self.context['request']) old_tags = set([tag._id for tag in node.tags]) if 'tags' in validated_data: current_tags = set(validated_data.get('tags')) del validated_data['tags'] elif self.partial: current_tags = set(old_tags) else: current_tags = set() for new_tag in (current_tags - old_tags): node.add_tag(new_tag, auth=auth) for deleted_tag in (old_tags - current_tags): node.remove_tag(deleted_tag, auth=auth) if validated_data: try: node.update(validated_data, auth=auth) except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) except PermissionsError: raise exceptions.PermissionDenied return node
class CollectionSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'title', 'date_created', 'date_modified', ]) id = IDField(source='_id', read_only=True) type = TypeField() title = ser.CharField(required=True) date_created = ser.DateTimeField(read_only=True) date_modified = ser.DateTimeField(read_only=True) links = LinksField({}) node_links = DevOnly(RelationshipField( related_view='collections:node-pointers', related_view_kwargs={'collection_id': '<pk>'}, related_meta={'count': 'get_node_links_count'} )) # TODO: Add a self link to this when it's available linked_nodes = DevOnly(RelationshipField( related_view='collections:linked-nodes', related_view_kwargs={'collection_id': '<pk>'}, related_meta={'count': 'get_node_links_count'} )) class Meta: type_ = 'collections' def get_absolute_url(self, obj): return obj.absolute_url def get_node_links_count(self, obj): return len(obj.nodes_pointer) def create(self, validated_data): node = Node(**validated_data) node.is_folder = True node.category = '' try: node.save() except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) return node def update(self, node, validated_data): """Update instance with the validated data. Requires the request to be in the serializer context. """ assert isinstance(node, Node), 'collection must be a Node' auth = get_user_auth(self.context['request']) if validated_data: try: node.update(validated_data, auth=auth) except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) except PermissionsError: raise exceptions.PermissionDenied return node
class NodeSerializer(JSONAPISerializer): # TODO: If we have to redo this implementation in any of the other serializers, subclass ChoiceField and make it # handle blank choices properly. Currently DRF ChoiceFields ignore blank options, which is incorrect in this # instance filterable_fields = frozenset([ 'id', 'title', 'description', 'public', 'tags', 'category', 'date_created', 'date_modified', 'root', 'parent', 'contributors' ]) non_anonymized_fields = [ 'id', 'title', 'description', 'category', 'date_created', 'date_modified', 'registration', 'tags', 'public', 'links', 'children', 'comments', 'contributors', 'files', 'node_links', 'parent', 'root', 'logs', ] id = IDField(source='_id', read_only=True) type = TypeField() category_choices = Node.CATEGORY_MAP.items() category_choices_string = ', '.join(["'{}'".format(choice[0]) for choice in category_choices]) title = ser.CharField(required=True) description = ser.CharField(required=False, allow_blank=True, allow_null=True) category = ser.ChoiceField(choices=category_choices, help_text="Choices: " + category_choices_string) date_created = ser.DateTimeField(read_only=True) date_modified = ser.DateTimeField(read_only=True) registration = ser.BooleanField(read_only=True, source='is_registration') fork = ser.BooleanField(read_only=True, source='is_fork') collection = ser.BooleanField(read_only=True, source='is_collection') tags = JSONAPIListField(child=NodeTagField(), required=False) template_from = ser.CharField(required=False, allow_blank=False, allow_null=False, help_text='Specify a node id for a node you would like to use as a template for the ' 'new node. Templating is like forking, except that you do not copy the ' 'files, only the project structure. Some information is changed on the top ' 'level project by submitting the appropriate fields in the request body, ' 'and some information will not change. By default, the description will ' 'be cleared and the project will be made private.') current_user_permissions = ser.SerializerMethodField(help_text='List of strings representing the permissions ' 'for the current user on this node.') # Public is only write-able by admins--see update method public = ser.BooleanField(source='is_public', required=False, help_text='Nodes that are made public will give read-only access ' 'to everyone. Private nodes require explicit read ' 'permission. Write and admin access are the same for ' 'public and private nodes. Administrators on a parent ' 'node have implicit read permissions for all child nodes') links = LinksField({'html': 'get_absolute_html_url'}) # TODO: When we have osf_permissions.ADMIN permissions, make this writable for admins children = RelationshipField( related_view='nodes:node-children', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_node_count'}, ) comments = RelationshipField( related_view='nodes:node-comments', related_view_kwargs={'node_id': '<pk>'}, related_meta={'unread': 'get_unread_comments_count'}) contributors = RelationshipField( related_view='nodes:node-contributors', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_contrib_count'}, ) files = RelationshipField( related_view='nodes:node-providers', related_view_kwargs={'node_id': '<pk>'} ) forked_from = RelationshipField( related_view='nodes:node-detail', related_view_kwargs={'node_id': '<forked_from_id>'} ) node_links = RelationshipField( related_view='nodes:node-pointers', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_pointers_count'}, ) parent = RelationshipField( related_view='nodes:node-detail', related_view_kwargs={'node_id': '<parent_node._id>'}, filter_key='parent_node' ) registrations = DevOnly(HideIfRegistration(RelationshipField( related_view='nodes:node-registrations', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_registration_count'} ))) primary_institution = RelationshipField( related_view='nodes:node-institution-detail', related_view_kwargs={'node_id': '<pk>'}, self_view='nodes:node-relationships-institution', self_view_kwargs={'node_id': '<pk>'} ) root = RelationshipField( related_view='nodes:node-detail', related_view_kwargs={'node_id': '<root._id>'} ) logs = RelationshipField( related_view='nodes:node-logs', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_logs_count'} ) def get_current_user_permissions(self, obj): user = self.context['request'].user if user.is_anonymous(): return ['read'] permissions = obj.get_permissions(user=user) if not permissions: permissions = ['read'] return permissions class Meta: type_ = 'nodes' def get_absolute_url(self, obj): return obj.get_absolute_url() # TODO: See if we can get the count filters into the filter rather than the serializer. def get_logs_count(self, obj): return len(obj.logs) def get_node_count(self, obj): auth = get_user_auth(self.context['request']) nodes = [node for node in obj.nodes if node.can_view(auth) and node.primary and not node.is_deleted] return len(nodes) def get_contrib_count(self, obj): return len(obj.contributors) def get_registration_count(self, obj): auth = get_user_auth(self.context['request']) registrations = [node for node in obj.registrations_all if node.can_view(auth)] return len(registrations) def get_pointers_count(self, obj): return len(obj.nodes_pointer) def get_unread_comments_count(self, obj): user = get_user_auth(self.context['request']).user node_comments = Comment.find_n_unread(user=user, node=obj, page='node') return { 'node': node_comments } def create(self, validated_data): if 'template_from' in validated_data: request = self.context['request'] user = request.user 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) return node def update(self, node, validated_data): """Update instance with the validated data. Requires the request to be in the serializer context. """ assert isinstance(node, Node), 'node must be a Node' auth = get_user_auth(self.context['request']) old_tags = set([tag._id for tag in node.tags]) if 'tags' in validated_data: current_tags = set(validated_data.get('tags')) del validated_data['tags'] elif self.partial: current_tags = set(old_tags) else: current_tags = set() for new_tag in (current_tags - old_tags): node.add_tag(new_tag, auth=auth) for deleted_tag in (old_tags - current_tags): node.remove_tag(deleted_tag, auth=auth) if validated_data: try: node.update(validated_data, auth=auth) except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) except PermissionsError: raise exceptions.PermissionDenied except NodeUpdateError as e: raise exceptions.ValidationError(detail=e.reason) return node
class RegistrationSerializer(NodeSerializer): pending_embargo_approval = HideIfRetraction( ser.BooleanField( read_only=True, source='is_pending_embargo', help_text= 'The associated Embargo is awaiting approval by project admins.')) pending_registration_approval = HideIfRetraction( ser.BooleanField( source='is_pending_registration', read_only=True, help_text= 'The associated RegistrationApproval is awaiting approval by project admins.' )) pending_retraction = HideIfRetraction( ser.BooleanField( source='is_pending_retraction', read_only=True, help_text= 'The registration is awaiting retraction approval by project admins.' )) retracted = ser.BooleanField( source='is_retracted', read_only=True, help_text='The registration has been retracted.') date_registered = ser.DateTimeField(source='registered_date', read_only=True, help_text='Date time of registration.') embargo_end_date = HideIfRetraction( ser.SerializerMethodField( help_text='When the embargo on this registration will be lifted.')) retraction_justification = ser.CharField(source='retraction.justification', read_only=True) registration_supplement = ser.SerializerMethodField() registered_meta = HideIfRetraction( ser.SerializerMethodField( help_text= 'A dictionary with supplemental registration questions and responses.' )) registered_by = HideIfRetraction( RelationshipField( related_view='users:user-detail', related_view_kwargs={'user_id': '<registered_user_id>'})) registered_from = HideIfRetraction( RelationshipField( related_view='nodes:node-detail', related_view_kwargs={'node_id': '<registered_from_id>'})) children = HideIfRetraction( RelationshipField( related_view='registrations:registration-children', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_node_count'}, )) comments = HideIfRetraction( RelationshipField(related_view='registrations:registration-comments', related_view_kwargs={'node_id': '<pk>'}, related_meta={'unread': 'get_unread_comments_count'})) contributors = RelationshipField( related_view='registrations:registration-contributors', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_contrib_count'}) files = HideIfRetraction( RelationshipField(related_view='registrations:registration-providers', related_view_kwargs={'node_id': '<pk>'})) forked_from = HideIfRetraction( RelationshipField(related_view='nodes:node-detail', related_view_kwargs={'node_id': '<forked_from_id>'})) node_links = DevOnly( HideIfRetraction( RelationshipField( related_view='registrations:registration-pointers', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_pointers_count'}))) parent = HideIfRetraction( RelationshipField(related_view='registrations:registration-detail', related_view_kwargs={'node_id': '<parent_id>'})) logs = HideIfRetraction( RelationshipField( related_view='registrations:registration-logs', related_view_kwargs={'node_id': '<pk>'}, )) root = HideIfRetraction( RelationshipField(related_view='registrations:registration-detail', related_view_kwargs={'node_id': '<root._id>'})) primary_institution = RelationshipField( related_view='registrations:registration-institution-detail', related_view_kwargs={'node_id': '<pk>'}) # TODO: Finish me # TODO: Override create? links = LinksField({ 'self': 'get_registration_url', 'html': 'get_absolute_html_url' }) def get_registration_url(self, obj): return absolute_reverse('registrations:registration-detail', kwargs={'node_id': obj._id}) def get_absolute_url(self, obj): return self.get_registration_url(obj) def get_registered_meta(self, obj): if obj.registered_meta: meta_values = obj.registered_meta.values()[0] try: return json.loads(meta_values) except TypeError: return meta_values except ValueError: return meta_values return None def get_embargo_end_date(self, obj): if obj.embargo_end_date: return obj.embargo_end_date return None def get_registration_supplement(self, obj): if obj.registered_schema: schema = obj.registered_schema[0] if schema is None: return None return schema.name return None def update(self, *args, **kwargs): raise exceptions.APIException('Registrations cannot be modified.') class Meta: type_ = 'registrations'
class UserSerializer(JSONAPISerializer): filterable_fields = frozenset( ['full_name', 'given_name', 'middle_names', 'family_name', 'id']) non_anonymized_fields = ['type'] 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( AllowMissing(ser.CharField(required=False, source='social.github', allow_blank=True, help_text='GitHub Handle'), required=False, source='social.github')) scholar = DevOnly( AllowMissing(ser.CharField(required=False, source='social.scholar', allow_blank=True, help_text='Google Scholar Account'), required=False, source='social.scholar')) personal_website = DevOnly( AllowMissing(ser.URLField(required=False, source='social.personal', allow_blank=True, help_text='Personal Website'), required=False, source='social.personal')) twitter = DevOnly( AllowMissing(ser.CharField(required=False, source='social.twitter', allow_blank=True, help_text='Twitter Handle'), required=False, source='social.twitter')) linkedIn = DevOnly( AllowMissing(ser.CharField(required=False, source='social.linkedIn', allow_blank=True, help_text='LinkedIn Account'), required=False, source='social.linkedIn')) impactStory = DevOnly( AllowMissing(ser.CharField(required=False, source='social.impactStory', allow_blank=True, help_text='ImpactStory Account'), required=False, source='social.impactStory')) orcid = DevOnly( AllowMissing(ser.CharField(required=False, source='social.orcid', allow_blank=True, help_text='ORCID'), required=False, source='social.orcid')) researcherId = DevOnly( AllowMissing(ser.CharField(required=False, source='social.researcherId', allow_blank=True, help_text='ResearcherId Account'), required=False, source='social.researcherId')) researchGate = DevOnly( AllowMissing(ser.CharField(required=False, source='social.researchGate', allow_blank=True, help_text='ResearchGate Account'), required=False, source='social.researchGate')) academiaInstitution = DevOnly( AllowMissing(ser.CharField(required=False, source='social.academiaInstitution', allow_blank=True, help_text='AcademiaInstitution Field'), required=False, source='social.academiaInstitution')) academiaProfileID = DevOnly( AllowMissing(ser.CharField(required=False, source='social.academiaProfileID', allow_blank=True, help_text='AcademiaProfileID Field'), required=False, source='social.academiaProfileID')) baiduScholar = DevOnly( AllowMissing(ser.CharField(required=False, source='social.baiduScholar', allow_blank=True, help_text='Baidu Scholar Account'), required=False, source='social.baiduScholar')) links = LinksField({ 'html': 'absolute_url', 'profile_image': 'profile_image_url', }) nodes = RelationshipField( related_view='users:user-nodes', related_view_kwargs={'user_id': '<pk>'}, ) institutions = RelationshipField( related_view='users:user-institutions', related_view_kwargs={'user_id': '<pk>'}, self_view='users:user-institutions-relationship', self_view_kwargs={'user_id': '<pk>'}, ) class Meta: type_ = 'users' def absolute_url(self, obj): if obj is not None: return obj.absolute_url return None def get_absolute_url(self, obj): return absolute_reverse('users:user-detail', kwargs={'user_id': obj._id}) def profile_image_url(self, user): size = self.context['request'].query_params.get('profile_image_size') return user.profile_image_url(size=size) 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) try: instance.save() except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) return instance
class UserSerializer(JSONAPISerializer): filterable_fields = frozenset( ['full_name', 'given_name', 'middle_names', 'family_name', 'id']) non_anonymized_fields = ['type'] 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 = HideIfDisabled( ser.CharField(required=False, allow_blank=True, help_text='For bibliographic citations')) date_registered = HideIfDisabled(DateByVersion(read_only=True)) active = HideIfDisabled( ser.BooleanField(read_only=True, source='is_active')) timezone = HideIfDisabled( ser.CharField(required=False, help_text="User's timezone, e.g. 'Etc/UTC")) locale = HideIfDisabled( ser.CharField(required=False, help_text="User's locale, e.g. 'en_US'")) social = ListDictField(required=False) can_view_reviews = ShowIfCurrentUser( ser.SerializerMethodField( help_text= 'Whether the current user has the `view_submissions` permission to ANY reviews provider.' )) links = HideIfDisabled( LinksField({ 'html': 'absolute_url', 'profile_image': 'profile_image_url', })) nodes = HideIfDisabled( RelationshipField( related_view='users:user-nodes', related_view_kwargs={'user_id': '<_id>'}, related_meta={'projects_in_common': 'get_projects_in_common'}, )) quickfiles = HideIfDisabled( QuickFilesRelationshipField( related_view='users:user-quickfiles', related_view_kwargs={'user_id': '<_id>'}, )) registrations = DevOnly( HideIfDisabled( RelationshipField( related_view='users:user-registrations', related_view_kwargs={'user_id': '<_id>'}, ))) institutions = HideIfDisabled( RelationshipField( related_view='users:user-institutions', related_view_kwargs={'user_id': '<_id>'}, self_view='users:user-institutions-relationship', self_view_kwargs={'user_id': '<_id>'}, )) actions = ShowIfCurrentUser( RelationshipField( related_view='users:user-action-list', related_view_kwargs={'user_id': '<_id>'}, )) class Meta: type_ = 'users' def get_projects_in_common(self, obj): user = get_user_auth(self.context['request']).user if obj == user: return user.contributor_to.count() return obj.n_projects_in_common(user) def absolute_url(self, obj): if obj is not None: return obj.absolute_url return None def get_absolute_url(self, obj): return absolute_reverse( 'users:user-detail', kwargs={ 'user_id': obj._id, 'version': self.context['request'].parser_context['kwargs']['version'] }) def get_can_view_reviews(self, obj): group_qs = GroupObjectPermission.objects.filter( group__user=obj, permission__codename='view_submissions') return group_qs.exists() or obj.userobjectpermission_set.filter( permission__codename='view_submissions') def profile_image_url(self, user): size = self.context['request'].query_params.get('profile_image_size') return user.profile_image_url(size=size) def update(self, instance, validated_data): assert isinstance(instance, OSFUser), 'instance must be a User' for attr, value in validated_data.items(): if 'social' == attr: for key, val in value.items(): # currently only profileWebsites are a list, the rest of the social key only has one value if key == 'profileWebsites': instance.social[key] = val else: if len(val) > 1: raise InvalidModelValueError( detail= '{} only accept a list of one single value'. format(key)) instance.social[key] = val[0] else: setattr(instance, attr, value) try: instance.save() except ValidationValueError as e: raise InvalidModelValueError(detail=e.message) except ValidationError as e: raise InvalidModelValueError(e) return instance
class RegistrationSerializer(NodeSerializer): retracted = ser.BooleanField( source='is_retracted', read_only=True, help_text='Whether this registration has been retracted.') date_registered = ser.DateTimeField(source='registered_date', read_only=True, help_text='Date time of registration.') retraction_justification = ser.CharField(source='retraction.justification', read_only=True) pending_retraction = HideIfRetraction( ser.BooleanField(source='is_pending_retraction', read_only=True, help_text='Is this registration pending retraction?')) pending_registration_approval = HideIfRetraction( ser.BooleanField( source='sanction.pending_approval', read_only=True, help_text='Does this registration have a sanction pending approval?' )) pending_embargo = HideIfRetraction( ser.BooleanField(read_only=True, source='is_pending_embargo', help_text='Is this registration pending embargo?')) registered_meta = HideIfRetraction( ser.SerializerMethodField( help_text= 'A dictionary with embargo end date, whether registration choice was immediate or embargoed,' ' and answers to supplemental registration questions')) registration_supplement = ser.SerializerMethodField() registered_by = HideIfRetraction( RelationshipField( related_view='users:user-detail', related_view_kwargs={'user_id': '<registered_user_id>'})) registered_from = HideIfRetraction( RelationshipField( related_view='nodes:node-detail', related_view_kwargs={'node_id': '<registered_from_id>'})) children = HideIfRetraction( RelationshipField( related_view='registrations:registration-children', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_node_count'}, )) comments = HideIfRetraction( RelationshipField(related_view='registrations:registration-comments', related_view_kwargs={'node_id': '<pk>'}, related_meta={'unread': 'get_unread_comments_count'})) contributors = RelationshipField( related_view='registrations:registration-contributors', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_contrib_count'}) files = HideIfRetraction( RelationshipField(related_view='registrations:registration-providers', related_view_kwargs={'node_id': '<pk>'})) forked_from = HideIfRetraction( RelationshipField(related_view='nodes:node-detail', related_view_kwargs={'node_id': '<forked_from_id>'})) node_links = DevOnly( HideIfRetraction( RelationshipField( related_view='registrations:registration-pointers', related_view_kwargs={'node_id': '<pk>'}, related_meta={'count': 'get_pointers_count'}))) parent = HideIfRetraction( RelationshipField(related_view='nodes:node-detail', related_view_kwargs={'node_id': '<parent_id>'})) logs = HideIfRetraction( RelationshipField( related_view='nodes:node-logs', related_view_kwargs={'node_id': '<pk>'}, )) # TODO: Finish me # TODO: Override create? links = LinksField({ 'self': 'get_registration_url', 'html': 'get_absolute_url' }) def get_registration_url(self, obj): return absolute_reverse('registrations:registration-detail', kwargs={'node_id': obj._id}) def get_absolute_url(self, obj): return obj.absolute_url def get_registered_meta(self, obj): if obj.registered_meta: meta_values = obj.registered_meta.values()[0] try: return json.loads(meta_values) except TypeError: return meta_values except ValueError: return meta_values return None def get_registration_supplement(self, obj): if obj.registered_schema: schema = obj.registered_schema[0] if schema is None: return None return schema.name return None def update(self, *args, **kwargs): raise exceptions.APIException('Registrations cannot be modified.') class Meta: type_ = 'registrations'