Exemplo n.º 1
0
class AffiliationSchema(Schema):
    """Affiliation of a creator/contributor."""

    name = SanitizedUnicode(required=True)
    identifiers = IdentifierSet(
        fields.Nested(IdentifierSchema,
                      allowed_schemes=RDM_RECORDS_IDENTIFIERS_SCHEMES), )
Exemplo n.º 2
0
class PersonOrOrganizationSchema(Schema):
    """Person or Organization schema."""

    NAMES = ["organizational", "personal"]

    type = SanitizedUnicode(
        required=True,
        validate=validate.OneOf(
            choices=NAMES,
            error=_('Invalid value. Choose one of {NAMES}.').format(
                NAMES=NAMES)),
        error_messages={
            # [] needed to mirror error message above
            "required":
            [_('Invalid value. Choose one of {NAMES}.').format(NAMES=NAMES)]
        })
    name = SanitizedUnicode()
    given_name = SanitizedUnicode()
    family_name = SanitizedUnicode()
    identifiers = IdentifierSet(
        fields.Nested(
            partial(
                IdentifierSchema,
                # It is intended to allow org schemes to be sent as personal
                # and viceversa. This is a trade off learnt from running
                # Zenodo in production.
                allowed_schemes=record_personorg_schemes)))

    @validates_schema
    def validate_names(self, data, **kwargs):
        """Validate names based on type."""
        if data['type'] == "personal":
            if not data.get('family_name'):
                messages = [_("Family name must be filled.")]
                raise ValidationError({"family_name": messages})

        elif data['type'] == "organizational":
            if not data.get('name'):
                messages = [_('Name cannot be blank.')]
                raise ValidationError({"name": messages})

    @post_load
    def update_names(self, data, **kwargs):
        """Update names for organization / person.

        Fill name from given_name and family_name if person.
        Remove given_name and family_name if organization.
        """
        if data["type"] == "personal":
            names = [data.get("family_name"), data.get("given_name")]
            data["name"] = ", ".join([n for n in names if n])

        elif data['type'] == "organizational":
            if 'family_name' in data:
                del data['family_name']
            if 'given_name' in data:
                del data['given_name']

        return data
Exemplo n.º 3
0
class AffiliationSchema(BaseVocabularySchema):
    """Service schema for affiliations."""

    acronym = SanitizedUnicode()
    identifiers = IdentifierSet(
        fields.Nested(
            partial(IdentifierSchema,
                    allowed_schemes=affiliation_schemes,
                    identifier_required=False)))
    name = SanitizedUnicode(required=True)
Exemplo n.º 4
0
class TestSchema(Schema):
    allowed_schemes = {
        "orcid": {
            "label": "ORCID",
            "validator": idutils.is_orcid
        },
        "doi": {
            "label": "DOI",
            "validator": idutils.is_doi
        }
    }
    identifiers = IdentifierSet(
        Nested(partial(IdentifierSchema, allowed_schemes=allowed_schemes)))
Exemplo n.º 5
0
class MetadataSchema(Schema):
    """Schema for the record metadata."""

    field_load_permissions = {
        # TODO: define "can_admin" action
    }

    field_dump_permissions = {
        # TODO: define "can_admin" action
    }

    class Meta:
        """Meta class to accept unknwon fields."""

        unknown = INCLUDE

    # Metadata fields
    resource_type = ResourceType(required=True)
    creators = fields.List(fields.Nested(CreatorSchema),
                           required=True,
                           validate=validate.Length(
                               min=1,
                               error=_("Missing data for required field.")))
    title = SanitizedUnicode(required=True, validate=validate.Length(min=3))
    additional_titles = fields.List(fields.Nested(TitleSchema))
    publisher = SanitizedUnicode()
    publication_date = EDTFDateString(required=True)
    subjects = fields.List(fields.Nested(SubjectSchema))
    contributors = fields.List(fields.Nested(ContributorSchema))
    dates = fields.List(fields.Nested(DateSchema))
    languages = fields.List(fields.Nested(LanguageSchema))
    # alternate identifiers
    identifiers = IdentifierSet(
        fields.Nested(partial(IdentifierSchema, allow_all=True)))
    related_identifiers = fields.List(fields.Nested(RelatedIdentifierSchema))
    sizes = fields.List(
        SanitizedUnicode(
            validate=_not_blank(_('Size cannot be a blank string.'))))
    formats = fields.List(
        SanitizedUnicode(
            validate=_not_blank(_('Format cannot be a blank string.'))))
    version = SanitizedUnicode()
    rights = fields.List(fields.Nested(RightsSchema))
    description = SanitizedHTML(validate=validate.Length(min=3))
    additional_descriptions = fields.List(fields.Nested(DescriptionSchema))
    locations = fields.List(fields.Nested(LocationSchema))
    funding = fields.List(fields.Nested(FundingSchema))
    references = fields.List(fields.Nested(ReferenceSchema))
