示例#1
0
class Marc21Draft(Draft):
    """Marc21 draft API."""

    model_cls = DraftMetadata

    index = IndexField("marc21records-drafts-marc21-v1.0.0",
                       search_alias="marc21records-marc21")

    pid = PIDField(
        key="id",
        provider=MarcDraftProvider,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
        delete=False,
    )

    conceptpid = PIDField(
        key="conceptid",
        provider=MarcDraftProvider,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
        delete=False,
    )

    files = FilesField(
        store=False,
        file_cls=DraftFile,
        delete=False,
    )

    bucket_id = ModelField(dump=False)

    bucket = ModelField(dump=False)
示例#2
0
class Marc21Record(Record):
    """Define API for Marc21 create and manipulate."""

    model_cls = RecordMetadata

    index = IndexField("marc21records-marc21-marc21-v1.0.0",
                       search_alias="marc21records-marc21")

    pid = PIDField(
        key="id",
        provider=MarcRecordProvider,
        delete=False,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
    )

    conceptpid = PIDField(
        key="conceptid",
        provider=MarcRecordProvider,
        delete=False,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
    )

    files = FilesField(
        store=False,
        file_cls=RecordFile,
        create=False,
        delete=False,
    )

    bucket_id = ModelField(dump=False)

    bucket = ModelField(dump=False)
示例#3
0
class Marc21Draft(Draft):
    """Marc21 draft API."""

    model_cls = DraftMetadata
    versions_model_cls = VersionsState
    parent_record_cls = Marc21Parent

    index = IndexField("marc21records-drafts-marc21-v1.0.0",
                       search_alias="marc21records-marc21")

    parent = ParentField(Marc21Parent,
                         create=True,
                         soft_delete=False,
                         hard_delete=True)

    pid = PIDField(
        key="id",
        provider=MarcDraftProvider,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
        delete=False,
    )

    files = FilesField(
        store=False,
        file_cls=DraftFile,
        delete=False,
    )

    bucket_id = ModelField(dump=False)

    bucket = ModelField(dump=False)
示例#4
0
class Marc21Record(Record):
    """Define API for Marc21 create and manipulate."""

    model_cls = RecordMetadata
    versions_model_cls = VersionsState
    parent_record_cls = Marc21Parent

    index = IndexField("marc21records-marc21-marc21-v1.0.0",
                       search_alias="marc21records-marc21")

    parent = ParentField(Marc21Parent,
                         create=True,
                         soft_delete=False,
                         hard_delete=True)

    pid = PIDField(
        key="id",
        provider=MarcRecordProvider,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
        delete=False,
    )

    files = FilesField(
        store=False,
        file_cls=RecordFile,
        create=False,
        delete=False,
    )

    bucket_id = ModelField(dump=False)

    bucket = ModelField(dump=False)
示例#5
0
class ParentRecord(RecordBase):
    """Parent record API."""

    # Configuration
    model_cls = None

    pid = PIDField('id', provider=DraftRecordIdProviderV2, delete=True)
示例#6
0
class Draft(Record):
    """Draft base API for metadata creation and manipulation."""

    # WHY: We want to force the model_cls to be specified by the user
    # No default one is given, only the base.
    model_cls = None

    pid = PIDField('id', provider=RecordIdProviderV2, delete=False)

    conceptpid = PIDField('conceptid',
                          provider=RecordIdProviderV2,
                          delete=False)

    expires_at = ModelField()

    fork_version_id = ModelField()
class LOMDraft(Draft):
    """For representing entries from the 'lom_drafts_metadata'-SQL-table."""

    model_cls = models.LOMDraftMetadata
    parent_record_cls = LOMParent
    versions_model_cls = models.LOMVersionsState

    pid = PIDField(
        key="id",
        provider=LOMDraftRecordIdProvider,
        resolver_cls=LOMResolver,
        context_cls=LOMPIDFieldContext,
        # flag for deleting pid from database on post-record-deletion,
        # delete pid via LOMRecordService instead
        delete=False,
    )
    files = FilesField(
        file_cls=LOMFileDraft,
        delete=False,
        store=False,
    )
    access = RecordAccessField()
    bucket_id = ModelField(dump=False)
    bucket = ModelField(dump=False)
    index = IndexField("lomrecords-drafts-draft-v1.0.0",
                       search_alias="lomrecords")
    is_published = PIDStatusCheckField(status=PIDStatus.REGISTERED, dump=True)
    resource_type = DictField()
