From 2e5771f2b7920fc246bf3c7e82b8238ed72ac8bb Mon Sep 17 00:00:00 2001 From: RoryPTB <47696929+RoryPTB@users.noreply.github.com> Date: Wed, 17 Jan 2024 15:11:41 +0100 Subject: [PATCH 1/2] Metadata component formatting --- src/components/DiscoveryMetadataForm.vue | 1305 +++++++++++----------- 1 file changed, 650 insertions(+), 655 deletions(-) diff --git a/src/components/DiscoveryMetadataForm.vue b/src/components/DiscoveryMetadataForm.vue index 627fb0f..f1e8ca9 100644 --- a/src/components/DiscoveryMetadataForm.vue +++ b/src/components/DiscoveryMetadataForm.vue @@ -217,7 +217,7 @@ import VueDatePicker from '@vuepic/vue-datepicker'; import '@vuepic/vue-datepicker/dist/main.css'; import { defineComponent, ref, onMounted, watch } from 'vue'; -import { VCard, VCardTitle, VCardText, VCardItem, VForm, VTextarea, VBtn, VListGroup } from 'vuetify/lib/components/index.mjs'; +import { VCard, VForm, VBtn } from 'vuetify/lib/components/index.mjs'; let oapi = window.VUE_APP_OAPI; let mqtt = window.MQTT_HOST; @@ -230,13 +230,8 @@ export default defineComponent({ BboxEditor, VueDatePicker, VCard, - VCardTitle, - VCardText, - VCardItem, - VTextarea, VForm, - VBtn, - VListGroup + VBtn }, setup() { // Reactive variables @@ -295,759 +290,759 @@ export default defineComponent({ identifier: value => /^urn:x-wmo:md:[a-z]{3}:[a-z0-9_-]+:[a-z0-9_-]+[a-z0-9:-]*$/.test(value) || 'Invalid identifier. Must start with \'urn:x-wmo:md:\'', topicHierarchy: value => /^[a-z]{3}\/[_a-z-]+\/(data|metadata|reports)\/(core|recommended)\/[\\w]+\/[\\w-]+\/[\\w]*$/.test(value) || 'Invalid topic hierarchy. Follow the specified pattern.', keywords: value => Array.isArray(value) && value.length >= 3 || 'Keywords must be an array with at least 3 items.', - }; - - // Methods - - // Fetches a list of metadata items from the OAPI and updates the list - const loadList = async (id) => { - try { - const response = await fetch( - `${oapi}/collections/discovery-metadata/items`, { method: 'get' } - ); - if (!response.ok) { - throw new Error('Network response was not ok') + }; + + // Methods + + // Fetches a list of metadata items from the OAPI and updates the list + const loadList = async (id) => { + try { + const response = await fetch( + `${oapi}/collections/discovery-metadata/items`, { method: 'get' } + ); + if (!response.ok) { + throw new Error('Network response was not ok') + } + const responseData = await response.json(); + responseData.features.forEach((item) => { + items.value.push(item.id); + }); + items.value.push('Create New...'); + specified.value = false; + } catch (error) { + console.error(error); + items.value = ['Create New...']; + message.value = 'Error loading discovery metadata list.'; } - const responseData = await response.json(); - responseData.features.forEach((item) => { - items.value.push(item.id); - }); - items.value.push('Create New...'); - specified.value = false; - } catch (error) { - console.error(error); - items.value = ['Create New...']; - message.value = 'Error loading discovery metadata list.'; - } - if (items.value.includes(id)) { - identifier.value = id; - specified.value = true; - await loadMetadata(); - } else if (id === 'new') { - identifier.value = 'Create New...'; - specified.value = true; - await loadMetadata(); - } - }; - - // Loads the metadata for a specific item or creates a new metadata structure - // Also fetches additional required data like country ISO codes - const loadMetadata = async () => { - loaded.value = false; - working.value = true; - message.value = "Working..."; - form.value.modified = false; - - // Fetch country ISO codes for injection - try { - const response = await fetch("https://api.worldbank.org/v2/country?format=json&per_page=500", { method: "get" }); - if (!response.ok) { - throw new Error('Network response was not ok') + if (items.value.includes(id)) { + identifier.value = id; + specified.value = true; + await loadMetadata(); + } else if (id === 'new') { + identifier.value = 'Create New...'; + specified.value = true; + await loadMetadata(); } - const responseData = await response.json(); - responseData[1].forEach((item) => { - form.country_codes.push(item.id); - }); - } catch (error) { - console.error(error); - } + }; - // Load a blank new discovery metadata file - if (identifier.value === "Create New...") { - isNew.value = true; // As metadata file is new - defaults.value = {}; - form.value.bounds = [0]; - form.value.initialized = false; - form.value.manual_ids = false; - validated.value = false; - } - // Load the corresponding discovery metadata file - else { - isNew.value = false; // As metadata file is already exists - validated.value = true; + // Loads the metadata for a specific item or creates a new metadata structure + // Also fetches additional required data like country ISO codes + const loadMetadata = async () => { + loaded.value = false; + working.value = true; + message.value = "Working..."; + form.value.modified = false; + + // Fetch country ISO codes for injection try { - const response = await fetch(`${oapi}/collections/discovery-metadata/items/${identifier.value}`, { method: "get" }); + const response = await fetch("https://api.worldbank.org/v2/country?format=json&per_page=500", { method: "get" }); if (!response.ok) { throw new Error('Network response was not ok') } const responseData = await response.json(); - defaults.value = demodulateModel(responseData); - form.value.initialized = true; + responseData[1].forEach((item) => { + form.country_codes.push(item.id); + }); } catch (error) { - console.log(error); - message.value = "Error loading discovery metadata file."; + console.error(error); } - } - // Finally, update the UI - if (!message.value.includes("Error")) { - model.value = defaults.value; - loaded.value = true; - } - working.value = false; - message.value = "Select existing discovery metadata file or create new."; - }; - - // Resets the metadata form to the default state - const resetMetadata = () => { - try { - model.value = defaults.value; - } - catch (error) { - console.error(error); - } - loadGeometry(); - form.value.initialized = false; - }; + // Load a blank new discovery metadata file + if (identifier.value === "Create New...") { + isNew.value = true; // As metadata file is new + defaults.value = {}; + form.value.bounds = [0]; + form.value.initialized = false; + form.value.manual_ids = false; + validated.value = false; + } + // Load the corresponding discovery metadata file + else { + isNew.value = false; // As metadata file is already exists + validated.value = true; + try { + const response = await fetch(`${oapi}/collections/discovery-metadata/items/${identifier.value}`, { method: "get" }); + if (!response.ok) { + throw new Error('Network response was not ok') + } + const responseData = await response.json(); + defaults.value = demodulateModel(responseData); + form.value.initialized = true; + } catch (error) { + console.log(error); + message.value = "Error loading discovery metadata file."; + } + } - // Validate the current metadata, such as checking the topic hierarchy and WCMP2 schema - const validateMetadata = async () => { - const isValid = true; + // Finally, update the UI + if (!message.value.includes("Error")) { + model.value = defaults.value; + loaded.value = true; + } + working.value = false; + message.value = "Select existing discovery metadata file or create new."; + }; - if (isValid) { + // Resets the metadata form to the default state + const resetMetadata = () => { try { - const response = await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - "inputs": { - "record": modulateModel(model.value) - } + model.value = defaults.value; + } + catch (error) { + console.error(error); + } + loadGeometry(); + form.value.initialized = false; + }; + + // Validate the current metadata, such as checking the topic hierarchy and WCMP2 schema + const validateMetadata = async () => { + const isValid = true; + + if (isValid) { + try { + const response = await fetch(`${oapi}/processes/pywcmp-wis2-wcmp2-ets/execution`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + "inputs": { + "record": modulateModel(model.value) + } + }) }) - }) - if (!response.ok) { - throw new Error('Network response was not ok') - } + if (!response.ok) { + throw new Error('Network response was not ok') + } - const responseData = await response.json() - console.log(responseData) + const responseData = await response.json() + console.log(responseData) - if ("code" in responseData) { + if ("code" in responseData) { + isValid.value = false + alert(responseData.description) + } + } catch (error) { + console.log(error) isValid.value = false - alert(responseData.description) + alert("Error validating WCMP2 schema.") } - } catch (error) { - console.log(error) - isValid.value = false - alert("Error validating WCMP2 schema.") } - } - // If network response valid, update the UI - if (isValid) { - validated.value = true; - } - }; - - // Generates a downloadable JSON file from the current metadata state - const downloadMetadata = () => { - const content = encodeURI(JSON.stringify(modulateModel(model.value), null, 4)) - const element = document.createElement("a") - element.href = "data:attachment/text," + content - element.target = "_blank" - element.download = "discovery-metadata.json" - element.click() - }; - - // Submits the current metadata to the OAPI endpoint - const submitMetadata = async () => { - try { - let response; - if (isNew.value) { - // Post to create new metadata - response = await fetch(`${oapi.value}/collections/discovery-metadata/items`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(modulateModel(model.value)) - }) - } else { - // Put to update existing metadata - response = await fetch(`${oapi.value}/collections/discovery-metadata/items/${defaults.value.settings.identifier}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(modulateModel(model.value)) - }) + // If network response valid, update the UI + if (isValid) { + validated.value = true; } + }; - if (!response.ok) { - throw new Error('Network response was not ok') - } + // Generates a downloadable JSON file from the current metadata state + const downloadMetadata = () => { + const content = encodeURI(JSON.stringify(modulateModel(model.value), null, 4)) + const element = document.createElement("a") + element.href = "data:attachment/text," + content + element.target = "_blank" + element.download = "discovery-metadata.json" + element.click() + }; - // If post/put successful, redirect to home page - if ((isNew.value && response.status === 201) || (!isNew.value && response.status === 204)) { - window.location.href = "/" - } - // // Otherwise, show an alert with the description of the response - else { - const responseData = await response.json() - alert(responseData.description) + // Submits the current metadata to the OAPI endpoint + const submitMetadata = async () => { + try { + let response; + if (isNew.value) { + // Post to create new metadata + response = await fetch(`${oapi.value}/collections/discovery-metadata/items`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(modulateModel(model.value)) + }) + } else { + // Put to update existing metadata + response = await fetch(`${oapi.value}/collections/discovery-metadata/items/${defaults.value.settings.identifier}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(modulateModel(model.value)) + }) + } + + if (!response.ok) { + throw new Error('Network response was not ok') + } + + // If post/put successful, redirect to home page + if ((isNew.value && response.status === 201) || (!isNew.value && response.status === 204)) { + window.location.href = "/" + } + // // Otherwise, show an alert with the description of the response + else { + const responseData = await response.json() + alert(responseData.description) + } + } catch (error) { + console.log(error) + alert(isNew.value ? "Error adding discovery metadata." : "Error updating discovery metadata.") } - } catch (error) { - console.log(error) - alert(is_new.value ? "Error adding discovery metadata." : "Error updating discovery metadata.") } - } const loadGeometry = () => { - updateModel({ "fullKey": "origin.northLatitude" }) + updateModel({ "fullKey": "origin.northLatitude" }) - document.querySelectorAll(".v-btn--icon").forEach((element) => { - element.tabIndex = -1 - }) + document.querySelectorAll(".v-btn--icon").forEach((element) => { + element.tabIndex = -1 + }) - const settingsKeywordsElement = document.getElementById("settings-keywords") - settingsKeywordsElement.addEventListener("keyup", (event) => { - if (event.key === " ") { - const ev = new KeyboardEvent('keydown', { - altKey: false, - bubbles: true, - cancelBubble: false, - cancelable: true, - charCode: 0, - code: "Enter", - composed: true, - ctrlKey: false, - currentTarget: null, - defaultPrevented: true, - detail: 0, - eventPhase: 0, - isComposing: false, - isTrusted: true, - key: "Enter", - keyCode: 13, - location: 0, - metaKey: false, - repeat: false, - returnValue: false, - shiftKey: false, - type: "keydown", - which: 13 - }) - settingsKeywordsElement.dispatchEvent(ev) - } - }) - }; - - // Update bounds of the form and origin of the model - const updateGeometry = (input) => { - console.log('Updating geometry'); - if (input.length === 4) { - form.value.bounds = input; - if (!("origin" in model.value)) { - model.value["origin"] = {}; - }; - model.value.origin["northLatitude"] = input[0] - model.value.origin["eastLongitude"] = input[1] - model.value.origin["southLatitude"] = input[2] - model.value.origin["westLongitude"] = input[4] - } - }; + const settingsKeywordsElement = document.getElementById("settings-keywords") + settingsKeywordsElement.addEventListener("keyup", (event) => { + if (event.key === " ") { + const ev = new KeyboardEvent('keydown', { + altKey: false, + bubbles: true, + cancelBubble: false, + cancelable: true, + charCode: 0, + code: "Enter", + composed: true, + ctrlKey: false, + currentTarget: null, + defaultPrevented: true, + detail: 0, + eventPhase: 0, + isComposing: false, + isTrusted: true, + key: "Enter", + keyCode: 13, + location: 0, + metaKey: false, + repeat: false, + returnValue: false, + shiftKey: false, + type: "keydown", + which: 13 + }) + settingsKeywordsElement.dispatchEvent(ev) + } + }) + }; - // Updates the form and model objects based on the provided event - const updateModel = ($event) => { - if ($event.fullKey !== "origin.northLatitude") { - form.value.modified = true; + // Update bounds of the form and origin of the model + const updateGeometry = (input) => { + console.log('Updating geometry'); + if (input.length === 4) { + form.value.bounds = input; + if (!("origin" in model.value)) { + model.value["origin"] = {}; + }; + model.value.origin["northLatitude"] = input[0] + model.value.origin["eastLongitude"] = input[1] + model.value.origin["southLatitude"] = input[2] + model.value.origin["westLongitude"] = input[4] + } }; - // Pre-fill form with automatic values - if (!form.value.initialized) { - const today = new Date(); - model.value.origin["dateStarted"] = today.toISOString().split('T')[0]; - model.value.origin["dateEnded"] = null; - - model.value.poc.individual = null; - model.value.poc.positionName = null; - model.value.poc.deliveryPoint = null; - model.value.poc.postalCode = null; - model.value.poc.hoursOfService = - `0900h - 1700h ${today.toLocaleTimeString('en-us', { timeZoneName: 'short' }).split(' ')[2]}`; - - model.value.distrib.individual = null; - model.value.distrib.positionName = null; - model.value.distrib.deliveryPoint = null; - model.value.distrib.postalCode = null; - model.value.distrib.hoursOfService = - `0900h - 1700h ${today.toLocaleTimeString('en-us', { timeZoneName: 'short' }).split(' ')[2]}`; - - form.value.initialized = true; - } + // Updates the form and model objects based on the provided event + const updateModel = ($event) => { + if ($event.fullKey !== "origin.northLatitude") { + form.value.modified = true; + }; - // Disable editing identifiers if manually changed - if ( - ($event.fullKey === "settings.identifier") || - ($event.fullKey === "settings.topicHierarchy") - ) { - form.value.manual_ids = true; - validated.value = false; - } + // Pre-fill form with automatic values + if (!form.value.initialized) { + const today = new Date(); + model.value.origin["dateStarted"] = today.toISOString().split('T')[0]; + model.value.origin["dateEnded"] = null; + + model.value.poc.individual = null; + model.value.poc.positionName = null; + model.value.poc.deliveryPoint = null; + model.value.poc.postalCode = null; + model.value.poc.hoursOfService = + `0900h - 1700h ${today.toLocaleTimeString('en-us', { timeZoneName: 'short' }).split(' ')[2]}`; + + model.value.distrib.individual = null; + model.value.distrib.positionName = null; + model.value.distrib.deliveryPoint = null; + model.value.distrib.postalCode = null; + model.value.distrib.hoursOfService = + `0900h - 1700h ${today.toLocaleTimeString('en-us', { timeZoneName: 'short' }).split(' ')[2]}`; - // Auto-fill topic identifiers - if ( - ($event.fullKey === "poc.country") || - ($event.fullKey === "origin.centre_id") || - ($event.fullKey === "properties.wmo:dataPolicy") - ) { + form.value.initialized = true; + } - // Only auto-fill if all components are entered, but not manually edited + // Disable editing identifiers if manually changed if ( - ("country" in model.value.distrib) && - (model.value.distrib.country.length > 0) && - ("centre_id" in model.value.origin) && - (model.value.origin.centre_id.length > 0) && - ("wmo:dataPolicy" in model.value.properties) && - !form.value.manual_ids + ($event.fullKey === "settings.identifier") || + ($event.fullKey === "settings.topicHierarchy") ) { + form.value.manual_ids = true; validated.value = false; - model.value.settings.identifier = - `urn:x-wmo:md:${model.value.distrib.country.toLowerCase()}:${model.value.origin.centre_id.toLowerCase()}:weather-observations`; - model.value.settings.topicHierarchy = - `${model.value.distrib.country.toLowerCase()}/${model.value.origin.centre_id.toLowerCase()}/data/${model.value.properties["wmo:dataPolicy"]}/weather/observations`; } - } - // Null out stop date if empty - if ($event.fullKey === "origin.dateStopped") { + // Auto-fill topic identifiers if ( - (!("dateStopped" in model.value.origin)) || - (model.value.origin.dateStopped === null) || - (model.value.origin.dateStopped === "") + ($event.fullKey === "poc.country") || + ($event.fullKey === "origin.centre_id") || + ($event.fullKey === "properties.wmo:dataPolicy") ) { - model.value.origin["dateStopped"] = null; - } - } - // Update map if coordinates are changed - if ( - ($event.fullKey === "origin.northLatitude") || - ($event.fullKey === "origin.eastLongitude") || - ($event.fullKey === "origin.southLatitude") || - ($event.fullKey === "origin.westLongitude") - ) { - try { + // Only auto-fill if all components are entered, but not manually edited if ( - ((parseFloat(model.value.origin.northLatitude) >= -90) && (parseFloat(model.value.origin.northLatitude) <= 90)) && - ((parseFloat(model.value.origin.eastLongitude) >= -180) && (parseFloat(model.value.origin.eastLongitude) <= 180)) && - ((parseFloat(model.value.origin.southLatitude) >= -90) && (parseFloat(model.value.origin.southLatitude) <= 90)) && - ((parseFloat(model.value.origin.westLongitude) >= -180) && (parseFloat(model.value.origin.westLongitude) <= 180)) + ("country" in model.value.distrib) && + (model.value.distrib.country.length > 0) && + ("centre_id" in model.value.origin) && + (model.value.origin.centre_id.length > 0) && + ("wmo:dataPolicy" in model.value.properties) && + !form.value.manual_ids ) { - form.value.bounds = [ - parseFloat(model.value.origin.northLatitude), - parseFloat(model.value.origin.eastLongitude), - parseFloat(model.value.origin.southLatitude), - parseFloat(model.value.origin.westLongitude) - ]; + validated.value = false; + model.value.settings.identifier = + `urn:x-wmo:md:${model.value.distrib.country.toLowerCase()}:${model.value.origin.centre_id.toLowerCase()}:weather-observations`; + model.value.settings.topicHierarchy = + `${model.value.distrib.country.toLowerCase()}/${model.value.origin.centre_id.toLowerCase()}/data/${model.value.properties["wmo:dataPolicy"]}/weather/observations`; } } - catch { - form.value.bounds = [0]; + + // Null out stop date if empty + if ($event.fullKey === "origin.dateStopped") { + if ( + (!("dateStopped" in model.value.origin)) || + (model.value.origin.dateStopped === null) || + (model.value.origin.dateStopped === "") + ) { + model.value.origin["dateStopped"] = null; + } } - } - // Auto-fill distributor if copy is selected - if ($event.fullKey === "distrib.duplicateFromContact") { - if (model.value.distrib.duplicateFromContact) { - const tmp = JSON.parse(JSON.stringify(model.value.poc)) - tmp["duplicateFromContact"] = true; - model.value.distrib = tmp; + // Update map if coordinates are changed + if ( + ($event.fullKey === "origin.northLatitude") || + ($event.fullKey === "origin.eastLongitude") || + ($event.fullKey === "origin.southLatitude") || + ($event.fullKey === "origin.westLongitude") + ) { + try { + if ( + ((parseFloat(model.value.origin.northLatitude) >= -90) && (parseFloat(model.value.origin.northLatitude) <= 90)) && + ((parseFloat(model.value.origin.eastLongitude) >= -180) && (parseFloat(model.value.origin.eastLongitude) <= 180)) && + ((parseFloat(model.value.origin.southLatitude) >= -90) && (parseFloat(model.value.origin.southLatitude) <= 90)) && + ((parseFloat(model.value.origin.westLongitude) >= -180) && (parseFloat(model.value.origin.westLongitude) <= 180)) + ) { + form.value.bounds = [ + parseFloat(model.value.origin.northLatitude), + parseFloat(model.value.origin.eastLongitude), + parseFloat(model.value.origin.southLatitude), + parseFloat(model.value.origin.westLongitude) + ]; + } + } + catch { + form.value.bounds = [0]; + } } - else { - model.value.distrib = {}; + + // Auto-fill distributor if copy is selected + if ($event.fullKey === "distrib.duplicateFromContact") { + if (model.value.distrib.duplicateFromContact) { + const tmp = JSON.parse(JSON.stringify(model.value.poc)) + tmp["duplicateFromContact"] = true; + model.value.distrib = tmp; + } + else { + model.value.distrib = {}; + } } - } - // Automatically correct case - if ($event.fullKey === "properties.language") { - model.value.properties.language = model.value.properties.language.toLowerCase(); - } - if ($event.fullKey === "origin.centre_id") { - model.value.origin.centre_id = model.value.origin.centre_id.toLowerCase(); - } - if ($event.fullKey === "poc.email") { - model.value.poc.email = model.value.poc.email.toLowerCase(); - } - if ($event.fullKey === "poc.postalCode") { - model.value.poc.postalCode = model.value.poc.postalCode.toUpperCase(); - } - if ($event.fullKey === "distrib.email") { - model.value.distrib.email = model.value.distrib.email.toLowerCase(); - } - if ($event.fullKey === "distrib.postalCode") { - model.value.distrib.postalCode = model.value.distrib.postalCode.toUpperCase(); - } - }; + // Automatically correct case + if ($event.fullKey === "properties.language") { + model.value.properties.language = model.value.properties.language.toLowerCase(); + } + if ($event.fullKey === "origin.centre_id") { + model.value.origin.centre_id = model.value.origin.centre_id.toLowerCase(); + } + if ($event.fullKey === "poc.email") { + model.value.poc.email = model.value.poc.email.toLowerCase(); + } + if ($event.fullKey === "poc.postalCode") { + model.value.poc.postalCode = model.value.poc.postalCode.toUpperCase(); + } + if ($event.fullKey === "distrib.email") { + model.value.distrib.email = model.value.distrib.email.toLowerCase(); + } + if ($event.fullKey === "distrib.postalCode") { + model.value.distrib.postalCode = model.value.distrib.postalCode.toUpperCase(); + } + }; - // Transforms the form data into a structured object, handling properties like "id", "conformsTo", and "time" - const modulateModel = (input) => { - const today = new Date(); - const output = {}; + // Transforms the form data into a structured object, handling properties like "id", "conformsTo", and "time" + const modulateModel = (input) => { + const today = new Date(); + const output = {}; - // "id" - output["id"] = input.settings.identifier; + // "id" + output["id"] = input.settings.identifier; - // "conformsTo" - output["conformsTo"] = [schema.value.version]; + // "conformsTo" + output["conformsTo"] = [schema.value.version]; - // "time" - output["time"] = {} - if (input.origin.dateStopped == null) { - output.time["interval"] = [input.origin.dateStarted.split(" ")[0], null] - } - else if (input.origin.dateStopped === input.origin.dateStarted) { - if (input.origin.dateStarted.split(" ").length > 1) { - const chunk = input.origin.dateStarted.split(" ") - output.time["timestamp"] = `${chunk[0]}T${chunk[1]}Z` + // "time" + output["time"] = {} + if (input.origin.dateStopped == null) { + output.time["interval"] = [input.origin.dateStarted.split(" ")[0], null] + } + else if (input.origin.dateStopped === input.origin.dateStarted) { + if (input.origin.dateStarted.split(" ").length > 1) { + const chunk = input.origin.dateStarted.split(" ") + output.time["timestamp"] = `${chunk[0]}T${chunk[1]}Z` + } + else { + output.time["timestamp"] = input.origin.dateStarted + } } else { - output.time["timestamp"] = input.origin.dateStarted + output.timestamp["interval"] = [input.origin.dateStarted.split(" ")[0], input.origin.dateStopped.split(" ")[0]] } - } - else { - output.timestamp["interval"] = [input.origin.dateStarted.split(" ")[0], input.origin.dateStopped.split(" ")[0]] - } - // "type" - output["type"] = "Feature" - - // "geometry" - output["geometry"] = {} - output["geometry"]["type"] = "Polygon" - output["geometry"]["coordinates"] = [[ - [input.origin.westLongitude, input.origin.southLatitude], - [input.origin.westLongitude, input.origin.northLatitude], - [input.origin.eastLongitude, input.origin.northLatitude], - [input.origin.eastLongitude, input.origin.southLatitude], - [input.origin.westLongitude, input.origin.southLatitude] - ]] - - // "properties" - output["properties"] = input.properties - output["properties"]["type"] = "dataset" - output["properties"]["wmo:topicHierarchy"] = `origin/a/wis2/${input.settings.topicHierarchy}` - output["properties"]["wmo:dataPolicy"] = input.settings["wmo:dataPolicy"] - output["properties"]["updated"] = today.toISOString().split('T')[0] - if (!("created" in output["properties"])) { - output["properties"]["created"] = today.toISOString().split('T')[0] - } + // "type" + output["type"] = "Feature" + + // "geometry" + output["geometry"] = {} + output["geometry"]["type"] = "Polygon" + output["geometry"]["coordinates"] = [[ + [input.origin.westLongitude, input.origin.southLatitude], + [input.origin.westLongitude, input.origin.northLatitude], + [input.origin.eastLongitude, input.origin.northLatitude], + [input.origin.eastLongitude, input.origin.southLatitude], + [input.origin.westLongitude, input.origin.southLatitude] + ]] + + // "properties" + output["properties"] = input.properties + output["properties"]["type"] = "dataset" + output["properties"]["wmo:topicHierarchy"] = `origin/a/wis2/${input.settings.topicHierarchy}` + output["properties"]["wmo:dataPolicy"] = input.settings["wmo:dataPolicy"] + output["properties"]["updated"] = today.toISOString().split('T')[0] + if (!("created" in output["properties"])) { + output["properties"]["created"] = today.toISOString().split('T')[0] + } - // "properties"."wis2box" - output["properties"]["wis2box"] = {} - output["properties"]["wis2box"]["country"] = input.poc.country.toLowerCase() - output["properties"]["wis2box"]["centre_id"] = input.origin.centre_id - output["properties"]["wis2box"]["retention"] = `P${input.settings.retention.toUpperCase()}` + // "properties"."wis2box" + output["properties"]["wis2box"] = {} + output["properties"]["wis2box"]["country"] = input.poc.country.toLowerCase() + output["properties"]["wis2box"]["centre_id"] = input.origin.centre_id + output["properties"]["wis2box"]["retention"] = `P${input.settings.retention.toUpperCase()}` - // "properties"."themes" - output["properties"]["themes"] = [{ "concepts": [] }] - input.settings.keywords.forEach(function (keyword) { - output.properties.themes[0]["concepts"].push({ "id": keyword }) - }) + // "properties"."themes" + output["properties"]["themes"] = [{ "concepts": [] }] + input.settings.keywords.forEach(function (keyword) { + output.properties.themes[0]["concepts"].push({ "id": keyword }) + }) - // "properties"."providers" - output["properties"]["providers"] = [] - const poc = modulateContact.value(input.poc) - poc.roles.push({ "name": "pointOfContact" }) - if (input.distrib.duplicateFromContact) { - poc.roles.push({ "name": "distributor" }) - } - else { - const distrib = modulateContact.value(input.distrib) - distrib.roles.push({ "name": "distributor" }) - output.properties.providers.push(distrib) - } - output.properties.providers.push(poc) - - // "links" - output["links"] = [] - output.links.push({ - "rel": "collection", - "href": `${oapi}/oapi/collections/${input.settings.identifier}`, - "type": "OAFeat", - "title": input.settings.identifier - }) - output.links.push({ - "rel": "data", - "href": `mqtt://everyone:everyone@${mqtt}:1883`, - "type": "MQTT", - "title": input.settings.topicHierarchy.replace(/\//g, "."), - "channel": `origin/a/wis2/${input.settings.topicHierarchy}` - }) - output.links.push({ - "rel": "canonical", - "href": `${oapi}/oapi/collections/discovery-metadata/items/${input.settings.identifier}`, - "type": "OARec", - "title": input.settings.identifier - }) + // "properties"."providers" + output["properties"]["providers"] = [] + const poc = modulateContact.value(input.poc) + poc.roles.push({ "name": "pointOfContact" }) + if (input.distrib.duplicateFromContact) { + poc.roles.push({ "name": "distributor" }) + } + else { + const distrib = modulateContact.value(input.distrib) + distrib.roles.push({ "name": "distributor" }) + output.properties.providers.push(distrib) + } + output.properties.providers.push(poc) + + // "links" + output["links"] = [] + output.links.push({ + "rel": "collection", + "href": `${oapi}/oapi/collections/${input.settings.identifier}`, + "type": "OAFeat", + "title": input.settings.identifier + }) + output.links.push({ + "rel": "data", + "href": `mqtt://everyone:everyone@${mqtt}:1883`, + "type": "MQTT", + "title": input.settings.topicHierarchy.replace(/\//g, "."), + "channel": `origin/a/wis2/${input.settings.topicHierarchy}` + }) + output.links.push({ + "rel": "canonical", + "href": `${oapi}/oapi/collections/discovery-metadata/items/${input.settings.identifier}`, + "type": "OARec", + "title": input.settings.identifier + }) - return output; - }; + return output; + }; - const modulateContact = (input) => { - const output = JSON.parse(JSON.stringify(input)); + const modulateContact = (input) => { + const output = JSON.parse(JSON.stringify(input)); - output["contactInfo"] = {}; + output["contactInfo"] = {}; - output.contactInfo["phone"] = {}; - output.contactInfo.phone = JSON.parse(JSON.stringify({ "office": output.phone })); - delete output.phone; + output.contactInfo["phone"] = {}; + output.contactInfo.phone = JSON.parse(JSON.stringify({ "office": output.phone })); + delete output.phone; - output.contactInfo["email"] = {}; - output.contactInfo.email = JSON.parse(JSON.stringify({ "office": output.email })); - delete output.email; + output.contactInfo["email"] = {}; + output.contactInfo.email = JSON.parse(JSON.stringify({ "office": output.email })); + delete output.email; - output.contactInfo["address"] = {}; - output.contactInfo.address = JSON.parse(JSON.stringify({ - "office": { - "deliveryPoint": output.deliveryPoint, - "city": output.city, - "administrativeArea": output.administrativeArea, - "postalCode": output.postalCode, - "country": output.country - } - })); - delete output.deliveryPoint; - delete output.city; - delete output.administrativeArea; - delete output.postalCode; - delete output.country; - - output.contactInfo["hoursOfService"] = JSON.parse(JSON.stringify(output.hoursOfService)); - delete output.hoursOfService; - - output.contactInfo["contactInstructions"] = JSON.parse(JSON.stringify(output.contactInstructions)); - delete output.contactInstructions; - - if ((input.url != null) && (input.url != "")) { - console.log(input.url); - output.contactInfo["url"] = {}; - output.contactInfo.url = JSON.parse(JSON.stringify({ - "rel": "canonical", - "type": "text/html", - "href": input.url + output.contactInfo["address"] = {}; + output.contactInfo.address = JSON.parse(JSON.stringify({ + "office": { + "deliveryPoint": output.deliveryPoint, + "city": output.city, + "administrativeArea": output.administrativeArea, + "postalCode": output.postalCode, + "country": output.country + } })); - } - else { - delete output.url; - }; + delete output.deliveryPoint; + delete output.city; + delete output.administrativeArea; + delete output.postalCode; + delete output.country; + + output.contactInfo["hoursOfService"] = JSON.parse(JSON.stringify(output.hoursOfService)); + delete output.hoursOfService; + + output.contactInfo["contactInstructions"] = JSON.parse(JSON.stringify(output.contactInstructions)); + delete output.contactInstructions; + + if ((input.url != null) && (input.url != "")) { + console.log(input.url); + output.contactInfo["url"] = {}; + output.contactInfo.url = JSON.parse(JSON.stringify({ + "rel": "canonical", + "type": "text/html", + "href": input.url + })); + } + else { + delete output.url; + }; - output["roles"] = []; + output["roles"] = []; - return output; - } + return output; + } const demodulateModel = (input) => { - const today = new Date(); - const output = {}; - - if (!("wis2box" in input.properties)) { - input.properties["wis2box"] = {} - input.properties.wis2box["country"] = input.properties["wmo:topicHierarchy"].split("/")[0] - input.properties.wis2box["centre_id"] = input.properties["wmo:topicHierarchy"].split("/")[1] - input.properties.wis2box["retention"] = "30d" - } + const today = new Date(); + const output = {}; - output["properties"] = {} - output.properties["title"] = input.properties.title - output.properties["description"] = input.properties.description - output.properties["wmo:dataPolicy"] = input.properties["wmo:dataPolicy"] - output.properties["created"] = input.properties.created + if (!("wis2box" in input.properties)) { + input.properties["wis2box"] = {} + input.properties.wis2box["country"] = input.properties["wmo:topicHierarchy"].split("/")[0] + input.properties.wis2box["centre_id"] = input.properties["wmo:topicHierarchy"].split("/")[1] + input.properties.wis2box["retention"] = "30d" + } - output.properties["language"] = "en" - if (("language" in input.properties) && (input.properties.language != null)) output.properties["language"] = input.properties.language + output["properties"] = {} + output.properties["title"] = input.properties.title + output.properties["description"] = input.properties.description + output.properties["wmo:dataPolicy"] = input.properties["wmo:dataPolicy"] + output.properties["created"] = input.properties.created - output["origin"] = {} - output.origin["centre_id"] = input.properties.wis2box.centre_id - if ("interval" in input.time) { - output.origin["dateStarted"] = input.time.interval[0] - output.origin["dateStopped"] = input.time.interval[1] - } - else if ("timestamp" in input.time) { - output.origin["dateStarted"] = input.time.timestamp.replace("T", "").replace("Z", "") - output.origin["dateStopped"] = input.time.timestamp.replace("T", "").replace("Z", "") - } - else { - output.origin["dateStarted"] = today.toISOString().split('T')[0] - output.origin["dateStopped"] = null - } + output.properties["language"] = "en" + if (("language" in input.properties) && (input.properties.language != null)) output.properties["language"] = input.properties.language - try { - output.origin["northLatitude"] = input.geometry.coordinates[0][0][1][1] - output.origin["eastLongitude"] = input.geometry.coordinates[0][0][2][0] - output.origin["southLatitude"] = input.geometry.coordinates[0][0][0][1] - output.origin["westLongitude"] = input.geometry.coordinates[0][0][0][0] - } - catch { - output.origin["northLatitude"] = input.geometry.coordinates[0][1][1] - output.origin["eastLongitude"] = input.geometry.coordinates[0][2][0] - output.origin["southLatitude"] = input.geometry.coordinates[0][0][1] - output.origin["westLongitude"] = input.geometry.coordinates[0][0][0] - } + output["origin"] = {} + output.origin["centre_id"] = input.properties.wis2box.centre_id + if ("interval" in input.time) { + output.origin["dateStarted"] = input.time.interval[0] + output.origin["dateStopped"] = input.time.interval[1] + } + else if ("timestamp" in input.time) { + output.origin["dateStarted"] = input.time.timestamp.replace("T", "").replace("Z", "") + output.origin["dateStopped"] = input.time.timestamp.replace("T", "").replace("Z", "") + } + else { + output.origin["dateStarted"] = today.toISOString().split('T')[0] + output.origin["dateStopped"] = null + } - input.properties.providers.forEach(function (provider) { - let is_poc = false - let is_distrib = false - provider.roles.forEach(function (role) { - if (role.name === "pointOfContact") { - is_poc = true - } - if (role.name === "pointOfContact") { - is_distrib = true + try { + output.origin["northLatitude"] = input.geometry.coordinates[0][0][1][1] + output.origin["eastLongitude"] = input.geometry.coordinates[0][0][2][0] + output.origin["southLatitude"] = input.geometry.coordinates[0][0][0][1] + output.origin["westLongitude"] = input.geometry.coordinates[0][0][0][0] + } + catch { + output.origin["northLatitude"] = input.geometry.coordinates[0][1][1] + output.origin["eastLongitude"] = input.geometry.coordinates[0][2][0] + output.origin["southLatitude"] = input.geometry.coordinates[0][0][1] + output.origin["westLongitude"] = input.geometry.coordinates[0][0][0] + } + + input.properties.providers.forEach(function (provider) { + let is_poc = false + let is_distrib = false + provider.roles.forEach(function (role) { + if (role.name === "pointOfContact") { + is_poc = true + } + if (role.name === "pointOfContact") { + is_distrib = true + } + }) + if (is_poc) output["poc"] = self.demodulateContact(provider) + if (is_distrib) { + output["distrib"] = self.demodulateContact(provider) + if (is_poc) { + output.distrib["duplicateFromContact"] = true + } + else { + output.distrib["duplicateFromContact"] = false + } } }) - if (is_poc) output["poc"] = self.demodulateContact(provider) - if (is_distrib) { - output["distrib"] = self.demodulateContact(provider) - if (is_poc) { - output.distrib["duplicateFromContact"] = true - } - else { - output.distrib["duplicateFromContact"] = false - } - } - }) - output["settings"] = {} - output.settings["identifier"] = input.id - output.settings["topicHierarchy"] = input.properties["wmo:topicHierarchy"].replace("origin/a/wis2/", "") - output.settings["retention"] = input.properties.wis2box.retention.toLowerCase().replace("p", "") + output["settings"] = {} + output.settings["identifier"] = input.id + output.settings["topicHierarchy"] = input.properties["wmo:topicHierarchy"].replace("origin/a/wis2/", "") + output.settings["retention"] = input.properties.wis2box.retention.toLowerCase().replace("p", "") - output.settings["wmo:dataPolicy"] = "core" - if ("wmo:dataPolicy" in input.properties) { - output.settings["wmo:dataPolicy"] = input.properties["wmo:dataPolicy"] - } + output.settings["wmo:dataPolicy"] = "core" + if ("wmo:dataPolicy" in input.properties) { + output.settings["wmo:dataPolicy"] = input.properties["wmo:dataPolicy"] + } - output.settings["keywords"] = [] - input.properties.themes.forEach(function (theme) { - theme.concepts.forEach(function (concept) { - output.settings.keywords.push(concept.id) + output.settings["keywords"] = [] + input.properties.themes.forEach(function (theme) { + theme.concepts.forEach(function (concept) { + output.settings.keywords.push(concept.id) + }) }) - }) - return JSON.parse(JSON.stringify(output)); - }; - - const demodulateContact = (input) => { - const output = {}; + return JSON.parse(JSON.stringify(output)); + }; - output["individual"] = null - if ("individual" in input) { - output["individual"] = input.individual - } + const demodulateContact = (input) => { + const output = {}; - output["positionName"] = null - if ("positionName" in input) { - output["positionName"] = input.positionName - } + output["individual"] = null + if ("individual" in input) { + output["individual"] = input.individual + } - output["name"] = null - if ("name" in input) { - output["name"] = input.name - } + output["positionName"] = null + if ("positionName" in input) { + output["positionName"] = input.positionName + } - output["url"] = null - if ("url" in input) { - output["url"] = input.url - } + output["name"] = null + if ("name" in input) { + output["name"] = input.name + } - output["phone"] = input.contactInfo.phone.office - output["email"] = input.contactInfo.email.office - output["deliveryPoint"] = input.contactInfo.address.office.deliveryPoint - output["city"] = input.contactInfo.address.office.city - output["administrativeArea"] = input.contactInfo.address.office.administrativeArea - output["postalCode"] = input.contactInfo.address.office.postalCode - output["country"] = input.contactInfo.address.office.country.slice(0, 3).toUpperCase() - output["hoursOfService"] = input.contactInfo.hoursOfService - output["contactInstructions"] = input.contactInfo.contactInstructions - - return JSON.parse(JSON.stringify(output)) - }; - - // Recursively process a node object - const prepareSchema = (title, node) => { - if (typeof node !== "object") { - return node; - } else if (node.length) { - for (let i = 0; i < node.length; i++) { - prepareSchema(`node-${i}`, node[i]) + output["url"] = null + if ("url" in input) { + output["url"] = input.url } - return node; - } else { - for (const [key, val] of Object.entries(node)) { - if (title === "properties") { - prepareSchema(key, val) + + output["phone"] = input.contactInfo.phone.office + output["email"] = input.contactInfo.email.office + output["deliveryPoint"] = input.contactInfo.address.office.deliveryPoint + output["city"] = input.contactInfo.address.office.city + output["administrativeArea"] = input.contactInfo.address.office.administrativeArea + output["postalCode"] = input.contactInfo.address.office.postalCode + output["country"] = input.contactInfo.address.office.country.slice(0, 3).toUpperCase() + output["hoursOfService"] = input.contactInfo.hoursOfService + output["contactInstructions"] = input.contactInfo.contactInstructions + + return JSON.parse(JSON.stringify(output)) + }; + + // Recursively process a node object + const prepareSchema = (title, node) => { + if (typeof node !== "object") { + return node; + } else if (node.length) { + for (let i = 0; i < node.length; i++) { + prepareSchema(`node-${i}`, node[i]) } - else { - prepareSchema("", val) + return node; + } else { + for (const [key, val] of Object.entries(node)) { + if (title === "properties") { + prepareSchema(key, val) + } + else { + prepareSchema("", val) + } } } - } - if (!title) { - node.title = clean(node.title) | clean(title); + if (!title) { + node.title = clean(node.title) | clean(title); + return node; + }; + return node; }; - return node; - }; - - // Mounted methods - onMounted(() => { - const tmp = window.location.href.split("/"); - loadList(tmp[tmp.length - 1]); + // Mounted methods + onMounted(() => { + const tmp = window.location.href.split("/"); + loadList(tmp[tmp.length - 1]); }) -// Watch for the distributor checkbox to enable/disable distributor fields -watch(() => model.value.distrib.duplicateFromContact, (newValue) => { - if (newValue) { - // If 'Yes', disable the fields - distributorFieldsEnabled.value = false; - // and copy the POC fields to the distributor fields - Object.keys(model.value.poc).forEach(key => { - model.value.distrib[key] = model.value.poc[key]; + // Watch for the distributor checkbox to enable/disable distributor fields + watch(() => model.value.distrib.duplicateFromContact, (newValue) => { + if (newValue) { + // If 'Yes', disable the fields + distributorFieldsEnabled.value = false; + // and copy the POC fields to the distributor fields + Object.keys(model.value.poc).forEach(key => { + model.value.distrib[key] = model.value.poc[key]; + }); + } else { + // If 'No', enable the fields + distributorFieldsEnabled.value = true; + } }); - } else { - // If 'No', enable the fields - distributorFieldsEnabled.value = true; - } -}); - -return { - loaded, - working, - validated, - filled, - specified, - message, - items, - identifier, - defaults, - isNew, - schema, - form, - model, - distributorFieldsEnabled, - rules, - loadList, - loadMetadata, - resetMetadata, - validateMetadata, - downloadMetadata, - submitMetadata, - loadGeometry, - updateGeometry, - updateModel, - modulateModel, - modulateContact, - demodulateModel, - demodulateContact, - prepareSchema -} + + return { + loaded, + working, + validated, + filled, + specified, + message, + items, + identifier, + defaults, + isNew, + schema, + form, + model, + distributorFieldsEnabled, + rules, + loadList, + loadMetadata, + resetMetadata, + validateMetadata, + downloadMetadata, + submitMetadata, + loadGeometry, + updateGeometry, + updateModel, + modulateModel, + modulateContact, + demodulateModel, + demodulateContact, + prepareSchema + } } }) From 7c9a0a8c809aa25cac8e02f8905236a2bcc46797 Mon Sep 17 00:00:00 2001 From: RoryPTB <47696929+RoryPTB@users.noreply.github.com> Date: Wed, 17 Jan 2024 17:16:08 +0100 Subject: [PATCH 2/2] Removed world bank fetch --- src/components/DiscoveryMetadataForm.vue | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/src/components/DiscoveryMetadataForm.vue b/src/components/DiscoveryMetadataForm.vue index f1e8ca9..cce8efa 100644 --- a/src/components/DiscoveryMetadataForm.vue +++ b/src/components/DiscoveryMetadataForm.vue @@ -36,7 +36,7 @@ + rules="[rules.required, rules.topicHierarchy]"> @@ -279,7 +279,7 @@ export default defineComponent({ const rules = { required: (value) => !!value || "Field is required", language: (value) => /^[a-z]{2}$/.test(value) || "Language must be a 2-letter code", - centreId: value => /^[a-z_-]{2,}$/.test(value) || 'Invalid centre ID. Must be lowercase with at least 2 characters.', + centre_id: value => /^[a-z_-]{2,}$/.test(value) || 'Invalid centre ID. Must be lowercase with at least 2 characters.', latitude: value => value >= -90 && value <= 90 || 'Latitude must be between -90 and 90.', longitude: value => value >= -180 && value <= 180 || 'Longitude must be between -180 and 180.', url: value => value === '' || /^https?:\/\/[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)$/.test(value) || 'Invalid URL format.', @@ -334,20 +334,6 @@ export default defineComponent({ message.value = "Working..."; form.value.modified = false; - // Fetch country ISO codes for injection - try { - const response = await fetch("https://api.worldbank.org/v2/country?format=json&per_page=500", { method: "get" }); - if (!response.ok) { - throw new Error('Network response was not ok') - } - const responseData = await response.json(); - responseData[1].forEach((item) => { - form.country_codes.push(item.id); - }); - } catch (error) { - console.error(error); - } - // Load a blank new discovery metadata file if (identifier.value === "Create New...") { isNew.value = true; // As metadata file is new