1 package eu.ehri.project.importers.links; 2 3 import com.tinkerpop.frames.FramedGraph; 4 import eu.ehri.project.core.GraphManager; 5 import eu.ehri.project.core.GraphManagerFactory; 6 import eu.ehri.project.definitions.EventTypes; 7 import eu.ehri.project.definitions.Ontology; 8 import eu.ehri.project.exceptions.DeserializationError; 9 import eu.ehri.project.exceptions.ItemNotFound; 10 import eu.ehri.project.exceptions.ValidationError; 11 import eu.ehri.project.importers.ImportLog; 12 import eu.ehri.project.models.EntityClass; 13 import eu.ehri.project.models.Link; 14 import eu.ehri.project.models.base.Accessor; 15 import eu.ehri.project.models.base.Actioner; 16 import eu.ehri.project.models.base.Linkable; 17 import eu.ehri.project.persistence.ActionManager; 18 import eu.ehri.project.persistence.Bundle; 19 import eu.ehri.project.persistence.BundleManager; 20 import eu.ehri.project.utils.Table; 21 import org.slf4j.Logger; 22 import org.slf4j.LoggerFactory; 23 24 import java.util.List; 25 import java.util.Optional; 26 27 public class LinkImporter { 28 29 private static final Logger logger = LoggerFactory.getLogger(LinkImporter.class); 30 31 private final FramedGraph<?> framedGraph; 32 private final GraphManager manager; 33 private final Actioner actioner; 34 private final ActionManager actionManager; 35 private final BundleManager bundleManager; 36 private final boolean tolerant; 37 38 public LinkImporter(FramedGraph<?> framedGraph, Actioner actioner, boolean tolerant) { 39 this.framedGraph = framedGraph; 40 this.manager = GraphManagerFactory.getInstance(framedGraph); 41 this.actioner = actioner; 42 this.actionManager = new ActionManager(framedGraph); 43 this.bundleManager = new BundleManager(framedGraph); 44 this.tolerant = tolerant; 45 } 46 47 public ImportLog importLinks(Table table, String logMessage) throws DeserializationError { 48 49 if (!table.rows().isEmpty() && table.rows().get(0).size() != 5) { 50 throw new DeserializationError("Input CSV must have 5 columns: " + 51 "source, target, type, field, description. Leave columns blank where " + 52 "not applicable."); 53 } 54 55 ImportLog log = new ImportLog(logMessage); 56 ActionManager.EventContext eventContext = actionManager.newEventContext( 57 actioner, 58 EventTypes.ingest, 59 Optional.ofNullable(logMessage)); 60 61 for (int i = 0; i < table.rows().size(); i++) { 62 List<String> row = table.rows().get(i); 63 String from = row.get(0); 64 String to = row.get(1); 65 String type = row.get(2); 66 String field = row.get(3); 67 String desc = row.get(4); 68 Bundle.Builder builder = Bundle.Builder.withClass(EntityClass.LINK) 69 .addDataValue(Ontology.LINK_HAS_TYPE, type); 70 if (!field.trim().isEmpty()) { 71 builder.addDataValue(Ontology.LINK_HAS_FIELD, field); 72 } 73 if (!desc.trim().isEmpty()) { 74 builder.addDataValue(Ontology.LINK_HAS_DESCRIPTION, desc); 75 } 76 Bundle bundle = builder.build(); 77 78 try { 79 Linkable t1 = manager.getEntity(from, Linkable.class); 80 Linkable t2 = manager.getEntity(to, Linkable.class); 81 82 Link link = bundleManager.create(bundle, Link.class); 83 link.addLinkTarget(t1); 84 link.addLinkTarget(t2); 85 link.setLinker(actioner.as(Accessor.class)); 86 eventContext.addSubjects(link); 87 log.addCreated(); 88 } catch (ItemNotFound e) { 89 logger.error("Item not found at row {}: {}", i, e.getValue()); 90 log.addError(e.getValue(), e.getMessage()); 91 if (!tolerant) { 92 throw new DeserializationError( 93 String.format("Item ID '%s' not found at row: %d", e.getValue(), i)); 94 } 95 } catch (ValidationError e) { 96 log.addError(from, e.getMessage()); 97 logger.error("Deserialization error at row {}: {}", i, e.getMessage()); 98 if (!tolerant) { 99 throw new DeserializationError( 100 String.format("Error validating link at row %d: %s", i, 101 e.getMessage())); 102 } 103 } 104 } 105 if (log.hasDoneWork()) { 106 eventContext.commit(); 107 } 108 109 return log; 110 } 111 }