From 28503c02b20252f5a6dcba334a3fa2c6fa3d7cec Mon Sep 17 00:00:00 2001 From: "Dusan Mijatovic (PC2020)" Date: Mon, 12 Sep 2022 18:18:41 +0200 Subject: [PATCH 1/2] fix: extract licenses without rightsIdentifier from DOI --- .../software/edit/contributors/index.tsx | 2 +- .../software/edit/editSoftwareContext.tsx | 8 ++-- .../edit/information/SoftwareLicenses.tsx | 42 ++++++++++++++----- .../software/edit/information/index.tsx | 16 +++---- .../edit/information/useSoftwareToEdit.tsx | 2 +- frontend/types/SoftwareTypes.ts | 10 +++-- frontend/utils/editSoftware.ts | 5 ++- frontend/utils/getInfoFromDatacite.test.ts | 2 +- frontend/utils/getInfoFromDatacite.ts | 8 ++-- frontend/utils/useSpdxLicenses.tsx | 3 +- 10 files changed, 63 insertions(+), 35 deletions(-) diff --git a/frontend/components/software/edit/contributors/index.tsx b/frontend/components/software/edit/contributors/index.tsx index 93a0b0b3e..ac56f3d2e 100644 --- a/frontend/components/software/edit/contributors/index.tsx +++ b/frontend/components/software/edit/contributors/index.tsx @@ -216,7 +216,7 @@ export default function SoftwareContributors({slug}: { slug: string }) { setLoading(true) const contribDoi: Contributor[] = await getContributorsFromDoi( - software?.id, software?.concept_doi + software?.id ?? '', software?.concept_doi ?? '' ) if (!contribDoi || contribDoi.length === 0) { diff --git a/frontend/components/software/edit/editSoftwareContext.tsx b/frontend/components/software/edit/editSoftwareContext.tsx index dc278c9c8..58fd6b439 100644 --- a/frontend/components/software/edit/editSoftwareContext.tsx +++ b/frontend/components/software/edit/editSoftwareContext.tsx @@ -11,10 +11,10 @@ import {editSoftwarePage, EditSoftwarePageProps} from './editSoftwareSteps' import {EditSoftwareAction, editSoftwareReducer} from './editSoftwareReducer' export type SoftwareInfo = { - id?: string, - slug?: string, - brand_name?: string, - concept_doi?: string, + id: string | null, + slug: string | null, + brand_name: string | null, + concept_doi: string | null, } export type EditSoftwareState = { diff --git a/frontend/components/software/edit/information/SoftwareLicenses.tsx b/frontend/components/software/edit/information/SoftwareLicenses.tsx index 32672cfc6..b5e1aac5b 100644 --- a/frontend/components/software/edit/information/SoftwareLicenses.tsx +++ b/frontend/components/software/edit/information/SoftwareLicenses.tsx @@ -62,28 +62,46 @@ export default function SoftwareLicenses( const licenses = await getLicensesFromDoi(concept_doi) // find licenses SPDX keys that match items in the options for (const license of licenses) { - // exlude if already in fields - const find = fields.filter(item => item.key.toLowerCase() === license.toLowerCase()) - if (find.length > 0) { + // find license by identifier + let spdx = allOptions.find(item => item.key.toLocaleLowerCase() === license.toLowerCase()) + if (typeof spdx == 'undefined') { + // if not found by identifier try to find it by name + spdx = allOptions.find(item => item.data.name.toLocaleLowerCase() === license.toLowerCase()) + } + let find + if (typeof spdx !== 'undefined') { + // exlude if already in fields based on spdx key + find = fields.find(item => item.key.toLowerCase() === spdx?.key.toLowerCase()) + } else { + // exlude if already in fields based on key + find = fields.find(item => item.key.toLowerCase() === license.toLowerCase()) + if (typeof find == 'undefined') { + // try to match on label + find = fields.find(item => item.label.toLowerCase() === license.toLowerCase()) + } + } + if (find) { + // go to next license thisone is already proccessed continue } + // add to collection added++ - // improve identifier - const spdx = allOptions.find(item=>item.key.toLocaleLowerCase()===license.toLowerCase()) - // add to fields collection append({ key: spdx?.key ?? license, label: spdx?.key ?? license, data: { id: undefined, software, - license: spdx?.key ?? license + license: spdx?.key ?? license, + name: spdx?.data.name ?? license } }) } setDoiLoad(false) - if (added > 0) { - showSuccessMessage(`${added} liceses imported from DOI ${concept_doi}`) + if (added === 1) { + showSuccessMessage(`${added} license imported from DOI ${concept_doi}`) + } else if (added > 1) { + showSuccessMessage(`${added} licenses imported from DOI ${concept_doi}`) } else { showInfoMessage(`No (additional) license to import from DOI ${concept_doi}`) } @@ -105,7 +123,8 @@ export default function SoftwareLicenses( id: undefined, software, license: item.label, - deprecated: item.data.deprecated + deprecated: item.data.deprecated, + name: item.data.name } } }) @@ -140,7 +159,8 @@ export default function SoftwareLicenses( data: { id: undefined, software, - license: newInputValue + license: newInputValue, + name: newInputValue } }) } diff --git a/frontend/components/software/edit/information/index.tsx b/frontend/components/software/edit/information/index.tsx index cad43798f..82e781ac2 100644 --- a/frontend/components/software/edit/information/index.tsx +++ b/frontend/components/software/edit/information/index.tsx @@ -27,7 +27,7 @@ import SoftwareKeywords from './SoftwareKeywords' import {getKeywordChanges} from './softwareKeywordsChanges' import ConceptDoi from './ConceptDoi' import useSoftwareContext from '../useSoftwareContext' -import useSoftwareToEdit from './useSoftwareToEdit' +import useSoftwareToEdit, {getSoftwareInfoForEdit} from './useSoftwareToEdit' export default function SoftwareInformation({slug}: {slug: string}) { const {token,user} = useSession() @@ -39,7 +39,7 @@ export default function SoftwareInformation({slug}: {slug: string}) { // destructure methods from react-hook-form const { register, handleSubmit, watch, formState, reset, - control, setValue, getValues + control, setValue } = useFormContext() const {update: updateKeyword} = useFieldArray({ @@ -68,7 +68,8 @@ export default function SoftwareInformation({slug}: {slug: string}) { setSoftwareInfo({ id: initalState.id, slug: initalState.slug, - brand_name: initalState.brand_name + brand_name: initalState.brand_name, + concept_doi: initalState.concept_doi }) setLoading(false) } @@ -110,11 +111,10 @@ export default function SoftwareInformation({slug}: {slug: string}) { // if OK if (resp.status === 200) { showSuccessMessage(`${formData?.brand_name} saved`) - // update software state - // reset form to remove dirty states with latest form data - const latestFormData = getValues() - // to be equal to data in the form - setEditSoftware(latestFormData) + // reload software from api (confirm all changes saved and retreive new ids) + const updated = await getSoftwareInfoForEdit({slug,token}) + // update local state (useEffect will then reset form to new values) + setEditSoftware(updated) } else { showErrorMessage(`Failed to save. ${resp.message}`) } diff --git a/frontend/components/software/edit/information/useSoftwareToEdit.tsx b/frontend/components/software/edit/information/useSoftwareToEdit.tsx index 452922445..eb10ab78f 100644 --- a/frontend/components/software/edit/information/useSoftwareToEdit.tsx +++ b/frontend/components/software/edit/information/useSoftwareToEdit.tsx @@ -21,7 +21,7 @@ function prepareLicenses(rawLicense: License[]=[]) { } -async function getSoftwareInfoForEdit({slug, token}: { slug: string, token: string }) { +export async function getSoftwareInfoForEdit({slug, token}: { slug: string, token: string }) { const software = await getSoftwareToEdit({slug, token}) if (software) { diff --git a/frontend/types/SoftwareTypes.ts b/frontend/types/SoftwareTypes.ts index ce1af15ad..613fbfa98 100644 --- a/frontend/types/SoftwareTypes.ts +++ b/frontend/types/SoftwareTypes.ts @@ -101,11 +101,15 @@ export type KeywordForSoftware = { * LiCENSES */ -export type License = { - id?: string, +export type LicenseForSoftware = { software: string license: string - deprecated?: boolean +} + +export type License = LicenseForSoftware & { + id?: string, + deprecated?: boolean, + name: string } diff --git a/frontend/utils/editSoftware.ts b/frontend/utils/editSoftware.ts index 94f8ffb7b..6317bec36 100644 --- a/frontend/utils/editSoftware.ts +++ b/frontend/utils/editSoftware.ts @@ -13,7 +13,8 @@ import { EditSoftwareItem, License, SoftwareItemFromDB, - KeywordForSoftware + KeywordForSoftware, + LicenseForSoftware } from '../types/SoftwareTypes' import {getPropsFromObject} from './getPropsFromObject' import {AutocompleteOption} from '../types/AutocompleteOptions' @@ -383,7 +384,7 @@ export async function createKeywordAndAddToSoftware({data, token, updateKeyword} } export async function addLicensesForSoftware({software, data, token}: - {software: string, data: License[], token: string}) { + { software: string, data: LicenseForSoftware[], token: string}) { try { const url = `/api/v1/license_for_software?software=eq.${software}` const resp = await fetch(url, { diff --git a/frontend/utils/getInfoFromDatacite.test.ts b/frontend/utils/getInfoFromDatacite.test.ts index fa885785c..c524d6579 100644 --- a/frontend/utils/getInfoFromDatacite.test.ts +++ b/frontend/utils/getInfoFromDatacite.test.ts @@ -537,7 +537,7 @@ it('skips invalid keyword subjects', async () => { it('returns all licenses from rightsList', async () => { mockResolvedValueOnce(exampleResponseRightsList) const resp = await getLicensesFromDoi('0') - expect(resp).toEqual(['cc-by-4.0', 'EUPL-1.2']) + expect(resp).toEqual(['cc-by-4.0', 'Open Access','EUPL-1.2']) }) it('returns no licenses if rightsList is missing', async () => { diff --git a/frontend/utils/getInfoFromDatacite.ts b/frontend/utils/getInfoFromDatacite.ts index 8a5d632ea..e24fc20a2 100644 --- a/frontend/utils/getInfoFromDatacite.ts +++ b/frontend/utils/getInfoFromDatacite.ts @@ -148,9 +148,9 @@ export async function getDoiInfo(doiId: string) { } export async function getContributorsFromDoi( - softwareId: string | undefined, doiId: string | undefined + softwareId: string, doiId: string ) { - if (!doiId || !softwareId) { + if (doiId==='' || softwareId==='') { return [] } @@ -264,9 +264,11 @@ export async function getLicensesFromDoi(doiId: string | null | undefined) { const spdxLicenses = [] for (const license of allLicenses) { - // extract all licenses with an identifier + // use identifier if present if (license.rightsIdentifier) { spdxLicenses.push(license.rightsIdentifier) + } else if (license.rights) { + spdxLicenses.push(license.rights) } } diff --git a/frontend/utils/useSpdxLicenses.tsx b/frontend/utils/useSpdxLicenses.tsx index 68a3e18c9..5b211f5e4 100644 --- a/frontend/utils/useSpdxLicenses.tsx +++ b/frontend/utils/useSpdxLicenses.tsx @@ -45,7 +45,8 @@ export default function useSpdxLicenses({software}:{software?:string}) { id: undefined, software, license: item.licenseId, - deprecated: item.isDeprecatedLicenseId + deprecated: item.isDeprecatedLicenseId, + name: item.name } } }) From dbb481c2d2b71131f7dfc7ba6c89d5bd6cedbcfa Mon Sep 17 00:00:00 2001 From: "Dusan Mijatovic (PC2020)" Date: Tue, 13 Sep 2022 15:09:28 +0200 Subject: [PATCH 2/2] fix: #259 deduplicate creators and contributors during import from DOI --- .../software/edit/contributors/index.tsx | 1 - .../software/edit/information/index.tsx | 19 +- .../software/edit/organisations/index.tsx | 2 +- frontend/utils/getInfoFromDatacite.test.ts | 362 ++++++++---------- frontend/utils/getInfoFromDatacite.ts | 13 +- 5 files changed, 183 insertions(+), 214 deletions(-) diff --git a/frontend/components/software/edit/contributors/index.tsx b/frontend/components/software/edit/contributors/index.tsx index ac56f3d2e..cdf729b3d 100644 --- a/frontend/components/software/edit/contributors/index.tsx +++ b/frontend/components/software/edit/contributors/index.tsx @@ -18,7 +18,6 @@ import { getAvatarUrl, getContributorsForSoftware, prepareContributorData, updateContributorInDb } from '~/utils/editContributors' -import useOnUnsaveChange from '~/utils/useOnUnsavedChange' import {getDisplayName} from '~/utils/getDisplayName' import {sortOnStrProp} from '~/utils/sortFn' import {getPropsFromObject} from '~/utils/getPropsFromObject' diff --git a/frontend/components/software/edit/information/index.tsx b/frontend/components/software/edit/information/index.tsx index 82e781ac2..fbcd290e6 100644 --- a/frontend/components/software/edit/information/index.tsx +++ b/frontend/components/software/edit/information/index.tsx @@ -69,7 +69,7 @@ export default function SoftwareInformation({slug}: {slug: string}) { id: initalState.id, slug: initalState.slug, brand_name: initalState.brand_name, - concept_doi: initalState.concept_doi + concept_doi: initalState.concept_doi, }) setLoading(false) } @@ -110,11 +110,20 @@ export default function SoftwareInformation({slug}: {slug: string}) { }) // if OK if (resp.status === 200) { - showSuccessMessage(`${formData?.brand_name} saved`) // reload software from api (confirm all changes saved and retreive new ids) - const updated = await getSoftwareInfoForEdit({slug,token}) - // update local state (useEffect will then reset form to new values) - setEditSoftware(updated) + const updated = await getSoftwareInfoForEdit({slug, token}) + if (updated) { + // update local state (useEffect will then reset form to new values) + setEditSoftware(updated) + // update shared software info + setSoftwareInfo({ + id: updated.id, + slug: updated.slug, + brand_name: updated.brand_name, + concept_doi: updated.concept_doi, + }) + } + showSuccessMessage(`${formData?.brand_name} saved`) } else { showErrorMessage(`Failed to save. ${resp.message}`) } diff --git a/frontend/components/software/edit/organisations/index.tsx b/frontend/components/software/edit/organisations/index.tsx index 63c77bbcd..46efa8955 100644 --- a/frontend/components/software/edit/organisations/index.tsx +++ b/frontend/components/software/edit/organisations/index.tsx @@ -160,7 +160,7 @@ export default function SoftwareOganisations() { // if it has id if (organisation?.id) { const resp = await deleteOrganisationFromSoftware({ - software: software?.id, + software: software?.id ?? undefined, organisation: organisation.id, token }) diff --git a/frontend/utils/getInfoFromDatacite.test.ts b/frontend/utils/getInfoFromDatacite.test.ts index c524d6579..3023c2038 100644 --- a/frontend/utils/getInfoFromDatacite.test.ts +++ b/frontend/utils/getInfoFromDatacite.test.ts @@ -115,241 +115,195 @@ const exampleResponse = { // taken from (and slightly modified): // https://github.com/inveniosoftware/datacite/blob/b15db91e1231135e5f2dba0cadca0c72ede037cf/tests/data/datacite-v4.1-full-example.json const dataciteFullExample = { - 'identifier': { - 'identifier': '10.1234/example-full', - 'identifierType': 'DOI' + 'id': 'https://doi.org/10.5880/riesgos.2021.003', + 'doi': '10.5880/RIESGOS.2021.003', + 'url': 'https://dataservices.gfz-potsdam.de/panmetaworks/showshort.php?id=bae8fc94-4799-11ec-947f-3811b03e280f', + 'types': { + 'ris': 'COMP', + 'bibtex': 'misc', + 'citeproc': 'article', + 'schemaOrg': 'SoftwareSourceCode', + 'resourceType': 'Software', + 'resourceTypeGeneral': 'Software' }, 'creators': [ { - 'creatorName': 'Miller, Elizabeth', + 'name': 'Pittore, Massimiliano', 'nameType': 'Personal', - 'givenName': 'Elizabeth', - 'familyName': 'Miller', - 'nameIdentifiers': [ + 'givenName': 'Massimiliano', + 'familyName': 'Pittore', + 'affiliation': [ { - 'nameIdentifier': '0000-0000-0000-0000', - 'nameIdentifierScheme': 'ORCID', - 'schemeURI': 'http://orcid.org/' + 'name': 'GFZ German Research Centre for Geosciences, Potsdam, Germany', + 'affiliationIdentifierScheme': 'ORCID' }, { - 'nameIdentifier': '0000-0000-0000-0001', - 'nameIdentifierScheme': 'ORCID', - 'schemeURI': 'http://orcid.org/' + 'name': 'EURAC Research (Accademia Europea): Bolzano, Trentino-Alto Adige, Italy' } ], - 'affiliation': ['DataCite', 'CERN', 'TIND'] - } - ], - 'titles': [ - { - 'title': 'Full DataCite XML Example', - 'lang': 'en-us' + 'nameIdentifiers': [ + { + 'schemeUri': 'https://orcid.org', + 'nameIdentifier': 'https://orcid.org/0000-0003-4940-3444', + 'nameIdentifierScheme': 'ORCID' + } + ] }, { - 'title': 'Demonstration of DataCite Properties.', - 'titleType': 'Subtitle', - 'lang': 'en-us' - } - ], - 'publisher': 'DataCite', - 'publicationYear': '2014', - 'subjects': [ - { - 'subject': '000 computer science', - 'subjectScheme': 'dewey', - 'valueURI': 'https://cern.ch', - 'schemeURI': 'http://dewey.info/', - 'lang': 'en-us' - } - ], - 'contributors': [ - { - 'contributorName': 'Starr, Joan', + 'name': 'Haas, Michael', 'nameType': 'Personal', - 'contributorType': 'ProjectLeader', - 'givenName': 'Joan', - 'familyName': 'Starr', - 'nameIdentifiers': [ + 'givenName': 'Michael', + 'familyName': 'Haas', + 'affiliation': [ { - 'nameIdentifier': '1000-0000-0000-000X', - 'nameIdentifierScheme': 'ORCID', - 'schemeURI': 'http://orcid.org/' + 'name': 'Formerly at GFZ German Research Centre for Geosciences, Potsdam, Germany' } ], - 'affiliation': ['California Digital Library'] + 'nameIdentifiers': [ + { + 'schemeUri': 'https://orcid.org', + 'nameIdentifier': 'https://orcid.org/0000-0002-1179-1659', + 'nameIdentifierScheme': 'ORCID' + } + ] } ], - 'dates': [ + 'titles': [ { - 'date': '2014-10-17', - 'dateType': 'Updated', - 'dateInformation': 'Date of the first publishing.' + 'title': 'Quakeledger: a web service to serve earthquake scenarios' } ], - 'language': 'en-us', - 'resourceType': { - 'resourceTypeGeneral': 'Software', - 'resourceType': 'XML' - }, - 'alternateIdentifiers': [ + 'publisher': 'GFZ Data Services', + 'container': {}, + 'subjects': [ { - 'alternateIdentifier': 'http://schema.datacite.org/schema/meta/kernel-4.1/example/datacite-example-full-v4.1.xml', - 'alternateIdentifierType': 'URL' - } - ], - 'relatedIdentifiers': [ + 'subject': 'Earthquake catalogue' + }, { - 'relatedIdentifier': 'http://data.datacite.org/application/citeproc+json/10.1234/example-full', - 'relatedIdentifierType': 'URL', - 'relationType': 'HasMetadata', - 'relatedMetadataScheme': 'citeproc+json', - 'schemeURI': 'https://github.com/citation-style-language/schema/raw/master/csl-data.json' + 'subject': 'provider' }, { - 'relatedIdentifier': 'arXiv:0706.0001', - 'relatedIdentifierType': 'arXiv', - 'relationType': 'IsReviewedBy' + 'subject': 'script' }, { - 'relatedIdentifier': 'arXiv:0706.0002', - 'relatedIdentifierType': 'arXiv', - 'relationType': 'IsDescribedBy', - 'resourceTypeGeneral': 'Text' + 'subject': 'python' }, { - 'relatedIdentifier': 'arXiv:0706.0003', - 'relatedIdentifierType': 'arXiv', - 'relationType': 'Describes' + 'subject': 'RIESGOS' }, { - 'relatedIdentifier': 'arXiv:0706.0004', - 'relatedIdentifierType': 'arXiv', - 'relationType': 'IsVersionOf' + 'subject': 'Scenario-based multi-risk assessment in the Andes region' }, { - 'relatedIdentifier': 'arXiv:0706.0005', - 'relatedIdentifierType': 'arXiv', - 'relationType': 'HasVersion' + 'subject': 'EARTH SCIENCE SERVICES > DATA ANALYSIS AND VISUALIZATION', + 'subjectScheme': 'NASA/GCMD Earth Science Keywords' }, { - 'relatedIdentifier': 'arXiv:0706.0006', - 'relatedIdentifierType': 'arXiv', - 'relationType': 'IsRequiredBy' + 'subject': 'EARTH SCIENCE SERVICES > DATA MANAGEMENT/DATA HANDLING', + 'subjectScheme': 'NASA/GCMD Earth Science Keywords' }, { - 'relatedIdentifier': 'arXiv:0706.0007', - 'relatedIdentifierType': 'arXiv', - 'relationType': 'Requires' + 'subject': 'EARTH SCIENCE SERVICES > WEB SERVICES', + 'subjectScheme': 'NASA/GCMD Earth Science Keywords' + }, + { + 'subject': 'EARTH SCIENCE SERVICES > WEB SERVICES > DATA PROCESSING SERVICES', + 'subjectScheme': 'NASA/GCMD Earth Science Keywords' } ], - 'sizes': [ - '3KB' + 'contributors': [ + { + 'name': 'Haas, Michael', + 'nameType': 'Personal', + 'givenName': 'Michael', + 'familyName': 'Haas', + 'affiliation': [ + { + 'name': 'Formerly at GFZ German Research Centre for Geosciences, Potsdam, Germany' + } + ], + 'nameIdentifiers': [ + { + 'schemeUri': 'https://orcid.org', + 'nameIdentifier': 'https://orcid.org/0000-0002-1179-1659', + 'nameIdentifierScheme': 'ORCID' + } + ] + }, ], - 'formats': [ - 'application/xml' + 'dates': [ + { + 'date': '2021', + 'dateType': 'Issued' + } ], - 'version': '4.1', + 'publicationYear': 2021, + 'identifiers': [], + 'sizes': [], + 'formats': [], + 'version': '1.0', 'rightsList': [ { - 'rights': 'CC0 1.0 Universal', - 'rightsURI': 'http://creativecommons.org/publicdomain/zero/1.0/', - 'lang': 'en-us' + 'rights': 'BSD 3-Clause "New" or "Revised" License', + 'rightsUri': 'https://opensource.org/licenses/BSD-3-Clause', + 'schemeUri': 'https://spdx.org/licenses/', + 'rightsIdentifier': 'bsd-3-clause', + 'rightsIdentifierScheme': 'SPDX' } ], 'descriptions': [ { - 'descriptionType': 'Abstract', - 'lang': 'en-us', - 'description': 'XML example of all DataCite Metadata Schema v4.1 properties.' + 'description': 'This version of Quakeledger (V.1.0) is a Python3 program that can also be used as a WPS (Web Processing Service). It returns the available earthquake events contained within a given local database (so called catalogue) that must be customised beforehand (e.g. historical, expert and/or stochastic events). This is a rewrite from: https://github.com/GFZ-Centre-for-Early-Warning/quakeledger and https://github.com/bpross-52n/quakeledger. In these original codes, an earthquake catalogue had to be initially provided in .CSV format. The main difference with this version is that, this code is refactored and uses a SQLITE database. The user can find the parser code in: “quakeledger/assistance/import_csv_in_sqlite.py”', + 'descriptionType': 'Abstract' + }, + { + 'description': 'License: BSD 3-Clause Copyright © 2021 Early Warning and Impact Assessment Group at Helmholtz Centre Potsdam GFZ German Research Centre for Geosciences Quakeledger is free software: you can redistribute it and/or modify it under the terms of the BSD 3-Clause License. Quakeledger is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the BSD 3-Clause License for more details. You should have received a copy of the BSD 3-Clause License along with this program. If not, see', + 'descriptionType': 'Other' } ], + 'geoLocations': [], 'fundingReferences': [ { - 'funderName': 'European Commission', - 'funderIdentifier': { - 'funderIdentifier': 'http://doi.org/10.13039/501100000780', - 'funderIdentifierType': 'Crossref Funder ID' - }, - 'awardNumber': { - 'awardNumber': '282625', - 'awardURI': 'http://cordis.europa.eu/project/rcn/100180_en.html' - }, - 'awardTitle': 'MOTivational strength of ecosystem services and alternative ways to express the value of BIOdiversity' + 'awardTitle': 'LIENT II – International Partnerships for Sustainable Innovations- RIESGOS', + 'funderName': 'Bundesministerium für Bildung und Forschung', + 'awardNumber': '03G0876', + 'funderIdentifier': 'https://doi.org/10.13039/501100002347', + 'funderIdentifierType': 'Crossref Funder ID' + } + ], + 'relatedIdentifiers': [ + { + 'relationType': 'References', + 'relatedIdentifier': '10.1785/0220130087', + 'relatedIdentifierType': 'DOI' }, { - 'funderName': 'European Commission', - 'funderIdentifier': { - 'funderIdentifier': 'http://doi.org/10.13039/501100000780', - 'funderIdentifierType': 'Crossref Funder ID' - }, - 'awardNumber': { - 'awardNumber': '284382', - 'awardURI': 'http://cordis.europa.eu/project/rcn/100603_en.html' - }, - 'awardTitle': 'Institutionalizing global genetic-resource commons. Global Strategies for accessingand using essential public knowledge assets in the life science' + 'relationType': 'References', + 'relatedIdentifier': 'https://www.riesgos.de/en/', + 'relatedIdentifierType': 'URL' + }, + { + 'relationType': 'IsNewVersionOf', + 'relatedIdentifier': 'https://github.com/GFZ-Centre-for-Early-Warning/quakeledger', + 'relatedIdentifierType': 'URL' + }, + { + 'relationType': 'IsNewVersionOf', + 'relatedIdentifier': 'https://github.com/bpross-52n/quakeledger', + 'relatedIdentifierType': 'URL' + }, + { + 'relationType': 'IsVariantFormOf', + 'relatedIdentifier': 'https://github.com/gfzriesgos/quakeledger/', + 'relatedIdentifierType': 'URL' } ], - 'geoLocations': [ - { - 'geoLocationPoint': { - 'pointLongitude': 31.233, - 'pointLatitude': -67.302 - }, - 'geoLocationBox': { - 'westBoundLongitude': -71.032, - 'eastBoundLongitude': -68.211, - 'southBoundLatitude': 41.090, - 'northBoundLatitude': 42.893 - }, - 'geoLocationPlace': 'Atlantic Ocean', - 'geoLocationPolygons': [ - { - 'polygonPoints': [ - { - 'pointLongitude': -71.032, - 'pointLatitude': 41.090 - }, - { - 'pointLongitude': -68.211, - 'pointLatitude': 42.893 - }, - { - 'pointLongitude': -72.032, - 'pointLatitude': 39.090 - }, - { - 'pointLongitude': -71.032, - 'pointLatitude': 41.090 - } - ] - }, - { - 'polygonPoints': [ - { - 'pointLongitude': -72.032, - 'pointLatitude': 42.090 - }, - { - 'pointLongitude': -69.211, - 'pointLatitude': 43.893 - }, - { - 'pointLongitude': -73.032, - 'pointLatitude': 41.090 - }, - { - 'pointLongitude': -72.032, - 'pointLatitude': 42.090 - } - ], - 'inPolygonPoint': { - 'pointLongitude': -52.032, - 'pointLatitude': 12.090 - } - } - ] - } - ] + 'relatedItems': [], + 'schemaVersion': 'http://datacite.org/schema/kernel-4', + 'providerId': 'gfz', + 'clientId': 'tib.gfz', + 'agency': 'datacite', + 'state': 'findable' } const exampleResponseInvalidPersons = { @@ -475,33 +429,31 @@ it('returns expected contributors', async () => { ) }) -it('returns authors and contributors', async () => { +it.only('returns authors and contributors (without duplicates)', async () => { mockResolvedValueOnce(dataciteFullExample) + const expectedList = [ + { + given_names: 'Massimiliano', + family_names: 'Pittore', + email_address: '', + software: '0', + affiliation: 'GFZ German Research Centre for Geosciences, Potsdam, Germany', + is_contact_person: false, + orcid: '0000-0003-4940-3444' + }, + { + given_names: 'Michael', + family_names: 'Haas', + email_address: '', + software: '0', + affiliation: 'Formerly at GFZ German Research Centre for Geosciences, Potsdam, Germany', + is_contact_person: false, + orcid: '0000-0002-1179-1659' + } + ] const resp = await getContributorsFromDoi('0', 'DOI') - - expect(resp).toEqual( - [ - { - 'affiliation': 'DataCite', - 'email_address': '', - 'family_names': 'Miller', - 'given_names': 'Elizabeth', - 'is_contact_person': false, - 'orcid': '0000-0000-0000-0000', - 'software': '0' - }, - { - 'affiliation': 'California Digital Library', - 'email_address': '', - 'family_names': 'Starr', - 'given_names': 'Joan', - 'is_contact_person': false, - 'orcid': '1000-0000-0000-000X', - 'software': '0' - } - ] - ) + expect(resp).toEqual(expectedList) }) it('skips invalid persons', async () => { diff --git a/frontend/utils/getInfoFromDatacite.ts b/frontend/utils/getInfoFromDatacite.ts index e24fc20a2..86e933f99 100644 --- a/frontend/utils/getInfoFromDatacite.ts +++ b/frontend/utils/getInfoFromDatacite.ts @@ -7,6 +7,7 @@ import {Contributor} from '~/types/Contributor' import {createJsonHeaders} from './fetchHelpers' +import {itemsNotInReferenceList} from './itemsNotInReferenceList' import logger from './logger' const exampleCreator = { @@ -164,11 +165,19 @@ export async function getContributorsFromDoi( let allPersons: DatacitePerson[] = [] if ('creators' in doiData) { - allPersons = allPersons.concat(doiData['creators']) + allPersons = doiData['creators'] } if ('contributors' in doiData) { - allPersons = allPersons.concat(doiData['contributors']) + const contributors = itemsNotInReferenceList({ + list: doiData['contributors'], + referenceList: allPersons, + key: 'name' + }) + allPersons = [ + ...allPersons, + ...contributors + ] } for (const person of allPersons) {