def _create_sequence_relation(self, record, relation_type, payload): """Create a Sequence relation. Expected payload: { next_pid: <pid_value>, next_pid_type: <pid_type>, previous_pid: <pid_value>, previous_pid_type: <pid_type>, relation_type: "<Relation name>", } """ next_pid, next_pid_type, previous_pid, previous_pid_type, metadata = \ self._validate_sequence_creation_payload(record, payload) next_rec = IlsRecord.get_record_by_pid(next_pid, pid_type=next_pid_type) previous_rec = IlsRecord.get_record_by_pid(previous_pid, pid_type=previous_pid_type) relation_sequence = RecordRelationsSequence() mod_prev, mod_next = relation_sequence.add(previous_rec=previous_rec, next_rec=next_rec, relation_type=relation_type, **metadata) return [mod_prev, mod_next], previous_rec, next_rec
def _delete_sequence_relation(self, record, relation_type, payload): """Delete sequence relation. Expected payload: { next_pid_value: <pid_value>, next_pid_type: <pid_type>, previous_pid_value: <pid_value>, previous_pid_type: <pid_type>, relation_type: "<Relation name>", } """ next_pid_value, next_pid_type, prev_pid_value, prev_pid_type, metadata = self._validate_sequence_creation_payload( record, payload ) next_rec = IlsRecord.get_record_by_pid( next_pid_value, pid_type=next_pid_type ) previous_rec = IlsRecord.get_record_by_pid( prev_pid_value, pid_type=prev_pid_type ) relation_sequence = RecordRelationsSequence() relation_sequence.remove( previous_rec=previous_rec, next_rec=next_rec, relation_type=relation_type, ) return previous_rec, next_rec
def _create_sibling_relation(self, record, relation_type, payload): """Create a Siblings relation from current record to the given PID. Expected payload: { pid_value: <pid_value>, pid_type: <pid_type>, relation_type: "<relation name>", [note: "<note>"] } """ pid_value, pid_type, metadata = self._validate_siblings_creation_payload( payload ) if pid_value == record["pid"] and pid_type == record._pid_type: raise RecordRelationsError( "Cannot create a relation for PID `{}` with itself".format( pid_value ) ) second = IlsRecord.get_record_by_pid(pid_value, pid_type=pid_type) rr = RecordRelationsSiblings() modified_record = rr.add( first=record, second=second, relation_type=relation_type, **metadata ) return modified_record, record, second
def _build_sequence(self, rec_pid, relation_type): """Return the relation object with metadata.""" pid = rec_pid.pid_value pid_type = rec_pid.pid_type r = self._build_relations_object(pid, pid_type) # retrieve any extra `relations_metadata` from invenio_app_ils.records.api import IlsRecord seq_record = IlsRecord.get_record_by_pid(pid, pid_type=pid_type) metadata = RecordRelationsMetadata.get_metadata_for( self.record, relation_type, pid, pid_type ) r.update(metadata or {}) r["title"] = seq_record.get("title", "") edition = seq_record.get("edition") if edition: r["edition"] = edition languages = seq_record.get("languages") if languages: r["languages"] = languages r["relation_type"] = relation_type return r
def _build_sibling(self, related_pid, relation_type): """Return the relation object with metadata.""" pid = related_pid.pid_value pid_type = related_pid.pid_type r = self._build_relations_object(pid, pid_type) # fetch the sibling to retrieve any extra `relations_metadata` from invenio_app_ils.records.api import IlsRecord sibling = IlsRecord.get_record_by_pid(pid, pid_type=pid_type) # get `relations_metadata` field data from the sibling referring to # this record metadata = RecordRelationsMetadata.get_metadata_for( self.record, relation_type, pid, pid_type ) # merge with any `relations_metadata` r.update(metadata or {}) # add also title, language and edition of the sibling r["title"] = sibling.get("title", "") r["document_type"] = sibling.get("document_type", "") r["mode_of_issuance"] = sibling.get("mode_of_issuance", "") languages = sibling.get("languages") if languages: r["languages"] = languages edition = sibling.get("edition", "") if edition: r["edition"] = edition r["relation_type"] = relation_type return r
def _get_record(self, record, pid, pid_type): """Return record if same PID or fetch the record.""" if record.pid == pid and record._pid_type == pid_type: rec = record else: rec = IlsRecord.get_record_by_pid(pid, pid_type=pid_type) return rec
def test_get_record_by_pid_and_pid_type(testdata, pid, pid_type, expected_cls): """Test get_record_by_pid with pid_type.""" record = IlsRecord.get_record_by_pid(pid, pid_type=pid_type) assert isinstance(record, expected_cls) assert record["pid"] == pid assert record._pid_type == pid_type
def _build_child(self, parent_pid, child_pid, relation_type): """Return the relation object with metadata of the parent.""" # this is a child, relations_metadata are stored in the parent pid = parent_pid.pid_value pid_type = parent_pid.pid_type r = self._build_relations_object(pid, pid_type) # fetch the parent to retrieve any extra `relations_metadata` from invenio_app_ils.records.api import IlsRecord parent = IlsRecord.get_record_by_pid(pid, pid_type=pid_type) # get `relations_metadata` field data from the parent referring to this # record metadata = RecordRelationsMetadata.get_metadata_for( parent, relation_type, child_pid.pid_value, child_pid.pid_type) # `relations_metadata` has pid and pid_type of the child # replace it with the parent ones if metadata and "pid" in metadata and "pid_type" in metadata: metadata["pid"] = pid metadata["pid_type"] = pid_type # merge with any `relations_metadata` r.update(metadata or {}) # add also the title of the parent r["title"] = parent.get("title", "") r["relation_type"] = relation_type return r
def index_related_records(indexed, related): """Index related records.""" referenced = [] for pid, pid_type in related: referenced.append( dict(pid_type=pid_type, record=IlsRecord.get_record_by_pid(pid, pid_type=pid_type))) indexer = ReferencedRecordsIndexer() indexer.index(indexed, referenced)
def test_get_record_by_pid_and_pid_type(): """Test get_record_by_pid with pid_type.""" tests = [("docid-1", "docid", Document), ("serid-1", "serid", Series)] for pid, pid_type, expected_cls in tests: record = IlsRecord.get_record_by_pid(pid, pid_type=pid_type) assert isinstance(record, expected_cls) assert record["pid"] == pid assert record._pid_type == pid_type
def index_related_record(pid, rec_type, related_pid, related_pid_type): """Index document to re-compute circulation information.""" related = IlsRecord.get_record_by_pid(related_pid, pid_type=related_pid_type) log_func = partial(_log, origin_rec_type=rec_type, origin_recid=pid, dest_rec_type=related.__class__.__name__) log_func(msg=MSG_ORIGIN) _index_record_by_pid(related.__class__, related["pid"], log_func)
def index_related_records_after_record_changed(pid, pid_type, rec_type): """Index related records after a document/series has been indexed.""" record = IlsRecord.get_record_by_pid(pid, pid_type=pid_type) relations = record.relations.get() eta = datetime.utcnow() + current_app.config["ILS_INDEXER_TASK_DELAY"] for relation_type, related_records in relations.items(): for obj in related_records: index_related_record.apply_async( (pid, rec_type, obj["pid"], obj["pid_type"]), eta=eta, )
def get_related_records(document_pid): """Get referenced records via relations.""" referenced = [] doc_record_cls = current_app_ils.document_record_cls record = doc_record_cls.get_record_by_pid(document_pid) relations = record.relations for relation_type, related_records in relations.items(): for obj in related_records: rec = IlsRecord.get_record_by_pid(obj["pid_value"], pid_type=obj["pid_type"]) referenced.append(dict(pid_type=obj["pid_type"], record=rec)) return referenced
def get_related_records(series_pid): """Get referenced records via relations.""" referenced = [] series_record_cls = current_app_ils.series_record_cls record = series_record_cls.get_record_by_pid(series_pid) relations = record.relations.get() for relation_type, related_records in relations.items(): for obj in related_records: rec = IlsRecord.get_record_by_pid(obj["pid"], pid_type=obj["pid_type"]) referenced.append(dict(pid_type=obj["pid_type"], record=rec)) return referenced
def _build_relation_obj(self, related_pid, relation_type): """Return the relation object with metadata.""" pid_value = related_pid.pid_value pid_type = related_pid.pid_type r = self.build_relations_object(pid_value, pid_type, relation_type) # fetch the sequence to retrieve useful metadata (title, ...) from invenio_app_ils.records.api import IlsRecord seq_rec = IlsRecord.get_record_by_pid(pid_value, pid_type=pid_type) # copy relevant fields from the sequence record into the relation relevant_fields = self.get_relevant_fields_from(seq_rec) r.update(relevant_fields) return r
def _extend_related_records(self, record, size): """Extend record's related_record data with more fields.""" count = { relation.name: 0 for relation in current_app.config["PIDRELATIONS_RELATION_TYPES"] } related_records = [] for obj in record["related_records"]: count[obj["relation_type"]] += 1 if count[obj["relation_type"]] > size: continue related = IlsRecord.get_record_by_pid(obj["pid"], pid_type=obj["pid_type"]) obj["title"] = related.get("title", "") obj["edition"] = related.get("edition", "") obj["language"] = related.get("language", "") related_records.append(obj) record["related_records"] = related_records record["related_records_count"] = count
def _build_relation_obj(self, child, parent_pid, relation_type): """Return the relation object with any extra metadata.""" pid_value = parent_pid.pid_value pid_type = parent_pid.pid_type r = self.build_relations_object(pid_value, pid_type, relation_type) metadata = RelationsExtraMetadata.get_extra_metadata_from( child, relation_type, parent_pid.pid_value, parent_pid.pid_type) # copy the extra metadata into the relation, if any r.update(metadata) # fetch the parent to retrieve useful metadata (title, ...) from invenio_app_ils.records.api import IlsRecord parent = IlsRecord.get_record_by_pid(pid_value, pid_type=pid_type) # copy relevant fields from parent into the relation relevant_fields = self.get_relevant_fields_from(parent) r.update(relevant_fields) return r
def post(self, record, **kwargs): """Update relations.""" for obj in request.get_json(): related = IlsRecord.get_record_by_pid(obj["pid"], pid_type=obj["pid_type"]) relation_type = RelatedRecords.get_relation_by_name( obj["relation_type"]) action = obj.get("action", "") if action == "add": record.related.add(related, relation_type) elif action == "remove": record.related.remove(related, relation_type) else: raise RelatedRecordError(("Failed to update related records - " "invalid action: {}").format(action)) record.commit() db.session.commit() size = self.get_size(record) self._extend_related_records(record, size) return self.make_response(record["pid"], record, 200)
def _build_relation_obj(self, sibling_pid, relation_type): """Return the relation object with metadata.""" pid_value = sibling_pid.pid_value pid_type = sibling_pid.pid_type r = self.build_relations_object(pid_value, pid_type, relation_type) # fetch the sibling to retrieve useful metadata (title, ...) and also # any optional extra metadata for this relation from invenio_app_ils.records.api import IlsRecord sibling = IlsRecord.get_record_by_pid(pid_value, pid_type=pid_type) metadata = self._get_extra_metadata(self.record, sibling, relation_type) # copy the extra metadata into the relation, if any r.update(metadata) # copy relevant fields from sibling into the relation relevant_fields = self.get_relevant_fields_from(sibling) r.update(relevant_fields) return r
def _delete_sibling_relation(self, record, relation_type, payload): """Delete a Siblings relation from current record to the given PID. Expected payload: { pid_value: <pid_value>, pid_type: <pid_type>, relation_type: "<relation name>" } """ pid_value, pid_type, _ = self._validate_siblings_creation_payload( payload) second = IlsRecord.get_record_by_pid(pid_value, pid_type=pid_type) rr = RecordRelationsSiblings() modified_record, _ = rr.remove(first=record, second=second, relation_type=relation_type) return modified_record, record, second