From 298690d9f8b54d7c88a1a6ef6474b42b74a90b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20L=C3=BCtzenkirchen?= Date: Wed, 17 Jan 2024 17:05:34 +0100 Subject: [PATCH 01/65] UBO-294 Added modsperson module, object type and event handler --- pom.xml | 1 + ubo-cli/pom.xml | 6 + .../mycore/ubo/dedup/DeDupEventHandler.java | 4 + ubo-modsperson/.gitignore | 3 + ubo-modsperson/pom.xml | 72 +++++++ .../src/main/datamodel/def/modsperson.xml | 14 ++ .../ubo/modsperson/MODSPersonLinker.java | 184 ++++++++++++++++++ .../config/ubo-modsperson/mycore.properties | 4 + ubo-webapp/pom.xml | 6 + 9 files changed, 294 insertions(+) create mode 100644 ubo-modsperson/.gitignore create mode 100644 ubo-modsperson/pom.xml create mode 100644 ubo-modsperson/src/main/datamodel/def/modsperson.xml create mode 100644 ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinker.java create mode 100644 ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties diff --git a/pom.xml b/pom.xml index 256ff54ca..494a5b3c6 100644 --- a/pom.xml +++ b/pom.xml @@ -445,6 +445,7 @@ ubo-common + ubo-modsperson ubo-webapp ubo-cli diff --git a/ubo-cli/pom.xml b/ubo-cli/pom.xml index 1e268830c..517907f4e 100644 --- a/ubo-cli/pom.xml +++ b/ubo-cli/pom.xml @@ -112,6 +112,12 @@ + + ${project.parent.groupId} + ubo-modsperson + ${project.version} + runtime + org.mycore mycore-ocfl diff --git a/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupEventHandler.java b/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupEventHandler.java index 1260a971a..7785d753d 100644 --- a/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupEventHandler.java +++ b/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupEventHandler.java @@ -28,6 +28,10 @@ protected void handleObjectRepaired(MCREvent evt, MCRObject obj) { } private void updateDeDupCriteria(MCRObject obj) { + if (!"mods".equals(obj.getId().getTypeId())) { + return; + } + LOGGER.info("updating deduplication keys for object " + obj.getId().toString()); Element mods = new MCRMODSWrapper(obj).getMODS(); new DeDupCriteriaBuilder().updateDeDupCriteria(mods); diff --git a/ubo-modsperson/.gitignore b/ubo-modsperson/.gitignore new file mode 100644 index 000000000..fbdf7ebb0 --- /dev/null +++ b/ubo-modsperson/.gitignore @@ -0,0 +1,3 @@ +/.classpath +/.project +/.settings/ diff --git a/ubo-modsperson/pom.xml b/ubo-modsperson/pom.xml new file mode 100644 index 000000000..59b436d51 --- /dev/null +++ b/ubo-modsperson/pom.xml @@ -0,0 +1,72 @@ + + + 4.0.0 + + + org.mycore.ubo + ubo + dev-SNAPSHOT + + + ubo-modsperson + Universitätsbibliographie - Person Module + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + + + + ${project.artifactId} + ${project.name} + 50 + + + + + + org.mycore.plugins + datamodel-plugin + 0.8 + + + + schema + + + + + + + + + + org.apache.commons + commons-lang3 + + + org.apache.logging.log4j + log4j-api + + + org.jdom + jdom2 + + + org.mycore + mycore-base + + + org.mycore + mycore-mods + + + + \ No newline at end of file diff --git a/ubo-modsperson/src/main/datamodel/def/modsperson.xml b/ubo-modsperson/src/main/datamodel/def/modsperson.xml new file mode 100644 index 000000000..fd2ea335a --- /dev/null +++ b/ubo-modsperson/src/main/datamodel/def/modsperson.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinker.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinker.java new file mode 100644 index 000000000..5146d93a0 --- /dev/null +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinker.java @@ -0,0 +1,184 @@ +package org.mycore.ubo.modsperson; + +import java.io.IOException; +import java.util.Date; + +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jdom2.Document; +import org.jdom2.Element; +import org.mycore.access.MCRAccessException; +import org.mycore.common.MCRConstants; +import org.mycore.common.MCRException; +import org.mycore.common.MCRPersistenceException; +import org.mycore.common.config.MCRConfiguration2; +import org.mycore.common.content.MCRJDOMContent; +import org.mycore.common.events.MCREvent; +import org.mycore.common.events.MCREventHandlerBase; +import org.mycore.common.xml.MCRXMLHelper; +import org.mycore.datamodel.common.MCRXMLMetadataManager; +import org.mycore.datamodel.metadata.MCRMetadataManager; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.mods.MCRMODSSorter; +import org.mycore.mods.MCRMODSWrapper; +import org.mycore.mods.merger.MCRMergeTool; + +// TODO: Documentation +// TODO: Wait for SOLR commit in batch mode? Or cache IDs in batch mode? + +public class MODSPersonLinker extends MCREventHandlerBase { + + private final static Logger LOGGER = LogManager.getLogger(); + + private final static Element PERSON_TEMPLATE; + + static { + String projectID = MCRConfiguration2.getStringOrThrow("UBO.projectid.default"); + String defaultID = MCRObjectID.formatID(projectID, "modsperson", 0); + + Element modsName = new Element("name", MCRConstants.MODS_NAMESPACE).setAttribute("type", "personal"); + Element mods = new Element("mods", MCRConstants.MODS_NAMESPACE).addContent(modsName); + Element mc = new Element("modsContainer").addContent(mods); + Element dmc = new Element("def.modsContainer").setAttribute("class", "MCRMetaXML").addContent(mc); + + Element mycoreobject = new Element("mycoreobject"); + mycoreobject.setAttribute("ID", defaultID); + mycoreobject.setAttribute("noNamespaceSchemaLocation", "datamodel-modsperson.xsd", MCRConstants.XSI_NAMESPACE); + mycoreobject.addContent(new Element("structure")); + mycoreobject.addContent(new Element("metadata").addContent(dmc)); + mycoreobject.addContent(new Element("service")); + + PERSON_TEMPLATE = mycoreobject; + } + + @Override + protected void handleObjectCreated(MCREvent evt, MCRObject obj) { + handleObject(obj); + } + + @Override + protected void handleObjectUpdated(MCREvent evt, MCRObject obj) { + handleObject(obj); + } + + @Override + protected void handleObjectRepaired(MCREvent evt, MCRObject obj) { + boolean wasModified = handleObject(obj); + if (wasModified) { + MCRXMLMetadataManager.instance().update(obj.getId(), obj.createXML(), new Date()); + } + } + + protected boolean handleObject(MCRObject obj) { + if (!"mods".equals(obj.getId().getTypeId())) { + return false; + } + + MCRMODSWrapper wrapper = new MCRMODSWrapper(obj); + wrapper.getElements("mods:name[@type='personal']").forEach(modsName -> { + + MCRObject person = getPersonReferencedIn(modsName); + if (person == null) { + person = findPersonMatching(modsName); + } + if ((person == null) && leadIDExists(modsName)) { + person = buildNewPerson(); + } + + if (person != null) { + mergeDataFromNameToPerson(modsName, person); + setReferencedPerson(modsName, person); + mergeDataFromPersonToName(person, modsName); + } + }); + + return wasModified(wrapper.getMODS()); + } + + private boolean wasModified(Element mods) { + boolean modified = mods.removeAttribute("modified"); + LOGGER.debug("Publication was modified? " + modified); + return modified; + } + + private MCRObject getPersonReferencedIn(Element modsName) { + String personID = modsName.getAttributeValue("href", MCRConstants.XLINK_NAMESPACE); + if (StringUtils.isEmpty(personID)) { + return null; + } else { + LOGGER.debug("Retrieving person object already referenced in publication: " + personID); + MCRObjectID oid = MCRObjectID.getInstance(personID); + return MCRMetadataManager.retrieveMCRObject(oid); + } + } + + private void setReferencedPerson(Element modsName, MCRObject person) { + if (modsName.getAttribute("href", MCRConstants.XLINK_NAMESPACE) == null) { + String personID = person.getId().toString(); + LOGGER.info("Linking mods:name in publication to " + personID); + modsName.setAttribute("href", personID, MCRConstants.XLINK_NAMESPACE); + modsName.getParentElement().setAttribute("modified", "true"); + MCRMODSSorter.sort(modsName); + } + } + + private MCRObject findPersonMatching(Element modsName) { + return null; // TODO: Find via matching valid ID and matching name + } + + private boolean leadIDExists(Element modsName) { + return modsName.getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) + .stream().anyMatch(ni -> "lsf".equals(ni.getAttributeValue("type"))); + } + + private MCRObject buildNewPerson() { + LOGGER.debug("Creating new person object..."); + return new MCRObject(new Document(PERSON_TEMPLATE.clone())); + } + + private void mergeDataFromNameToPerson(Element modsName, MCRObject person) { + LOGGER.info("Merging data from mods:name in publication to person object..."); + + MCRMODSWrapper wrapper = new MCRMODSWrapper(person); + + Element personName = wrapper.getElement("mods:name[@type='personal']"); + Element personOld = personName.clone(); + + MCRMergeTool.merge(personName, filterMODS(modsName)); + + MCRMODSSorter.sort(wrapper.getMODS()); + MCRMODSSorter.sort(personName); + + if (!MCRXMLHelper.deepEqual(personName, personOld)) { + LOGGER.info("Person object is changed after merge, saving..."); + try { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(new MCRJDOMContent(personName.clone()).asString()); + } + MCRMetadataManager.update(person); + } catch (MCRPersistenceException | MCRAccessException | IOException ex) { + throw new MCRException(ex); + } + } else { + LOGGER.info("Person object is unchanged after merge, nothing to do."); + } + } + + private Element filterMODS(Element modsName) { + Element cloned = modsName.clone(); + cloned.removeAttribute("href", MCRConstants.XLINK_NAMESPACE); + cloned.removeChildren("displayForm", MCRConstants.MODS_NAMESPACE); + cloned.removeChildren("alternativeName", MCRConstants.MODS_NAMESPACE); + cloned.removeChildren("role", MCRConstants.MODS_NAMESPACE); + cloned.removeChildren("description", MCRConstants.MODS_NAMESPACE); + cloned.removeChildren("etal", MCRConstants.MODS_NAMESPACE); + return cloned; + } + + private void mergeDataFromPersonToName(MCRObject person, Element modsName) { + LOGGER.info("Merging data from person object to mods:name in publication..."); + // TODO + } +} diff --git a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties new file mode 100644 index 000000000..d84f66781 --- /dev/null +++ b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties @@ -0,0 +1,4 @@ +MCR.Metadata.Type.modsperson=true + +MCR.EventHandler.MCRObject.019.Class=org.mycore.ubo.modsperson.MODSPersonLinker + diff --git a/ubo-webapp/pom.xml b/ubo-webapp/pom.xml index f63d6bb6c..5d7d57860 100644 --- a/ubo-webapp/pom.xml +++ b/ubo-webapp/pom.xml @@ -80,5 +80,11 @@ ubo-common runtime + + org.mycore.ubo + ubo-modsperson + ${project.version} + runtime + From 2f2eb5ab3717f97fc31515bce41bf53fbb4866d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20L=C3=BCtzenkirchen?= Date: Wed, 17 Jan 2024 17:06:29 +0100 Subject: [PATCH 02/65] UBO-294 Work in progress, add class to lookup person data --- .../ubo/modsperson/MODSPersonLookup.java | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java new file mode 100644 index 000000000..898ebeda5 --- /dev/null +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java @@ -0,0 +1,52 @@ +package org.mycore.ubo.modsperson; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.jdom2.Element; +import org.mycore.common.MCRConstants; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.mods.MCRMODSWrapper; + +public class MODSPersonLookup { + + private Map nameID2person = new HashMap(); + + private Map id2person = new HashMap(); + + public void add(MCRObject person) { + Element mods = new MCRMODSWrapper(person).getMODS().clone(); + id2person.put(person.getId().toString(), mods); + getNameIdentifiers(mods).forEach(nameID -> nameID2person.put(buildKey(nameID), mods)); + } + + public void remove(MCRObject person) { + Element mods = id2person.remove(person.getId().toString()); + getNameIdentifiers(mods).forEach(nameID -> nameID2person.remove(buildKey(nameID))); + } + + public void update(MCRObject person) { + remove(person); + add(person); + } + + private String buildKey(Element nameIdentifier) { + String key = String.join("|", nameIdentifier.getTextTrim(), nameIdentifier.getAttributeValue("type")); + return key; + } + + private List getNameIdentifiers(Element mods) { + return mods.getChild("name", MCRConstants.MODS_NAMESPACE).getChildren("nameIdentifier", + MCRConstants.MODS_NAMESPACE); + } + + public Element lookup(Element modsName) { + Set keys2lookup = modsName.getChildren("nameIdentifer", MCRConstants.MODS_NAMESPACE) + .stream().map(nI -> buildKey(nI)).collect(Collectors.toSet()); + keys2lookup.retainAll(nameID2person.keySet()); + return keys2lookup.isEmpty() ? null : nameID2person.get(keys2lookup.iterator().next()).clone(); + } +} From 2eca91cbbedd9659d27578330fcc3b87619de8ee Mon Sep 17 00:00:00 2001 From: Eva Roddeck Date: Tue, 1 Oct 2024 12:43:50 +0200 Subject: [PATCH 03/65] first changes --- ubo-modsperson/pom.xml | 30 +++- .../MODSPersonCachingStartupHandler.java | 40 +++++ ...ava => MODSPersonLinkingEventHandler.java} | 64 +++++--- .../ubo/modsperson/MODSPersonLookup.java | 43 ++++-- .../config/ubo-modsperson/mycore.properties | 3 +- .../MODSPersonLinkingEventHandlerTest.java | 144 ++++++++++++++++++ .../ubo/modsperson/MODSPersonLookupTest.java | 104 +++++++++++++ .../test/resources/META-INF/persistence.xml | 25 +++ .../junit_mods_00000001.xml | 20 +++ .../junit_mods_00000002.xml | 20 +++ .../junit_mods_00000003.xml | 20 +++ .../src/test/resources/mycore.properties | 7 + 12 files changed, 481 insertions(+), 39 deletions(-) create mode 100644 ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java rename ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/{MODSPersonLinker.java => MODSPersonLinkingEventHandler.java} (74%) create mode 100644 ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java create mode 100644 ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java create mode 100644 ubo-modsperson/src/test/resources/META-INF/persistence.xml create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000001.xml create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000002.xml create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000003.xml create mode 100644 ubo-modsperson/src/test/resources/mycore.properties diff --git a/ubo-modsperson/pom.xml b/ubo-modsperson/pom.xml index 59b436d51..9fd74d17c 100644 --- a/ubo-modsperson/pom.xml +++ b/ubo-modsperson/pom.xml @@ -1,5 +1,5 @@ - + + 4.0.0 @@ -8,7 +8,7 @@ ubo dev-SNAPSHOT - + ubo-modsperson Universitätsbibliographie - Person Module @@ -45,8 +45,12 @@ - + + + jakarta.servlet + jakarta.servlet-api + org.apache.commons commons-lang3 @@ -67,6 +71,22 @@ org.mycore mycore-mods + + com.h2database + h2 + test + + + junit + junit + test + + + org.mycore + mycore-base + test-jar + test + - \ No newline at end of file + diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java new file mode 100644 index 000000000..0bf2592c3 --- /dev/null +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java @@ -0,0 +1,40 @@ +package org.mycore.ubo.modsperson; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.mycore.common.events.MCRStartupHandler; +import org.mycore.datamodel.metadata.MCRMetadataManager; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.frontend.cli.MCRCommandUtils; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Loads all mpdsperson data into {@link MODSPersonLookup} on startup. + */ +public class MODSPersonCachingStartupHandler implements MCRStartupHandler.AutoExecutable { + + private final static Logger LOGGER = LogManager.getLogger(); + + @Override + public String getName() { + return "MODSPersonCachingStartupHandler"; + } + + @Override + public int getPriority() { + return 0; + } + + @Override + public void startUp(jakarta.servlet.ServletContext servletContext) { + AtomicInteger counter = new AtomicInteger(0); + MCRCommandUtils.getIdsForType("modsperson").forEach(idString -> { + MCRObject obj = MCRMetadataManager.retrieveMCRObject(MCRObjectID.getInstance(idString)); + MODSPersonLookup.add(obj); + counter.incrementAndGet(); + }); + LOGGER.info("Loaded " + counter.get() + " modsperson-objects into the cache"); + } +} diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinker.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java similarity index 74% rename from ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinker.java rename to ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java index 5146d93a0..783a927fc 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinker.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java @@ -8,6 +8,8 @@ import org.apache.logging.log4j.Logger; import org.jdom2.Document; import org.jdom2.Element; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; import org.mycore.access.MCRAccessException; import org.mycore.common.MCRConstants; import org.mycore.common.MCRException; @@ -28,7 +30,7 @@ // TODO: Documentation // TODO: Wait for SOLR commit in batch mode? Or cache IDs in batch mode? -public class MODSPersonLinker extends MCREventHandlerBase { +public class MODSPersonLinkingEventHandler extends MCREventHandlerBase { private final static Logger LOGGER = LogManager.getLogger(); @@ -51,6 +53,7 @@ public class MODSPersonLinker extends MCREventHandlerBase { mycoreobject.addContent(new Element("service")); PERSON_TEMPLATE = mycoreobject; + System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(mycoreobject)); } @Override @@ -90,7 +93,7 @@ protected boolean handleObject(MCRObject obj) { if (person != null) { mergeDataFromNameToPerson(modsName, person); setReferencedPerson(modsName, person); - mergeDataFromPersonToName(person, modsName); + mergeDataFromPersonToName(modsName, person); } }); @@ -125,7 +128,14 @@ private void setReferencedPerson(Element modsName, MCRObject person) { } private MCRObject findPersonMatching(Element modsName) { - return null; // TODO: Find via matching valid ID and matching name + Element personElement = MODSPersonLookup.lookup(modsName); + if (personElement == null) { + return null; + } + MCRObject obj = new MCRObject(new Document(PERSON_TEMPLATE.clone())); + MCRMODSWrapper wrapper = new MCRMODSWrapper(obj); + wrapper.setMODS(personElement); + return obj; } private boolean leadIDExists(Element modsName) { @@ -144,25 +154,27 @@ private void mergeDataFromNameToPerson(Element modsName, MCRObject person) { MCRMODSWrapper wrapper = new MCRMODSWrapper(person); Element personName = wrapper.getElement("mods:name[@type='personal']"); - Element personOld = personName.clone(); - - MCRMergeTool.merge(personName, filterMODS(modsName)); - - MCRMODSSorter.sort(wrapper.getMODS()); - MCRMODSSorter.sort(personName); - - if (!MCRXMLHelper.deepEqual(personName, personOld)) { - LOGGER.info("Person object is changed after merge, saving..."); - try { - if (LOGGER.isDebugEnabled()) { - LOGGER.debug(new MCRJDOMContent(personName.clone()).asString()); + if (personName != null) { + Element personOld = personName.clone(); + + MCRMergeTool.merge(personName, filterMODS(modsName)); + + MCRMODSSorter.sort(wrapper.getMODS()); + MCRMODSSorter.sort(personName); + + if (!MCRXMLHelper.deepEqual(personName, personOld)) { + LOGGER.info("Person object is changed after merge, saving..."); + try { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(new MCRJDOMContent(personName.clone()).asString()); + } + MCRMetadataManager.update(person); + } catch (MCRPersistenceException | MCRAccessException | IOException ex) { + throw new MCRException(ex); } - MCRMetadataManager.update(person); - } catch (MCRPersistenceException | MCRAccessException | IOException ex) { - throw new MCRException(ex); + } else { + LOGGER.info("Person object is unchanged after merge, nothing to do."); } - } else { - LOGGER.info("Person object is unchanged after merge, nothing to do."); } } @@ -177,8 +189,14 @@ private Element filterMODS(Element modsName) { return cloned; } - private void mergeDataFromPersonToName(MCRObject person, Element modsName) { - LOGGER.info("Merging data from person object to mods:name in publication..."); - // TODO + private void mergeDataFromPersonToName(Element modsName, MCRObject person) { + LOGGER.info("Merging data from person object into mods:name in publication..."); + MCRMODSWrapper wrapper = new MCRMODSWrapper(person); + Element personNameElement = wrapper.getMODS() != null + ? wrapper.getMODS().getChild("name", MCRConstants.MODS_NAMESPACE) + : null; + if (personNameElement != null) { + MCRMergeTool.merge(modsName, personNameElement); + } } } diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java index 898ebeda5..e57850590 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java @@ -10,43 +10,66 @@ import org.mycore.common.MCRConstants; import org.mycore.datamodel.metadata.MCRObject; import org.mycore.mods.MCRMODSWrapper; +import org.mycore.mods.merger.MCRMerger; +import org.mycore.mods.merger.MCRMergerFactory; public class MODSPersonLookup { - private Map nameID2person = new HashMap(); + private static Map nameID2person = new HashMap<>(); - private Map id2person = new HashMap(); + private static Map id2person = new HashMap<>(); - public void add(MCRObject person) { + public static void add(MCRObject person) { Element mods = new MCRMODSWrapper(person).getMODS().clone(); id2person.put(person.getId().toString(), mods); getNameIdentifiers(mods).forEach(nameID -> nameID2person.put(buildKey(nameID), mods)); } - public void remove(MCRObject person) { + public static void remove(MCRObject person) { Element mods = id2person.remove(person.getId().toString()); getNameIdentifiers(mods).forEach(nameID -> nameID2person.remove(buildKey(nameID))); } - public void update(MCRObject person) { + public static void update(MCRObject person) { remove(person); add(person); } - private String buildKey(Element nameIdentifier) { + private static String buildKey(Element nameIdentifier) { String key = String.join("|", nameIdentifier.getTextTrim(), nameIdentifier.getAttributeValue("type")); return key; } - private List getNameIdentifiers(Element mods) { + private static List getNameIdentifiers(Element mods) { return mods.getChild("name", MCRConstants.MODS_NAMESPACE).getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE); } - public Element lookup(Element modsName) { - Set keys2lookup = modsName.getChildren("nameIdentifer", MCRConstants.MODS_NAMESPACE) - .stream().map(nI -> buildKey(nI)).collect(Collectors.toSet()); + public static Element lookup(Element modsName) { + Set keys2lookup = modsName.getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) + .stream().map(MODSPersonLookup::buildKey).collect(Collectors.toSet()); + keys2lookup.retainAll(nameID2person.keySet()); + + keys2lookup.removeIf(s -> { + Element savedElement = nameID2person.get(s) != null + ? nameID2person.get(s).getChild("name", MCRConstants.MODS_NAMESPACE).clone() + : null; + return savedElement == null || !hasSameNames(modsName, savedElement); + }); + return keys2lookup.isEmpty() ? null : nameID2person.get(keys2lookup.iterator().next()).clone(); } + + public static Element lookup(String identifier, String identifierType) { + String key = String.join("|", identifier.trim(), identifierType.trim()); + return nameID2person.get(key); + } + + private static boolean hasSameNames(Element element1, Element element2) { + MCRMerger merger1 = MCRMergerFactory.buildFrom(element1); + MCRMerger merger2 = MCRMergerFactory.buildFrom(element2); + return merger1.isProbablySameAs(merger2); + } + } diff --git a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties index d84f66781..e0621de3e 100644 --- a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties +++ b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties @@ -1,4 +1,5 @@ MCR.Metadata.Type.modsperson=true -MCR.EventHandler.MCRObject.019.Class=org.mycore.ubo.modsperson.MODSPersonLinker +MCR.EventHandler.MCRObject.022.Class=org.mycore.ubo.modsperson.MODSPersonLinkingEventHandler +MCR.Startup.Class=%MCR.Startup.Class%, org.mycore.ubo.modsperson.MODSPersonCachingStartupHandler diff --git a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java new file mode 100644 index 000000000..c4c116868 --- /dev/null +++ b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java @@ -0,0 +1,144 @@ +package org.mycore.ubo.modsperson; + +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; +import org.mycore.access.MCRAccessException; +import org.mycore.common.MCRConstants; +import org.mycore.common.MCRStoreTestCase; +import org.mycore.common.content.MCRURLContent; +import org.mycore.datamodel.metadata.MCRMetadataManager; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectID; +import org.mycore.datamodel.metadata.MCRObjectMetadataTest; +import org.mycore.mods.MCRMODSWrapper; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class MODSPersonLinkingEventHandlerTest extends MCRStoreTestCase { + + private static boolean isInitialized = false; + + @Before + public void init() throws IOException, JDOMException, MCRAccessException { + if (!isInitialized) { + URL url1 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000001.xml"); + Document doc1 = new MCRURLContent(url1).asXML(); + MCRObject obj1 = new MCRObject(doc1); + + URL url2 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000002.xml"); + Document doc2 = new MCRURLContent(url2).asXML(); + MCRObject obj2 = new MCRObject(doc2); + + URL url3 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000003.xml"); + Document doc3 = new MCRURLContent(url3).asXML(); + MCRObject obj3 = new MCRObject(doc3); + + MCRMetadataManager.create(obj1); + MCRMetadataManager.create(obj2); + MCRMetadataManager.create(obj3); + isInitialized = true; + } + } + + @Test + @Ignore + public void testHandleCreate() { + + MCRObject person1 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_modsperson_00000001")); + assertNotNull(person1); + assertPerson(person1, "Müller", "Lisa", "11111", "1112222333"); + + MCRObject person2 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_modsperson_00000002")); + assertNotNull(person2); + assertPerson(person2, "Meyer", "Lena", "12345", "1112222444"); + + MCRObject person3 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_modsperson_00000003")); + assertNotNull(person3); + assertPerson(person3, "Müller", "Luisa", "22222", "444555666"); + } + + @Test + public void testHandleCreateWithLookup() { + + MCRObject mods1 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_mods_00000001")); + MCRObject mods2 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_mods_00000001")); + MCRObject mods3 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_mods_00000001")); + + MODSPersonLookup.add(mods1); + MODSPersonLookup.add(mods2); + MODSPersonLookup.add(mods3); + + MCRObject person1 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_modsperson_00000001")); + assertNotNull(person1); + assertPerson(person1, "Müller", "Lisa", "11111", "1112222333"); + + MCRObject person2 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_modsperson_00000002")); + assertNotNull(person2); + assertPerson(person2, "Meyer", "Lena", "12345", "1112222444"); + + MCRObject person3 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + .getInstance("junit_modsperson_00000003")); + assertNotNull(person3); + assertPerson(person3, "Müller", "Luisa", "22222", "444555666"); + } + + /** + * Assert that the Object contains the two name parts and an LSF and SCOPUS ID + * @param person tested {@link MCRObject} + * @param familyName the expected family name + * @param givenName the expected given name + * @param lsfId the expected LSF ID + * @param scopusId the expected Scopus ID + */ + private void assertPerson(MCRObject person, String familyName, + String givenName, String lsfId, String scopusId) { + + MCRMODSWrapper wrapper = new MCRMODSWrapper(person); + List nameElements = wrapper.getMODS().getChild("name", MCRConstants.MODS_NAMESPACE) + .getChildren(); + List familyNameElements = nameElements.stream() + .filter(e -> "family".equals(e.getAttributeValue("type"))) + .toList(); + + assertEquals(1, familyNameElements.size()); + assertEquals(familyName, familyNameElements.get(0).getText()); + + List givenNameElements = nameElements.stream() + .filter(e -> "given".equals(e.getAttributeValue("type"))) + .toList(); + + assertEquals(1, givenNameElements.size()); + assertEquals(givenName, givenNameElements.get(0).getText()); + + List lsfElements = nameElements.stream() + .filter(e -> "lsf".equals(e.getAttributeValue("type"))) + .toList(); + + assertEquals(1, lsfElements.size()); + assertEquals(lsfId, lsfElements.get(0).getText()); + + List scopusElements = nameElements.stream() + .filter(e -> "scopus".equals(e.getAttributeValue("type"))) + .toList(); + + assertEquals(1, scopusElements.size()); + assertEquals(scopusId, scopusElements.get(0).getText()); + } +} diff --git a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java new file mode 100644 index 000000000..e7f294864 --- /dev/null +++ b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java @@ -0,0 +1,104 @@ +package org.mycore.ubo.modsperson; + +import org.jdom2.Document; +import org.jdom2.Element; +import org.jdom2.JDOMException; +import org.jdom2.output.Format; +import org.jdom2.output.XMLOutputter; +import org.junit.Test; +import org.mycore.common.MCRConstants; +import org.mycore.common.MCRTestCase; +import org.mycore.common.content.MCRURLContent; +import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectMetadataTest; +import org.mycore.mods.MCRMODSWrapper; + +import java.io.IOException; +import java.net.URL; +import java.util.List; + +import static org.junit.Assert.*; + +public class MODSPersonLookupTest extends MCRTestCase { + + @Test + public void testLookup() throws IOException, JDOMException { + URL url1 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000001.xml"); + Document doc1 = new MCRURLContent(url1).asXML(); + MCRObject obj1 = new MCRObject(doc1); + + URL url2 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000002.xml"); + Document doc2 = new MCRURLContent(url2).asXML(); + MCRObject obj2 = new MCRObject(doc2); + + URL url3 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000003.xml"); + Document doc3 = new MCRURLContent(url3).asXML(); + MCRObject obj3 = new MCRObject(doc3); + + MODSPersonLookup.add(obj1); + MODSPersonLookup.add(obj2); + + MCRMODSWrapper wrapper = new MCRMODSWrapper(obj1); + List names1 = wrapper.getElements("mods:name[@type='personal']"); + + Element person1 = MODSPersonLookup.lookup(names1.get(0)); + System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(person1)); + + assertPersonElement(person1, "Müller", "Lisa", "11111", "1112222333"); + + wrapper = new MCRMODSWrapper(obj2); + List names2 = wrapper.getElements("mods:name[@type='personal']"); + Element person2 = MODSPersonLookup.lookup(names2.get(0)); + System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(person2)); + + assertPersonElement(person2, "Meyer", "Lena", "12345", "1112222444"); + + wrapper = new MCRMODSWrapper(obj3); + List names3 = wrapper.getElements("mods:name[@type='personal']"); + Element person3 = MODSPersonLookup.lookup(names3.get(0)); + assertNull(person3); + } + + /** + * Assert that the Element contains the two name parts and an LSF and SCOPUS ID + * @param person tested Element + * @param familyName the expected family name + * @param givenName the expected given name + * @param lsfId the expected LSF ID + * @param scopusId the expected Scopus ID + */ + private void assertPersonElement(Element person, String familyName, + String givenName, String lsfId, String scopusId) { + + List nameElements = person.getChild("name", MCRConstants.MODS_NAMESPACE) + .getChildren(); + List familyNameElements = nameElements.stream() + .filter(e -> "family".equals(e.getAttributeValue("type"))) + .toList(); + + assertEquals(1, familyNameElements.size()); + assertEquals(familyName, familyNameElements.get(0).getText()); + + List givenNameElements = nameElements.stream() + .filter(e -> "given".equals(e.getAttributeValue("type"))) + .toList(); + + assertEquals(1, givenNameElements.size()); + assertEquals(givenName, givenNameElements.get(0).getText()); + + List lsfElements = nameElements.stream() + .filter(e -> "lsf".equals(e.getAttributeValue("type"))) + .toList(); + + assertEquals(1, lsfElements.size()); + assertEquals(lsfId, lsfElements.get(0).getText()); + + List scopusElements = nameElements.stream() + .filter(e -> "scopus".equals(e.getAttributeValue("type"))) + .toList(); + + assertEquals(1, scopusElements.size()); + assertEquals(scopusId, scopusElements.get(0).getText()); + } + +} diff --git a/ubo-modsperson/src/test/resources/META-INF/persistence.xml b/ubo-modsperson/src/test/resources/META-INF/persistence.xml new file mode 100644 index 000000000..528311c5c --- /dev/null +++ b/ubo-modsperson/src/test/resources/META-INF/persistence.xml @@ -0,0 +1,25 @@ + + + + + META-INF/mycore-base-mappings.xml + META-INF/mycore-pi-mappings.xml + META-INF/mycore-user2-mappings.xml + META-INF/mycore-jobqueue-mappings.xml + + + + + + + + + + + + + + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000001.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000001.xml new file mode 100644 index 000000000..006000f37 --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000001.xml @@ -0,0 +1,20 @@ + + + + + + + + This is a test-publication + + + Müller + Lisa + 11111 + 1112222333 + + + + + + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000002.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000002.xml new file mode 100644 index 000000000..c9d7b0447 --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000002.xml @@ -0,0 +1,20 @@ + + + + + + + + This is a test-publication + + + Meyer + Lena + 12345 + 1112222444 + + + + + + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000003.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000003.xml new file mode 100644 index 000000000..59cae735a --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000003.xml @@ -0,0 +1,20 @@ + + + + + + + + This is a test-publication + + + Müller + Luisa + 22222 + 444555666 + + + + + + diff --git a/ubo-modsperson/src/test/resources/mycore.properties b/ubo-modsperson/src/test/resources/mycore.properties new file mode 100644 index 000000000..409a47c68 --- /dev/null +++ b/ubo-modsperson/src/test/resources/mycore.properties @@ -0,0 +1,7 @@ +MCR.user2.IdentityManagement.UserCreation.Unvalidated.Realm=local +MCR.user2.matching.chain=org.mycore.ubo.matcher.MCRUserMatcherDummy +MCR.user2.matching.lead_id=lsf +MCR.user2.matching.publication.connection.strategy=uuid +MCR.EventHandler.MCRObject.019.Class=org.mycore.ubo.modsperson.MODSPersonLinkingEventHandler +MCR.Access.Class=org.mycore.access.MCRAccessBaseImpl +UBO.projectid.default=junit From a709b7475d34c09c9d68620a16b7a61b415d2af0 Mon Sep 17 00:00:00 2001 From: Eva Roddeck Date: Tue, 8 Oct 2024 16:45:16 +0200 Subject: [PATCH 04/65] Refactored lookup mechanism + add new person to lookup + corrected unit tests --- .../MODSPersonLinkingEventHandler.java | 27 ++++-- .../ubo/modsperson/MODSPersonLookup.java | 51 ++++++----- .../MODSPersonLinkingEventHandlerTest.java | 86 +++++-------------- .../ubo/modsperson/MODSPersonLookupTest.java | 32 ++++--- .../junit_mods_00000001.xml | 6 +- .../junit_mods_00000002.xml | 6 +- .../junit_mods_00000003.xml | 6 +- .../junit_modsperson_00000010.xml | 17 ++++ .../junit_modsperson_00000011.xml | 17 ++++ .../junit_modsperson_00000012.xml | 17 ++++ 10 files changed, 151 insertions(+), 114 deletions(-) rename ubo-modsperson/src/test/resources/{MODSPersonLookupTest => MODSPersonLinkingEventHandlerTest}/junit_mods_00000001.xml (80%) rename ubo-modsperson/src/test/resources/{MODSPersonLookupTest => MODSPersonLinkingEventHandlerTest}/junit_mods_00000002.xml (80%) rename ubo-modsperson/src/test/resources/{MODSPersonLookupTest => MODSPersonLinkingEventHandlerTest}/junit_mods_00000003.xml (80%) create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000010.xml create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000011.xml create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000012.xml diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java index 783a927fc..8d7fbff57 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.util.Date; +import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -86,14 +88,20 @@ protected boolean handleObject(MCRObject obj) { if (person == null) { person = findPersonMatching(modsName); } + + boolean isNewPerson = false; if ((person == null) && leadIDExists(modsName)) { person = buildNewPerson(); + isNewPerson = true; } if (person != null) { mergeDataFromNameToPerson(modsName, person); setReferencedPerson(modsName, person); mergeDataFromPersonToName(modsName, person); + if (isNewPerson) { + MODSPersonLookup.add(person); + } } }); @@ -128,19 +136,24 @@ private void setReferencedPerson(Element modsName, MCRObject person) { } private MCRObject findPersonMatching(Element modsName) { - Element personElement = MODSPersonLookup.lookup(modsName); - if (personElement == null) { + Set personElements = MODSPersonLookup.lookup(modsName); + if (personElements == null || personElements.isEmpty()) { return null; } - MCRObject obj = new MCRObject(new Document(PERSON_TEMPLATE.clone())); - MCRMODSWrapper wrapper = new MCRMODSWrapper(obj); - wrapper.setMODS(personElement); - return obj; + MCRObject firstMatch = personElements.iterator().next(); + if (personElements.size() > 1) { + String allIDs = personElements.stream() + .map(o -> o.getId().toString()).collect(Collectors.joining(", ")); + + LOGGER.warn("There are multiple modsperson-objects matching the person in publication: " + + "["+ allIDs +"]. Chosing " + firstMatch.getId().toString()); + } + return firstMatch; } private boolean leadIDExists(Element modsName) { return modsName.getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) - .stream().anyMatch(ni -> "lsf".equals(ni.getAttributeValue("type"))); + .stream().anyMatch(ni -> "lsf".equals(ni.getAttributeValue("type"))); // TODO: lsf generic? } private MCRObject buildNewPerson() { diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java index e57850590..07cea704d 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java @@ -1,6 +1,7 @@ package org.mycore.ubo.modsperson; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -15,19 +16,33 @@ public class MODSPersonLookup { - private static Map nameID2person = new HashMap<>(); + private static Map> nameId2person = new HashMap<>(); - private static Map id2person = new HashMap<>(); + private static Map id2person = new HashMap<>(); public static void add(MCRObject person) { Element mods = new MCRMODSWrapper(person).getMODS().clone(); - id2person.put(person.getId().toString(), mods); - getNameIdentifiers(mods).forEach(nameID -> nameID2person.put(buildKey(nameID), mods)); + id2person.put(person.getId().toString(), person); + getNameIdentifiers(mods).forEach(nameID -> { + String key = buildKey(nameID); + nameId2person.computeIfAbsent(key, k -> new HashSet<>()).add(person); + }); } public static void remove(MCRObject person) { - Element mods = id2person.remove(person.getId().toString()); - getNameIdentifiers(mods).forEach(nameID -> nameID2person.remove(buildKey(nameID))); + Element mods = new MCRMODSWrapper(person).getMODS().clone(); + id2person.remove(person.getId().toString()); + + getNameIdentifiers(mods).forEach(nameID -> { + String key = buildKey(nameID); + Set personSet = nameId2person.get(key); + if (personSet != null) { + personSet.removeIf(testedPerson -> testedPerson.getId().equals(person.getId())); + if (personSet.isEmpty()) { + nameId2person.remove(key); + } + } + }); } public static void update(MCRObject person) { @@ -45,25 +60,21 @@ private static List getNameIdentifiers(Element mods) { MCRConstants.MODS_NAMESPACE); } - public static Element lookup(Element modsName) { + public static Set lookup(Element modsName) { Set keys2lookup = modsName.getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) .stream().map(MODSPersonLookup::buildKey).collect(Collectors.toSet()); - keys2lookup.retainAll(nameID2person.keySet()); + keys2lookup.retainAll(nameId2person.keySet()); - keys2lookup.removeIf(s -> { - Element savedElement = nameID2person.get(s) != null - ? nameID2person.get(s).getChild("name", MCRConstants.MODS_NAMESPACE).clone() - : null; - return savedElement == null || !hasSameNames(modsName, savedElement); - }); - - return keys2lookup.isEmpty() ? null : nameID2person.get(keys2lookup.iterator().next()).clone(); - } + keys2lookup.removeIf(s -> nameId2person.get(s) == null || nameId2person.get(s).stream() + .filter(obj -> { + Element savedElement = new MCRMODSWrapper(obj).getMODS() + .getChild("name", MCRConstants.MODS_NAMESPACE); + return savedElement == null || !hasSameNames(modsName, savedElement); + }).count() == nameId2person.get(s).size()); - public static Element lookup(String identifier, String identifierType) { - String key = String.join("|", identifier.trim(), identifierType.trim()); - return nameID2person.get(key); + // TODO: If there are multiple objects, we need the one that has most IDs in common with keys2lookup + return keys2lookup.isEmpty() ? null : nameId2person.get(keys2lookup.iterator().next()); } private static boolean hasSameNames(Element element1, Element element2) { diff --git a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java index c4c116868..331537e95 100644 --- a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java +++ b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java @@ -3,8 +3,6 @@ import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; -import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.mycore.access.MCRAccessException; import org.mycore.common.MCRConstants; @@ -25,78 +23,38 @@ public class MODSPersonLinkingEventHandlerTest extends MCRStoreTestCase { - private static boolean isInitialized = false; - - @Before - public void init() throws IOException, JDOMException, MCRAccessException { - if (!isInitialized) { - URL url1 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000001.xml"); - Document doc1 = new MCRURLContent(url1).asXML(); - MCRObject obj1 = new MCRObject(doc1); - - URL url2 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000002.xml"); - Document doc2 = new MCRURLContent(url2).asXML(); - MCRObject obj2 = new MCRObject(doc2); - - URL url3 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000003.xml"); - Document doc3 = new MCRURLContent(url3).asXML(); - MCRObject obj3 = new MCRObject(doc3); - - MCRMetadataManager.create(obj1); - MCRMetadataManager.create(obj2); - MCRMetadataManager.create(obj3); - isInitialized = true; - } - } - @Test - @Ignore - public void testHandleCreate() { - - MCRObject person1 = MCRMetadataManager.retrieveMCRObject(MCRObjectID - .getInstance("junit_modsperson_00000001")); - assertNotNull(person1); - assertPerson(person1, "Müller", "Lisa", "11111", "1112222333"); - - MCRObject person2 = MCRMetadataManager.retrieveMCRObject(MCRObjectID - .getInstance("junit_modsperson_00000002")); - assertNotNull(person2); - assertPerson(person2, "Meyer", "Lena", "12345", "1112222444"); + public void testHandleCreateWithoutLookup() throws IOException, JDOMException, MCRAccessException { + URL url1 = MCRObjectMetadataTest.class.getResource("/MODSPersonLinkingEventHandlerTest/junit_mods_00000001.xml"); + Document doc1 = new MCRURLContent(url1).asXML(); + MCRObject obj1 = new MCRObject(doc1); - MCRObject person3 = MCRMetadataManager.retrieveMCRObject(MCRObjectID - .getInstance("junit_modsperson_00000003")); - assertNotNull(person3); - assertPerson(person3, "Müller", "Luisa", "22222", "444555666"); - } - - @Test - public void testHandleCreateWithLookup() { + URL url2 = MCRObjectMetadataTest.class.getResource("/MODSPersonLinkingEventHandlerTest/junit_mods_00000002.xml"); + Document doc2 = new MCRURLContent(url2).asXML(); + MCRObject obj2 = new MCRObject(doc2); - MCRObject mods1 = MCRMetadataManager.retrieveMCRObject(MCRObjectID - .getInstance("junit_mods_00000001")); - MCRObject mods2 = MCRMetadataManager.retrieveMCRObject(MCRObjectID - .getInstance("junit_mods_00000001")); - MCRObject mods3 = MCRMetadataManager.retrieveMCRObject(MCRObjectID - .getInstance("junit_mods_00000001")); + URL url3 = MCRObjectMetadataTest.class.getResource("/MODSPersonLinkingEventHandlerTest/junit_mods_00000003.xml"); + Document doc3 = new MCRURLContent(url3).asXML(); + MCRObject obj3 = new MCRObject(doc3); - MODSPersonLookup.add(mods1); - MODSPersonLookup.add(mods2); - MODSPersonLookup.add(mods3); + MCRMetadataManager.create(obj1); + MCRMetadataManager.create(obj2); + MCRMetadataManager.create(obj3); - MCRObject person1 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + MCRObject person4 = MCRMetadataManager.retrieveMCRObject(MCRObjectID .getInstance("junit_modsperson_00000001")); - assertNotNull(person1); - assertPerson(person1, "Müller", "Lisa", "11111", "1112222333"); + assertNotNull(person4); + assertPerson(person4, "Müller", "Lisa", "77777", "1112222999"); - MCRObject person2 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + MCRObject person5 = MCRMetadataManager.retrieveMCRObject(MCRObjectID .getInstance("junit_modsperson_00000002")); - assertNotNull(person2); - assertPerson(person2, "Meyer", "Lena", "12345", "1112222444"); + assertNotNull(person5); + assertPerson(person5, "Meyer", "Lena", "44444", "1112222888"); - MCRObject person3 = MCRMetadataManager.retrieveMCRObject(MCRObjectID + MCRObject person6 = MCRMetadataManager.retrieveMCRObject(MCRObjectID .getInstance("junit_modsperson_00000003")); - assertNotNull(person3); - assertPerson(person3, "Müller", "Luisa", "22222", "444555666"); + assertNotNull(person6); + assertPerson(person6, "Müller", "Luisa", "99999", "444555777"); } /** diff --git a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java index e7f294864..8225c1e86 100644 --- a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java +++ b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java @@ -16,6 +16,8 @@ import java.io.IOException; import java.net.URL; import java.util.List; +import java.util.Objects; +import java.util.Set; import static org.junit.Assert.*; @@ -23,15 +25,15 @@ public class MODSPersonLookupTest extends MCRTestCase { @Test public void testLookup() throws IOException, JDOMException { - URL url1 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000001.xml"); + URL url1 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_modsperson_00000010.xml"); Document doc1 = new MCRURLContent(url1).asXML(); MCRObject obj1 = new MCRObject(doc1); - URL url2 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000002.xml"); + URL url2 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_modsperson_00000011.xml"); Document doc2 = new MCRURLContent(url2).asXML(); MCRObject obj2 = new MCRObject(doc2); - URL url3 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_mods_00000003.xml"); + URL url3 = MCRObjectMetadataTest.class.getResource("/MODSPersonLookupTest/junit_modsperson_00000012.xml"); Document doc3 = new MCRURLContent(url3).asXML(); MCRObject obj3 = new MCRObject(doc3); @@ -39,24 +41,26 @@ public void testLookup() throws IOException, JDOMException { MODSPersonLookup.add(obj2); MCRMODSWrapper wrapper = new MCRMODSWrapper(obj1); - List names1 = wrapper.getElements("mods:name[@type='personal']"); + Element name1 = wrapper.getElement("mods:name[@type='personal']"); - Element person1 = MODSPersonLookup.lookup(names1.get(0)); - System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(person1)); + MCRObject person1 = Objects.requireNonNull(MODSPersonLookup.lookup(name1)).iterator().next(); + Element person1Element = new MCRMODSWrapper(person1).getMODS(); + System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(person1Element)); - assertPersonElement(person1, "Müller", "Lisa", "11111", "1112222333"); + assertPersonElement(person1Element, "Müller", "Adam", "98765", "2222222333"); wrapper = new MCRMODSWrapper(obj2); - List names2 = wrapper.getElements("mods:name[@type='personal']"); - Element person2 = MODSPersonLookup.lookup(names2.get(0)); - System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(person2)); + Element name2 = wrapper.getElement("mods:name[@type='personal']"); + MCRObject person2 = Objects.requireNonNull(MODSPersonLookup.lookup(name2)).iterator().next(); + Element person2Element = new MCRMODSWrapper(person2).getMODS(); + System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(person2Element)); - assertPersonElement(person2, "Meyer", "Lena", "12345", "1112222444"); + assertPersonElement(person2Element, "Meyer", "Gustav", "112233", "2222222444"); wrapper = new MCRMODSWrapper(obj3); - List names3 = wrapper.getElements("mods:name[@type='personal']"); - Element person3 = MODSPersonLookup.lookup(names3.get(0)); - assertNull(person3); + Element name3 = wrapper.getElement("mods:name[@type='personal']"); + Set persons3 = MODSPersonLookup.lookup(name3); + assertNull(persons3); } /** diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000001.xml b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000001.xml similarity index 80% rename from ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000001.xml rename to ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000001.xml index 006000f37..87ec49df2 100644 --- a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000001.xml +++ b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000001.xml @@ -1,5 +1,5 @@ - + @@ -10,8 +10,8 @@ Müller Lisa - 11111 - 1112222333 + 77777 + 1112222999 diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000002.xml b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000002.xml similarity index 80% rename from ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000002.xml rename to ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000002.xml index c9d7b0447..0466a8519 100644 --- a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000002.xml +++ b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000002.xml @@ -1,5 +1,5 @@ - + @@ -10,8 +10,8 @@ Meyer Lena - 12345 - 1112222444 + 44444 + 1112222888 diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000003.xml b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000003.xml similarity index 80% rename from ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000003.xml rename to ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000003.xml index 59cae735a..3f23e71bf 100644 --- a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_mods_00000003.xml +++ b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000003.xml @@ -1,5 +1,5 @@ - + @@ -10,8 +10,8 @@ Müller Luisa - 22222 - 444555666 + 99999 + 444555777 diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000010.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000010.xml new file mode 100644 index 000000000..b910c2212 --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000010.xml @@ -0,0 +1,17 @@ + + + + + + + + Müller + Adam + 98765 + 2222222333 + + + + + + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000011.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000011.xml new file mode 100644 index 000000000..442542a0f --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000011.xml @@ -0,0 +1,17 @@ + + + + + + + + Meyer + Gustav + 112233 + 2222222444 + + + + + + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000012.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000012.xml new file mode 100644 index 000000000..ae0ed6a9d --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000012.xml @@ -0,0 +1,17 @@ + + + + + + + + Müller + Gustavo + 77777 + 555555666 + + + + + + From 318fec8cd9035b3b7f5c59d7a4fd3dd5b5023303 Mon Sep 17 00:00:00 2001 From: Eva Roddeck Date: Wed, 16 Oct 2024 11:02:08 +0200 Subject: [PATCH 05/65] UBO-294 leadID instead of LSF-ID + new test --- .../MODSPersonLinkingEventHandler.java | 21 +++-- .../ubo/modsperson/MODSPersonLookup.java | 2 - .../MODSPersonLinkingEventHandlerTest.java | 89 +++++++++++++++++-- .../junit_mods_00000004.xml | 20 +++++ .../junit_mods_00000005.xml | 20 +++++ .../junit_mods_00000006.xml | 20 +++++ .../junit_modsperson_00000010.xml | 2 +- .../junit_modsperson_00000011.xml | 2 +- .../junit_modsperson_00000012.xml | 2 +- 9 files changed, 159 insertions(+), 19 deletions(-) create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000004.xml create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000005.xml create mode 100644 ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000006.xml diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java index 8d7fbff57..07b604226 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java @@ -10,8 +10,6 @@ import org.apache.logging.log4j.Logger; import org.jdom2.Document; import org.jdom2.Element; -import org.jdom2.output.Format; -import org.jdom2.output.XMLOutputter; import org.mycore.access.MCRAccessException; import org.mycore.common.MCRConstants; import org.mycore.common.MCRException; @@ -38,6 +36,8 @@ public class MODSPersonLinkingEventHandler extends MCREventHandlerBase { private final static Element PERSON_TEMPLATE; + private String leadIDName; + static { String projectID = MCRConfiguration2.getStringOrThrow("UBO.projectid.default"); String defaultID = MCRObjectID.formatID(projectID, "modsperson", 0); @@ -55,7 +55,11 @@ public class MODSPersonLinkingEventHandler extends MCREventHandlerBase { mycoreobject.addContent(new Element("service")); PERSON_TEMPLATE = mycoreobject; - System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(mycoreobject)); + } + + public MODSPersonLinkingEventHandler() { + super(); + this.leadIDName = MCRConfiguration2.getString("MCR.user2.matching.lead_id").orElse(""); } @Override @@ -82,11 +86,12 @@ protected boolean handleObject(MCRObject obj) { } MCRMODSWrapper wrapper = new MCRMODSWrapper(obj); + final String publicationId = obj.getId().toString(); wrapper.getElements("mods:name[@type='personal']").forEach(modsName -> { MCRObject person = getPersonReferencedIn(modsName); if (person == null) { - person = findPersonMatching(modsName); + person = findPersonMatching(modsName, publicationId); } boolean isNewPerson = false; @@ -135,7 +140,7 @@ private void setReferencedPerson(Element modsName, MCRObject person) { } } - private MCRObject findPersonMatching(Element modsName) { + private MCRObject findPersonMatching(Element modsName, String publicationID) { Set personElements = MODSPersonLookup.lookup(modsName); if (personElements == null || personElements.isEmpty()) { return null; @@ -145,15 +150,15 @@ private MCRObject findPersonMatching(Element modsName) { String allIDs = personElements.stream() .map(o -> o.getId().toString()).collect(Collectors.joining(", ")); - LOGGER.warn("There are multiple modsperson-objects matching the person in publication: " - + "["+ allIDs +"]. Chosing " + firstMatch.getId().toString()); + LOGGER.warn("There are multiple modsperson-objects matching the person in publication " + + publicationID + ": ["+ allIDs +"]. Chosing " + firstMatch.getId().toString() + "."); } return firstMatch; } private boolean leadIDExists(Element modsName) { return modsName.getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) - .stream().anyMatch(ni -> "lsf".equals(ni.getAttributeValue("type"))); // TODO: lsf generic? + .stream().anyMatch(ni -> leadIDName.equals(ni.getAttributeValue("type"))); } private MCRObject buildNewPerson() { diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java index 07cea704d..d71d942c7 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java @@ -72,8 +72,6 @@ public static Set lookup(Element modsName) { .getChild("name", MCRConstants.MODS_NAMESPACE); return savedElement == null || !hasSameNames(modsName, savedElement); }).count() == nameId2person.get(s).size()); - - // TODO: If there are multiple objects, we need the one that has most IDs in common with keys2lookup return keys2lookup.isEmpty() ? null : nameId2person.get(keys2lookup.iterator().next()); } diff --git a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java index 331537e95..a7b89c92f 100644 --- a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java +++ b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandlerTest.java @@ -12,28 +12,37 @@ import org.mycore.datamodel.metadata.MCRObject; import org.mycore.datamodel.metadata.MCRObjectID; import org.mycore.datamodel.metadata.MCRObjectMetadataTest; +import org.mycore.frontend.cli.MCRCommandUtils; import org.mycore.mods.MCRMODSWrapper; import java.io.IOException; import java.net.URL; import java.util.List; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; public class MODSPersonLinkingEventHandlerTest extends MCRStoreTestCase { + /** + * TODO + * @throws IOException in case of error + * @throws JDOMException in case of error + * @throws MCRAccessException in case of error + */ @Test - public void testHandleCreateWithoutLookup() throws IOException, JDOMException, MCRAccessException { - URL url1 = MCRObjectMetadataTest.class.getResource("/MODSPersonLinkingEventHandlerTest/junit_mods_00000001.xml"); + public void testHandleCreate() throws IOException, JDOMException, MCRAccessException { + URL url1 = MCRObjectMetadataTest.class.getResource( + "/MODSPersonLinkingEventHandlerTest/junit_mods_00000001.xml"); Document doc1 = new MCRURLContent(url1).asXML(); MCRObject obj1 = new MCRObject(doc1); - URL url2 = MCRObjectMetadataTest.class.getResource("/MODSPersonLinkingEventHandlerTest/junit_mods_00000002.xml"); + URL url2 = MCRObjectMetadataTest.class.getResource( + "/MODSPersonLinkingEventHandlerTest/junit_mods_00000002.xml"); Document doc2 = new MCRURLContent(url2).asXML(); MCRObject obj2 = new MCRObject(doc2); - URL url3 = MCRObjectMetadataTest.class.getResource("/MODSPersonLinkingEventHandlerTest/junit_mods_00000003.xml"); + URL url3 = MCRObjectMetadataTest.class.getResource( + "/MODSPersonLinkingEventHandlerTest/junit_mods_00000003.xml"); Document doc3 = new MCRURLContent(url3).asXML(); MCRObject obj3 = new MCRObject(doc3); @@ -57,6 +66,74 @@ public void testHandleCreateWithoutLookup() throws IOException, JDOMException, M assertPerson(person6, "Müller", "Luisa", "99999", "444555777"); } + /** + * Tests the case that a lookup returns multiple person-objects. This needs a constellation of test data + * that is unlikely to occur in production. + * @throws IOException in case of error + * @throws JDOMException in case of error + * @throws MCRAccessException in case of error + */ + @Test + public void testHandleCreateDuplicatePerson() throws IOException, JDOMException, MCRAccessException { + URL url1 = MCRObjectMetadataTest.class.getResource( + "/MODSPersonLinkingEventHandlerTest/junit_mods_00000004.xml"); + Document doc1 = new MCRURLContent(url1).asXML(); + MCRObject obj1 = new MCRObject(doc1); + + URL url2 = MCRObjectMetadataTest.class.getResource( + "/MODSPersonLinkingEventHandlerTest/junit_mods_00000005.xml"); + Document doc2 = new MCRURLContent(url2).asXML(); + MCRObject obj2 = new MCRObject(doc2); + + URL url3 = MCRObjectMetadataTest.class.getResource( + "/MODSPersonLinkingEventHandlerTest/junit_mods_00000006.xml"); + Document doc3 = new MCRURLContent(url3).asXML(); + MCRObject obj3 = new MCRObject(doc3); + + MCRMetadataManager.create(obj1); + MCRMetadataManager.create(obj2); + MCRMetadataManager.create(obj3); + + List modspersons = MCRCommandUtils.getIdsForType("modsperson").toList(); + assertEquals(2, modspersons.size()); + + MCRObject modsperson1 = MCRMetadataManager.retrieveMCRObject(MCRObjectID.getInstance(modspersons.get(0))); + MCRMODSWrapper wrapper = new MCRMODSWrapper(modsperson1); + List lsfs = wrapper.getMODS().getChild("name", MCRConstants.MODS_NAMESPACE) + .getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) + .stream() + .filter(ni -> "lsf".equals(ni.getAttributeValue("type"))) + .map(Element::getText).toList(); + + if (lsfs.size() == 1) { + assertTrue(lsfs.get(0).equals("77766") || lsfs.get(0).equals("10101")); + } + else if (lsfs.size() == 2) { + assertTrue(lsfs.get(0).equals("77788") || lsfs.get(1).equals("77788")); + } + else { + fail(modsperson1.getId().toString() + " is expected to have either one or two LSF-IDs"); + } + + MCRObject modsperson2 = MCRMetadataManager.retrieveMCRObject(MCRObjectID.getInstance(modspersons.get(1))); + wrapper = new MCRMODSWrapper(modsperson2); + lsfs = wrapper.getMODS().getChild("name", MCRConstants.MODS_NAMESPACE) + .getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) + .stream() + .filter(ni -> "lsf".equals(ni.getAttributeValue("type"))) + .map(Element::getText).toList(); + + if (lsfs.size() == 1) { + assertTrue(lsfs.get(0).equals("77766") || lsfs.get(0).equals("10101")); + } + else if (lsfs.size() == 2) { + assertTrue(lsfs.get(0).equals("77788") || lsfs.get(1).equals("77788")); + } + else { + fail(modsperson2.getId().toString() + " is expected to have either one or two LSF-IDs"); + } + } + /** * Assert that the Object contains the two name parts and an LSF and SCOPUS ID * @param person tested {@link MCRObject} diff --git a/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000004.xml b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000004.xml new file mode 100644 index 000000000..b9668abf9 --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000004.xml @@ -0,0 +1,20 @@ + + + + + + + + This is a test-publication + + + Müller + Adam + 77766 + 444000777 + + + + + + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000005.xml b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000005.xml new file mode 100644 index 000000000..3e2100dae --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000005.xml @@ -0,0 +1,20 @@ + + + + + + + + This is a test-publication + + + Anders + Peter + 10101 + 444000777 + + + + + + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000006.xml b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000006.xml new file mode 100644 index 000000000..be2158b24 --- /dev/null +++ b/ubo-modsperson/src/test/resources/MODSPersonLinkingEventHandlerTest/junit_mods_00000006.xml @@ -0,0 +1,20 @@ + + + + + + + + This is a test-publication + + + Müller + Adam + 77788 + 444000777 + + + + + + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000010.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000010.xml index b910c2212..bd78e9644 100644 --- a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000010.xml +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000010.xml @@ -1,5 +1,5 @@ - + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000011.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000011.xml index 442542a0f..eaba1c6ca 100644 --- a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000011.xml +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000011.xml @@ -1,5 +1,5 @@ - + diff --git a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000012.xml b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000012.xml index ae0ed6a9d..5f83843c4 100644 --- a/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000012.xml +++ b/ubo-modsperson/src/test/resources/MODSPersonLookupTest/junit_modsperson_00000012.xml @@ -1,5 +1,5 @@ - + From 645bc1209d4b243720cab15fdcf91a0ea771d17b Mon Sep 17 00:00:00 2001 From: Eva Roddeck Date: Tue, 22 Oct 2024 13:41:58 +0200 Subject: [PATCH 06/65] UBO-294 adapted caching object --- .../MODSPersonLinkingEventHandler.java | 16 +-- .../ubo/modsperson/MODSPersonLookup.java | 127 +++++++++++++++--- .../ubo/modsperson/MODSPersonLookupTest.java | 31 +++-- 3 files changed, 136 insertions(+), 38 deletions(-) diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java index 07b604226..f6a45d4bc 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLinkingEventHandler.java @@ -141,19 +141,19 @@ private void setReferencedPerson(Element modsName, MCRObject person) { } private MCRObject findPersonMatching(Element modsName, String publicationID) { - Set personElements = MODSPersonLookup.lookup(modsName); - if (personElements == null || personElements.isEmpty()) { + Set cachedPersons = MODSPersonLookup.lookup(modsName); + if (cachedPersons == null || cachedPersons.isEmpty()) { return null; } - MCRObject firstMatch = personElements.iterator().next(); - if (personElements.size() > 1) { - String allIDs = personElements.stream() - .map(o -> o.getId().toString()).collect(Collectors.joining(", ")); + MODSPersonLookup.PersonCache firstMatch = cachedPersons.iterator().next(); + if (cachedPersons.size() > 1) { + String allIDs = cachedPersons.stream() + .map(o -> o.getPersonmodsId().toString()).collect(Collectors.joining(", ")); LOGGER.warn("There are multiple modsperson-objects matching the person in publication " - + publicationID + ": ["+ allIDs +"]. Chosing " + firstMatch.getId().toString() + "."); + + publicationID + ": ["+ allIDs +"]. Chosing " + firstMatch.getPersonmodsId().toString() + "."); } - return firstMatch; + return MCRMetadataManager.retrieveMCRObject(firstMatch.getPersonmodsId()); } private boolean leadIDExists(Element modsName) { diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java index d71d942c7..9541048f4 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java @@ -1,5 +1,6 @@ package org.mycore.ubo.modsperson; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -7,25 +8,28 @@ import java.util.Set; import java.util.stream.Collectors; +import org.apache.commons.compress.utils.Lists; import org.jdom2.Element; import org.mycore.common.MCRConstants; import org.mycore.datamodel.metadata.MCRObject; +import org.mycore.datamodel.metadata.MCRObjectID; import org.mycore.mods.MCRMODSWrapper; import org.mycore.mods.merger.MCRMerger; import org.mycore.mods.merger.MCRMergerFactory; public class MODSPersonLookup { - private static Map> nameId2person = new HashMap<>(); + private static Map> nameId2person = new HashMap<>(); - private static Map id2person = new HashMap<>(); + private static Map id2person = new HashMap<>(); public static void add(MCRObject person) { Element mods = new MCRMODSWrapper(person).getMODS().clone(); - id2person.put(person.getId().toString(), person); + PersonCache personCache = PersonCache.newPersonCacheFromMCRObject(person); + id2person.put(person.getId().toString(), personCache); getNameIdentifiers(mods).forEach(nameID -> { String key = buildKey(nameID); - nameId2person.computeIfAbsent(key, k -> new HashSet<>()).add(person); + nameId2person.computeIfAbsent(key, k -> new HashSet<>()).add(personCache); }); } @@ -35,9 +39,9 @@ public static void remove(MCRObject person) { getNameIdentifiers(mods).forEach(nameID -> { String key = buildKey(nameID); - Set personSet = nameId2person.get(key); + Set personSet = nameId2person.get(key); if (personSet != null) { - personSet.removeIf(testedPerson -> testedPerson.getId().equals(person.getId())); + personSet.removeIf(testedPerson -> testedPerson.getPersonmodsId().equals(person.getId())); if (personSet.isEmpty()) { nameId2person.remove(key); } @@ -51,8 +55,7 @@ public static void update(MCRObject person) { } private static String buildKey(Element nameIdentifier) { - String key = String.join("|", nameIdentifier.getTextTrim(), nameIdentifier.getAttributeValue("type")); - return key; + return String.join("|", nameIdentifier.getTextTrim(), nameIdentifier.getAttributeValue("type")); } private static List getNameIdentifiers(Element mods) { @@ -60,25 +63,113 @@ private static List getNameIdentifiers(Element mods) { MCRConstants.MODS_NAMESPACE); } - public static Set lookup(Element modsName) { + public static Set lookup(Element modsName) { Set keys2lookup = modsName.getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) .stream().map(MODSPersonLookup::buildKey).collect(Collectors.toSet()); keys2lookup.retainAll(nameId2person.keySet()); - keys2lookup.removeIf(s -> nameId2person.get(s) == null || nameId2person.get(s).stream() - .filter(obj -> { - Element savedElement = new MCRMODSWrapper(obj).getMODS() - .getChild("name", MCRConstants.MODS_NAMESPACE); - return savedElement == null || !hasSameNames(modsName, savedElement); - }).count() == nameId2person.get(s).size()); + keys2lookup.removeIf(key -> nameId2person.get(key) == null || nameId2person.get(key).stream() + .filter(savedPerson -> savedPerson == null || !hasSameNames(modsName, savedPerson)) + .count() == nameId2person.get(key).size()); return keys2lookup.isEmpty() ? null : nameId2person.get(keys2lookup.iterator().next()); } - private static boolean hasSameNames(Element element1, Element element2) { - MCRMerger merger1 = MCRMergerFactory.buildFrom(element1); - MCRMerger merger2 = MCRMergerFactory.buildFrom(element2); + private static boolean hasSameNames(Element elementToCompare, PersonCache cache) { + Element savedElement = new Element("name", MCRConstants.MODS_NAMESPACE); + savedElement.setAttribute("type", "personal"); + + Element familyNameElement = new Element("namePart", MCRConstants.MODS_NAMESPACE); + familyNameElement.setAttribute("type", "family"); + familyNameElement.setText(cache.getFamilyName()); + + Element givenNameElement = new Element("namePart", MCRConstants.MODS_NAMESPACE); + givenNameElement.setAttribute("type", "given"); + givenNameElement.setText(cache.getGivenName()); + + savedElement.setContent(Arrays.asList(familyNameElement,givenNameElement)); + + MCRMerger merger1 = MCRMergerFactory.buildFrom(elementToCompare); + MCRMerger merger2 = MCRMergerFactory.buildFrom(savedElement); return merger1.isProbablySameAs(merger2); } + public static class PersonCache { + + public PersonCache(MCRObjectID personmodsId, String familyName, String givenName, Set keys) { + this.personmodsId = personmodsId; + this.familyName = familyName; + this.givenName = givenName; + this.keys = keys; + } + + private MCRObjectID personmodsId; + private String familyName; + private String givenName; + private Set keys; + + public static PersonCache newPersonCacheFromMCRObject(MCRObject obj) { + MCRMODSWrapper wrapper = new MCRMODSWrapper(obj); + return newPersonCacheFromModsNameElement(wrapper.getMODS(), obj.getId()); + } + + public static PersonCache newPersonCacheFromModsNameElement(Element modsName, MCRObjectID personmodsId) { + List familyNames = modsName.getChild("name", MCRConstants.MODS_NAMESPACE).getChildren() + .stream().filter(e -> "family".equals(e.getAttributeValue("type"))).toList(); + String familyNameString = ""; + if (!familyNames.isEmpty()) { + familyNameString = familyNames.get(0).getText(); + } + + List givenNames = modsName.getChild("name", MCRConstants.MODS_NAMESPACE).getChildren() + .stream().filter(e -> "given".equals(e.getAttributeValue("type"))).toList(); + String givenNameString = ""; + if (!givenNames.isEmpty()) { + givenNameString = givenNames.get(0).getText(); + } + + Set allKeys = modsName.getChild("name", MCRConstants.MODS_NAMESPACE) + .getChildren("nameIdentifier", MCRConstants.MODS_NAMESPACE) + .stream().map(MODSPersonLookup::buildKey).collect(Collectors.toSet()); + + return new PersonCache(personmodsId, familyNameString, givenNameString, allKeys); + } + + public MCRObjectID getPersonmodsId() { + return personmodsId; + } + + public void setPersonmodsId(MCRObjectID personmodsId) { + this.personmodsId = personmodsId; + } + + public String getFamilyName() { + return familyName; + } + + public void setFamilyName(String familyName) { + this.familyName = familyName; + } + + public String getGivenName() { + return givenName; + } + + public void setGivenName(String givenName) { + this.givenName = givenName; + } + + public Set getKeys() { + return keys; + } + + public void setKeys(Set keys) { + this.keys = keys; + } + + public void addKey(String key) { + this.keys.add(key); + } + } + } diff --git a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java index 8225c1e86..39154f9de 100644 --- a/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java +++ b/ubo-modsperson/src/test/java/org/mycore/ubo/modsperson/MODSPersonLookupTest.java @@ -42,27 +42,34 @@ public void testLookup() throws IOException, JDOMException { MCRMODSWrapper wrapper = new MCRMODSWrapper(obj1); Element name1 = wrapper.getElement("mods:name[@type='personal']"); - - MCRObject person1 = Objects.requireNonNull(MODSPersonLookup.lookup(name1)).iterator().next(); - Element person1Element = new MCRMODSWrapper(person1).getMODS(); - System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(person1Element)); - - assertPersonElement(person1Element, "Müller", "Adam", "98765", "2222222333"); + MODSPersonLookup.PersonCache person1 = Objects.requireNonNull(MODSPersonLookup.lookup(name1)).iterator().next(); + assertPerson(person1,"Müller", "Adam", "98765", "2222222333"); wrapper = new MCRMODSWrapper(obj2); Element name2 = wrapper.getElement("mods:name[@type='personal']"); - MCRObject person2 = Objects.requireNonNull(MODSPersonLookup.lookup(name2)).iterator().next(); - Element person2Element = new MCRMODSWrapper(person2).getMODS(); - System.out.println(new XMLOutputter(Format.getPrettyFormat()).outputString(person2Element)); - - assertPersonElement(person2Element, "Meyer", "Gustav", "112233", "2222222444"); + MODSPersonLookup.PersonCache person2 = Objects.requireNonNull(MODSPersonLookup.lookup(name2)).iterator().next(); + assertPerson(person2, "Meyer", "Gustav", "112233", "2222222444"); wrapper = new MCRMODSWrapper(obj3); Element name3 = wrapper.getElement("mods:name[@type='personal']"); - Set persons3 = MODSPersonLookup.lookup(name3); + Set persons3 = MODSPersonLookup.lookup(name3); assertNull(persons3); } + private void assertPerson(MODSPersonLookup.PersonCache assertPerson, String familyName, + String givenName, String lsfId, String scopusId) { + assertEquals(familyName, assertPerson.getFamilyName()); + assertEquals(givenName, assertPerson.getGivenName()); + assertEquals(2, assertPerson.getKeys().size()); + + String lsfIdKey = String.join("|", lsfId, "lsf"); + String scopusIdKey = String.join("|", scopusId, "scopus"); + + assertPerson.getKeys().forEach(key -> { + assertTrue(key.equals(lsfIdKey) || key.equals(scopusIdKey)); + }); + } + /** * Assert that the Element contains the two name parts and an LSF and SCOPUS ID * @param person tested Element From 0af04ef02df04e1b726607b2f620b5282d2176d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20L=C3=BCtzenkirchen?= Date: Thu, 24 Oct 2024 10:05:48 +0200 Subject: [PATCH 07/65] UBO-294 intermediate solution to index modsperson names and identifiers --- .../src/main/resources/xsl/ubo-solr.xsl | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ubo-common/src/main/resources/xsl/ubo-solr.xsl b/ubo-common/src/main/resources/xsl/ubo-solr.xsl index cb868c3cc..b9dd18ada 100644 --- a/ubo-common/src/main/resources/xsl/ubo-solr.xsl +++ b/ubo-common/src/main/resources/xsl/ubo-solr.xsl @@ -14,7 +14,7 @@ - + @@ -26,6 +26,26 @@ + + + + + + + + + + + + + + + + + + + + From b67e01ccd411ee870e03992efaad28fecea6d358 Mon Sep 17 00:00:00 2001 From: Eva Roddeck Date: Fri, 25 Oct 2024 14:24:11 +0200 Subject: [PATCH 08/65] UBO-294 Loading Startup Cache through Solr --- ubo-modsperson/pom.xml | 4 + .../MODSPersonCachingStartupHandler.java | 87 +++++++++++++++++-- .../ubo/modsperson/MODSPersonLookup.java | 7 ++ 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/ubo-modsperson/pom.xml b/ubo-modsperson/pom.xml index 9fd74d17c..807d97964 100644 --- a/ubo-modsperson/pom.xml +++ b/ubo-modsperson/pom.xml @@ -71,6 +71,10 @@ org.mycore mycore-mods + + org.mycore + mycore-solr + com.h2database h2 diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java index 0bf2592c3..be2b513af 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java @@ -2,16 +2,23 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.apache.solr.client.solrj.SolrQuery; +import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.client.solrj.response.QueryResponse; +import org.apache.solr.common.SolrDocument; +import org.apache.solr.common.SolrDocumentList; import org.mycore.common.events.MCRStartupHandler; -import org.mycore.datamodel.metadata.MCRMetadataManager; -import org.mycore.datamodel.metadata.MCRObject; import org.mycore.datamodel.metadata.MCRObjectID; -import org.mycore.frontend.cli.MCRCommandUtils; +import org.mycore.solr.MCRSolrClientFactory; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; /** - * Loads all mpdsperson data into {@link MODSPersonLookup} on startup. + * Loads all modsperson data into {@link MODSPersonLookup} on startup. */ public class MODSPersonCachingStartupHandler implements MCRStartupHandler.AutoExecutable { @@ -27,14 +34,76 @@ public int getPriority() { return 0; } + @Override public void startUp(jakarta.servlet.ServletContext servletContext) { AtomicInteger counter = new AtomicInteger(0); - MCRCommandUtils.getIdsForType("modsperson").forEach(idString -> { - MCRObject obj = MCRMetadataManager.retrieveMCRObject(MCRObjectID.getInstance(idString)); - MODSPersonLookup.add(obj); - counter.incrementAndGet(); - }); + int offset = 0; + int blockSize = 1000; + + final SolrQuery query = new SolrQuery("objectType:modsperson") + .setFields("*").setRows(blockSize).setParam("wt", "json"); + + try { + int resultsFound = fetchAndProcessResults(query, offset, counter); + if (resultsFound > blockSize) { + for (int i = 0; i < resultsFound / blockSize; i++) { + offset += 1000; + fetchAndProcessResults(query, offset, counter); + } + } + } catch (SolrServerException | IOException e) { + LOGGER.error(e.getMessage()); + throw new RuntimeException(e); + } LOGGER.info("Loaded " + counter.get() + " modsperson-objects into the cache"); } + + private int fetchAndProcessResults(SolrQuery query, int offset, AtomicInteger counter) + throws SolrServerException, IOException { + query.setStart(offset); + + QueryResponse queryResponse = MCRSolrClientFactory.getMainSolrClient().query(query); + SolrDocumentList results = queryResponse.getResults(); + + for (SolrDocument doc : results) { + addSolrDocToLookup(doc, counter); + } + + return (int) results.getNumFound(); + } + + private void addSolrDocToLookup(SolrDocument doc, AtomicInteger counter) { + try { + MCRObjectID personmodsId = MCRObjectID.getInstance((String) doc.getFieldValue("id")); + String familyName; + String givenName; + Set keys = new HashSet<>(); + String personName = (String) doc.getFieldValue("name"); + if (personName.contains(",")) { + String[] parts = personName.split(","); + familyName = parts[0].trim(); + givenName = parts[1].trim(); + } + else { + return; + } + for (String fieldName : doc.getFieldNames()) { + if (fieldName.startsWith("nid_") && !fieldName.equals("nid_connection")) { + List fieldValues = (List) doc.getFieldValue(fieldName); + for (String fieldValue : fieldValues) { + String idType = fieldName.substring(4); + keys.add(String.join("|", idType.trim(), fieldValue.trim())); + } + + } + } + MODSPersonLookup.add(new MODSPersonLookup.PersonCache(personmodsId, familyName, givenName, keys)); + counter.incrementAndGet(); + } catch (ClassCastException | NullPointerException | IndexOutOfBoundsException ex) { + // parsing a single faulty SolrDocument shouldn't interrupt the whole startup + LOGGER.warn("Error while loading modsperson " + doc.get("id") + " into cache", ex); + } + } + } diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java index 9541048f4..c004832fc 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonLookup.java @@ -33,6 +33,13 @@ public static void add(MCRObject person) { }); } + public static void add(PersonCache personCache) { + id2person.put(personCache.getPersonmodsId().toString(), personCache); + personCache.getKeys().forEach(key -> { + nameId2person.computeIfAbsent(key, k -> new HashSet<>()).add(personCache); + }); + } + public static void remove(MCRObject person) { Element mods = new MCRMODSWrapper(person).getMODS().clone(); id2person.remove(person.getId().toString()); From df1d8c7107a253b27b2736e62fafe9855e9a85e6 Mon Sep 17 00:00:00 2001 From: Eva Roddeck Date: Fri, 25 Oct 2024 14:30:28 +0200 Subject: [PATCH 09/65] UBO-294 add license file --- ubo-modsperson/LICENSE.txt | 674 +++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 ubo-modsperson/LICENSE.txt diff --git a/ubo-modsperson/LICENSE.txt b/ubo-modsperson/LICENSE.txt new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/ubo-modsperson/LICENSE.txt @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 7b81627cb67fc9fdd9f43f6d5a59103ae27f1cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20L=C3=BCtzenkirchen?= Date: Tue, 29 Oct 2024 16:34:17 +0100 Subject: [PATCH 10/65] UBO-295 Separate modsperson indexing and fixed repeated given names --- .../src/main/resources/xsl/ubo-solr.xsl | 22 +--------- .../config/ubo-modsperson/mycore.properties | 6 +++ .../main/resources/xsl/modsperson-solr.xsl | 43 +++++++++++++++++++ 3 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 ubo-modsperson/src/main/resources/xsl/modsperson-solr.xsl diff --git a/ubo-common/src/main/resources/xsl/ubo-solr.xsl b/ubo-common/src/main/resources/xsl/ubo-solr.xsl index b9dd18ada..d15a3bcf1 100644 --- a/ubo-common/src/main/resources/xsl/ubo-solr.xsl +++ b/ubo-common/src/main/resources/xsl/ubo-solr.xsl @@ -12,7 +12,7 @@ - + @@ -26,26 +26,6 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties index e0621de3e..27cc24f1c 100644 --- a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties +++ b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties @@ -1,5 +1,11 @@ +# Register the metadata type "modsperson" MCR.Metadata.Type.modsperson=true +# Event handler to collect and link person data in separate modsperson objects MCR.EventHandler.MCRObject.022.Class=org.mycore.ubo.modsperson.MODSPersonLinkingEventHandler + +# Startup handler to initially fill the cache of all known person data MCR.Startup.Class=%MCR.Startup.Class%, org.mycore.ubo.modsperson.MODSPersonCachingStartupHandler +# Extend SOLR indexing to also index modsperson objects with custom fields +MCR.URIResolver.xslImports.solr-document=%MCR.URIResolver.xslImports.solr-document%,modsperson-solr.xsl diff --git a/ubo-modsperson/src/main/resources/xsl/modsperson-solr.xsl b/ubo-modsperson/src/main/resources/xsl/modsperson-solr.xsl new file mode 100644 index 000000000..2a916c1d3 --- /dev/null +++ b/ubo-modsperson/src/main/resources/xsl/modsperson-solr.xsl @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + From 22012119b6b8665f56d5ca391b7027dae7cd86ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frank=20L=C3=BCtzenkirchen?= Date: Tue, 29 Oct 2024 18:45:41 +0100 Subject: [PATCH 11/65] UBO-294 Added details page display for modsperson object --- .../src/main/resources/xsl/mods-display.xsl | 48 +++++++------ .../src/main/resources/xsl/mycoreobject.xsl | 34 ++++++--- .../config/ubo-modsperson/mycore.properties | 3 + .../resources/xsl/mycoreobject-modsperson.xsl | 72 +++++++++++++++++++ 4 files changed, 126 insertions(+), 31 deletions(-) create mode 100644 ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson.xsl diff --git a/ubo-common/src/main/resources/xsl/mods-display.xsl b/ubo-common/src/main/resources/xsl/mods-display.xsl index e886b7511..fd471fa67 100644 --- a/ubo-common/src/main/resources/xsl/mods-display.xsl +++ b/ubo-common/src/main/resources/xsl/mods-display.xsl @@ -670,26 +670,9 @@
- - - - - - - ORCID iD - - - - - - - - - - - - - + + +
@@ -712,6 +695,31 @@
+ + + + + + + + + + + ORCID iD + + + + + + + + + + + + + + diff --git a/ubo-common/src/main/resources/xsl/mycoreobject.xsl b/ubo-common/src/main/resources/xsl/mycoreobject.xsl index 06c86e6a5..5384266fb 100644 --- a/ubo-common/src/main/resources/xsl/mycoreobject.xsl +++ b/ubo-common/src/main/resources/xsl/mycoreobject.xsl @@ -23,6 +23,8 @@ + + @@ -37,6 +39,11 @@ + <xsl:apply-templates select="/mycoreobject" mode="pageTitle" /> + + + + @@ -45,7 +52,6 @@ - @@ -57,12 +63,14 @@ - - - - - + + + +
+ +

+ +

+
+ +
+ + + + + + + + + + + + + + + + +
NameLSF-IDScopus-IDOrcid-IDGND-IDResearcher-ID
+ +
+
+
+
+
+ + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ubo-common/src/main/resources/xsl/response.xsl b/ubo-common/src/main/resources/xsl/response.xsl index 6dc56a259..6d16c8f70 100644 --- a/ubo-common/src/main/resources/xsl/response.xsl +++ b/ubo-common/src/main/resources/xsl/response.xsl @@ -21,6 +21,7 @@ + @@ -53,6 +54,7 @@
+ @@ -136,7 +138,14 @@ - + + + + + + + + diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java index b91238c61..c097eac98 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java @@ -82,7 +82,7 @@ private void addSolrDocToLookup(SolrDocument doc, AtomicInteger counter) { String givenName; Set keys = new HashSet<>(); String personName = (String) doc.getFieldValue("name"); - if (personName.contains(",")) { + if (personName != null && personName.contains(",")) { String[] parts = personName.split(","); familyName = parts[0].trim(); givenName = parts[1].trim(); diff --git a/ubo-modsperson/src/main/resources/META-INF/resources/edit-person.xed b/ubo-modsperson/src/main/resources/META-INF/resources/edit-person.xed new file mode 100644 index 000000000..f680f9472 --- /dev/null +++ b/ubo-modsperson/src/main/resources/META-INF/resources/edit-person.xed @@ -0,0 +1,147 @@ + + + + Person erfassen + Acquire person + +
+
+

+ Die mit * gekennzeichneten Felder sind Pflichtfelder. + Durch klicken auf [+] können Sie ein Eingabefeld wiederholen! +

+
+
+
+
+

+ Fields marked with * are required fields. + By clicking on [+] you can repeat an input field! +

+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + +
+
+ + +
+ + +
+
+ + +
+ + + + + + insert remove +
+
+ + +
+ +
+ + +
+
+ + +
+ + +
+
+ insert remove +
+ +
+ + +
+ + + insert remove +
+
+ +
+ +
+ + + + +
+ +
+
+
+
diff --git a/ubo-modsperson/src/main/resources/xsl/modsperson-solr.xsl b/ubo-modsperson/src/main/resources/xsl/modsperson-solr.xsl index 61338652d..c23581260 100644 --- a/ubo-modsperson/src/main/resources/xsl/modsperson-solr.xsl +++ b/ubo-modsperson/src/main/resources/xsl/modsperson-solr.xsl @@ -24,7 +24,7 @@
- + , diff --git a/ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson.xsl b/ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson.xsl index 1e64fb783..30a0d1dbe 100644 --- a/ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson.xsl +++ b/ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson.xsl @@ -5,68 +5,117 @@ - - - - - - - - + + + + + + + + + + - - Person Nr. - - - - + + diff --git a/ubo-common/src/main/vue/dedup/src/router/index.ts b/ubo-common/src/main/vue/dedup/src/router/index.ts index f669cc5b3..c66e09630 100644 --- a/ubo-common/src/main/vue/dedup/src/router/index.ts +++ b/ubo-common/src/main/vue/dedup/src/router/index.ts @@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router' import DedupListView from '../views/DedupListView.vue' import {getWebApplicationBaseURL} from "@/api/Utils.ts"; import NoDuplicateListView from "@/views/NoDuplicateListView.vue"; +import DedupPersonListView from "@/views/DedupPersonListView.vue"; export function getContext(): string { if (import.meta.env.DEV) { @@ -24,6 +25,11 @@ const router = createRouter({ path: '/no-duplicates', name: 'noDuplicates', component: NoDuplicateListView + }, + { + path: '/person-duplicates', + name: 'personDuplicates', + component: DedupPersonListView } ], }) diff --git a/ubo-common/src/main/vue/dedup/src/views/DedupPersonListView.vue b/ubo-common/src/main/vue/dedup/src/views/DedupPersonListView.vue new file mode 100644 index 000000000..1da8d0152 --- /dev/null +++ b/ubo-common/src/main/vue/dedup/src/views/DedupPersonListView.vue @@ -0,0 +1,171 @@ + + + + + + diff --git a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties index 86a6d5103..f25bf8792 100644 --- a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties +++ b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties @@ -21,3 +21,9 @@ MCR.MODS.Merger.name=org.mycore.ubo.modsperson.merger.MCRNameMerger MCR.MODS.Merger.alternativeName=org.mycore.ubo.modsperson.merger.MCRNameMerger +MCR.ContentTransformer.modsperson-json-normal.Class=org.mycore.common.content.transformer.MCRXsonifyTransformer +MCR.ContentTransformer.modsperson-json-normal.Schema=datamodel-mods.xsd +MCR.ContentTransformer.modsperson-json-normal.Settings.Class=org.mycore.common.content.transformer.MCRXsonifyTransformer$SettingsBuilder +MCR.ContentTransformer.modsperson-json-normal.Style.Class=org.mycore.common.content.transformer.MCRXsonifyTransformer$StyleBuilder +MCR.ContentTransformer.modsperson-json-normal.Style.TextKey=text + From e6fd36aefaac6ef3392f7c1965b6e54a22d24c21 Mon Sep 17 00:00:00 2001 From: Eva Roddeck Date: Fri, 11 Apr 2025 14:34:52 +0200 Subject: [PATCH 25/65] UBO-376 null save name criteria --- .../ubo/dedup/DeDupCriteriaBuilder.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupCriteriaBuilder.java b/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupCriteriaBuilder.java index caf6359fa..2eb851c93 100644 --- a/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupCriteriaBuilder.java +++ b/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupCriteriaBuilder.java @@ -138,14 +138,22 @@ public Set buildFromMODSPerson(Element modsperson) { } for (Element name : getNodes(modsperson, "mods:name[@type='personal']")) { - Element familyName = getNodes(name, "mods:namePart[@type='family']").get(0); - Element givenName = getNodes(name, "mods:namePart[@type='given']").get(0); - criteria.add(buildFromFullName(givenName, familyName)); + Element familyName = getNodes(name, "mods:namePart[@type='family']") == null ? + null : getNodes(name, "mods:namePart[@type='family']").get(0); + Element givenName = getNodes(name, "mods:namePart[@type='given']") == null ? + null : getNodes(name, "mods:namePart[@type='given']").get(0); + if (givenName != null && familyName != null) { + criteria.add(buildFromFullName(givenName, familyName)); + } for (Element alternativeName : getNodes(modsperson, "mods:alternativeName")) { - Element altFamilyName = getNodes(alternativeName, "mods:namePart[@type='family']").get(0); - Element altGivenName = getNodes(alternativeName, "mods:namePart[@type='given']").get(0); - criteria.add(buildFromFullName(altGivenName, altFamilyName)); + Element altFamilyName = getNodes(alternativeName, "mods:namePart[@type='family']") == null ? + null : getNodes(alternativeName, "mods:namePart[@type='family']").get(0); + Element altGivenName = getNodes(alternativeName, "mods:namePart[@type='given']") == null ? + null : getNodes(alternativeName, "mods:namePart[@type='given']").get(0); + if (altGivenName != null && altFamilyName != null) { + criteria.add(buildFromFullName(altGivenName, altFamilyName)); + } } } From 7e3e8e0e07ddafc1eab58d3801b1475d5144a899 Mon Sep 17 00:00:00 2001 From: Eva Roddeck Date: Wed, 23 Apr 2025 10:21:59 +0200 Subject: [PATCH 26/65] UBO-376 modsperson-structure --- .../ubo/dedup/DeDupCriteriaBuilder.java | 4 +- .../jpa/DeduplicationCriterionResolver.java | 10 +- .../config/ubo-common/messages_de.properties | 1 + .../config/ubo-common/messages_en.properties | 1 + .../src/main/resources/xsl/mods-display.xsl | 18 + .../dedup/src/components/PersonDisplay.vue | 2 +- .../MODSPersonCachingStartupHandler.java | 2 +- .../config/ubo-modsperson/mycore.properties | 3 + .../xsl/mycoreobject-modsperson-structure.xsl | 348 ++++++++++++++++++ 9 files changed, 383 insertions(+), 6 deletions(-) create mode 100644 ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson-structure.xsl diff --git a/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupCriteriaBuilder.java b/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupCriteriaBuilder.java index 2eb851c93..fe9229ff5 100644 --- a/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupCriteriaBuilder.java +++ b/ubo-common/src/main/java/org/mycore/ubo/dedup/DeDupCriteriaBuilder.java @@ -138,9 +138,9 @@ public Set buildFromMODSPerson(Element modsperson) { } for (Element name : getNodes(modsperson, "mods:name[@type='personal']")) { - Element familyName = getNodes(name, "mods:namePart[@type='family']") == null ? + Element familyName = getNodes(name, "mods:namePart[@type='family']").isEmpty() ? null : getNodes(name, "mods:namePart[@type='family']").get(0); - Element givenName = getNodes(name, "mods:namePart[@type='given']") == null ? + Element givenName = getNodes(name, "mods:namePart[@type='given']").isEmpty() ? null : getNodes(name, "mods:namePart[@type='given']").get(0); if (givenName != null && familyName != null) { criteria.add(buildFromFullName(givenName, familyName)); diff --git a/ubo-common/src/main/java/org/mycore/ubo/dedup/jpa/DeduplicationCriterionResolver.java b/ubo-common/src/main/java/org/mycore/ubo/dedup/jpa/DeduplicationCriterionResolver.java index 872a39807..ec32d2188 100644 --- a/ubo-common/src/main/java/org/mycore/ubo/dedup/jpa/DeduplicationCriterionResolver.java +++ b/ubo-common/src/main/java/org/mycore/ubo/dedup/jpa/DeduplicationCriterionResolver.java @@ -54,7 +54,8 @@ public Source resolve(String href, String base) throws TransformerException { } String relation = parts[2]; - if(!Objects.equals("base", relation) && Objects.equals("parent", relation) && Objects.equals("host", relation)) { + if(!(Objects.equals("base", relation) || Objects.equals("parent", relation) + || Objects.equals("host", relation) || Objects.equals("person", relation))) { LOGGER.error("Unknown relation: {} in {}", relation, href); return null; } @@ -82,7 +83,7 @@ public Source resolve(String href, String base) throws TransformerException { criteria.forEach(criterion -> { possibleDuplicates.addAll(DeduplicationKeyManager.getInstance().getDuplicates(id.toString(), criterion.getType(), criterion.getKey())); }); - } else { + } else if (Objects.equals("host", relation)) { for (Element host : deDupCriteriaBuilder.getNodes(mods, "mods:relatedItem[@type='host']")) { String externalID = host.getAttributeValue("href", MCRConstants.XLINK_NAMESPACE); if(externalID != null) { @@ -93,6 +94,11 @@ public Source resolve(String href, String base) throws TransformerException { possibleDuplicates.addAll(DeduplicationKeyManager.getInstance().getDuplicates(id.toString(), criterion.getType(), criterion.getKey())); }); } + } else { + Set criteria = deDupCriteriaBuilder.buildFromMODSPerson(mods); + criteria.forEach(criterion -> { + possibleDuplicates.addAll(DeduplicationKeyManager.getInstance().getDuplicates(id.toString(), criterion.getType(), criterion.getKey())); + }); } possibleDuplicates diff --git a/ubo-common/src/main/resources/config/ubo-common/messages_de.properties b/ubo-common/src/main/resources/config/ubo-common/messages_de.properties index b0a388b61..1d9c2bf21 100644 --- a/ubo-common/src/main/resources/config/ubo-common/messages_de.properties +++ b/ubo-common/src/main/resources/config/ubo-common/messages_de.properties @@ -97,6 +97,7 @@ button.delete = L\u00F6schen button.deleteFileSelected = markierte Dateien l\u00F6schen button.deleteFilespace = Dateibereich l\u00F6schen button.deletePublication = Diese Publikation l\u00F6schen? +button.deletePerson = Diese Person l\u00F6schen? button.deleteYes = Ja, l\u00F6schen! button.edit = Bearbeiten button.email = E-Mail senden diff --git a/ubo-common/src/main/resources/config/ubo-common/messages_en.properties b/ubo-common/src/main/resources/config/ubo-common/messages_en.properties index b1c6b8678..cfa797c94 100644 --- a/ubo-common/src/main/resources/config/ubo-common/messages_en.properties +++ b/ubo-common/src/main/resources/config/ubo-common/messages_en.properties @@ -97,6 +97,7 @@ button.delete = Delete button.deleteFileSelected = Delete selected files button.deleteFilespace = Delete filespace button.deletePublication = Delete this publication? +button.deletePerson = Delete this person? button.deleteYes = Yes, delete! button.edit = Edit button.email = Send e-mail diff --git a/ubo-common/src/main/resources/xsl/mods-display.xsl b/ubo-common/src/main/resources/xsl/mods-display.xsl index e8d041598..2b61e321b 100644 --- a/ubo-common/src/main/resources/xsl/mods-display.xsl +++ b/ubo-common/src/main/resources/xsl/mods-display.xsl @@ -377,6 +377,18 @@ + + + + plain + + + - + + + + + plain @@ -1169,6 +1181,12 @@ + + + + + + diff --git a/ubo-common/src/main/vue/dedup/src/components/PersonDisplay.vue b/ubo-common/src/main/vue/dedup/src/components/PersonDisplay.vue index a9825a801..372713033 100644 --- a/ubo-common/src/main/vue/dedup/src/components/PersonDisplay.vue +++ b/ubo-common/src/main/vue/dedup/src/components/PersonDisplay.vue @@ -15,7 +15,7 @@ diff --git a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java index c097eac98..2f7207360 100644 --- a/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java +++ b/ubo-modsperson/src/main/java/org/mycore/ubo/modsperson/MODSPersonCachingStartupHandler.java @@ -81,7 +81,7 @@ private void addSolrDocToLookup(SolrDocument doc, AtomicInteger counter) { String familyName; String givenName; Set keys = new HashSet<>(); - String personName = (String) doc.getFieldValue("name"); + String personName = (String)((List) doc.getFieldValue("person")).get(0); if (personName != null && personName.contains(",")) { String[] parts = personName.split(","); familyName = parts[0].trim(); diff --git a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties index f25bf8792..68fa7a268 100644 --- a/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties +++ b/ubo-modsperson/src/main/resources/config/ubo-modsperson/mycore.properties @@ -27,3 +27,6 @@ MCR.ContentTransformer.modsperson-json-normal.Settings.Class=org.mycore.common.c MCR.ContentTransformer.modsperson-json-normal.Style.Class=org.mycore.common.content.transformer.MCRXsonifyTransformer$StyleBuilder MCR.ContentTransformer.modsperson-json-normal.Style.TextKey=text +MCR.ContentTransformer.mycoreobject-modsperson-structure.Stylesheet=xsl/mycoreobject-modsperson-structure.xsl,xsl/html-layout.xsl,xsl/i18n-transformation-step.xsl,xsl/xhtml2html.xsl +MCR.ContentTransformer.mycoreobject-modsperson-structure.TransformerFactoryClass=org.apache.xalan.processor.TransformerFactoryImpl + diff --git a/ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson-structure.xsl b/ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson-structure.xsl new file mode 100644 index 000000000..8d067af9a --- /dev/null +++ b/ubo-modsperson/src/main/resources/xsl/mycoreobject-modsperson-structure.xsl @@ -0,0 +1,348 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <xsl:value-of select="$title" /> + <xsl:value-of select="i18n:translate('structure.editor.edit')"/> + + + + +
+ +
+ + + +
+ + + + + + + + + person + + + + base + + + + + + + + + + + + + + + + + + + + + duplicate + + + + + + + + + + + + + +
+ + + +
+ + col-11 ubo-structure__content + + ubo-structure__content--info + ubo-structure__content--warning + ubo-structure__content--default + + + + + + + + + + + + + + +
+
+ +
+ + + + + + +
+ +
+
+
+
+ + + + + + + + + + Evtl. Dublette + + + + + + + + + + + + +
+ + divs + +
+
+ + +
+ +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + +