def create_sequence_relation(previous_rec, next_rec, relation_type): """Create sequence relations.""" rr = RecordRelationsSequence() click.echo("Creating relations: {0} - {1}".format(previous_rec["pid"], next_rec["pid"])) rel = Relation(relation_type) if not rel.relation_exists(previous_rec.pid, next_rec.pid): rr.add( previous_rec=previous_rec, next_rec=next_rec, relation_type=relation_type, )
def delete(payload): try: relation_type = payload.pop("relation_type") except KeyError as key: return abort(400, "The `{}` is a required field".format(key)) rt = Relation.get_relation_by_name(relation_type) if rt in current_app.config["PARENT_CHILD_RELATION_TYPES"]: modified, first, second = self._delete_parent_child_relation( record, rt, payload) elif rt in current_app.config["SIBLINGS_RELATION_TYPES"]: modified, first, second = self._delete_sibling_relation( record, rt, payload) else: raise RecordRelationsError("Invalid relation type `{}`".format( rt.name)) db.session.commit() records_to_index.append(first) records_to_index.append(second) # if the record is the modified, return the modified version if (modified.pid == record.pid and modified._pid_type == record._pid_type): return modified return record
def create_sequence_relation(previous_rec, next_rec, relation_type): """Create sequence relations.""" relations_logger = logging.getLogger("relations_logger") rr = RecordRelationsSequence() click.echo("Creating relations: {0} - {1}".format(previous_rec["pid"], next_rec["pid"])) rel = Relation(relation_type) if not rel.relation_exists(previous_rec.pid, next_rec.pid): rr.add( previous_rec=previous_rec, next_rec=next_rec, relation_type=relation_type, ) relations_logger.info("Created: {0} - {1}".format( previous_rec["pid"], next_rec["pid"]), extra=dict(legacy_id=None, status="SUCCESS", new_pid=previous_rec["pid"]))
def find_related_record(relation): """Find related document record or first volume of series.""" document_class = current_app_ils.document_record_cls series_class = current_app_ils.series_record_cls document_legacy_pid_type = \ current_app.config["CDS_ILS_RECORD_LEGACY_PID_TYPE"] try: related_sibling = get_record_by_legacy_recid( document_class, document_legacy_pid_type, relation["related_recid"], ) return related_sibling except PIDDoesNotExistError as e: # If there is no document it means it can be related to a # multipart. If this is the case we relate it to the first # document of the multipart series_legacy_pid = current_app.config[ "CDS_ILS_SERIES_LEGACY_PID_TYPE"] try: # Search for the series with related legacy_recid from # the document related_series = get_record_by_legacy_recid( series_class, series_legacy_pid, relation["related_recid"], ) multipart_relation = Relation.get_relation_by_name( "multipart_monograph") pcr = ParentChildRelation(multipart_relation) volumes = pcr.get_children_of(related_series.pid) if len(volumes) > 0: # Selects the first volume as the one to be related # with the document related_sibling = document_class.get_record_by_pid( volumes[0].pid_value) return related_sibling else: click.secho( "No document related volume found with legacy_recid: {}". format(relation["related_recid"]), fg="red", ) raise RelationMigrationError( f"No related volume record found " f"with legacy_recid {relation['related_recid']}") except PIDDoesNotExistError as e: click.secho( "No record found with legacy_recid: {}".format( relation["related_recid"]), fg="red", ) raise RelationMigrationError( f"No related record found " f"with legacy_recid {relation['related_recid']}")
def migrate_document_siblings_relation(raise_exceptions=False): """Create siblings relations.""" document_class = current_app_ils.document_record_cls search = search_documents_with_siblings_relations() results = search.params(scroll='4h').scan() for document in results: current_document_record = document_class.get_record_by_pid( document.pid) relations = current_document_record["_migration"]["related"] for relation in relations: try: # clean the found sibling related_sibling = None extra_metadata = {} related_sibling = find_related_record(relation) if not related_sibling: continue # validate relation type relation_type = Relation.get_relation_by_name( relation["relation_type"]) if relation_type.name == EDITION_RELATION.name: validate_edition_field(current_document_record, related_sibling, relation) if relation_type.name == OTHER_RELATION.name: extra_metadata.update( {"note": relation["relation_description"]}) # create relation if related_sibling and related_sibling["pid"] != document.pid: create_sibling_relation( current_document_record, related_sibling, relation_type, **extra_metadata, ) db.session.commit() except Exception as exc: click.secho(str(exc), fg="red") handler = relation_exception_handlers.get(exc.__class__) if handler: handler( exc, new_pid=current_document_record["pid"], legacy_id=current_document_record.get("legacy_recid")) else: if raise_exceptions: raise exc current_document_record["_migration"]["has_related"] = False current_document_record.commit() db.session.commit()
def delete_record(self): """Deletes the eitems of the record.""" document_indexer = current_app_ils.document_indexer series_class = current_app_ils.series_record_cls document_class = current_app_ils.document_record_cls self._validate_provider() exact_match, partial_matches = self._match_document() partial_matches = self.find_partial_matches(partial_matches, exact_match) if exact_match: matched_document = document_class.get_record_by_pid(exact_match) eitem = self.delete_eitem(matched_document) db.session.commit() current_search.flush_and_refresh(index="*") document_has_only_serial_relations = \ len(matched_document.relations.keys()) \ and 'serial' in matched_document.relations.keys() if not matched_document.has_references() \ or document_has_only_serial_relations: # remove serial relations rr = RecordRelationsParentChild() serial_relations = matched_document.relations.get('serial', []) relation_type = Relation.get_relation_by_name("serial") for relation in serial_relations: serial = series_class.get_record_by_pid( relation["pid_value"]) rr.remove(serial, matched_document, relation_type) pid = matched_document.pid # will fail if any relations / references present matched_document.delete() # mark all PIDs as DELETED all_pids = PersistentIdentifier.query.filter( PersistentIdentifier.object_type == pid.object_type, PersistentIdentifier.object_uuid == pid.object_uuid, ).all() for rec_pid in all_pids: if not rec_pid.is_deleted(): rec_pid.delete() db.session.commit() document_indexer.delete(matched_document) return self.report(document=matched_document, action="delete", partial_matches=partial_matches, eitem=eitem) return self.report(partial_matches=partial_matches)
def migrate_document_siblings_relation(): """Create siblings relations.""" document_class = current_app_ils.document_record_cls series_class = current_app_ils.series_record_cls search = search_documents_with_siblings_relations() results = search.scan() extra_metadata = {} for document in results: relations = document["_migration"]["related"] for relation in relations: related_sibling = None try: related_sibling = get_record_by_legacy_recid( document_class, relation["related_recid"]) except PIDDoesNotExistError as e: pass # try to find sibling in series if related_sibling is None: try: related_sibling = get_record_by_legacy_recid( series_class, relation["related_recid"]) except PIDDoesNotExistError as e: continue # validate relation type relation_type = Relation.get_relation_by_name( relation["relation_type"]) if relation_type == OTHER_RELATION.name: extra_metadata.update( {"note": relation["relation_description"]}) # create relation if related_sibling and related_sibling["pid"] != document.pid: current_document_record = document_class.get_record_by_pid( document.pid) try: create_sibling_relation( current_document_record, related_sibling, relation_type, **extra_metadata, ) db.session.commit() except RecordRelationsError as e: click.secho(e.description, fg="red") continue
def import_serial_relation(self, series_record, document_record, json_series): """Create serial relation type.""" try: rr = RecordRelationsParentChild() serial_relation = Relation.get_relation_by_name("serial") volume = json_series.get("volume", None) rr.add( series_record, document_record, relation_type=serial_relation, volume=volume, ) except RecordRelationsError as e: click.secho(str(e), fg="red")
def migrate_series_relations(): """Create relations for series.""" series_class = current_app_ils.series_record_cls search = search_series_with_relations() results = search.scan() for series in results: relations = series["_migration"]["related"] for relation in relations: related_series = get_record_by_legacy_recid( series_class, relation["related_recid"]) # validate relation type relation_type = Relation.get_relation_by_name( relation["relation_type"]) # create relation current_series_record = series_class.get_record_by_pid(series.pid) try: if relation_type in SIBLINGS_RELATION_TYPES: create_sibling_relation( current_series_record, related_series, relation_type, note=relation["relation_description"], ) elif relation_type in SEQUENCE_RELATION_TYPES: if relation["sequence_order"] == "previous": create_sequence_relation( current_series_record, related_series, relation_type, ) else: create_sequence_relation( related_series, current_series_record, relation_type, ) db.session.commit() except RecordRelationsError as e: click.secho(e.description, fg="red") continue
def create(payload): try: relation_type = payload.pop("relation_type") except KeyError as key: return abort(400, "The `{}` is a required field".format(key)) rt = Relation.get_relation_by_name(relation_type) if rt in PARENT_CHILD_RELATION_TYPES: modified, first, second = self._create_parent_child_relation( record, rt, payload ) elif rt in SIBLINGS_RELATION_TYPES: modified, first, second = self._create_sibling_relation( record, rt, payload ) elif rt in SEQUENCE_RELATION_TYPES: first, second = self._create_sequence_relation( record, rt, payload ) modified = second else: raise RecordRelationsError( "Invalid relation type `{}`".format(rt.name) ) db.session.commit() records_to_index.append(first) records_to_index.append(second) def is_modified(x, r): return x.pid == r.pid and x._pid_type == r._pid_type # NOTE: modified can be a record or a list of records, if one # matches our record return the modified one. _modified = modified if isinstance(modified, list) else [modified] for mod_record in _modified: if is_modified(mod_record, record): return mod_record return record
def test_max_one_multipart(testdata): doc = testdata["documents"][0] series1 = testdata["series"][0] rr = RecordRelationsParentChild() res = rr.add( series1, doc, Relation.get_relation_by_name("multipart_monograph"), ) assert res series2 = testdata["series"][1] with pytest.raises(RecordRelationsError) as error: rr = RecordRelationsParentChild() res = rr.add( series2, doc, "multipart_monograph", ) assert isinstance(error.value, RecordRelationsError)