Beispiel #1
0
class WikiSerializer(JSONAPISerializer):

    filterable_fields = frozenset(['name', 'date_modified'])

    id = IDField(source='_id', read_only=True)
    type = TypeField()
    name = ser.CharField(source='page_name')
    kind = ser.SerializerMethodField()
    size = ser.SerializerMethodField()
    path = ser.SerializerMethodField()
    materialized_path = ser.SerializerMethodField(method_name='get_path')
    date_modified = ser.DateTimeField(source='date')
    content_type = ser.SerializerMethodField()
    extra = ser.SerializerMethodField(
        help_text='Additional metadata about this wiki')

    user = RelationshipField(related_view='users:user-detail',
                             related_view_kwargs={'user_id': '<user._id>'})
    node = RelationshipField(related_view='nodes:node-detail',
                             related_view_kwargs={'node_id': '<node._id>'})
    comments = RelationshipField(
        related_view='nodes:node-comments',
        related_view_kwargs={'node_id': '<node._id>'},
        related_meta={'unread': 'get_unread_comments_count'},
        filter={'target': '<pk>'})

    # LinksField.to_representation adds link to "self"
    links = LinksField({
        'info':
        Link('wikis:wiki-detail', kwargs={'wiki_id': '<_id>'}),
        'download':
        'get_wiki_content'
    })

    class Meta:
        type_ = 'wikis'

    def get_absolute_url(self, obj):
        return obj.get_absolute_url()

    def get_path(self, obj):
        return '/{}'.format(obj)

    def get_kind(self, obj):
        return 'file'

    def get_size(self, obj):
        return sys.getsizeof(obj.content)

    def get_content_type(self, obj):
        return 'text/markdown'

    def get_extra(self, obj):
        return {'version': obj.version}

    def get_wiki_content(self, obj):
        return absolute_reverse('wikis:wiki-content',
                                kwargs={
                                    'wiki_id': obj._id,
                                })
Beispiel #2
0
class NodeFilesSerializer(JSONAPISerializer):

    id = ser.CharField(read_only=True, source='_id')
    provider = ser.CharField(read_only=True)
    path = ser.CharField(read_only=True)
    item_type = ser.CharField(read_only=True)
    name = ser.CharField(read_only=True)
    metadata = ser.DictField(read_only=True)

    class Meta:
        type_ = 'files'

    links = LinksField({
        'self': WaterbutlerLink(kwargs={'node_id': '<node_id>'}),
        'self_methods': 'valid_self_link_methods',
        'related': Link('nodes:node-files', kwargs={'node_id': '<node_id>'},
                        query_kwargs={'path': '<path>', 'provider': '<provider>'}),
    })

    @staticmethod
    def valid_self_link_methods(obj):
        return obj['valid_self_link_methods']

    def create(self, validated_data):
        # TODO
        pass

    def update(self, instance, validated_data):
        # TODO
        pass
Beispiel #3
0
class UserQuickFilesSerializer(QuickFilesSerializer):
    links = LinksField({
        'info': Link('files:file-detail', kwargs={'file_id': '<_id>'}),
        'upload': WaterbutlerLink(),
        'delete': WaterbutlerLink(),
        'move': WaterbutlerLink(),
        'download': WaterbutlerLink(must_be_file=True),
    })
Beispiel #4
0
class UserSerializer(JSONAPISerializer):
    filterable_fields = frozenset(
        ['fullname', 'given_name', 'middle_name', 'family_name', 'id'])
    id = ser.CharField(read_only=True, source='_id')
    fullname = ser.CharField(
        help_text='Display name used in the general user interface')
    given_name = ser.CharField(help_text='For bibliographic citations')
    middle_name = ser.CharField(source='middle_names',
                                help_text='For bibliographic citations')
    family_name = ser.CharField(help_text='For bibliographic citations')
    suffix = ser.CharField(help_text='For bibliographic citations')
    date_registered = ser.DateTimeField(read_only=True)
    gravatar_url = ser.CharField(
        help_text=
        'URL for the icon used to identify the user. Relies on http://gravatar.com '
    )
    employment_institutions = ser.ListField(
        source='jobs',
        help_text='An array of dictionaries representing the '
        'places the user has worked')
    educational_institutions = ser.ListField(
        source='schools',
        help_text='An array of dictionaries representing the '
        'places the user has attended school')
    social_accounts = ser.DictField(
        source='social',
        help_text='A dictionary of various social media account '
        'identifiers including an array of user-defined URLs')

    links = LinksField({
        'html': 'absolute_url',
        'nodes': {
            'relation': Link('users:user-nodes', kwargs={'user_id': '<pk>'})
        }
    })

    class Meta:
        type_ = 'users'

    def absolute_url(self, obj):
        return obj.absolute_url

    def update(self, instance, validated_data):
        # TODO
        pass
