Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

127 Add entry field for Associated Datasets #281

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4e5eee9
add fields
fostermh Nov 23, 2023
bba1df0
remove url field. assume that identifier is a valid url.
fostermh Nov 23, 2023
a1a7610
add test for more code lists
fostermh Nov 23, 2023
730fce3
fix url validator and remove ca.cioos authority match in favour of us…
fostermh Nov 23, 2023
31003cb
remove intitive type question, switch identifier type back to select …
fostermh Nov 24, 2023
83c501a
fix type
fostermh Nov 24, 2023
3f075a9
rename related tab and update wording.
fostermh Dec 2, 2023
6b67963
remove requirement for the identifier to be a url
fostermh Dec 2, 2023
fc696e7
Merge branch 'main' into 127-add-entry-field-for-associated-datasets
fostermh Dec 6, 2023
fe792f9
change association type field label to relation type
fostermh Dec 6, 2023
202e54c
add validation for relate works
fostermh Dec 7, 2023
77d4078
fix tab name
fostermh Dec 7, 2023
4553611
update some text
fostermh Dec 19, 2023
e56eb80
Merge branch 'main' into 127-add-entry-field-for-associated-datasets
fostermh Jan 18, 2024
8105c09
Merge branch 'main' into 127-add-entry-field-for-associated-datasets
fostermh Jan 19, 2024
ab2098f
change association type to use the datacite list of options and map t…
fostermh Jan 23, 2024
331d34d
populate datacite json with related works
fostermh Jan 24, 2024
bc19598
skip output of related works if not set
fostermh Jan 24, 2024
d4429a3
fix array mapping for related identifiers in datacite json output
fostermh Jan 24, 2024
beb773c
Update RelatedWorks.jsx
Feb 8, 2024
015c794
Update RelatedWorks.jsx
Feb 8, 2024
1f19269
Merge branch 'main' into 127-add-entry-field-for-associated-datasets
fostermh Feb 14, 2024
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
1 change: 1 addition & 0 deletions firebase_to_xml/firebase_to_xml/record_json_to_yaml.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def record_json_to_yaml(record):
"title": record.get("title"),
"identifier": record.get("datasetIdentifier"),
"abstract": record.get("abstract"),
"associated_resources": record.get("associated_resources", []),
"dates": {
"creation": date_from_datetime_str(record.get("dateStart")),
"publication": date_from_datetime_str(record.get("datePublished")),
Expand Down
4 changes: 3 additions & 1 deletion src/__tests__/isoCodeList.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { eovList, progressCodes, roleCodes } from "../isoCodeLists";
import { eovList, progressCodes, roleCodes, associationTypeCode, initiativeTypeCode } from "../isoCodeLists";

it("Defines constants", () => {
expect(eovList).toBeDefined();
expect(progressCodes).toBeDefined();
expect(roleCodes).toBeDefined();
expect(associationTypeCode).toBeDefined();
expect(initiativeTypeCode).toBeDefined();
});
38 changes: 38 additions & 0 deletions src/associationTypeMapping.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const associationTypeToIso = {
"IsCitedBy": "crossReference",
"Cites": "crossReference",
"IsSupplementTo": "crossReference",
"IsSupplementedBy": "crossReference",
"IsContinuedBy": "series",
"Continues": "series",
"IsDescribedBy": "crossReference",
"Describes": "crossReference",
"HasMetadata": "crossReference",
"IsMetadataFor": "crossReference",
"HasVersion": "revisionOf",
"IsVersionOf": "revisionOf",
"IsNewVersionOf": "revisionOf",
"PreviousVersionOf": "crossReference",
"IsPartOf": "largerWorkCitation",
"HasPart": "isComposedOf",
"IsPublishedIn": "largerWorkCitation",
"IsReferencedBy": "crossReference",
"References": "crossReference",
"IsDocumentedBy": "crossReference",
"Documents": "crossReference",
"IsCompiledBy": "dependency",
"Compiles": "dependency",
"IsVariantFormOf": "crossReference",
"IsOriginalFormOf": "crossReference",
"IsIdenticalTo": "crossReference",
"IsReviewedBy": "crossReference",
"Reviews": "crossReference",
"IsDerivedFrom": "dependency",
"IsSourceOf": "dependency",
"Requires": "dependency",
"IsRequiredBy": "dependency",
"IsObsoletedBy": "crossReference",
"Obsoletes": "revisionOf",
};

export default associationTypeToIso;
266 changes: 266 additions & 0 deletions src/components/FormComponents/RelatedWorks.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
import React from "react";
import {
Add,
Delete,
ArrowUpwardSharp,
ArrowDownwardSharp,
} from "@material-ui/icons";
import { Button, Grid, Paper, TextField } from "@material-ui/core";
import validator from "validator";
import { useParams } from "react-router-dom";
import { En, Fr, I18n } from "../I18n";
import { associationTypeCode, identifierType } from "../../isoCodeLists";

import BilingualTextInput from "./BilingualTextInput";
import RequiredMark from "./RequiredMark";
import SelectInput from "./SelectInput";
import { deepCopy } from "../../utils/misc";
import { QuestionText, paperClass, SupplementalText } from "./QuestionStyles";

import associationTypeToIso from "../../associationTypeMapping"
const validateURL = (url) => !url || validator.isURL(url);

const RelatedWorks = ({ updateResources, resources, disabled }) => {

const emptyResource = { title: { en: "", fr: "" }, authority: "", code: "", association_type: "", association_type_iso: ""};
const { language } = useParams();

function addResource() {
updateResources(resources.concat(deepCopy(emptyResource)));
}

// removes the resource section from the list at index i
function removeResource(i) {
updateResources(resources.filter((e, index) => index !== i));
}

// move the resource section
function moveResource(i, newIndex) {
if (newIndex < 0 || newIndex >= resources.length) return;
const element = resources.splice(i, 1)[0];
resources.splice(newIndex, 0, element);
updateResources(resources);
}

return (
<div>
{resources.map((dist = deepCopy(emptyResource), i) => {
function urlIsValid(url) {
return !url || validateURL(url);
}
function handleResourceChange(key) {
return (e) => {
const newValue = [...resources];
newValue[i][key] = e.target.value;
updateResources(newValue);
};
}

function handleAssociationTypeChange() {
let key = "association_type";
console.log("In handleAssociationTypeChange")
return (e) => {
const newValue = [...resources];
newValue[i]["association_type_iso"] = associationTypeToIso[e.target.value];
newValue[i][key] = e.target.value;
updateResources(newValue);
};
}
function handleIdentifierChange(key) {
return (e) => {

const newValue = [...resources];
newValue[i][key] = e.target.value;

let s = newValue[i]['code']
switch (true) {
case urlIsValid(newValue[i]['code']) && /^http.?:\/\/doi\.org\//i.test(s):
newValue[i]['authority'] = 'DOI'
console.log('MATCH DOI')
break;
case urlIsValid(newValue[i]['code']):
newValue[i]['authority'] = 'URL'
console.log('MATCH URL')
break;
default:
newValue[i]['authority'] = ''
break;
}
updateResources(newValue);
};
}
return (
<Paper key={i} style={paperClass}>
<Grid container direction="column" spacing={3}>
<Grid item xs>
<QuestionText>
<I18n>
<En>Enter the title of the related resource</En>
<Fr>Entrez le titre de l'œuvre concernée</Fr>
</I18n>
<RequiredMark passes={dist.title?.en || dist.title?.fr} />
</QuestionText>{" "}
<BilingualTextInput
name="title"
label={<I18n en="Title" fr="Titre" />}
value={dist.title}
onChange={handleResourceChange("title")}
disabled={disabled}
/>
</Grid>
<Grid item xs>
<QuestionText>
<I18n>
<En>Enter the identifier for the related resource</En>
<Fr>Saisissez l'identifiant de l'œuvre concernée</Fr>
</I18n>

<RequiredMark passes={dist.code} />
<SupplementalText>
<I18n>
<En>
<p>
The identifier may be to a resource, or metadata record on another
repository or another record within CIOOS. A DOI or full URL are preferred.
</p>
</En>
<Fr>
<p>
L'identifiant peut provenir d'une ressource ou d'un enregistrement de métadonnées sur un autre
référentiel ou un autre enregistrement dans CIOOS. Un DOI ou une URL complète sont préférés.
</p>
</Fr>
</I18n>
</SupplementalText>
</QuestionText>
<TextField
label={<I18n en="Identifier" fr="identifiant" />}
value={dist.code}
onChange={handleIdentifierChange("code")}
fullWidth
disabled={disabled}
/>
</Grid>
<Grid item xs>
<QuestionText>
<I18n>
<En>Enter the identifier type</En>
<Fr>Entrez le type d'identifiant</Fr>
</I18n>
<RequiredMark passes={dist.authority} />
</QuestionText>

<SelectInput
value={dist.authority}
onChange={handleResourceChange("authority")}
options={identifierType}
optionLabels={identifierType}
disabled={disabled}
label={< I18n en="Identifier Type" fr="Type d'identifiant" />}
fullWidth={false}
/>
</Grid>
<Grid item xs>
<QuestionText>
<I18n>
<En>What is the relation type?</En>
<Fr>
Quel est le type de relation?</Fr>
</I18n>
<RequiredMark passes={dist.association_type} />
<SupplementalText>
<I18n>
<En>
<p>
Specify the relationship from (A) the primary resource; to (B) the related resource. For example:
</p>
<ul>
<li>Use 'Is New Version Of' to indicate the primary resource described in this metadata record (A) is a new version of (B) the related resource.</li>
<li>Use 'Is Part of' to indicate the primary resource (A) is a subset of (B) the related larger resource.</li>
<li>Use 'Has Part' to indicate the primary resource (A) is the larger work that includes (B) the related resource.</li>
<li>Use 'Cites' to indicate that (A) cites (B).</li>
<li>Use 'Is Cited by to indicate that (B) cites (A)</li>
</ul>

</En>
<Fr>
<p>
Spécifiez la relation à partir de (A) la ressource principale ; à (B) la ressource associée. Par exemple:
</p>
<ul>
<li>Utilisez "Est une nouvelle version de" pour indiquer que la ressource principale décrite dans cet enregistrement de métadonnées (A) est une nouvelle version de (B) la ressource associée.</li>
<li>Utilisez "Fait partie de" pour indiquer que la ressource principale (A) est un sous-ensemble de (B) la ressource plus grande associée.</li>
<li>Utilisez "A une partie" pour indiquer que la ressource principale (A) est le travail le plus important qui comprend (B) la ressource associée.</li>
<li>Utilisez "Cites" pour indiquer que (A) cite (B).</li>
<li>Utilisez "Est cité par" pour indiquer que (B) cite (A)</li>
</ul>

</Fr>
</I18n>
</SupplementalText>
</QuestionText>
<SelectInput
value={dist.association_type}
onChange={handleAssociationTypeChange()}
options={Object.keys(associationTypeCode)}
optionLabels={Object.values(associationTypeCode).map(
({ title }) => title[language]
)}
optionTooltips={Object.values(associationTypeCode).map(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the current Tooltip that describe how the DataCite relation types map to ISO isn't particularly useful for the user. Could you update the tooltip so it explains the DataCite relation type using the definitions here?: https://datacite-metadata-schema.readthedocs.io/en/4.5/appendices/appendix-1/relationType/#issourceof

({ text }) => text[language]
)}
disabled={disabled}
label={<I18n en="Relation Type" fr="Type de relation" />}
fullWidth={false}
/>
</Grid>
<Grid item xs>
<Button
startIcon={<Delete />}
disabled={disabled}
onClick={() => removeResource(i)}
>
<I18n>
<En>Remove item</En>
<Fr>Supprimer la ressource</Fr>
</I18n>
</Button>
<Button
startIcon={<ArrowUpwardSharp />}
disabled={disabled || i - 1 < 0}
onClick={() => moveResource(i, i - 1)}
>
<I18n>
<En>Move up</En>
<Fr>Déplacer vers le haut</Fr>
</I18n>
</Button>
<Button
startIcon={<ArrowDownwardSharp />}
disabled={disabled || i + 1 >= resources.length}
onClick={() => moveResource(i, i + 1)}
>
<I18n>
<En>Move down</En>
<Fr>Déplacer vers le bas</Fr>
</I18n>
</Button>
</Grid>
</Grid>
</Paper>
);
})}

<Paper style={paperClass}>
<Button startIcon={<Add />} disabled={disabled} onClick={addResource}>
<I18n>
<En>Add item</En>
<Fr>Ajouter une ressource</Fr>
</I18n>
</Button>
</Paper>
</div>
);
};

export default RelatedWorks;
11 changes: 10 additions & 1 deletion src/components/Pages/MetadataForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import SimpleModal from "../FormComponents/SimpleModal";
import StartTab from "../Tabs/StartTab";
import ContactTab from "../Tabs/ContactTab";
import ResourcesTab from "../Tabs/ResourcesTab";
import RelatedWorksTab from "../Tabs/RelatedWorksTab";
import IdentificationTab from "../Tabs/IdentificationTab";
import PlatformTab from "../Tabs/PlatformTab";
import SpatialTab from "../Tabs/SpatialTab";
Expand Down Expand Up @@ -468,6 +469,12 @@ class MetadataForm extends FormClassTemplate {
label={tabs.resources[language]}
value="distribution"
/>
<Tab
fullWidth
classes={{ root: classes.tabRoot }}
label={tabs.relatedworks[language]}
value="related"
/>
<Tab
fullWidth
classes={{ root: classes.tabRoot }}
Expand Down Expand Up @@ -528,7 +535,9 @@ class MetadataForm extends FormClassTemplate {
<TabPanel value={tabIndex} index="distribution">
<ResourcesTab {...tabProps} />
</TabPanel>

<TabPanel value={tabIndex} index="related">
<RelatedWorksTab {...tabProps} />
</TabPanel>
<TabPanel value={tabIndex} index="submit">
<SubmitTab
{...tabProps}
Expand Down
Loading
Loading