From 8b8e3a63f761302505658805c3464328216052c2 Mon Sep 17 00:00:00 2001 From: Marco Cano Date: Fri, 22 Dec 2023 16:20:19 -0800 Subject: [PATCH 1/2] fix: :hammer: fix expected type routing, NDE guides added --- nuxt-app/assets/css/styles.css | 9 ++ nuxt-app/components/JS_DefinitionBox.vue | 2 +- nuxt-app/components/QueryBox.vue | 14 +- nuxt-app/components/global/AnyOf.vue | 4 +- nuxt-app/components/global/OneOf.vue | 4 +- nuxt-app/components/global/TypeSelector.vue | 158 +++++++++++++++++--- nuxt-app/components/guide/InputBox.vue | 98 +++++++----- nuxt-app/components/guide/InputPreview.vue | 10 +- nuxt-app/components/guide/Vocabulary.vue | 2 +- nuxt-app/pages/guide/Guide.vue | 29 +++- nuxt-app/store/index.js | 2 +- nuxt-app/store/modules/auth.js | 22 +-- nuxt-app/store/modules/guide.js | 28 +++- 13 files changed, 287 insertions(+), 95 deletions(-) diff --git a/nuxt-app/assets/css/styles.css b/nuxt-app/assets/css/styles.css index 5ea7a213..ef5a4f16 100644 --- a/nuxt-app/assets/css/styles.css +++ b/nuxt-app/assets/css/styles.css @@ -50,6 +50,15 @@ max-width: 200px; } +.inputbox{ + border-bottom: 1px #e1e1e1 solid; + transition: all .5s; +} + +.inputbox:hover{ + background-color: white; +} + @media (prefers-color-scheme: dark) { :root { --color-background: var(--vt-c-black); diff --git a/nuxt-app/components/JS_DefinitionBox.vue b/nuxt-app/components/JS_DefinitionBox.vue index 2fff824a..4cb4ad68 100644 --- a/nuxt-app/components/JS_DefinitionBox.vue +++ b/nuxt-app/components/JS_DefinitionBox.vue @@ -244,7 +244,7 @@ export default { let children = self.info["vocabulary"]["children_of"].toString(); let url = - `https://www.ebi.ac.uk/ols/api/search?q=` + + `https://www.ebi.ac.uk/ols4/api/search?q=` + query + "&ontology=" + ontologies + diff --git a/nuxt-app/components/QueryBox.vue b/nuxt-app/components/QueryBox.vue index fa39e099..611f6d51 100644 --- a/nuxt-app/components/QueryBox.vue +++ b/nuxt-app/components/QueryBox.vue @@ -129,11 +129,11 @@ Expected Type: @@ -245,15 +245,7 @@ export default { return link; } catch (e) { if (qName.includes(":")) { - let arr = qName.split(":"); - return ["./" + arr[arr.length - 1]]; - } else { - return false; - } - } finally { - if (qName.includes(":")) { - let arr = qName.split(":"); - return ["./" + arr[arr.length - 1]]; + return ["/ns/" + qName.split(":")[0] + "/" + qName]; } else { return false; } diff --git a/nuxt-app/components/global/AnyOf.vue b/nuxt-app/components/global/AnyOf.vue index 114c8afc..adc73a13 100644 --- a/nuxt-app/components/global/AnyOf.vue +++ b/nuxt-app/components/global/AnyOf.vue @@ -142,7 +142,7 @@ export default { let children = self.option["vocabulary"]["children_of"].toString(); let url = - `https://www.ebi.ac.uk/ols/api/search?q=` + + `https://www.ebi.ac.uk/ols4/api/search?q=` + query + "&ontology=" + ontologies + @@ -217,7 +217,7 @@ export default { if (ontologies) { let url = - `https://www.ebi.ac.uk/ols/api/search?q=` + + `https://www.ebi.ac.uk/ols4/api/search?q=` + query + "&ontology=" + ontologies + diff --git a/nuxt-app/components/global/OneOf.vue b/nuxt-app/components/global/OneOf.vue index f17f3263..a8ea40ec 100644 --- a/nuxt-app/components/global/OneOf.vue +++ b/nuxt-app/components/global/OneOf.vue @@ -141,7 +141,7 @@ export default { let children = self.option["vocabulary"]["children_of"].toString(); let url = - `https://www.ebi.ac.uk/ols/api/search?q=` + + `https://www.ebi.ac.uk/ols4/api/search?q=` + query + "&ontology=" + ontologies + @@ -216,7 +216,7 @@ export default { if (ontologies) { let url = - `https://www.ebi.ac.uk/ols/api/search?q=` + + `https://www.ebi.ac.uk/ols4/api/search?q=` + query + "&ontology=" + ontologies + diff --git a/nuxt-app/components/global/TypeSelector.vue b/nuxt-app/components/global/TypeSelector.vue index 03983857..e2cdd8b5 100644 --- a/nuxt-app/components/global/TypeSelector.vue +++ b/nuxt-app/components/global/TypeSelector.vue @@ -18,11 +18,11 @@ ]" >
- +
- +
@@ -41,7 +41,7 @@ @submit.prevent="handleSubmit(name, value)" > +
+ + @@ -368,7 +412,7 @@ export default { Vocabulary, }, methods: { - parseOptions() { + findTypesAvailable() { var self = this; // get options from oneOf or anyOf self.info && self.info.oneOf @@ -398,9 +442,18 @@ export default { } else if (option && option["type"] == "array") { let name = self.main_name.split("_").join(" "); if (!self.parsed_options.hasOwnProperty(name)) { - option["keywords"] = true; - self.parsed_options[name] = option; + if (option?.items) { + //keywords are complex objects + option["keywords"] = false; + self.parsed_options[name] = option.items; + }else{ + // keywords are simple strings + option["keywords"] = true; + self.parsed_options[name] = option; + } } + }else{ + console.log('Option unhandled from: ' + this.main_name, option) } } } else if ( @@ -411,25 +464,86 @@ export default { ) { // OBJECT TYPE FIELD self.parsed_options[self.info["@type"]] = self.info; + }else{ + console.log('@type not found for child node, using Thing as default: ', self.childName) + // use @type Thing as default + self.parsed_options['Thing'] = self.info; } - // console.log(self.main_name, self.parsed_options) + console.log('%c RESULTS FOR ' + self.main_name, 'background-color:yellow') + console.log({...self.parsed_options}) // self.checkAutoSelect(); }, isRequired(requiredList, name) { return requiredList.includes(name) ? true : false; }, + isNumeric(value) { + return /^-?\d+$/.test(value); + }, updateObject(prop, event) { var self = this; - self.userObject[prop] = event.target.value; + // ratingVAlue from NDE is string + if (this.isNumeric(event.target.value) && prop !== 'ratingValue') { + self.userObject[prop] = parseInt(event.target.value); + } else { + self.userObject[prop] = event.target.value; + } }, updateParent(childValue) { var self = this; + // console.log(self.userObject) + // console.log(childValue.subfield) + // console.log('INFO', self.info) + let mustBeArray = false; + if (self.info?.oneOf) { + self.info.oneOf.forEach(option => { + if (option?.items?.properties) { + for (const key in option?.items?.properties) { + if (Object.hasOwnProperty.call(option?.items?.properties, key)) { + //if found child in prop info properties + if (key == childValue?.subfield && option?.items?.properties[key]?.oneOf) { + option?.items?.properties[key]?.oneOf.forEach(subOp => { + if (subOp?.['type'] == 'array' && subOp?.items) { + //look through child items for matched type + if (Array.isArray(subOp?.items)) { + console.log('Items are an array', subOp?.items) + subOp?.items?.forEach(item => { + if (item?.['@type'] == childValue?.value?.["@type"]) { + //confirmed + console.log('updating parent', childValue) + console.log('%c Child value must be array: ' + childValue?.subfield, 'color: orange') + mustBeArray = true; + } + }) + }else if(typeof subOp?.items == 'object'){ + console.log('Items are an object', subOp?.items) + if ('@type' in subOp?.items && subOp?.items?.['@type'] == childValue?.value?.["@type"]) { + //confirmed + console.log('updating parent', childValue) + console.log('%c Child value must be array: ' + childValue?.subfield, 'color: orange') + mustBeArray = true; + }else{ + console.log('oh no', subOp?.items.constructor) + } + } + } + }) + } + } + } + } + }) + } + if (self.userObject && self.userObject[childValue.subfield]) { let existing_val = [self.userObject[childValue.subfield]]; existing_val.push(childValue.value); self.userObject[childValue.subfield] = existing_val; } else { - self.userObject[childValue.subfield] = childValue.value; + if (mustBeArray) { + self.userObject[childValue.subfield] = [childValue.value]; + } else { + self.userObject[childValue.subfield] = childValue.value; + } } }, select(name) { @@ -473,7 +587,7 @@ export default { let ontologies = propInfo["vocabulary"]["ontology"].toString(); let children = propInfo["vocabulary"]["children_of"].toString(); let url = - `https://www.ebi.ac.uk/ols/api/search?q=${query}` + + `https://www.ebi.ac.uk/ols4/api/search?q=${query}` + "&ontology=" + ontologies + "&childrenOf=" + @@ -544,6 +658,9 @@ export default { var payload = {}; payload["item"] = res; payload["from"] = self.main_name; + if (self.info?.oneOf?.length == 1 && self.info?.oneOf?.[0]?.type == 'array') { + payload["forceArray"] = true; + } this.$store.commit("addToArrayFrom", payload); this.$store.dispatch("saveProgress"); } @@ -603,11 +720,16 @@ export default { // CHILD self.$emit("update", { value: data, subfield: self.childName }); self.type_selected = ""; + self.$store.dispatch("saveProgress"); } else { // PARENT var payload = {}; payload["item"] = data; payload["from"] = self.main_name; + if (self.info?.oneOf?.length == 1 && self.info?.oneOf?.[0]?.type == 'array') { + payload["forceArray"] = true; + } + // console.log('PAYLOAD', payload) this.$store.commit("addToArrayFrom", payload); this.$store.dispatch("saveProgress"); //reset @@ -636,10 +758,8 @@ export default { let self = this; if (self.requirementsFulfilled(fieldInfo)) { if (!self.isChild) { - console.log(1) self.animatedSubmit(ClassType, fieldInfo); } else { - console.log(2) self.regularSubmit(ClassType, fieldInfo); } } else { @@ -719,6 +839,9 @@ export default { var payload = {}; payload["item"] = result.value[i]; payload["from"] = propName; + if (self.info?.oneOf?.length == 1 && self.info?.oneOf?.[0]?.type == 'array') { + payload["forceArray"] = true; + } this.$store.commit("addToArrayFrom", payload); this.$store.dispatch("saveProgress"); @@ -745,8 +868,7 @@ export default { }, }, mounted: function () { - // console.log('TYPE SELECTOR : ' + this.main_name, this.info) - this.parseOptions(); + this.findTypesAvailable(); }, }; diff --git a/nuxt-app/components/guide/InputBox.vue b/nuxt-app/components/guide/InputBox.vue index a97ab6b8..2ccfc8ca 100644 --- a/nuxt-app/components/guide/InputBox.vue +++ b/nuxt-app/components/guide/InputBox.vue @@ -451,7 +451,7 @@ - @@ -36,10 +36,10 @@ > guide.name.toLowerCase() == self.guideQuery.toLowerCase() ); - console.log("Loading from context Query", found); if (found) { + console.log("%c ⭐ (3) URL match found. Loading guide: " + self.guideQuery, "background-color: purple; color: white; padding: 5px;"); self.$store.commit("setStartingPoint", { startingPoint: found }); payload["step"] = 3; self.$store.commit("changeStep", payload); self.getFormValues(); } else { + console.log("%c ⭐ (3) No Match found. User will select.", "background-color: purple; color: white; padding: 5px;"); payload["step"] = 1; self.$store.commit("changeStep", payload); } } // Choose first item from GUIDE_PRESETS as default else { + console.log("%c ⭐ (3) Loading default guide - no exact match", "background-color: purple; color: white; padding: 5px;"); payload["startingPoint"] = self.presets[0]; self.$store.commit("setStartingPoint", payload); if (self.portals && self.portals.length) { @@ -1704,6 +1710,7 @@ export default { getFormValues() { var self = this; // Handle selections for startingPoint and Portals IF any + console.log("%c ⭐ (4) Getting schema", "background-color: purple; color: white; padding: 5px;"); self.getStartingPointSchema(self.$store.getters.startingPoint); let payload = {}; payload["step"] = 3; @@ -1719,6 +1726,7 @@ export default { return text.replaceAll("_", " "); }, setStartingPoint(startingPointInfo) { + console.log("%c ⭐ (2) Setting Starting Point: " + startingPointInfo, "background-color: purple; color: white; padding: 5px;"); var self = this; let payload = {}; payload["startingPoint"] = startingPointInfo; @@ -1743,6 +1751,7 @@ export default { checkProgress() { let self = this; let p = sessionStorage.getItem("guideProgress"); + console.log("%c ⭐ (7) Checking progress", "background-color: purple; color: white; padding: 5px;"); if (p) { new Notify({ status: "success", @@ -1762,7 +1771,7 @@ export default { position: "right top", }); let selected = JSON.parse(p); - + console.log("%c ⭐ (8) Progress found. Values recovered.", "background-color: purple; color: white; padding: 5px;"); for (let key in selected) { if (!["@context", "@type", "_id"].includes(key)) { var payload = {}; @@ -1771,6 +1780,9 @@ export default { self.$store.commit("markCompleted", payload); } } + self.$store.commit("formPreviewForGuide"); + }else{ + console.log("%c ⭐ (8) No progress found, starting blank.", "background-color: purple; color: white; padding: 5px;"); } }, viewErrors() { @@ -1804,6 +1816,13 @@ export default { }, }, mounted: function () { + //check if prefilled N3C fields should be used + if (!window.location.href.includes('n3c')) { + this.$store.commit('setUsePrefilled', false); + }else{ + this.$store.commit('setUsePrefilled', true); + } + const runtimeConfig = useRuntimeConfig(); this.apiUrl = runtimeConfig.public.apiUrl; if (this.guide_query) { @@ -1858,7 +1877,7 @@ export default { // EBI API LOOKUP TIP DESCRIPTION axios .get( - "https://www.ebi.ac.uk/ols/api/search?q=" + + "https://www.ebi.ac.uk/ols4/api/search?q=" + encodeURI(info) + "&exact=1" ) diff --git a/nuxt-app/store/index.js b/nuxt-app/store/index.js index 89c2b3f6..59ed0885 100644 --- a/nuxt-app/store/index.js +++ b/nuxt-app/store/index.js @@ -64,7 +64,7 @@ export default createStore({ // EBI API LOOKUP TIP DESCRIPTION axios .get( - "https://www.ebi.ac.uk/ols/api/search?q=" + + "https://www.ebi.ac.uk/ols4/api/search?q=" + encodeURI(info) + "&exact=1" ) diff --git a/nuxt-app/store/modules/auth.js b/nuxt-app/store/modules/auth.js index 410c6691..9e269628 100644 --- a/nuxt-app/store/modules/auth.js +++ b/nuxt-app/store/modules/auth.js @@ -22,16 +22,16 @@ export const auth = { }, actions: { checkUser({ commit }) { - // if (process.env.NODE_ENV == "development") { - // commit("saveUser", { - // user: { - // name: "Marco Cano", - // email: "artofmarco@gmail.com", - // login: "marcodarko", - // avatar_url: "https://avatars.githubusercontent.com/u/23092057?v=4", - // }, - // }); - // } else { + if (process.env.NODE_ENV == "development") { + commit("saveUser", { + user: { + name: "Marco Cano", + email: "artofmarco@gmail.com", + login: "marcodarko", + avatar_url: "https://avatars.githubusercontent.com/u/23092057?v=4", + }, + }); + } else { let config = useRuntimeConfig(); axios .get(config.public.apiUrl + "/user") @@ -42,7 +42,7 @@ export const auth = { commit("resetUser"); throw err; }); - // } + } }, }, getters: { diff --git a/nuxt-app/store/modules/guide.js b/nuxt-app/store/modules/guide.js index f21ea081..b377b90e 100644 --- a/nuxt-app/store/modules/guide.js +++ b/nuxt-app/store/modules/guide.js @@ -103,7 +103,7 @@ export const guide = { }, saveSchema(state, payload) { state.schema = payload["schema"]; - console.log("Schema saved", state.schema); + console.log("Schema Saved", {...state.schema}); state.output["@type"] = state.schema?.name || state.schema?.label; state.output["@context"] = payload["schema"]["@context"]; let obj = Object.assign({}, state.output); @@ -423,7 +423,13 @@ export const guide = { let item = payload["item"]; //Initialize array if (!state.schema.validation.properties[field].value) { - state.schema.validation.properties[field]["value"] = item; + if (payload?.forceArray) { + //for cases that oneOf only has one value and it's array only + console.log('%c Force array for ' + field, 'color:hotpink') + state.schema.validation.properties[field]["value"] = [item]; + } else { + state.schema.validation.properties[field]["value"] = item; + } } else { if ( state.schema.validation.properties[field]["value"].constructor === @@ -447,6 +453,7 @@ export const guide = { } }, formPreviewForGuide(state) { + console.log("%c 🔆 Creating Metadata...", "color: blue; padding: 5px;"); let props = state.schema.validation.properties; // Add prefilled values from config state.output = Object.assign({}, state.output_default); @@ -462,13 +469,22 @@ export const guide = { Array.isArray(props[key]["value"]) && props[key]["value"].length === 1 ) { - state.output[key] = props[key].value[0]; + if (props[key]?.oneOf?.length == 1 && props[key]?.oneOf?.[0]?.type == 'array') { + //NOTE for cases where oneOF only has one option and it's array + //if array is only one item do not remove array structure keep as is + console.log('%c oneOf with one value found and must be array: ' + key, 'color: red') + state.output[key] = props[key].value; + }else{ + //else take the one item and place out of the array + state.output[key] = props[key].value[0]; + } } else { state.output[key] = props[key].value; } } } } + console.log("%c ✅ Validating Metadata...", "color: #4338c9; padding: 5px;"); // VALIDATION ON COMPLETE var ajv = new Ajv({ allErrors: true, strict: false }); addFormats(ajv); @@ -485,6 +501,7 @@ export const guide = { // }); if (!isValid) { + console.log("%c ❌ Validation Failed", "background-color: #a50202; color: white; padding: 5px;"); state.valid = false; state.errors = ajv.errors; for (var key in props) { @@ -517,6 +534,7 @@ export const guide = { } } } else { + console.log("%c ✅ Validation Passed", "background-color: #b7ea68; padding: 5px;"); state.valid = true; state.errors = []; for (var key in props) { @@ -862,7 +880,8 @@ export const guide = { } }, isInputHidden: (state) => (propname) => { - if (propname in state.guide_prefilled) { + // N3C wants to hide some fields from user + if (propname in state.guide_prefilled && window.location.href.includes('n3c')) { return true; } else { return false; @@ -1043,6 +1062,7 @@ export const guide = { commit("reset"); }, saveProgress({ commit, state }) { + console.log("%c ⭐ Saving progress", "background-color: lightblue; padding: 5px;"); commit("formPreviewForGuide"); let obj = state.output; sessionStorage.setItem("guideProgress", JSON.stringify(obj)); From e01ccc1b116bbc577a9dd6181bf1f60e0bd1475f Mon Sep 17 00:00:00 2001 From: Marco Cano Date: Fri, 22 Dec 2023 16:20:59 -0800 Subject: [PATCH 2/2] fix: :hammer: fix expected type routing, NDE guides added --- nuxt-app/store/modules/auth.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/nuxt-app/store/modules/auth.js b/nuxt-app/store/modules/auth.js index 9e269628..597ba40d 100644 --- a/nuxt-app/store/modules/auth.js +++ b/nuxt-app/store/modules/auth.js @@ -22,16 +22,16 @@ export const auth = { }, actions: { checkUser({ commit }) { - if (process.env.NODE_ENV == "development") { - commit("saveUser", { - user: { - name: "Marco Cano", - email: "artofmarco@gmail.com", - login: "marcodarko", - avatar_url: "https://avatars.githubusercontent.com/u/23092057?v=4", - }, - }); - } else { + // if (process.env.NODE_ENV == "development") { + // commit("saveUser", { + // user: { + // name: "Marco Cano", + // email: "artofmarco@gmail.com", + // login: "marcodarko", + // avatar_url: "https://avatars.githubusercontent.com/u/23092057?v=4", + // }, + // }); + // } else { let config = useRuntimeConfig(); axios .get(config.public.apiUrl + "/user") @@ -43,7 +43,7 @@ export const auth = { throw err; }); } - }, + // }, }, getters: { userInfo: (state) => {