Beispiel #5
0
class BaseFileSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'id',
        'name',
        'kind',
        'path',
        'materialized_path',
        'size',
        'provider',
        'last_touched',
        'tags',
    ])
    id = IDField(source='_id', read_only=True)
    type = TypeField()
    guid = ser.SerializerMethodField(
        read_only=True,
        method_name='get_file_guid',
        help_text='OSF GUID for this file (if one has been assigned)',
    )
    checkout = CheckoutField()
    name = ser.CharField(
        read_only=True,
        help_text='Display name used in the general user interface')
    kind = ser.CharField(read_only=True, help_text='Either folder or file')
    path = ser.CharField(
        read_only=True,
        help_text='The unique path used to reference this object')
    size = ser.SerializerMethodField(
        read_only=True, help_text='The size of this file at this version')
    provider = ser.CharField(
        read_only=True,
        help_text='The Add-on service this file originates from')
    materialized_path = ser.CharField(
        read_only=True,
        help_text=
        'The Unix-style path of this object relative to the provider root',
    )
    last_touched = VersionedDateTimeField(
        read_only=True,
        help_text=
        'The last time this file had information fetched about it via the OSF')
    date_modified = ser.SerializerMethodField(
        read_only=True, help_text='Timestamp when the file was last modified')
    date_created = ser.SerializerMethodField(
        read_only=True, help_text='Timestamp when the file was created')
    extra = ser.SerializerMethodField(
        read_only=True, help_text='Additional metadata about this file')
    tags = JSONAPIListField(child=FileTagField(), required=False)
    current_user_can_comment = ser.SerializerMethodField(
        help_text='Whether the current user is allowed to post comments')
    current_version = ser.IntegerField(help_text='Latest file version',
                                       read_only=True,
                                       source='current_version_number')
    delete_allowed = ser.BooleanField(read_only=True, required=False)

    parent_folder = RelationshipField(
        related_view='files:file-detail',
        related_view_kwargs={'file_id': '<parent._id>'},
        help_text='The folder in which this file exists',
    )
    files = NodeFileHyperLinkField(
        related_view=lambda node: disambiguate_files_related_view(node),
        view_lambda_argument='target',
        related_view_kwargs=lambda filenode:
        disambiguate_files_related_view_kwargs(filenode),
        kind='folder',
    )
    versions = NodeFileHyperLinkField(
        related_view='files:file-versions',
        related_view_kwargs={'file_id': '<_id>'},
        kind='file',
    )
    comments = HideIfPreprint(
        FileRelationshipField(
            related_view='nodes:node-comments',
            related_view_kwargs={'node_id': '<target._id>'},
            related_meta={'unread': 'get_unread_comments_count'},
            filter={'target': 'get_file_guid'},
        ))
    metadata_records = FileRelationshipField(
        related_view='files:metadata-records',
        related_view_kwargs={'file_id': '<_id>'},
    )

    links = LinksField({
        'info':
        Link('files:file-detail', kwargs={'file_id': '<_id>'}),
        'move':
        WaterbutlerLink(),
        'upload':
        WaterbutlerLink(),
        'delete':
        WaterbutlerLink(),
        'download':
        'get_download_link',
        'render':
        'get_render_link',
        'html':
        'absolute_url',
        'new_folder':
        WaterbutlerLink(must_be_folder=True, kind='folder'),
    })

    def absolute_url(self, obj):
        if obj.is_file:
            return furl.furl(settings.DOMAIN).set(
                path=(obj.target._id, 'files', obj.provider,
                      obj.path.lstrip('/')), ).url

    def get_download_link(self, obj):
        if obj.is_file:
            return get_file_download_link(
                obj,
                view_only=self.context['request'].query_params.get(
                    'view_only'))

    def get_render_link(self, obj):
        if obj.is_file:
            mfr_url = get_mfr_url(obj.target, obj.provider)
            download_url = self.get_download_link(obj)
            return get_file_render_link(mfr_url, download_url)

    class Meta:
        type_ = 'files'

    def get_size(self, obj):
        if obj.versions.exists():
            self.size = obj.versions.first().size
            return self.size
        return None

    def get_date_modified(self, obj):
        mod_dt = None
        if obj.provider == 'osfstorage' and obj.versions.exists():
            # Each time an osfstorage file is added or uploaded, a new version object is created with its
            # date_created equal to the time of the update.  The external_modified is the modified date
            # from the backend the file is stored on.  This field refers to the modified date on osfstorage,
            # so prefer to use the created of the latest version.
            mod_dt = obj.versions.first().created
        elif obj.provider != 'osfstorage' and obj.history:
            mod_dt = obj.history[-1].get('modified', None)

        if self.context['request'].version >= '2.2' and obj.is_file and mod_dt:
            return datetime.strftime(mod_dt, '%Y-%m-%dT%H:%M:%S.%fZ')

        return mod_dt and mod_dt.replace(tzinfo=pytz.utc)

    def get_date_created(self, obj):
        creat_dt = None
        if obj.provider == 'osfstorage' and obj.versions.exists():
            creat_dt = obj.versions.last().created
        elif obj.provider != 'osfstorage' and obj.history:
            # Non-osfstorage files don't store a created date, so instead get the modified date of the
            # earliest entry in the file history.
            creat_dt = obj.history[0].get('modified', None)

        if self.context[
                'request'].version >= '2.2' and obj.is_file and creat_dt:
            return datetime.strftime(creat_dt, '%Y-%m-%dT%H:%M:%S.%fZ')

        return creat_dt and creat_dt.replace(tzinfo=pytz.utc)

    def get_extra(self, obj):
        metadata = {}
        if obj.provider == 'osfstorage' and obj.versions.exists():
            metadata = obj.versions.first().metadata
        elif obj.provider != 'osfstorage' and obj.history:
            metadata = obj.history[-1].get('extra', {})

        extras = {}
        extras['hashes'] = {  # mimic waterbutler response
            'md5': metadata.get('md5', None),
            'sha256': metadata.get('sha256', None),
        }
        if obj.provider == 'osfstorage' and obj.is_file:
            extras['downloads'] = obj.get_download_count()
        return extras

    def get_current_user_can_comment(self, obj):
        user = self.context['request'].user
        auth = Auth(user if not user.is_anonymous else None)
        if isinstance(obj.target, AbstractNode):
            return obj.target.can_comment(auth)
        return False

    def get_unread_comments_count(self, obj):
        user = self.context['request'].user
        if user.is_anonymous:
            return 0
        return Comment.find_n_unread(user=user,
                                     node=obj.target,
                                     page='files',
                                     root_id=obj.get_guid()._id)

    def user_id(self, obj):
        # NOTE: obj is the user here, the meta field for
        # Hyperlinks is weird
        if obj:
            return obj._id
        return None

    def update(self, instance, validated_data):
        assert isinstance(instance,
                          BaseFileNode), 'Instance must be a BaseFileNode'
        if instance.provider != 'osfstorage' and 'tags' in validated_data:
            raise Conflict(
                'File service provider {} does not support tags on the OSF.'.
                format(instance.provider))
        auth = get_user_auth(self.context['request'])
        old_tags = set(instance.tags.values_list('name', flat=True))
        if 'tags' in validated_data:
            current_tags = set(validated_data.pop('tags', []))
        else:
            current_tags = set(old_tags)

        for new_tag in (current_tags - old_tags):
            instance.add_tag(new_tag, auth=auth)
        for deleted_tag in (old_tags - current_tags):
            instance.remove_tag(deleted_tag, auth=auth)

        for attr, value in validated_data.items():
            if attr == 'checkout':
                user = self.context['request'].user
                instance.check_in_or_out(user, value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance

    def is_valid(self, **kwargs):
        return super(BaseFileSerializer, self).is_valid(clean_html=False,
                                                        **kwargs)

    def get_file_guid(self, obj):
        if obj:
            guid = obj.get_guid()
            if guid:
                return guid._id
        return None

    def get_absolute_url(self, obj):
        return api_v2_url('files/{}/'.format(obj._id))
Beispiel #6
0
class WikiSerializer(JSONAPISerializer):

    filterable_fields = frozenset(['name', 'date_modified'])

    id = IDField(source='_id', read_only=True)
    type = TypeField()
    name = ser.CharField(source='page_name')
    kind = ser.SerializerMethodField()
    size = ser.SerializerMethodField()
    path = ser.SerializerMethodField()
    materialized_path = ser.SerializerMethodField(method_name='get_path')
    date_modified = ser.DateTimeField(source='date')
    content_type = ser.SerializerMethodField()
    current_user_can_comment = ser.SerializerMethodField(
        help_text='Whether the current user is allowed to post comments')
    extra = ser.SerializerMethodField(
        help_text='Additional metadata about this wiki')

    user = RelationshipField(related_view='users:user-detail',
                             related_view_kwargs={'user_id': '<user._id>'})

    # LinksField.to_representation adds link to "self"
    links = LinksField({
        'info':
        Link('wikis:wiki-detail', kwargs={'wiki_id': '<_id>'}),
        'download':
        'get_wiki_content'
    })

    class Meta:
        type_ = 'wikis'

    def get_absolute_url(self, obj):
        return obj.get_absolute_url()

    def get_path(self, obj):
        return '/{}'.format(obj)

    def get_kind(self, obj):
        return 'file'

    def get_size(self, obj):
        return sys.getsizeof(obj.content)

    def get_current_user_can_comment(self, obj):
        user = self.context['request'].user
        auth = Auth(user if not user.is_anonymous() else None)
        return obj.node.can_comment(auth)

    def get_content_type(self, obj):
        return 'text/markdown'

    def get_extra(self, obj):
        return {'version': obj.version}

    def get_wiki_content(self, obj):
        return absolute_reverse(
            'wikis:wiki-content',
            kwargs={
                'wiki_id':
                obj._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })
Beispiel #7
0
class UserSerializer(JSONAPISerializer):
    filterable_fields = frozenset(
        ['fullname', 'given_name', 'middle_names', 'family_name', 'id'])
    id = ser.CharField(read_only=True, source='_id')
    fullname = ser.CharField(
        required=True,
        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)
    gravatar_url = ser.URLField(
        required=False,
        read_only=True,
        help_text=
        'URL for the icon used to identify the user. Relies on http://gravatar.com '
    )

    # Social Fields are broken out to get around DRF complex object bug and to make API updating more user friendly.
    gitHub = ser.CharField(required=False,
                           source='social.github',
                           allow_blank=True,
                           help_text='GitHub Handle')
    scholar = ser.CharField(required=False,
                            source='social.scholar',
                            allow_blank=True,
                            help_text='Google Scholar Account')
    personal_website = ser.URLField(required=False,
                                    source='social.personal',
                                    allow_blank=True,
                                    help_text='Personal Website')
    twitter = ser.CharField(required=False,
                            source='social.twitter',
                            allow_blank=True,
                            help_text='Twitter Handle')
    linkedIn = ser.CharField(required=False,
                             source='social.linkedIn',
                             allow_blank=True,
                             help_text='LinkedIn Account')
    impactStory = ser.CharField(required=False,
                                source='social.impactStory',
                                allow_blank=True,
                                help_text='ImpactStory Account')
    orcid = ser.CharField(required=False,
                          source='social.orcid',
                          allow_blank=True,
                          help_text='ORCID')
    researcherId = ser.CharField(required=False,
                                 source='social.researcherId',
                                 allow_blank=True,
                                 help_text='ResearcherId Account')

    links = LinksField({
        'html': 'absolute_url',
        'nodes': {
            'relation': Link('users:user-nodes', kwargs={'user_id': '<pk>'})
        }
    })

    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():
            setattr(instance, attr, value)
        instance.save()
        return instance
Beispiel #8
0
class FileSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'id',
        'name',
        'node',
        'kind',
        'path',
        'materialized_path',
        'size',
        'provider',
        'last_touched',
        'tags',
    ])
    id = IDField(source='_id', read_only=True)
    type = TypeField()
    checkout = CheckoutField()
    name = ser.CharField(
        read_only=True,
        help_text='Display name used in the general user interface')
    kind = ser.CharField(read_only=True, help_text='Either folder or file')
    path = ser.CharField(
        read_only=True,
        help_text='The unique path used to reference this object')
    size = ser.SerializerMethodField(
        read_only=True, help_text='The size of this file at this version')
    provider = ser.CharField(
        read_only=True,
        help_text='The Add-on service this file originates from')
    materialized_path = ser.CharField(
        read_only=True,
        help_text=
        'The Unix-style path of this object relative to the provider root')
    last_touched = ser.DateTimeField(
        read_only=True,
        help_text=
        'The last time this file had information fetched about it via the OSF')
    date_modified = ser.SerializerMethodField(
        read_only=True, help_text='Timestamp when the file was last modified')
    date_created = ser.SerializerMethodField(
        read_only=True, help_text='Timestamp when the file was created')
    extra = ser.SerializerMethodField(
        read_only=True, help_text='Additional metadata about this file')
    tags = JSONAPIListField(child=FileTagField(), required=False)

    files = NodeFileHyperLinkField(related_view='nodes:node-files',
                                   related_view_kwargs={
                                       'node_id': '<node_id>',
                                       'path': '<path>',
                                       'provider': '<provider>'
                                   },
                                   kind='folder')
    versions = NodeFileHyperLinkField(related_view='files:file-versions',
                                      related_view_kwargs={'file_id': '<_id>'},
                                      kind='file')
    comments = FileCommentRelationshipField(
        related_view='nodes:node-comments',
        related_view_kwargs={'node_id': '<node._id>'},
        related_meta={'unread': 'get_unread_comments_count'},
        filter={'target': 'get_file_guid'})
    links = LinksField({
        'info':
        Link('files:file-detail', kwargs={'file_id': '<_id>'}),
        'move':
        WaterbutlerLink(),
        'upload':
        WaterbutlerLink(),
        'delete':
        WaterbutlerLink(),
        'download':
        WaterbutlerLink(must_be_file=True),
        'new_folder':
        WaterbutlerLink(must_be_folder=True, kind='folder'),
    })

    class Meta:
        type_ = 'files'

    def get_size(self, obj):
        if obj.versions:
            return obj.versions[-1].size
        return None

    def get_date_modified(self, obj):
        mod_dt = None
        if obj.provider == 'osfstorage' and obj.versions:
            # Each time an osfstorage file is added or uploaded, a new version object is created with its
            # date_created equal to the time of the update.  The date_modified is the modified date
            # from the backend the file is stored on.  This field refers to the modified date on osfstorage,
            # so prefer to use the date_created of the latest version.
            mod_dt = obj.versions[-1].date_created
        elif obj.provider != 'osfstorage' and obj.history:
            mod_dt = obj.history[-1].get('modified', None)

        return mod_dt and mod_dt.replace(tzinfo=pytz.utc)

    def get_date_created(self, obj):
        creat_dt = None
        if obj.provider == 'osfstorage' and obj.versions:
            creat_dt = obj.versions[0].date_created
        elif obj.provider != 'osfstorage' and obj.history:
            # Non-osfstorage files don't store a created date, so instead get the modified date of the
            # earliest entry in the file history.
            creat_dt = obj.history[0].get('modified', None)

        return creat_dt and creat_dt.replace(tzinfo=pytz.utc)

    def get_extra(self, obj):
        metadata = {}
        if obj.provider == 'osfstorage' and obj.versions:
            metadata = obj.versions[-1].metadata
        elif obj.provider != 'osfstorage' and obj.history:
            metadata = obj.history[-1].get('extra', {})

        extras = {}
        extras['hashes'] = {  # mimic waterbutler response
            'md5': metadata.get('md5', None),
            'sha256': metadata.get('sha256', None),
        }
        return extras

    def get_unread_comments_count(self, obj):
        user = self.context['request'].user
        if user.is_anonymous():
            return 0
        return Comment.find_n_unread(user=user,
                                     node=obj.node,
                                     page='files',
                                     root_id=obj.get_guid()._id)

    def user_id(self, obj):
        # NOTE: obj is the user here, the meta field for
        # Hyperlinks is weird
        if obj:
            return obj._id
        return None

    def update(self, instance, validated_data):
        assert isinstance(instance, FileNode), 'Instance must be a FileNode'
        if instance.provider != 'osfstorage' and 'tags' in validated_data:
            raise Conflict(
                'File service provider {} does not support tags on the OSF.'.
                format(instance.provider))
        auth = get_user_auth(self.context['request'])
        old_tags = set([tag._id for tag in instance.tags])
        if 'tags' in validated_data:
            current_tags = set(validated_data.pop('tags', []))
        else:
            current_tags = set(old_tags)

        for new_tag in (current_tags - old_tags):
            instance.add_tag(new_tag, auth=auth)
        for deleted_tag in (old_tags - current_tags):
            instance.remove_tag(deleted_tag, auth=auth)

        for attr, value in validated_data.items():
            if attr == 'checkout':
                user = self.context['request'].user
                instance.check_in_or_out(user, value)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance

    def is_valid(self, **kwargs):
        return super(FileSerializer, self).is_valid(clean_html=False, **kwargs)

    def get_file_guid(self, obj):
        if obj:
            guid = obj.get_guid()
            if guid:
                return guid._id
        return None

    def get_absolute_url(self, obj):
        return api_v2_url('files/{}/'.format(obj._id))
