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)
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)
class Draft(DraftBase): """Example record API.""" # Configuration model_cls = DraftMetadata versions_model_cls = ParentState parent_record_cls = ParentRecord # System fields schema = ConstantField( '$schema', 'http://localhost/schemas/records/record-v1.0.0.json') index = IndexField('draftsresources-drafts-draft-v1.0.0', search_alias='draftsresources-drafts') files = FilesField( store=False, file_cls=FileDraft, # Don't delete, we'll manage in the service delete=False, ) bucket_id = ModelField(dump=False) bucket = ModelField(dump=False)
class CommonFieldsMixin: """Common system fields between records and drafts.""" versions_model_cls = models.RDMVersionsState parent_record_cls = RDMParent schema = ConstantField( '$schema', 'local://records/record-v2.0.0.json') dumper = ElasticsearchDumper( extensions=[ EDTFDumperExt('metadata.publication_date'), EDTFListDumperExt("metadata.dates", "date"), RelationDumperExt('relations'), ] ) relations = RelationsField( languages=PIDListRelation( 'metadata.languages', attrs=['id', 'title'], pid_field=Vocabulary.pid.with_type_ctx('languages') ), ) bucket_id = ModelField(dump=False) bucket = ModelField(dump=False) access = RecordAccessField() is_published = PIDStatusCheckField(status=PIDStatus.REGISTERED, dump=True) pids = DictField("pids")
class BibliographicRecord(Record): """Bibliographic Record API.""" model_cls = models.RecordMetadata index = IndexField('rdmrecords-records-record-v1.0.0', search_alias='rdmrecords-records') dumper = ElasticsearchDumper(extensions=[ EDTFDumperExt('metadata.publication_date'), RelationDumperExt('relations'), ]) relations = RelationsField(languages=PIDListRelation( 'metadata.languages', attrs=['metadata'], pid_field=Language.pid), ) files = FilesField( store=False, file_cls=RecordFile, # Don't create create=False, # Don't delete, we'll manage in the service delete=False, ) bucket_id = ModelField(dump=False) bucket = ModelField(dump=False)
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)
class CommonFieldsMixin: """Common system fields between records and drafts.""" versions_model_cls = models.RDMVersionsState parent_record_cls = RDMParent schema = ConstantField( '$schema', 'http://localhost/schemas/records/record-v2.0.0.json') dumper = ElasticsearchDumper( extensions=[ EDTFDumperExt('metadata.publication_date'), EDTFListDumperExt("metadata.dates", "date"), RelationDumperExt('relations'), ] ) relations = RelationsField( languages=PIDListRelation( 'metadata.languages', attrs=['id', 'title'], pid_field=Vocabulary.pid.with_type_ctx('languages') ), ) bucket_id = ModelField(dump=False) bucket = ModelField(dump=False) access = RecordAccessField() # We redefine the property as we extend the `PIDStatusCheckField` to dump # the property in ES in order to be available for aggregation is_published = IsPublishedField(status=PIDStatus.REGISTERED)
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 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)
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)
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 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, )
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)
class CommonFieldsMixin: """Common system fields between records and drafts.""" versions_model_cls = models.RDMVersionsState parent_record_cls = RDMParent schema = ConstantField( '$schema', 'local://records/record-v5.0.0.json') dumper = ElasticsearchDumper( extensions=[ EDTFDumperExt('metadata.publication_date'), EDTFListDumperExt("metadata.dates", "date"), RelationDumperExt('relations'), ] ) relations = RelationsField( creator_affiliations=PIDNestedListRelation( 'metadata.creators', relation_field='affiliations', keys=['name'], pid_field=Affiliation.pid, cache_key='affiliations', ), contributor_affiliations=PIDNestedListRelation( 'metadata.contributors', relation_field='affiliations', keys=['name'], pid_field=Affiliation.pid, cache_key='affiliations', ), languages=PIDListRelation( 'metadata.languages', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('languages'), cache_key='languages', ), resource_type=PIDRelation( 'metadata.resource_type', keys=['title', 'props.type', 'props.subtype'], pid_field=Vocabulary.pid.with_type_ctx('resourcetypes'), cache_key='resource_type', value_check=dict(tags=['depositable']), ), subjects=PIDListRelation( 'metadata.subjects', keys=['subject', 'scheme'], pid_field=Subject.pid, cache_key='subjects', ), licenses=PIDListRelation( 'metadata.rights', keys=['title', 'description', 'icon', 'props.url', 'props.scheme'], pid_field=Vocabulary.pid.with_type_ctx('licenses'), cache_key='licenses', ), related_identifiers=PIDListRelation( 'metadata.related_identifiers', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('resourcetypes'), cache_key='resource_type', relation_field='resource_type', value_check=dict(tags=['linkable']), ), title_types=PIDListRelation( 'metadata.additional_titles', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('titletypes'), cache_key='title_type', relation_field='type', ), title_languages=PIDListRelation( 'metadata.additional_titles', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('languages'), cache_key='languages', relation_field='lang', ), creators_role=PIDListRelation( 'metadata.creators', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('creatorsroles'), cache_key='role', relation_field='role' ), contributors_role=PIDListRelation( 'metadata.contributors', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('contributorsroles'), cache_key='role', relation_field='role' ), description_type=PIDListRelation( 'metadata.additional_descriptions', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('descriptiontypes'), cache_key='description_type', relation_field='type', ), description_languages=PIDListRelation( 'metadata.additional_descriptions', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('languages'), cache_key='languages', relation_field='lang', ), date_types=PIDListRelation( 'metadata.dates', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('datetypes'), cache_key='date_types', relation_field='type', ), relation_types=PIDListRelation( 'metadata.related_identifiers', keys=['title'], pid_field=Vocabulary.pid.with_type_ctx('relationtypes'), cache_key='relation_types', relation_field='relation_type', ), ) bucket_id = ModelField(dump=False) bucket = ModelField(dump=False) access = RecordAccessField() is_published = PIDStatusCheckField(status=PIDStatus.REGISTERED, dump=True) pids = DictField("pids")
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
class Record(RecordBase): files = FilesField(store=True, file_cls=FileRecord) bucket_id = ModelField() bucket = ModelField(dump=False)
class RecordWithFiles(Record): """Example record with file API.""" files = FilesField(store=False, file_cls=FileRecord) bucket_id = ModelField() bucket = ModelField(dump=False)
class Record(BaseMarc21Record): """Test record class.""" files = FilesField(store=True, file_cls=RecordFile) bucket_id = ModelField() bucket = ModelField(dump=False)
class Record(RecordBase): """Mock record class.""" model_cls = RecordMetadataWithPID pid = ModelPIDField() pid_status = ModelField()
class Record1(Record, SystemFieldsMixin): model_cls = Record1Metadata dumper = ElasticsearchDumper() # Don't do this at home (two system fields on the same model field): expires_at = ModelField() expires = ModelField('expires_at', dump=False)