Esempio n. 1
0
class RecordSchema(RecordSchemaBase):
    """Schema for records in JSON."""

    parent = NestedAttribute(ParentSchema, dump_only=True)
    versions = NestedAttribute(VersionsSchema, dump_only=True)
    is_published = fields.Boolean(dump_only=True)
    expires_at = fields.Str(dump_only=True)
Esempio n. 2
0
class AccessSchema(Schema):
    """Access schema."""

    record = SanitizedUnicode(required=True)
    files = SanitizedUnicode(required=True)
    embargo = NestedAttribute(EmbargoSchema)
    owned_by = fields.List(fields.Nested(Agent))

    def validate_protection_value(self, value, field_name):
        """Check that the protection value is valid."""
        if value not in ["public", "restricted"]:
            raise ValidationError(
                _(f"'{field_name}' must be either 'public' or 'restricted'"),
                "record")

    def get_attribute(self, obj, key, default):
        """Override from where we get attributes when serializing."""
        if key in ["record", "files"]:
            return getattr(obj.protection, key, default)
        elif key == "status":
            return obj.status.value
        return getattr(obj, key, default)

    @validates("record")
    def validate_record_protection(self, value):
        """Validate the record protection value."""
        self.validate_protection_value(value, "record")

    @validates("files")
    def validate_files_protection(self, value):
        """Validate the files protection value."""
        self.validate_protection_value(value, "files")
class ParentAccessSchema(Schema):
    """Access schema."""

    metadata = SanitizedUnicode(required=True)
    files = SanitizedUnicode(required=True)
    status = SanitizedUnicode(dump_only=False)
    embargo = NestedAttribute(EmbargoSchema)
    owned_by = List(fields.Nested(Agent))

    @validates_schema
    def validate_embargo(self, data, **kwargs):
        """Validate that the properties are consistent with each other."""
        metadata = data.get("metadata", "")
        embargo = data.get("embargo", None)
        if AccessStatusEnum.EMBARGOED.value == metadata and not embargo:
            raise ValidationError(
                _("Embargo schema must be set if metadata is Embargoed"),
                field_name="embargo",
            )
class AccessSchema(Schema):
    """Access schema."""

    metadata = SanitizedUnicode(required=True)
    files = SanitizedUnicode(required=True)
    embargo = NestedAttribute(EmbargoSchema)
    status = SanitizedUnicode(dump_only=False)
    owned_by = List(Nested(Agent))

    def validate_protection_value(self, value, field_name):
        """Check that the protection value is valid."""
        if value not in AccessStatusEnum.list():
            raise ValidationError(
                _("'{}' must be either '{}', '{}' or '{}'").format(
                    field_name,
                    *AccessStatusEnum.list(),
                ),
                "record",
            )

    @validates("metadata")
    def validate_record_protection(self, value):
        """Validate the record protection value."""
        self.validate_protection_value(value, "metadata")

    @validates_schema
    def validate_embargo(self, data, **kwargs):
        """Validate that the properties are consistent with each other."""
        metadata = data.get("metadata", "")
        embargo = data.get("embargo", "")
        if AccessStatusEnum.EMBARGOED.value == metadata and not embargo:
            raise ValidationError(
                _("Embargo must be set if metadata is Embargoed"),
                field_name="embargo",
            )

    @validates("files")
    def validate_files_protection(self, value):
        """Validate the files protection value."""
        self.validate_protection_value(value, "files")