Beispiel #9
0
class FileSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'id',
        'name',
        'node',
        'kind',
        'path',
        'size',
        'provider',
        'last_touched',
    ])
    id = ser.CharField(read_only=True, source='_id')
    name = ser.CharField(
        read_only=True,
        help_text='Display name used in the general user interface')
    kind = ser.CharField(read_only=True, help_text='Either folder or file')
    path = ser.CharField(
        read_only=True,
        help_text='The unique path used to reference this object')
    size = ser.SerializerMethodField(
        read_only=True, help_text='The size of this file at this version')
    provider = ser.CharField(
        read_only=True,
        help_text='The Add-on service this file originates from')
    last_touched = ser.DateTimeField(
        read_only=True,
        help_text=
        'The last time this file had information fetched about it via the OSF')

    checkout = CheckoutField()

    files = NodeFileHyperLink(kind='folder',
                              link_type='related',
                              view_name='nodes:node-files',
                              kwargs=('node_id', 'path', 'provider'))
    versions = NodeFileHyperLink(kind='file',
                                 link_type='related',
                                 view_name='files:file-versions',
                                 kwargs=(('file_id', '_id'), ))

    links = LinksField({
        'info':
        Link('files:file-detail', kwargs={'file_id': '<_id>'}),
        'move':
        WaterbutlerLink(),
        'upload':
        WaterbutlerLink(),
        'delete':
        WaterbutlerLink(),
        'download':
        WaterbutlerLink(must_be_file=True),
        'new_folder':
        WaterbutlerLink(must_be_folder=True, kind='folder')
    })

    class Meta:
        type_ = 'files'

    def get_size(self, obj):
        if obj.versions:
            return obj.versions[-1].size
        return None

    def update(self, instance, validated_data):
        assert isinstance(instance, FileNode), 'Instance must be a FileNode'
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def is_valid(self, **kwargs):
        return super(FileSerializer, self).is_valid(clean_html=False, **kwargs)
