<commit_before>from guardian.models import GroupObjectPermission

from django.utils import timezone
from rest_framework import serializers as ser

from api.base.exceptions import InvalidModelValueError
from api.base.serializers import (
    BaseAPISerializer, JSONAPISerializer, JSONAPIRelationshipSerializer,
    VersionedDateTimeField, HideIfDisabled, IDField,
    Link, LinksField, ListDictField, TypeField, RelationshipField,
    WaterbutlerLink, ShowIfCurrentUser, DevOnly
)
from api.base.utils import absolute_reverse, get_user_auth, waterbutler_api_url_for
from api.files.serializers import QuickFilesSerializer
from osf.exceptions import ValidationValueError, ValidationError
from osf.models import OSFUser, QuickFilesNode, Registration, PreprintService


class QuickFilesRelationshipField(RelationshipField):

    def to_representation(self, value):
        relationship_links = super(QuickFilesRelationshipField, self).to_representation(value)
        quickfiles_guid = value.nodes_created.filter(type=QuickFilesNode._typedmodels_type).values_list('guids___id', flat=True).get()
        upload_url = waterbutler_api_url_for(quickfiles_guid, 'osfstorage')
        relationship_links['links']['upload'] = {
            'href': upload_url,
            'meta': {}
        }
        relationship_links['links']['download'] = {
            'href': '{}?zip='.format(upload_url),
            'meta': {}
        }
        return relationship_links


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 = ListDictField(required=False)
    employment = DevOnly(ser.ListField(child=ser.DictField(), source='jobs', read_only=True))
    education = DevOnly(ser.ListField(child=ser.DictField(), source='schools', read_only=True))
    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'}
    ))

    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):
        return obj.nodes.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):
        return Registration.objects.filter(contributor__user__id=obj.id).count()

    def get_preprint_count(self, obj):
        return PreprintService.objects.filter(node___contributors__guids___id=obj._id).count()

    def get_institutions_count(self, obj):
        return obj.affiliated_institutions.all().count()

    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 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 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:<commit_msg>Modify counts to be more accurate to our use case<commit_after>from guardian.models import GroupObjectPermission

from django.utils import timezone
from django.db.models import Q

from rest_framework import serializers as ser

from api.base.exceptions import InvalidModelValueError
from api.base.serializers import (
    BaseAPISerializer, JSONAPISerializer, JSONAPIRelationshipSerializer,
    VersionedDateTimeField, HideIfDisabled, IDField,
    Link, LinksField, ListDictField, TypeField, RelationshipField,
    WaterbutlerLink, ShowIfCurrentUser, DevOnly
)
from api.base.utils import absolute_reverse, get_user_auth, waterbutler_api_url_for, default_node_list_queryset
from api.files.serializers import QuickFilesSerializer
from osf.exceptions import ValidationValueError, ValidationError
from osf.models import OSFUser, QuickFilesNode, Registration, PreprintService, Node

class QuickFilesRelationshipField(RelationshipField):

    def to_representation(self, value):
        relationship_links = super(QuickFilesRelationshipField, self).to_representation(value)
        quickfiles_guid = value.nodes_created.filter(type=QuickFilesNode._typedmodels_type).values_list('guids___id', flat=True).get()
        upload_url = waterbutler_api_url_for(quickfiles_guid, 'osfstorage')
        relationship_links['links']['upload'] = {
            'href': upload_url,
            'meta': {}
        }
        relationship_links['links']['download'] = {
            'href': '{}?zip='.format(upload_url),
            'meta': {}
        }
        return relationship_links


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 = ListDictField(required=False)
    employment = DevOnly(ser.ListField(child=ser.DictField(), source='jobs', read_only=True))
    education = DevOnly(ser.ListField(child=ser.DictField(), source='schools', read_only=True))
    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'}
    ))

    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):
        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):
        return default_node_list_queryset(model_cls=Registration).filter(contributor__user__id=obj.id).count()

    def get_preprint_count(self, obj):
        return PreprintService.objects.filter(Q(node___contributors__guids___id=obj._id)
                                              & Q(Q(node__is_public=True) |
                                                  Q(is_published=True) |
                                                  Q(node__contributor__admin=True))
                                              ).count()

    def get_institutions_count(self, obj):
        return obj.affiliated_institutions.all().count()

    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 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 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:
{'fraction_non_alphanumeric': 0.10784863349684654, 'fraction_numerical': 0.0004204625087596356, 'mean_word_length': 3.6897798225435428, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5751614', 'n_tokens_mistral': 4190, 'n_tokens_neox': 4069, 'n_words': 734}