Skip to content

Commit

Permalink
Merged in DSC-1243 (pull request DSpace#1139)
Browse files Browse the repository at this point in the history
[DSC-1243] added virtualField generator for primary and alt dois
  • Loading branch information
aliaksei.bykau authored and corrad82-4s committed Oct 5, 2023
2 parents 8f75b72 + 81326f2 commit 3ca6ca7
Show file tree
Hide file tree
Showing 14 changed files with 331 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content.integration.crosswalks.virtualfields;

import java.util.Comparator;
import java.util.List;

import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.service.ItemService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;


public class ItemDOIService {
static final String CFG_PREFIX = "identifier.doi.prefix";

static final String DOI_METADATA = "dc.identifier.doi";

@Autowired
protected ItemService itemService;
@Autowired
private ConfigurationService configurationService;

public String[] getAlternativeDOIFromItem(Item item) {
List<MetadataValue> metadataValueList = itemService.getMetadataByMetadataString(item, DOI_METADATA);
return getAlternativeDOI(metadataValueList, getPrimaryDOI(metadataValueList));
}
private String[] getAlternativeDOI(List<MetadataValue> metadataValueList, String primaryValue) {
return metadataValueList.stream().map(MetadataValue::getValue)
.filter(value -> !value.equals(primaryValue)).toArray(String[]::new);
}

public String getPrimaryDOIFromItem(Item item) {
return getPrimaryDOI(itemService.getMetadataByMetadataString(item, DOI_METADATA));
}

private String getPrimaryDOI(List<MetadataValue> metadataValueList) {
return metadataValueList.stream().filter(metadata -> metadata.getValue().contains(getPrefix()))
.min(Comparator.comparingInt(MetadataValue::getPlace)).map(MetadataValue::getValue)
.orElse(!metadataValueList.isEmpty() ? metadataValueList.get(0).getValue() : null);
}

protected String getPrefix() {
String prefix;
prefix = this.configurationService.getProperty(CFG_PREFIX);
if (null == prefix) {
throw new RuntimeException("Unable to load DOI prefix from "
+ "configuration. Cannot find property " +
CFG_PREFIX + ".");
}
return prefix;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content.integration.crosswalks.virtualfields;

import org.apache.commons.lang3.StringUtils;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;


public class VirtualFieldAlternativeDOI implements VirtualField {

@Autowired
private ItemDOIService itemDOIService;

@Override
public String[] getMetadata(Context context, Item item, String fieldName) {
String[] qualifiers = StringUtils.split(fieldName, ".");
if (qualifiers.length != 3) {
throw new IllegalArgumentException("Invalid field name " + fieldName);
}

return itemDOIService.getAlternativeDOIFromItem(item);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.content.integration.crosswalks.virtualfields;

import org.apache.commons.lang3.StringUtils;
import org.dspace.content.Item;
import org.dspace.core.Context;
import org.springframework.beans.factory.annotation.Autowired;


public class VirtualFieldPrimaryDOI implements VirtualField {

@Autowired
private ItemDOIService itemDOIService;

@Override
public String[] getMetadata(Context context, Item item, String fieldName) {
String[] qualifiers = StringUtils.split(fieldName, ".");
if (qualifiers.length != 3) {
throw new IllegalArgumentException("Invalid field name " + fieldName);
}

return new String[] {itemDOIService.getPrimaryDOIFromItem(item)};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"primary-doi": "@virtual.primary-doi.dc-identifier-doi@",
"alternative-doi": "@virtual.alternative-doi.dc-identifier-doi@",
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@
<property name="linesPostProcessor" ref="jsonPostProcessor" />
</bean>

<bean class="org.dspace.content.integration.crosswalks.ReferCrosswalk" id="referCrosswalkVirtualFieldDOI">
<property name="templateFileName" value="crosswalks/template/virtual-field-doi-json.template"/>
<property name="mimeType" value="application/json; charset=UTF-8"/>
<property name="fileName" value="test.json"/>
<property name="entityType" value="Publication"/>
<property name="crosswalkMode" value="#{T(org.dspace.content.crosswalk.CrosswalkMode).SINGLE}"/>
<property name="converter" ref="jsonValueConverter" />
<property name="linesPostProcessor" ref="jsonPostProcessor" />
</bean>

<bean class="org.dspace.content.integration.crosswalks.ReferCrosswalk" id="referCrosswalkVirtualFieldVocabulary">
<property name="templateFileName" value="crosswalks/template/virtual-field-vocabulary-json.template"/>
<property name="mimeType" value="application/json; charset=UTF-8"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import org.dspace.eperson.EPerson;
import org.dspace.layout.CrisLayoutBox;
import org.dspace.layout.LayoutSecurity;
import org.dspace.services.ConfigurationService;
import org.dspace.utils.DSpace;
import org.json.JSONObject;
import org.junit.After;
Expand All @@ -82,6 +83,7 @@
*
*/
public class ReferCrosswalkIT extends AbstractIntegrationTestWithDatabase {
static final String CFG_PREFIX = "identifier.doi.prefix";

private static final String BASE_OUTPUT_DIR_PATH = "./target/testing/dspace/assetstore/crosswalk/";

Expand All @@ -99,6 +101,8 @@ public class ReferCrosswalkIT extends AbstractIntegrationTestWithDatabase {

private VirtualField virtualFieldId;

private ConfigurationService configurationService;

@Before
public void setup() throws SQLException, AuthorizeException {

Expand All @@ -117,6 +121,8 @@ public void setup() throws SQLException, AuthorizeException {
when(mockedVirtualFieldId.getMetadata(any(), any(), any())).thenReturn(new String[] { "mock-id" });
this.virtualFieldMapper.setVirtualField("id", mockedVirtualFieldId);

this.configurationService = new DSpace().getSingletonService(ConfigurationService.class);

context.turnOffAuthorisationSystem();
community = createCommunity(context).build();
collection = createCollection(context, community).withAdminGroup(eperson).build();
Expand Down Expand Up @@ -2530,6 +2536,82 @@ public void testVirtualBitstreamFieldWithProject() throws Exception {
assertThat(resultLines[54].trim(), equalTo("</project>"));
}

@Test
public void testExportToDataciteFormatItemWithThreeDOI() throws Exception {
String prefix;
prefix = this.configurationService.getProperty(CFG_PREFIX);
if (null == prefix) {
throw new RuntimeException("Unable to load DOI prefix from "
+ "configuration. Cannot find property " +
CFG_PREFIX + ".");
}

context.turnOffAuthorisationSystem();

Item publication = createItem(context, collection)
.withEntityType("Publication")
.withTitle("publication title")
.withDoiIdentifier("test doi")
.withDoiIdentifier("test doi2")
.withDoiIdentifier("test" + prefix + "test")
.build();

context.restoreAuthSystemState();

ReferCrosswalk referCrosswalk = new DSpace().getServiceManager()
.getServiceByName("referCrosswalkVirtualFieldDOI", ReferCrosswalk.class);
assertThat(referCrosswalk, notNullValue());

ByteArrayOutputStream out = new ByteArrayOutputStream();
referCrosswalk.disseminate(context, publication, out);

String[] resultLines = out.toString().split("\n");

assertThat(resultLines.length, is(5));
assertThat(resultLines[0].trim(), is("{"));
assertThat(resultLines[1].trim(), is("\"primary-doi\": \"test" + prefix + "test\","));
assertThat(resultLines[2].trim(), is("\"alternative-doi\": \"test doi\","));
assertThat(resultLines[3].trim(), is("\"alternative-doi\": \"test doi2\""));
assertThat(resultLines[4].trim(), is("}"));
}

@Test
public void testExportToDataciteFormatItemWithSingleDOINotMatchingPrefix() throws Exception {
String prefix;
prefix = this.configurationService.getProperty(CFG_PREFIX);
if (null == prefix) {
throw new RuntimeException("Unable to load DOI prefix from "
+ "configuration. Cannot find property " +
CFG_PREFIX + ".");
}

context.turnOffAuthorisationSystem();

Item publication = createItem(context, collection)
.withEntityType("Publication")
.withTitle("publication title")
.withDoiIdentifier("test doi")
.build();

context.restoreAuthSystemState();

ReferCrosswalk referCrosswalk = new DSpace().getServiceManager()
.getServiceByName("referCrosswalkVirtualFieldDOI", ReferCrosswalk.class);
assertThat(referCrosswalk, notNullValue());

ByteArrayOutputStream out = new ByteArrayOutputStream();
referCrosswalk.disseminate(context, publication, out);

String[] resultLines = out.toString().split("\n");

assertThat(resultLines.length, is(3));
assertThat(resultLines[0].trim(), is("{"));
assertThat(resultLines[1].trim(), is("\"primary-doi\": \"test doi\""));
assertThat(resultLines[2].trim(), is("}"));
}




private void createSelectedRelationship(Item author, Item publication, RelationshipType selectedRelationshipType) {
createRelationshipBuilder(context, publication, author, selectedRelationshipType, -1, -1).build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.xoai.app;

import java.util.Arrays;
import java.util.List;

import com.lyncode.xoai.dataprovider.xml.xoai.Element;
import com.lyncode.xoai.dataprovider.xml.xoai.Metadata;
import org.apache.commons.lang.StringUtils;
import org.dspace.content.Item;
import org.dspace.content.integration.crosswalks.virtualfields.ItemDOIService;
import org.dspace.core.Context;
import org.dspace.xoai.util.ItemUtils;
import org.springframework.beans.factory.annotation.Autowired;


/**
* XOAIExtensionItemCompilePlugin aims to add structured information about the
* DOIs of the item (if any).
* The xoai document will be enriched with a structure like that
* <code>
* <element name="other">
* <element name="datacite">
* <element name="primary">
* <field name="doi"></field>
* </element>
* <element name="alternative">
* <field name="doi"></field>
* ...
* <field name="doi"></field>
* </element>
* </element>
* </element>
* </code>
*
*/
public class DataciteDOIItemCompilePlugin implements XOAIExtensionItemCompilePlugin {

@Autowired
private ItemDOIService itemDOIService;

@Override
public Metadata additionalMetadata(Context context, Metadata metadata, Item item) {
String primaryDoiValue = itemDOIService.getPrimaryDOIFromItem(item);
String[] alternativeDoiValue = itemDOIService.getAlternativeDOIFromItem(item);
Element datacite = ItemUtils.create("datacite");
if (StringUtils.isNotBlank(primaryDoiValue)) {
Element primary = ItemUtils.create("primary");
datacite.getElement().add(primary);
primary.getField().add(ItemUtils.createValue("doi", primaryDoiValue));
if (alternativeDoiValue != null && alternativeDoiValue.length != 0) {
Element alternative = ItemUtils.create("alternative");
datacite.getElement().add(alternative);
Arrays.stream(alternativeDoiValue)
.forEach(value -> alternative.getField().add(ItemUtils.createValue("doi", value)));
}
Element other;
List<Element> elements = metadata.getElement();
if (ItemUtils.getElement(elements, "others") != null) {
other = ItemUtils.getElement(elements, "others");
} else {
other = ItemUtils.create("others");
}
other.getElement().add(datacite);
}
return metadata;
}

}
28 changes: 28 additions & 0 deletions dspace/config/crosswalks/oai/metadataFormats/oai_openaire.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,14 @@
<!-- CREATIVE COMMON LICENSE -->
<xsl:apply-templates
select="doc:metadata/doc:element[@name='others']/doc:element[@name='cc']" mode="oaire" />
<!-- primary doi identifier -->
<xsl:apply-templates
select="doc:metadata/doc:element[@name='others']/doc:element[@name='datacite']/doc:element[@name='primary']"
mode="datacite" />
<!-- alternative doi identifiers-->
<xsl:apply-templates
select="doc:metadata/doc:element[@name='others']/doc:element[@name='datacite']/doc:element[@name='alternative']"
mode="datacite_altid" />
</oaire:resource>
</xsl:template>

Expand Down Expand Up @@ -1668,6 +1676,26 @@
</oaire:licenseCondition>
</xsl:template>

<xsl:template match="doc:element[@name='others']/doc:element[@name='datacite']/doc:element[@name='primary']"
mode="datacite">
<!-- only process the first element -->
<datacite:identifier>
<xsl:attribute name="identifierType">doi</xsl:attribute>
<xsl:value-of select="./doc:field[@name='doi']/text()"/>
</datacite:identifier>
</xsl:template>

<!-- for each alternative doi -->
<xsl:template match="doc:element[@name='others']/doc:element[@name='datacite']/doc:element[@name='alternative']" mode="datacite_altid">
<xsl:for-each select="./doc:field[@name='doi']">

<datacite:alternateIdentifier>
<xsl:attribute name="alternateIdentifierType">doi</xsl:attribute>
<xsl:value-of select="./text()"/>
</datacite:alternateIdentifier>
</xsl:for-each>
</xsl:template>

<!-- ignore all non specified text values or attributes -->
<xsl:template match="text()|@*"/>
<xsl:template match="text()|@*" mode="oaire"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://datacite.org/schema/kernel-4" xsi:schemaLocation="http://datacite.org/schema/kernel-4 http://schema.datacite.org/meta/kernel-4.4/metadata.xsd">
<identifier identifierType="DOI">@dc.identifier.doi@</identifier>
<identifier identifierType="DOI">@virtual.primary-doi.dc-identifier-doi@</identifier>
<creators>
@group.dc-contributor-author.start@
<creator>
Expand Down Expand Up @@ -28,6 +28,7 @@
<resourceType resourceTypeGeneral="@virtual.mapConverter.type2datacite.dc-type@" />
<alternateIdentifiers>
<alternateIdentifier alternateIdentifierType="url">@dc.identifier.uri@</alternateIdentifier>
<alternateIdentifier alternateIdentifierType="doi">@virtual.alternative-doi.dc-identifier-doi@</alternateIdentifier>
</alternateIdentifiers>
<version>@dc.description.version@</version>
<rightsList>
Expand Down
Loading

0 comments on commit 3ca6ca7

Please sign in to comment.