Beispiel #10
0
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
    category_choices = Node.CATEGORY_MAP.keys()
    category_choices_string = ', '.join(["'{}'".format(choice) for choice in category_choices])
    filterable_fields = frozenset(['title', 'description', 'public'])

    id = ser.CharField(read_only=True, source='_id')
    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)
    tags = ser.ListField(child=NodeTagField(), required=False)

    links = LinksField({
        'html': 'get_absolute_url',
        'children': {
            'related': Link('nodes:node-children', kwargs={'node_id': '<pk>'}),
            'count': 'get_node_count',
        },
        'contributors': {
            'related': Link('nodes:node-contributors', kwargs={'node_id': '<pk>'}),
            'count': 'get_contrib_count',
        },
        'node_links': {
            'related': Link('nodes:node-pointers', kwargs={'node_id': '<pk>'}),
            'count': 'get_pointers_count',
        },
        'registrations': {
            'related': Link('nodes:node-registrations', kwargs={'node_id': '<pk>'}),
            'count': 'get_registration_count',
        },
        'files': {
            'related': Link('nodes:node-files', kwargs={'node_id': '<pk>'})
        },
        'parent': {
            'self': Link('nodes:node-detail', kwargs={'node_id': '<parent_id>'})
        }
    })
    properties = ser.SerializerMethodField(help_text='A dictionary of read-only booleans: registration, collection,'
                                                     'and dashboard. Collections are special nodes used by the Project '
                                                     'Organizer to, as you would imagine, organize projects. '
                                                     'A dashboard is a collection node that serves as the root of '
                                                     'Project Organizer collections. Every user will always have '
                                                     'one Dashboard')
    # TODO: When we have 'admin' permissions, make this writable for admins
    public = ser.BooleanField(source='is_public', read_only=True,
                              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',
                              )
    # TODO: finish me

    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)

    @staticmethod
    def get_properties(obj):
        ret = {
            'registration': obj.is_registration,
            'collection': obj.is_folder,
            'dashboard': obj.is_dashboard,
        }
        return ret

    def create(self, validated_data):
        node = Node(**validated_data)
        node.save()
        return node

    def update(self, instance, validated_data):
        """Update instance with the validated data. Requires
        the request to be in the serializer context.
        """
        assert isinstance(instance, Node), 'instance must be a Node'
        auth = self.get_user_auth(self.context['request'])
        for attr, value in validated_data.items():
            if attr == 'tags':
                old_tags = set([tag._id for tag in instance.tags])
                if value:
                    current_tags = set(value)
                else:
                    current_tags = set()
                for new_tag in (current_tags - old_tags):
                    instance.add_tag(new_tag, auth=auth)
                for deleted_tag in (old_tags - current_tags):
                    instance.remove_tag(deleted_tag, auth=auth)
            else:
                setattr(instance, attr, value)
        instance.save()
        return instance