Esempio n. 5
0
class Marc21RecordSchema(BaseRecordSchema):
    """Record schema."""

    id = fields.Str()
    # pid
    pids = fields.List(NestedAttribute(PIDSchema))

    parent = NestedAttribute(Marc21ParentSchema, dump_only=True)

    metadata = NestedAttribute(MetadataSchema)
    access = NestedAttribute(AccessSchema)
    files = NestedAttribute(FilesSchema, dump_only=True)

    created = fields.Str(dump_only=True)
    updated = fields.Str(dump_only=True)
    revision = fields.Integer(dump_only=True)

    versions = NestedAttribute(VersionsSchema, dump_only=True)

    is_published = fields.Boolean(dump_only=True)

    # Add version to record schema
    # versions = NestedAttribute(VersionsSchema, dump_only=True)

    @post_dump
    def default_nested(self, data, many, **kwargs):
        """Serialize metadata as empty dict for partial drafts.

        Cannot use marshmallow for Nested fields due to issue:
        https://github.com/marshmallow-code/marshmallow/issues/1566
        https://github.com/marshmallow-code/marshmallow/issues/41
        and more.
        """
        if not data.get("metadata"):
            data["metadata"] = {}

        return data
Esempio n. 6
0
class RDMRecordSchema(RecordSchema, FieldPermissionsMixin):
    """Record schema."""

    class Meta:
        """Meta class."""

        # TODO: RAISE instead!
        unknown = EXCLUDE

    field_load_permissions = {
        'access': 'manage',
        'files': 'update',
    }

    field_dump_permissions = {
        'files': 'read_files',
    }

    pids = fields.List(NestedAttribute(PIDSchema))
    metadata = NestedAttribute(MetadataSchema)
    # ext = fields.Method('dump_extensions', 'load_extensions')
    # tombstone
    # provenance
    access = fields.Nested(AccessSchema)
    # files = NestedAttribute(FilesSchema, dump_only=True)
    # notes = fields.List(fields.Nested(InternalNoteSchema))
    revision = fields.Integer(dump_only=True)
    versions = NestedAttribute(VersionsSchema, dump_only=True)
    parent = NestedAttribute(RDMParentSchema, dump_only=True)

    is_published = fields.Boolean(dump_only=True)

    # communities = NestedAttribute(CommunitiesSchema)
    # stats = NestedAttribute(StatsSchema, dump_only=True)
    # relations = NestedAttribute(RelationsSchema, dump_only=True)
    # schema_version = fields.Interger(dump_only=True)

    # def dump_extensions(self, obj):
    #     """Dumps the extensions value.

    #     :params obj: invenio_records_files.api.Record instance
    #     """
    #     current_app_metadata_extensions = (
    #         current_app.extensions['invenio-rdm-records'].metadata_extensions
    #     )
    #     ExtensionSchema = current_app_metadata_extensions.to_schema()
    #     return ExtensionSchema().dump(obj.get('extensions', {}))

    # def load_extensions(self, value):
    #     """Loads the 'extensions' field.

    #     :params value: content of the input's 'extensions' field
    #     """
    #     current_app_metadata_extensions = (
    #         current_app.extensions['invenio-rdm-records'].metadata_extensions
    #     )
    #     ExtensionSchema = current_app_metadata_extensions.to_schema()

    #     return ExtensionSchema().load(value)

    @post_dump
    def default_nested(self, data, many, **kwargs):
        """Serialize metadata as empty dict for partial drafts.

        Cannot use marshmallow for Nested fields due to issue:
        https://github.com/marshmallow-code/marshmallow/issues/1566
        https://github.com/marshmallow-code/marshmallow/issues/41
        and more.
        """
        if not data.get("metadata"):
            data["metadata"] = {}

        return data