class Vocabulary(Record):
    """A generic vocabulary record."""

    # Configuration
    model_cls = VocabularyMetadata

    # System fields
    # TODO: Can schema name be changed (remove localhost)
    schema = ConstantField(
        "$schema",
        "https://localhost/schemas/vocabularies/vocabulary-v1.0.0.json",
    )

    index = IndexField(
        "vocabularies-vocabulary-v1.0.0", search_alias="vocabularies"
    )

    #: Disable the metadata system field.
    metadata = None

    type = RelatedModelField(VocabularyType, required=True)

    pid = PIDField(
        'id',
        provider=VocabularyIdProvider,
        context_cls=VocabularyPIDFieldContext,
        create=False,
    )
示例#9
0
class Vocabulary(RecordBase):
    """Example record API."""

    # Configuration
    model_cls = VocabularyMetadata

    dumper = ElasticsearchDumper(
        extensions=[VocabularyTypeElasticsearchDumperExt()]
    )

    # System fields
    schema = ConstantField(
        "$schema",
        "https://localhost/schemas/vocabularies/vocabulary-v1.0.0.json",
    )

    index = IndexField(
        "vocabularies-vocabulary-v1.0.0", search_alias="vocabularies"
    )

    # TODO: This should be changed to use something else than the recidv2
    pid = PIDField("id", provider=RecordIdProviderV2)

    vocabulary_type_id = ModelField()

    vocabulary_type = VocabularyTypeField(dump=False)
示例#10
0
class Record(RecordBase):
    """Record base API.

    Note: This class is meant to work along a with a draft class.
    """

    # Configuration
    model_cls = None

    pid = PIDField('id', provider=RecordIdProviderV2)

    conceptpid = PIDField('conceptid', provider=RecordIdProviderV2)

    is_published = PIDStatusCheckField(status=PIDStatus.REGISTERED)

    @classmethod
    def create_or_update_from(cls, draft):
        """Create of update the record based on the draft content."""
        try:
            # New version
            record = cls.get_record(draft.id)
        except NoResultFound:
            # New revision
            record = cls.create({},
                                id_=draft.id,
                                pid=draft.pid,
                                conceptpid=draft.conceptpid)

        # NOTE: Merge pid/conceptpid into the current db session if not already
        # in the session.
        cls.pid.session_merge(record)
        cls.conceptpid.session_merge(record)

        # Overwrite data
        # FIXME: Data validation should be done one step up self.schema access
        # TODO: Does this overwrite the pids/conceptpids?
        record.update(**draft)

        return record

    def register(self):
        """Register the persistent identifiers associated with teh record."""
        if not self.conceptpid.is_registered():
            self.conceptpid.register()
        self.pid.register()
示例#11
0
class Record(RecordBase):
    """Example record API."""

    # Configuration
    model_cls = RecordMetadata

    # Model fields
    expires_at = ModelField()

    # System fields
    schema = ConstantField(
        '$schema', 'http://localhost/schemas/records/record-v1.0.0.json')

    index = IndexField('records-record-v1.0.0', search_alias='records')

    pid = PIDField('id', provider=RecordIdProviderV2)

    conceptpid = PIDField('conceptid', provider=RecordIdProviderV2)

    is_published = PIDStatusCheckField(status=PIDStatus.REGISTERED)
示例#12
0
 def create_record_class(self):
     """Create record class."""
     record_class_attributes = {
         "model_cls": self.model_cls,
         "schema": ConstantField("$schema", self.schema_path),
         "index": IndexField(self.index_name),
         "pid": PIDField("id", provider=RecordIdProviderV2),
         "dumper": self.record_dumper or ElasticsearchDumper(),
     }
     self.record_cls = type(self.record_type_name, (Record, ),
                            record_class_attributes)
示例#13
0
class Marc21Record(Record):
    """Define API for Marc21 creation and manipulation."""

    model_cls = models.RecordMetadata

    index = IndexField("marc21records-marc21-marc21-v1.0.0",
                       search_alias="marc21records-marc21")

    pid = PIDField(
        key="id",
        provider=MarcRecordProvider,
        delete=False,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
    )

    conceptpid = PIDField(
        key="conceptid",
        provider=MarcRecordProvider,
        delete=False,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
    )
示例#14
0
class Marc21Draft(Draft):
    """Marc21 draft API."""

    model_cls = models.DraftMetadata

    index = IndexField("marc21records-drafts-marc21-v1.0.0",
                       search_alias="marc21records-marc21")

    pid = PIDField(
        key="id",
        provider=MarcDraftProvider,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
        delete=False,
    )

    conceptpid = PIDField(
        key="conceptid",
        provider=MarcDraftProvider,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
        delete=False,
    )