Exemplo n.º 6
0
class AwardRelationSchema(Schema):
    """Award relation schema."""

    id = SanitizedUnicode()
    number = SanitizedUnicode()
    title = i18n_strings
    identifiers = IdentifierSet(
        fields.Nested(
            partial(IdentifierSchema,
                    allowed_schemes=award_schemes,
                    identifier_required=False)))

    @validates_schema
    def validate_data(self, data, **kwargs):
        """Validate either id or number/title are present."""
        id_ = data.get("id")
        number = data.get("number")
        title = data.get("title")
        if not id_ and not (number and title):
            raise ValidationError(
                _("An existing id or number/title must be present."), "award")
Exemplo n.º 7
0
class AwardSchema(BaseVocabularySchema):
    """Award schema."""

    identifiers = IdentifierSet(
        fields.Nested(
            partial(IdentifierSchema,
                    allowed_schemes=award_schemes,
                    identifier_required=False)))
    number = SanitizedUnicode(required=True,
                              validate=validate.Length(
                                  min=1, error=_('Number cannot be blank.')))
    funder = fields.Nested(FunderRelationSchema)

    acronym = SanitizedUnicode()

    id = SanitizedUnicode(
        validate=validate.Length(min=1, error=_('Pid cannot be blank.')))

    @validates_schema
    def validate_id(self, data, **kwargs):
        """Validates ID."""
        is_create = "record" not in self.context
        if is_create and "id" not in data:
            raise ValidationError(_("Missing PID."), "id")
        if not is_create:
            data.pop("id", None)

    @post_load(pass_many=False)
    def move_id(self, data, **kwargs):
        """Moves id to pid."""
        if "id" in data:
            data["pid"] = data.pop("id")
        return data

    @pre_dump(pass_many=False)
    def extract_pid_value(self, data, **kwargs):
        """Extracts the PID value."""
        data['id'] = data.pid.pid_value
        return data
Exemplo n.º 8
0
class MetadataSchema(Schema):
    """Schema for the record metadata."""

    # Metadata fields
    resource_type = fields.Nested(VocabularySchema, required=True)
    creators = fields.List(fields.Nested(CreatorSchema),
                           required=True,
                           validate=validate.Length(
                               min=1,
                               error=_("Missing data for required field.")))
    title = SanitizedUnicode(required=True, validate=validate.Length(min=3))
    additional_titles = fields.List(fields.Nested(TitleSchema))
    publisher = SanitizedUnicode()
    publication_date = EDTFDateString(required=True)
    subjects = fields.List(fields.Nested(SubjectSchema))
    contributors = fields.List(fields.Nested(ContributorSchema))
    dates = fields.List(fields.Nested(DateSchema))
    languages = fields.List(fields.Nested(VocabularySchema))
    # alternate identifiers
    identifiers = IdentifierSet(
        fields.Nested(
            partial(IdentifierSchema,
                    allowed_schemes=record_identifiers_schemes)))
    related_identifiers = fields.List(fields.Nested(RelatedIdentifierSchema))
    sizes = fields.List(
        SanitizedUnicode(
            validate=_not_blank(_('Size cannot be a blank string.'))))
    formats = fields.List(
        SanitizedUnicode(
            validate=_not_blank(_('Format cannot be a blank string.'))))
    version = SanitizedUnicode()
    rights = fields.List(fields.Nested(RightsSchema))
    description = SanitizedHTML(validate=validate.Length(min=3))
    additional_descriptions = fields.List(fields.Nested(DescriptionSchema))
    locations = fields.Nested(FeatureSchema)
    funding = fields.List(fields.Nested(FundingSchema))
    references = fields.List(fields.Nested(ReferenceSchema))
class AffiliationSchema(Schema):
    """Affiliation of a creator/contributor."""

    name = SanitizedUnicode(required=True)
    identifiers = IdentifierSet(fields.Nested(IdentifierSchema), )
Exemplo n.º 10
0
class TestSchema(Schema):
    identifiers = IdentifierSet(
        Nested(partial(IdentifierSchema, allowed_schemes=["doi", "orcid"])))