Esempio n. 7
0
class RDMRecordSchema(RecordSchema, FieldPermissionsMixin):
    """Record schema."""

    field_load_permissions = {
        'access': 'manage',
        'files': 'update_draft',
    }

    # ATTENTION: In this schema you should be using the ``NestedAttribute``
    # instead  of Marshmallow's ``fields.Nested``. Using NestedAttribute
    # ensures that the nested schema will receive the system field instead of
    # the record dict (i.e. record.myattr instead of record['myattr']).

    pids = fields.Dict(keys=fields.String(), values=fields.Nested(PIDSchema))
    metadata = NestedAttribute(MetadataSchema)
    # ext = fields.Method('dump_extensions', 'load_extensions')
    # tombstone
    # provenance
    access = NestedAttribute(AccessSchema)
    files = NestedAttribute(FilesSchema)
    # notes = fields.List(fields.Nested(InternalNoteSchema))
    revision = fields.Integer(dump_only=True)
    versions = NestedAttribute(VersionsSchema, dump_only=True)
    parent = NestedAttribute(RDMParentSchema, dump_only=True)

    is_published = fields.Boolean(dump_only=True)

    # communities = NestedAttribute(CommunitiesSchema)
    # stats = NestedAttribute(StatsSchema, dump_only=True)
    # relations = NestedAttribute(RelationsSchema, dump_only=True)
    # schema_version = fields.Interger(dump_only=True)

    # def dump_extensions(self, obj):
    #     """Dumps the extensions value.

    #     :params obj: invenio_records_files.api.Record instance
    #     """
    #     current_app_metadata_extensions = (
    #         current_app.extensions['invenio-rdm-records'].metadata_extensions
    #     )
    #     ExtensionSchema = current_app_metadata_extensions.to_schema()
    #     return ExtensionSchema().dump(obj.get('extensions', {}))

    # def load_extensions(self, value):
    #     """Loads the 'extensions' field.

    #     :params value: content of the input's 'extensions' field
    #     """
    #     current_app_metadata_extensions = (
    #         current_app.extensions['invenio-rdm-records'].metadata_extensions
    #     )
    #     ExtensionSchema = current_app_metadata_extensions.to_schema()

    #     return ExtensionSchema().load(value)

    @validates("pids")
    def validate_pids(self, value):
        """Validates 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(
                fail_on_unknown=False, identifier_required=True)
            id_schema.load({
                "scheme": scheme,
                "identifier": pid_attrs.get("identifier")
            })

    @post_dump
    def default_nested(self, data, many, **kwargs):
        """Serialize metadata as empty dict for partial drafts.

        Cannot use marshmallow for Nested fields due to issue:
        https://github.com/marshmallow-code/marshmallow/issues/1566
        https://github.com/marshmallow-code/marshmallow/issues/41
        and more.
        """
        if not data.get("metadata"):
            data["metadata"] = {}

        return data
Esempio n. 8
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")
            })
Esempio n. 9
0
class RDMRecordSchema(RecordSchema, FieldPermissionsMixin):
    """Record schema."""

    field_load_permissions = {
        'access': 'manage',
        'files': 'update',
    }

    field_dump_permissions = {
        'files': 'read_files',
    }

    # PIDS-FIXME: Re-enable when pids implementation is ready
    # pids = fields.Dict(keys=fields.String(), values=fields.Nested(PIDSchema))
    metadata = NestedAttribute(MetadataSchema)
    # ext = fields.Method('dump_extensions', 'load_extensions')
    # tombstone
    # provenance
    access = fields.Nested(AccessSchema)
    # files = NestedAttribute(FilesSchema, dump_only=True)
    # notes = fields.List(fields.Nested(InternalNoteSchema))
    revision = fields.Integer(dump_only=True)
    versions = NestedAttribute(VersionsSchema, dump_only=True)
    parent = NestedAttribute(RDMParentSchema, dump_only=True)

    is_published = fields.Boolean(dump_only=True)

    # communities = NestedAttribute(CommunitiesSchema)
    # stats = NestedAttribute(StatsSchema, dump_only=True)
    # relations = NestedAttribute(RelationsSchema, dump_only=True)
    # schema_version = fields.Interger(dump_only=True)

    # def dump_extensions(self, obj):
    #     """Dumps the extensions value.

    #     :params obj: invenio_records_files.api.Record instance
    #     """
    #     current_app_metadata_extensions = (
    #         current_app.extensions['invenio-rdm-records'].metadata_extensions
    #     )
    #     ExtensionSchema = current_app_metadata_extensions.to_schema()
    #     return ExtensionSchema().dump(obj.get('extensions', {}))

    # def load_extensions(self, value):
    #     """Loads the 'extensions' field.

    #     :params value: content of the input's 'extensions' field
    #     """
    #     current_app_metadata_extensions = (
    #         current_app.extensions['invenio-rdm-records'].metadata_extensions
    #     )
    #     ExtensionSchema = current_app_metadata_extensions.to_schema()

    #     return ExtensionSchema().load(value)

    # @validates("pids")
    # def validate_pids(self, value):
    #     """Validates 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(allow_all=True, required=True)
    #         id_schema.load({
    #             "scheme": scheme,
    #             "identifier": pid_attrs.get("identifier")
    #         })

    @post_dump
    def default_nested(self, data, many, **kwargs):
        """Serialize metadata as empty dict for partial drafts.

        Cannot use marshmallow for Nested fields due to issue:
        https://github.com/marshmallow-code/marshmallow/issues/1566
        https://github.com/marshmallow-code/marshmallow/issues/41
        and more.
        """
        if not data.get("metadata"):
            data["metadata"] = {}

        return data
