Exemple #1
0
class BaseRegistrationSerializer(NodeSerializer):

    title = ser.CharField(read_only=True)
    description = ser.CharField(read_only=True)
    category_choices = NodeSerializer.category_choices
    category_choices_string = NodeSerializer.category_choices_string
    category = HideIfWithdrawal(
        ser.ChoiceField(read_only=True,
                        choices=category_choices,
                        help_text='Choices: ' + category_choices_string))
    date_modified = VersionedDateTimeField(source='last_logged',
                                           read_only=True)
    fork = HideIfWithdrawal(ser.BooleanField(read_only=True, source='is_fork'))
    collection = HideIfWithdrawal(
        ser.BooleanField(read_only=True, source='is_collection'))
    access_requests_enabled = HideIfWithdrawal(
        ser.BooleanField(read_only=True))
    node_license = HideIfWithdrawal(NodeLicenseSerializer(read_only=True))
    tags = HideIfWithdrawal(
        JSONAPIListField(child=NodeTagField(), required=False))
    public = HideIfWithdrawal(
        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'))
    current_user_permissions = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='List of strings representing the permissions '
            'for the current user on this node.'))

    pending_embargo_approval = HideIfWithdrawal(
        ser.BooleanField(
            read_only=True,
            source='is_pending_embargo',
            help_text=
            'The associated Embargo is awaiting approval by project admins.'))
    pending_registration_approval = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_registration',
            read_only=True,
            help_text=
            'The associated RegistrationApproval is awaiting approval by project admins.'
        ))
    pending_withdrawal = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_retraction',
            read_only=True,
            help_text=
            'The registration is awaiting withdrawal approval by project admins.'
        ))
    withdrawn = ser.BooleanField(
        source='is_retracted',
        read_only=True,
        help_text='The registration has been withdrawn.')

    date_registered = VersionedDateTimeField(
        source='registered_date',
        read_only=True,
        help_text='Date time of registration.')
    date_withdrawn = VersionedDateTimeField(
        source='retraction.date_retracted',
        read_only=True,
        help_text='Date time of when this registration was retracted.')
    embargo_end_date = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='When the embargo on this registration will be lifted.'))

    withdrawal_justification = ser.CharField(source='retraction.justification',
                                             read_only=True)
    template_from = HideIfWithdrawal(
        ser.CharField(
            read_only=True,
            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.'))
    registration_supplement = ser.SerializerMethodField()
    registered_meta = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text=
            'A dictionary with supplemental registration questions and responses.'
        ))

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

    registered_from = HideIfWithdrawal(
        RelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<registered_from._id>'}))

    children = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-children',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_node_count'},
        ))

    comments = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-comments',
                          related_view_kwargs={'node_id': '<_id>'},
                          related_meta={'unread': 'get_unread_comments_count'},
                          filter={'target': '<_id>'}))

    contributors = RelationshipField(
        related_view='registrations:registration-contributors',
        related_view_kwargs={'node_id': '<_id>'},
        related_meta={'count': 'get_contrib_count'})

    implicit_contributors = RelationshipField(
        related_view='registrations:registration-implicit-contributors',
        related_view_kwargs={'node_id': '<_id>'},
        help_text=
        'This feature is experimental and being tested. It may be deprecated.')

    files = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-providers',
                          related_view_kwargs={'node_id': '<_id>'}))

    wikis = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-wikis',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    forked_from = HideIfWithdrawal(
        RelationshipField(
            related_view=lambda n: 'registrations:registration-detail'
            if getattr(n, 'is_registration', False) else 'nodes:node-detail',
            related_view_kwargs={'node_id': '<forked_from_id>'}))

    template_node = HideIfWithdrawal(
        RelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<template_node._id>'}))

    license = HideIfWithdrawal(
        RelationshipField(
            related_view='licenses:license-detail',
            related_view_kwargs={
                'license_id': '<node_license.node_license._id>'
            },
        ))

    logs = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-logs',
            related_view_kwargs={'node_id': '<_id>'},
        ))

    forks = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-forks',
                          related_view_kwargs={'node_id': '<_id>'}))

    node_links = ShowIfVersion(HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-pointers',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_pointers_count'},
            help_text=
            'This feature is deprecated as of version 2.1. Use linked_nodes instead.'
        )),
                               min_version='2.0',
                               max_version='2.0')

    parent = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<parent_node._id>'},
                          filter_key='parent_node'))

    root = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<root._id>'}))

    affiliated_institutions = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-institutions',
            related_view_kwargs={'node_id': '<_id>'}))

    registration_schema = RelationshipField(
        related_view='metaschemas:metaschema-detail',
        related_view_kwargs={'metaschema_id': '<registered_schema_id>'})

    registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-registrations',
                          related_view_kwargs={'node_id': '<_id>'}))

    draft_registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-draft-registrations',
                          related_view_kwargs={'node_id': '<_id>'}))

    preprints = HideIfWithdrawal(
        HideIfRegistration(
            RelationshipField(related_view='nodes:node-preprints',
                              related_view_kwargs={'node_id': '<_id>'})))

    identifiers = HideIfWithdrawal(
        RelationshipField(related_view='registrations:identifier-list',
                          related_view_kwargs={'node_id': '<_id>'}))

    linked_nodes = HideIfWithdrawal(
        RelationshipField(related_view='registrations:linked-nodes',
                          related_view_kwargs={'node_id': '<_id>'},
                          related_meta={'count': 'get_node_links_count'},
                          self_view='registrations:node-pointer-relationship',
                          self_view_kwargs={'node_id': '<_id>'}))

    linked_registrations = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:linked-registrations',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_registration_links_count'},
            self_view='registrations:node-registration-pointer-relationship',
            self_view_kwargs={'node_id': '<_id>'}))

    view_only_links = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-view-only-links',
            related_view_kwargs={'node_id': '<_id>'},
            related_meta={'count': 'get_view_only_links_count'},
        ))

    citation = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-citation',
                          related_view_kwargs={'node_id': '<_id>'}))

    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,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

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

    def create(self, validated_data):
        auth = get_user_auth(self.context['request'])
        draft = validated_data.pop('draft')
        registration_choice = validated_data.pop('registration_choice',
                                                 'immediate')
        embargo_lifted = validated_data.pop('lift_embargo', None)
        reviewer = is_prereg_admin_not_project_admin(self.context['request'],
                                                     draft)

        try:
            draft.validate_metadata(metadata=draft.registration_metadata,
                                    reviewer=reviewer,
                                    required_fields=True)
        except ValidationValueError as e:
            raise exceptions.ValidationError(e.message)

        registration = draft.register(auth, save=True)

        if registration_choice == 'embargo':
            if not embargo_lifted:
                raise exceptions.ValidationError(
                    'lift_embargo must be specified.')
            embargo_end_date = embargo_lifted.replace(tzinfo=pytz.utc)
            try:
                registration.embargo_registration(auth.user, embargo_end_date)
            except ValidationError as err:
                raise exceptions.ValidationError(err.message)
        else:
            try:
                registration.require_approval(auth.user)
            except NodeStateError as err:
                raise exceptions.ValidationError(err)

        registration.save()
        return registration

    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.first()
            if schema is None:
                return None
            return schema.name
        return None

    def get_current_user_permissions(self, obj):
        return NodeSerializer.get_current_user_permissions(self, obj)

    def update(self, registration, validated_data):
        auth = Auth(self.context['request'].user)
        # Update tags
        if 'tags' in validated_data:
            new_tags = validated_data.pop('tags', [])
            try:
                registration.update_tags(new_tags, auth=auth)
            except NodeStateError as err:
                raise Conflict(err.message)

        is_public = validated_data.get('is_public', None)
        if is_public is not None:
            if is_public:
                try:
                    registration.update(validated_data, auth=auth)
                except NodeUpdateError as err:
                    raise exceptions.ValidationError(err.reason)
                except NodeStateError as err:
                    raise exceptions.ValidationError(err.message)
            else:
                raise exceptions.ValidationError(
                    'Registrations can only be turned from private to public.')
        return registration

    class Meta:
        type_ = 'registrations'