Exemplo n.º 11
0
class DataSetMetadataSchemaV2(InvenioRecordMetadataFilesMixin,
                              InvenioRecordMetadataSchemaV1Mixin,
                              StrictKeysMixin):
    """DataSet metaddata schema."""

    resource_type = ResourceType(required=True)
    creators = fields.List(fields.Nested(CreatorSchema),
                           required=True,
                           validate=validate.Length(
                               min=1,
                               error=_("Missing data for required field.")))
    creator = SanitizedUnicode()
    title = MultilingualStringV2(required=True)
    additional_titles = List(MultilingualStringV2())
    publisher = SanitizedUnicode()
    publication_date = EDTFDateString(required=True)
    subjects = List(fields.Nested(SubjectSchema))
    contributors = List(fields.Nested(ContributorSchema))
    dates = List(fields.Nested(DateSchema))
    languages = TaxonomyField(mixins=[TitledMixin], many=True)
    # alternate identifiers
    identifiers = IdentifierSet(
        fields.Nested(
            partial(IdentifierSchema,
                    allowed_schemes=RDM_RECORDS_IDENTIFIERS_SCHEMES)))
    related_identifiers = List(fields.Nested(RelatedIdentifierSchema))
    version = SanitizedUnicode()
    rights = TaxonomyField(mixins=[TitledMixin, RightsMixin], many=True)
    abstract = MultilingualStringV2(
        required=True)  # WARNING: May contain user-input HTML
    additional_descriptions = fields.List(MultilingualStringV2())
    references = fields.List(fields.Nested(ReferenceSchema))
    pids = fields.Dict(keys=fields.String(), values=fields.Nested(PIDSchema))
    access = NestedAttribute(AccessSchema)
    keywords = List(SanitizedUnicode())

    @pre_load
    def sanitize_html_fields(self, data, **kwargs):
        """Sanitize fields that may contain user-input HTML strings."""
        if 'abstract' in data:
            for lang, val in data.get('abstract').items():
                raw = data['abstract'][lang]
                data['abstract'][lang] = SanitizedHTML()._deserialize(
                    raw, 'abstract', data)

        return data

    @pre_load
    def set_created(self, data, **kwargs):
        """Set created timestamp if not already set."""
        dates = data.get('dates') or []
        created = None

        for dat in dates:
            if dat.get('type', '') == 'created':
                created = dat.get('date')

        if not created:
            dates.append({
                'date': datetime.today().strftime('%Y-%m-%d'),
                'type': 'created'
            })
            data['dates'] = dates

        return data

    @pre_load
    def set_creator(self, data, **kwargs):
        """Set creator to record metadata if not known."""
        if not data.get('creator'):
            if current_user and current_user.is_authenticated:
                data['creator'] = current_user.email
            else:
                data['creator'] = 'anonymous'
        return data

    @validates('pids')
    def validate_pids(self, value):
        """Validate the keys of the pids are supported providers."""
        for scheme, pid_attrs in value.items():
            # The required flag applies to the identifier value
            # It won't fail for empty allowing the components to reserve one
            id_schema = IdentifierSchema(
                identifier_required=True,
                allowed_schemes=RDM_RECORDS_IDENTIFIERS_SCHEMES)
            id_schema.load({
                "scheme": scheme,
                "identifier": pid_attrs.get("identifier")
            })
Exemplo n.º 12
0
class PersonOrOrganizationSchema(Schema):
    """Person or Organization schema."""

    NAMES = ["organizational", "personal"]

    type = SanitizedUnicode(
        required=True,
        validate=validate.OneOf(
            choices=NAMES, error=_(f'Invalid value. Choose one of {NAMES}.')),
        error_messages={
            # NOTE: [] needed to mirror above error message
            "required": [_(f'Invalid value. Choose one of {NAMES}.')]
        })
    name = SanitizedUnicode()
    given_name = SanitizedUnicode()
    family_name = SanitizedUnicode()
    identifiers = IdentifierSet(
        fields.Nested(
            partial(
                IdentifierSchema,
                # It is intendedly allowing org schemes to be sent as personal
                # and viceversa. This is a trade off learnt from running
                # Zenodo in production.
                allowed_schemes={
                    "orcid": {
                        "label": "ORCID",
                        "validator": idutils.is_orcid
                    },
                    "isni": {
                        "label": "ISNI",
                        "validator": idutils.is_isni
                    },
                    "gnd": {
                        "label": "GND",
                        "validator": idutils.is_gnd
                    },
                    "ror": {
                        "label": "ROR",
                        "validator": idutils.is_ror
                    }
                })))

    @validates_schema
    def validate_names(self, data, **kwargs):
        """Validate names based on type."""
        if data['type'] == "personal":
            if not (data.get('given_name') or data.get('family_name')):
                messages = [_("Family name or given name must be filled.")]
                raise ValidationError({
                    "given_name": messages,
                    "family_name": messages
                })

        elif data['type'] == "organizational":
            if not data.get('name'):
                messages = [_('Name cannot be blank.')]
                raise ValidationError({"name": messages})

    @post_load
    def update_names(self, data, **kwargs):
        """Update names for organization / person.

        Fill name from given_name and family_name if person.
        Remove given_name and family_name if organization.
        """
        if data["type"] == "personal":
            names = [data.get("family_name"), data.get("given_name")]
            data["name"] = ", ".join([n for n in names if n])

        elif data['type'] == "organizational":
            if 'family_name' in data:
                del data['family_name']
            if 'given_name' in data:
                del data['given_name']

        return data
Exemplo n.º 13
0
class AffiliationSchema(BaseVocabularySchema):
    """Service schema for affiliations."""

    acronym = SanitizedUnicode()
    identifiers = IdentifierSet(fields.Nested(IdentifierSchema))
    name = SanitizedUnicode(required=True)