Skip to content

Commit

Permalink
WIP array analysis panel
Browse files Browse the repository at this point in the history
  • Loading branch information
Logende committed Feb 4, 2025
1 parent 324fa4e commit f2dcbec
Show file tree
Hide file tree
Showing 6 changed files with 248 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
} from '@/components/dialogs/csvimport/delimiterSeparatorUtils';
import {type CsvError, parse} from 'csv-parse/browser/esm';
import type {JsonSchemaType} from '@/schema/jsonSchemaType';
import {identifyArraysInJson} from "@/utility/arrayPathUtils";

export function requestUploadFileToRef(resultString: Ref<string>, resultTableName: Ref<string>) {
const {open, onChange} = useFileDialog();
Expand Down Expand Up @@ -152,18 +153,7 @@ export function userStringToIdentifier(input: string, cutExtension: boolean = fa

// note that this function does not look for a table within a table
export function detectPossibleTablesInJson(json: any, path: Path = []): Path[] {
const tables: Path[] = [];
for (const key in json) {
if (json.hasOwnProperty(key)) {
const newPath = path ? [...path, key] : [key];
if (Array.isArray(json[key])) {
tables.push(newPath);
} else if (typeof json[key] === 'object' && json[key] !== null) {
tables.push(...detectPossibleTablesInJson(json[key], newPath));
}
}
}
return tables;
return identifyArraysInJson(json, path, false, true);
}

export function detectPropertiesOfTableInJson(json: any, tablePath: Path): string[] {
Expand Down
10 changes: 10 additions & 0 deletions meta_configurator/src/components/panels/defaultPanelTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import GuiEditorPanel from '@/components/panels/gui-editor/GuiEditorPanel.vue';
import SchemaDiagramPanel from '@/components/panels/schema-diagram/SchemaDiagramPanel.vue';
import DebugPanel from '@/components/panels/debug-panel/DebugPanel.vue';
import AiPromptsPanel from '@/components/panels/ai-prompts/AiPromptsPanel.vue';
import ListAnalysisPanel from './list-analysis/ListAnalysisPanel.vue';

export const panelTypeTextEditor: PanelTypeDefinition = {
getComponent: () => CodeEditorPanel,
Expand Down Expand Up @@ -39,6 +40,14 @@ export const panelTypeAiPrompts: PanelTypeDefinition = {
name: 'aiPrompts',
};

export const panelTypeListAnalysis: PanelTypeDefinition = {
getComponent: () => ListAnalysisPanel,
supportedModes: [SessionMode.DataEditor],
label: 'List Analysis',
icon: 'fa-solid fa-table',
name: 'listAnalysis',
};

export const panelTypeDebug: PanelTypeDefinition = {
getComponent: () => DebugPanel,
supportedModes: [SessionMode.DataEditor, SessionMode.SchemaEditor, SessionMode.Settings],
Expand All @@ -55,5 +64,6 @@ export function registerDefaultPanelTypes() {
panelTypeRegistry.registerPanelType('guiEditor', panelTypeGuiEditor);
panelTypeRegistry.registerPanelType('schemaDiagram', panelTypeSchemaDiagram);
panelTypeRegistry.registerPanelType('aiPrompts', panelTypeAiPrompts);
panelTypeRegistry.registerPanelType('listAnalysis', panelTypeListAnalysis);
panelTypeRegistry.registerPanelType('debug', panelTypeDebug);
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function updateData(path: Path, newValue: any) {
sortedProperties[key] = parentData[key];
}
});
// after adding properties from the schema in proper order, add the rest of the properties
// after adding properties from the schema in proper order, add the rest of the properties f
dataKeys.forEach(key => {
if (!schemaKeys.includes(key)) {
sortedProperties[key] = parentData[key];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<script setup lang="ts">
import {SessionMode} from '@/store/sessionMode';
import {computed, type ComputedRef, onMounted, ref, type Ref} from "vue";
import {identifyArraysInJson} from "@/utility/arrayPathUtils";
import type {Path} from "@/utility/path";
import {getDataForMode} from "@/data/useDataLink";
import SelectButton from 'primevue/selectbutton';
import Button from 'primevue/button';
import Column from 'primevue/column';
import DataTable from "primevue/datatable";
import ScrollPanel from "primevue/scrollpanel";
import {jsonPointerToPathTyped, pathToJsonPointer} from "@/utility/pathUtils";
import {
createItemsRowsFromJson,
formatJsonPointerAsPropertyName,
formatJsonPointerForUser
} from "@/components/panels/list-analysis/listAnalysisUtils";
import TreeTable from "primevue/treetable";
const props = defineProps<{
sessionMode: SessionMode;
}>();
const data = getDataForMode(props.sessionMode);
onMounted(() => {
updatePossibleArrays(data.data.value);
});
const possibleArrays: Ref<string[]> = ref([]);
const selectedArrayPointer: Ref<string | null> = ref(null);
const selectedArrayPath = computed(() => {
if (selectedArrayPointer.value == null) {
return null;
}
return jsonPointerToPathTyped(selectedArrayPointer.value);
});
const selectedArray: Ref<any | null> = computed(() => {
if (selectedArrayPath.value == null) {
return null;
}
return data.dataAt(selectedArrayPath.value);
});
const tableData: ComputedRef<null | { rows: any[], columnNames: string[] }> = computed(() => {
if (selectedArrayPath.value == null) {
return null;
}
const currentData = data.dataAt(selectedArrayPath.value);
return createItemsRowsFromJson(currentData);
});
const itemRows = computed(() => {
console.log("itemRows computed" + tableData.value?.rows);
return tableData.value?.rows;
});
// function to update the possible arrays based on the data
function updatePossibleArrays(newData: any) {
possibleArrays.value = identifyArraysInJson(newData, [], true, true).map( (path: Path) => {
return pathToJsonPointer(path)
});
if (possibleArrays.value.length == 0) {
selectedArrayPointer.value = null;
} else if (possibleArrays.value.length == 1) {
selectedArrayPointer.value = possibleArrays.value[0];
} else {
// if there are multiple arrays, we do not change the selection
}
console.log("tableData " , tableData.value);
}
</script>

<template>

<div >
<label class="heading">Array Analysis</label>
<Button label="Update Data" icon="pi pi-refresh" @click="updatePossibleArrays(data.data.value)" />
<div class="mt-3">
<div v-if="possibleArrays.length==0">
<b>No arrays available.</b>
</div>
<div v-else>
<label for="arrayPath">Select an array to analyze:</label>
<SelectButton v-model="selectedArrayPointer" :options="possibleArrays" />
</div>
</div>


<div v-if="tableData" >

<ScrollPanel style="width: 100%; height: 100%" aria-orientation="horizontal">

<DataTable :value="selectedArray" :paginator="true" :rows="20"
tableStyle="min-width: 50rem"
showGridlines
stripedRows
removable-sort
scrollable scrollHeight="flex"
class="flex-grow"
size="small"
>
<Column v-for="columnName in tableData.columnNames" :field="columnName" :header="columnName" :sortable="true"/>
</DataTable>
</ScrollPanel>
</div>

</div>

</template>

<style scoped>
.heading {
font-size: 24px; /* Make the text bigger */
font-weight: bold; /* Make the text bold */
text-align: center; /* Center the text horizontally */
display: block; /* Ensure the label behaves like a block element */
margin-bottom: 10px; /* Add some space below the label */
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import _ from 'lodash';
import {dataAt} from "@/utility/resolveDataAtPath";
import {jsonPointerToPathTyped} from "@/utility/pathUtils";

export function createItemsRowsFromJson(itemsJson: any): { rows: any[], columnNames: string[] } {
const columnNames = collectItemColumnNames(itemsJson);

const rows = itemsJson.map((itemJson: any) => {
return createItemRow(itemJson, columnNames);
});

const columnNamesFormatted: string[] = Array.from(columnNames).map((columnName: string) => {
return formatJsonPointerForUser(columnName);
});

return { rows: rows, columnNames: columnNamesFormatted };
}

export function createItemRow(itemJson: any, columnNames: Set<string>): any {
const row: any = {};

for (const columnName of columnNames) {

const columnData = dataAt(jsonPointerToPathTyped(columnName), itemJson);

const formattedColumnName = formatJsonPointerAsPropertyName(columnName);
if (columnData !== undefined) {
row[formattedColumnName] = columnData;
} else {
row[formattedColumnName] = null;
}
}

return row;
}


// collect all properties of the items, including nested properties
function collectItemColumnNames(itemsJson: any): Set<string> {
const columnNames: Set<string> = new Set();

for (const itemJson of itemsJson) {
for (const itemProperty in itemJson) {
if (itemJson.hasOwnProperty(itemProperty)) {

// if it is an object: recursively collect all nested properties
if (_.isObject(itemJson[itemProperty])) {
const nestedColumnNames = collectItemColumnNames([itemJson[itemProperty]]);
nestedColumnNames.forEach((nestedColumnName: string) => {
columnNames.add(`/${itemProperty}${nestedColumnName}`);
});
} else {
// if it is a simple property: add it to the list
if (itemProperty !== undefined) {
columnNames.add("/" + itemProperty);
}
}
}
}
}

return columnNames;
}


export function formatJsonPointerAsPropertyName(pointer: string): string {
// remove first slash and replace others by dot
return pointer.replaceAll("/", "");
}

export function formatJsonPointerForUser(pointer: string): string {
// remove first slash and replace others by dot
return pointer.substring(1).replaceAll("/", ".");
}
29 changes: 29 additions & 0 deletions meta_configurator/src/utility/arrayPathUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

// note that this function does not look for a table within a table
import type {Path} from "@/utility/path";

export function identifyArraysInJson(json: any, path: Path = [], allowNestedArrays: boolean, onlyObjectArray: boolean): Path[] {
const arrayPaths: Path[] = [];


if (Array.isArray(json) && (!onlyObjectArray || json.length > 0 && typeof json[0] === 'object')) {
arrayPaths.push(path);
if (allowNestedArrays) {
// for each array element, recursively search for nested arrays
for (let i = 0; i < json.length; i++) {
arrayPaths.push(...identifyArraysInJson(json[i], [...path, i], allowNestedArrays, onlyObjectArray));
}
}

} else if (typeof json === 'object' && json !== null) {
// for each key, recursively search for nested arrays
for (const key in json) {
if (json.hasOwnProperty(key)) {
arrayPaths.push(...identifyArraysInJson(json[key], [...path, key], allowNestedArrays, onlyObjectArray));
}
}
}


return arrayPaths;
}

0 comments on commit f2dcbec

Please sign in to comment.