示例#15
0
class Marc21Parent(BaseParentRecord):
    """Parent record."""

    versions_model_cls = VersionsState
    model_cls = ParentMetadata

    schema = ConstantField("$schema", "local://marc21/parent-v1.0.0.json")

    pid = PIDField(
        key="id",
        provider=MarcDraftProvider,
        context_cls=MarcPIDFieldContext,
        resolver_cls=MarcResolver,
        delete=False,
    )
示例#16
0
class LOMParent(ParentRecord):
    """For representing entries from the 'lom_parents_metadata'-SQL-table."""

    model_cls = models.LOMParentMetadata

    pid = PIDField(
        key="id",
        provider=LOMDraftRecordIdProvider,
        resolver_cls=LOMResolver,
        context_cls=LOMPIDFieldContext,
        # flag for deleting pid from database on post-record-deletion,
        # pid is deleted via LOMRecordService instead
        delete=False,
    )
    access = ParentRecordAccessField()
示例#17
0
class Record(RecordBase):
    """Example bibliographic record API."""

    model_cls = models.RecordMetadata
    schema = ConstantField('$schema', 'local://records/record-v1.0.0.json')
    index = IndexField('records-record-v1.0.0', search_alias='records')
    pid = PIDField('id', provider=RecordIdProviderV2)

    # Definitions of relationships from a bibliographic record to the
    # generic vocabularies.
    relations = RelationsField(languages=PIDListRelation(
        'metadata.languages',
        keys=['id', 'title'],
        pid_field=Vocabulary.pid.with_type_ctx('languages')), )

    dumper = ElasticsearchDumper(extensions=[
        RelationDumperExt('relations'),
    ])
class Community(Record):
    """Community API."""

    pid = PIDField('id', provider=CommunitiesIdProvider, create=False)
    schema = ConstantField('$schema',
                           'local://communities/communities-v1.0.0.json')

    model_cls = models.CommunityMetadata

    index = IndexField("communities-communities-v1.0.0",
                       search_alias="communities")

    access = CommunityAccessField()

    bucket_id = ModelField(dump=False)
    bucket = ModelField(dump=False)
    files = FilesField(
        store=False,
        file_cls=CommunityFile,
        # Don't delete, we'll manage in the service
        delete=False,
    )
示例#19
0
class Record(BaseRecord):
    """API for projects resources."""

    # Configuration
    model_cls = models.RecordMetadata

    # System fields
    index = IndexField('projects-project-v1.0.0', search_alias='projects')

    # The `pid_type` must not be filled as argument in this constructor.
    # Instead it is guessed from RecordIdProvider.
    pid = PIDField('id', provider=RecordIdProvider)

    # PID type retrieved from provider
    pid_type = RecordIdProvider.pid_type

    dumper = ElasticsearchDumper(extensions=[ElasticsearchDumperObjectsExt()])

    _extensions = [ValidationExtension()]

    @cached_property
    def schema(self):
        """Return the schema."""
        schema_key = 'projects' if not has_custom_resource(
            'projects') else f'{current_organisation["code"]}/projects'

        schema = f'https://sonar.ch/schemas/{schema_key}/project-v1.0.0.json'

        return ConstantField('$schema', schema)

    def __repr__(self):
        """String representation of object.

        :returns: A string representing the object.
        """
        return self['metadata']['name']
示例#20
0
 class Record(RecordBase):
     model_cls = RecordMetadata
     pid = PIDField()
示例#21
0
 class Record(RecordBase):
     model_cls = RecordMetadata
     pid = PIDField('pid.id', provider=RecordIdProviderV2)