Esempio n. 10
0
class CommunitySchema(BaseRecordSchema):
    """Schema for the community metadata."""

    id = SanitizedUnicode(validate=_not_blank(max=100))
    metadata = NestedAttribute(CommunityMetadataSchema, required=True)
    access = NestedAttribute(CommunityAccessSchema, required=True)
Esempio n. 11
0
class RDMRecordSchema(RecordSchema, FieldPermissionsMixin):
    """Record schema."""

    field_load_permissions = {
        'files': 'update_draft',
    }

    # ATTENTION: In this schema you should be using the ``NestedAttribute``
    # instead  of Marshmallow's ``fields.Nested``. Using NestedAttribute
    # ensures that the nested schema will receive the system field instead of
    # the record dict (i.e. record.myattr instead of record['myattr']).

    pids = fields.Dict(
        keys=SanitizedUnicode(validate=validate_scheme),
        values=fields.Nested(PIDSchema),
    )
    metadata = NestedAttribute(MetadataSchema)
    # ext = fields.Method('dump_extensions', 'load_extensions')
    # tombstone
    # provenance
    access = NestedAttribute(AccessSchema)
    files = NestedAttribute(FilesSchema)
    # notes = fields.List(fields.Nested(InternalNoteSchema))
    revision = fields.Integer(dump_only=True)
    versions = NestedAttribute(VersionsSchema, dump_only=True)
    parent = NestedAttribute(RDMParentSchema)
    is_published = fields.Boolean(dump_only=True)
    status = fields.String(dump_only=True)

    # stats = NestedAttribute(StatsSchema, dump_only=True)
    # relations = NestedAttribute(RelationsSchema, dump_only=True)
    # schema_version = fields.Interger(dump_only=True)

    # def dump_extensions(self, obj):
    #     """Dumps the extensions value.

    #     :params obj: invenio_records_files.api.Record instance
    #     """
    #     current_app_metadata_extensions = (
    #         current_app.extensions['invenio-rdm-records'].metadata_extensions
    #     )
    #     ExtensionSchema = current_app_metadata_extensions.to_schema()
    #     return ExtensionSchema().dump(obj.get('extensions', {}))

    # def load_extensions(self, value):
    #     """Loads the 'extensions' field.

    #     :params value: content of the input's 'extensions' field
    #     """
    #     current_app_metadata_extensions = (
    #         current_app.extensions['invenio-rdm-records'].metadata_extensions
    #     )
    #     ExtensionSchema = current_app_metadata_extensions.to_schema()

    #     return ExtensionSchema().load(value)

    @post_dump
    def default_nested(self, data, many, **kwargs):
        """Serialize fields as empty dict for partial drafts.

        Cannot use marshmallow for Nested fields due to issue:
        https://github.com/marshmallow-code/marshmallow/issues/1566
        https://github.com/marshmallow-code/marshmallow/issues/41
        and more.
        """
        if not data.get("metadata"):
            data["metadata"] = {}
        if not data.get("pids"):
            data["pids"] = {}

        return data