Exemple #2
0
class PreprintSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'id',
        'date_created',
        'date_modified',
        'date_published',
        'original_publication_date',
        'provider',
        'is_published',
        'subjects',
        'reviews_state',
        'node_is_public',
    ])

    id = IDField(source='_id', read_only=True)
    subjects = ser.SerializerMethodField()
    date_created = DateByVersion(read_only=True)
    date_modified = DateByVersion(read_only=True)
    date_published = DateByVersion(read_only=True)
    original_publication_date = DateByVersion(required=False)
    doi = ser.CharField(source='article_doi', required=False, allow_null=True)
    is_published = ser.BooleanField(required=False)
    is_preprint_orphan = ser.BooleanField(read_only=True)
    license_record = NodeLicenseSerializer(required=False, source='license')
    title = ser.CharField(source='node.title', required=False)
    description = ser.CharField(required=False,
                                allow_blank=True,
                                allow_null=True,
                                source='node.description')
    tags = JSONAPIListField(child=NodeTagField(),
                            required=False,
                            source='node.tags')
    node_is_public = ser.BooleanField(read_only=True, source='node__is_public')

    contributors = RelationshipField(
        related_view='nodes:node-contributors',
        related_view_kwargs={'node_id': '<node._id>'},
    )

    reviews_state = ser.CharField(read_only=True, max_length=15)
    date_last_transitioned = DateByVersion(read_only=True)

    citation = RelationshipField(related_view='preprints:preprint-citation',
                                 related_view_kwargs={'preprint_id': '<_id>'})

    identifiers = RelationshipField(
        related_view='preprints:identifier-list',
        related_view_kwargs={'preprint_id': '<_id>'})

    node = NodeRelationshipField(related_view='nodes:node-detail',
                                 related_view_kwargs={'node_id': '<node._id>'},
                                 read_only=False)

    license = PreprintLicenseRelationshipField(
        related_view='licenses:license-detail',
        related_view_kwargs={'license_id': '<license.node_license._id>'},
        read_only=False)

    provider = PreprintProviderRelationshipField(
        related_view='preprint_providers:preprint_provider-detail',
        related_view_kwargs={'provider_id': '<provider._id>'},
        read_only=False)

    files = RelationshipField(related_view='nodes:node-providers',
                              related_view_kwargs={'node_id': '<_id>'})

    primary_file = PrimaryFileRelationshipField(
        related_view='files:file-detail',
        related_view_kwargs={'file_id': '<primary_file._id>'},
        lookup_url_kwarg='file_id',
        read_only=False)

    actions = RelationshipField(related_view='preprints:preprint-action-list',
                                related_view_kwargs={'preprint_id': '<_id>'})

    links = LinksField({
        'self': 'get_preprint_url',
        'html': 'get_absolute_html_url',
        'doi': 'get_article_doi_url',
        'preprint_doi': 'get_preprint_doi_url'
    })

    class Meta:
        type_ = 'preprints'

    def get_subjects(self, obj):
        return [[TaxonomyField().to_representation(subj) for subj in hier]
                for hier in obj.subject_hierarchy]

    def get_preprint_url(self, obj):
        return absolute_reverse(
            'preprints:preprint-detail',
            kwargs={
                'preprint_id':
                obj._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

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

    def get_article_doi_url(self, obj):
        return 'https://dx.doi.org/{}'.format(
            obj.article_doi) if obj.article_doi else None

    def get_preprint_doi_url(self, obj):
        doi_identifier = obj.get_identifier('doi')
        return 'https://dx.doi.org/{}'.format(
            doi_identifier.value) if doi_identifier else None

    def run_validation(self, *args, **kwargs):
        # Overrides construtor for validated_data to allow writes to a SerializerMethodField
        # Validation for `subjects` happens in the model
        _validated_data = super(PreprintSerializer,
                                self).run_validation(*args, **kwargs)
        if 'subjects' in self.initial_data:
            _validated_data['subjects'] = self.initial_data['subjects']
        return _validated_data

    def update(self, preprint, validated_data):
        assert isinstance(
            preprint,
            PreprintService), 'You must specify a valid preprint to be updated'
        assert isinstance(
            preprint.node, Node
        ), 'You must specify a preprint with a valid node to be updated.'

        auth = get_user_auth(self.context['request'])
        if not preprint.node.has_permission(auth.user, 'admin'):
            raise exceptions.PermissionDenied(
                detail='User must be an admin to update a preprint.')

        published = validated_data.pop('is_published', None)
        if published and preprint.provider.is_reviewed:
            raise Conflict(
                '{} uses a moderation workflow, so preprints must be submitted for review instead of published directly. Submit a preprint by creating a `submit` Action at {}'
                .format(
                    preprint.provider.name,
                    absolute_reverse(
                        'actions:create-action',
                        kwargs={
                            'version':
                            self.context['request'].parser_context['kwargs']
                            ['version']
                        })))

        save_node = False
        save_preprint = False
        recently_published = False
        primary_file = validated_data.pop('primary_file', None)
        if primary_file:
            self.set_field(preprint.set_primary_file, primary_file, auth)
            save_node = True

        old_tags = set(preprint.node.tags.values_list('name', flat=True))
        if validated_data.get('node') and 'tags' in validated_data['node']:
            current_tags = set(validated_data['node'].pop('tags', []))
        elif self.partial:
            current_tags = set(old_tags)
        else:
            current_tags = set()

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

        if 'node' in validated_data:
            preprint.node.update(fields=validated_data.pop('node'))
            save_node = True

        if 'subjects' in validated_data:
            subjects = validated_data.pop('subjects', None)
            self.set_field(preprint.set_subjects, subjects, auth)
            save_preprint = True

        if 'article_doi' in validated_data:
            preprint.node.preprint_article_doi = validated_data['article_doi']
            save_node = True

        if 'license_type' in validated_data or 'license' in validated_data:
            license_details = get_license_details(preprint, validated_data)
            self.set_field(preprint.set_preprint_license, license_details,
                           auth)
            save_preprint = True

        if 'original_publication_date' in validated_data:
            preprint.original_publication_date = validated_data[
                'original_publication_date']
            save_preprint = True

        if published is not None:
            if not preprint.primary_file:
                raise exceptions.ValidationError(
                    detail=
                    'A valid primary_file must be set before publishing a preprint.'
                )
            self.set_field(preprint.set_published, published, auth)
            save_preprint = True
            recently_published = published
            preprint.node.set_privacy('public')
            save_node = True

        if save_node:
            try:
                preprint.node.save()
            except ValidationError as e:
                # Raised from invalid DOI
                raise exceptions.ValidationError(detail=e.messages[0])

        if save_preprint:
            preprint.save()

        # Send preprint confirmation email signal to new authors on preprint! -- only when published
        # TODO: Some more thought might be required on this; preprints made from existing
        # nodes will send emails making it seem like a new node.
        if recently_published:
            for author in preprint.node.contributors:
                if author != auth.user:
                    project_signals.contributor_added.send(
                        preprint.node,
                        contributor=author,
                        auth=auth,
                        email_template='preprint')

        return preprint

    def set_field(self, func, val, auth, save=False):
        try:
            func(val, auth)
        except PermissionsError as e:
            raise exceptions.PermissionDenied(detail=e.message)
        except (ValueError, ValidationError, NodeStateError) as e:
            raise exceptions.ValidationError(detail=e.message)
Exemple #3
0
class RegistrationSerializer(NodeSerializer):

    category_choices = NodeSerializer.category_choices
    category_choices_string = NodeSerializer.category_choices_string
    category = HideIfWithdrawal(
        ser.ChoiceField(choices=category_choices,
                        help_text="Choices: " + category_choices_string))

    date_modified = HideIfWithdrawal(ser.DateTimeField(read_only=True))
    fork = HideIfWithdrawal(ser.BooleanField(read_only=True, source='is_fork'))
    collection = HideIfWithdrawal(
        ser.BooleanField(read_only=True, source='is_collection'))
    node_license = HideIfWithdrawal(NodeLicenseSerializer())
    tags = HideIfWithdrawal(
        JSONAPIListField(child=NodeTagField(), required=False))
    public = HideIfWithdrawal(
        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'))
    current_user_permissions = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='List of strings representing the permissions '
            'for the current user on this node.'))

    pending_embargo_approval = HideIfWithdrawal(
        ser.BooleanField(
            read_only=True,
            source='is_pending_embargo',
            help_text=
            'The associated Embargo is awaiting approval by project admins.'))
    pending_registration_approval = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_registration',
            read_only=True,
            help_text=
            'The associated RegistrationApproval is awaiting approval by project admins.'
        ))
    pending_withdrawal = HideIfWithdrawal(
        ser.BooleanField(
            source='is_pending_retraction',
            read_only=True,
            help_text=
            'The registration is awaiting withdrawal approval by project admins.'
        ))
    withdrawn = ser.BooleanField(
        source='is_retracted',
        read_only=True,
        help_text='The registration has been withdrawn.')

    date_registered = ser.DateTimeField(source='registered_date',
                                        read_only=True,
                                        help_text='Date time of registration.')
    embargo_end_date = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text='When the embargo on this registration will be lifted.'))

    withdrawal_justification = ser.CharField(source='retraction.justification',
                                             read_only=True)
    template_from = HideIfWithdrawal(
        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.'))
    registration_supplement = ser.SerializerMethodField()
    registered_meta = HideIfWithdrawal(
        ser.SerializerMethodField(
            help_text=
            'A dictionary with supplemental registration questions and responses.'
        ))

    registered_by = HideIfWithdrawal(
        RelationshipField(
            related_view='users:user-detail',
            related_view_kwargs={'user_id': '<registered_user_id>'}))

    registered_from = HideIfWithdrawal(
        RelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<registered_from_id>'}))

    children = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-children',
            related_view_kwargs={'node_id': '<pk>'},
            related_meta={'count': 'get_node_count'},
        ))

    comments = HideIfWithdrawal(
        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 = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-providers',
                          related_view_kwargs={'node_id': '<pk>'}))

    wikis = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-wikis',
            related_view_kwargs={'node_id': '<pk>'},
        ))

    forked_from = HideIfWithdrawal(
        RelationshipField(
            related_view=lambda n: 'registrations:registration-detail'
            if getattr(n, 'is_registration', False) else 'nodes:node-detail',
            related_view_kwargs={'node_id': '<forked_from_id>'}))

    license = HideIfWithdrawal(
        RelationshipField(
            related_view='licenses:license-detail',
            related_view_kwargs={
                'license_id': '<node_license.node_license._id>'
            },
        ))

    forks = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-forks',
                          related_view_kwargs={'node_id': '<pk>'}))

    node_links = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-pointers',
                          related_view_kwargs={'node_id': '<pk>'},
                          related_meta={'count': 'get_pointers_count'}))

    parent = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<parent_node._id>'},
                          filter_key='parent_node'))

    logs = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-logs',
            related_view_kwargs={'node_id': '<pk>'},
        ))

    root = HideIfWithdrawal(
        RelationshipField(related_view='registrations:registration-detail',
                          related_view_kwargs={'node_id': '<root._id>'}))

    affiliated_institutions = HideIfWithdrawal(
        RelationshipField(
            related_view='registrations:registration-institutions',
            related_view_kwargs={'node_id': '<pk>'}))
    registrations = HideIfRegistration(
        RelationshipField(related_view='nodes:node-registrations',
                          related_view_kwargs={'node_id': '<pk>'}))
    identifiers = HideIfWithdrawal(
        RelationshipField(related_view='registrations:identifier-list',
                          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 get_current_user_permissions(self, obj):
        return NodeSerializer.get_current_user_permissions(self, obj)

    def update(self, *args, **kwargs):
        raise exceptions.APIException('Registrations cannot be modified.')

    class Meta:
        type_ = 'registrations'
Exemple #4
0
class PreprintSerializer(TaxonomizableSerializerMixin, MetricsSerializerMixin,
                         JSONAPISerializer):
    filterable_fields = frozenset([
        'id',
        'date_created',
        'date_modified',
        'date_published',
        'original_publication_date',
        'provider',
        'is_published',
        'subjects',
        'reviews_state',
        'node_is_public',
    ])
    available_metrics = frozenset([
        'downloads',
        'views',
    ])

    id = IDField(source='_id', read_only=True)
    type = TypeField()

    date_created = VersionedDateTimeField(source='created', read_only=True)
    date_modified = VersionedDateTimeField(source='modified', read_only=True)
    date_published = VersionedDateTimeField(read_only=True)
    original_publication_date = VersionedDateTimeField(required=False,
                                                       allow_null=True)
    doi = ser.CharField(source='article_doi', required=False, allow_null=True)
    title = ser.CharField(required=True, max_length=512)
    description = ser.CharField(required=False,
                                allow_blank=True,
                                allow_null=True)
    is_published = NoneIfWithdrawal(ser.BooleanField(required=False))
    is_preprint_orphan = NoneIfWithdrawal(ser.BooleanField(read_only=True))
    license_record = NodeLicenseSerializer(required=False, source='license')
    tags = JSONAPIListField(child=NodeTagField(), required=False)
    node_is_public = ser.BooleanField(
        read_only=True,
        source='node__is_public',
        help_text='Is supplementary project public?')
    preprint_doi_created = NoneIfWithdrawal(
        VersionedDateTimeField(read_only=True))
    date_withdrawn = VersionedDateTimeField(read_only=True, allow_null=True)
    withdrawal_justification = HideIfNotWithdrawal(
        ser.CharField(required=False, read_only=True, allow_blank=True))
    current_user_permissions = ser.SerializerMethodField(
        help_text='List of strings representing the permissions '
        'for the current user on this preprint.', )
    public = ser.BooleanField(source='is_public',
                              required=False,
                              read_only=True)
    contributors = RelationshipField(
        related_view='preprints:preprint-contributors',
        related_view_kwargs={'preprint_id': '<_id>'},
    )
    reviews_state = ser.CharField(source='machine_state',
                                  read_only=True,
                                  max_length=15)
    date_last_transitioned = NoneIfWithdrawal(
        VersionedDateTimeField(read_only=True))

    citation = NoneIfWithdrawal(
        RelationshipField(
            related_view='preprints:preprint-citation',
            related_view_kwargs={'preprint_id': '<_id>'},
        ))

    identifiers = NoneIfWithdrawal(
        RelationshipField(
            related_view='preprints:identifier-list',
            related_view_kwargs={'preprint_id': '<_id>'},
        ))

    node = NoneIfWithdrawal(
        NodeRelationshipField(
            related_view='nodes:node-detail',
            related_view_kwargs={'node_id': '<node._id>'},
            read_only=False,
            many=False,
            self_view='preprints:preprint-node-relationship',
            self_view_kwargs={'preprint_id': '<_id>'},
        ))

    license = PreprintLicenseRelationshipField(
        related_view='licenses:license-detail',
        related_view_kwargs={'license_id': '<license.node_license._id>'},
        read_only=False,
    )

    provider = PreprintProviderRelationshipField(
        related_view='providers:preprint-providers:preprint-provider-detail',
        related_view_kwargs={'provider_id': '<provider._id>'},
        read_only=False,
    )

    files = NoneIfWithdrawal(
        RelationshipField(
            related_view='preprints:preprint-storage-providers',
            related_view_kwargs={'preprint_id': '<_id>'},
        ))

    primary_file = NoneIfWithdrawal(
        PrimaryFileRelationshipField(
            related_view='files:file-detail',
            related_view_kwargs={'file_id': '<primary_file._id>'},
            read_only=False,
        ))

    review_actions = RelationshipField(
        related_view='preprints:preprint-review-action-list',
        related_view_kwargs={'preprint_id': '<_id>'},
    )

    requests = NoneIfWithdrawal(
        RelationshipField(
            related_view='preprints:preprint-request-list',
            related_view_kwargs={'preprint_id': '<_id>'},
        ))

    links = LinksField(
        {
            'self': 'get_preprint_url',
            'html': 'get_absolute_html_url',
            'doi': 'get_article_doi_url',
            'preprint_doi': 'get_preprint_doi_url',
        }, )

    class Meta:
        type_ = 'preprints'

    def get_preprint_url(self, obj):
        return absolute_reverse(
            'preprints:preprint-detail',
            kwargs={
                'preprint_id':
                obj._id,
                'version':
                self.context['request'].parser_context['kwargs']['version']
            })

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

    def get_article_doi_url(self, obj):
        return 'https://doi.org/{}'.format(
            obj.article_doi) if obj.article_doi else None

    def get_current_user_permissions(self, obj):
        user = self.context['request'].user
        all_perms = ['read', 'write', 'admin']
        user_perms = []
        for p in all_perms:
            if obj.has_permission(user, p):
                user_perms.append(p)
        return user_perms

    def get_preprint_doi_url(self, obj):
        doi = None
        doi_identifier = obj.get_identifier('doi')
        if doi_identifier:
            doi = doi_identifier.value
        # if a preprint hasn't been published yet, don't show the DOI prematurely
        elif obj.is_published:
            client = obj.get_doi_client()
            doi = client.build_doi(preprint=obj) if client else None
        return 'https://doi.org/{}'.format(doi) if doi else None

    def update(self, preprint, validated_data):
        assert isinstance(
            preprint,
            Preprint), 'You must specify a valid preprint to be updated'

        auth = get_user_auth(self.context['request'])
        if not preprint.has_permission(auth.user, osf_permissions.WRITE):
            raise exceptions.PermissionDenied(
                detail=
                'User must have admin or write permissions to update a preprint.'
            )

        published = validated_data.pop('is_published', None)
        if published and preprint.provider.is_reviewed:
            raise Conflict(
                '{} uses a moderation workflow, so preprints must be submitted for review instead of published directly. Submit a preprint by creating a `submit` Action at {}'
                .format(
                    preprint.provider.name,
                    absolute_reverse(
                        'preprints:preprint-review-action-list',
                        kwargs={
                            'version':
                            self.context['request'].parser_context['kwargs']
                            ['version'],
                            'preprint_id':
                            preprint._id,
                        },
                    ),
                ))

        save_preprint = False
        recently_published = False

        primary_file = validated_data.pop('primary_file', None)
        if primary_file:
            self.set_field(preprint.set_primary_file, primary_file, auth)
            save_preprint = True

        old_tags = set(preprint.tags.values_list('name', flat=True))
        if 'tags' in validated_data:
            current_tags = set(validated_data.pop('tags', []))
        elif self.partial:
            current_tags = set(old_tags)
        else:
            current_tags = set()

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

        if 'node' in validated_data:
            node = validated_data.pop('node', None)
            self.set_field(preprint.set_supplemental_node, node, auth)
            save_preprint = True

        if 'subjects' in validated_data:
            subjects = validated_data.pop('subjects', None)
            self.set_field(preprint.set_subjects, subjects, auth)
            save_preprint = True

        if 'title' in validated_data:
            title = validated_data['title']
            self.set_field(preprint.set_title, title, auth)
            save_preprint = True

        if 'description' in validated_data:
            description = validated_data['description']
            self.set_field(preprint.set_description, description, auth)
            save_preprint = True

        if 'article_doi' in validated_data:
            preprint.article_doi = validated_data['article_doi']
            save_preprint = True

        if 'license_type' in validated_data or 'license' in validated_data:
            license_details = get_license_details(preprint, validated_data)
            self.set_field(preprint.set_preprint_license, license_details,
                           auth)
            save_preprint = True

        if 'original_publication_date' in validated_data:
            preprint.original_publication_date = validated_data[
                'original_publication_date'] or None
            save_preprint = True

        if published is not None:
            if not preprint.primary_file:
                raise exceptions.ValidationError(
                    detail=
                    'A valid primary_file must be set before publishing a preprint.'
                )
            self.set_field(preprint.set_published, published, auth)
            save_preprint = True
            recently_published = published
            preprint.set_privacy('public', log=False, save=True)

        if save_preprint:
            preprint.save()

        if recently_published:
            for author in preprint.contributors:
                if author != auth.user:
                    project_signals.contributor_added.send(
                        preprint,
                        contributor=author,
                        auth=auth,
                        email_template='preprint')

        return preprint

    def set_field(self, func, val, auth, save=False):
        try:
            func(val, auth)
        except PermissionsError as e:
            raise exceptions.PermissionDenied(detail=str(e))
        except (ValueError, ValidationError, NodeStateError) as e:
            raise exceptions.ValidationError(detail=e.message)
Exemple #5
0
class PreprintSerializer(JSONAPISerializer):
    filterable_fields = frozenset([
        'id', 'title', 'tags', 'date_created', 'date_modified', 'contributors',
        'subjects', 'doi'
    ])

    title = ser.CharField(required=False)
    subjects = JSONAPIListField(child=TaxonomyField(),
                                required=False,
                                source='preprint_subjects')
    provider = ser.CharField(source='preprint_provider', required=False)
    date_created = ser.DateTimeField(read_only=True, source='preprint_created')
    date_modified = ser.DateTimeField(read_only=True)
    id = IDField(source='_id', required=False)
    abstract = ser.CharField(source='description', required=False)
    tags = JSONAPIListField(child=NodeTagField(), required=False)
    doi = ser.CharField(source='preprint_doi', required=False)

    primary_file = PrimaryFileRelationshipField(
        related_view='files:file-detail',
        related_view_kwargs={'file_id': '<preprint_file._id>'},
        lookup_url_kwarg='file_id',
        read_only=False)

    files = RelationshipField(related_view='nodes:node-providers',
                              related_view_kwargs={'node_id': '<pk>'})

    providers = RelationshipField(
        related_view='preprints:preprint-preprint_providers',
        related_view_kwargs={'node_id': '<pk>'},
        self_view='preprints:preprint-relationships-preprint_providers',
        self_view_kwargs={'node_id': '<pk>'})

    links = LinksField({
        'self': 'get_preprint_url',
        'html': 'get_absolute_html_url',
        'doi': 'get_doi_url'
    })

    contributors = RelationshipField(
        related_view='nodes:node-contributors',
        related_view_kwargs={'node_id': '<pk>'},
        related_meta={'count': 'get_contrib_count'},
    )

    class Meta:
        type_ = 'preprints'

    def get_preprint_url(self, obj):
        return absolute_reverse('preprints:preprint-detail',
                                kwargs={'node_id': obj._id})

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

    def get_doi_url(self, obj):
        return 'https://dx.doi.org/{}'.format(
            obj.preprint_doi) if obj.preprint_doi else None

    def create(self, validated_data):
        node = Node.load(validated_data.pop('_id', None))
        if not node:
            raise exceptions.NotFound('Unable to find Node with specified id.')

        auth = get_user_auth(self.context['request'])
        if not node.has_permission(auth.user, permissions.ADMIN):
            raise exceptions.PermissionDenied

        if node.is_preprint:
            raise Conflict(
                'This node already stored as a preprint, use the update method instead.'
            )

        primary_file = validated_data.pop('primary_file', None)
        if not primary_file:
            raise exceptions.ValidationError(
                detail='You must specify a primary_file to create a preprint.')

        self.set_node_field(node.set_preprint_file, primary_file, auth)

        subjects = validated_data.pop('preprint_subjects', None)
        if not subjects:
            raise exceptions.ValidationError(
                detail=
                'You must specify at least one subject to create a preprint.')

        self.set_node_field(node.set_preprint_subjects, subjects, auth)

        tags = validated_data.pop('tags', None)
        if tags:
            for tag in tags:
                node.add_tag(tag, auth, save=False, log=False)

        for key, value in validated_data.iteritems():
            setattr(node, key, value)
        try:
            node.save()
        except ValidationValueError as e:
            raise exceptions.ValidationError(detail=e.message)

        # Send preprint confirmation email signal to new authors on preprint!
        for author in node.contributors:
            if author != auth.user:
                project_signals.contributor_added.send(
                    node,
                    contributor=author,
                    auth=auth,
                    email_template='preprint')

        return node

    def update(self, node, validated_data):
        from website.models import Node
        assert isinstance(node,
                          Node), 'You must specify a valid node to be updated.'
        auth = get_user_auth(self.context['request'])
        primary_file = validated_data.pop('primary_file', None)
        if primary_file:
            self.set_node_field(node.set_preprint_file, primary_file, auth)
        subjects = validated_data.pop('preprint_subjects', None)
        if subjects:
            self.set_node_field(node.set_preprint_subjects, subjects, auth)

        old_tags = set([tag._id for tag in node.tags])
        if 'tags' in validated_data:
            current_tags = set(validated_data.pop('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)

        for key, value in validated_data.iteritems():
            setattr(node, key, value)
        try:
            node.save()
        except ValidationValueError as e:
            raise exceptions.ValidationError(detail=e.message)
        return node

    def set_node_field(self, func, val, auth):
        try:
            func(val, auth, save=False)
        except PermissionsError:
            raise exceptions.PermissionDenied(
                'Not authorized to update this node.')
        except ValueError as e:
            raise exceptions.ValidationError(detail=e.message)