diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/components/WidgetInput.vue b/packages/piveau-hub-ui-modules/lib/data-provider-interface/components/WidgetInput.vue new file mode 100644 index 0000000000000000000000000000000000000000..6f6d3d9f3346bcec85a0814ff3ba9813db75cb7f --- /dev/null +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/components/WidgetInput.vue @@ -0,0 +1,123 @@ +<template> + <div class="formkitProperty"> + <h4 class="">Tag</h4> + + <div class="formkitCmpWrap d-flex p-3 w-100"> + + <div class="formkit-inner"> + <input readonly="" placeholder="Wählen Sie die Art des Widgets aus" class="formkit-input" type="text" + name="mode" aria-required="true" :value="widgetType" aria-describedby="input_164-rule_required" + @click="activeList = !activeList"> + <ul ref="dropdownListWidget" v-show="activeList" class="autocompleteResultList"> + <li v-for="match in matches" :key="match" @click="setValue(match); activeList = !activeList" + class="p-2 border-b border-gray-200 data-[selected=true]:bg-blue-100 choosableItemsAC">{{ match + }} + </li> + </ul> + </div> + + <!-- Liste Distribution/ Widget/ Report --- leading kann nur 1 sein und auch nur ein widget --> + <button class="ml-3 widgetButton" v-if="widgetType === 'Widget' " + :class="{ activeLeading: activeButton || props.context.value === 'LeadingWidget', }" type="button" + @click="isLeading"> + Leading + </button> + </div> + </div> + + + +</template> +<script setup> +import { ref, watch } from 'vue'; +import { getNode } from '@formkit/core' + +const props = defineProps({ + context: Object +}) +let disList = ref(getNode('Distributions').value['distributionList']) +let activeButton = ref(false) + +const isLeading = () => { + + checkForLead() + + // console.log(props.context.value['type']); + if (props.context.value['type'] != 'LeadingWidget' && !activeLeadingWidget.value) { + props.context.node.input('LeadingWidget') + activeButton.value = !activeButton.value + } else { + activeLeadingWidget.value = false + activeButton.value = false + widgetType.value = "Widget" + props.context.node.input('Widget') + } + +} +const activeList = ref(false) +const matches = ref(['Distribution', 'Widget', 'Report']) +let activeLeadingWidget = ref(false) +let widgetType = ref('') + + +// Init every dis as distribution if there's nothing set - default state + + +if (props.context.value != undefined) { + if (props.context.value === 'LeadingWidget') { + widgetType.value = 'Widget' + } else widgetType.value = props.context.value +} else widgetType.value = 'Distribution' + +console.log(widgetType); + + +const checkForLead = () => { + disList.value = getNode('Distributions').value['distributionList'] + for (let index = 0; index < disList.value.length; index++) { + + if (disList.value[index]['Mandatory']['pv:DistributionType'] === 'LeadingWidget') { + activeLeadingWidget.value = true + } + + } +} + +const setValue = (item) => { + checkForLead() + props.context.node.input(item) + widgetType.value = item +} +</script> +<style scoped> +.formkit-inner:focus-within { + margin-top: 0; +} + +.formkit-inner { + + flex-grow: 1; + +} + +.formkitCmpWrap { + position: relative; +} + +.widgetButton { + border-color: lightgray; + + &:hover { + background-color: green; + color: white; + } + +} + +.activeLeading { + background-color: green; + color: white; + border-color: green; + +} +</style> \ No newline at end of file diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/format-types.js b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/format-types.js new file mode 100644 index 0000000000000000000000000000000000000000..0fc89bc6f9060f2aee375e25a64184c7de0bf16f --- /dev/null +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/format-types.js @@ -0,0 +1,270 @@ +// all properties which value is a single URI +const singularURI = { + datasets: [ + "dct:accrualPeriodicity", + "dct:accessRights", + "dct:type", + "dcatap:availability", + "dcatde:qualityProcessURI", + + // singular URIs nested within other properties + 'vcard:hasEmail', // contact point + 'vcard:hasURL', // contect point + 'dext:isUsedBy', // isUsedBy + 'foaf:mbox', // creator, publisher + 'foaf:homepage', // creator, publisher + 'dct:format', // page + ], + distributions: [ + "dct:format", + "dct:type", + "dcat:mediaType", + "dcatap:availability", + "dcat:compressFormat", + "dcat:packageFormat", + "adms:status", + // singular URIs nested within other properties + 'spdx:algorithm', // checksum + 'dcat:endpointURL', // accessservice + "skos:exactMatch", // license, + 'dct:license' + ], + catalogues: [ + 'foaf:homepage', // homepage and creator + 'dct:isPartOf', + // singular URIs nested within other properties + 'foaf:mbox', // creator + 'dct:format', // page + "skos:exactMatch", // license + ], +}; + +// all properties with multiple URI values +const multipleURI = { + datasets: [ + "dcatde:politicalGeocodingLevelURI", + "dcatde:politicalGeocodingURI", + "dcatde:contributorID", + "dct:language", + "dct:subject", + "dcat:theme", + "dct:source", + "dcat:landingPage", + "dct:relation", + "dcat:qualifiedRelation", + "prov:qualifiedAttribution", + "dct:isReferencedBy", + "prov:wasGeneratedBy", + "dct:isVersionOf", + "dct:hasVersion", + "dct:references", + "dct:spatial", + ], + distributions: [ + "dcat:accessURL", + "dcat:downloadURL", + "dct:language", + "odrl:hasPolicy", + ], + catalogues: [ + "dct:hasPart", + 'dcat:catalog', + 'dct:language', + 'dct:spatial', + ], +}; + +// all properties which are typed strings +const typedStrings = { + datasets: [ + "dct:issued", + "dct:modified", + "dcat:spatialResolutionInMeters", + // nested typed strings + 'dcat:endDate', + 'dcat:startDate', + ], + distributions: [ + "dct:issued", + "dct:modified", + "dcat:spatialResolutionInMeters", + "pv:DistributionType", + "dcat:byteSize", + ], + catalogues: [], +}; + +// all properties with a singular string +const singularString = { + datasets: [ + "owl:versionInfo", + // nested singulat strings + 'vcard:fn', // contactPoint + 'vcard:hasOrganizationName', // contactPoint + 'vcard:hasTelephone', // contactPoint + "vcard:country_name", // hasAddress + "vcard:locality", // hasAddress + "vcard:postal_code", // hasAddress + "vcard:street_address", // hasAddress + 'rdfs:label', // conformsTo and provenance + 'foaf:name', // creator, publisher + ], + distributions: [ + // nested singular string + 'spdx:checksumValue', //checksum + 'rdfs:label', // rights !!! + "skos:prefLabel", //license + ], + catalogues: [ + // nested singular strings + 'rdfs:label', // conformsTo and rights + 'foaf:name', // creator + "skos:prefLabel", // license + ], +}; + +// all properties which can be provided in different languages +const multilingualStrings = { + datasets: [ + "dct:title", // also nested within page + "dct:description", // also nested within page + "dcat:keyword", + "adms:versionNotes", + "dcatde:geocodingDescription", + "dcatde:legalBasis", + + ], + distributions: [ + "dct:title", // also nested within page + "dct:description", // also nested within page + "dcatde:licenseAttributionByText", + ], + catalogues: [ + 'dct:title', + 'dct:description', + ], +}; + +// all properties which contain grouped values +const groupedProperties = { + datasets: [ + 'dcat:contactPoint', + 'dct:creator', + 'dext:metadataExtension', + 'dct:provenance', + 'dct:conformsTo', + 'foaf:page', + 'dct:temporal', + 'adms:identifier', + // nested grouped properties + 'vcard:hasAddress', + 'skos:notation', + "dct:contributor", + "dcatde:originator", + "dcatde:maintainer", + ], + distributions: [ + 'foaf:page', + 'dcat:accessService', + 'spdx:checksum', + 'dct:conformsTo', + ], + catalogues: [ + 'dct:creator', + 'dct:conformsTo', + ] +}; + +// some properties provide the ability to choose the input type and therefore the respective fields which will be provided +const conditionalProperties = { + datasets: [ + 'dct:publisher', + ], + distributions: [], + catalogues: [ + 'dct:publisher', + 'dct:license' + ], +} + +// some properties have additional statement included which must be added to the linked data +const additionalPropertyTypes = { + 'dct:temporal': 'dct:PeriodOfTime', + 'dct:conformsTo': 'dct:Standard', + 'foaf:page': 'foaf:Document', + 'dct:provenance': 'dct:ProvenanceStatement', + 'dext:metadataExtension': 'dext:MetadataExtension', + 'spdx:checksum': 'spdx:Checksum', + 'dcat:accessService': 'dcat:DataService', + 'dct:publisher': 'foaf:Agent', +} + +// some inputs need URIs in diefferent formats +const URIformat = { + // {'name': '', 'resource': ''} mainly needed for vocabulary data + voc: [ + 'dct:publisher', + 'dcat:theme', + "dct:accrualPeriodicity", + "dct:accessRights", + "dct:type", + "dct:format", + "dcat:mediaType", + "dcatap:availability", + "dcat:compressFormat", + "dcat:packageFormat", + 'spdx:algorithm', + "dct:subject", + "dct:language", + "adms:status", + "dct:spatial", + "dcatde:politicalGeocodingLevelURI", + "dcatde:contributorID", + "dcatde:politicalGeocodingURI", + 'dct:license' + + ], + // 'URI' mainly used for mail addresses + string: [ + 'vcard:hasEmail', + 'vcard:hasURL', + 'foaf:mbox', + "skos:exactMatch", + 'foaf:homepage', + 'dext:isUsedBy', + 'dcat:endpointURL', + 'dcatde:qualityProcessURI', + ], + // {'@id': ''} mainly used for repeated links + id: [ + 'dct:source', + "dcat:accessURL", + "dcat:downloadURL", + "odrl:hasPolicy", + "dct:hasPart", + 'dcat:catalog', + "dct:source", + "dcat:landingPage", + "dct:relation", + "dcat:qualifiedRelation", + "prov:qualifiedAttribution", + "dct:isReferencedBy", + "prov:wasGeneratedBy", + "dct:isVersionOf", + "dct:hasVersion", + 'dct:isPartOf', + "dct:references", + ] +} + +export default { + singularURI, + multipleURI, + typedStrings, + singularString, + multilingualStrings, + groupedProperties, + additionalPropertyTypes, + conditionalProperties, + URIformat, +}; \ No newline at end of file diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/input-definition.ts b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/input-definition.ts new file mode 100644 index 0000000000000000000000000000000000000000..657020c246f849b524101f2136987afb25728aaa --- /dev/null +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/input-definition.ts @@ -0,0 +1,2279 @@ +import { type FormKitSchemaDefinition } from "@formkit/core"; + +import language from "../selector-languages.json"; +import config from "./page-content-config"; + +/** + * Available properties for datasets. + */ +export type DcatApDatasetsProperty = + // Append new properties here for accurate type checking + | "datasetID" + | "overview" + | "politicalGeocodingLevelURI" + | "politicalGeocodingURI" + | "availabilityDE" + | "geocodingDescription" + | "legalBasis" + | "qualityProcessURI" + | "references" + | "contributor" + | "contributorID" + | "originator" + | "maintainer" + | "description" + | "title" + | "contactPoint" + | "subject" + | "keyword" + | "publisher" + | "spatial" + | "temporal" + | "theme" + | "accessRights" + | "creator" + | "conformsTo" + | "page" + | "accrualPeriodicity" + | "hasVersion" + | "isVersionOf" + | "source" + | "identifier" + | "isReferencedBy" + | "landingPage" + | "language" + | "admsIdentifier" + | "provenance" + | "qualifiedAttribution" + | "wasGeneratedBy" + | "qualifiedRelation" + | "relation" + | "issued" + | "modified" + | "spatialResolutionInMeters" + | "temporalResolution" + | "type" + | "versionInfo" + | "versionNotes" + | "catalog" + | "isUsedBy"; + +/** + * Available properties for distributions. + */ +export type DcatApDistributionsProperty = + // Append new properties here for accurate type checking + | "accessURL" + | "isWidget" + | "licenseAttributionByText" + | "availabilityDisDE" + | "availability" + | "description" + | "format" + | "title" + | "byteSize" + | "conformsTo" + | "issued" + | "modified" + | "rights" + | "spatialResolutionInMeters" + | "temporalResolution" + | "mediaType" + | "downloadUrl" + | "accessService" + | "checksum" + | "compressFormat" + | "packageFormat" + | "page" + | "language" + | "licence" + | "conformsTo" + | "issued" + | "modified" + | "rights" + | "spatialResolutionInMeters" + | "temporalResolution" + | "type" + | "hasPolicy" + | "status"; + +export type DcatApCataloguesProperty = + // Append new properties here for accurate type checking + | "datasetID" + | "overview" + | "availabilityCatDE" + | "title" + | "description" + | "publisher" + | "language" + | "licence" + | "spatial" + | "homepage" + | "hasPart" + | "isPartOf" + | "rights" + | "catalog" + | "creator"; + +export type InputDefinition = { + datasets: Record<DcatApDatasetsProperty, FormKitSchemaDefinition>; + distributions: Record<DcatApDistributionsProperty, FormKitSchemaDefinition>; + catalogues: Record<DcatApCataloguesProperty, FormKitSchemaDefinition>; +}; + +const dcatapProperties: InputDefinition = { + datasets: { + overview: { + $cmp: "OverviewPage", + props: { + property: "datasets", + }, + }, + // Dcatap.de Properties #### Start #### + politicalGeocodingLevelURI: { + identifier: "politicalGeocodingLevelURI", + $formkit: "auto", + name: "dcatde:politicalGeocodingLevelURI", + class: "property", + voc: "political-geocoding-level", + multiple: true, + }, + politicalGeocodingURI: { + identifier: "politicalGeocodingURI", + $formkit: "repeatable", + name: "dcatde:politicalGeocodingURI", + children: [ + { + $formkit: "spatialinput", + name: "dcatde:politicalGeocodingURI", + identifier: "politicalGeocodingURI", + }, + ], + }, + availabilityDE: { + identifier: "availabilityDE", + $formkit: "auto", + name: "dcatap:availability", + class: "property", + voc: "planned-availability", + }, + geocodingDescription: { + identifier: "geocodingDescription", + $formkit: "repeatable", + name: "dcatde:geocodingDescription", + children: [ + { + identifier: "geocodingDescription", + $formkit: "group", + name: "dcatde:geocodingDescription", + class: "property langDescriptionInput", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "geocodingDescription", + $formkit: "textarea", + name: "@value", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + legalBasis: { + identifier: "legalBasis", + $formkit: "repeatable", + name: "dcatde:legalBasis", + children: [ + { + identifier: "legalBasis", + $formkit: "group", + name: "dcatde:legalBasis", + class: "property langDescriptionInput", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "legalBasis", + $formkit: "textarea", + name: "@value", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + qualityProcessURI: { + identifier: "qualityProcessURI", + name: "dcatde:qualityProcessURI", + class: "property", + $formkit: "simpleInput", + validationType: "url", + classes: { outer: "formkitProperty formkitCmpWrap mx-0 my-3 p-3" }, + }, + references: { + identifier: "references", + name: "dct:references", + $formkit: "repeatable", + children: [ + { + $formkit: "group", + identifier: "references", + name: "dct:references", + class: "property", + children: [ + { + name: "@id", + identifier: "references", + $formkit: "simpleInput", + validationType: "url", + insideRepeatable: true, + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + contributor: { + identifier: "contributor", + $formkit: "repeatable", + name: "dct:contributor", + children: [ + { + identifier: "contributor", + $formkit: "group", + name: "dct:contributor", + class: "property", + children: [ + { + identifier: "contributorType", + $formkit: "select", + name: "rdf:type", + options: { + "": "---", + "vcard:Individual": "Person", + "vcard:Organization": "Organization", + }, + }, + { + identifier: "contributorEmail", + $formkit: "email", + name: "foaf:mbox", + validation: "optional|email", + }, + { + identifier: "contributorName", + $formkit: "text", + name: "foaf:name", + }, + { + identifier: "contributorHomepage", + $formkit: "url", + name: "foaf:homepage", + validation: "optional|url", + }, + ], + }, + ], + }, + contributorID: { + identifier: "contributorID", + $formkit: "auto", + name: "dcatde:contributorID", + class: "property", + voc: "contributors", + multiple: true, + }, + originator: { + identifier: "originator", + $formkit: "repeatable", + name: "dcatde:originator", + children: [ + { + identifier: "originator", + $formkit: "group", + name: "dcatde:originator", + class: "property", + children: [ + { + identifier: "originatorType", + $formkit: "select", + name: "rdf:type", + options: { + "": "---", + "vcard:Individual": "Person", + "vcard:Organization": "Organization", + }, + }, + { + identifier: "originatorEmail", + $formkit: "email", + name: "foaf:mbox", + validation: "optional|email", + }, + { + identifier: "originatorName", + $formkit: "text", + name: "foaf:name", + }, + { + identifier: "originatorHomepage", + $formkit: "url", + name: "foaf:homepage", + validation: "optional|url", + }, + ], + }, + ], + }, + maintainer: { + identifier: "maintainer", + $formkit: "repeatable", + name: "dcatde:maintainer", + children: [ + { + identifier: "maintainer", + $formkit: "group", + name: "dcatde:maintainer", + class: "property", + children: [ + { + identifier: "maintainerType", + $formkit: "select", + name: "rdf:type", + options: { + "": "---", + "vcard:Individual": "Person", + "vcard:Organization": "Organization", + }, + }, + { + identifier: "maintainerEmail", + $formkit: "email", + name: "foaf:mbox", + validation: "optional|email", + }, + { + identifier: "maintainerName", + $formkit: "text", + name: "foaf:name", + }, + { + identifier: "maintainerHomepage", + $formkit: "url", + name: "foaf:homepage", + validation: "optional|url", + }, + ], + }, + ], + }, + // Dcatap.de Properties #### End #### + datasetID: { + identifier: "datasetID", + $formkit: "id", + name: "datasetID", + class: "property mandatory", + }, + description: { + identifier: "description", + $formkit: "repeatable", + name: "dct:description", + minimum: 1, + children: [ + { + identifier: "datasetDescription", + $formkit: "group", + name: "dct:description", + class: "property langDescriptionInput mandatory", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + validation: "required", + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "description", + $formkit: "textarea", + name: "@value", + validation: "required", + classes: { + outer: "w75-descField", + }, + }, + ], + }, + ], + }, + title: { + identifier: "title", + $formkit: "repeatable", + name: "dct:title", + children: [ + { + identifier: "title", + $formkit: "group", + name: "dct:title", + class: "property langStringInput mandatory", + mandatory: true, + minimum: 1, + children: [ + { + identifier: "dctTitle", + value: "de", + $formkit: "select", + validation: "required", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "title", + $formkit: "text", + name: "@value", + validation: "required", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + contactPoint: { + identifier: "contactPoint", + $formkit: "repeatable", + name: "dcat:contactPoint", + children: [ + { + identifier: "contactPoint", + $formkit: "group", + name: "dcat:contactPoint", + class: "property", + children: [ + { + identifier: "contactPointType", + $formkit: "select", + name: "rdf:type", + options: { + "": "---", + "vcard:Individual": "Person", + "vcard:Organization": "Organization", + }, + }, + { + identifier: "contactPointName", + $formkit: "text", + name: "vcard:fn", + }, + { + identifier: "contactPointEmail", + $formkit: "email", + name: "vcard:hasEmail", + validation: "optional|email", + }, + { + identifier: "contactPointAddress", + $formkit: "group", + name: "vcard:hasAddress", + children: [ + { + identifier: "contactPointAddressStreet", + $formkit: "text", + name: "vcard:street_address", + }, + { + identifier: "contactPointAddressPostcode", + $formkit: "text", + name: "vcard:postal_code", + }, + { + identifier: "contactPointAddressCity", + $formkit: "text", + name: "vcard:locality", + }, + { + identifier: "contactPointAddressCountry", + $formkit: "text", + name: "vcard:country_name", + }, + ], + }, + { + identifier: "contactPointTelephone", + $formkit: "tel", + name: "vcard:hasTelephone", + }, + { + identifier: "contactPointUrl", + $formkit: "url", + name: "vcard:hasURL", + validation: "optional|url", + }, + { + identifier: "contactPointOrganisationName", + $formkit: "text", + name: "vcard:hasOrganizationName", + }, + ], + }, + ], + }, + subject: { + identifier: "subject", + $formkit: "auto", + name: "dct:subject", + multiple: true, + annifTheme: true, + class: "property", + voc: "eurovoc", + "@annifSuggestion": false, + }, + keyword: { + identifier: "keyword", + $formkit: "repeatable", + name: "dcat:keyword", + children: [ + { + identifier: "keywordHeader", + $formkit: "group", + name: "dcat:keyword", + class: "property langStringInput", + children: [ + { + identifier: "keywordsLanguage", + value: "de", + $formkit: "select", + name: "@language", + classes: { + outer: "w25-textfield", + }, + options: language, + }, + { + identifier: "keyword", + $formkit: "text", + name: "@value", + classes: { + outer: "w75-descField", + }, + }, + ], + }, + ], + }, + publisher: { + $formkit: "simpleConditional", + name: "dct:publisher", + identifier: "publisher", + voc: "corporate-body", + options: { text: "foaf:name", email: "foaf:mbox", url: "foaf:homepage" }, + selection: { 1: "vocabulary", 2: "manually" }, + }, + spatial: { + identifier: "spatial", + $formkit: "repeatable", + name: "dct:spatial", + children: [ + { + $formkit: "spatialinput", + name: "dct:spatial", + identifier: "spatial", + }, + ], + }, + temporal: { + identifier: "temporal", + $formkit: "repeatable", + name: "dct:temporal", + children: [ + { + $formkit: "group", + name: "dct:temporal", + identifier: "temporal", + children: [ + { + identifier: "temporalStart", + $formkit: "datetime-local", + name: "dcat:startDate", + end: "dct:temporal", + classes: { + outer: "w100-textfield", + }, + }, + { + identifier: "temporalEnd", + $formkit: "datetime-local", + name: "dcat:endDate", + start: "dct:temporal", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + theme: { + identifier: "theme", + $formkit: "auto", + multiple: true, + annifTheme: true, + voc: "data-theme", + name: "dcat:theme", + class: "property", + "@annifSuggestion": false, + }, + accessRights: { + identifier: "accessRights", + $formkit: "auto", + voc: "access-right", + name: "dct:accessRights", + class: "property", + }, + creator: { + identifier: "creator", + $formkit: "formkitGroup", + name: "dct:creator", + class: "property", + children: [ + { + identifier: "creatorType", + $formkit: "select", + name: "rdf:type", + options: { + "": "---", + "foaf:Person": "Person", + "foaf:Organization": "Organization", + }, + }, + { + identifier: "creatorName", + $formkit: "text", + name: "foaf:name", + }, + { + identifier: "creatorEmail", + $formkit: "email", + name: "foaf:mbox", + validation: "optional|email", + }, + { + identifier: "creatorHomepage", + $formkit: "url", + name: "foaf:homepage", + validation: "optional|url", + }, + ], + }, + conformsTo: { + identifier: "conformsTo", + $formkit: "repeatable", + name: "dct:conformsTo", + children: [ + { + identifier: "conformsTo", + $formkit: "group", + name: "dct:conformsTo", + children: [ + { + identifier: "conformsToTitle", + $formkit: "text", + name: "rdfs:label", + classes: { + outer: "w100-textfield", + }, + }, + { + identifier: "conformsToUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + page: { + identifier: "page", + $formkit: "repeatable", + name: "foaf:page", + children: [ + { + identifier: "page", + $formkit: "group", + name: "foaf:page", + children: [ + { + identifier: "pageTitle", + $formkit: "repeatable", + name: "dct:title", + children: [ + { + identifier: "pageTitle", + $formkit: "group", + name: "dct:title", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "pageTitleSub", + $formkit: "text", + name: "@value", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + { + identifier: "pageDescription", + $formkit: "repeatable", + name: "dct:description", + children: [ + { + identifier: "pageDescription", + $formkit: "group", + name: "dct:description", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "pageDescription", + $formkit: "textarea", + name: "@value", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + { + $formkit: "auto", + identifier: "pageFormat", + voc: "file-type", + class: "property", + name: "dct:format", + id: "pageFormat", + classes: { + outer: "w97-textfield", + }, + }, + { + identifier: "pageUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + class: "property", + classes: { + outer: "w97-textfield", + }, + }, + ], + }, + ], + }, + accrualPeriodicity: { + identifier: "accrualPeriodicity", + $formkit: "auto", + voc: "frequency", + name: "dct:accrualPeriodicity", + class: "property", + }, + hasVersion: { + identifier: "hasVersion", + $formkit: "repeatable", + name: "dct:hasVersion", + children: [ + { + $formkit: "group", + identifier: "hasVersion", + name: "dct:hasVersion", + class: "property", + children: [ + { + identifier: "hasVersionUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + isVersionOf: { + identifier: "isVersionOf", + $formkit: "repeatable", + name: "dct:isVersionOf", + children: [ + { + $formkit: "group", + identifier: "isVersionOf", + name: "dct:isVersionOf", + class: "property", + children: [ + { + identifier: "isVersionOfUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + source: { + identifier: "source", + $formkit: "repeatable", + name: "dct:source", + children: [ + { + $formkit: "group", + identifier: "source", + name: "dct:source", + class: "property", + children: [ + { + name: "@id", + identifier: "sourceUrl", + $formkit: "url", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + identifier: { + identifier: "identifier", + $formkit: "repeatable", + name: "dct:identifier", + children: [ + { + $formkit: "group", + class: "property", + name: "dct:identifier", + identifier: "identifier", + children: [ + { + identifier: "identifier", + name: "@value", + $formkit: "text", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + isReferencedBy: { + identifier: "isReferencedBy", + $formkit: "repeatable", + name: "dct:isReferencedBy", + children: [ + { + $formkit: "group", + identifier: "isReferencedBy", + name: "dct:isReferencedBy", + class: "property", + children: [ + { + identifier: "isReferencedByUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + landingPage: { + identifier: "landingPage", + $formkit: "repeatable", + name: "dcat:landingPage", + children: [ + { + $formkit: "group", + identifier: "landingPage", + name: "dcat:landingPage", + class: "property", + children: [ + { + identifier: "landingPageUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + language: { + identifier: "language", + $formkit: "auto", + name: "dct:language", + class: "property", + multiple: true, + voc: "language", + }, + admsIdentifier: { + identifier: "admsIdentifier", + $formkit: "repeatable", + name: "adms:identifier", + children: [ + { + $formkit: "group", + name: "adms:identifier", + identifier: "admsIdentifier", + children: [ + { + identifier: "admsIdentifierUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w97-textfield", + }, + }, + { + identifier: "admsIdentifierSkosNotation", + $formkit: "group", + name: "skos:notation", + children: [ + { + identifier: "admsIdentifierValue", + $formkit: "text", + name: "@value", + classes: { + outer: "w97-textfield", + }, + }, + { + // todo: check if this is correct + $formkit: "auto", + identifier: "admsIdentifierType", + voc: "notation-type", + name: "@type", + id: "admsIdentifierType", + classes: { + outer: "w97-textfield", + }, + }, + ], + }, + ], + }, + ], + }, + provenance: { + identifier: "provenanceGroup", + $formkit: "repeatable", + name: "dct:provenance", + children: [ + { + $formkit: "group", + identifier: "provenanceGroup", + name: "dct:provenance", + class: "property", + children: [ + { + identifier: "provenance", + $formkit: "text", + name: "rdfs:label", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + qualifiedAttribution: { + identifier: "qualifiedAttribution", + $formkit: "repeatable", + name: "prov:qualifiedAttribution", + children: [ + { + $formkit: "group", + identifier: "qualifiedAttribution", + name: "prov:qualifiedAttribution", + class: "property", + children: [ + { + identifier: "qualifiedAttributionUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + wasGeneratedBy: { + identifier: "wasGeneratedBy", + $formkit: "repeatable", + name: "prov:wasGeneratedBy", + children: [ + { + $formkit: "group", + identifier: "wasGeneratedBy", + name: "prov:wasGeneratedBy", + class: "property", + children: [ + { + identifier: "wasGeneratedByUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + qualifiedRelation: { + identifier: "qualifiedRelation", + $formkit: "repeatable", + name: "dcat:qualifiedRelation", + children: [ + { + $formkit: "group", + identifier: "qualifiedRelation", + name: "dcat:qualifiedRelation", + class: "property", + children: [ + { + identifier: "qualifiedRelationUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + relation: { + identifier: "relation", + $formkit: "repeatable", + name: "dct:relation", + children: [ + { + $formkit: "group", + identifier: "relation", + name: "dct:relation", + class: "property", + children: [ + { + identifier: "relationUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + issued: { + identifier: "issued", + $formkit: "formkitGroup", + name: "dct:issued", + children: [ + { + identifier: "issued", + id: "issuedCondDataset", + classes: { + outer: "w-100", + }, + $formkit: "select", + name: "@type", + options: { date: "Date", datetime: "Datetime" }, + }, + { + identifier: "issued", + $cmp: "FormKit", + if: "$get(issuedCondDataset).value", + props: { + if: "$get(issuedCondDataset).value === date", + then: { + type: "date", + name: "@value", + // validation: 'optional|date_after:' + new Date(new Date().getTime() - (24 * 60 * 60 * 1000)), + // 'validation-visibility': 'live', + classes: { + outer: "w-100", + }, + }, + else: { + type: "datetime-local", + name: "@value", + // validation: 'optional|date_after:' + new Date(new Date().getTime() - (24 * 60 * 60 * 1000)), + // 'validation-visibility': 'live', + classes: { + outer: "w-100", + }, + }, + }, + }, + ], + }, + modified: { + identifier: "modified", + $formkit: "formkitGroup", + name: "dct:modified", + children: [ + { + identifier: "modified", + id: "modifiedCondDataset", + name: "@type", + classes: { + outer: "w-100", + }, + $formkit: "select", + options: { date: "Date", datetime: "Datetime" }, + }, + { + identifier: "modified", + $cmp: "FormKit", + if: "$get(modifiedCondDataset).value", + props: { + name: "dct:modified", + if: "$get(modifiedCondDataset).value === date", + then: { + type: "date", + name: "@value", + classes: { + outer: "w-100", + }, + // validation: 'optional|date_after:' + new Date(new Date().getTime() - (24 * 60 * 60 * 1000)), + // 'validation-visibility': 'live', + }, + else: { + type: "datetime-local", + name: "@value", + classes: { + outer: "w-100", + }, + // validation: 'optional|date_after:' + new Date(new Date().getTime() - (24 * 60 * 60 * 1000)), + // 'validation-visibility': 'live', + }, + }, + }, + ], + }, + spatialResolutionInMeters: { + identifier: "spatialResolutionInMeters", + $formkit: "simpleInput", + name: "dcat:spatialResolutionInMeters", + class: "property", + validationType: "number", + classes: { outer: "formkitProperty formkitCmpWrap mx-0 my-3 p-3" }, + }, + temporalResolution: { + identifier: "temporalResolution", + $formkit: "formkitGroup", + name: "dcat:temporalResolution", + class: "property tempResWrapper", + children: [ + { + identifier: "temporalResolutionYear", + $formkit: "number", + validation: "min:1950|max:2100|optional", + name: "Year", + }, + { + identifier: "temporalResolutionMonth", + $formkit: "number", + validation: "min:0|max:12|optional", + "validation-behavior": "live", + name: "Month", + }, + { + identifier: "temporalResolutionDay", + $formkit: "number", + validation: "min:0|max:31|optional", + "validation-behavior": "live", + name: "Day", + }, + { + identifier: "temporalResolutionHour", + $formkit: "number", + validation: "min:0|max:23|optional", + name: "Hour", + }, + { + identifier: "temporalResolutionMinute", + $formkit: "number", + validation: "min:0|max:59|optional", + name: "Minute", + }, + { + identifier: "temporalResolutionSecond", + $formkit: "number", + validation: "min:0|max:59|optional", + name: "Second", + }, + ], + }, + type: { + identifier: "type", + $formkit: "auto", + voc: "dataset-type", + name: "dct:type", + class: "property", + }, + versionInfo: { + identifier: "versionInfo", + $formkit: "simpleInput", + name: "owl:versionInfo", + class: "property", + classes: { outer: "formkitProperty formkitCmpWrap mx-0 my-3 p-3" }, + }, + versionNotes: { + identifier: "versionNotes", + $formkit: "repeatable", + name: "adms:versionNotes", + children: [ + { + identifier: "versionNotes", + $formkit: "group", + name: "adms:versionNotes", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + name: "@language", + options: language, + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "versionNotes", + $formkit: "textarea", + name: "@value", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + catalog: { + identifier: "catalog", + $formkit: "simpleSelect", + // Intentionally not setting something like dcat:catalog here + // Because we don't neeed catalog to be part of the payload + name: "catalog", + id: "catalog", + class: "property mandatory", + options: {}, + classes: { outer: "formkitProperty formkitCmpWrap mx-0 my-3 p-3" }, + }, + isUsedBy: { + identifier: "isUsedBy", + $formkit: "repeatable", + name: "dext:metadataExtension", + children: [ + { + $formkit: "group", + identifier: "isUsedBy", + name: "dext:metadataExtension", + children: [ + { + $formkit: "url", + identifier: "isUsedBy", + validation: "optional|url", + name: "dext:isUsedBy", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + }, + distributions: { + isWidget: { + identifier: "widget", + name: "pv:DistributionType", + $formkit: "widget", + class: "property inDistribution", + }, + licenseAttributionByText: { + identifier: "licenseAttributionByText", + name: "dcatde:licenseAttributionByText", + $formkit: "repeatable", + class: "property inDistribution", + children: [ + { + identifier: "licenseAttributionByText", + $formkit: "group", + name: "dcatde:licenseAttributionByText", + class: "property langStringInput", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "licenseAttributionByTextTitle", + $formkit: "text", + name: "@value", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + accessURL: { + identifier: "accessUrl", + $formkit: "repeatable", + name: "dcat:accessURL", + class: "property inDistribution", + children: [ + { + identifier: "accessUrl", + name: "dcat:accessURL", + $formkit: "fileupload", + }, + ], + }, + availability: { + identifier: "availability", + $formkit: "auto", + voc: "planned-availability", + name: "dcatap:availability", + class: "property inDistribution", + }, + description: { + identifier: "description", + $formkit: "repeatable", + name: "dct:description", + class: "property langDescriptionInput mandatory inDistribution", + minimum: 1, + children: [ + { + identifier: "description", + $formkit: "group", + name: "dct:description", + mandatory: true, + minimum: 1, + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "description", + $formkit: "textarea", + name: "@value", + classes: { + outer: "w75-descField", + }, + }, + ], + }, + ], + }, + format: { + identifier: "format", + $formkit: "auto", + class: "property inDistribution", + voc: "file-type", + name: "dct:format", + classes: { + outer: "w88-textfield", + }, + }, + licence: { + $formkit: "simpleConditional", + name: "dct:license", + identifier: "licence", + // 👇 Intentionally set to 'licenses' instead of 'licence'. License submission and fetching will break otherwise for dcat-ap.de + voc: "licenses", + class: "property inDistribution", + options: { + text: "dct:title", + textarea: "skos:prefLabel", + url: "skos:exactMatch", + }, + selection: { 1: "vocabulary", 2: "manually" }, + }, + title: { + identifier: "title", + $formkit: "repeatable", + name: "dct:title", + class: "property langStringInput mandatory inDistribution", + minimum: 1, + children: [ + { + identifier: "title", + $formkit: "group", + name: "dct:title", + mandatory: true, + minimum: 1, + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "title", + $formkit: "text", + name: "@value", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + mediaType: { + identifier: "mediaType", + $formkit: "auto", + voc: "iana-media-types", + name: "dcat:mediaType", + class: "property inDistribution", + }, + downloadUrl: { + $formkit: "repeatable", + identifier: "downloadUrl", + name: "dcat:downloadURL", + class: "property inDistribution", + children: [ + { + $formkit: "group", + identifier: "relation", + name: "dct:relation", + class: "property", + children: [ + { + identifier: "downloadUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + availabilityDisDE: { + identifier: "availabilityDisDE", + $formkit: "auto", + name: "dcatap:availability", + class: "property inDistribution", + voc: "planned-availability", + }, + accessService: { + identifier: "accessService", + $formkit: "repeatable", + name: "dcat:accessService", + class: "property inDistribution", + children: [ + { + identifier: "accessService", + $formkit: "group", + name: "dcat:accessService", + children: [ + { + identifier: "accessServiceEndpointURL", + $formkit: "url", + name: "dcat:endpointURL", + class: "property ", + validation: "optional|url", + }, + { + identifier: "accessServiceTitle", + $formkit: "repeatable", + name: "dct:title", + class: "property langStringInput inDistribution", + children: [ + { + identifier: "accessServiceTitle", + $formkit: "group", + name: "dct:title", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + name: "@language", + class: "selectLangField", + options: language, + }, + { + identifier: "title", + $formkit: "text", + name: "@value", + class: "w-100 inputTextfield", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + { + identifier: "accessServiceDescription", + $formkit: "repeatable", + name: "dct:description", + class: "property langDescriptionInput inDistribution", + children: [ + { + identifier: "accessServiceDescription", + $formkit: "group", + name: "dct:description", + children: [ + { + identifier: "descriptionLanguage", + value: "de", + $formkit: "select", + name: "@language", + class: "selectLangField", + options: language, + }, + { + identifier: "description", + $formkit: "textarea", + name: "@value", + class: "inputTextfield", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + ], + }, + ], + }, + byteSize: { + identifier: "byteSize", + $formkit: "simpleInput", + validationType: "number", + name: "dcat:byteSize", + class: "property inDistribution", + classes: { outer: "formkitProperty formkitCmpWrap mx-0 my-3 p-3" }, + }, + checksum: { + $formkit: "formkitGroup", + identifier: "checksum", + name: "spdx:checksum", + class: "property inDistribution", + children: [ + { + identifier: "checksum", + $formkit: "text", + name: "spdx:checksumValue", + classes: { outer: "formkitProperty formkitCmpWrap mx-0 my-3 p-3" }, + }, + { + identifier: "checksumAlgorithm", + $formkit: "auto", + voc: "spdx-checksum-algorithm", + name: "spdx:algorithm", + }, + ], + }, + compressFormat: { + identifier: "compressFormat", + $formkit: "auto", + voc: "iana-media-types", + name: "dcat:compressFormat", + class: "property inDistribution", + }, + packageFormat: { + identifier: "packageFormat", + $formkit: "auto", + voc: "iana-media-types", + name: "dcat:packageFormat", + class: "property inDistribution", + }, + page: { + identifier: "page", + $formkit: "repeatable", + name: "foaf:page", + class: "property inDistribution", + children: [ + { + identifier: "page", + $formkit: "group", + name: "foaf:page", + children: [ + { + identifier: "pageTitle", + $formkit: "repeatable", + name: "dct:title", + children: [ + { + identifier: "pageTitle", + $formkit: "group", + name: "dct:title", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "pageTitle", + $formkit: "text", + name: "@value", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + { + identifier: "pageDescription", + $formkit: "repeatable", + name: "dct:description", + children: [ + { + identifier: "pageDescription", + $formkit: "group", + name: "dct:description", + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "pageDescription", + $formkit: "textarea", + name: "@value", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + { + $formkit: "auto", + identifier: "pageFormat", + voc: "file-type", + name: "dct:format", + class: "property inDistribution", + classes: { + outer: "w88-textfield", + }, + }, + { + identifier: "pageUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + class: "property inDistribution", + }, + ], + }, + ], + }, + language: { + identifier: "language", + $formkit: "auto", + multiple: true, + name: "dct:language", + voc: "language", + class: "property inDistribution", + }, + conformsTo: { + identifier: "conformsTo", + $formkit: "repeatable", + name: "dct:conformsTo", + class: "property inDistribution", + children: [ + { + identifier: "conformsTo", + $formkit: "group", + name: "dct:conformsTo", + children: [ + { + identifier: "conformsToTitle", + $formkit: "text", + name: "rdfs:label", + classes: { + outer: "w100-textfield", + }, + }, + { + identifier: "conformsToUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + issued: { + identifier: "issued", + $formkit: "formkitGroup", + name: "dct:issued", + class: "property inDistribution", + children: [ + { + identifier: "issued", + $cmp: "FormKit", + + props: { + type: "datetime-local", + name: "@value", + // validation: 'optional|date_after:' + new Date(new Date().getTime() - (24 * 60 * 60 * 1000)), + // 'validation-visibility': 'live', + classes: { + outer: "w-100", + }, + }, + }, + ], + }, + modified: { + identifier: "modified", + $formkit: "formkitGroup", + name: "dct:modified", + class: "property inDistribution", + children: [ + { + identifier: "modified", + $cmp: "FormKit", + props: { + type: "datetime-local", + name: "@value", + classes: { + outer: "w-100", + }, + // validation: 'optional|date_after:' + new Date(new Date().getTime() - (24 * 60 * 60 * 1000)), + }, + }, + ], + }, + rights: { + identifier: "rights", + id: "rightsCondDataset", + $formkit: "simpleConditional", + name: "dct:rights", + class: "property inDistribution", + options: { url: "rdfs:label", text: "rdfs:label" }, + selection: { 1: "URL", 2: "Text" }, + }, + spatialResolutionInMeters: { + identifier: "spatialResolutionInMeters", + $formkit: "simpleInput", + name: "dcat:spatialResolutionInMeters", + class: "property inDistribution", + validationType: "number", + }, + temporalResolution: { + identifier: "temporalResolution", + $formkit: "formkitGroup", + name: "dcat:temporalResolution", + class: "property inDistribution tempResWrapper", + children: [ + { + identifier: "temporalResolutionYear", + $formkit: "number", + min: 0, + max: 2023, + name: "Year", + }, + { + identifier: "temporalResolutionMonth", + $formkit: "number", + min: 0, + max: 12, + name: "Month", + }, + { + identifier: "temporalResolutionDay", + $formkit: "number", + min: 0, + max: 31, + name: "Day", + }, + { + identifier: "temporalResolutionHour", + $formkit: "number", + min: 0, + max: 23, + name: "Hour", + }, + { + identifier: "temporalResolutionMinute", + $formkit: "number", + min: 0, + max: 59, + name: "Minute", + }, + { + identifier: "temporalResolutionSecond", + $formkit: "number", + min: 0, + max: 59, + name: "Second", + }, + ], + }, + type: { + identifier: "type", + $formkit: "auto", + voc: "distribution-type", + name: "dct:type", + class: "property inDistribution", + }, + status: { + identifier: "status", + $formkit: "auto", + voc: "dataset-status", + name: "adms:status", + class: "property inDistribution", + }, + hasPolicy: { + identifier: "hasPolicy", + $formkit: "repeatable", + name: "odrl:hasPolicy", + class: "property inDistribution", + children: [ + { + identifier: "hasPolicy", + $formkit: "group", + name: "odrl:hasPolicy", + children: [ + { + identifier: "hasPolicyUrl", + $formkit: "url", + name: "@id", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + }, + catalogues: { + availabilityCatDE: { + identifier: "availabilityCatDE", + $formkit: "auto", + name: "dcatap:availability", + class: "property inDistribution", + voc: "planned-availability", + }, + overview: { + $cmp: "OverviewPage", + props: { + property: "datasets", + }, + }, + datasetID: { + $formkit: "id", + identifier: "datasetID", + name: "datasetID", + mandatory: true, + }, + title: { + identifier: "title", + $formkit: "repeatable", + name: "dct:title", + children: [ + { + identifier: "title", + $formkit: "group", + name: "dct:title", + mandatory: true, + minimum: 1, + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + validation: "required", + options: language, + name: "@language", + classes: { + outer: "w25-textfield", + }, + }, + { + identifier: "title", + $formkit: "text", + name: "@value", + validation: "required", + mandatory: true, + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + description: { + identifier: "description", + $formkit: "repeatable", + name: "dct:description", + children: [ + { + identifier: "description", + $formkit: "group", + name: "dct:description", + mandatory: true, + minimum: 1, + children: [ + { + identifier: "language", + value: "de", + $formkit: "select", + options: language, + validation: "required", + name: "@language", + classes: { + outer: "w25-descField", + }, + }, + { + identifier: "description", + $formkit: "textarea", + name: "@value", + validation: "required", + classes: { + outer: "w75-textfield", + }, + }, + ], + }, + ], + }, + publisher: { + $formkit: "simpleConditional", + identifier: "publisher", + name: "dct:publisher", + voc: "corporate-body", + options: { text: "foaf:name", email: "foaf:mbox", url: "foaf:homepage" }, + selection: { 1: "vocabulary", 2: "manually" }, + }, + language: { + identifier: "language", + $formkit: "auto", + class: "", + multiple: true, + name: "dct:language", + voc: "language", + id: "language", + }, + licence: { + $formkit: "simpleConditional", + name: "dct:license", + identifier: "licence", + voc: "licence", + class: "", + options: { + text: "dct:title", + textarea: "skos:prefLabel", + url: "skos:exactMatch", + }, + selection: { 1: "vocabulary", 2: "manually" }, + }, + spatial: { + identifier: "spatial", + $formkit: "repeatable", + name: "dct:spatial", + class: "", + children: [ + { + $formkit: "spatialinput", + name: "dct:spatial", + identifier: "spatial", + }, + ], + }, + homepage: { + identifier: "homepage", + $formkit: "simpleInput", + name: "foaf:homepage", + validationType: "url", + class: "", + }, + hasPart: { + identifier: "hasPart", + $formkit: "repeatable", + name: "dct:hasPart", + class: "", + children: [ + { + $formkit: "group", + identifier: "hasPart", + name: "dct:hasPart", + children: [ + { + identifier: "hasPartURL", + $formkit: "url", + name: "@id", + validationType: "url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + isPartOf: { + identifier: "isPartOf", + $formkit: "simpleInput", + name: "dct:isPartOf", + validationType: "url", + class: "", + }, + rights: { + identifier: "rights", + $formkit: "simpleConditional", + name: "dct:rights", + class: "", + options: { url: "rdfs:label", text: "rdfs:label" }, + selection: { 1: "URL", 2: "text" }, + // children: [ + // { + // identifier: 'rights', + // $formkit: "select", + // name: '@type', + // options: { url: 'Provide an URL', str: 'String' }, + // id: "rightsModeCatalogue" + // }, + // { + // identifier: 'rights', + // $cmp: "FormKit", + // if: "$get(rightsModeCatalogue).value", + // props: { + // if: "$get(rightsModeCatalogue).value === url", + // then: { + // identifier: 'rightsUrl', + // type: "url", + // label: "URL", + // name: 'rdfs:label', + // }, + // else: { + // type: "text", + // name: 'rdfs:label', + // } + // } + // } + // ] + }, + catalog: { + identifier: "catalog", + $formkit: "repeatable", + name: "dcat:catalog", + class: "inDistribution", + children: [ + { + $formkit: "group", + identifier: "catalog", + name: "dcat:catalog", + children: [ + { + identifier: "catalogURL", + $formkit: "url", + validationType: "url", + name: "@id", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + ], + }, + creator: { + identifier: "creator", + $formkit: "formkitGroup", + name: "dct:creator", + class: "inDistribution", + children: [ + { + identifier: "creatorType", + $formkit: "select", + name: "rdf:type", + options: { + "": "---", + "foaf:Person": "Person", + "foaf:Organization": "Organization", + }, + classes: { + outer: "w100-textfield", + }, + }, + { + identifier: "creatorName", + $formkit: "text", + name: "foaf:name", + classes: { + outer: "w100-textfield", + }, + }, + { + identifier: "creatorEmail", + $formkit: "email", + name: "foaf:mbox", + validation: "optional|email", + classes: { + outer: "w100-textfield", + }, + }, + { + identifier: "creatorHomepage", + $formkit: "url", + name: "foaf:homepage", + validation: "optional|url", + classes: { + outer: "w100-textfield", + }, + }, + ], + }, + }, +}; + +// Dynamically add a collapsed property to all fields that are component of +// a set of specific pages steps. +// ['datasets', 'distributions'].forEach((type) => { +// [].concat( +// // advised and additional fields for datasets/distributions +// Object.keys(config?.[type].step2), +// Object.keys(config?.[type].step3), +// ).forEach((key) => { +// dcatapProperties[type][key].collapsed = true; +// }); +// }) + +export default dcatapProperties; diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/page-content-config.js b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/page-content-config.js new file mode 100644 index 0000000000000000000000000000000000000000..765ca58e0e02a362dfce05fd5ca866d0ed5d15b0 --- /dev/null +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/page-content-config.js @@ -0,0 +1,23 @@ +const config = { + datasets: { + Mandatory: [ 'title', 'datasetID', 'description', 'catalog', 'publisher', 'theme', 'issued', 'modified' ], + Advised: [ 'politicalGeocodingLevelURI', 'politicalGeocodingURI', 'availabilityDE', 'contributorID', 'geocodingDescription', 'legalBasis', 'qualityProcessURI', 'references', 'contributor', 'originator', 'maintainer', 'keyword', 'contactPoint', 'landingPage', 'accrualPeriodicity', 'language', 'spatial', 'temporal', 'creator', 'identifier', 'admsIdentifier', 'page', 'accessRights' ], + Recommended: [ 'type', 'isUsedBy', 'conformsTo', 'versionInfo', 'versionNotes', 'temporalResolution', 'spatialResolutionInMeters', 'relation', 'qualifiedRelation', 'isReferencedBy', 'hasVersion', 'isVersionOf', 'source', 'provenance', 'qualifiedAttribution', 'wasGeneratedBy' ], + Distributions: [], + Overview: [ 'overview' ] + }, + distributions: { + Mandatory: [ 'isWidget','accessURL', 'title', 'description' ], + Advised: [ 'licence', 'licenseAttributionByText', 'downloadUrl', 'format', 'mediaType', 'status', 'availability', 'issued', 'modified' ], + Recommended: [ 'type', 'byteSize', 'checksum', 'compressFormat', 'packageFormat', 'language', 'page', 'conformsTo', 'rights', 'hasPolicy', 'temporalResolution', 'spatialResolutionInMeters' ], + DataService: [ 'accessService' ] + }, + catalogues: { + Mandatory: [ 'title', 'availabilityCatDE', 'datasetID', 'description', 'publisher', 'language', 'homepage', 'licence' ], + Advised: [ 'spatial', 'hasPart', 'isPartOf', 'rights', 'catalog', 'creator' ], + Overview: ['overview'] + } + }; + + export default config; + \ No newline at end of file diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/prefixes.js b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/prefixes.js new file mode 100644 index 0000000000000000000000000000000000000000..007e1aa75e5b8c845f8d0d27a2869f46a7bf3470 --- /dev/null +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/prefixes.js @@ -0,0 +1,26 @@ +const prefixes = { + adms: "http://www.w3.org/ns/adms#", + dcat: "http://www.w3.org/ns/dcat#", + dcatap: "http://data.europa.eu/r5r/", + dct: "http://purl.org/dc/terms/", + foaf: "http://xmlns.com/foaf/0.1/", + locn: "http://www.w3.org/ns/locn#", + owl: "http://www.w3.org/2002/07/owl#", + odrl: "http://www.w3.org/ns/odrl/2/", + prov: "http://www.w3.org/ns/prov#", + rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + rdfs: "http://www.w3.org/2000/01/rdf-schema#", + schema: "http://schema.org/", + skos: "http://www.w3.org/2004/02/skos/core#", + spdx: "http://spdx.org/rdf/terms#", + xsd: "http://www.w3.org/2001/XMLSchema#", + vann: "http://purl.org/vocab/vann/", + voaf: "http://purl.org/vocommons/voaf#", + vcard: "http://www.w3.org/2006/vcard/ns#", + time: "http://www.w3.org/2006/time#", + dext: "https://data.europa.eu/ns/ext#", + dcatde: "http://dcat-ap.de/def/dcatde/", + pv: "https://piveau.eu/ns/voc#", +}; + +export default prefixes; diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/vocab-prefixes.js b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/vocab-prefixes.js new file mode 100644 index 0000000000000000000000000000000000000000..614a35d52ca0edbfe5fa378020c633a897b313cf --- /dev/null +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dcatapde_BFS/vocab-prefixes.js @@ -0,0 +1,32 @@ +const vocabPrefixesDCATAPDE = { + "licence": "http://dcat-ap.de/def/licenses/", + "contributors": "http://dcat-ap.de/def/contributors/", + "dataset-type": "http://publications.europa.eu/resource/authority/dataset-type/", + "dataset-types": "http://dcat-ap.de/def/datasetTypes/", + "hash-algorithm": "http://dcat-ap.de/def/hashAlgorithms/", + "planned-availability": "http://dcat-ap.de/def/plannedAvailability/", + "political-geocoding-level": "http://dcat-ap.de/def/politicalGeocoding/Level/", + "political-geocoding-district-key": "http://dcat-ap.de/def/politicalGeocoding/districtKey/", + "political-geocoding-government-district-key": "http://dcat-ap.de/def/politicalGeocoding/governmentDistrictKey/", + "political-geocoding-municipal-association-key": "http://dcat-ap.de/def/politicalGeocoding/municipalAssociationKey/", + "political-geocoding-municipality-key": "http://dcat-ap.de/def/politicalGeocoding/municipalityKey/", + "political-geocoding-regional-key": "http://dcat-ap.de/def/politicalGeocoding/regionalKey/", + "political-geocoding-state-key": "http://dcat-ap.de/def/politicalGeocoding/", + "eurovoc": "http://eurovoc.europa.eu/", + "corporate-body": "http://publications.europa.eu/resource/authority/corporate-body/", + "continent": "http://publications.europa.eu/resource/authority/continent/", + "country": "http://publications.europa.eu/resource/authority/country/", + "place": "http://publications.europa.eu/resource/authority/place/", + "data-theme": "http://publications.europa.eu/resource/authority/data-theme/", + "access-right": "http://publications.europa.eu/resource/authority/access-right/", + "file-type": "http://publications.europa.eu/resource/authority/file-type/", + "frequency": "http://publications.europa.eu/resource/authority/frequency/", + "language": "http://publications.europa.eu/resource/authority/language/", + "notation-type": "http://publications.europa.eu/resource/authority/notation-type/", + "iana-media-types": "https://www.iana.org/assignments/media-types/", + "spdx-checksum-algorithm": "http://spdx.org/rdf/terms#", + "distribution-type": "http://publications.europa.eu/resource/authority/distribution-type/", + "dataset-status": "http://publications.europa.eu/resource/authority/dataset-status/", +}; + +export default vocabPrefixesDCATAPDE; diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dpi-spec-config.js b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dpi-spec-config.js index 87fae73a97317fed8fe8657379711ce1d1436a85..343bde6c18e31229771e6a68740b5906638520ae 100644 --- a/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dpi-spec-config.js +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/config/dpi-spec-config.js @@ -15,6 +15,13 @@ import prefixesDCATAPDE from './dcatapde/prefixes'; import formatTypesDCATAPDE from './dcatapde/format-types'; import vocabPrefixesDCATAPDE from './dcatapde/vocab-prefixes'; +// import DCAT-AP.de for BFS (with widgets) +import pageContentDCATAPDE_BFS from './dcatapde_BFS/page-content-config'; +import inputDefinitionDCATAPDE_BFS from './dcatapde_BFS/input-definition'; +import prefixesDCATAPDE_BFS from './dcatapde_BFS/prefixes'; +import formatTypesDCATAPDE_BFS from './dcatapde_BFS/format-types'; +import vocabPrefixesDCATAPDE_BFS from './dcatapde_BFS/vocab-prefixes'; + // import DCAT-AP.de for ODB import pageContentDCATAPDEODB from './dcatapdeODB/page-content-config'; import inputDefinitionDCATAPDEODB from './dcatapdeODB/input-definition'; @@ -38,6 +45,13 @@ export const config = { prefixes: prefixesDCATAPDE, vocabPrefixes: vocabPrefixesDCATAPDE, }, + dcatapde_BFS: { + pageConent: pageContentDCATAPDE_BFS, + inputDefinition: inputDefinitionDCATAPDE_BFS, + formatTypes: formatTypesDCATAPDE_BFS, + prefixes: prefixesDCATAPDE_BFS, + vocabPrefixes: vocabPrefixesDCATAPDE_BFS, + }, dcatapdeODB: { pageConent: pageContentDCATAPDEODB, inputDefinition: inputDefinitionDCATAPDEODB, diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/store/modules/conversionStore.ts b/packages/piveau-hub-ui-modules/lib/data-provider-interface/store/modules/conversionStore.ts index 726471f40425f406a68f2af9fa00d12985af75fa..0ac48f9d032cf4dc6b59238555b7ecbb1eca2379 100644 --- a/packages/piveau-hub-ui-modules/lib/data-provider-interface/store/modules/conversionStore.ts +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/store/modules/conversionStore.ts @@ -106,6 +106,8 @@ const actions = { catalogues: generalHelper.mergeNestedObjects(state.catalogues) }; + // console.log(data); + // merging each distribution object within the overall array of distributions if (has(state.datasets, 'Distributions') && has(state.datasets.Distributions, 'distributionList') && !isEmpty(state.datasets.Distributions.distributionList)) { for (let index = 0; index < state.datasets.Distributions.distributionList.length; index++) { diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/RDFconverter.js b/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/RDFconverter.js index 8315b505402f9fbd94c971421b36a81cc1511af5..8ad6fa924b3e9626dbc730cfcb6177f5060ed34e 100644 --- a/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/RDFconverter.js +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/RDFconverter.js @@ -1,42 +1,62 @@ -import N3 from 'n3'; -import { isEmpty } from 'lodash'; -import { has } from 'lodash'; +import N3 from "n3"; +import { isEmpty } from "lodash"; +import { has } from "lodash"; -import generalDpiConfig from '../config/dpi-spec-config'; +import generalDpiConfig from "../config/dpi-spec-config"; -import generalHelper from './general-helper'; +import generalHelper from "./general-helper"; /** * Converts all properties for given data from form input data into RDF (N-Triples) - * @param {Object} data Data given within an object. Data stored as follows { datasets: {...}, distributions: [{...},...], catalogues: {...}} + * @param {Object} data Data given within an object. Data stored as follows { datasets: {...}, distributions: [{...},...], catalogues: {...}} * @param {String} property Name of property which should be converted (either 'datasets' or 'catalogues') * @returns String of converted data in RDF format (N-Triples) */ -function convertToRDF(data, property, specification) { - - let finishedRDFdata; - - let dpiConfig = specification; - - // writer for adding data as quads - const RDFdata = new N3.Writer({ prefixes: dpiConfig.prefixes, format: 'N-Triples' }); - // datasetURI also needed for distribution creation (add distributionURI to dataset (dcat:distribution)) - const datasetURI = `https://piveau.eu/set/data/${data.datasets.datasetID}`; - - // convert values for datasets/catalogues - convertPropertyValues(RDFdata, data[property], property, '', '', true, datasetURI, dpiConfig); // datasets and catalogues - - // include distribution data into same graph - // differentiation neccessary because datasets also include distributions - if (property === 'datasets') { - // multiple distributions possible -> [{data of distribution 1}, {data of distribution 2}, ...] - for (let index = 0; index < data.distributions.length; index += 1) { - convertPropertyValues(RDFdata, data.distributions[index], 'distributions', '', '', true, datasetURI, dpiConfig); - } +function convertToRDF(data, property, specification) { + let finishedRDFdata; + + let dpiConfig = specification; + + // writer for adding data as quads + const RDFdata = new N3.Writer({ + prefixes: dpiConfig.prefixes, + format: "N-Triples", + }); + // datasetURI also needed for distribution creation (add distributionURI to dataset (dcat:distribution)) + const datasetURI = `https://piveau.eu/set/data/${data.datasets.datasetID}`; + + // convert values for datasets/catalogues + convertPropertyValues( + RDFdata, + data[property], + property, + "", + "", + true, + datasetURI, + dpiConfig + ); // datasets and catalogues + + // include distribution data into same graph + // differentiation neccessary because datasets also include distributions + if (property === "datasets") { + // multiple distributions possible -> [{data of distribution 1}, {data of distribution 2}, ...] + for (let index = 0; index < data.distributions.length; index += 1) { + convertPropertyValues( + RDFdata, + data.distributions[index], + "distributions", + "", + "", + true, + datasetURI, + dpiConfig + ); } + } - RDFdata.end((error, result) => finishedRDFdata = result); - return finishedRDFdata; + RDFdata.end((error, result) => (finishedRDFdata = result)); + return finishedRDFdata; } /** @@ -47,322 +67,464 @@ function convertToRDF(data, property, specification) { * @param {DataFactory} preMainURI (can be undefined) Could be a namedNode or BlankNode containing an URI * @param {DataFactory} preMainType (can be undefined) NamedNode determining the type of the current property (e.g. dcat:Dataset (as object)) * @param {Boolean} setMain Value determining if additional values should be set (type, id, sample...) - * @param {String} datasetURI URI of dataset for use in distribution conversion + * @param {String} datasetURI URI of dataset for use in distribution conversion */ -function convertPropertyValues(RDFdataset, data, property, preMainURI, preMainType, setMain, datasetURI, dpiConfig) { - - const formatTypes = dpiConfig.formatTypes; - - // method can be called recursively for nested properties - // need to access id of parent node for later use as subject -> provide via method parameters (preMainURI & preMainType) - let mainURI; - let mainType; - - // parent method can be called recursively for nested values - // if called on non-nested values a overall id and type muste be set (setMain -> true) - if (setMain) { - if (property === 'datasets') { - mainType = generalHelper.addNamespace('dcat:Dataset', dpiConfig); - mainURI = N3.DataFactory.namedNode(datasetURI); // datasetID should never be empty because of frontend checking - } else if (property === 'catalogues') { - mainType = generalHelper.addNamespace('dcat:Catalog', dpiConfig); - mainURI = N3.DataFactory.namedNode(`https://piveau.eu/set/data/${data.datasetID}`); // datasetID should never be empty because of frontend checking - } else { - mainType = generalHelper.addNamespace('dcat:Distribution', dpiConfig); - const randomId = generalHelper.makeId(10); - // distribution id can be random, will be overwritten by backend on saving data - mainURI = N3.DataFactory.namedNode(`https://piveau.eu/set/data/${randomId}`); - } - - // parent method can be called recursively to convert nested values - // but setting the overal type and id of a dataset/catalogue is only required once at the beginning - // -> only set additional properties when setMain === true - setAdditionalProperties(RDFdataset, data, mainURI, mainType, property, datasetURI, dpiConfig); +function convertPropertyValues( + RDFdataset, + data, + property, + preMainURI, + preMainType, + setMain, + datasetURI, + dpiConfig +) { + + console.log( RDFdataset, + data, + property, + preMainURI, + preMainType, + setMain, + datasetURI, + dpiConfig); + + const formatTypes = dpiConfig.formatTypes; + + // method can be called recursively for nested properties + // need to access id of parent node for later use as subject -> provide via method parameters (preMainURI & preMainType) + let mainURI; + let mainType; + + // parent method can be called recursively for nested values + // if called on non-nested values a overall id and type muste be set (setMain -> true) + if (setMain) { + if (property === "datasets") { + mainType = generalHelper.addNamespace("dcat:Dataset", dpiConfig); + mainURI = N3.DataFactory.namedNode(datasetURI); // datasetID should never be empty because of frontend checking + } else if (property === "catalogues") { + mainType = generalHelper.addNamespace("dcat:Catalog", dpiConfig); + mainURI = N3.DataFactory.namedNode( + `https://piveau.eu/set/data/${data.datasetID}` + ); // datasetID should never be empty because of frontend checking } else { - // called on nested properties with already given URI and type which should used in the following conversion process - mainURI = preMainURI; - mainType = preMainType; + mainType = generalHelper.addNamespace("dcat:Distribution", dpiConfig); + const randomId = generalHelper.makeId(10); + // distribution id can be random, will be overwritten by backend on saving data + mainURI = N3.DataFactory.namedNode( + `https://piveau.eu/set/data/${randomId}` + ); } - // distributions may have download URLs, if no downloadURL is provided -> provided accessUrls will be also set as downloadUrls - // accessUrl is a required property and therefore always provided (made sure by the frontend) - // const downloadUrlsProvided = has(data, 'dcat:downloadURL') && !isEmpty(data['dcat:downloadURL']) && data['dcat:downloadURL'].map(el => !isEmpty(el['@id'])).reduce((a, b) => b); - - // loop trough all keys within data object and convert values (or nested values) to RDF - const valueKeys = Object.keys(data); - for (let index = 0; index < valueKeys.length; index += 1) { - const key = valueKeys[index]; // key format: either a normal name for special properties (e.g. datasetID) or namespaced keys (e.g. dct:title) - - if(generalHelper.propertyHasValue(data[key])) { - // all properties are sorted by their format (see .../data-provider-interface/config/format-types.js) - // depending on the format the corresponding conversion-method is used, writing the result to the overall RDF-writer - if (formatTypes.singularString[property].includes(key)) { - convertSingularString(RDFdataset, mainURI, data, key, dpiConfig); - } else if (formatTypes.singularURI[property].includes(key)) { - convertSingularURI(RDFdataset, mainURI, data, key, dpiConfig); - } else if (formatTypes.multipleURI[property].includes(key)) { - // if no dowloadURL is provided, set accessUrls as downloadUrls - // if (!downloadUrlsProvided && key === 'dcat:accessURL') { - // // copy accessurl array to donwloadurl array and convert data - - // data['dcat:downloadURL'] = cloneDeep(data['dcat:accessURL']); - // convertMultipleURI(RDFdataset, mainURI, data, 'dcat:downloadURL', property, dpiConfig); - // } - - convertMultipleURI(RDFdataset, mainURI, data, key, property, dpiConfig); - } else if (formatTypes.typedStrings[property].includes(key)) { - convertTypedString(RDFdataset, mainURI, data, key, dpiConfig); - } else if (formatTypes.multilingualStrings[property].includes(key)) { - convertMultilingual(RDFdataset, mainURI, data, key, dpiConfig); - } else if (formatTypes.groupedProperties[property].includes(key)) { - - // grouped properties are properties provided by the form which consist of multiple properties (e.g contactPoint) - // the properties values are stored within an object located within an array - // for repeatable properties there are multiple objects in this array, otherwise there is just one - - let actualData; - // vcard:hasAdress is an object as well as dct:creator and skos:notation - if (key === 'vcard:hasAddress' || key === 'dct:creator' || key === 'skos:notation' || key === 'spdx:checksum' ) actualData = [data[key]]; - else actualData = data[key]; - - // looping trough all existing objects within the array - for (let groupId = 0; groupId < actualData.length; groupId += 1) { - let currentGroupData = actualData[groupId]; - - if (!isEmpty(currentGroupData)) { - if (key === 'skos:notation') { - // property skos:notation work a little bit different then other properties - // the form provides a value and a type from two seperated fields ({'@value': '...', '@type': '...'}) - // the resulting RDF should merge these values into a typed literal (value^^type) - if (has(currentGroupData, '@value') && !isEmpty(currentGroupData['@value'])) { - let notationValue; - - // if a type is given, use to form typed literal - // if no type is given, only use value to create literal - if (has(currentGroupData, '@type') && !isEmpty(currentGroupData['@type'])) { // typed literal - notationValue = N3.DataFactory.literal(currentGroupData['@value'], N3.DataFactory.namedNode(currentGroupData['@type'].resource)); - } else { // literal - notationValue = N3.DataFactory.literal(currentGroupData['@value']); - } - - // add type for adms:identifier - RDFdataset.addQuad(N3.DataFactory.quad( - mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace('rdf:type', dpiConfig)), - N3.DataFactory.namedNode(generalHelper.addNamespace('adms:Identifier', dpiConfig)) - )) - - // save quadruple with typed or untyped literal - RDFdataset.addQuad(N3.DataFactory.quad( - mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - notationValue - )) - - // resulting rdf quads should look like this: - // datasetId adms:identifier admsIdentifierUtl - // admsIdentifierUrl rdf:type adms:Identifier - // admsIdentifierUrl skos:notation value^^type - } - } else { - let groupBlankNode; - - // because grouped properties have a list of nested properties we need an initial quadruple stating the parent property - // using a blank node as object which later serves as subject for the nested properties - // RDF example: - // datasetID dct:contactPoint blankNodeId - // blankNodeId foaf:mbox email@exmaple.com - // blankNodeId fn:name InsitutionName ... - - // some form fields provide an URL which should serves as namedNode for other nested values (e.g. conformsTo) - // RDF example: - // datasetID dct:conformsTo conformsToURI - // conformsToURI dct:title conformsTitle - if ((key === 'foaf:page' || key === 'adms:identifier' || key === 'dct:conformsTo') && has(currentGroupData, '@id')) { - groupBlankNode = N3.DataFactory.namedNode(currentGroupData['@id']); - } - // all properties that don't provide an URL serving as namedNode for nested values need to define a blank node - - // page gets type but also has multilingual fields with preseleted langauge - // don't create blank node if there is not data for page beside the preselected language - let emptyPage = false; - - if (key === 'foaf:page') { - - // if page has title and/or description property given, check if there are values given - const hasTitle = has(currentGroupData, 'dct:title'); - const hasDescription = has(currentGroupData, 'dct:description'); - - let hasNoValueKeysTitle = true; - let hasEmptyValueTitle = true; - let hasNoValueKeysDescription = true; - let hasEmptyValueDescription = true; - - if (hasTitle) { - hasNoValueKeysTitle = !currentGroupData['dct:title'].every(el => has(el, '@value')); - hasEmptyValueTitle = currentGroupData['dct:title'].every(el => isEmpty(el['@value'])); - } - - if (hasDescription) { - hasNoValueKeysDescription = !currentGroupData['dct:description'].every(el => has(el, '@value')); - hasEmptyValueDescription = currentGroupData['dct:description'].every(el => isEmpty(el['@value'])); - } - - // page should be handled as empty if: - // no title and/or no description given - // if properties given: no value given or value empty - if ((hasNoValueKeysTitle || hasEmptyValueTitle) && (hasNoValueKeysDescription || hasEmptyValueDescription)) emptyPage = true; - } - - if (!emptyPage) { - if (!groupBlankNode) groupBlankNode = N3.DataFactory.blankNode(''); - - // save inital quadruple using the named or blank node as object - // e.g. datasetId dct:contactPoint blankNode/namedNode - RDFdataset.addQuad(N3.DataFactory.quad( - mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - groupBlankNode - )) - - // some properties provide additional types - if (has(formatTypes.additionalPropertyTypes, key)) { - RDFdataset.addQuad(N3.DataFactory.quad( - groupBlankNode, - N3.DataFactory.namedNode(generalHelper.addNamespace('rdf:type', dpiConfig)), - N3.DataFactory.namedNode(generalHelper.addNamespace(formatTypes.additionalPropertyTypes[key], dpiConfig)) - )) - } - - // temporal values nested inside another object: "dct:temporal": [{ "dct:temporal": { "dcat:startDate": "...", "dcat:endDate": "..." } }] - if (key === 'dct:temporal') { - if (has(currentGroupData, 'dct:temporal')) { - currentGroupData = currentGroupData['dct:temporal']; - } - } - - // convert all nested values provided by form - convertPropertyValues(RDFdataset, currentGroupData, property, groupBlankNode, mainType, false, dpiConfig, dpiConfig); - } - } - } + // parent method can be called recursively to convert nested values + // but setting the overal type and id of a dataset/catalogue is only required once at the beginning + // -> only set additional properties when setMain === true + setAdditionalProperties( + RDFdataset, + data, + mainURI, + mainType, + property, + datasetURI, + dpiConfig + ); + } else { + // called on nested properties with already given URI and type which should used in the following conversion process + mainURI = preMainURI; + mainType = preMainType; + } + + // distributions may have download URLs, if no downloadURL is provided -> provided accessUrls will be also set as downloadUrls + // accessUrl is a required property and therefore always provided (made sure by the frontend) + // const downloadUrlsProvided = has(data, 'dcat:downloadURL') && !isEmpty(data['dcat:downloadURL']) && data['dcat:downloadURL'].map(el => !isEmpty(el['@id'])).reduce((a, b) => b); + + // loop trough all keys within data object and convert values (or nested values) to RDF + const valueKeys = Object.keys(data); + for (let index = 0; index < valueKeys.length; index += 1) { + const key = valueKeys[index]; // key format: either a normal name for special properties (e.g. datasetID) or namespaced keys (e.g. dct:title) + + if (generalHelper.propertyHasValue(data[key])) { + // all properties are sorted by their format (see .../data-provider-interface/config/format-types.js) + // depending on the format the corresponding conversion-method is used, writing the result to the overall RDF-writer + if (formatTypes.singularString[property].includes(key)) { + convertSingularString(RDFdataset, mainURI, data, key, dpiConfig); + } else if (formatTypes.singularURI[property].includes(key)) { + convertSingularURI(RDFdataset, mainURI, data, key, dpiConfig); + } else if (formatTypes.multipleURI[property].includes(key)) { + // if no dowloadURL is provided, set accessUrls as downloadUrls + // if (!downloadUrlsProvided && key === 'dcat:accessURL') { + // // copy accessurl array to donwloadurl array and convert data + + // data['dcat:downloadURL'] = cloneDeep(data['dcat:accessURL']); + // convertMultipleURI(RDFdataset, mainURI, data, 'dcat:downloadURL', property, dpiConfig); + // } + + convertMultipleURI(RDFdataset, mainURI, data, key, property, dpiConfig); + } else if (formatTypes.typedStrings[property].includes(key)) { + convertTypedString(RDFdataset, mainURI, data, key, dpiConfig); + } else if (formatTypes.multilingualStrings[property].includes(key)) { + convertMultilingual(RDFdataset, mainURI, data, key, dpiConfig); + } else if (formatTypes.groupedProperties[property].includes(key)) { + // grouped properties are properties provided by the form which consist of multiple properties (e.g contactPoint) + // the properties values are stored within an object located within an array + // for repeatable properties there are multiple objects in this array, otherwise there is just one + + let actualData; + // vcard:hasAdress is an object as well as dct:creator and skos:notation + if ( + key === "vcard:hasAddress" || + key === "dct:creator" || + key === "skos:notation" || + key === "spdx:checksum" + ) + actualData = [data[key]]; + else actualData = data[key]; + + // looping trough all existing objects within the array + for (let groupId = 0; groupId < actualData.length; groupId += 1) { + let currentGroupData = actualData[groupId]; + + if (!isEmpty(currentGroupData)) { + if (key === "skos:notation") { + // property skos:notation work a little bit different then other properties + // the form provides a value and a type from two seperated fields ({'@value': '...', '@type': '...'}) + // the resulting RDF should merge these values into a typed literal (value^^type) + if ( + has(currentGroupData, "@value") && + !isEmpty(currentGroupData["@value"]) + ) { + let notationValue; + + // if a type is given, use to form typed literal + // if no type is given, only use value to create literal + if ( + has(currentGroupData, "@type") && + !isEmpty(currentGroupData["@type"]) + ) { + // typed literal + notationValue = N3.DataFactory.literal( + currentGroupData["@value"], + N3.DataFactory.namedNode(currentGroupData["@type"].resource) + ); + } else { + // literal + notationValue = N3.DataFactory.literal( + currentGroupData["@value"] + ); } - } else if (formatTypes.conditionalProperties[property].includes(key)) { - // publisher either is an URI or a group with multiple values (name, homepage, email) - // license either is an URI or a group with multiple values () - if (key === 'dct:publisher' || key === 'dct:license') { - - // data contains either {resource: '...', name: '...'} or object containing other keys - if (has(data[key], 'resource')) { - convertSingularURI(RDFdataset, mainURI, data, key, dpiConfig); - } else { - const groupBlankNode = N3.DataFactory.blankNode(''); - - // some properties provide additional types - if (has(formatTypes.additionalPropertyTypes, key)) { - RDFdataset.addQuad(N3.DataFactory.quad( - groupBlankNode, - N3.DataFactory.namedNode(generalHelper.addNamespace('rdf:type', dpiConfig)), - N3.DataFactory.namedNode(generalHelper.addNamespace(formatTypes.additionalPropertyTypes[key], dpiConfig)) - )) - } - - // save inital quadruple using the named or blank node as object - // e.g. datasetId dct:contactPoint blankNode/namedNode - RDFdataset.addQuad(N3.DataFactory.quad( - mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - groupBlankNode - )) - - convertPropertyValues(RDFdataset, data[key], property, groupBlankNode, mainType, false, dpiConfig, dpiConfig); - } - + // add type for adms:identifier + RDFdataset.addQuad( + N3.DataFactory.quad( + mainURI, + N3.DataFactory.namedNode( + generalHelper.addNamespace("rdf:type", dpiConfig) + ), + N3.DataFactory.namedNode( + generalHelper.addNamespace("adms:Identifier", dpiConfig) + ) + ) + ); + + // save quadruple with typed or untyped literal + RDFdataset.addQuad( + N3.DataFactory.quad( + mainURI, + N3.DataFactory.namedNode( + generalHelper.addNamespace(key, dpiConfig) + ), + notationValue + ) + ); + + // resulting rdf quads should look like this: + // datasetId adms:identifier admsIdentifierUtl + // admsIdentifierUrl rdf:type adms:Identifier + // admsIdentifierUrl skos:notation value^^type + } + } else { + let groupBlankNode; + + // because grouped properties have a list of nested properties we need an initial quadruple stating the parent property + // using a blank node as object which later serves as subject for the nested properties + // RDF example: + // datasetID dct:contactPoint blankNodeId + // blankNodeId foaf:mbox email@exmaple.com + // blankNodeId fn:name InsitutionName ... + + // some form fields provide an URL which should serves as namedNode for other nested values (e.g. conformsTo) + // RDF example: + // datasetID dct:conformsTo conformsToURI + // conformsToURI dct:title conformsTitle + if ( + (key === "foaf:page" || + key === "adms:identifier" || + key === "dct:conformsTo") && + has(currentGroupData, "@id") + ) { + groupBlankNode = N3.DataFactory.namedNode( + currentGroupData["@id"] + ); + } + // all properties that don't provide an URL serving as namedNode for nested values need to define a blank node + + // page gets type but also has multilingual fields with preseleted langauge + // don't create blank node if there is not data for page beside the preselected language + let emptyPage = false; + + if (key === "foaf:page") { + // if page has title and/or description property given, check if there are values given + const hasTitle = has(currentGroupData, "dct:title"); + const hasDescription = has(currentGroupData, "dct:description"); + + let hasNoValueKeysTitle = true; + let hasEmptyValueTitle = true; + let hasNoValueKeysDescription = true; + let hasEmptyValueDescription = true; + + if (hasTitle) { + hasNoValueKeysTitle = !currentGroupData["dct:title"].every( + (el) => has(el, "@value") + ); + hasEmptyValueTitle = currentGroupData["dct:title"].every( + (el) => isEmpty(el["@value"]) + ); } - } else if (key === 'dcat:temporalResolution') { - // temporal resolution is displayed as group of input forms for each property (year, month, day, ...) - // the form provides the data as following: [ { 'Year': '...', 'Month': '...', ... } ] - // the final format of this property should look like this: P?Y?M?DT?H?M?S - // not all values must be filled and therefore be present -> default behavior if not given: value = 0 - - const resolutionValues = data[key]; - const valueString = `P${resolutionValues.Year ? resolutionValues.Year : 0}Y${resolutionValues.Month ? resolutionValues.Month : 0}M${resolutionValues.Day ? resolutionValues.Day : 0}DT${resolutionValues.Hour ? resolutionValues.Hour : 0}H${resolutionValues.Minute ? resolutionValues.Minute : 0}M${resolutionValues.Second ? resolutionValues.Second : 0}S`; - - // frontend always provides temporalResolution even if there is no value resulting in P0Y0M0DT0H0M0S - // don't save if value is equal to P0Y0M0DT0H0M0S - if (valueString !== "P0Y0M0DT0H0M0S") { - RDFdataset.addQuad(N3.DataFactory.quad( - mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.literal(valueString, N3.DataFactory.namedNode(generalHelper.addNamespace('xsd:duration', dpiConfig))) - )) - } - } else if (key === 'dct:identifier') { - // form provides data as array of objects with strings: [ { '@value': 'string1' }, { '@value': 'string2' }, ... ] - // create quadruple for each given object in the array - for (let valueId = 0; valueId < data[key].length; valueId += 1) { - const currentValue = data[key][valueId]; - if (has(currentValue, '@value') && !isEmpty(currentValue['@value'])) { - RDFdataset.addQuad(N3.DataFactory.quad( - mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.literal(currentValue['@value']) - )) - } + + if (hasDescription) { + hasNoValueKeysDescription = !currentGroupData[ + "dct:description" + ].every((el) => has(el, "@value")); + hasEmptyValueDescription = currentGroupData[ + "dct:description" + ].every((el) => isEmpty(el["@value"])); } - } else if (key === 'dct:rights') { - // rights has a static type (RightsStatement) which needs to be added to linked data as additional node - // therefore we need to create an initial quadruple for with 'rights' being the predicate having a blank node - // blank node serves as subject for the following quadruples which contain the type and actual value of the form field - // RDF: - // datasetID dct:rights blankNodeID - // blankNodeId rdf:type RightsStatement - // blankNodeId rdfs:label LabelValue - - // blank node as object for inital quadruple and also as subject for following quadruples - const rightsBlankNode = N3.DataFactory.blankNode(''); - - RDFdataset.addQuad(N3.DataFactory.quad( + + // page should be handled as empty if: + // no title and/or no description given + // if properties given: no value given or value empty + if ( + (hasNoValueKeysTitle || hasEmptyValueTitle) && + (hasNoValueKeysDescription || hasEmptyValueDescription) + ) + emptyPage = true; + } + + if (!emptyPage) { + if (!groupBlankNode) + groupBlankNode = N3.DataFactory.blankNode(""); + + // save inital quadruple using the named or blank node as object + // e.g. datasetId dct:contactPoint blankNode/namedNode + RDFdataset.addQuad( + N3.DataFactory.quad( mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - rightsBlankNode - )) - - // add additional type declaration - RDFdataset.addQuad(N3.DataFactory.quad( - rightsBlankNode, - N3.DataFactory.namedNode(generalHelper.addNamespace('rdf:type', dpiConfig)), - N3.DataFactory.namedNode(generalHelper.addNamespace('dct:RightsStatement', dpiConfig)) - )) - - // rights is a conditional property and provides either an URI or a string ( { rdfs:label : 'URL/string' } ) - let rightsValue; - - if (data[key]['@type'] === 'url') { - rightsValue = N3.DataFactory.namedNode(data[key]['rdfs:label']); - } else { - rightsValue = N3.DataFactory.literal(data[key]['rdfs:label']); + N3.DataFactory.namedNode( + generalHelper.addNamespace(key, dpiConfig) + ), + groupBlankNode + ) + ); + + // some properties provide additional types + if (has(formatTypes.additionalPropertyTypes, key)) { + RDFdataset.addQuad( + N3.DataFactory.quad( + groupBlankNode, + N3.DataFactory.namedNode( + generalHelper.addNamespace("rdf:type", dpiConfig) + ), + N3.DataFactory.namedNode( + generalHelper.addNamespace( + formatTypes.additionalPropertyTypes[key], + dpiConfig + ) + ) + ) + ); } - // add actual value - RDFdataset.addQuad(N3.DataFactory.quad( - rightsBlankNode, - N3.DataFactory.namedNode(generalHelper.addNamespace('rdfs:label', dpiConfig)), - rightsValue - )) - - } else if (key === 'rdf:type') { - // some properties have additional type information which needs to be added to graph - // e.g contactPoint -> vcard:Individual - RDFdataset.addQuad(N3.DataFactory.quad( - mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace('rdf:type', dpiConfig)), - N3.DataFactory.namedNode(generalHelper.addNamespace(data[key], dpiConfig)) - )) + // temporal values nested inside another object: "dct:temporal": [{ "dct:temporal": { "dcat:startDate": "...", "dcat:endDate": "..." } }] + if (key === "dct:temporal") { + if (has(currentGroupData, "dct:temporal")) { + currentGroupData = currentGroupData["dct:temporal"]; + } + } + + // convert all nested values provided by form + convertPropertyValues( + RDFdataset, + currentGroupData, + property, + groupBlankNode, + mainType, + false, + dpiConfig, + dpiConfig + ); + } + } + } + } + } else if (formatTypes.conditionalProperties[property].includes(key)) { + // publisher either is an URI or a group with multiple values (name, homepage, email) + // license either is an URI or a group with multiple values () + if (key === "dct:publisher" || key === "dct:license") { + // data contains either {resource: '...', name: '...'} or object containing other keys + if (has(data[key], "resource")) { + convertSingularURI(RDFdataset, mainURI, data, key, dpiConfig); + } else { + const groupBlankNode = N3.DataFactory.blankNode(""); + + // some properties provide additional types + if (has(formatTypes.additionalPropertyTypes, key)) { + RDFdataset.addQuad( + N3.DataFactory.quad( + groupBlankNode, + N3.DataFactory.namedNode( + generalHelper.addNamespace("rdf:type", dpiConfig) + ), + N3.DataFactory.namedNode( + generalHelper.addNamespace( + formatTypes.additionalPropertyTypes[key], + dpiConfig + ) + ) + ) + ); } - } + + // save inital quadruple using the named or blank node as object + // e.g. datasetId dct:contactPoint blankNode/namedNode + RDFdataset.addQuad( + N3.DataFactory.quad( + mainURI, + N3.DataFactory.namedNode( + generalHelper.addNamespace(key, dpiConfig) + ), + groupBlankNode + ) + ); + + convertPropertyValues( + RDFdataset, + data[key], + property, + groupBlankNode, + mainType, + false, + dpiConfig, + dpiConfig + ); + } + } + } else if (key === "dcat:temporalResolution") { + // temporal resolution is displayed as group of input forms for each property (year, month, day, ...) + // the form provides the data as following: [ { 'Year': '...', 'Month': '...', ... } ] + // the final format of this property should look like this: P?Y?M?DT?H?M?S + // not all values must be filled and therefore be present -> default behavior if not given: value = 0 + + const resolutionValues = data[key]; + const valueString = `P${ + resolutionValues.Year ? resolutionValues.Year : 0 + }Y${resolutionValues.Month ? resolutionValues.Month : 0}M${ + resolutionValues.Day ? resolutionValues.Day : 0 + }DT${resolutionValues.Hour ? resolutionValues.Hour : 0}H${ + resolutionValues.Minute ? resolutionValues.Minute : 0 + }M${resolutionValues.Second ? resolutionValues.Second : 0}S`; + + // frontend always provides temporalResolution even if there is no value resulting in P0Y0M0DT0H0M0S + // don't save if value is equal to P0Y0M0DT0H0M0S + if (valueString !== "P0Y0M0DT0H0M0S") { + RDFdataset.addQuad( + N3.DataFactory.quad( + mainURI, + N3.DataFactory.namedNode( + generalHelper.addNamespace(key, dpiConfig) + ), + N3.DataFactory.literal( + valueString, + N3.DataFactory.namedNode( + generalHelper.addNamespace("xsd:duration", dpiConfig) + ) + ) + ) + ); + } + } else if (key === "dct:identifier") { + // form provides data as array of objects with strings: [ { '@value': 'string1' }, { '@value': 'string2' }, ... ] + // create quadruple for each given object in the array + for (let valueId = 0; valueId < data[key].length; valueId += 1) { + const currentValue = data[key][valueId]; + if (has(currentValue, "@value") && !isEmpty(currentValue["@value"])) { + RDFdataset.addQuad( + N3.DataFactory.quad( + mainURI, + N3.DataFactory.namedNode( + generalHelper.addNamespace(key, dpiConfig) + ), + N3.DataFactory.literal(currentValue["@value"]) + ) + ); + } + } + } else if (key === "dct:rights") { + // rights has a static type (RightsStatement) which needs to be added to linked data as additional node + // therefore we need to create an initial quadruple for with 'rights' being the predicate having a blank node + // blank node serves as subject for the following quadruples which contain the type and actual value of the form field + // RDF: + // datasetID dct:rights blankNodeID + // blankNodeId rdf:type RightsStatement + // blankNodeId rdfs:label LabelValue + + // blank node as object for inital quadruple and also as subject for following quadruples + const rightsBlankNode = N3.DataFactory.blankNode(""); + + RDFdataset.addQuad( + N3.DataFactory.quad( + mainURI, + N3.DataFactory.namedNode( + generalHelper.addNamespace(key, dpiConfig) + ), + rightsBlankNode + ) + ); + + // add additional type declaration + RDFdataset.addQuad( + N3.DataFactory.quad( + rightsBlankNode, + N3.DataFactory.namedNode( + generalHelper.addNamespace("rdf:type", dpiConfig) + ), + N3.DataFactory.namedNode( + generalHelper.addNamespace("dct:RightsStatement", dpiConfig) + ) + ) + ); + + // rights is a conditional property and provides either an URI or a string ( { rdfs:label : 'URL/string' } ) + let rightsValue; + + if (data[key]["@type"] === "url") { + rightsValue = N3.DataFactory.namedNode(data[key]["rdfs:label"]); + } else { + rightsValue = N3.DataFactory.literal(data[key]["rdfs:label"]); + } + + // add actual value + RDFdataset.addQuad( + N3.DataFactory.quad( + rightsBlankNode, + N3.DataFactory.namedNode( + generalHelper.addNamespace("rdfs:label", dpiConfig) + ), + rightsValue + ) + ); + } } + } } /** @@ -374,38 +536,57 @@ function convertPropertyValues(RDFdataset, data, property, preMainURI, preMainTy * @param {String} property String determining which property is converted (datasets/distributions/catalogues) * @param {String} datasetURI URI of dataset used to add distribution URI to dct:distribution within dataset graph */ -function setAdditionalProperties(RDFdataset, data, mainURI, mainType, property, datasetURI, dpiConfig) { - - // adding id and type of property - RDFdataset.addQuad(N3.DataFactory.quad( +function setAdditionalProperties( + RDFdataset, + data, + mainURI, + mainType, + property, + datasetURI, + dpiConfig +) { + // adding id and type of property + RDFdataset.addQuad( + N3.DataFactory.quad( + mainURI, + N3.DataFactory.namedNode( + generalHelper.addNamespace("rdf:type", dpiConfig) + ), + N3.DataFactory.namedNode(mainType) + ) + ); + + // catalogues always have to contain the property dct:type with the value 'dcat-ap' + if (property === "catalogues") { + RDFdataset.addQuad( + N3.DataFactory.quad( mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace('rdf:type', dpiConfig)), - N3.DataFactory.namedNode(mainType) - )) - - // catalogues always have to contain the property dct:type with the value 'dcat-ap' - if (property === 'catalogues') { - RDFdataset.addQuad(N3.DataFactory.quad( - mainURI, - N3.DataFactory.namedNode(generalHelper.addNamespace('dct:type', dpiConfig)), - N3.DataFactory.literal('dcat-ap') - )) - } - - // add distribution id to dataset graph (dcat:distribution) - if (property === 'distributions') { - RDFdataset.addQuad(N3.DataFactory.quad( - N3.DataFactory.namedNode(datasetURI), - N3.DataFactory.namedNode(generalHelper.addNamespace('dcat:distribution', dpiConfig)), - mainURI - )) - } + N3.DataFactory.namedNode( + generalHelper.addNamespace("dct:type", dpiConfig) + ), + N3.DataFactory.literal("dcat-ap") + ) + ); + } + + // add distribution id to dataset graph (dcat:distribution) + if (property === "distributions") { + RDFdataset.addQuad( + N3.DataFactory.quad( + N3.DataFactory.namedNode(datasetURI), + N3.DataFactory.namedNode( + generalHelper.addNamespace("dcat:distribution", dpiConfig) + ), + mainURI + ) + ); + } } //----------------------------------------------------------------------------------------------------- // basic conversion (input to RDF) methods for different categories of data //----------------------------------------------------------------------------------------------------- -// seems unnecessary at first but if we want to convert nested properties as well, we need these +// seems unnecessary at first but if we want to convert nested properties as well, we need these // methods (especially to provide the correct parent URI) /** @@ -416,13 +597,15 @@ function setAdditionalProperties(RDFdataset, data, mainURI, mainType, property, * @param {String} key Name of current value (e.g. dct:title) used as predicate in quad */ function convertSingularString(RDFdataset, id, data, key, dpiConfig) { - if (!isEmpty(data[key])) { - RDFdataset.addQuad(N3.DataFactory.quad( - id, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.literal(data[key]) - )) - } + if (!isEmpty(data[key])) { + RDFdataset.addQuad( + N3.DataFactory.quad( + id, + N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), + N3.DataFactory.literal(data[key]) + ) + ); + } } /** @@ -433,36 +616,37 @@ function convertSingularString(RDFdataset, id, data, key, dpiConfig) { * @param {String} key Name of current value (e.g. dct:title) used as predicate in quad */ function convertSingularURI(RDFdataset, id, data, key, dpiConfig) { - // there are two different formats the frontend delivers URIs - // 1: 'URI' or 2: {'name': 'abc', 'resource': 'URI'} - - // URIs can either be a normal URL or an email address - // mail addresses typicall include '@' which is used to determine if the given string is a normal URL or an email address - if (!isEmpty(data[key])) { - - let singleURI; - - if (typeof data[key] === 'object') { - if (has(data[key], 'resource')) { - singleURI = data[key].resource; - } - } else { - if (data[key].includes('@')) { - // mail address - singleURI = `mailto:${data[key]}`; - } else { - // normal URL - singleURI = data[key]; - } - } - - // save quad to dataset - RDFdataset.addQuad(N3.DataFactory.quad( - id, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.namedNode(singleURI) - )); + // there are two different formats the frontend delivers URIs + // 1: 'URI' or 2: {'name': 'abc', 'resource': 'URI'} + + // URIs can either be a normal URL or an email address + // mail addresses typicall include '@' which is used to determine if the given string is a normal URL or an email address + if (!isEmpty(data[key])) { + let singleURI; + + if (typeof data[key] === "object") { + if (has(data[key], "resource")) { + singleURI = data[key].resource; + } + } else { + if (data[key].includes("@")) { + // mail address + singleURI = `mailto:${data[key]}`; + } else { + // normal URL + singleURI = data[key]; + } } + + // save quad to dataset + RDFdataset.addQuad( + N3.DataFactory.quad( + id, + N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), + N3.DataFactory.namedNode(singleURI) + ) + ); + } } /** @@ -474,26 +658,27 @@ function convertSingularURI(RDFdataset, id, data, key, dpiConfig) { * @param {String} property Determining which property is concerted (datasets/distributions/catalogues) */ function convertMultipleURI(RDFdataset, id, data, key, property, dpiConfig) { - // there are two different formats the frontend delivers multiple URIs - // 1: [ {"name": '...', "resource": 'URI'}, {...} ] -> multi-autocomplete fields - // 2: [ { "@id": "URI1" }, { "@id": "URI2" } ] repeatable fields - - for (let uriIndex = 0; uriIndex < data[key].length; uriIndex += 1) { - - let currentURI; - const valueObject = data[key][uriIndex]; - if (!isEmpty(valueObject)) { - if (has(valueObject, 'resource')) currentURI = valueObject.resource; - else if (has(valueObject, '@id')) currentURI = valueObject['@id']; - } - - // save quad to dataset - RDFdataset.addQuad(N3.DataFactory.quad( - id, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.namedNode(currentURI) - )); + // there are two different formats the frontend delivers multiple URIs + // 1: [ {"name": '...', "resource": 'URI'}, {...} ] -> multi-autocomplete fields + // 2: [ { "@id": "URI1" }, { "@id": "URI2" } ] repeatable fields + + for (let uriIndex = 0; uriIndex < data[key].length; uriIndex += 1) { + let currentURI; + const valueObject = data[key][uriIndex]; + if (!isEmpty(valueObject)) { + if (has(valueObject, "resource")) currentURI = valueObject.resource; + else if (has(valueObject, "@id")) currentURI = valueObject["@id"]; } + + // save quad to dataset + RDFdataset.addQuad( + N3.DataFactory.quad( + id, + N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), + N3.DataFactory.namedNode(currentURI) + ) + ); + } } /** @@ -504,41 +689,63 @@ function convertMultipleURI(RDFdataset, id, data, key, property, dpiConfig) { * @param {String} key Name of current value (e.g. dct:title) used as predicate in quad and to determine quad-object type */ function convertTypedString(RDFdataset, id, data, key, dpiConfig) { - if (!isEmpty(data[key])) { - - // there is a variety of properties which can have different types - // issued and motified already provide a type definition ({'@type': 'date/datetime', '@value': '...'}) - if (key === 'dct:issued' || key === 'dct:modified') { - if (has(data[key], '@value') && !isEmpty(data[key]['@value'])) { - const imValueType = data[key]['@type'] === 'date' ? data[key]['@type'] : 'dateTime'; - const valueType = generalHelper.addNamespace(`xsd:${imValueType}`, dpiConfig); - - /// save quad to dataset - RDFdataset.addQuad(N3.DataFactory.quad( - id, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.literal(data[key]['@value'], N3.DataFactory.namedNode(valueType)) - )); - } - } else { - // all other properties are given as a simple string - let valueType; - if (key === 'dcat:endDate' || key === 'dcat:startDate') { - // dcat:endDate and dcat:startDate are xsd:dateTime - valueType = generalHelper.addNamespace('xsd:dateTime', dpiConfig); - } else if (key === 'dcat:spatialResolutionInMeters' || key === "dcat:byteSize") { - // dcat:spatialResolutionInMeters and dcat:byteSize are xsd:decimal - valueType = generalHelper.addNamespace('xsd:decimal', dpiConfig); - } - - /// save quad to dataset - RDFdataset.addQuad(N3.DataFactory.quad( - id, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.literal(data[key], N3.DataFactory.namedNode(valueType)) - )); - } + if (!isEmpty(data[key])) { + // there is a variety of properties which can have different types + // issued and motified already provide a type definition ({'@type': 'date/datetime', '@value': '...'}) + if (key === "dct:issued" || key === "dct:modified") { + if (has(data[key], "@value") && !isEmpty(data[key]["@value"])) { + const imValueType = + data[key]["@type"] === "date" ? data[key]["@type"] : "dateTime"; + const valueType = generalHelper.addNamespace( + `xsd:${imValueType}`, + dpiConfig + ); + + /// save quad to dataset + RDFdataset.addQuad( + N3.DataFactory.quad( + id, + N3.DataFactory.namedNode( + generalHelper.addNamespace(key, dpiConfig) + ), + N3.DataFactory.literal( + data[key]["@value"], + N3.DataFactory.namedNode(valueType) + ) + ) + ); + } + } else { + // all other properties are given as a simple string + let valueType; + if (key === "dcat:endDate" || key === "dcat:startDate") { + // dcat:endDate and dcat:startDate are xsd:dateTime + valueType = generalHelper.addNamespace("xsd:dateTime", dpiConfig); + } else if ( + key === "dcat:spatialResolutionInMeters" || + key === "dcat:byteSize" + ) { + // dcat:spatialResolutionInMeters and dcat:byteSize are xsd:decimal + valueType = generalHelper.addNamespace("xsd:decimal", dpiConfig); + } + else if ( + key === "pv:DistributionType" + + ) { + valueType = generalHelper.addNamespace("xsd:string", dpiConfig); + } + + /// save quad to dataset + RDFdataset.addQuad( + N3.DataFactory.quad( + id, + N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), + N3.DataFactory.literal(data[key], N3.DataFactory.namedNode(valueType)) + ) + ); + } + } } /** @@ -549,46 +756,58 @@ function convertTypedString(RDFdataset, id, data, key, dpiConfig) { * @param {String} key Name of current value (e.g. dct:title) used as predicate in quads */ function convertMultilingual(RDFdataset, id, data, key, dpiConfig) { - // multilingual fields mostly provide data as followed - // [ { '@value': '....', '@language': '...' }, ... ] - // only the licence title provides no language - - if (!isEmpty(data[key])) { - - // licence title - if (!Array.isArray(data[key])) { - RDFdataset.addQuad(N3.DataFactory.quad( - id, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.literal(data[key]) - )) - } else { - for (let langIndex = 0; langIndex < data[key].length; langIndex += 1) { - const currentData = data[key][langIndex]; - // only save data if a value is given (forntend provides preselected language which don't need to be saved if there is no actaul value) - if (!isEmpty(currentData) && has(currentData, '@value') && !isEmpty(currentData['@value'])) { - let languageTag; - - // if there is no langauge given, set language to english - if (!has(currentData, '@language') || isEmpty(currentData, '@language')) { - languageTag = 'en'; - } else { - // if language is given, use given tag - languageTag = currentData['@language']; - } - - // saving quad to dataset - RDFdataset.addQuad(N3.DataFactory.quad( - id, - N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), - N3.DataFactory.literal(currentData['@value'], languageTag) - )) - } - } + // multilingual fields mostly provide data as followed + // [ { '@value': '....', '@language': '...' }, ... ] + // only the licence title provides no language + + if (!isEmpty(data[key])) { + // licence title + if (!Array.isArray(data[key])) { + RDFdataset.addQuad( + N3.DataFactory.quad( + id, + N3.DataFactory.namedNode(generalHelper.addNamespace(key, dpiConfig)), + N3.DataFactory.literal(data[key]) + ) + ); + } else { + for (let langIndex = 0; langIndex < data[key].length; langIndex += 1) { + const currentData = data[key][langIndex]; + // only save data if a value is given (forntend provides preselected language which don't need to be saved if there is no actaul value) + if ( + !isEmpty(currentData) && + has(currentData, "@value") && + !isEmpty(currentData["@value"]) + ) { + let languageTag; + + // if there is no langauge given, set language to english + if ( + !has(currentData, "@language") || + isEmpty(currentData, "@language") + ) { + languageTag = "en"; + } else { + // if language is given, use given tag + languageTag = currentData["@language"]; + } + + // saving quad to dataset + RDFdataset.addQuad( + N3.DataFactory.quad( + id, + N3.DataFactory.namedNode( + generalHelper.addNamespace(key, dpiConfig) + ), + N3.DataFactory.literal(currentData["@value"], languageTag) + ) + ); } + } } + } } export default { - convertToRDF, -}; \ No newline at end of file + convertToRDF, +}; diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/general-helper.js b/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/general-helper.js index 25e397085c61637707aabf660e52f47796717b6c..7d0ff569dab73fb7d1b74026ca1dafcefa1ebddc 100644 --- a/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/general-helper.js +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/general-helper.js @@ -1,19 +1,18 @@ -import { isEmpty, isNil, has, cloneDeep } from 'lodash'; -import axios from 'axios'; +import { isEmpty, isNil, has, cloneDeep } from "lodash"; +import axios from "axios"; import { getTranslationFor } from "../../utils/helpers"; - /** * Merges multiple Objects nested within an object into one main objects with al key-value-pairs originally located within the nested objects * @param {Object} data Object containing nested objects * @returns Object with key-value pairs merged from nested objects */ function mergeNestedObjects(data) { - let mergedObject = {}; - for (const key in data) { - mergedObject = Object.assign(mergedObject, data[key]); - } - return mergedObject; + let mergedObject = {}; + for (const key in data) { + mergedObject = Object.assign(mergedObject, data[key]); + } + return mergedObject; } /** @@ -22,27 +21,33 @@ function mergeNestedObjects(data) { * @returns */ function addNamespace(prefix, dpiConfig) { - // the prefix had the following format: namespace:property (e.g. dct:title) - // the short version of the namespace noe should be replaced by the long version (e.g. http://purl.org/dc/terms/title) - let fullDescriptor; - const colonIndex = prefix.indexOf(':'); + // the prefix had the following format: namespace:property (e.g. dct:title) + // the short version of the namespace noe should be replaced by the long version (e.g. http://purl.org/dc/terms/title) + let fullDescriptor; + + // console.log(prefix); +// if (prefix === "pv:DistributionType") { +// return "pv:DistributionType"; +// } else { + const colonIndex = prefix.indexOf(":"); // there are also prefixes with no namespace which should sty the same if (colonIndex !== -1) { - const namespaceAbbreviation = prefix.substr(0, colonIndex); - const propertyName = prefix.substr(colonIndex + 1); + const namespaceAbbreviation = prefix.substr(0, colonIndex); + const propertyName = prefix.substr(colonIndex + 1); - // the long version of the namespace is saved within the context.json (config) - // there is an object containing the namespace abbreviation(key) and the corresponding value is the long version of the namespace + // the long version of the namespace is saved within the context.json (config) + // there is an object containing the namespace abbreviation(key) and the corresponding value is the long version of the namespace - const longNamespace = dpiConfig.prefixes[namespaceAbbreviation]; - fullDescriptor = `${longNamespace}${propertyName}`; + const longNamespace = dpiConfig.prefixes[namespaceAbbreviation]; + fullDescriptor = `${longNamespace}${propertyName}`; } else { - fullDescriptor = prefix; + fullDescriptor = prefix; } return fullDescriptor; -} + } +// } /** * Removes long namespace and replaces it with the abbreviation of the namespace @@ -50,20 +55,22 @@ function addNamespace(prefix, dpiConfig) { * @returns Returns value with short namespace (e.g. rdf:type) */ function removeNamespace(longValue, dpiConfig) { - let lastIndex; - - // long namespace either ends with an # or a \ - if (longValue.includes('#')) { - lastIndex = longValue.lastIndexOf('#') - } else { - lastIndex = longValue.lastIndexOf('/') - } - - const shortValue = longValue.substr(lastIndex + 1); - const longPrefix = longValue.substr(0, lastIndex + 1); - const shortPrefix = Object.keys(dpiConfig.prefixes).find(key => dpiConfig.prefixes[key] === longPrefix); - - return `${shortPrefix}:${shortValue}`; + let lastIndex; + + // long namespace either ends with an # or a \ + if (longValue.includes("#")) { + lastIndex = longValue.lastIndexOf("#"); + } else { + lastIndex = longValue.lastIndexOf("/"); + } + + const shortValue = longValue.substr(lastIndex + 1); + const longPrefix = longValue.substr(0, lastIndex + 1); + const shortPrefix = Object.keys(dpiConfig.prefixes).find( + (key) => dpiConfig.prefixes[key] === longPrefix + ); + + return `${shortPrefix}:${shortValue}`; } /** @@ -72,13 +79,13 @@ function removeNamespace(longValue, dpiConfig) { * @returns Array of shortened keys */ function getNestedKeys(data, dpiConfig) { - const keys = []; + const keys = []; - for (let el of data) { - keys.push(removeNamespace(el.predicate.value, dpiConfig)); - } + for (let el of data) { + keys.push(removeNamespace(el.predicate.value, dpiConfig)); + } - return keys; + return keys; } /** @@ -87,13 +94,14 @@ function getNestedKeys(data, dpiConfig) { * @returns String formed of random characters with given length */ function makeId(length) { - var result = ''; - var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - var charactersLength = characters.length; - for (var i = 0; i < length; i++) { - result += characters.charAt(Math.floor(Math.random() * charactersLength)); - } - return result; + var result = ""; + var characters = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var charactersLength = characters.length; + for (var i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; } /** @@ -102,13 +110,13 @@ function makeId(length) { * @returns Boolean determining if given string is an Url */ function isUrl(string) { - let url; - try { - url = new URL(string); - } catch (_) { - return false; - } - return url.protocol === "http:" || url.protocol === "https:"; + let url; + try { + url = new URL(string); + } catch (_) { + return false; + } + return url.protocol === "http:" || url.protocol === "https:"; } /** @@ -118,58 +126,61 @@ function isUrl(string) { * @returns Returns promise of fetched data */ async function fetchLinkedData(endpoint, token) { - let response; - let requestOptions; - - // if token is given, provide token (for drafts and other non-public elements) - if (token !== '') { - requestOptions = { - method: 'GET', - headers: { - Authorization: `Bearer ${token}`, - }, - url: endpoint, - }; - } else { - requestOptions = { - method: 'GET', - url: endpoint, - }; - } + let response; + let requestOptions; + + // if token is given, provide token (for drafts and other non-public elements) + if (token !== "") { + requestOptions = { + method: "GET", + headers: { + Authorization: `Bearer ${token}`, + }, + url: endpoint, + }; + } else { + requestOptions = { + method: "GET", + url: endpoint, + }; + } + + try { + response = fetch(endpoint, requestOptions) + .then((response) => { + const reader = response?.body?.getReader(); + return new ReadableStream({ + start(controller) { + // The following function handles each data chunk + function push() { + // "done" is a Boolean and value a "Uint8Array" + reader?.read().then(({ done, value }) => { + // If there is no more data to read + if (done) { + controller.close(); + return; + } + // Get the data and send it to the browser via the controller + controller.enqueue(value); + // Check chunks by logging to the console + push(); + }); + } - try { - response = fetch(endpoint, requestOptions) - .then(response => { - const reader = response?.body?.getReader(); - return new ReadableStream({ - start(controller) { - // The following function handles each data chunk - function push() { - // "done" is a Boolean and value a "Uint8Array" - reader?.read().then(({ done, value }) => { - // If there is no more data to read - if (done) { - controller.close(); - return; - } - // Get the data and send it to the browser via the controller - controller.enqueue(value); - // Check chunks by logging to the console - push(); - }); - } - - push(); - }, - }); - }).then((stream) => - new Response(stream, { headers: { 'Content-Type': 'text/html' } }).text() - ); - } catch (err) { - // TODO: Handle (network) errors - throw Error(`Error occured during fetching endpoint: ${endpoint}`); - } - return response; + push(); + }, + }); + }) + .then((stream) => + new Response(stream, { + headers: { "Content-Type": "text/html" }, + }).text() + ); + } catch (err) { + // TODO: Handle (network) errors + throw Error(`Error occured during fetching endpoint: ${endpoint}`); + } + return response; } /** @@ -180,29 +191,33 @@ async function fetchLinkedData(endpoint, token) { * @returns Object containing keys of properties for each page */ function getPagePrefixedNames(property, formDefinitions, pageContent) { - - const prefixedNames = { - datasets: {}, - distributions: {}, - catalogues: {} - }; - - // get property keys for each page - for (let pageName in pageContent[property]) { - prefixedNames[property][pageName] = []; - const propertyKeys = pageContent[property][pageName]; - for (let propertyindex = 0; propertyindex < propertyKeys.length; propertyindex++) { - const propertyName = propertyKeys[propertyindex]; - const prefixedName = formDefinitions[property][propertyName].name; // form definition includes name-property which contains key - if (prefixedName !== undefined) prefixedNames[property][pageName].push(prefixedName); - } + const prefixedNames = { + datasets: {}, + distributions: {}, + catalogues: {}, + }; + + // get property keys for each page + for (let pageName in pageContent[property]) { + prefixedNames[property][pageName] = []; + const propertyKeys = pageContent[property][pageName]; + for ( + let propertyindex = 0; + propertyindex < propertyKeys.length; + propertyindex++ + ) { + const propertyName = propertyKeys[propertyindex]; + const prefixedName = formDefinitions[property][propertyName].name; // form definition includes name-property which contains key + if (prefixedName !== undefined) + prefixedNames[property][pageName].push(prefixedName); } + } - return prefixedNames; + return prefixedNames; } function isValid(id) { - return /^[a-z0-9-]*$/.test(id); + return /^[a-z0-9-]*$/.test(id); } /** @@ -213,183 +228,187 @@ function isValid(id) { * @returns {string|null} */ function getFileIdByAccessUrl({ accessUrl, fileUploadUrl }) { - const accessUrlWithTrailingSlash = accessUrl.endsWith('/') - ? accessUrl - : `${accessUrl}/`; - const fileUploadUrlWithTrailingSlash = fileUploadUrl.endsWith('/') - ? fileUploadUrl - : `${fileUploadUrl}/`; - - // Check if accessUrl starts with fileUploadApi - if (accessUrlWithTrailingSlash.startsWith(fileUploadUrlWithTrailingSlash)) { - const accessUrlParts = accessUrlWithTrailingSlash.split('/'); - const fileId = accessUrlParts[accessUrlParts.length - 2]; - - return fileId || null; - } - - return null; + const accessUrlWithTrailingSlash = accessUrl.endsWith("/") + ? accessUrl + : `${accessUrl}/`; + const fileUploadUrlWithTrailingSlash = fileUploadUrl.endsWith("/") + ? fileUploadUrl + : `${fileUploadUrl}/`; + + // Check if accessUrl starts with fileUploadApi + if (accessUrlWithTrailingSlash.startsWith(fileUploadUrlWithTrailingSlash)) { + const accessUrlParts = accessUrlWithTrailingSlash.split("/"); + const fileId = accessUrlParts[accessUrlParts.length - 2]; + + return fileId || null; + } + + return null; } /** - * Adds given key to format type - * @param {String} key - * @param {String} format - * @param {String} property - * @param {Object} typeDefinition + * Adds given key to format type + * @param {String} key + * @param {String} format + * @param {String} property + * @param {Object} typeDefinition */ function addKeyToFormatType(key, format, property, typeDefinition) { - typeDefinition[format][property].push(key); + typeDefinition[format][property].push(key); } /** * Removes key from format type - * @param {String} key - * @param {String} format - * @param {String} property - * @param {Object} typeDefinition + * @param {String} key + * @param {String} format + * @param {String} property + * @param {Object} typeDefinition */ function removeKeyFromFormatType(key, format, property, typeDefinition) { - typeDefinition[format][property].splice(typeDefinition[format][property].indexOf(key), 1); + typeDefinition[format][property].splice( + typeDefinition[format][property].indexOf(key), + 1 + ); } function propertyObjectHasValues(objectData) { - let objectHasValues = false; - if (!isNil(objectData) && !isEmpty(objectData)) { - // language tag is always given - let copiedData = cloneDeep(objectData) + let objectHasValues = false; + if (!isNil(objectData) && !isEmpty(objectData)) { + // language tag is always given + let copiedData = cloneDeep(objectData); - if (has(copiedData, '@language')) { - delete copiedData['@language']; - } + if (has(copiedData, "@language")) { + delete copiedData["@language"]; + } - // removing all falsy values (undefined, null, "", '', NaN, 0) - const actualValues = Object.values(copiedData).filter(el => el); // filters all real values - if (!isEmpty(actualValues)) { - // there are keys containing an object or array as value - for (let valueIndex = 0; valueIndex < actualValues.length; valueIndex++) { - // if at least one elemnt within the array is set, return true - const currentValue = actualValues[valueIndex]; - - // testing content of array - if (Array.isArray(currentValue)) { - // there are only objects wihtin those arrays - for (let arrIndex = 0; arrIndex < currentValue.length; arrIndex++) { - if (propertyObjectHasValues(currentValue[arrIndex])) objectHasValues = true; - } - } else if (typeof currentValue === 'object') { // testing content of object - if (propertyObjectHasValues(currentValue)) objectHasValues = true; - } else { - objectHasValues = true; - } - } + // removing all falsy values (undefined, null, "", '', NaN, 0) + const actualValues = Object.values(copiedData).filter((el) => el); // filters all real values + if (!isEmpty(actualValues)) { + // there are keys containing an object or array as value + for (let valueIndex = 0; valueIndex < actualValues.length; valueIndex++) { + // if at least one elemnt within the array is set, return true + const currentValue = actualValues[valueIndex]; + + // testing content of array + if (Array.isArray(currentValue)) { + // there are only objects wihtin those arrays + for (let arrIndex = 0; arrIndex < currentValue.length; arrIndex++) { + if (propertyObjectHasValues(currentValue[arrIndex])) + objectHasValues = true; + } + } else if (typeof currentValue === "object") { + // testing content of object + if (propertyObjectHasValues(currentValue)) objectHasValues = true; + } else { + objectHasValues = true; } + } } + } - return objectHasValues; + return objectHasValues; } function propertyHasValue(data) { - - let isSet = false; - - if (data !== undefined && data !== "" && !isEmpty(data) && !isNil(data)) { - // testing array data - if (Array.isArray(data)) { - // there are arreay of objects or arrays of values - if (data.every(el => typeof el === 'string')) { - isSet = !isEmpty(data.filter(el => el)); - } else if (data.every(el => typeof el === 'object')) { - for (let index = 0; index < data.length; index++) { - // if at least one array element is set, return true - if (propertyObjectHasValues(data[index])) isSet = true; - } - } - } else if (typeof data === 'object') { - // testing object data - isSet = propertyObjectHasValues(data); - } else { - isSet = true; + let isSet = false; + + if (data !== undefined && data !== "" && !isEmpty(data) && !isNil(data)) { + // testing array data + if (Array.isArray(data)) { + // there are arreay of objects or arrays of values + if (data.every((el) => typeof el === "string")) { + isSet = !isEmpty(data.filter((el) => el)); + } else if (data.every((el) => typeof el === "object")) { + for (let index = 0; index < data.length; index++) { + // if at least one array element is set, return true + if (propertyObjectHasValues(data[index])) isSet = true; } + } + } else if (typeof data === "object") { + // testing object data + isSet = propertyObjectHasValues(data); + } else { + isSet = true; } + } - return isSet; + return isSet; } /** - * + * */ async function requestUriLabel(uri, dpiConfig, envs) { + // get vocabulary by finding vocab-url within given URI + const voc = Object.keys(dpiConfig.vocabPrefixes).find((key) => + uri.includes(dpiConfig.vocabPrefixes[key]) + ); - // get vocabulary by finding vocab-url within given URI - const voc = Object.keys(dpiConfig.vocabPrefixes).find(key => uri.includes(dpiConfig.vocabPrefixes[key])); - - try { - let req; - - // vocabularies for spdx checksum and inana-media-types are structured differently in the backend then other vocabularies - if (voc === 'iana-media-types' || voc === 'spdx-checksum-algorithm') { - req = `${envs.api.baseUrl}vocabularies/${voc}`; - - } else { - const value = uri.replace(dpiConfig.vocabPrefixes[voc], ''); - req = `${envs.api.baseUrl}vocabularies/${voc}/${value}`; - } + try { + let req; - return new Promise((resolve, reject) => { - axios.get(req) - .then((res) => { - resolve(res); - }) - .catch((err) => { - reject(err); + // vocabularies for spdx checksum and inana-media-types are structured differently in the backend then other vocabularies + if (voc === "iana-media-types" || voc === "spdx-checksum-algorithm") { + req = `${envs.api.baseUrl}vocabularies/${voc}`; + } else { + const value = uri.replace(dpiConfig.vocabPrefixes[voc], ""); + req = `${envs.api.baseUrl}vocabularies/${voc}/${value}`; + } - }); + return new Promise((resolve, reject) => { + axios + .get(req) + .then((res) => { + resolve(res); + }) + .catch((err) => { + reject(err); }); - } catch (error) { - // - } + }); + } catch (error) { + // + } } - /** - * + * */ async function getUriLabel(uri, dpiConfig, locale, envs) { - let URIlabel; + let URIlabel; - const voc = Object.keys(dpiConfig.vocabPrefixes).find(key => uri.includes(dpiConfig.vocabPrefixes[key])); + const voc = Object.keys(dpiConfig.vocabPrefixes).find((key) => + uri.includes(dpiConfig.vocabPrefixes[key]) + ); - // if vocabulary iana media type or spdx checksum endpoint returns values in a different way - let vocMatch = (voc === "iana-media-types" || voc === "spdx-checksum-algorithm"); + // if vocabulary iana media type or spdx checksum endpoint returns values in a different way + let vocMatch = + voc === "iana-media-types" || voc === "spdx-checksum-algorithm"; - await requestUriLabel(uri, dpiConfig, envs).then( - (response) => { - let result = vocMatch - ? response.data.result.results - .filter((dataset) => dataset.resource === uri) - .map((dataset) => dataset.pref_label)[0].en - : getTranslationFor(response.data.result.pref_label, locale, []); + await requestUriLabel(uri, dpiConfig, envs).then((response) => { + let result = vocMatch + ? response.data.result.results + .filter((dataset) => dataset.resource === uri) + .map((dataset) => dataset.pref_label)[0].en + : getTranslationFor(response.data.result.pref_label, locale, []); - URIlabel = result; - } - ); + URIlabel = result; + }); - return URIlabel; + return URIlabel; } export default { - mergeNestedObjects, - addNamespace, - makeId, - isUrl, - fetchLinkedData, - getPagePrefixedNames, - getNestedKeys, - removeNamespace, - getFileIdByAccessUrl, - addKeyToFormatType, - removeKeyFromFormatType, - propertyHasValue, - getUriLabel, + mergeNestedObjects, + addNamespace, + makeId, + isUrl, + fetchLinkedData, + getPagePrefixedNames, + getNestedKeys, + removeNamespace, + getFileIdByAccessUrl, + addKeyToFormatType, + removeKeyFromFormatType, + propertyHasValue, + getUriLabel, }; diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/inputConverter.js b/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/inputConverter.js index c4005d65b1b011caa7aa82a93146017975155d79..7bab57da177a200693e6d60cd3c8d2dae50e4496 100644 --- a/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/inputConverter.js +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/utils/inputConverter.js @@ -1,6 +1,5 @@ import generalHelper from "./general-helper"; -import { has, isEmpty } from 'lodash'; - +import { has, isEmpty } from "lodash"; /** * Converts given data for given property into input form format @@ -8,48 +7,83 @@ import { has, isEmpty } from 'lodash'; * @param {*} property Property to convert data for (datasets/catalogues) * @param {*} data Linked data within a dataset */ -async function convertToInput(state, property, data, dpiConfig ) { - - let generalID; - let namespaceKeys; - let propertyQuads; - - if (property === 'datasets') { - propertyQuads = data.match(null, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'http://www.w3.org/ns/dcat#Dataset', null); - } else { - propertyQuads = data.match(null, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'http://www.w3.org/ns/dcat#Catalog', null); - } - - // extract data for datasets/catalogues - namespaceKeys = generalHelper.getPagePrefixedNames(property, dpiConfig.inputDefinition, dpiConfig.pageConent); - state[property] = {}; - for (let el of propertyQuads) { - // there should be only one dataset id - generalID = el.subject.value; - - for (let pageName in namespaceKeys[property]) { - state[property][pageName] = {}; - convertProperties(property, state[property][pageName], generalID, data, namespaceKeys[property][pageName], dpiConfig); - } - +async function convertToInput(state, property, data, dpiConfig) { + let generalID; + let namespaceKeys; + let propertyQuads; + + if (property === "datasets") { + propertyQuads = data.match( + null, + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + "http://www.w3.org/ns/dcat#Dataset", + null + ); + } else { + propertyQuads = data.match( + null, + "http://www.w3.org/1999/02/22-rdf-syntax-ns#type", + "http://www.w3.org/ns/dcat#Catalog", + null + ); + } + + // extract data for datasets/catalogues + namespaceKeys = generalHelper.getPagePrefixedNames( + property, + dpiConfig.inputDefinition, + dpiConfig.pageConent + ); + state[property] = {}; + for (let el of propertyQuads) { + // there should be only one dataset id + generalID = el.subject.value; + + for (let pageName in namespaceKeys[property]) { + state[property][pageName] = {}; + convertProperties( + property, + state[property][pageName], + generalID, + data, + namespaceKeys[property][pageName], + dpiConfig + ); } - - // also add distribution data - if (property === 'datasets') { - const distributionQuads = data.match(generalID, 'http://www.w3.org/ns/dcat#distribution', null, null); - namespaceKeys = generalHelper.getPagePrefixedNames('distributions', dpiConfig.inputDefinition, dpiConfig.pageConent); - state.datasets.Distributions['distributionList'] = []; - for (let el of distributionQuads) { - const currentDistribution = {}; - - const distributionId = el.object.value; - for (let pageName in namespaceKeys['distributions']) { - currentDistribution[pageName] = {}; - convertProperties('distributions', currentDistribution[pageName], distributionId, data, namespaceKeys['distributions'][pageName], dpiConfig); - } - state.datasets.Distributions.distributionList.push(currentDistribution); - } + } + + // also add distribution data + if (property === "datasets") { + const distributionQuads = data.match( + generalID, + "http://www.w3.org/ns/dcat#distribution", + null, + null + ); + namespaceKeys = generalHelper.getPagePrefixedNames( + "distributions", + dpiConfig.inputDefinition, + dpiConfig.pageConent + ); + state.datasets.Distributions["distributionList"] = []; + for (let el of distributionQuads) { + const currentDistribution = {}; + + const distributionId = el.object.value; + for (let pageName in namespaceKeys["distributions"]) { + currentDistribution[pageName] = {}; + convertProperties( + "distributions", + currentDistribution[pageName], + distributionId, + data, + namespaceKeys["distributions"][pageName], + dpiConfig + ); + } + state.datasets.Distributions.distributionList.push(currentDistribution); } + } } /** @@ -61,282 +95,337 @@ async function convertToInput(state, property, data, dpiConfig ) { * @param {*} propertyKeys Keys of properties to check */ function convertProperties(property, state, id, data, propertyKeys, dpiConfig) { - - const formatType = dpiConfig.formatTypes; - - for (let index = 0; index < propertyKeys.length; index += 1) { - const key = propertyKeys[index]; - let subData = data.match(id, generalHelper.addNamespace(key, dpiConfig), null, null); - - if (formatType.singularString[property].includes(key)) { - convertSingularStrings(subData, state, key); - } else if (formatType.singularURI[property].includes(key)) { - convertSingularURI(subData, state, key, dpiConfig); - } else if (formatType.multipleURI[property].includes(key)) { - convertMultipleURI(subData, state, key, property, dpiConfig); - } else if (formatType.typedStrings[property].includes(key)) { - convertTypedString(subData, state, key); - } else if (formatType.multilingualStrings[property].includes(key)) { - convertMultilingual(subData, state, key); - } else if (formatType.conditionalProperties[property].includes(key)) { - // publisher either is an URI or a group with multiple values (name, homepage, email) - if (key === 'dct:publisher' || key === 'dct:license') { - for (let el of subData) { - if (el.object.termType === 'NamedNode') { - state[key] = {name: el.object.value, resource: el.object.value }; - } else if (el.object.termType === "BlankNode") { - - // get keys for nested values without dct'title (special format) - const nestedKeys = generalHelper.getNestedKeys(data.match(el.object, null, null, null), dpiConfig).filter(el => el !== 'dct:title'); - const nestedProperties = {}; - - // convert nested values - if (key === 'dct:license') { - const licenceTitleQuad = data.match(el.object, generalHelper.addNamespace('dct:title', dpiConfig), null, null); - for (let el of licenceTitleQuad) { - nestedProperties['dct:title'] = el.object.value; - } - } - - convertProperties(property, nestedProperties, el.object, data, nestedKeys, dpiConfig); - state[key] = nestedProperties; - } - } - } - } else if (formatType.groupedProperties[property].includes(key)) { - if (subData.size > 0) { - - state[key] = []; - // there could be multiple nodes with data for a property - for (let el of subData) { - let currentState = {} - if (key === 'skos:notation') { - // skos notation behaves differently - // there should be a typed literal given which should be seperated into @value and @type - if (el.object.value) currentState['@value'] = el.object.value; - if (el.object.datatypeString) currentState['@type'] = { name: el.object.datatypeString, resource: el.object.datatypeString }; - } else { - // some properties have a named node containing data, the value of this named node also is a value form the input form (typically @id) - if (el.object.termType === 'NamedNode') currentState['@id'] = el.object.value; - // get keys of node properties - const nestedKeys = generalHelper.getNestedKeys(data.match(el.object, null, null, null), dpiConfig); - convertProperties(property, currentState, el.object, data, nestedKeys, dpiConfig); - } - // creator not an array - if (key === 'dct:creator' || key === 'vcard:hasAddress' || key === 'skos:notation' || key === 'spdx:checksum') state[key] = currentState; - else state[key].push(currentState); - } - } - } else if (key === 'dcat:temporalResolution') { - // temporal resolution is displayed as group of input forms for each property (year, month, day, ...) - // the form provides the data as following: [ { 'Year': '...', 'Month': '...', ... } ] - // the linked data format of this property looks like this: P?Y?M?DT?H?M?S - if (subData.size > 0) { - - state[key] = {}; - - const shorts = ['Y', 'M', 'D', 'H', 'M', 'S']; - const forms = { - 0: 'Year', - 1: 'Month', - 2: 'Day', - 3: 'Hour', - 4: 'Minute', - 5: 'Second', - }; - - // should be oly one quad - let resolutionValue; - for (let el of subData) { - resolutionValue = el.object.value; - } - - // backend converts temporalResolution values without a date into seconds for time values - if (!resolutionValue.startsWith("P")) { - - // setting year, month and day to 0 - state[key][forms[0]] = 0; - state[key][forms[1]] = 0; - state[key][forms[2]] = 0; - - // converting seconds into HH:MM:SS - const data = new Date(resolutionValue * 1000).toISOString().slice(11, 19); - state[key][forms[3]] = data.slice(0, 2); - state[key][forms[4]] = data.slice(3, 5); - state[key][forms[5]] = data.slice(7, 9); - - } else { - // find index of letter for time period - // extract substring until this index - // extract number from string and set as according value for input - for (let tempIndex = 0; tempIndex < shorts.length; tempIndex += 1) { - const position = resolutionValue.indexOf(shorts[tempIndex]); // position of duration letter - const subDuration = resolutionValue.substring(0, position); // substring until position of duration letter - const value = subDuration?.match(/\d+/g)?.[0] ?? ''; // extract number - resolutionValue = resolutionValue.substring(position); // overwrite resolution string with shortened version (missing the extracted part) - state[key][forms[tempIndex]] = value; // write to result object - } - } - } - } else if (key === 'dct:identifier') { - if (subData.size > 0) { - // identifier should be provided as array of strings - // [{'@value': '...'}, {'@value': '...'}, ...] - state[key] = []; - - for (let el of subData) { - state[key].push({ '@value': el.object.value }); - } - } - } else if (key === 'dct:rights') { - // rights is conditional and gets a string - // rights always includes a type so everything is located within a blank node - // also rights is a singular property - if (subData.size > 0) { - let nodeData; - // get id of blank node and associated label data - for (let el of subData) { - const rightsBlankNode = el.object; - nodeData = data.match(rightsBlankNode, generalHelper.addNamespace('rdfs:label', dpiConfig), null, null); - for (let label of nodeData) { - if (generalHelper.isUrl(label.object.value)) state[key] = { '@type': 'url', 'rdfs:value': label.object.value }; - else state[key] = { '@type': 'text', 'rdfs:value': label.object.value }; - } - } - } - } else if (key === 'datasetID' && property !== 'datatsets') { - // id is given as complete URI - // dataset-/catalogue-id is string following the last / - state[key] = id.substr(id.lastIndexOf('/') + 1); - state['hidden_datasetIDFormHidden'] = id.substr(id.lastIndexOf('/') + 1); - } else if (key === 'dcat:catalog' && property === 'datasets') { - // datasets also have a property called dcat:catalog (not valid DCAT-AP) - // property is needed to determine catalog the dataset belongs to - if (!(subData.size > 0)) { - // bceause dcat:catalog is no valid DCAT-AP it is possible that the prefix is not resolved - // therefore it is also possible to get the data by using the shortned key - subData = data.match(id, 'dcat:catalog', null, null); + const formatType = dpiConfig.formatTypes; + + for (let index = 0; index < propertyKeys.length; index += 1) { + const key = propertyKeys[index]; + let subData = data.match( + id, + generalHelper.addNamespace(key, dpiConfig), + null, + null + ); + + if (formatType.singularString[property].includes(key)) { + convertSingularStrings(subData, state, key); + } else if (formatType.singularURI[property].includes(key)) { + convertSingularURI(subData, state, key, dpiConfig); + } else if (formatType.multipleURI[property].includes(key)) { + convertMultipleURI(subData, state, key, property, dpiConfig); + } else if (formatType.typedStrings[property].includes(key)) { + convertTypedString(subData, state, key); + } else if (formatType.multilingualStrings[property].includes(key)) { + convertMultilingual(subData, state, key); + } else if (formatType.conditionalProperties[property].includes(key)) { + // publisher either is an URI or a group with multiple values (name, homepage, email) + if (key === "dct:publisher" || key === "dct:license") { + for (let el of subData) { + if (el.object.termType === "NamedNode") { + state[key] = { name: el.object.value, resource: el.object.value }; + } else if (el.object.termType === "BlankNode") { + // get keys for nested values without dct'title (special format) + const nestedKeys = generalHelper + .getNestedKeys(data.match(el.object, null, null, null), dpiConfig) + .filter((el) => el !== "dct:title"); + const nestedProperties = {}; + + // convert nested values + if (key === "dct:license") { + const licenceTitleQuad = data.match( + el.object, + generalHelper.addNamespace("dct:title", dpiConfig), + null, + null + ); + for (let el of licenceTitleQuad) { + nestedProperties["dct:title"] = el.object.value; + } } - state[key] = ''; + convertProperties( + property, + nestedProperties, + el.object, + data, + nestedKeys, + dpiConfig + ); + state[key] = nestedProperties; + } + } + } + } else if (formatType.groupedProperties[property].includes(key)) { + if (subData.size > 0) { + state[key] = []; + // there could be multiple nodes with data for a property + for (let el of subData) { + let currentState = {}; + if (key === "skos:notation") { + // skos notation behaves differently + // there should be a typed literal given which should be seperated into @value and @type + if (el.object.value) currentState["@value"] = el.object.value; + if (el.object.datatypeString) + currentState["@type"] = { + name: el.object.datatypeString, + resource: el.object.datatypeString, + }; + } else { + // some properties have a named node containing data, the value of this named node also is a value form the input form (typically @id) + if (el.object.termType === "NamedNode") + currentState["@id"] = el.object.value; + // get keys of node properties + const nestedKeys = generalHelper.getNestedKeys( + data.match(el.object, null, null, null), + dpiConfig + ); + convertProperties( + property, + currentState, + el.object, + data, + nestedKeys, + dpiConfig + ); + } + // creator not an array + if ( + key === "dct:creator" || + key === "vcard:hasAddress" || + key === "skos:notation" || + key === "spdx:checksum" + ) + state[key] = currentState; + else state[key].push(currentState); + } + } + } else if (key === "dcat:temporalResolution") { + // temporal resolution is displayed as group of input forms for each property (year, month, day, ...) + // the form provides the data as following: [ { 'Year': '...', 'Month': '...', ... } ] + // the linked data format of this property looks like this: P?Y?M?DT?H?M?S + if (subData.size > 0) { + state[key] = {}; + + const shorts = ["Y", "M", "D", "H", "M", "S"]; + const forms = { + 0: "Year", + 1: "Month", + 2: "Day", + 3: "Hour", + 4: "Minute", + 5: "Second", + }; + + // should be oly one quad + let resolutionValue; + for (let el of subData) { + resolutionValue = el.object.value; + } - // there should only be one catalog - for (let cat of subData) { - state[key] = cat.object.value; - } - } else if (key === 'rdf:type') { - // some properties have a type which can be selected - // the type also has a namespace and therefore need to be shortened ( e.g. from https://...Individual to vcard:Individual) - if (subData.size > 0) { - state[key] = ''; - - // typically there is only on type provided for each property instance - for (let el of subData) { - state[key] = generalHelper.removeNamespace(el.object.value, dpiConfig); - } - } + // backend converts temporalResolution values without a date into seconds for time values + if (!resolutionValue.startsWith("P")) { + // setting year, month and day to 0 + state[key][forms[0]] = 0; + state[key][forms[1]] = 0; + state[key][forms[2]] = 0; + + // converting seconds into HH:MM:SS + const data = new Date(resolutionValue * 1000) + .toISOString() + .slice(11, 19); + state[key][forms[3]] = data.slice(0, 2); + state[key][forms[4]] = data.slice(3, 5); + state[key][forms[5]] = data.slice(7, 9); + } else { + // find index of letter for time period + // extract substring until this index + // extract number from string and set as according value for input + for (let tempIndex = 0; tempIndex < shorts.length; tempIndex += 1) { + const position = resolutionValue.indexOf(shorts[tempIndex]); // position of duration letter + const subDuration = resolutionValue.substring(0, position); // substring until position of duration letter + const value = subDuration?.match(/\d+/g)?.[0] ?? ""; // extract number + resolutionValue = resolutionValue.substring(position); // overwrite resolution string with shortened version (missing the extracted part) + state[key][forms[tempIndex]] = value; // write to result object + } + } + } + } else if (key === "dct:identifier") { + if (subData.size > 0) { + // identifier should be provided as array of strings + // [{'@value': '...'}, {'@value': '...'}, ...] + state[key] = []; + + for (let el of subData) { + state[key].push({ "@value": el.object.value }); + } + } + } else if (key === "dct:rights") { + // rights is conditional and gets a string + // rights always includes a type so everything is located within a blank node + // also rights is a singular property + if (subData.size > 0) { + let nodeData; + // get id of blank node and associated label data + for (let el of subData) { + const rightsBlankNode = el.object; + nodeData = data.match( + rightsBlankNode, + generalHelper.addNamespace("rdfs:label", dpiConfig), + null, + null + ); + for (let label of nodeData) { + if (generalHelper.isUrl(label.object.value)) + state[key] = { "@type": "url", "rdfs:value": label.object.value }; + else + state[key] = { + "@type": "text", + "rdfs:value": label.object.value, + }; + } } + } + } else if (key === "datasetID" && property !== "datatsets") { + // id is given as complete URI + // dataset-/catalogue-id is string following the last / + state[key] = id.substr(id.lastIndexOf("/") + 1); + state["hidden_datasetIDFormHidden"] = id.substr(id.lastIndexOf("/") + 1); + } else if (key === "dcat:catalog" && property === "datasets") { + // datasets also have a property called dcat:catalog (not valid DCAT-AP) + // property is needed to determine catalog the dataset belongs to + if (!(subData.size > 0)) { + // bceause dcat:catalog is no valid DCAT-AP it is possible that the prefix is not resolved + // therefore it is also possible to get the data by using the shortned key + subData = data.match(id, "dcat:catalog", null, null); + } + + state[key] = ""; + + // there should only be one catalog + for (let cat of subData) { + state[key] = cat.object.value; + } + } else if (key === "rdf:type") { + // some properties have a type which can be selected + // the type also has a namespace and therefore need to be shortened ( e.g. from https://...Individual to vcard:Individual) + if (subData.size > 0) { + state[key] = ""; + + // typically there is only on type provided for each property instance + for (let el of subData) { + state[key] = generalHelper.removeNamespace( + el.object.value, + dpiConfig + ); + } + } + } else if (key === "pv:DistributionType") { + // Displays the Tag of the Distribution TODO + console.log(state[key]); } + } } //----------------------------------------------------------------------------------------------------- // basic conversion methods for different categories of data //----------------------------------------------------------------------------------------------------- -// seems at first to be unnecessary but if we want to convert nested properties as well, we need these +// seems at first to be unnecessary but if we want to convert nested properties as well, we need these // methods (especially to provide the correct parent URI) /** - * - * @param {*} data - * @param {*} state - * @param {*} key + * + * @param {*} data + * @param {*} state + * @param {*} key */ function convertSingularStrings(data, state, key) { - if (data.size > 0) { - state[key] = ''; + if (data.size > 0) { + state[key] = ""; - for (let el of data) { - state[key] = el.object.value; - } + for (let el of data) { + state[key] = el.object.value; } + } } /** - * - * @param {*} data - * @param {*} state - * @param {*} key + * + * @param {*} data + * @param {*} state + * @param {*} key */ function convertSingularURI(data, state, key, dpiConfig) { - - const formatType = dpiConfig.formatTypes; - - if (data.size > 0) { - state[key] = ''; - - for (let el of data) { - const value = el.object.value; - - if (value.startsWith('mailto:')) { - state[key] = value.replace('mailto:', ''); - } else { - if (formatType.URIformat.voc.includes(key)) state[key] = { name: value, resource: value }; - else if (formatType.URIformat.string.includes(key)) state[key] = value; - else state[key] = { '@id': value }; - } - } + const formatType = dpiConfig.formatTypes; + + if (data.size > 0) { + state[key] = ""; + + for (let el of data) { + const value = el.object.value; + + if (value.startsWith("mailto:")) { + state[key] = value.replace("mailto:", ""); + } else { + if (formatType.URIformat.voc.includes(key)) + state[key] = { name: value, resource: value }; + else if (formatType.URIformat.string.includes(key)) state[key] = value; + else state[key] = { "@id": value }; + } } + } } /** - * - * @param {*} data - * @param {*} state - * @param {*} key + * + * @param {*} data + * @param {*} state + * @param {*} key */ function convertMultipleURI(data, state, key, property, dpiConfig) { - // there are two different formats the frontend need to deliver multiple URIs - // 1: [ "URI1", "URI2" ] - // 2: [ { "@id": "URI1" }, { "@id": "URI2" } ] - - const formatType = dpiConfig.formatTypes; - - if (data.size > 0) { - state[key] = []; - for (let el of data) { - if (formatType.URIformat.voc.includes(key)) state[key].push({ name: el.object.value, resource: el.object.value }); - else if (formatType.URIformat.string.includes(key)) state[key].push(el.object.value); - else state[key].push({ '@id': el.object.value }); - } + // there are two different formats the frontend need to deliver multiple URIs + // 1: [ "URI1", "URI2" ] + // 2: [ { "@id": "URI1" }, { "@id": "URI2" } ] + + const formatType = dpiConfig.formatTypes; + + if (data.size > 0) { + state[key] = []; + for (let el of data) { + if (formatType.URIformat.voc.includes(key)) + state[key].push({ name: el.object.value, resource: el.object.value }); + else if (formatType.URIformat.string.includes(key)) + state[key].push(el.object.value); + else state[key].push({ "@id": el.object.value }); } + } } /** - * - * @param {*} data - * @param {*} state - * @param {*} key + * + * @param {*} data + * @param {*} state + * @param {*} key */ function convertTypedString(data, state, key) { - // some properties have a type - // normally this type is not used within the forntend form and therefore won't be saved to frontend values - if (data.size > 0) { - state[key] = ''; - for (let el of data) { - if (key === 'dcat:spatialResolutionInMeters' || key === 'dcat:byteSize') state[key] = el.object.value; - else if (key === 'dcat:startDate' || key === 'dcat:endDate') { - state[key] = el.object.value; - } - else { - let dateType; - if (el.object.value.includes('T')) dateType = 'dateTime'; - else dateType = 'date'; - - state[key] = { '@type': dateType, '@value': el.object.value }; - } - } + // some properties have a type + // normally this type is not used within the forntend form and therefore won't be saved to frontend values + if (data.size > 0) { + state[key] = ""; + for (let el of data) { + if (key === "dcat:spatialResolutionInMeters" || key === "dcat:byteSize") + state[key] = el.object.value; + else if (key === "dcat:startDate" || key === "dcat:endDate") { + state[key] = el.object.value; + } else if (key === "pv:DistributionType") { + state[key] = el.object.value; + } else { + let dateType; + if (el.object.value.includes("T")) dateType = "dateTime"; + else dateType = "date"; + + state[key] = { "@type": dateType, "@value": el.object.value }; + } } - + } } /** @@ -346,22 +435,23 @@ function convertTypedString(data, state, key) { * @param {*} key Name of current property (e.g: 'dct:title') */ function convertMultilingual(data, state, key) { - // multilingual data is always stored within an array containing object with the value and it's language - // [ {'@value': '...', '@language': '...'}, ...] - if (data.size > 0) { - state[key] = []; - - for (let el of data) { - if (!el.object.language.includes("-")) { // machine translation language tags look like this "fi-t-en-t0-mtec" and should not be included - const currentElement = {}; - currentElement['@value'] = el.object.value; // actual value - currentElement['@language'] = el.object.language; // language of value - state[key].push(currentElement); - } - } + // multilingual data is always stored within an array containing object with the value and it's language + // [ {'@value': '...', '@language': '...'}, ...] + if (data.size > 0) { + state[key] = []; + + for (let el of data) { + if (!el.object.language.includes("-")) { + // machine translation language tags look like this "fi-t-en-t0-mtec" and should not be included + const currentElement = {}; + currentElement["@value"] = el.object.value; // actual value + currentElement["@language"] = el.object.language; // language of value + state[key].push(currentElement); + } } + } } export default { - convertToInput, + convertToInput, }; diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/views/InputPage.vue b/packages/piveau-hub-ui-modules/lib/data-provider-interface/views/InputPage.vue index 5ca7d86540bea456e6d3e8e95d313b90216857af..ca65d52495e8e77df7e38a9d800608f13156562f 100644 --- a/packages/piveau-hub-ui-modules/lib/data-provider-interface/views/InputPage.vue +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/views/InputPage.vue @@ -3,7 +3,7 @@ <div ref="fkInputContainer" class="inputContainer" v-if="isInput"> <div class="formContainer formkit position-relative"> - <!-- <details>{{ formValues }}</details> --> + <details>{{ formValues }}</details> <FormKit type="form" v-model="formValues" :actions="false" :plugins="[stepPlugin]" id="dpiForm" @change="saveFormValues({ property: property, page: page, distid: id, values: formValues })" @click="saveFormValues({ property: property, page: page, distid: id, values: formValues })" @submit.prevent="" diff --git a/packages/piveau-hub-ui-modules/lib/data-provider-interface/views/OverviewPage/Properties/StringProp.vue b/packages/piveau-hub-ui-modules/lib/data-provider-interface/views/OverviewPage/Properties/StringProp.vue index 03bc6534fe8fd50fc91bdf2fcc9c4cff3e6fcdbf..a0554b4cf4566a6d9b060c8a39eaf570733a3ae7 100644 --- a/packages/piveau-hub-ui-modules/lib/data-provider-interface/views/OverviewPage/Properties/StringProp.vue +++ b/packages/piveau-hub-ui-modules/lib/data-provider-interface/views/OverviewPage/Properties/StringProp.vue @@ -9,8 +9,6 @@ <!-- SINGULAR STRING --> <td v-if="value.type === 'singularString'"> - - <span v-if="data[property]['@type'] != '' && property === 'dct:rights'"> {{ data[property]['rdfs:label'] }}</span> <span v-if="property != 'dct:rights'"> {{ data[property] }} <span v-if="property === 'dcat:spatialResolutionInMeters'">Meters</span> diff --git a/packages/piveau-hub-ui-modules/lib/datasetDetails/properties/DatasetDetailsProperty.vue b/packages/piveau-hub-ui-modules/lib/datasetDetails/properties/DatasetDetailsProperty.vue index 19f632134f9d08e8e080c34cf85a0b2d3a1d6c4d..4ad15f552c2cf866a36b84069f422319236e9ae3 100644 --- a/packages/piveau-hub-ui-modules/lib/datasetDetails/properties/DatasetDetailsProperty.vue +++ b/packages/piveau-hub-ui-modules/lib/datasetDetails/properties/DatasetDetailsProperty.vue @@ -157,6 +157,8 @@ export default { }); }, interpretTranslateKey(key, prefix) { + console.log(key); + if (key === '') return ''; if (key.startsWith('/')) return `${this.i18n.global.t(key.substring(1))}:`; if (key.startsWith('\'')) return key.substring(1, key.length - 1); diff --git a/packages/piveau-hub-ui-modules/lib/form/inputDefinitions.ts b/packages/piveau-hub-ui-modules/lib/form/inputDefinitions.ts index 3468ec11e376792751be35f42f5e70bcab3a2012..b5e5dc9b5f19e237c955cc11d46b62aa093c9a08 100644 --- a/packages/piveau-hub-ui-modules/lib/form/inputDefinitions.ts +++ b/packages/piveau-hub-ui-modules/lib/form/inputDefinitions.ts @@ -2,6 +2,7 @@ import Repeatable from "./Repeatable.vue"; import FormKitGroup from "./FormKitGroup.vue"; import { FormKitLibrary } from "@formkit/core"; import AutocompleteInput from "../data-provider-interface/components/AutocompleteInput.vue"; +import WidgetInput from "../data-provider-interface/components/WidgetInput.vue"; import FileUpload from "../data-provider-interface/components/FileUpload.vue"; import UniqueIdentifierInput from "../data-provider-interface/components/UniqueIdentifierInput.vue"; import SpatialInput from "../data-provider-interface/components/SpatialInput.vue"; @@ -10,49 +11,49 @@ import SimpleSelect from "../data-provider-interface/components/SimpleSelect.vue import SimpleInput from "../data-provider-interface/components/SimpleInput.vue"; import SimpleAccessURLInput from "../data-provider-interface/components/SimpleAccessURLInput.vue"; - export default { - - repeatable: { - type: 'list', - component: Repeatable - }, - id: { - type: 'input', - component: UniqueIdentifierInput, - }, - auto: { - type: 'group', - component: AutocompleteInput - }, - fileupload: { - type: 'group', - component: FileUpload - }, - spatialinput: { - type: 'group', - component: SpatialInput - }, - formkitGroup: { - type: 'group', - component: FormKitGroup - }, - simpleConditional: { - type: 'group', - component: ConditionalInput - }, - simpleSelect: { - type: 'input', - component: SimpleSelect, - }, - simpleInput: { - type: 'input', - component: SimpleInput, - }, - simpleAccessURLInput: { - type: 'group', - component: SimpleAccessURLInput, - }, - - -} as FormKitLibrary; \ No newline at end of file + repeatable: { + type: "list", + component: Repeatable, + }, + widget: { + type: "input", + component: WidgetInput, + }, + id: { + type: "input", + component: UniqueIdentifierInput, + }, + auto: { + type: "group", + component: AutocompleteInput, + }, + fileupload: { + type: "group", + component: FileUpload, + }, + spatialinput: { + type: "group", + component: SpatialInput, + }, + formkitGroup: { + type: "group", + component: FormKitGroup, + }, + simpleConditional: { + type: "group", + component: ConditionalInput, + }, + simpleSelect: { + type: "input", + component: SimpleSelect, + }, + simpleInput: { + type: "input", + component: SimpleInput, + }, + simpleAccessURLInput: { + type: "group", + component: SimpleAccessURLInput, + }, +} as FormKitLibrary;