Merge pull request #9238 from 4Science/main_CST-12825

ROR Integration - Live Import
This commit is contained in:
Tim Donohue
2024-02-14 15:16:23 -06:00
committed by GitHub
13 changed files with 3250 additions and 6 deletions

View File

@@ -11,7 +11,9 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.dspace.content.MetadataFieldName;
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
/**
@@ -94,6 +96,31 @@ public class ImportRecord {
return values;
}
/**
* Returns an {@code Optional<String>} representing the value
* of the metadata {@code field} found inside the {@code valueList}.
* @param field String of the MetadataField to search
* @return {@code Optional<String>} non empty if found.
*/
public Optional<String> getSingleValue(String field) {
MetadataFieldName metadataFieldName = new MetadataFieldName(field);
return getSingleValue(metadataFieldName.schema, metadataFieldName.element, metadataFieldName.qualifier);
}
/**
* Retrieves a single value for the given schema, element, and qualifier.
*
* @param schema the schema for the value
* @param element the element for the value
* @param qualifier the qualifier for the value
* @return an optional containing the single value, if present
*/
public Optional<String> getSingleValue(String schema, String element, String qualifier) {
return getValue(schema, element, qualifier).stream()
.map(MetadatumDTO::getValue)
.findFirst();
}
/**
* Add a value to the valueList
*

View File

@@ -0,0 +1,130 @@
/**
* 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.importer.external.metadatamapping.contributor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dspace.importer.external.metadatamapping.MetadatumDTO;
/**
* A ROR JsonPath Metadata processor that should be configured inside the {@code ror-integration.xml} file.
* This allows the extraction of a given contributor with a specific mappings from the ROR JSON response.
*
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
*/
public class RorParentOrgUnitMetadataContributor extends SimpleJsonPathMetadataContributor {
/**
* Determines which field of the JSON detains the {@code type} of this
* specific node (that needs to be mapped).
*
*/
private String typeField;
/**
* Determines which is the type of the main parent node that needs to be mapped.
* It should match the value of the {@code typeField} of the JSON node.
*
*/
private String parentType;
/**
* Determines which is the field of the JSON that contains the value
* that needs to be mapped into a {@code MetadatumDTO}.
*/
private String labelField;
/**
* Creates a {@code MetadatumDTO} for each correctly mapped JSON node
* of the ROR response.
* Partial / Unmatched parent-type metadatum will be ignored from this mapping.
*
* @param fullJson ROR response
* @return a collection of read ROR metadata.
*/
@Override
public Collection<MetadatumDTO> contributeMetadata(String fullJson) {
Collection<MetadatumDTO> metadata = new ArrayList<>();
Collection<String> metadataValue = new ArrayList<>();
JsonNode jsonNode = convertStringJsonToJsonNode(fullJson);
JsonNode array = jsonNode.at(getQuery());
if (!array.isArray()) {
return metadata;
}
Iterator<JsonNode> nodes = array.iterator();
while (nodes.hasNext()) {
JsonNode node = nodes.next();
if (!node.has(labelField)) {
continue;
}
String type = node.has(typeField) ? node.get(typeField).asText() : null;
String label = node.get(labelField).asText();
if (parentType.equalsIgnoreCase(type)) {
metadataValue.add(label);
}
}
for (String value : metadataValue) {
MetadatumDTO metadatumDto = new MetadatumDTO();
metadatumDto.setValue(value);
metadatumDto.setElement(getField().getElement());
metadatumDto.setQualifier(getField().getQualifier());
metadatumDto.setSchema(getField().getSchema());
metadata.add(metadatumDto);
}
return metadata;
}
private JsonNode convertStringJsonToJsonNode(String json) {
ObjectMapper mapper = new ObjectMapper();
JsonNode body = null;
try {
body = mapper.readTree(json);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return body;
}
public String getTypeField() {
return typeField;
}
public void setTypeField(String typeField) {
this.typeField = typeField;
}
public String getLabelField() {
return labelField;
}
public void setLabelField(String labelField) {
this.labelField = labelField;
}
public String getParentType() {
return parentType;
}
public void setParentType(String parentType) {
this.parentType = parentType;
}
}

View File

@@ -0,0 +1,38 @@
/**
* 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.importer.external.ror.service;
import java.util.Map;
import javax.annotation.Resource;
import org.dspace.importer.external.metadatamapping.AbstractMetadataFieldMapping;
/**
* An implementation of {@link AbstractMetadataFieldMapping}
* Responsible for defining the mapping of the ROR metadatum fields on the DSpace metadatum fields
*
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
*/
public class RorFieldMapping extends AbstractMetadataFieldMapping {
/**
* Defines which metadatum is mapped on which metadatum. Note that while the key must be unique it
* only matters here for postprocessing of the value. The mapped MetadatumContributor has full control over
* what metadatafield is generated.
*
* @param metadataFieldMap The map containing the link between retrieve metadata and metadata that will be set to
* the item.
*/
@Override
@Resource(name = "rorMetadataFieldMap")
public void setMetadataFieldMap(Map metadataFieldMap) {
super.setMetadataFieldMap(metadataFieldMap);
}
}

View File

@@ -0,0 +1,278 @@
/**
* 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.importer.external.ror.service;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import javax.el.MethodNotFoundException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.URIBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.content.Item;
import org.dspace.importer.external.datamodel.ImportRecord;
import org.dspace.importer.external.datamodel.Query;
import org.dspace.importer.external.exception.MetadataSourceException;
import org.dspace.importer.external.liveimportclient.service.LiveImportClient;
import org.dspace.importer.external.service.AbstractImportMetadataSourceService;
import org.dspace.importer.external.service.components.QuerySource;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Implements a {@code AbstractImportMetadataSourceService} for querying ROR services.
*
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
*/
public class RorImportMetadataSourceServiceImpl extends AbstractImportMetadataSourceService<String>
implements QuerySource {
private final static Logger log = LogManager.getLogger();
protected static final String ROR_IDENTIFIER_PREFIX = "https://ror.org/";
private String url;
private int timeout = 1000;
@Autowired
private LiveImportClient liveImportClient;
@Override
public String getImportSource() {
return "ror";
}
@Override
public ImportRecord getRecord(String id) throws MetadataSourceException {
List<ImportRecord> records = retry(new SearchByIdCallable(id));
return CollectionUtils.isEmpty(records) ? null : records.get(0);
}
@Override
public int getRecordsCount(String query) throws MetadataSourceException {
return retry(new CountByQueryCallable(query));
}
@Override
public int getRecordsCount(Query query) throws MetadataSourceException {
return retry(new CountByQueryCallable(query));
}
@Override
public Collection<ImportRecord> getRecords(String query, int start, int count) throws MetadataSourceException {
return retry(new SearchByQueryCallable(query));
}
@Override
public Collection<ImportRecord> getRecords(Query query) throws MetadataSourceException {
return retry(new SearchByQueryCallable(query));
}
@Override
public ImportRecord getRecord(Query query) throws MetadataSourceException {
List<ImportRecord> records = retry(new SearchByIdCallable(query));
return CollectionUtils.isEmpty(records) ? null : records.get(0);
}
@Override
public Collection<ImportRecord> findMatchingRecords(Query query) throws MetadataSourceException {
throw new MethodNotFoundException("This method is not implemented for ROR");
}
@Override
public Collection<ImportRecord> findMatchingRecords(Item item) throws MetadataSourceException {
throw new MethodNotFoundException("This method is not implemented for ROR");
}
@Override
public void init() throws Exception {
}
/**
* This class is a Callable implementation to get ROR entries based on query
* object. This Callable use as query value the string queryString passed to
* constructor. If the object will be construct through Query.class instance, a
* Query's map entry with key "query" will be used. Pagination is supported too,
* using the value of the Query's map with keys "start" and "count".
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
*/
private class SearchByQueryCallable implements Callable<List<ImportRecord>> {
private Query query;
private SearchByQueryCallable(String queryString) {
query = new Query();
query.addParameter("query", queryString);
}
private SearchByQueryCallable(Query query) {
this.query = query;
}
@Override
public List<ImportRecord> call() throws Exception {
return search(query.getParameterAsClass("query", String.class));
}
}
/**
* This class is a Callable implementation to get an ROR entry using bibcode The
* bibcode to use can be passed through the constructor as a String or as
* Query's map entry, with the key "id".
*
* @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com)
*/
private class SearchByIdCallable implements Callable<List<ImportRecord>> {
private Query query;
private SearchByIdCallable(Query query) {
this.query = query;
}
private SearchByIdCallable(String id) {
this.query = new Query();
query.addParameter("id", id);
}
@Override
public List<ImportRecord> call() throws Exception {
return searchById(query.getParameterAsClass("id", String.class));
}
}
/**
* This class is a Callable implementation to count the number of entries for a
* ROR query. This Callable uses as query value to ROR the string queryString
* passed to constructor. If the object will be construct through {@code Query}
* instance, the value of the Query's map with the key "query" will be used.
*
* @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com)
*/
private class CountByQueryCallable implements Callable<Integer> {
private Query query;
private CountByQueryCallable(String queryString) {
query = new Query();
query.addParameter("query", queryString);
}
private CountByQueryCallable(Query query) {
this.query = query;
}
@Override
public Integer call() throws Exception {
return count(query.getParameterAsClass("query", String.class));
}
}
/**
* Counts the number of results for the given query.
*
* @param query the query string to count results for
* @return the number of results for the given query
*/
public Integer count(String query) {
try {
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
URIBuilder uriBuilder = new URIBuilder(this.url);
uriBuilder.addParameter("query", query);
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
if (StringUtils.isEmpty(resp)) {
return 0;
}
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
return jsonNode.at("/number_of_results").asInt();
} catch (URISyntaxException e) {
e.printStackTrace();
}
return 0;
}
private List<ImportRecord> searchById(String id) {
List<ImportRecord> importResults = new ArrayList<>();
id = StringUtils.removeStart(id, ROR_IDENTIFIER_PREFIX);
try {
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
URIBuilder uriBuilder = new URIBuilder(this.url + "/" + id);
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
if (StringUtils.isEmpty(resp)) {
return importResults;
}
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
importResults.add(transformSourceRecords(jsonNode.toString()));
} catch (URISyntaxException e) {
e.printStackTrace();
}
return importResults;
}
private List<ImportRecord> search(String query) {
List<ImportRecord> importResults = new ArrayList<>();
try {
Map<String, Map<String, String>> params = new HashMap<String, Map<String, String>>();
URIBuilder uriBuilder = new URIBuilder(this.url);
uriBuilder.addParameter("query", query);
String resp = liveImportClient.executeHttpGetRequest(timeout, uriBuilder.toString(), params);
if (StringUtils.isEmpty(resp)) {
return importResults;
}
JsonNode jsonNode = convertStringJsonToJsonNode(resp);
JsonNode docs = jsonNode.at("/items");
if (docs.isArray()) {
Iterator<JsonNode> nodes = docs.elements();
while (nodes.hasNext()) {
JsonNode node = nodes.next();
importResults.add(transformSourceRecords(node.toString()));
}
} else {
importResults.add(transformSourceRecords(docs.toString()));
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
return importResults;
}
private JsonNode convertStringJsonToJsonNode(String json) {
try {
return new ObjectMapper().readTree(json);
} catch (JsonProcessingException e) {
log.error("Unable to process json response.", e);
}
return null;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@@ -150,6 +150,12 @@
<property name="viewMode" value="${scopus.search-api.viewMode}"/>
</bean>
<bean id="scopusMetadataFieldMapping" class="org.dspace.importer.external.scopus.service.ScopusFieldMapping"/>
<bean id="rorImportService" class="org.dspace.importer.external.ror.service.RorImportMetadataSourceServiceImpl">
<property name="metadataFieldMapping" ref="rorMetadataFieldMapping"/>
<property name="url" value="${ror.orgunit-import.api-url}"/>
</bean>
<bean id="rorMetadataFieldMapping" class="org.dspace.importer.external.ror.service.RorFieldMapping"/>
<bean id="vufindImportService" class="org.dspace.importer.external.vufind.VuFindImportMetadataSourceServiceImpl" scope="singleton">
<!-- Set to empty to use the default set of fields -->

View File

@@ -0,0 +1,159 @@
/**
* 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.app.rest;
import static org.dspace.app.matcher.LambdaMatcher.matches;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Optional;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.impl.client.CloseableHttpClient;
import org.dspace.importer.external.datamodel.ImportRecord;
import org.dspace.importer.external.liveimportclient.service.LiveImportClientImpl;
import org.dspace.importer.external.ror.service.RorImportMetadataSourceServiceImpl;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.mockito.ArgumentMatchers;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
public class RorImportMetadataSourceServiceIT extends AbstractLiveImportIntegrationTest {
@Autowired
private LiveImportClientImpl liveImportClient;
@Autowired
private RorImportMetadataSourceServiceImpl rorServiceImpl;
@Test
public void tesGetRecords() throws Exception {
context.turnOffAuthorisationSystem();
CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
try (InputStream file = getClass().getResourceAsStream("ror-records.json")) {
String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
liveImportClient.setHttpClient(httpClient);
CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
context.restoreAuthSystemState();
Collection<ImportRecord> recordsImported = rorServiceImpl.getRecords("test query", 0, 2);
assertThat(recordsImported, hasSize(20));
ImportRecord record = recordsImported.iterator().next();
assertThat(record.getValueList(), hasSize(9));
assertThat(
record.getSingleValue("organization.legalName"),
is("The University of Texas")
);
assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/02f6dcw23"));
assertThat(record.getSingleValue("dc.type"), is("Education"));
assertThat(record.getSingleValue("organization.address.addressCountry"), is("US"));
assertThat(record.getSingleValue("organization.foundingDate"), is("1959"));
assertThat(record.getValue("organization", "identifier", "crossrefid"), hasSize(2));
assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0001 0629 5880"));
assertThat(record.getSingleValue("organization.parentOrganization"), is("The University of Texas System"));
} finally {
liveImportClient.setHttpClient(originalHttpClient);
}
}
@Test
public void tesCount() throws Exception {
context.turnOffAuthorisationSystem();
CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
try (InputStream file = getClass().getResourceAsStream("ror-records.json")) {
String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
liveImportClient.setHttpClient(httpClient);
CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
context.restoreAuthSystemState();
Integer count = rorServiceImpl.count("test");
assertThat(count, equalTo(200));
} finally {
liveImportClient.setHttpClient(originalHttpClient);
}
}
@Test
public void tesGetRecord() throws Exception {
context.turnOffAuthorisationSystem();
CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
try (InputStream file = getClass().getResourceAsStream("ror-record.json")) {
String jsonResponse = IOUtils.toString(file, Charset.defaultCharset());
liveImportClient.setHttpClient(httpClient);
CloseableHttpResponse response = mockResponse(jsonResponse, 200, "OK");
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
context.restoreAuthSystemState();
ImportRecord record = rorServiceImpl.getRecord("https://ror.org/01sps7q28");
assertThat(record.getValueList(), hasSize(7));
assertThat(
record.getSingleValue("organization.legalName"),
is("The University of Texas Health Science Center at Tyler")
);
assertThat(record.getSingleValue("organization.identifier.ror"), is("https://ror.org/01sps7q28"));
assertThat(record.getSingleValue("dc.type"), is("Healthcare"));
assertThat(record.getSingleValue("organization.address.addressCountry"), is("US"));
assertThat(record.getSingleValue("organization.foundingDate"), is("1947"));
assertThat(record.getSingleValue("organization.identifier.isni"), is("0000 0000 9704 5790"));
assertThat(record.getSingleValue("organization.parentOrganization"), is("The University of Texas System"));
} finally {
liveImportClient.setHttpClient(originalHttpClient);
}
}
@Test
public void tesGetRecordsCount() throws Exception {
context.turnOffAuthorisationSystem();
CloseableHttpClient originalHttpClient = liveImportClient.getHttpClient();
CloseableHttpClient httpClient = Mockito.mock(CloseableHttpClient.class);
try (InputStream rorResponse = getClass().getResourceAsStream("ror-records.json")) {
String rorJsonResponse = IOUtils.toString(rorResponse, Charset.defaultCharset());
liveImportClient.setHttpClient(httpClient);
CloseableHttpResponse response = mockResponse(rorJsonResponse, 200, "OK");
when(httpClient.execute(ArgumentMatchers.any())).thenReturn(response);
context.restoreAuthSystemState();
int tot = rorServiceImpl.getRecordsCount("test query");
assertEquals(200, tot);
} finally {
liveImportClient.setHttpClient(originalHttpClient);
}
}
private Matcher<Optional<String>> is(String value) {
return matches(optionalValue -> optionalValue.isPresent() && optionalValue.get().equals(value));
}
}

View File

@@ -0,0 +1,107 @@
{
"id": "https://ror.org/01sps7q28",
"name": "The University of Texas Health Science Center at Tyler",
"email_address": null,
"ip_addresses": [
],
"established": 1947,
"types": [
"Healthcare"
],
"relationships": [
{
"label": "The University of Texas System",
"type": "Parent",
"id": "https://ror.org/01gek1696"
}
],
"addresses": [
{
"lat": 32.426014,
"lng": -95.212728,
"state": "Texas",
"state_code": "US-TX",
"city": "Tyler",
"geonames_city": {
"id": 4738214,
"city": "Tyler",
"geonames_admin1": {
"name": "Texas",
"id": 4736286,
"ascii_name": "Texas",
"code": "US.TX"
},
"geonames_admin2": {
"name": "Smith County",
"id": 4729130,
"ascii_name": "Smith County",
"code": "US.TX.423"
},
"license": {
"attribution": "Data from geonames.org under a CC-BY 3.0 license",
"license": "http://creativecommons.org/licenses/by/3.0/"
},
"nuts_level1": {
"name": null,
"code": null
},
"nuts_level2": {
"name": null,
"code": null
},
"nuts_level3": {
"name": null,
"code": null
}
},
"postcode": null,
"primary": false,
"line": null,
"country_geonames_id": 6252001
}
],
"links": [
"https://www.utsystem.edu/institutions/university-texas-health-science-center-tyler"
],
"aliases": [
"East Texas Tuberculosis Sanitarium",
"UT Health Northeast"
],
"acronyms": [
"UTHSCT"
],
"status": "active",
"wikipedia_url": "https://en.wikipedia.org/wiki/University_of_Texas_Health_Science_Center_at_Tyler",
"labels": [
],
"country": {
"country_name": "United States",
"country_code": "US"
},
"external_ids": {
"ISNI": {
"preferred": null,
"all": [
"0000 0000 9704 5790"
]
},
"OrgRef": {
"preferred": null,
"all": [
"3446655"
]
},
"Wikidata": {
"preferred": null,
"all": [
"Q7896437"
]
},
"GRID": {
"preferred": "grid.267310.1",
"all": "grid.267310.1"
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -92,3 +92,8 @@ wos.url.search = https://wos-api.clarivate.com/api/wos/?databaseId=WOS&lang=en&u
datacite.url = https://api.datacite.org/dois/
datacite.timeout = 180000
#################################################################
#--------------------------- ROR -------------------------------#
#---------------------------------------------------------------#
ror.orgunit-import.api-url = https://api.ror.org/organizations
#################################################################

View File

@@ -80,6 +80,12 @@
<qualifier>crossrefid</qualifier>
<scope_note>Crossref identifier</scope_note>
</dc-type>
<dc-type>
<schema>organization</schema>
<element>parentOrganization</element>
<scope_note>The larger organization that this organization is a subOrganization of, if any.</scope_note>
</dc-type>
</dspace-dc-types>

View File

@@ -93,7 +93,7 @@
</list>
</property>
</bean>
<bean id="orcidPublicationDataProvider" class="org.dspace.external.provider.impl.OrcidPublicationDataProvider">
<property name="sourceIdentifier" value="orcidWorks"/>
<property name="fieldMapping" ref="orcidPublicationDataProviderFieldMapping"/>
@@ -199,6 +199,18 @@
</list>
</property>
</bean>
<bean id="rorDataProvider" class="org.dspace.external.provider.impl.LiveImportDataProvider" >
<property name="metadataSource" ref="rorImportService"/>
<property name="sourceIdentifier" value="ror"/>
<property name="recordIdMetadata" value="organization.identifier.ror"/>
<property name="displayMetadata" value="organization.legalName"/>
<property name="supportedEntityTypes">
<list>
<value>OrgUnit</value>
</list>
</property>
</bean>
<bean id="wosLiveImportDataProvider" class="org.dspace.external.provider.impl.LiveImportDataProvider">
<property name="metadataSource" ref="WosImportService"/>

View File

@@ -0,0 +1,93 @@
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-autowire-candidates="*Service,*DAO,javax.sql.DataSource">
<context:annotation-config/>
<!-- allows us to use spring annotations in beans -->
<util:map id="rorMetadataFieldMap" key-type="org.dspace.importer.external.metadatamapping.MetadataFieldConfig" value-type="org.dspace.importer.external.metadatamapping.contributor.MetadataContributor">
<entry key-ref="ror.title" value-ref="rorTitleContrib"/>
<entry key-ref="ror.identifier" value-ref="rorIdentifierContrib"/>
<entry key-ref="ror.types" value-ref="rorTypesContrib"/>
<entry key-ref="ror.country" value-ref="rorCountryContrib"/>
<entry key-ref="ror.established" value-ref="rorEstablishedContrib"/>
<entry key-ref="ror.fundref" value-ref="rorFundRefContrib"/>
<entry key-ref="ror.isni" value-ref="rorISNIContrib"/>
<entry key-ref="ror.parent" value-ref="rorParentContrib"/>
</util:map>
<bean id="rorTitleContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.title"/>
<property name="query" value="/name"/>
</bean>
<bean id="ror.title" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.legalName"/>
</bean>
<bean id="rorIdentifierContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.identifier"/>
<property name="query" value="/id"/>
</bean>
<bean id="ror.identifier" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.identifier.ror"/>
</bean>
<bean id="rorTypesContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.types"/>
<property name="query" value="/types"/>
</bean>
<bean id="ror.types" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="dc.type"/>
</bean>
<bean id="rorCountryContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.country"/>
<property name="query" value="/country/country_code"/>
</bean>
<bean id="ror.country" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.address.addressCountry"/>
</bean>
<bean id="rorEstablishedContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.established"/>
<property name="query" value="/established"/>
</bean>
<bean id="ror.established" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.foundingDate"/>
</bean>
<bean id="rorFundRefContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.fundref"/>
<property name="query" value="/external_ids/FundRef/all"/>
</bean>
<bean id="ror.fundref" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.identifier.crossrefid"/>
</bean>
<bean id="rorISNIContrib" class="org.dspace.importer.external.metadatamapping.contributor.SimpleJsonPathMetadataContributor">
<property name="field" ref="ror.isni"/>
<property name="query" value="/external_ids/ISNI/all"/>
</bean>
<bean id="ror.isni" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.identifier.isni"/>
</bean>
<bean id="rorParentContrib" class="org.dspace.importer.external.metadatamapping.contributor.RorParentOrgUnitMetadataContributor">
<property name="field" ref="ror.parent"/>
<property name="query" value="/relationships"/>
<property name="typeField" value="type"/>
<property name="labelField" value="label"/>
<property name="parentType" value="Parent"/>
</bean>
<bean id="ror.parent" class="org.dspace.importer.external.metadatamapping.MetadataFieldConfig">
<constructor-arg value="organization.parentOrganization"/>
</bean>
</beans>

View File

@@ -531,18 +531,18 @@
</row>
<row>
<relation-field>
<relationship-type>isProjectOfOrgUnit</relationship-type>
<relationship-type>isOrgUnitOfProject</relationship-type>
<search-configuration>orgunit</search-configuration>
<repeatable>false</repeatable>
<label>Organization</label>
<hint>Enter the organization's name</hint>
<linked-metadata-field>
<dc-schema>project</dc-schema>
<dc-element>funder</dc-element>
<dc-qualifier>name</dc-qualifier>
<dc-schema>dc</dc-schema>
<dc-element>contributor</dc-element>
<dc-qualifier>other</dc-qualifier>
<input-type>onebox</input-type>
</linked-metadata-field>
<externalsources/>
<externalsources>ror</externalsources>
</relation-field>
</row>
<row>