Skip to content

Commit

Permalink
Fix/data centric monitoring (#57)
Browse files Browse the repository at this point in the history
* fix: replace topic hierarchy with dataset identifier to align monitoring page with data-centric approach

* feat: only show inspect button for BUFR files

* fix: replace topic selectors with dataset ID selectors + return object 'metadata' containing both dataset ID and topic + style: improve look of station editor and other pages

* fix: query API by metadata ID rather than data ID in monitoring page

* build: lock Vuetify version to 3.7.0 to prevent breaking changes
  • Loading branch information
maaikelimper authored Aug 22, 2024
1 parent 978b0ec commit 7fe07c2
Show file tree
Hide file tree
Showing 27 changed files with 1,214 additions and 1,047 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ FROM node:lts-alpine
WORKDIR /wis2box-webapp

# copy both 'package.json' and 'package-lock.json' (if available)
COPY package*.json .
COPY package*.json ./

# install project dependencies
RUN npm install
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"vue-router": "^4.0.0",
"vue-tel-input": "^8.3.1",
"vue3-apexcharts": "^1.4.4",
"vuetify": "^3.4.0",
"vuetify": "3.7.0",
"webfontloader": "^1.0.0"
},
"devDependencies": {
Expand All @@ -43,6 +43,6 @@
"vite-plugin-vuetify": "^1.0.0"
},
"overrides": {
"esbuild": "^0.21.0"
"esbuild": "^0.21.5"
}
}
8 changes: 5 additions & 3 deletions src/components/CodeListSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
clearable
persistent-hint
return-object
variant="outlined"
class="my-5"
/>
</div>
<div v-else class="error">
Expand All @@ -29,14 +31,14 @@
</template>

