diff --git a/pass-deposit-services/assembler-api/src/main/java/org/eclipse/pass/deposit/assembler/PackageOptions.java b/pass-deposit-services/assembler-api/src/main/java/org/eclipse/pass/deposit/assembler/PackageOptions.java index 95f7a019..052a8e55 100644 --- a/pass-deposit-services/assembler-api/src/main/java/org/eclipse/pass/deposit/assembler/PackageOptions.java +++ b/pass-deposit-services/assembler-api/src/main/java/org/eclipse/pass/deposit/assembler/PackageOptions.java @@ -98,4 +98,11 @@ enum OPTS { } + /** + * Funder Mapping for Nihms Packages + */ + interface FunderMapping { + String KEY = "FUNDER-MAPPING"; + } + } diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/builder/DepositSubmissionMapper.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/builder/DepositSubmissionMapper.java index 0ecdc8a1..c59b7b69 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/builder/DepositSubmissionMapper.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/builder/DepositSubmissionMapper.java @@ -100,6 +100,8 @@ public DepositSubmission createDepositSubmission(Submission submissionEntity, Li metadata.setJournalMetadata(journal); ArrayList persons = new ArrayList<>(); metadata.setPersons(persons); + ArrayList grants = new ArrayList<>(); + metadata.setGrantsMetadata(grants); // Data from the Submission resource submission.setId(submissionEntity.getId()); @@ -129,6 +131,7 @@ public DepositSubmission createDepositSubmission(Submission submissionEntity, Li // Data from the User resources for the PI and CoPIs User piEntity = grantEntity.getPi(); persons.add(createPerson(piEntity, DepositMetadata.PERSON_TYPE.pi)); + grants.add(createGrant(piEntity, grantEntity)); for (User copiEntity : grantEntity.getCoPis()) { persons.add(createPerson(copiEntity, DepositMetadata.PERSON_TYPE.copi)); @@ -188,6 +191,15 @@ private DepositMetadata.Person createAuthor(String fullName) { return person; } + private DepositMetadata.Grant createGrant(User userEntity, Grant grantEntity) { + DepositMetadata.Grant grant = new DepositMetadata.Grant(); + grant.setGrantId(grantEntity.getAwardNumber()); + grant.setGrantPi(createPerson(userEntity, DepositMetadata.PERSON_TYPE.pi)); + grant.setFunder(grantEntity.getPrimaryFunder().getName()); + grant.setFunderLocalKey(grantEntity.getPrimaryFunder().getLocalKey()); + return grant; + } + /** * Convenience method for retrieving a string property. * diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/RepositoryDepositConfig.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/RepositoryDepositConfig.java index 2c659e60..71176fca 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/RepositoryDepositConfig.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/config/repository/RepositoryDepositConfig.java @@ -72,6 +72,6 @@ public int hashCode() { @Override public String toString() { return "RepositoryDepositConfig{" + "depositProcessing=" + depositProcessing + - ", statusMapping=" + statusMapping + '}'; + ", statusMapping=" + statusMapping + "}"; } } diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/nihms/NihmsMetadataSerializer.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/nihms/NihmsMetadataSerializer.java index 67700fd6..96fc2106 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/nihms/NihmsMetadataSerializer.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/nihms/NihmsMetadataSerializer.java @@ -21,6 +21,8 @@ import java.time.Period; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; @@ -30,6 +32,8 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.pass.deposit.assembler.PackageOptions; import org.eclipse.pass.deposit.assembler.SizedStream; import org.eclipse.pass.deposit.model.DepositMetadata; import org.eclipse.pass.deposit.model.JournalPublicationType; @@ -48,9 +52,19 @@ public class NihmsMetadataSerializer implements StreamingSerializer { private static final Logger LOG = LoggerFactory.getLogger(NihmsMetadataSerializer.class); private DepositMetadata metadata; + private Map funderMapping; - public NihmsMetadataSerializer(DepositMetadata metadata) { + public NihmsMetadataSerializer(DepositMetadata metadata, Map packageOptions) { this.metadata = metadata; + Object funderMappingObj = packageOptions.get(PackageOptions.FunderMapping.KEY); + if (funderMappingObj instanceof Map) { + this.funderMapping = ((Map) funderMappingObj).entrySet().stream() + .collect(Collectors.toMap( + e -> (String) e.getKey(), + e -> (String) e.getValue())); + } else { + throw new ClassCastException("FunderMapping is not a Map"); + } } @Override @@ -92,16 +106,27 @@ private void add_text_element(Document doc, Element parent, String name, String } private void write_metadata(Document doc) { - DepositMetadata.Manuscript manuscript = metadata.getManuscriptMetadata(); - DepositMetadata.Article article = metadata.getArticleMetadata(); - DepositMetadata.Journal journal = metadata.getJournalMetadata(); - Element root = doc.createElement("manuscript-submit"); doc.appendChild(root); - if (manuscript.getNihmsId() != null) { + addManuscriptMetadata(root); + addArticleMetadata(root); + addJournalMetadata(doc, root); + addManuscriptTitle(doc, root); + addContacts(doc, root); + addGrants(doc, root); + } + + private void addManuscriptMetadata(Element root) { + DepositMetadata.Manuscript manuscript = metadata.getManuscriptMetadata(); + + if (StringUtils.isNotBlank(manuscript.getNihmsId())) { root.setAttribute("manuscript-id", manuscript.getNihmsId()); } + } + + private void addArticleMetadata(Element root) { + DepositMetadata.Article article = metadata.getArticleMetadata(); if (article != null && metadata.getArticleMetadata().getEmbargoLiftDate() != null) { LocalDate lift = article.getEmbargoLiftDate().toLocalDate(); @@ -128,6 +153,10 @@ private void write_metadata(Document doc) { root.setAttribute("doi", path); } + } + + private void addJournalMetadata(Document doc, Element root) { + DepositMetadata.Journal journal = metadata.getJournalMetadata(); // There is an optional agency attribute. // Should only be used for non-NIH funders when grant information is also given @@ -143,7 +172,7 @@ private void write_metadata(Document doc) { // See https://github.com/OA-PASS/metadata-schemas/pull/28 and // https://github.com/OA-PASS/jhu-package-providers/issues/16 journal.getIssnPubTypes().values().forEach(issnPubType -> { - if (issnPubType.pubType == null || issnPubType.issn == null || issnPubType.issn.trim().isEmpty()) { + if (issnPubType.pubType == null || StringUtils.isBlank(issnPubType.issn)) { LOG.debug("Discarding incomplete ISSN: {}", issnPubType); return; } @@ -159,15 +188,21 @@ private void write_metadata(Document doc) { add_text_element(doc, journal_meta, "journal-title", journal.getJournalTitle()); } - if (manuscript != null && manuscript.title != null) { + } + + private void addManuscriptTitle(Document doc, Element root) { + DepositMetadata.Manuscript manuscript = metadata.getManuscriptMetadata(); + + if (manuscript != null && StringUtils.isNotBlank(manuscript.title)) { add_text_element(doc, root, "manuscript-title", manuscript.title); } - // Could add a citation element if we had all the information + } + private void addContacts(Document doc, Element root) { List persons = metadata.getPersons(); - if (persons != null && persons.size() > 0) { + if (persons != null && !persons.isEmpty()) { Element contacts = doc.createElement("contacts"); root.appendChild(contacts); @@ -177,20 +212,20 @@ private void write_metadata(Document doc) { Element p = doc.createElement("person"); contacts.appendChild(p); - if (person.getFirstName() != null) { + if (StringUtils.isNotBlank(person.getFirstName())) { p.setAttribute("fname", person.getFirstName()); } else { - if (person.getFullName() != null) { + if (StringUtils.isNotBlank(person.getFullName())) { p.setAttribute("fname", person.getFullName().split("\\s")[0]); } } - if (person.getMiddleName() != null) { + if (StringUtils.isNotBlank(person.getMiddleName())) { p.setAttribute("mname", person.getMiddleName()); } - if (person.getLastName() != null) { + if (StringUtils.isNotBlank(person.getLastName())) { p.setAttribute("lname", person.getLastName()); } else { - if (person.getFullName() != null) { + if (StringUtils.isNotBlank(person.getFullName())) { String[] split = person.getFullName().split("\\s"); if (split.length > 2) { // middle name is present @@ -201,7 +236,7 @@ private void write_metadata(Document doc) { } } } - if (person.getEmail() != null) { + if (StringUtils.isNotBlank(person.getEmail())) { p.setAttribute("email", person.getEmail()); } @@ -209,8 +244,54 @@ private void write_metadata(Document doc) { } } } + } + + private void addGrants(Document doc, Element root) { + List grantsList = metadata.getGrantsMetadata(); + + if (grantsList != null && !grantsList.isEmpty()) { + List funderKeys = grantsList.stream() + .map(DepositMetadata.Grant::getFunderLocalKey) + .toList(); + + //Only create the top level grant element if at least one of the keys is associated with nihms + if (funderKeys.stream().anyMatch(funderMapping::containsKey)) { + + Element grantsElement = doc.createElement("grants"); + root.appendChild(grantsElement); + + for (DepositMetadata.Grant grant : grantsList) { + if (funderMapping.containsKey(grant.getFunderLocalKey())) { + if (StringUtils.isNotBlank(grant.getFunder())) { + Element grantElement = doc.createElement("grant"); + grantsElement.appendChild(grantElement); - // Could add grant information here if it was useful. - // Can only be used for a set list of funders + if (StringUtils.isNotBlank(grant.getGrantId())) { + grantElement.setAttribute("id", grant.getGrantId()); + } + + //use the nihms abbreviations for funders, the accepted list is in the nihms DTD + grantElement.setAttribute("funder", funderMapping.get(grant.getFunderLocalKey())); + + DepositMetadata.Person pi = grant.getGrantPi(); + if (pi != null) { + Element piElement = doc.createElement("PI"); + grantElement.appendChild(piElement); + + if (StringUtils.isNotBlank(pi.getFirstName())) { + piElement.setAttribute("fname", pi.getFirstName()); + } + if (StringUtils.isNotBlank(pi.getLastName())) { + piElement.setAttribute("lname", pi.getLastName()); + } + if (StringUtils.isNotBlank(pi.getEmail())) { + piElement.setAttribute("email", pi.getEmail()); + } + } + } + } + } + } + } } } diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/nihms/NihmsPackageProvider.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/nihms/NihmsPackageProvider.java index 008851a2..c32d0402 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/nihms/NihmsPackageProvider.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/provider/nihms/NihmsPackageProvider.java @@ -74,7 +74,7 @@ public static String getNonCollidingFilename(String fileName, DepositFileType fi public void start(DepositSubmission submission, List custodialResources, Map packageOptions) { manifestSerializer = new NihmsManifestSerializer(submission.getManifest()); - metadataSerializer = new NihmsMetadataSerializer(submission.getMetadata()); + metadataSerializer = new NihmsMetadataSerializer(submission.getMetadata(), packageOptions); } /** diff --git a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/service/DepositTask.java b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/service/DepositTask.java index a47a7565..16ec48f4 100644 --- a/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/service/DepositTask.java +++ b/pass-deposit-services/deposit-core/src/main/java/org/eclipse/pass/deposit/service/DepositTask.java @@ -424,6 +424,7 @@ static Function performDeposit(DepositWorkerContext try { packager = dc.packager(); + packageStream = packager.getAssembler().assemble( dc.depositSubmission(), packager.getAssemblerOptions()); packagerConfig = packager.getConfiguration(); diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/builder/DepositSubmissionModelBuilderIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/builder/DepositSubmissionModelBuilderIT.java index cec9207f..fec64145 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/builder/DepositSubmissionModelBuilderIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/builder/DepositSubmissionModelBuilderIT.java @@ -49,10 +49,12 @@ public class DepositSubmissionModelBuilderIT extends AbstractDepositSubmissionIT private static final String EXPECTED_DOI = "10.1039/c7fo01251a"; private static final String EXPECTED_EMBARGO_END_DATE = "2018-06-30"; private static final int EXPECTED_SUBMITER_COUNT = 1; - private static final int EXPECTED_PI_COUNT = 1; - private static final int EXPECTED_CO_PI_COUNT = 2; + private static final int EXPECTED_PI_COUNT = 2; + private static final int EXPECTED_CO_PI_COUNT = 4; private static final int EXPECTED_AUTHOR_COUNT = 6; private static final String EXPECTED_NLMTA = "Food Funct"; + private static final String EXPECTED_GRANT1 = "R01EY026617"; + private static final String EXPECTED_GRANT2 = "R01EY026618"; private static final Map EXPECTED_ISSNS = new HashMap<>() { { @@ -159,10 +161,15 @@ public void testBuildDepositSubmission() throws IOException { .anyMatch(author -> author.getName().equals("Raymond J. Playford"))); + //test the grants associated with the submission + List grants = submission.getMetadata().getGrantsMetadata(); + + assertNotNull(grants.stream().filter(matchGrant -> matchGrant.getGrantId() == EXPECTED_GRANT1)); + assertNotNull(grants.stream().filter(matchGrant -> matchGrant.getGrantId() == EXPECTED_GRANT2)); + // Read something out of the submission metadata assertTrue(submission.getSubmissionMeta().has("agreements")); JsonObject agreement = submission.getSubmissionMeta().getAsJsonObject("agreements"); assertTrue(agreement.has("JScholarship")); } - } \ No newline at end of file diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/MetadataIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/MetadataIT.java index 88c4b35f..3d743240 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/MetadataIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/j10p/MetadataIT.java @@ -86,7 +86,7 @@ public void commonContributorsAndFewAuthors() throws Exception { assertEquals("Elsevier", qdc.getElementsByTagNameNS(DC_NS, DC_PUBLISHER).item(0).getTextContent()); // Contributor list does not include submitting user, only PIs (from Grant) and authors (from metadata) - assertEquals(5, qdc.getElementsByTagNameNS(DC_NS, DC_CONTRIBUTOR).getLength()); + assertEquals(8, qdc.getElementsByTagNameNS(DC_NS, DC_CONTRIBUTOR).getLength()); for (int i = 0; i < 5; i++) { // Contributors must have names, whether they come from // first/middle/last or only display name (in Submission), or from metadata. diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsAssemblerIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsAssemblerIT.java index 6b96194e..f56cfe77 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsAssemblerIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsAssemblerIT.java @@ -27,16 +27,22 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -45,6 +51,7 @@ import org.eclipse.pass.deposit.assembler.AbstractAssemblerIT; import org.eclipse.pass.deposit.assembler.PackageOptions.Archive; import org.eclipse.pass.deposit.assembler.PackageOptions.Compression; +import org.eclipse.pass.deposit.assembler.PackageOptions.FunderMapping; import org.eclipse.pass.deposit.assembler.PackageOptions.Spec; import org.eclipse.pass.deposit.assembler.PackageStream; import org.eclipse.pass.deposit.model.DepositFile; @@ -56,6 +63,8 @@ import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; /** * Creates a package, then extracts it. Performs some basic tests on the extracted package. @@ -76,11 +85,15 @@ public void setUp() throws Exception { @Override protected Map getOptions() { + Map funderMapping = + Map.of("johnshopkins.edu:funder:300293", "cdc", + "johnshopkins.edu:funder:300484", "nih"); return new HashMap<>() { { put(Spec.KEY, SPEC_NIHMS_NATIVE_2022_05); put(Archive.KEY, Archive.OPTS.TAR); put(Compression.KEY, Compression.OPTS.GZIP); + put(FunderMapping.KEY, funderMapping); } }; } @@ -200,19 +213,14 @@ public void testPackageManifest() throws Exception { @Test public void testPackageMetadata() throws Exception { - Document metaDom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(metadata); - assertNotNull(metaDom); - - // root element is - Element root = metaDom.getDocumentElement(); - assertEquals("manuscript-submit", root.getTagName()); + Element root = initDom(); // required element is present with the manuscript title as the value Element title = asList(root.getElementsByTagName("manuscript-title")).get(0); assertEquals(submission.getMetadata().getManuscriptMetadata().getTitle(), title.getTextContent()); - // Insure that only one element is present in the submission metadata - // and insure that the is a PI or a Co-PI for the grant that funded the submission. + // Ensure that only one element is present in the submission metadata + // and Ensure that the is a PI or a Co-PI for the grant that funded the submission. List personElements = asList(root.getElementsByTagName("person")); // Assert that there is only one Person present in the metadata @@ -228,7 +236,7 @@ public void testPackageMetadata() throws Exception { return asPerson; }).toList(); - // Insure that the Person in the metadata matches a Person on the Submission, and that the person is a + // Ensure that the Person in the metadata matches a Person on the Submission, and that the person is a // corresponding pi asPersons.stream().forEach(person -> { assertTrue(submission.getMetadata().getPersons().stream().anyMatch(candidate -> @@ -260,7 +268,61 @@ public void testPackageMetadata() throws Exception { assertEquals(issn.getAttribute("issn-type"), "print"); } }); + } + + @Test + public void testPackageMetadataGrantFunder() throws Exception { + Element root = initDom(); + //TODO - for testing, remove once done + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + StringWriter writer = new StringWriter(); + + transformer.transform(new DOMSource(root), new StreamResult(writer)); + + String rootAsString = writer.toString(); + //Ensure that the grants in the metadata matches a Grant on the submission, Check the attributes of a grant on + //submission against what is found in the metadata + List grantElements = asList(root.getElementsByTagName("grant")); + + // Assert that there is only two grants present in the metadata. If more grants are needed to test, + // add to sample1.json + assertEquals(2, grantElements.size()); + + List asGrants = grantElements.stream().map(element -> { + DepositMetadata.Grant asGrant = new DepositMetadata.Grant(); + Person pi = new Person(); + + NodeList grantPis = element.getElementsByTagName("PI"); + for (int i = 0; i < grantPis.getLength(); i++) { + Node grantPiNode = grantPis.item(i); + Element grantPiElement = (Element) grantPiNode; + pi.setFirstName(grantPiElement.getAttribute("fname")); + pi.setLastName(grantPiElement.getAttribute("lname")); + pi.setEmail(grantPiElement.getAttribute("email")); + } + asGrant.setGrantId(element.getAttribute("id")); + asGrant.setFunder(element.getAttribute("funder")); + asGrant.setGrantPi(pi); + return asGrant; + }).toList(); + List subGrantMetaData = submission.getMetadata().getGrantsMetadata(); + for (DepositMetadata.Grant subGrant : subGrantMetaData) { + String subGrantId = subGrant.getGrantId(); + + // Find the associated grant from asGrants that matches the ID + Optional metaDataGrant = asGrants.stream() + .filter(asGrant -> asGrant.getGrantId().equals(subGrantId)) + .findFirst(); + + //assert that the grant ID exists and is valid to the submission data + assertEquals(subGrant.getGrantId(), metaDataGrant.get().getGrantId()); + assertTrue(List.of("nih", "cdc").contains(metaDataGrant.get().getFunder())); + assertEquals(subGrant.getGrantPi().getFirstName(), metaDataGrant.get().getGrantPi().getFirstName()); + assertEquals(subGrant.getGrantPi().getLastName(), metaDataGrant.get().getGrantPi().getLastName()); + assertEquals(subGrant.getGrantPi().getEmail(), metaDataGrant.get().getGrantPi().getEmail()); + } } /** @@ -281,6 +343,16 @@ private static boolean isNullOrEmpty(String s) { return s == null || s.trim().length() == 0; } + private Element initDom() throws Exception { + Document metaDom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(metadata); + assertNotNull(metaDom); + + // root element is + Element root = metaDom.getDocumentElement(); + assertEquals("manuscript-submit", root.getTagName()); + return root; + } + private static class ManifestLine { private static final String ERR = "File %s, line %s is missing %s"; private File manifestFile; diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsMetadataSerializerTest.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsMetadataSerializerTest.java index 772d1860..7cd96f14 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsMetadataSerializerTest.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsMetadataSerializerTest.java @@ -35,6 +35,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Spliterators.AbstractSpliterator; import java.util.function.Consumer; import java.util.stream.Stream; @@ -45,6 +46,7 @@ import com.github.jknack.handlebars.internal.Files; import org.apache.commons.io.IOUtils; +import org.eclipse.pass.deposit.assembler.PackageOptions; import org.eclipse.pass.deposit.assembler.SizedStream; import org.eclipse.pass.deposit.model.DepositMetadata; import org.eclipse.pass.deposit.model.JournalPublicationType; @@ -71,6 +73,7 @@ public class NihmsMetadataSerializerTest { private Path tempDir; private static NihmsMetadataSerializer underTest; + private static HashMap packageOptions; private static final DepositMetadata metadata = new DepositMetadata(); @BeforeEach @@ -80,6 +83,7 @@ public void setup() throws Exception { DepositMetadata.Manuscript manuscript = new DepositMetadata.Manuscript(); DepositMetadata.Article article = new DepositMetadata.Article(); List personList = new ArrayList<>(); + List grantList = new ArrayList<>(); //populate journal metadata journal.setJournalId("FJ001"); @@ -143,12 +147,39 @@ public void setup() throws Exception { person4.setMiddleName("The"); personList.add(person4); + DepositMetadata.Grant grant1 = new DepositMetadata.Grant(); + grant1.setGrantId("R0123456789"); + grant1.setFunder("FOGARTY INTERNATIONAL CENTER"); + grant1.setFunderLocalKey("johnshopkins.edu:funder:300484"); + grant1.setGrantPi(person1); + grantList.add(grant1); + + DepositMetadata.Grant grant2 = new DepositMetadata.Grant(); + grant2.setGrantId("R0123456000"); + grant2.setFunder("CENTERS FOR DISEASE CONTROL"); + grant2.setFunderLocalKey("johnshopkins.edu:funder:300293"); + grant2.setGrantPi(person2); + grantList.add(grant2); + + DepositMetadata.Grant grant3 = new DepositMetadata.Grant(); + grant2.setGrantId("R0123456897"); + grant2.setFunder("MYRIAD GENETICS INC"); + grant2.setFunderLocalKey("johnshopkins.edu:funder:301885"); + grant2.setGrantPi(person2); + grantList.add(grant2); + metadata.setJournalMetadata(journal); metadata.setManuscriptMetadata(manuscript); metadata.setPersons(personList); - metadata.setArticleMetadata(article); + metadata.setGrantsMetadata(grantList); + + Map funderMapping = + Map.of("johnshopkins.edu:funder:300293", "cdc", + "johnshopkins.edu:funder:300484", "nih"); + packageOptions = new HashMap<>(); + packageOptions.put(PackageOptions.FunderMapping.KEY, funderMapping); - underTest = new NihmsMetadataSerializer(metadata); + underTest = new NihmsMetadataSerializer(metadata, packageOptions); } @Test @@ -212,6 +243,58 @@ public void testSerializedMetadataDoi() throws Exception { assertTrue(doi.contentEquals(path)); } + /** + * Test that the number of grants and their associated information is valid when serialized + */ + @Test + public void testSerializedMetaDataGrants() throws Exception { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + SizedStream sizedStream; + InputStream is; + + List validAwardNumbers = new ArrayList<>(List.of("R0123456789", "R0123456000")); + List validFunders = new ArrayList<>(List.of("nih")); + List validPiFname = new ArrayList<>(List.of("Bessie", "Elsie")); + List validPiLname = new ArrayList<>(List.of("Cow")); + List validPiEmail = new ArrayList<>(List.of("person@farm.com")); + + sizedStream = underTest.serialize(); + is = sizedStream.getInputStream(); + Document document = builder.parse(is); + NodeList grantNodes = document.getElementsByTagName("grants"); + + Node grantNode = grantNodes.item(0); + NodeList grantNodeChildList = grantNode.getChildNodes(); + + for (int i = 0; i < grantNodeChildList.getLength(); i++) { + Node grantChildNode = grantNodeChildList.item(i); + + if (grantChildNode.getNodeType() == Node.ELEMENT_NODE) { + Element grantElement = (Element) grantChildNode; + + // Extracting grant attributes from the XML doc + String grantId = grantElement.getAttribute("id"); + String funder = grantElement.getAttribute("funder"); + + assertTrue(validAwardNumbers.contains(grantId)); + assertTrue(validFunders.contains(funder)); + + // Extracting PI information from the XML doc + NodeList piNodes = grantElement.getElementsByTagName("PI"); + for (int k = 0; k < piNodes.getLength(); k++) { + Element piElement = (Element) piNodes.item(k); + String piFname = piElement.getAttribute("fname"); + String piLname = piElement.getAttribute("lname"); + String piEmail = piElement.getAttribute("email"); + + assertTrue(validPiFname.contains(piFname)); + assertTrue(validPiLname.contains(piLname)); + assertTrue(validPiEmail.contains(piEmail)); + } + } + } + } + /** * A complete IssnPubType (having a non null publication type and issn value) should be serialized to the metadata. */ @@ -231,7 +314,7 @@ public void completeIssnPubtype() throws IOException, ParserConfigurationExcepti metadata.setJournalMetadata(journalMd); - underTest = new NihmsMetadataSerializer(metadata); + underTest = new NihmsMetadataSerializer(metadata, packageOptions); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Node doc = builder.parse(underTest.serialize().getInputStream()); @@ -266,7 +349,7 @@ public void incompleteIssnPubtypeNullIssn() throws IOException { metadata.setJournalMetadata(journalMd); - underTest = new NihmsMetadataSerializer(metadata); + underTest = new NihmsMetadataSerializer(metadata, packageOptions); assertFalse(IOUtils.toString(underTest.serialize().getInputStream(), UTF_8).contains("issn")); } @@ -288,7 +371,7 @@ public void incompleteIssnPubtypeNullPubType() throws IOException { metadata.setJournalMetadata(journalMd); - underTest = new NihmsMetadataSerializer(metadata); + underTest = new NihmsMetadataSerializer(metadata, packageOptions); assertFalse(IOUtils.toString(underTest.serialize().getInputStream(), UTF_8).contains("issn")); } diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsPackageVerifier.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsPackageVerifier.java index 249dfe62..2f0a3687 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsPackageVerifier.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsPackageVerifier.java @@ -65,7 +65,7 @@ public void verify(DepositSubmission submission, ExplodedPackage explodedPackage String expectedManifest = IOUtils.toString( new NihmsManifestSerializer(submission.getManifest()).serialize().getInputStream(), UTF_8); String expectedBulkMeta = IOUtils.toString( - new NihmsMetadataSerializer(submission.getMetadata()).serialize().getInputStream(), UTF_8); + new NihmsMetadataSerializer(submission.getMetadata(), map).serialize().getInputStream(), UTF_8); assertEquals(expectedManifest, IOUtils.toString(new FileInputStream(manifest), UTF_8)); assertEquals(expectedBulkMeta, IOUtils.toString(new FileInputStream(bulk_meta), UTF_8)); diff --git a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsThreadedAssemblyIT.java b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsThreadedAssemblyIT.java index 9ee2bbb5..1223dd2b 100644 --- a/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsThreadedAssemblyIT.java +++ b/pass-deposit-services/deposit-core/src/test/java/org/eclipse/pass/deposit/provider/nihms/NihmsThreadedAssemblyIT.java @@ -39,12 +39,16 @@ protected AbstractAssembler assemblerUnderTest() { @Override protected Map packageOptions() { + Map funderMapping = + Map.of("johnshopkins.edu:funder:300293", "cdc", + "johnshopkins.edu:funder:300484", "nih"); return new HashMap<>() { { put(PackageOptions.Spec.KEY, SPEC_NIHMS_NATIVE_2022_05); put(PackageOptions.Archive.KEY, PackageOptions.Archive.OPTS.TAR); put(PackageOptions.Compression.KEY, PackageOptions.Compression.OPTS.GZIP); put(PackageOptions.Checksum.KEY, singletonList(PackageOptions.Checksum.OPTS.SHA256)); + put(PackageOptions.FunderMapping.KEY, funderMapping); } }; } diff --git a/pass-deposit-services/deposit-core/src/test/resources/repositories.json b/pass-deposit-services/deposit-core/src/test/resources/repositories.json new file mode 100644 index 00000000..7166232d --- /dev/null +++ b/pass-deposit-services/deposit-core/src/test/resources/repositories.json @@ -0,0 +1,128 @@ +{ + "JScholarship": { + "assembler": { + "specification": "simple", + "beanName": "simpleAssembler", + "options": { + "archive": "ZIP", + "compression": "NONE", + "algorithms": [ + "sha512", + "md5" + ] + } + }, + "transport-config": { + "protocol-binding": { + "protocol": "filesystem", + "baseDir": "target/packages", + "createIfMissing": "true", + "overwrite": "true" + } + } + }, + "PubMed Central": { + "assembler": { + "specification": "simple", + "beanName": "simpleAssembler", + "options": { + "archive": "ZIP", + "compression": "NONE", + "algorithms": [ + "sha512", + "md5" + ], + "funder-mapping": { + "johnshopkins.edu:funder:300032": "ahqr", + "johnshopkins.edu:funder:300293": "cdc", + "johnshopkins.edu:funder:300859": "cdc", + "johnshopkins.edu:funder:301459": "va", + "johnshopkins.edu:funder:300453": "epa", + "johnshopkins.edu:funder:303444": "hhmi", + "johnshopkins.edu:funder:300484": "nih", + "johnshopkins.edu:funder:300865": "nih", + "johnshopkins.edu:funder:300869": "nih", + "johnshopkins.edu:funder:300866": "nih", + "johnshopkins.edu:funder:308302": "nih", + "johnshopkins.edu:funder:305950": "nih", + "johnshopkins.edu:funder:302727": "nih", + "johnshopkins.edu:funder:300863": "nih", + "johnshopkins.edu:funder:300874": "nih", + "johnshopkins.edu:funder:300861": "nih", + "johnshopkins.edu:funder:300842": "nih", + "johnshopkins.edu:funder:303587": "nih", + "johnshopkins.edu:funder:303586": "nih", + "johnshopkins.edu:funder:306099": "nih", + "johnshopkins.edu:funder:300858": "nih", + "johnshopkins.edu:funder:301479": "nih", + "johnshopkins.edu:funder:300870": "nih", + "johnshopkins.edu:funder:303589": "nih", + "johnshopkins.edu:funder:300860": "nih", + "johnshopkins.edu:funder:300852": "nih", + "johnshopkins.edu:funder:302822": "nih", + "johnshopkins.edu:funder:302592": "nih", + "johnshopkins.edu:funder:303585": "nih", + "johnshopkins.edu:funder:303574": "nih", + "johnshopkins.edu:funder:300867": "nih", + "johnshopkins.edu:funder:303580": "nih", + "johnshopkins.edu:funder:301978": "nih", + "johnshopkins.edu:funder:305204": "aspr", + "johnshopkins.edu:funder:303395": "fda" + } + } + }, + "transport-config": { + "protocol-binding": { + "protocol": "filesystem", + "baseDir": "target/packages", + "createIfMissing": "true", + "overwrite": "true" + } + } + }, + "BagIt": { + "assembler": { + "specification": "simple", + "beanName": "simpleAssembler", + "options": { + "archive": "ZIP", + "compression": "NONE", + "algorithms": [ + "sha512", + "md5" + ], + "baginfo-template-resource": "/bag-info.hbm" + } + }, + "transport-config": { + "protocol-binding": { + "protocol": "filesystem", + "baseDir": "target/packages", + "createIfMissing": "true", + "overwrite": "true" + } + } + }, + "dash": { + "assembler": { + "specification": "simple", + "beanName": "simpleAssembler", + "options": { + "archive": "ZIP", + "compression": "NONE", + "algorithms": [ + "sha512", + "md5" + ] + } + }, + "transport-config": { + "protocol-binding": { + "protocol": "filesystem", + "baseDir": "target/packages", + "createIfMissing": "true", + "overwrite": "true" + } + } + } +} \ No newline at end of file diff --git a/pass-deposit-services/deposit-core/src/test/resources/submissions/sample1.json b/pass-deposit-services/deposit-core/src/test/resources/submissions/sample1.json index 5b1d56aa..fa0b43c9 100644 --- a/pass-deposit-services/deposit-core/src/test/resources/submissions/sample1.json +++ b/pass-deposit-services/deposit-core/src/test/resources/submissions/sample1.json @@ -136,7 +136,7 @@ { "name": "National Eye Institute", "url": "http://example.com/eyeguys", - "localKey": "aabbcc", + "localKey": "johnshopkins.edu:funder:300484", "policy": "10", "id": "11", "@type": "Funder" @@ -144,7 +144,7 @@ { "name": "International Eye Institute", "url": "http://example.com/othereyeguys", - "localKey": "ddeeff", + "localKey": "johnshopkins.edu:funder:300293", "policy": "10", "id": "12", "@type": "Funder" @@ -167,6 +167,24 @@ "id": "13", "@type": "Grant" }, + { + "awardNumber": "R01EY026618", + "awardStatus": "ACTIVE", + "localKey": "112234", + "projectName": "Optimal magnification and oculomotor strategies in low vision patients", + "awardDate": "2017-08-01T00:00:00.000Z", + "startDate": "2017-09-01T00:00:00.000Z", + "endDate": "2020-06-01T00:00:00.000Z", + "primaryFunder": "12", + "directFunder": "11", + "pi": "2", + "coPis": [ + "3", + "1" + ], + "id": "14", + "@type": "Grant" + }, { "metadata": "{\n \"agreements\": {\n \"JScholarship\": \"NON-EXCLUSIVE LICENSE FOR USE OF MATERIALS This non-exclusive license defines the terms for the deposit of Materials in all formats into the digital repository of materials collected, preserved and made available through the Johns Hopkins Digital Repository, JScholarship. The Contributor hereby grants to Johns Hopkins a royalty free, non-exclusive worldwide license to use, re-use, display, distribute, transmit, publish, re-publish or copy the Materials, either digitally or in print, or in any other medium, now or hereafter known, for the purpose of including the Materials hereby licensed in the collection of materials in the Johns Hopkins Digital Repository for educational use worldwide. In some cases, access to content may be restricted according to provisions established in negotiation with the copyright holder. This license shall not authorize the commercial use of the Materials by Johns Hopkins or any other person or organization, but such Materials shall be restricted to non-profit educational use. Persons may apply for commercial use by contacting the copyright holder. Copyright and any other intellectual property right in or to the Materials shall not be transferred by this agreement and shall remain with the Contributor, or the Copyright holder if different from the Contributor. Other than this limited license, the Contributor or Copyright holder retains all rights, title, copyright and other interest in the images licensed. If the submission contains material for which the Contributor does not hold copyright, the Contributor represents that s/he has obtained the permission of the Copyright owner to grant Johns Hopkins the rights required by this license, and that such third-party owned material is clearly identified and acknowledged within the text or content of the submission. If the submission is based upon work that has been sponsored or supported by an agency or organization other than Johns Hopkins, the Contributor represents that s/he has fulfilled any right of review or other obligations required by such contract or agreement. Johns Hopkins will not make any alteration, other than as allowed by this license, to your submission. This agreement embodies the entire agreement of the parties. No modification of this agreement shall be of any effect unless it is made in writing and signed by all of the parties to the agreement.\"\n },\n \"abstract\": \"Differential enhancement of luminal growth factor bioactivity and targeted regional gut growth occurs dependent on dietary protein supplement.\",\n \"authors\": [\n {\n \"author\": \"Tania Marchbank\",\n \"orcid\": \"http://orcid.org/0000-0003-2076-9098\"\n },\n {\n \"author\": \"Nikki Mandir\"\n },\n {\n \"author\": \"Denis Calnan\"\n },\n {\n \"author\": \"Robert A. Goodlad\"\n },\n {\n \"author\": \"Theo Podas\"\n },\n {\n \"author\": \"Raymond J. Playford\",\n \"orcid\": \"http://orcid.org/0000-0003-1235-8504\"\n }\n ],\n \"doi\": \"10.1039/c7fo01251a\",\n \"Embargo-end-date\": \"2018-06-30\",\n \"journal-NLMTA-ID\": \"Food Funct\",\n \"journal-title\": \"Food & Function\",\n \"journal-title-short\": \"Food Funct.\",\n \"issue\": \"1\",\n \"issns\": [\n {\n \"issn\": \"2042-6496\",\n \"pubType\": \"Print\"\n },\n {\n \"issn\": \"2042-650X\",\n \"pubType\": \"Online\"\n }\n ],\n \"publisher\": \"Royal Society of Chemistry (RSC)\",\n \"publicationDate\": \"2018-09-12\",\n \"title\": \"Specific protein supplementation using soya, casein or whey differentially affects regional gut growth and luminal growth factor bioactivity in rats; implications for the treatment of gut injury and stimulating repair\",\n \"volume\": \"9\"\n}", "source": "PASS", @@ -183,9 +201,10 @@ ], "submitter": "2", "grants": [ - "13" + "13", + "14" ], - "id": "14", + "id": "15", "@type": "Submission" }, { @@ -194,8 +213,8 @@ "description": "Custodial content", "fileRole": "SUPPLEMENTAL", "mimeType": "image/jpg", - "submission": "14", - "id": "15", + "submission": "15", + "id": "16", "@type": "File" }, { @@ -204,8 +223,8 @@ "description": "Custodial content", "fileRole": "FIGURE", "mimeType": "image/tiff", - "submission": "14", - "id": "16", + "submission": "15", + "id": "17", "@type": "File" }, { @@ -214,8 +233,8 @@ "description": "Custodial content", "fileRole": "FIGURE", "mimeType": "image/png", - "submission": "14", - "id": "17", + "submission": "15", + "id": "18", "@type": "File" }, { @@ -224,8 +243,8 @@ "description": "Custodial content", "fileRole": "MANUSCRIPT", "mimeType": "application/msword", - "submission": "14", - "id": "18", + "submission": "15", + "id": "19", "@type": "File" }, { @@ -234,8 +253,8 @@ "description": "Custodial content", "fileRole": "TABLE", "mimeType": "application/vnd.oasis.opendocument.spreadsheet", - "submission": "14", - "id": "19", + "submission": "15", + "id": "20", "@type": "File" }, { @@ -244,8 +263,8 @@ "description": "Custodial content, meant to conflict with NIH package spec", "fileRole": "SUPPLEMENTAL", "mimeType": "application/xml", - "submission": "14", - "id": "20", + "submission": "15", + "id": "21", "@type": "File" }, { @@ -254,8 +273,8 @@ "description": "Custodial content, meant to conflict with NIH package spec", "fileRole": "SUPPLEMENTAL", "mimeType": "text/plain", - "submission": "14", - "id": "21", + "submission": "15", + "id": "22", "@type": "File" }, { @@ -264,8 +283,8 @@ "description": "Custodial content with a space in the filename", "fileRole": "SUPPLEMENTAL", "mimeType": "text/plain", - "submission": "14", - "id": "22", + "submission": "15", + "id": "23", "@type": "File" } ] diff --git a/pass-deposit-services/deposit-model/src/main/java/org/eclipse/pass/deposit/model/DepositMetadata.java b/pass-deposit-services/deposit-model/src/main/java/org/eclipse/pass/deposit/model/DepositMetadata.java index 4ea12928..f95ff630 100644 --- a/pass-deposit-services/deposit-model/src/main/java/org/eclipse/pass/deposit/model/DepositMetadata.java +++ b/pass-deposit-services/deposit-model/src/main/java/org/eclipse/pass/deposit/model/DepositMetadata.java @@ -118,6 +118,11 @@ public enum PERSON_TYPE { */ private Article articleMetadata; + /** + * Metadata about the grants that funded the publication + */ + private List grants; + /** * Manuscript-related metadata fields */ @@ -536,6 +541,46 @@ public void setType(PERSON_TYPE type) { } } + public static class Grant { + //this is the grant awardNumber in PASS + String grantId; + String funder; + String funderLocalKey; + Person grantPi; + + public String getGrantId() { + return grantId; + } + + public void setGrantId(String grantId) { + this.grantId = grantId; + } + + public String getFunder() { + return funder; + } + + public void setFunder(String funder) { + this.funder = funder; + } + + public String getFunderLocalKey() { + return funderLocalKey; + } + + public void setFunderLocalKey(String funderLocalKey) { + this.funderLocalKey = funderLocalKey; + } + + public Person getGrantPi() { + return grantPi; + } + + public void setGrantPi(Person grantPi) { + this.grantPi = grantPi; + } + } + public DepositMetadata() { this.manuscriptMetadata = new Manuscript(); this.journalMetadata = new Journal(); @@ -575,6 +620,14 @@ public void setArticleMetadata(Article articleMetadata) { this.articleMetadata = articleMetadata; } + public List getGrantsMetadata() { + return grants; + } + + public void setGrantsMetadata(List grants) { + this.grants = grants; + } + @Override public String toString() { return "DepositMetadata{" +