Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions osf/metadata/schemas/datacite.json
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,9 @@
"ROR",
"Other"
]
},
"schemeURI": {
"$ref": "#/definitions/uri"
}
},
"additionalProperties": false,
Expand Down
16 changes: 9 additions & 7 deletions osf/metadata/serializers/datacite/datacite_tree_walker.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,12 @@ def _identifier_type_and_value(self, identifier: str):
return ('URL', identifier)
logger.warning('skipping non-IRI-shaped identifier "%s"', identifier)

def _funder_identifier_type(self, identifier: str):
def _funder_identifier_type_and_scheme(self, identifier: str):
if identifier.startswith(DxDOI) or identifier.startswith(DOI):
return 'Crossref Funder ID'
return ('Crossref Funder ID', 'https://www.crossref.org/services/funder-registry/')
if identifier.startswith(ROR):
return 'ROR'
return 'Other'
return ('ROR', str(ROR))
return ('Other', '')

def _get_name_type(self, agent_iri):
if (agent_iri, RDF.type, FOAF.Person) in self.basket:
Expand Down Expand Up @@ -312,13 +312,15 @@ def _funding_reference(self, fundrefs_el, funder, funding_award=None):
_fundref_el = self.visit(fundrefs_el, 'fundingReference')
self.visit(_fundref_el, 'funderName', text=next(self.basket[funder:FOAF.name], ''))
_funder_identifier = next(self.basket[funder:DCTERMS.identifier], '')
_funder_id_type, _funder_scheme_uri = self._funder_identifier_type_and_scheme(_funder_identifier)
_funder_id_attrib = {'funderIdentifierType': _funder_id_type}
if _funder_scheme_uri:
_funder_id_attrib['schemeURI'] = _funder_scheme_uri
self.visit(
_fundref_el,
'funderIdentifier',
text=_funder_identifier,
attrib={
'funderIdentifierType': self._funder_identifier_type(_funder_identifier),
},
attrib=_funder_id_attrib,
)
if funding_award is not None:
self.visit(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
"awardTitle": "because reasons",
"funderIdentifier": {
"funderIdentifier": "https://doi.org/10.$$$$",
"funderIdentifierType": "Crossref Funder ID"
"funderIdentifierType": "Crossref Funder ID",
"schemeURI": "https://www.crossref.org/services/funder-registry/"
},
"funderName": "Mx. Moneypockets"
},
Expand All @@ -68,14 +69,16 @@
"awardTitle": "because reasons!",
"funderIdentifier": {
"funderIdentifier": "https://doi.org/10.$$$$",
"funderIdentifierType": "Crossref Funder ID"
"funderIdentifierType": "Crossref Funder ID",
"schemeURI": "https://www.crossref.org/services/funder-registry/"
},
"funderName": "Mx. Moneypockets"
},
{
"funderIdentifier": {
"funderIdentifier": "https://ror.org/0example",
"funderIdentifierType": "ROR"
"funderIdentifierType": "ROR",
"schemeURI": "https://ror.org/"
},
"funderName": "Caring Fan"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,19 @@
<fundingReferences>
<fundingReference>
<funderName>Mx. Moneypockets</funderName>
<funderIdentifier funderIdentifierType="Crossref Funder ID">https://doi.org/10.$$$$</funderIdentifier>
<funderIdentifier funderIdentifierType="Crossref Funder ID" schemeURI="https://www.crossref.org/services/funder-registry/">https://doi.org/10.$$$$</funderIdentifier>
<awardNumber awardURI="https://moneypockets.example/millions">10000000</awardNumber>
<awardTitle>because reasons</awardTitle>
</fundingReference>
<fundingReference>
<funderName>Mx. Moneypockets</funderName>
<funderIdentifier funderIdentifierType="Crossref Funder ID">https://doi.org/10.$$$$</funderIdentifier>
<funderIdentifier funderIdentifierType="Crossref Funder ID" schemeURI="https://www.crossref.org/services/funder-registry/">https://doi.org/10.$$$$</funderIdentifier>
<awardNumber awardURI="https://moneypockets.example/millions-more">2000000</awardNumber>
<awardTitle>because reasons!</awardTitle>
</fundingReference>
<fundingReference>
<funderName>Caring Fan</funderName>
<funderIdentifier funderIdentifierType="ROR">https://ror.org/0example</funderIdentifier>
<funderIdentifier funderIdentifierType="ROR" schemeURI="https://ror.org/">https://ror.org/0example</funderIdentifier>
</fundingReference>
</fundingReferences>
<relatedIdentifiers>
Expand Down
147 changes: 146 additions & 1 deletion tests/identifiers/test_datacite.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.utils import timezone

from framework.auth import Auth
from osf.models import Outcome
from osf.models import GuidMetadataRecord, Outcome
from osf.utils.outcomes import ArtifactTypes
from osf_tests.factories import AuthUserFactory, IdentifierFactory, RegistrationFactory
from tests.base import OsfTestCase
Expand Down Expand Up @@ -300,6 +300,151 @@ def test_datacite_format_related_resources__ignores_inactive_resources(self, dat
]
_assert_unordered_list_of_dicts_equal(metadata_dict['relatedIdentifiers'], expected_relationships)

def _set_funding_info(self, registration, funding_info):
metadata_record = GuidMetadataRecord.objects.for_guid(registration._id)
metadata_record.funding_info = funding_info
metadata_record.save()

def test_datacite_funding_references_with_ror_identifier_xml(self, registration, datacite_client):
self._set_funding_info(registration, [
{
'funder_name': 'National Science Foundation',
'funder_identifier': 'https://ror.org/021nxhr62',
'funder_identifier_type': 'ROR',
},
])
metadata_xml = datacite_client.build_metadata(registration)
parser = lxml.etree.XMLParser(ns_clean=True, recover=True, encoding='utf-8')
root = lxml.etree.fromstring(metadata_xml, parser=parser)
ns = schema40.ns[None]

funding_refs = root.find(f'{{{ns}}}fundingReferences')
refs = funding_refs.findall(f'{{{ns}}}fundingReference')
assert len(refs) == 1

funder_name = refs[0].find(f'{{{ns}}}funderName')
assert funder_name.text == 'National Science Foundation'

funder_id = refs[0].find(f'{{{ns}}}funderIdentifier')
assert funder_id.text == 'https://ror.org/021nxhr62'
assert funder_id.attrib['funderIdentifierType'] == 'ROR'
assert funder_id.attrib['schemeURI'] == 'https://ror.org/'

def test_datacite_funding_references_with_ror_identifier_json(self, registration, datacite_client):
self._set_funding_info(registration, [
{
'funder_name': 'National Science Foundation',
'funder_identifier': 'https://ror.org/021nxhr62',
'funder_identifier_type': 'ROR',
},
])
metadata_dict = datacite_client.build_metadata(registration, as_xml=False)

funding_refs = metadata_dict['fundingReferences']
assert len(funding_refs) == 1
assert str(funding_refs[0]['funderName']) == 'National Science Foundation'
assert funding_refs[0]['funderIdentifier']['funderIdentifier'] == 'https://ror.org/021nxhr62'
assert funding_refs[0]['funderIdentifier']['funderIdentifierType'] == 'ROR'
assert funding_refs[0]['funderIdentifier']['schemeURI'] == 'https://ror.org/'

def test_datacite_funding_references_with_crossref_funder_id(self, registration, datacite_client):
self._set_funding_info(registration, [
{
'funder_name': 'Mx. Moneypockets',
'funder_identifier': 'https://doi.org/10.13039/100000001',
'funder_identifier_type': 'Crossref Funder ID',
'award_number': '10000000',
'award_uri': 'https://moneypockets.example/millions',
'award_title': 'because reasons',
},
])
metadata_xml = datacite_client.build_metadata(registration)
parser = lxml.etree.XMLParser(ns_clean=True, recover=True, encoding='utf-8')
root = lxml.etree.fromstring(metadata_xml, parser=parser)
ns = schema40.ns[None]

funding_refs = root.find(f'{{{ns}}}fundingReferences')
refs = funding_refs.findall(f'{{{ns}}}fundingReference')
assert len(refs) == 1

funder_id = refs[0].find(f'{{{ns}}}funderIdentifier')
assert funder_id.text == 'https://doi.org/10.13039/100000001'
assert funder_id.attrib['funderIdentifierType'] == 'Crossref Funder ID'
assert funder_id.attrib['schemeURI'] == 'https://www.crossref.org/services/funder-registry/'

award_number = refs[0].find(f'{{{ns}}}awardNumber')
assert award_number.text == '10000000'

def test_datacite_funding_references_mixed_ror_and_crossref(self, registration, datacite_client):
self._set_funding_info(registration, [
{
'funder_name': 'Mx. Moneypockets',
'funder_identifier': 'https://doi.org/10.13039/100000001',
'funder_identifier_type': 'Crossref Funder ID',
'award_number': '10000000',
'award_uri': 'https://moneypockets.example/millions',
'award_title': 'because reasons',
},
{
'funder_name': 'National Science Foundation',
'funder_identifier': 'https://ror.org/021nxhr62',
'funder_identifier_type': 'ROR',
},
])
metadata_dict = datacite_client.build_metadata(registration, as_xml=False)
funding_refs = metadata_dict['fundingReferences']
assert len(funding_refs) == 2

# Build a lookup by funder name for order-independent assertions
refs_by_name = {str(ref['funderName']): ref for ref in funding_refs}

crossref_ref = refs_by_name['Mx. Moneypockets']
assert crossref_ref['funderIdentifier']['funderIdentifier'] == 'https://doi.org/10.13039/100000001'
assert crossref_ref['funderIdentifier']['funderIdentifierType'] == 'Crossref Funder ID'
assert crossref_ref['funderIdentifier']['schemeURI'] == 'https://www.crossref.org/services/funder-registry/'
assert crossref_ref['awardNumber']['awardNumber'] == '10000000'

ror_ref = refs_by_name['National Science Foundation']
assert ror_ref['funderIdentifier']['funderIdentifier'] == 'https://ror.org/021nxhr62'
assert ror_ref['funderIdentifier']['funderIdentifierType'] == 'ROR'
assert ror_ref['funderIdentifier']['schemeURI'] == 'https://ror.org/'

def test_datacite_funding_references_ror_with_award_info(self, registration, datacite_client):
self._set_funding_info(registration, [
{
'funder_name': 'National Institutes of Health',
'funder_identifier': 'https://ror.org/01cwqze88',
'funder_identifier_type': 'ROR',
'award_number': 'R01-GM123456',
'award_uri': 'https://reporter.nih.gov/project-details/123456',
'award_title': 'Studying important things',
},
])
metadata_xml = datacite_client.build_metadata(registration)
parser = lxml.etree.XMLParser(ns_clean=True, recover=True, encoding='utf-8')
root = lxml.etree.fromstring(metadata_xml, parser=parser)
ns = schema40.ns[None]

funding_refs = root.find(f'{{{ns}}}fundingReferences')
refs = funding_refs.findall(f'{{{ns}}}fundingReference')
assert len(refs) == 1

funder_id = refs[0].find(f'{{{ns}}}funderIdentifier')
assert funder_id.text == 'https://ror.org/01cwqze88'
assert funder_id.attrib['funderIdentifierType'] == 'ROR'
assert funder_id.attrib['schemeURI'] == 'https://ror.org/'

award_number = refs[0].find(f'{{{ns}}}awardNumber')
assert award_number.text == 'R01-GM123456'

award_title = refs[0].find(f'{{{ns}}}awardTitle')
assert award_title.text == 'Studying important things'

def test_datacite_funding_references_no_funding_info(self, registration, datacite_client):
# With no funding info set, fundingReferences should be empty
metadata_dict = datacite_client.build_metadata(registration, as_xml=False)
assert metadata_dict.get('fundingReferences', []) == []


@pytest.mark.django_db
class TestDataCiteViews(OsfTestCase):
Expand Down
Loading