Beispiel #11
0
class FileSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'id',
        'name',
        'node',
        'kind',
        'path',
        'size',
        'provider',
        'last_touched',
    ])
    id = IDField(source='_id', read_only=True)
    type = TypeField()
    checkout = CheckoutField()
    name = ser.CharField(read_only=True, help_text='Display name used in the general user interface')
    kind = ser.CharField(read_only=True, help_text='Either folder or file')
    path = ser.CharField(read_only=True, help_text='The unique path used to reference this object')
    size = ser.SerializerMethodField(read_only=True, help_text='The size of this file at this version')
    provider = ser.CharField(read_only=True, help_text='The Add-on service this file originates from')
    last_touched = ser.DateTimeField(read_only=True, help_text='The last time this file had information fetched about it via the OSF')
    date_modified = ser.SerializerMethodField(read_only=True, help_text='The size of this file at this version')
    extra = ser.SerializerMethodField(read_only=True, help_text='Additional metadata about this file')

    files = NodeFileHyperLinkField(
        related_view='nodes:node-files',
        related_view_kwargs={'node_id': '<node_id>', 'path': '<path>', 'provider': '<provider>'},
        kind='folder'
    )
    versions = NodeFileHyperLinkField(
        related_view='files:file-versions',
        related_view_kwargs={'file_id': '<_id>'},
        kind='file'
    )
    links = LinksField({
        'info': Link('files:file-detail', kwargs={'file_id': '<_id>'}),
        'move': WaterbutlerLink(),
        'upload': WaterbutlerLink(),
        'delete': WaterbutlerLink(),
        'download': WaterbutlerLink(must_be_file=True),
        'new_folder': WaterbutlerLink(must_be_folder=True, kind='folder'),
    })

    class Meta:
        type_ = 'files'

    def get_size(self, obj):
        if obj.versions:
            return obj.versions[-1].size
        return None

    def get_date_modified(self, obj):
        if obj.versions:
            if obj.provider == 'osfstorage':
                # Odd case osfstorage's date created is when the version was create
                # (when the file was modified) its date modified is referencing whatever backend it is on
                return obj.versions[-1].date_created
            return obj.versions[-1].date_modified
        return None

    def get_extra(self, obj):
        extras = {}
        if obj.versions:
            metadata = obj.versions[-1].metadata
            extras['hashes'] = {  # mimic waterbutler response
                'md5': metadata.get('md5', None),
                'sha256': metadata.get('sha256', None),
            }
        return extras

    def user_id(self, obj):
        # NOTE: obj is the user here, the meta field for
        # Hyperlinks is weird
        if obj:
            return obj._id
        return None

    def update(self, instance, validated_data):
        assert isinstance(instance, FileNode), 'Instance must be a FileNode'
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        return instance

    def is_valid(self, **kwargs):
        return super(FileSerializer, self).is_valid(clean_html=False, **kwargs)