From 4e5eee9cd4feaa83959c49f35811f0635ff3e86d Mon Sep 17 00:00:00 2001 From: Matthew Foster Date: Wed, 22 Nov 2023 21:43:43 -0800 Subject: [PATCH 01/18] add fields --- .../FormComponents/AssociatedResources.jsx | 282 ++++++++++++++++++ src/components/Pages/MetadataForm.jsx | 11 +- .../Tabs/AssociatedResourcesTab.jsx | 73 +++++ src/isoCodeLists.js | 191 ++++++++++++ src/utils/tabs.js | 1 + 5 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 src/components/FormComponents/AssociatedResources.jsx create mode 100644 src/components/Tabs/AssociatedResourcesTab.jsx diff --git a/src/components/FormComponents/AssociatedResources.jsx b/src/components/FormComponents/AssociatedResources.jsx new file mode 100644 index 00000000..1d7965f9 --- /dev/null +++ b/src/components/FormComponents/AssociatedResources.jsx @@ -0,0 +1,282 @@ +import React from "react"; +import { + Add, + Delete, + ArrowUpwardSharp, + ArrowDownwardSharp, +} from "@material-ui/icons"; +import { Button, Grid, Paper, TextField } from "@material-ui/core"; +import Autocomplete from '@material-ui/lab/Autocomplete'; +import validator from "validator"; +import { useParams } from "react-router-dom"; +import { En, Fr, I18n } from "../I18n"; +import { associationTypeCode, initiativeTypeCode, identifierType } from "../../isoCodeLists"; + +import BilingualTextInput from "./BilingualTextInput"; +import RequiredMark from "./RequiredMark"; +import SelectInput from "../FormComponents/SelectInput"; +import { deepCopy } from "../../utils/misc"; +import { QuestionText, paperClass, SupplementalText } from "./QuestionStyles"; + +const validateURL = (url) => !url || validator.isURL(url); + +const AssociatedResources = ({ updateResources, resources, disabled }) => { + + const emptyResource = { title: { en: "", fr: "" }, authority: "", code: "", association_type: "", initiative_type: "", url: "" }; + 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 ( +
+ {resources.map((dist = deepCopy(emptyResource), i) => { + function urlIsValid(url) { + return (e) => { + !url || validateURL(url) + }; + } + function handleResourceChange(key) { + return (e) => { + const newValue = [...resources]; + newValue[i][key] = e.target.value; + updateResources(newValue); + }; + } + function handleIdentifierChange(key) { + return (e) => { + + const newValue = [...resources]; + newValue[i][key] = e.target.value; + + if (urlIsValid(newValue[i]['code']) && !newValue[i]['urls']) { + console.log('Code is Valid URL') + newValue[i]['url'] = newValue[i]['code'] + } + + 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']) && /^http.?:\/\/ca\.cioos/i.test(s): + newValue[i]['authority'] = 'ca.cioos' + 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); + console.log(newValue[i]) + + }; + } + return ( + + + + + + Enter a title of the resource + Entrez une titre de la ressource + + + {" "} + } + value={dist.title} + onChange={handleResourceChange("title")} + disabled={disabled} + /> + + + + + Enter the identifier for the related resource + Saisissez l'identifiant de la ressource associée + + + + + + +

+ The identifier may be to a metadata resource on another + repository or another record within CIOOS. A DOI or full URL are prefered. +

+
+ +

+ L'identifiant peut provenir d'une ressource de métadonnées sur un autre + référentiel ou d'un autre enregistrement au sein de CIOOS. Un DOI ou une + URL complète sont préférables. +

+
+
+
+
+ } + value={dist.code} + onChange={handleIdentifierChange("code")} + fullWidth + disabled={disabled} + /> +
+ + + + Enter the identifier type + Entrez le type d'identifiant + + + + + } + + onChange={handleResourceChange("authority")} + fullWidth + disabled={disabled} + />} + /> + + + + } + value={dist.url} + onChange={handleIdentifierChange("url")} + fullWidth + disabled={disabled} + /> + + + + + + + + + What is the relation type? + + Quel est le type de relation? + + + + title[language] + )} + optionTooltips={Object.values(associationTypeCode).map( + ({ text }) => text[language] + )} + disabled={disabled} + label={} + fullWidth={false} + /> + + + + + What is the initiative type? + + Quel est le type d'initiative ? + + + title[language] + )} + optionTooltips={Object.values(initiativeTypeCode).map( + ({ text }) => text[language] + )} + disabled={disabled} + label={} + fullWidth={false} + /> + + + + + + +
+
+ ); + })} + + + + +
+ ); +}; + +export default AssociatedResources; diff --git a/src/components/Pages/MetadataForm.jsx b/src/components/Pages/MetadataForm.jsx index 98d38627..b49c0e7d 100644 --- a/src/components/Pages/MetadataForm.jsx +++ b/src/components/Pages/MetadataForm.jsx @@ -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 AssociatedResourcesTab from "../Tabs/AssociatedResourcesTab"; import IdentificationTab from "../Tabs/IdentificationTab"; import PlatformTab from "../Tabs/PlatformTab"; import SpatialTab from "../Tabs/SpatialTab"; @@ -464,6 +465,12 @@ class MetadataForm extends FormClassTemplate { label={tabs.resources[language]} value="distribution" /> + - + + + { + const updateResources = updateRecord("associated_resources"); + return ( +
+ + + Enter any links to other related metadata records that are associated with this dataset. + + Entrez les liens vers les informations associées à ce jeu de + données. + + + + + + Related metadata records are: +
    +
  • Other datasets within CIOOS, hosted on the same catalogue or a different one
  • +
  • Metadata records on other catalogues such as OBIS or FRDR
  • +
+ + Some of the ways a record can be related are: +
    +
  • Cross Reference - Reference from one dataset to another. Use to identify related documents or related resources
  • +
  • Dependency - Associated through a dependency
  • +
  • Is Composed Of/Larger Work Citation - Reference to record that are parts of this resource or which this one is a part of
  • +
  • Revision Of - Resource is a revision of associated record
  • +
+
+ + Les enregistrements de métadonnées associés sont: +
    +
  • Autres ensembles de données au sein de CIOOS, hébergés sur le même catalogue ou sur un autre
  • +
  • Enregistrements de métadonnées sur d'autres catalogues tels que OBIS ou FRDR
  • +
+ + Voici quelques façons de relier un enregistrement: +
    +
  • Référence croisée - référence d'un ensemble de données à un autre. À utiliser pour identifier des documents ou des ressources associés
  • +
  • Dépendance - Associé via une dépendance
  • +
  • Est composé de/Citation de travail plus grande - Référence à l'enregistrement qui fait partie de cette ressource ou dont celle-ci fait partie
  • +
  • Révision de - la ressource est une révision de l'enregistrement associé
  • +
+
+
+
+
+
+ +
+ ); +}; + +export default AssociatedResourcesTab; diff --git a/src/isoCodeLists.js b/src/isoCodeLists.js index c7f2772f..392f4ed8 100644 --- a/src/isoCodeLists.js +++ b/src/isoCodeLists.js @@ -217,3 +217,194 @@ export const depthDirections = { heightPositive: { en: "Depth Positive", fr: "Profondeur positive" }, depthPositive: { en: "Height Positive", fr: "Hauteur positive" }, }; + +export const associationTypeCode = { + collectiveTitle: { + title: { en: "Collective Title", fr: "Titre Collectif" }, + text: { + en: "Common title for a collection of resources", + fr: "Titre commun pour une collection de ressources", + }, + }, + crossReference: { + title: { en: "Cross Reference", fr: "Références Croisées" }, + text: { + en: "Reference from one dataset to another. Use to identify related documents or related resources", + fr: "Référence d'un jeu de données à un autre. À utiliser pour identifier des documents ou des ressources connexes", + }, + }, + dependency: { + title: { en: "Dependency", fr: "Dépendance" }, + text: { + en: "Associated through a dependency", + fr: "Associé par une dépendance", + }, + }, + isComposedOf: { + title: { + en: "Is Composed Of", fr: "Est composé de" + }, + text: { + en: "Reference to resources that are parts of this resource", + fr: "Référence aux ressources qui font partie de cette ressource", + }, + }, + largerWorkCitation: { + title: { + en: "Larger Work Citation", fr: "Citation de travail plus grande" + }, + text: { + en: "Reference to a master dataset of which this one is a part", + fr: "Référence à un jeu de données maître dont celui- ci fait partie", + }, + }, + partOfSeamlessDatabase: { + title: { en: "Part Of Seamless Database", fr: "Partie d'une base de données transparente" }, + text: { + en: "Part of the same structured set of data held in a computer", + fr: "Fait partie du même ensemble structuré de données contenues dans un ordinateur", + }, + }, + revisionOf: { + title: { en: "Revision Of", fr: "Révision de" }, + text: { + en: "Resource is a revision of associated resource", + fr: "La ressource est une révision de la ressource associée", + }, + }, + series: { + title: { en: "Series", fr: "Série" }, + text: { + en: "Associated through a common heritage such as produced to a common product specification", + fr: "Associé à travers un héritage commun tel que produit selon une spécification de produit commune", + }, + }, + stereoMate: { + title: { en: "Stereo Mate", fr: "Compagnon Stéréo" }, + text: { + en: "Part of a set of imagery that when used together, provides three-dimensional images", + fr: "Fait partie d'un ensemble d'images qui, lorsqu'elles sont utilisées ensemble, fournissent des images en trois dimensions", + }, + }, + +}; + + + +export const initiativeTypeCode = { + campaign: { + title: { en: "campaign", fr: "" }, + text: { + en: "series of organized planned actions", fr: "", + }, + }, + collection: { + title: { en: "collection", fr: "" }, + text: { + en: "accumulation of datasets assembled for a specific purpose", fr: "", + }, + }, + exercise: { + title: { en: "exercise", fr: "" }, + text: { + en: "specific performance of a function or group of functions", fr: "", + }, + }, + experiment: { + title: { en: "experiment", fr: "" }, + text: { + en: "process designed to find if something is effective or valid", fr: "", + }, + }, + investigation: { + title: { en: "investigation", fr: "" }, + text: { + en: "search or systematic inquiry", fr: "", + }, + }, + mission: { + title: { en: "mission", fr: "" }, + text: { + en: "specific operation of a data collection system", fr: "", + }, + }, + operation: { + title: { en: "operation", fr: "" }, + text: { + en: "action that is part of a series of actions", fr: "", + }, + }, + platform: { + title: { en: "platform", fr: "" }, + text: { + en: "vehicle or other support base that holds a sensor", fr: "", + }, + }, + process: { + title: { en: "process", fr: "" }, + text: { + en: "method of doing something involving a number of steps", fr: "", + }, + }, + program: { + title: { en: "program", fr: "" }, + text: { + en: "specific planned activity", fr: "", + }, + }, + project: { + title: { en: "project", fr: "" }, + text: { + en: "organized undertaking, research, or development", fr: "", + }, + }, + sensor: { + title: { en: "sensor", fr: "" }, + text: { + en: "device or piece of equipment which detects or records", fr: "", + }, + }, + study: { + title: { en: "study", fr: "" }, + text: { + en: "examination or investigation", fr: "", + }, + }, + task: { + title: { en: "task", fr: "" }, + text: { + en: "piece of work", fr: "", + }, + }, + trial: { + title: { en: "trial", fr: "" }, + text: { + en: "process of testing to discover or demonstrate somethin", fr: "", + }, + }, + +}; + +export const identifierType = [ + "ARK", + "arXiv", + "bibcode", + "ca.cioos", + "DOI", + "EAN13", + "EISSN", + "Handle", + "IGSN", + "ISBN", + "ISSN", + "ISTC", + "LISSN", + "LSID", + "PMID", + "PURL", + "UPC", + "URL", + "URN", + "w3id", +] + diff --git a/src/utils/tabs.js b/src/utils/tabs.js index 666bc85d..86915ed1 100644 --- a/src/utils/tabs.js +++ b/src/utils/tabs.js @@ -5,6 +5,7 @@ const tabs = { spatial: { en: "Spatial", fr: "Spatial" }, contacts: { en: "Contacts", fr: "Contacts" }, resources: { en: "Resources", fr: "Ressources" }, + associatedresources: { en: "Related", fr: "En rapport" }, platform: { en: "Platform", fr: "Plateforme" }, platformInstruments: { en: "Platform - instruments", From bba1df00d680d6900c87541f3f472aab02c19956 Mon Sep 17 00:00:00 2001 From: Matthew Foster Date: Wed, 22 Nov 2023 21:51:37 -0800 Subject: [PATCH 02/18] remove url field. assume that identifier is a valid url. --- .../FormComponents/AssociatedResources.jsx | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/components/FormComponents/AssociatedResources.jsx b/src/components/FormComponents/AssociatedResources.jsx index 1d7965f9..a893b68f 100644 --- a/src/components/FormComponents/AssociatedResources.jsx +++ b/src/components/FormComponents/AssociatedResources.jsx @@ -63,13 +63,7 @@ const AssociatedResources = ({ updateResources, resources, disabled }) => { const newValue = [...resources]; newValue[i][key] = e.target.value; - if (urlIsValid(newValue[i]['code']) && !newValue[i]['urls']) { - console.log('Code is Valid URL') - newValue[i]['url'] = newValue[i]['code'] - } - let s = newValue[i]['code'] - switch (true) { case urlIsValid(newValue[i]['code']) && /^http.?:\/\/doi\.org\//i.test(s): newValue[i]['authority'] = 'DOI' @@ -87,10 +81,7 @@ const AssociatedResources = ({ updateResources, resources, disabled }) => { newValue[i]['authority'] = '' break; } - updateResources(newValue); - console.log(newValue[i]) - }; } return ( @@ -169,20 +160,6 @@ const AssociatedResources = ({ updateResources, resources, disabled }) => { />} /> - - - } - value={dist.url} - onChange={handleIdentifierChange("url")} - fullWidth - disabled={disabled} - /> - - - - - From a1a76105ec7948be2f36d997657c34cfd2c1cf93 Mon Sep 17 00:00:00 2001 From: Matthew Foster Date: Thu, 23 Nov 2023 11:01:02 -0800 Subject: [PATCH 03/18] add test for more code lists --- src/__tests__/isoCodeList.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/__tests__/isoCodeList.test.js b/src/__tests__/isoCodeList.test.js index b3d0ff82..0cb822dc 100644 --- a/src/__tests__/isoCodeList.test.js +++ b/src/__tests__/isoCodeList.test.js @@ -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(); }); From 730fce388a10eb3938805bf89da60534de124963 Mon Sep 17 00:00:00 2001 From: Matthew Foster Date: Thu, 23 Nov 2023 12:47:46 -0800 Subject: [PATCH 04/18] fix url validator and remove ca.cioos authority match in favour of using URL --- src/components/FormComponents/AssociatedResources.jsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/components/FormComponents/AssociatedResources.jsx b/src/components/FormComponents/AssociatedResources.jsx index a893b68f..4eedc7e6 100644 --- a/src/components/FormComponents/AssociatedResources.jsx +++ b/src/components/FormComponents/AssociatedResources.jsx @@ -46,9 +46,7 @@ const AssociatedResources = ({ updateResources, resources, disabled }) => {
{resources.map((dist = deepCopy(emptyResource), i) => { function urlIsValid(url) { - return (e) => { - !url || validateURL(url) - }; + return !url || validateURL(url); } function handleResourceChange(key) { return (e) => { @@ -69,10 +67,6 @@ const AssociatedResources = ({ updateResources, resources, disabled }) => { newValue[i]['authority'] = 'DOI' console.log('MATCH DOI') break; - case urlIsValid(newValue[i]['code']) && /^http.?:\/\/ca\.cioos/i.test(s): - newValue[i]['authority'] = 'ca.cioos' - console.log('MATCH DOI') - break; case urlIsValid(newValue[i]['code']): newValue[i]['authority'] = 'URL' console.log('MATCH URL') From 31003cb9c3ec7cb5eac963e52ae2e0ec4f86d639 Mon Sep 17 00:00:00 2001 From: Matthew Foster Date: Fri, 24 Nov 2023 09:47:10 -0800 Subject: [PATCH 05/18] remove intitive type question, switch identifier type back to select box, update identifier type label, and help text to relation type field --- .../FormComponents/AssociatedResources.jsx | 70 +++++++++---------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/src/components/FormComponents/AssociatedResources.jsx b/src/components/FormComponents/AssociatedResources.jsx index 4eedc7e6..a7fc0b35 100644 --- a/src/components/FormComponents/AssociatedResources.jsx +++ b/src/components/FormComponents/AssociatedResources.jsx @@ -6,11 +6,10 @@ import { ArrowDownwardSharp, } from "@material-ui/icons"; import { Button, Grid, Paper, TextField } from "@material-ui/core"; -import Autocomplete from '@material-ui/lab/Autocomplete'; import validator from "validator"; import { useParams } from "react-router-dom"; import { En, Fr, I18n } from "../I18n"; -import { associationTypeCode, initiativeTypeCode, identifierType } from "../../isoCodeLists"; +import { associationTypeCode, identifierType } from "../../isoCodeLists"; import BilingualTextInput from "./BilingualTextInput"; import RequiredMark from "./RequiredMark"; @@ -22,7 +21,7 @@ const validateURL = (url) => !url || validator.isURL(url); const AssociatedResources = ({ updateResources, resources, disabled }) => { - const emptyResource = { title: { en: "", fr: "" }, authority: "", code: "", association_type: "", initiative_type: "", url: "" }; + const emptyResource = { title: { en: "", fr: "" }, authority: "", code: "", association_type: "", url: "" }; const { language } = useParams(); function addResource() { @@ -140,18 +139,14 @@ const AssociatedResources = ({ updateResources, resources, disabled }) => { - } - - onChange={handleResourceChange("authority")} - fullWidth - disabled={disabled} - />} + onChange={handleResourceChange("authority")} + options={identifierType} + optionLabels={identifierType} + disabled={disabled} + label={< I18n en="Identifier Iype" fr="Type d'identifiant" />} + fullWidth={false} /> @@ -162,6 +157,30 @@ const AssociatedResources = ({ updateResources, resources, disabled }) => { Quel est le type de relation? + + + +

+ Specify the relationship between this record and another. The relationship is from the perspective of What the other record is to this one. for example: +

+
    +
  • Use the 'crossReference' code value to identify related datasets.
  • +
  • Use 'largerWorkCitation' code value to identify a larger program or operation of which this record is a part.
  • +
+ +
+ +

+ Spécifiez la relation entre cet enregistrement et un autre. La relation est du point de vue de ce qu'est l'autre disque par rapport à celui-ci. Par exemple: +

+
    +
  • Utilisez la valeur du code « crossReference » pour identifier les ensembles de données associés.
  • +
  • Utilisez la valeur de code « largerWorkCitation » pour identifier un programme ou une opération plus vaste dont cet enregistrement fait partie.
  • +
+ +
+
+
{ fullWidth={false} />
- - - - What is the initiative type? - - Quel est le type d'initiative ? - - - title[language] - )} - optionTooltips={Object.values(initiativeTypeCode).map( - ({ text }) => text[language] - )} - disabled={disabled} - label={} - fullWidth={false} - /> -