class UserSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'full_name', 'given_name', 'middle_names', 'family_name', 'id', ]) writeable_method_fields = frozenset([ 'accepted_terms_of_service', ]) 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', max_length=186) 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(VersionedDateTimeField(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 = SocialField(required=False, min_version='2.10') employment = JSONAPIListField(required=False, source='jobs') education = JSONAPIListField(required=False, source='schools') can_view_reviews = ShowIfCurrentUser( ser.SerializerMethodField( help_text= 'Whether the current user has the `view_submissions` permission to ANY reviews provider.' )) accepted_terms_of_service = ShowIfCurrentUser(ser.SerializerMethodField()) 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', 'count': 'get_node_count', }, )) quickfiles = HideIfDisabled( QuickFilesRelationshipField( related_view='users:user-quickfiles', related_view_kwargs={'user_id': '<_id>'}, related_meta={'count': 'get_quickfiles_count'}, )) registrations = HideIfDisabled( RelationshipField( related_view='users:user-registrations', related_view_kwargs={'user_id': '<_id>'}, related_meta={'count': 'get_registration_count'}, )) 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>'}, related_meta={'count': 'get_institutions_count'}, )) preprints = HideIfDisabled( RelationshipField( related_view='users:user-preprints', related_view_kwargs={'user_id': '<_id>'}, related_meta={'count': 'get_preprint_count'}, )) emails = ShowIfCurrentUser( RelationshipField( related_view='users:user-emails', related_view_kwargs={'user_id': '<_id>'}, )) default_region = ShowIfCurrentUser( RegionRelationshipField( related_view='regions:region-detail', related_view_kwargs={'region_id': 'get_default_region_id'}, read_only=False, )) settings = ShowIfCurrentUser( RelationshipField( related_view='users:user_settings', related_view_kwargs={'user_id': '<_id>'}, read_only=True, )) 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_node_count(self, obj): auth = get_user_auth(self.context['request']) if obj != auth.user: return default_node_list_permission_queryset( user=auth.user, model_cls=Node).filter(contributor__user__id=obj.id).count() return default_node_list_queryset(model_cls=Node).filter( contributor__user__id=obj.id).count() def get_quickfiles_count(self, obj): return QuickFilesNode.objects.get( contributor__user__id=obj.id).files.filter( type='osf.osfstoragefile').count() def get_registration_count(self, obj): auth = get_user_auth(self.context['request']) user_registration = default_node_list_queryset( model_cls=Registration).filter(contributor__user__id=obj.id) return user_registration.can_view( user=auth.user, private_link=auth.private_link).count() def get_preprint_count(self, obj): auth_user = get_user_auth(self.context['request']).user user_preprints_query = Preprint.objects.filter( _contributors__guids___id=obj._id).exclude(machine_state='initial') return Preprint.objects.can_view(user_preprints_query, auth_user, allow_contribs=False).count() def get_institutions_count(self, obj): return obj.affiliated_institutions.count() def get_can_view_reviews(self, obj): group_qs = AbstractProviderGroupObjectPermission.objects.filter( group__user=obj, permission__codename='view_submissions') return group_qs.exists( ) or obj.abstractprovideruserobjectpermission_set.filter( permission__codename='view_submissions') def get_default_region_id(self, obj): try: # use the annotated value if possible region_id = obj.default_region except AttributeError: # use computed property if region annotation does not exist region_id = obj.osfstorage_region._id return region_id def get_accepted_terms_of_service(self, obj): return bool(obj.accepted_terms_of_service) def profile_image_url(self, user): size = self.context['request'].query_params.get('profile_image_size') return user.profile_image_url(size=size) def validate_employment(self, value): validate_user_json(value, 'employment-schema.json') return value def validate_education(self, value): validate_user_json(value, 'education-schema.json') return value def validate_social(self, value): schema = from_json('social-schema.json') try: jsonschema.validate(value, schema) except jsonschema.ValidationError as e: raise InvalidModelValueError(e) return value 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(): instance.social[key] = val elif 'accepted_terms_of_service' == attr: if value and not instance.accepted_terms_of_service: instance.accepted_terms_of_service = timezone.now() elif 'region_id' == attr: region_id = validated_data.get('region_id') user_settings = instance._settings_model( 'osfstorage').objects.get(owner=instance) user_settings.default_region_id = region_id user_settings.save() instance.default_region = self.context['request'].data[ 'default_region'] 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 UserSerializer(JSONAPISerializer): filterable_fields = frozenset([ 'full_name', 'given_name', 'middle_names', 'family_name', 'id', 'uid' ]) non_anonymized_fields = ['type'] id = IDField(source='_id', read_only=True) uid = 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', max_length=186) 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(VersionedDateTimeField(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 = 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>'}, )) preprints = HideIfDisabled( RelationshipField( related_view='users:user-preprints', 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