示例#22
0
class Draft(Record):
    """Draft base API for metadata creation and manipulation."""

    #: Class attribute to make it easy to check if record is a draft or not.
    is_draft = True

    #
    # Configuration to be set by a subclass
    #

    #: The record's SQLAlchemy model class. Must be set by the subclass.
    model_cls = None
    #: The parent state's SQLAlchemy model class. Must be set by the subclass.
    versions_model_cls = None
    #: The parent record's API class. Must be set by the subclass.
    parent_record_cls = None

    #
    # System fields
    #

    #: The internal persistent identifier. Records and drafts share UUID.
    pid = PIDField('id', provider=DraftRecordIdProviderV2, delete=False)

    #: The parent record - the draft is responsible for creating the parent.
    parent = ParentField(ParentRecord,
                         create=True,
                         soft_delete=False,
                         hard_delete=True)

    #: Version relationship
    versions = VersionsField(create=True, set_next=True)

    #: The expiry date of the draft.
    expires_at = ModelField()

    #: Revision id of record from which this draft was created.
    fork_version_id = ModelField()

    @classmethod
    def new_version(cls, record):
        """Create a draft for a new version of a record.

        The caller is responsible for:
        1) checking if a draft for a new version already exists
        2) moving the record data into the draft data.
        """
        return cls.create(
            {},
            # We create a new id, because this is for a new version.
            id=uuid.uuid4(),
            # Links the draft with the same parent (i.e. a new version).
            parent=record.parent,
            versions=record.versions,
            # New drafts without a record (i.e. unpublished drafts) must set
            # the fork version id to None.
            fork_version_id=None,
        )

    @classmethod
    def edit(cls, record):
        """Create a draft for editing an existing version of a record."""
        try:
            # We soft-delete a draft once it has been published, in order to
            # keep the version_id counter around for optimistic concurrency
            # control (both for ES indexing and for REST API clients)
            draft = cls.get_record(record.id, with_deleted=True)
            if draft.is_deleted:
                draft.undelete()
                # Below line is needed to dump PID back into the draft.
                draft.pid = record.pid
                # Ensure record is link with the parent
                draft.parent = record.parent
                draft.versions = record.versions
                # Ensure we record the revision id we forked from
                draft.fork_version_id = record.revision_id
                # Note, other values like bucket_id values was kept in the
                # soft-deleted record, so we are not setting them again here.
        except NoResultFound:
            # If a draft was ever force deleted, then we will create the draft.
            # This is a very exceptional case as normally, when we edit a
            # record then the soft-deleted draft exists and we are in above
            # case.
            draft = cls.create(
                {},
                # A draft to edit a record must share the id and uuid.
                id_=record.id,
                pid=record.pid,
                # Link it with the same parent record
                parent=record.parent,
                versions=record.versions,
                # Record which record version we forked from.
                fork_version_id=record.revision_id,
            )
        return draft
示例#23
0
class Record(RecordBase):
    """Record API."""

    #: Class attribute to make it easy to check if record is a draft or not.
    is_draft = False

    #
    # Configuration to be set by a subclass
    #

    #: The record's SQLAlchemy model class. Must be set by the subclass.
    model_cls = None
    #: The parent state's SQLAlchemy model class. Must be set by the subclass.
    versions_model_cls = None
    #: The parent record's API class. Must be set by the subclass.
    parent_record_cls = None

    #
    # System fields
    #
    #: The internal persistent identifier. Records and drafts share UUID.
    pid = PIDField('id', provider=DraftRecordIdProviderV2, delete=True)

    #: System field to check if a record has been published.
    is_published = PIDStatusCheckField(status=PIDStatus.REGISTERED)

    #: The parent record - the draft is responsible for creating the parent.
    parent = ParentField(ParentRecord,
                         create=False,
                         soft_delete=False,
                         hard_delete=False)

    #: Version relationship
    versions = VersionsField(create=True, set_latest=True)

    @classmethod
    def get_records_by_parent(cls, parent):
        """Get all sibling records for the specified parent record."""
        versions = cls.model_cls.query.filter_by(parent_id=parent.id).all()
        return [cls(rec_model.data, model=rec_model) for rec_model in versions]

    @classmethod
    def publish(cls, draft):
        """Publish a draft as a new record.

        If a record already exists, we simply get the record. If a draft has
        not yet been published, we create the record.

        The caller is responsible for registering the internal persistent
        identifiers (see ``register()``).
        """
        if draft.is_published:
            record = cls.get_record(draft.id)
        else:
            record = cls.create(
                {},
                # A draft and record share UUID, so we reuse the draft's id/pid
                id_=draft.id,
                pid=draft.pid,
                # Link the record with the parent record and set the versioning
                # relationship.
                parent=draft.parent,
                versions=draft.versions,
            )
            # Merge the PIDs into the current db session if not already in the
            # session.
            cls.pid.session_merge(record)
            cls.parent_record_cls.pid.session_merge(record.parent)
        return record

    def register(self):
        """Register the internal persistent identifiers."""
        if not self.parent.pid.is_registered():
            self.parent.pid.register()
            self.parent.commit()
        self.pid.register()