<script>
import { defineComponent, ref, onBeforeMount, watch, onMounted} from 'vue';
import { VCard, VCardItem, VAutocomplete} from 'vuetify/lib/components/index.mjs';
import { defineComponent, ref, onBeforeMount, watch } from 'vue';
import { VCard, VAutocomplete} from 'vuetify/lib/components/index.mjs';
export default defineComponent({
name: 'CodeListSelector',
components: {
VCard, VCardItem, VAutocomplete
VCard, VAutocomplete
},
props: {
codeList: {type: String},
Expand Down
116 changes: 54 additions & 62 deletions src/components/CsvToBUFRForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
value="2" :color="step2Color">
</v-stepper-item>
<v-stepper-item
:complete="status.topicHierarchy"
title="Select topic hierarchy"
:complete="status.datasetIdentifier"
title="Select dataset"
value="3" :color="step3Color">
</v-stepper-item>
<v-stepper-item
Expand Down Expand Up @@ -76,15 +76,15 @@
</v-card-item>
<v-card-item>
<v-list v-if="validationWarnings.length > 0">
<v-list-item v-for="message in validationWarnings" base-color="warning">
<v-list-item v-for="message in validationWarnings" :key="message" base-color="warning">
<template v-slot:prepend>
<v-icon icon="mdi-alert-circle"></v-icon>
</template>
{{ message }}
</v-list-item>
</v-list>
<v-list v-if="validationErrors.length > 0">
<v-list-item v-for="message in validationErrors" base-color="error">
<v-list-item v-for="message in validationErrors" :key="message" base-color="error">
<template v-slot:prepend>
<v-icon icon="mdi-alert-circle"></v-icon>
</template>
Expand All @@ -96,18 +96,18 @@
</v-stepper-window-item>
<v-stepper-window-item value="3">
<v-card>
<v-card-title>Select topic hierarchy</v-card-title>
<v-card-item>
<TopicHierarchySelector :value="topicSelected" @update:modelValue="newValue => topicSelected = newValue"/>
</v-card-item>
<v-card-title>Select dataset identifier</v-card-title>
<v-col cols="12">
<DatasetIdentifierSelector :value="datasetSelected" @update:modelValue="newValue => datasetSelected = newValue"/>
</v-col>
</v-card>
</v-stepper-window-item>
<v-stepper-window-item value="4">
<v-card>
<v-card-title>Authorize and publish</v-card-title>
<v-card-text>
<v-text-field label="wis2box auth token for 'processes/wis2box'" v-model="token" rows="1"
:append-icon="showToken ? 'mdi-eye' : 'mdi-eye-off'" :type="showToken ? 'text' : 'password'"
:append-icon="showToken ? 'mdi-eye' : 'mdi-eye-off'" :type="showToken ? 'text' : 'password'" autocomplete="one-time-code"
@click:append="showToken = !showToken"
:rules="[v => !!v || 'Token is required']"
variant="outlined">
Expand Down Expand Up @@ -253,7 +253,7 @@
import { VStepper, VStepperHeader, VStepperItem, VStepperWindow, VStepperWindowItem, VStepperActions} from 'vuetify/lib/components/index.mjs';
import InspectBufrButton from '@/components/InspectBufrButton.vue';
import DownloadButton from '@/components/DownloadButton.vue';
import TopicHierarchySelector from '@/components/TopicHierarchySelector.vue';
import DatasetIdentifierSelector from '@/components/DatasetIdentifierSelector.vue';
import * as d3 from 'd3';
export default defineComponent({
name: 'CsvToBUFRForm',
Expand All @@ -262,7 +262,7 @@
VChip, VTooltip, VListItem, VList, VContainer,
VCardTitle, VIcon, VStepper, VStepperHeader, VStepperItem, VStepperWindow, VStepperWindowItem,
VStepperActions, VDialog, InspectBufrButton, DownloadButton,
TopicHierarchySelector
DatasetIdentifierSelector
},
setup() {
// reactive variables
Expand All @@ -273,21 +273,20 @@
const step=ref(0);
const validationWarnings = ref([]);
const validationErrors = ref([]);
const status = ref(null);
const status = ref({
fileLoaded: false,
fileValidated: false,
datasetIdentifier: false,
password: false
});
// Variable to control whether token is seen or not
const showToken = ref(false);
const token = ref(null);
const topicSelected = ref(null);
const datasetSelected = ref(null);
const rawCSV = ref(null);
const msg = ref(null);
const showDialog = ref(null);
const scrollRef = ref(null);
status.value = {
fileLoaded: false,
fileValidated: false,
topicHierarchy: false,
password: false
}
const result = ref(null);
const notificationsOnPending = ref(false);
Expand All @@ -309,7 +308,7 @@
return "#003DA5"
})
const step3Color = computed(() => {
if (status.value.topicHierarchy) {
if (status.value.datasetIdentifier) {
return "#64BF40"
}
return "#003DA5"
Expand All @@ -321,7 +320,7 @@
return "#003DA5"
})
const step5Complete = computed(() => {
return status.value.fileLoaded && status.value.fileValidated && status.value.topicHierarchy && status.value.password && status.value.submitted;
return status.value.fileLoaded && status.value.fileValidated && status.value.datasetIdentifier && status.value.password && status.value.submitted;
})
const step5Color = computed(() => {
if (step5Complete.value) {
Expand All @@ -337,11 +336,11 @@
return "Unknown";
});
const numberNotifications = computed( () => {
var messagesPublished = 0;
let messagesPublished = 0;
if( result.value ){
messagesPublished = result.value['messages published'];
}
return "WIS2 notifications published: " + messagesPublished;;
return "WIS2 notifications published: " + messagesPublished;
});
// life cycle hooks
onMounted( () => {
Expand All @@ -359,8 +358,8 @@
// load schema
await fetch("./csv2bufr/csvw_schema.json").then( (response) => response.json() ).then( (theData) => {schema.value = theData.tableSchema.columns});
// now load the data file
var reader = new FileReader();
reader.readAsText(incomingFile.value[0]);
let reader = new FileReader();
reader.readAsText(incomingFile.value);
reader.onload = async () => {
validationErrors.value = [];
validationWarnings.value = [];
Expand All @@ -376,9 +375,9 @@
divider: true
}));
// check the headers against the schema
for(var column in headers.value ){
var key = headers.value[column].key
var schemaColumn = schema.value.find( col => col.titles === key )
for(let column in headers.value ){
let key = headers.value[column].key
let schemaColumn = schema.value.find( col => col.titles === key )
if( ! schemaColumn ){
validationWarnings.value.push("Column '" + key +
"' not found in schema, data will be skipped")
Expand All @@ -390,29 +389,29 @@
schemaColumn.present = true;
}
}
for(var col in schema.value){
for(let col in schema.value){
if(!schema.value[col].present){
key = schema.value[col].titles;
let key = schema.value[col].titles;
validationWarnings.value.push("Column '" + key +
"' missing from data file, data will be set to missing in BUFR encoding");
}
}
// now validate the data
var count = 0;
let count = 0;
for(const record of theData.value){
count++;
for( const key in record){
var header = headers.value.find(header => header.key === key );
var value = record[key];
let header = headers.value.find(header => header.key === key );
let value = record[key];
record[key] = {
value: value,
status: "",
msg: ""
}
var valid_min = false;
var valid_max = false;
var msg = "";
var status = "success";
let valid_min = false;
let valid_max = false;
let msg = "";
let status = "success";
if( header.inSchema ){
if( header.dataType.minimum ){
valid_min = header.dataType.minimum
Expand All @@ -433,7 +432,6 @@
status = 'error';
validationErrors.value.push(msg);
}
// console.log(status + ": " + msg);
}else{
status = "warning";
msg = "Field not in schema";
Expand All @@ -446,11 +444,11 @@
record[key].status = status;
record[key].msg = msg;
}
};
}
// get limits and kind from schema
};
step.value = 1;
};
}
};
const submit = async() => {
CsvToBUFR();
Expand All @@ -460,7 +458,8 @@
const payload = {
inputs: {
data: rawCSV.value,
channel: topicSelected.value.id,
channel: datasetSelected.value.metadata.topic,
metadata_id: datasetSelected.value.metadata.id,
notify: notificationsOnPending.value,
template: "aws-template"
}
Expand Down Expand Up @@ -501,7 +500,7 @@
const data = await response.json();
result.value = data;
result.value.files = [];
for( var item in result.value.data_items){
for( let item in result.value.data_items){
result.value.files.push( result.value.data_items[item].file_url);
}
}
Expand All @@ -510,7 +509,7 @@
step.value = step.value === 0 ? 0 : step.value - 1;
};
const next = () => {
var proceed = false;
let proceed = false;
switch (step.value){
case 0:
if( status.value.fileLoaded){
Expand All @@ -532,12 +531,11 @@
}
break;
case 2:
if( status.value.topicHierarchy ){
if( status.value.datasetIdentifier ){
proceed = true;
}else{
showDialog.value = true;
console.log(topicSelected.value)
msg.value = "Please select a topic to publish on before proceeding";
msg.value = "Please select a dataset to publish on before proceeding";
}
break;
case 3:
Expand All @@ -555,30 +553,24 @@
}
};
// Define watches
watch( topicSelected, (val) => {
console.log(val);
if( val ){
status.value.topicHierarchy = true;
}else{
status.value.topicHierarchy = false;
}
// Watchers
watch( datasetSelected, (val) => {
status.value.datasetIdentifier = !!val;
});
watch( incomingFile, (val) => {
if( val && val.length > 0){
status.value.fileLoaded = true;
}else{
status.value.fileLoaded = false;
}
} );
status.value.fileLoaded = !!val;
});
watch( validationErrors, (val) => {
if( val && val.length > 0 ){
status.value.fileValidated = false;
}else{
status.value.fileValidated = true;
}
});
watch( token, (val) => {
watch( token, () => {
if( token.value && token.value.length > 0 ){
status.value.password = true;
}else{
Expand All @@ -588,7 +580,7 @@
return {theData, headers, incomingFile, loadCSV, step, prev, next, scrollToRef,
validationWarnings, validationErrors, status, showToken, token, notificationsOnPending, step1Color, step2Color, step3Color, step4Color, step5Complete, step5Color,
topicSelected, submit, msg, showDialog, result, resultTitle, numberNotifications};
datasetSelected, submit, msg, showDialog, result, resultTitle, numberNotifications};
},
})
</script>
Loading

0 comments on commit 7fe07c2

Please sign in to comment.