From a267e8c7dc65a83f93a28e2adf47f98451ad082c Mon Sep 17 00:00:00 2001 From: Jake Wagoner Date: Thu, 4 Apr 2024 12:56:32 -0600 Subject: [PATCH] Add data tables to UI, needs styling --- .../src/components/DeepSSM/DeepSSMTab.vue | 84 +++++++++++++++++-- web/shapeworks/src/helper.ts | 54 +++++++++++- web/shapeworks/src/store/index.ts | 8 +- web/shapeworks/src/store/methods.ts | 1 - 4 files changed, 136 insertions(+), 11 deletions(-) diff --git a/web/shapeworks/src/components/DeepSSM/DeepSSMTab.vue b/web/shapeworks/src/components/DeepSSM/DeepSSMTab.vue index 1c49c553..6543460f 100644 --- a/web/shapeworks/src/components/DeepSSM/DeepSSMTab.vue +++ b/web/shapeworks/src/components/DeepSSM/DeepSSMTab.vue @@ -7,9 +7,13 @@ import { spawnJob, spawnJobProgressPoll, abort, - deepSSMResult + deepSSMDataTab, + deepSSMResult, + deepSSMAugShowOrgData, + deepSSMAugShowGenData, } from '@/store'; import { Ref, computed, onMounted, ref, watch } from 'vue'; +import { parseCSVFromURL } from '@/helper'; export default { @@ -37,7 +41,6 @@ export default { const openExpansionPanel = ref(0); const controlsTabs = ref(); - const dataTabs = ref(); const showAbortConfirmation = ref(false); const prepData = { @@ -65,6 +68,25 @@ export default { ftLearningRate: ref(0.001), } + // headers are assigned from the parseCSV function's output + const dataTables = { + aug_table: ref(undefined), + aug_headers: ref(undefined), + training_table: ref(undefined), + training_headers: ref([ + {text: "Training Stage", value: '0'}, + {text: "Epoch", value: '1'}, + {text: "LR", value: '2'}, + {text: "Train_Err", value: '3'}, + {text: "Train_Rel_Err", value: '4'}, + {text: "Val_Err", value: '5'}, + {text: "Val_Rel_Err", value: '6'}, + {text: "Sec", value: '7'}, + ]), + testing_table: ref(undefined), + testing_headers: ref([{text: "Name", value: '0'}, {text: "Distance", value: '1'}]), + } + /** * Converts an object of reactive properties to a plain object for use in api formData fields. */ @@ -98,16 +120,40 @@ export default { return taskId; } + async function getCSVDataFromURL(url: string) { + return await parseCSVFromURL(url); + } + onMounted(async () => { if (!deepSSMResult.value && selectedProject.value) { await loadDeepSSMDataForProject(); } + if (deepSSMResult.value) { + try { + Promise.all([ + await getCSVDataFromURL(deepSSMResult.value.result.aug_total_data), + await getCSVDataFromURL(deepSSMResult.value.result.training_data_table), + await getCSVDataFromURL(deepSSMResult.value.result.testing_distances), + ]).then((res) => { + dataTables.aug_table.value = res[0]; + dataTables.training_table.value = res[1]; + dataTables.testing_table.value = res[2]; + + // Augmentation data table doesn't have headers, only a numbered list + dataTables.aug_headers.value = dataTables.aug_table.value.map((_: string, index: number) => { + return {text: index+1, value: `${index}`, align:'start'} + }); + }); + } + catch (e) { + console.error(e); + } + } }) return { openExpansionPanel, controlsTabs, - dataTabs, prepData, augmentationData, trainingData, @@ -117,7 +163,11 @@ export default { taskData, abort, showAbortConfirmation, + deepSSMDataTab, deepSSMResult, + deepSSMAugShowOrgData, + deepSSMAugShowGenData, + dataTables, } }, } @@ -237,18 +287,26 @@ export default { Data - - Prep + Augmentation Training Testing - +
+
+ + +
- total + + +

Violin Plot

@@ -256,6 +314,11 @@ export default {
+ + Training Plot @@ -269,6 +332,11 @@ export default {
+ +
@@ -300,7 +368,7 @@ input[type=number] { margin: 0 10px; } -.image-spacing { +.image-spacing, .aug-data-checkboxes { display: flex; flex-direction: row; justify-content: space-between; diff --git a/web/shapeworks/src/helper.ts b/web/shapeworks/src/helper.ts index 1aef38fd..642ba504 100644 --- a/web/shapeworks/src/helper.ts +++ b/web/shapeworks/src/helper.ts @@ -1,5 +1,11 @@ // Helper functions that may come in handy in component or view files +/** + * Groups an array of objects by a specified key. + * @param xs The array of objects to be grouped. + * @param key The key to group the objects by. + * @returns An object where the keys are unique values of the specified key, and the values are arrays of objects with that key value. + */ export function groupBy(xs: any[], key: string) { return xs.reduce(function(rv, x) { (rv[x[key]] = rv[x[key]] || []).push(x); @@ -7,15 +13,32 @@ export function groupBy(xs: any[], key: string) { }, {}); } +/** + * Returns the short date string from a given date string. + * @param date The date string. + * @returns The short date string. + */ export function shortDateString(date: string) { return date.split('T')[0] } +/** + * Returns the short file name from a given file path. + * @param file The file path. + * @returns The short file name. + */ export function shortFileName(file: string) { const split = file.split('?')[0].split('/') return split[split.length-1] } +/** + * Calculates the Euclidean distance between two points in n-dimensional space. + * @param one The coordinates of the first point. + * @param two The coordinates of the second point. + * @param signed (Optional) Whether to return a signed distance. Defaults to false. + * @returns The Euclidean distance between the two points. + */ export function getDistance( one: number[], two: number[], signed=false ){ @@ -32,7 +55,11 @@ export function getDistance( return Math.sqrt(squaredDimSum); } -// from https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb +/** + * Converts a hexadecimal color code to an RGB array. + * @param hex The hexadecimal color code. + * @returns The RGB array representation of the color. + */ export function hexToRgb(hex: string) { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? [ @@ -53,3 +80,28 @@ export function rgbToHex(rgb) { export function distance(a, b){ return Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2) + Math.pow(a.z - b.z, 2); } + +/** + * Parses a CSV file from the specified URL and returns the data as an array of objects. + * @param url - The URL of the CSV file to parse. + * @returns A promise that resolves to an array of objects representing the CSV data. + */ +export async function parseCSVFromURL(url: string) { + return fetch(url) + .then(response => response.text()) + .then(text => { + // last value of the substring is blank, so we remove it + const splitstring = text.split('\n').slice(0, -1); + + const data: {[x: string]: any} = []; + for (let i = 0; i < splitstring.length; i++) { + const d = {}; + const row = splitstring[i].split(','); + for (let j = 0; j < row.length; j++) { + d[j] = row[j]; + } + data.push(d); + } + return data; + }) +} diff --git a/web/shapeworks/src/store/index.ts b/web/shapeworks/src/store/index.ts index 9e10e4fe..92b96430 100644 --- a/web/shapeworks/src/store/index.ts +++ b/web/shapeworks/src/store/index.ts @@ -125,4 +125,10 @@ export const imageViewLevel = ref(0); export const imageViewLevelRange = ref([0, 1]); -export const deepSSMResult = ref>(); \ No newline at end of file +export const deepSSMResult = ref>(); + +export const deepSSMDataTab = ref(0); + +export const deepSSMAugShowOrgData = ref(true); + +export const deepSSMAugShowGenData = ref(false); \ No newline at end of file diff --git a/web/shapeworks/src/store/methods.ts b/web/shapeworks/src/store/methods.ts index ee0546bc..e249da08 100644 --- a/web/shapeworks/src/store/methods.ts +++ b/web/shapeworks/src/store/methods.ts @@ -186,7 +186,6 @@ export const loadDeepSSMDataForProject = async () => { training_images: results[2], test_images: results[3] } - console.log(deepSSMResult.value) } }