@@ -686,18 +680,18 @@
class="borderColumn borderColumnHeader"
on:mousemove={(event) => throttledHandleReorderDragOver(event, 0)}>#
- {#each $currentState.table.columns as column, index}
+ {#each $table.columns as column, index}
handleColumnInteractionStart(event, index)}
on:mousemove={(event) => throttledHandleReorderDragOver(event, index)}
- >{column[1].name}
+ >{column.name}
- throttledHandleReorderDragOver(event, $currentState.table?.columns.length ?? 0)}># | throttledHandleReorderDragOver(event, $table?.columns.length ?? 0)}
+ >#
@@ -732,22 +726,22 @@
class="borderColumn borderRight profiling"
on:mousemove={(event) => throttledHandleReorderDragOver(event, 0)}
>
- {#each $currentState.table.columns as column, index}
+ {#each $table.columns as column, index}
throttledHandleReorderDragOver(event, index)}
>
- {#if !column[1].profiling}
+ {#if !column.profiling}
Loading ...
{:else}
{/if}
@@ -756,8 +750,7 @@
{/each}
- throttledHandleReorderDragOver(event, $currentState.table?.columns.length ?? 0)}
+ on:mousemove={(event) => throttledHandleReorderDragOver(event, $table?.columns.length ?? 0)}
> |
@@ -784,8 +777,8 @@
|
- {#each $currentState.table.columns as _column, index}
- {#if index !== $currentState.table.columns.length - 1}
+ {#each $table.columns as _column, index}
+ {#if index !== $table.columns.length - 1}
- throttledHandleReorderDragOver(event, $currentState.table?.columns.length ?? 0)}
+ on:mousemove={(event) => throttledHandleReorderDragOver(event, $table?.columns.length ?? 0)}
> |
@@ -812,23 +804,23 @@
class:selectedColumn={selectedRowIndexes.includes(visibleStart + i)}
>{visibleStart + i}
- {#each $currentState.table.columns as column, index}
+ {#each $table.columns as column, index}
throttledHandleReorderDragOver(event, index)}
class:selectedColumn={selectedColumnIndexes.includes(index) ||
selectedRowIndexes.includes(visibleStart + i)}
- >{column[1].values[visibleStart + i] !== null &&
- column[1].values[visibleStart + i] !== undefined
- ? column[1].values[visibleStart + i]
+ >{column.values[visibleStart + i] !== null &&
+ column.values[visibleStart + i] !== undefined
+ ? column.values[visibleStart + i]
: ''} |
{/each}
- throttledHandleReorderDragOver(event, $currentState.table?.columns.length ?? 0)}
+ throttledHandleReorderDragOver(event, $table?.columns.length ?? 0)}
on:click={(event) => handleRowClick(event, visibleStart + i)}
class:selectedColumn={selectedRowIndexes.includes(visibleStart + i)}
>{visibleStart + i} | column[1].name) || [];
+ const columnNames = $table?.columns.map((column) => column.name) || [];
const possibleTableNames = ['Histogram', 'Boxplot', 'Heatmap', 'Lineplot', 'Scatterplot'];
let isLoadingGeneratedTab = false;
diff --git a/packages/safe-ds-eda/src/webviewState.ts b/packages/safe-ds-eda/src/webviewState.ts
index 24fbf0fc7..3ccf194fe 100644
--- a/packages/safe-ds-eda/src/webviewState.ts
+++ b/packages/safe-ds-eda/src/webviewState.ts
@@ -1,47 +1,48 @@
import type { FromExtensionMessage } from '../types/messaging';
-import type { State } from '../types/state';
+import type { HistoryEntry, Tab, Table } from '../types/state';
import { get, writable } from 'svelte/store';
-let currentTabIndex = writable
(undefined);
+const tabs = writable([]);
-let preventClicks = writable(false);
+const currentTabIndex = writable(undefined);
-let cancelTabIdsWaiting = writable([]);
+const preventClicks = writable(false);
+
+const cancelTabIdsWaiting = writable([]);
// Define the stores, current state to default in case the extension never calls setWebviewState( Shouldn't happen)
-const currentState = writable({ tableIdentifier: undefined, history: [], defaultState: true, tabs: [] });
+const table = writable();
+
+const history = writable([]);
window.addEventListener('message', (event) => {
const message = event.data as FromExtensionMessage;
// eslint-disable-next-line no-console
console.log(Date.now() + ': ' + message.command + ' called');
switch (message.command) {
- case 'setWebviewState':
- // This should be fired immediately whenever the panel is created or made visible again
- currentState.set(message.value);
+ case 'setInitialTable':
+ if (!get(table)) {
+ table.set(message.value);
+ } else {
+ throw new Error('setInitialTable called more than once');
+ }
break;
case 'setProfiling':
- if (get(currentState) && get(currentState).table) {
- currentState.update((state) => {
+ if (get(table)) {
+ table.update((currentTable) => {
return {
- ...state,
- table: {
- ...state.table!,
- columns: state.table!.columns.map((column) => {
- const profiling = message.value.find((p) => p.columnName === column[1].name);
- if (profiling) {
- return [
- column[0],
- {
- ...column[1],
- profiling: profiling.profiling,
- },
- ];
- } else {
- return column;
- }
- }),
- },
+ ...currentTable!,
+ columns: currentTable!.columns.map((column) => {
+ const profiling = message.value.find((p) => p.columnName === column.name);
+ if (profiling) {
+ return {
+ ...column,
+ profiling: profiling.profiling,
+ };
+ } else {
+ return column;
+ }
+ }),
};
});
}
@@ -49,4 +50,4 @@ window.addEventListener('message', (event) => {
}
});
-export { currentState, currentTabIndex, preventClicks, cancelTabIdsWaiting };
+export { history, tabs, table, currentTabIndex, preventClicks, cancelTabIdsWaiting };
diff --git a/packages/safe-ds-eda/types/messaging.ts b/packages/safe-ds-eda/types/messaging.ts
index 7a009310b..1ca6a3550 100644
--- a/packages/safe-ds-eda/types/messaging.ts
+++ b/packages/safe-ds-eda/types/messaging.ts
@@ -32,15 +32,15 @@ export type ToExtensionMessage =
| ToExtensionExecuteRunnerMessage;
// From extension
-type FromExtensionCommand = 'setWebviewState' | 'setProfiling' | 'runnerExecutionResult' | 'cancelRunnerExecution';
+type FromExtensionCommand = 'setInitialTable' | 'setProfiling' | 'runnerExecutionResult' | 'cancelRunnerExecution';
interface FromExtensionCommandMessage {
command: FromExtensionCommand;
value: any;
}
-interface FromExtensionSetStateMessage extends FromExtensionCommandMessage {
- command: 'setWebviewState';
- value: defaultTypes.State;
+interface FromExtensionSetInitialTableMessage extends FromExtensionCommandMessage {
+ command: 'setInitialTable';
+ value: defaultTypes.Table;
}
interface FromExtensionSetProfilingMessage extends FromExtensionCommandMessage {
@@ -79,7 +79,7 @@ export interface CancelRunnerExecutionMessage extends FromExtensionCommandMessag
}
export type FromExtensionMessage =
- | FromExtensionSetStateMessage
+ | FromExtensionSetInitialTableMessage
| FromExtensionSetProfilingMessage
| RunnerExecutionResultMessage
| CancelRunnerExecutionMessage;
diff --git a/packages/safe-ds-eda/types/state.ts b/packages/safe-ds-eda/types/state.ts
index c61cc4f6b..87c813926 100644
--- a/packages/safe-ds-eda/types/state.ts
+++ b/packages/safe-ds-eda/types/state.ts
@@ -1,12 +1,3 @@
-export interface State {
- tableIdentifier?: string;
- table?: Table;
- tabs: Tab[];
- defaultState?: boolean;
- history: HistoryEntry[];
- settings?: UserSettings;
-}
-
type InternalAction = 'reorderColumns' | 'resizeColumn' | 'hideColumn' | 'highlightColumn' | 'emptyTab';
type ExternalManipulatingAction = 'filterColumn' | 'sortColumn' | TableFilterTypes;
type ExternalVisualizingAction = TabType | 'refreshTab';
@@ -197,11 +188,12 @@ export type PlotTab = OneColumnTab | TwoColumnTab | NoColumnTab;
// ------------------ Types for the Table ------------------
export interface Table {
- columns: [number, Column][];
+ tableIdentifier?: string;
+ name: string;
+ columns: Column[];
visibleRows?: number;
totalRows: number;
- name: string;
- appliedFilters: TableFilter[];
+ appliedFilters: TableFilter;
}
// ------------ Types for the Profiling -----------
@@ -314,13 +306,14 @@ type TableFilterTypes =
| 'hideDuplicateRows'
| 'hideRowsWithOutliers';
-export interface TableFilter extends FilterBase {
- type: TableFilterTypes;
-}
+export type TableFilter = {
+ [key in TableFilterTypes]?: boolean;
+};
// ------------ Types for the Settings -----------
export interface UserSettings {
profiling: ProfilingSettings;
+ darkMode: boolean;
}
interface ProfilingSettingsBase {
diff --git a/packages/safe-ds-vscode/src/extension/eda/apis/runnerApi.ts b/packages/safe-ds-vscode/src/extension/eda/apis/runnerApi.ts
index 93d0d2f72..85382f178 100644
--- a/packages/safe-ds-vscode/src/extension/eda/apis/runnerApi.ts
+++ b/packages/safe-ds-vscode/src/extension/eda/apis/runnerApi.ts
@@ -313,7 +313,6 @@ export class RunnerApi {
appliedFilters: [] as Table['appliedFilters'],
};
- let i = 0;
let currentMax = 0;
for (const [columnName, columnValues] of Object.entries(pythonTableColumns)) {
if (!Array.isArray(columnValues)) {
@@ -336,7 +335,7 @@ export class RunnerApi {
appliedSort: null,
coloredHighLow: false,
};
- table.columns.push([i++, column]);
+ table.columns.push(column);
}
table.totalRows = currentMax;
table.visibleRows = currentMax;
@@ -368,31 +367,27 @@ export class RunnerApi {
// Generate SDS code to get missing value ratio for each column
for (const column of columns) {
- const newMvPlaceholderName = this.genPlaceholderName(column[1].name + '_mv');
+ const newMvPlaceholderName = this.genPlaceholderName(column.name + '_mv');
placeholderNames.push(newMvPlaceholderName);
- columnNameToPlaceholderMVNameMap.set(column[1].name, newMvPlaceholderName);
- sdsStrings += this.sdsStringForMissingValueRatioByColumnName(
- column[1].name,
- table.name,
- newMvPlaceholderName,
- );
+ columnNameToPlaceholderMVNameMap.set(column.name, newMvPlaceholderName);
+ sdsStrings += this.sdsStringForMissingValueRatioByColumnName(column.name, table.name, newMvPlaceholderName);
// Find unique values
// TODO reevaluate when image stuck problem fixed
let uniqueValues = new Set();
- for (let j = 0; j < column[1].values.length; j++) {
- uniqueValues.add(column[1].values[j]);
+ for (let j = 0; j < column.values.length; j++) {
+ uniqueValues.add(column.values[j]);
}
- uniqueValuesMap.set(column[1].name, uniqueValues);
+ uniqueValuesMap.set(column.name, uniqueValues);
// Different histogram conditions for numerical and categorical columns
- if (column[1].type !== 'numerical') {
+ if (column.type !== 'numerical') {
if (uniqueValues.size <= 3 || uniqueValues.size > 10) {
// Must match conidtions below that choose to display histogram for categorical columns
continue; // This historam only generated if between 4-10 categorigal uniques or numerical type
}
} else {
- if (uniqueValues.size > column[1].values.length * 0.9) {
+ if (uniqueValues.size > column.values.length * 0.9) {
// Must match conidtions below that choose to display histogram for numerical columns
// If 90% of values are unique, it's not a good idea to display histogram
continue;
@@ -400,14 +395,10 @@ export class RunnerApi {
}
// Histogram for numerical columns or categorical columns with 4-10 unique values
- const newHistogramPlaceholderName = this.genPlaceholderName(column[1].name + '_hist');
+ const newHistogramPlaceholderName = this.genPlaceholderName(column.name + '_hist');
placeholderNames.push(newHistogramPlaceholderName);
- columnNameToPlaceholderHistogramNameMap.set(column[1].name, newHistogramPlaceholderName);
- sdsStrings += this.sdsStringForHistogramByColumnName(
- column[1].name,
- table.name,
- newHistogramPlaceholderName,
- );
+ columnNameToPlaceholderHistogramNameMap.set(column.name, newHistogramPlaceholderName);
+ sdsStrings += this.sdsStringForHistogramByColumnName(column.name, table.name, newHistogramPlaceholderName);
}
// Execute with generated SDS code
@@ -439,7 +430,7 @@ export class RunnerApi {
for (const column of columns) {
// Base info for the top of the profiling
const missingValuesRatio =
- missingValueRatioMap.get(columnNameToPlaceholderMVNameMap.get(column[1].name)!)! * 100;
+ missingValueRatioMap.get(columnNameToPlaceholderMVNameMap.get(column.name)!)! * 100;
const validRatio: ProfilingDetailStatistical = {
type: 'numerical',
@@ -455,19 +446,16 @@ export class RunnerApi {
interpretation: missingValuesRatio > 0 ? 'error' : 'default',
};
- const uniqueValues = uniqueValuesMap.get(column[1].name)!.size;
+ const uniqueValues = uniqueValuesMap.get(column.name)!.size;
// If not numerical, add proper profilings according to idness results
- if (column[1].type !== 'numerical') {
+ if (column.type !== 'numerical') {
if (uniqueValues <= 3) {
// Can display each separate percentages of unique values
// Find all unique values and count them
const uniqueValueCounts = new Map();
- for (let i = 0; i < column[1].values.length; i++) {
- if (column[1].values[i] !== undefined && column[1].values[i] !== null)
- uniqueValueCounts.set(
- column[1].values[i],
- (uniqueValueCounts.get(column[1].values[i]) || 0) + 1,
- );
+ for (let i = 0; i < column.values.length; i++) {
+ if (column.values[i] !== undefined && column.values[i] !== null)
+ uniqueValueCounts.set(column.values[i], (uniqueValueCounts.get(column.values[i]) || 0) + 1);
}
let uniqueProfilings: ProfilingDetailStatistical[] = [];
@@ -475,13 +463,13 @@ export class RunnerApi {
uniqueProfilings.push({
type: 'numerical',
name: key,
- value: ((value / column[1].values.length) * 100).toFixed(2) + '%',
+ value: ((value / column.values.length) * 100).toFixed(2) + '%',
interpretation: 'category',
});
}
profiling.push({
- columnName: column[1].name,
+ columnName: column.name,
profiling: {
validRatio,
missingRatio,
@@ -493,10 +481,10 @@ export class RunnerApi {
});
} else if (uniqueValues <= 10) {
// Display histogram for 4-10 unique values, has to match the condition above where histogram is generated
- const histogram = histogramMap.get(columnNameToPlaceholderHistogramNameMap.get(column[1].name)!)!;
+ const histogram = histogramMap.get(columnNameToPlaceholderHistogramNameMap.get(column.name)!)!;
profiling.push({
- columnName: column[1].name,
+ columnName: column.name,
profiling: {
validRatio,
missingRatio,
@@ -509,7 +497,7 @@ export class RunnerApi {
} else {
// Display only the number of unique values vs total valid values
profiling.push({
- columnName: column[1].name,
+ columnName: column.name,
profiling: {
validRatio,
missingRatio,
@@ -524,10 +512,10 @@ export class RunnerApi {
type: 'text',
value:
Math.round(
- column[1].values.length *
+ column.values.length *
(1 -
(missingValueRatioMap.get(
- columnNameToPlaceholderMVNameMap.get(column[1].name)!,
+ columnNameToPlaceholderMVNameMap.get(column.name)!,
) || 0)),
) + ' Total Valids',
interpretation: 'default',
@@ -537,9 +525,9 @@ export class RunnerApi {
});
}
} else {
- if (uniqueValues > column[1].values.length * 0.9) {
+ if (uniqueValues > column.values.length * 0.9) {
profiling.push({
- columnName: column[1].name,
+ columnName: column.name,
profiling: {
validRatio,
missingRatio,
@@ -554,10 +542,10 @@ export class RunnerApi {
type: 'text',
value:
Math.round(
- column[1].values.length *
+ column.values.length *
(1 -
(missingValueRatioMap.get(
- columnNameToPlaceholderMVNameMap.get(column[1].name)!,
+ columnNameToPlaceholderMVNameMap.get(column.name)!,
) || 0)),
) + ' Total Valids',
interpretation: 'default',
@@ -566,10 +554,10 @@ export class RunnerApi {
},
});
} else {
- const histogram = histogramMap.get(columnNameToPlaceholderHistogramNameMap.get(column[1].name)!)!;
+ const histogram = histogramMap.get(columnNameToPlaceholderHistogramNameMap.get(column.name)!)!;
profiling.push({
- columnName: column[1].name,
+ columnName: column.name,
profiling: {
validRatio,
missingRatio,
diff --git a/packages/safe-ds-vscode/src/extension/eda/edaPanel.ts b/packages/safe-ds-vscode/src/extension/eda/edaPanel.ts
index bb7ee5ecd..b744cae19 100644
--- a/packages/safe-ds-vscode/src/extension/eda/edaPanel.ts
+++ b/packages/safe-ds-vscode/src/extension/eda/edaPanel.ts
@@ -1,7 +1,7 @@
import * as vscode from 'vscode';
import { ToExtensionMessage } from '@safe-ds/eda/types/messaging.js';
import * as webviewApi from './apis/webviewApi.ts';
-import { State } from '@safe-ds/eda/types/state.ts';
+import { Table } from '@safe-ds/eda/types/state.ts';
import { SafeDsServices, ast } from '@safe-ds/lang';
import { RunnerApi } from './apis/runnerApi.ts';
import { safeDsLogger } from '../helpers/logging.js';
@@ -201,28 +201,18 @@ export class EDAPanel {
dark: vscode.Uri.joinPath(edaPanel.extensionUri, 'img', 'binoculars-solid.png'),
};
await edaPanel.waitForUpdateHtmlDone(10000);
- const stateInfo = await edaPanel.constructCurrentState();
+ const table = await edaPanel.getBaseTable();
webviewApi.postMessage(edaPanel!.panel.webview, {
- command: 'setWebviewState',
- value: stateInfo.state,
+ command: 'setInitialTable',
+ value: table,
});
- // TODO: if from existing state, show disclaimer that updated data is loading and execute pipeline + history + profiling and send
-
- if (
- !stateInfo.fromExisting ||
- !stateInfo.state.table ||
- !stateInfo.state.table!.columns.find((c) => c[1].profiling)
- ) {
- const profiling = await EDAPanel.panelsMap
- .get(tableIdentifier)!
- .runnerApi.getProfiling(stateInfo.state.table!);
-
- webviewApi.postMessage(edaPanel!.panel.webview, {
- command: 'setProfiling',
- value: profiling,
- });
- }
+ const profiling = await EDAPanel.panelsMap.get(tableIdentifier)!.runnerApi.getProfiling(table);
+
+ webviewApi.postMessage(edaPanel!.panel.webview, {
+ command: 'setProfiling',
+ value: profiling,
+ });
}
}
//#endregion
@@ -255,7 +245,7 @@ export class EDAPanel {
//#endregion
//#region State handling
- private async constructCurrentState(): Promise<{ state: State; fromExisting: boolean }> {
+ private async getBaseTable(): Promise {
const panel = EDAPanel.panelsMap.get(this.tableIdentifier);
if (!panel) {
throw new Error('Panel not found.');
@@ -264,16 +254,7 @@ export class EDAPanel {
if (!table) {
throw new Error('Timeout waiting for placeholder value');
} else {
- return {
- state: {
- tableIdentifier: panel.tableIdentifier,
- history: [],
- defaultState: false,
- table,
- tabs: [],
- },
- fromExisting: false,
- };
+ return table;
}
}
}