From e4a7a92b7692b8a71c29056b85e10e272f8e9d3e Mon Sep 17 00:00:00 2001 From: Sjoerd <svink@graphpolaris.com> Date: Thu, 24 Oct 2024 12:42:44 +0000 Subject: [PATCH] feat: incorporate new statistics refactoring --- libs/shared/lib/components/inputs/index.tsx | 2 +- .../lib/components/textEditor/TextEditor.tsx | 2 +- .../textEditor/plugins/PreviewPlugin.tsx | 7 +- .../lib/data-access/statistics/dimensions.ts | 51 ------ .../lib/data-access/statistics/index.ts | 3 - .../lib/data-access/statistics/statistics.ts | 168 ------------------ .../statistics/statistics.types.ts | 84 --------- .../store/graphQueryResultSlice.ts | 36 +--- libs/shared/lib/data-access/store/hooks.ts | 6 +- libs/shared/lib/statistics/graphStatistics.ts | 5 +- libs/shared/lib/statistics/index.ts | 1 + libs/shared/lib/vis/common/types.ts | 6 +- .../visualizations/arcplotvis/arcplotvis.tsx | 25 +-- .../mapvis/components/MapSettings.tsx | 17 +- .../layers/nodelink-layer/NodeLinkOptions.tsx | 10 +- .../vis/visualizations/mapvis/mapvis.types.ts | 4 +- .../configPanel/SemSubsConfigPanel.tsx | 12 +- .../lib/vis/visualizations/vis1D/Vis1D.tsx | 2 +- 18 files changed, 61 insertions(+), 380 deletions(-) delete mode 100644 libs/shared/lib/data-access/statistics/dimensions.ts delete mode 100644 libs/shared/lib/data-access/statistics/index.ts delete mode 100644 libs/shared/lib/data-access/statistics/statistics.ts delete mode 100644 libs/shared/lib/data-access/statistics/statistics.types.ts diff --git a/libs/shared/lib/components/inputs/index.tsx b/libs/shared/lib/components/inputs/index.tsx index 57d4600de..58f207dff 100644 --- a/libs/shared/lib/components/inputs/index.tsx +++ b/libs/shared/lib/components/inputs/index.tsx @@ -34,7 +34,7 @@ type TextProps = { className?: string; validate?: (value: any) => boolean; onChange?: (value: string) => void; - onClick?: (e: React.MouseEvent<HTMLElement>) => void; + onClick?: (e: React.MouseEvent<HTMLInputElement>) => void; }; type NumberProps = { diff --git a/libs/shared/lib/components/textEditor/TextEditor.tsx b/libs/shared/lib/components/textEditor/TextEditor.tsx index 9d71b27bb..f3ba1ce03 100644 --- a/libs/shared/lib/components/textEditor/TextEditor.tsx +++ b/libs/shared/lib/components/textEditor/TextEditor.tsx @@ -36,7 +36,7 @@ export function TextEditor({ editorState, setEditorState, showToolbar, placehold nodes: [VariableNode], }; - const contentEditableRef = useRef(); + const contentEditableRef = useRef<HTMLDivElement>(null); return ( <LexicalComposer initialConfig={initialConfig}> diff --git a/libs/shared/lib/components/textEditor/plugins/PreviewPlugin.tsx b/libs/shared/lib/components/textEditor/plugins/PreviewPlugin.tsx index cb81db1ae..9ac8ccc21 100644 --- a/libs/shared/lib/components/textEditor/plugins/PreviewPlugin.tsx +++ b/libs/shared/lib/components/textEditor/plugins/PreviewPlugin.tsx @@ -1,10 +1,9 @@ -import { LegacyRef, useRef, useEffect, MutableRefObject } from 'react'; +import { LegacyRef, useRef, useEffect, MutableRefObject, RefObject } from 'react'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; -import { FORMAT_TEXT_COMMAND } from 'lexical'; import { Button } from '../../buttons'; import { $generateHtmlFromNodes } from '@lexical/html'; -export function PreviewPlugin({ contentEditable }: { contentEditable: MutableRefObject<HTMLDivElement | undefined> }): JSX.Element { +export function PreviewPlugin({ contentEditable }: { contentEditable: RefObject<HTMLDivElement | undefined> }): JSX.Element { const [editor] = useLexicalComposerContext(); function updatePreview() { @@ -47,7 +46,7 @@ export function PreviewPlugin({ contentEditable }: { contentEditable: MutableRef if (editor == null) return; editor.classList.remove('hidden'); - editor.querySelector('div').focus(); + editor?.querySelector('div')?.focus(); let preview = editor.parentElement?.querySelector('.editor + .preview'); if (preview == null) return; diff --git a/libs/shared/lib/data-access/statistics/dimensions.ts b/libs/shared/lib/data-access/statistics/dimensions.ts deleted file mode 100644 index 11e21cd34..000000000 --- a/libs/shared/lib/data-access/statistics/dimensions.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { DimensionType } from '@graphpolaris/shared/lib/schema'; - -const isNumerical = (value: any): boolean => { - if (typeof value === 'number') return true; - const parsedNumber = parseFloat(value); - if (!isNaN(parsedNumber)) return true; - return false; -}; - -const isSpatial = (value: any): boolean => { - if (Array.isArray(value) && value.length === 2) { - const [latitude, longitude] = value; - if (typeof latitude === 'number' && typeof longitude === 'number') { - return true; - } - } else if (typeof value === 'object' && value !== null) { - if ('latitude' in value && 'longitude' in value) { - const { latitude, longitude } = value; - if (typeof latitude === 'number' && typeof longitude === 'number') { - return true; - } - } - } - return false; -}; - -// TODO: implement temporal classifier -function isTemporal(value: any): boolean { - return false; - // const datetime = moment(value); - // if (datetime.isValid()) { - // return true; - // } - // return false; -} - -export const getDimension = (value: any): DimensionType => { - if (Array.isArray(value)) { - if (value.length === 0) return 'categorical'; - return getDimension(value[0]); - } - - if (isTemporal(value)) { - return 'temporal'; - } else if (isSpatial(value)) { - return 'spatial'; - } else if (isNumerical(value)) { - return 'numerical'; - } - return 'categorical'; -}; diff --git a/libs/shared/lib/data-access/statistics/index.ts b/libs/shared/lib/data-access/statistics/index.ts deleted file mode 100644 index 58f57b693..000000000 --- a/libs/shared/lib/data-access/statistics/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { getDimension } from './dimensions'; -export { extractStatistics } from './statistics'; -export * from './statistics.types'; diff --git a/libs/shared/lib/data-access/statistics/statistics.ts b/libs/shared/lib/data-access/statistics/statistics.ts deleted file mode 100644 index f44855884..000000000 --- a/libs/shared/lib/data-access/statistics/statistics.ts +++ /dev/null @@ -1,168 +0,0 @@ -import { DimensionType } from '@graphpolaris/shared/lib/schema'; -import { GraphMetadata, DimensionStatistics } from './statistics.types'; -import { CategoricalStats, NumericalStats, SpatialStats, TemporalStats } from './statistics.types'; - -function extractCategoricalStats(data: any[]): CategoricalStats { - const uniqueItems = new Set<string>(data.map((item) => String(item).slice(0, 50))); - const uniqueItemCount = uniqueItems.size; - - // Calculate mode - const frequencyMap: { [key: string]: number } = {}; - data.forEach((item) => { - const strItem = String(item).slice(0, 100); - frequencyMap[strItem] = (frequencyMap[strItem] || 0) + 1; - }); - const mode = Object.keys(frequencyMap).reduce((a, b) => (frequencyMap[a] > frequencyMap[b] ? a : b), ''); - - // Calculate entropy - const totalItems = data.length; - const entropy = -Object.values(frequencyMap).reduce((acc, frequency) => { - const probability = frequency / totalItems; - if (probability === 0) return acc; - return acc + probability * Math.log2(probability); - }, 0); - - // Calculate Gini Index - const giniIndex = - 1 - - Object.values(frequencyMap).reduce((acc, frequency) => { - const probability = frequency / totalItems; - if (probability === 0) return acc; - return acc + Math.pow(probability, 2); - }, 0); - - return { - uniqueItems: uniqueItemCount, - mode, - entropy, - giniIndex, - values: Array.from(uniqueItems), - }; -} - -function extractNumericalStats(data: number[]): NumericalStats { - const n = data.length; - if (n === 0) { - return { - min: 0, - max: 0, - average: 0, - variance: 0, - std: 0, - kurtosis: 0, - }; - } - - const min = Math.min(...data); - const max = Math.max(...data); - const sum = data.reduce((acc, curr) => acc + curr, 0); - const average = sum / n; - - let variance = 0; - for (const value of data) { - variance += Math.pow(value - average, 2); - } - variance /= n; - const std = Math.sqrt(variance); - - let kurtosis = 0; - if (std !== 0) { - const fourthMoment = data.reduce((acc, curr) => acc + Math.pow(curr - average, 4), 0) / n; - kurtosis = fourthMoment / Math.pow(std, 4) - 3; - } - - return { - min, - max, - average, - variance, - std, - kurtosis: Number.isFinite(kurtosis) ? kurtosis : 0, - }; -} - -function extractTemporalStats(data: Date[]): TemporalStats { - const sortedDates = data.slice().sort((a, b) => a.getTime() - b.getTime()); - const minDate = sortedDates[0]; - const maxDate = sortedDates[sortedDates.length - 1]; - const range = maxDate.getTime() - minDate.getTime(); - - return { - minDate, - maxDate, - range, - minTimestamp: minDate.getTime(), - maxTimestamp: maxDate.getTime(), - }; -} - -function extractSpatialStats(data: [number, number][]): SpatialStats { - const totalPoints = data.length; - const sumCoordinates = data.reduce(([x1, y1], [x2, y2]) => [x1 + x2, y1 + y2], [0, 0]); - const middlePoint: [number, number] = [sumCoordinates[0] / totalPoints, sumCoordinates[1] / totalPoints]; - - const minX = Math.min(...data.map(([x, _]) => x)); - const minY = Math.min(...data.map(([_, y]) => y)); - const maxX = Math.max(...data.map(([x, _]) => x)); - const maxY = Math.max(...data.map(([_, y]) => y)); - - const boundingBox: [[number, number], [number, number]] = [ - [minX, minY], - [maxX, maxY], - ]; - - return { - middlePoint, - minX, - minY, - maxX, - maxY, - boundingBox, - }; -} - -export const extractStatistics = (summaryGraph: GraphMetadata): GraphMetadata => { - const graph = summaryGraph; - - Object.entries(graph.nodes.types).forEach(([label, node]) => { - Object.entries(node.attributes).forEach(([id, object]) => { - const { values, dimension } = object; - const statistics = calculateStatistics(values || [], dimension); - graph.nodes.types[label].attributes[id] = { dimension, ...statistics }; - }); - }); - - Object.entries(graph.edges.types).forEach(([label, edge]) => { - Object.entries(edge.attributes).forEach(([id, object]) => { - const { values, dimension } = object; - const statistics = calculateStatistics(values || [], dimension); - graph.edges.types[label].attributes[id] = { dimension, ...statistics }; - }); - }); - - console.debug('Graph query statistics', summaryGraph); - return graph; -}; - -export const calculateStatistics = (item: any[], dimensionType: DimensionType) => { - let statistics: DimensionStatistics | undefined; - - switch (dimensionType) { - case 'categorical': - statistics = extractCategoricalStats(item); - break; - case 'numerical': - statistics = extractNumericalStats(item); - break; - case 'spatial': - statistics = extractSpatialStats(item); - break; - case 'temporal': - statistics = extractTemporalStats(item); - break; - default: - statistics = extractCategoricalStats(item); - } - - return statistics; -}; diff --git a/libs/shared/lib/data-access/statistics/statistics.types.ts b/libs/shared/lib/data-access/statistics/statistics.types.ts deleted file mode 100644 index ac170024e..000000000 --- a/libs/shared/lib/data-access/statistics/statistics.types.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { DimensionType } from '../../schema'; - -export type CategoricalStats = { - uniqueItems: number; - values: string[]; - mode: string; - entropy: number; - giniIndex: number; -}; - -export type NumericalStats = { - min: number; - max: number; - average: number; - variance: number; - std: number; - kurtosis: number; -}; - -export type TemporalStats = { - minDate: Date; - maxDate: Date; - range: number; - minTimestamp: number; - maxTimestamp: number; -}; - -export type Coordinate = [number, number]; - -export type SpatialStats = { - middlePoint: Coordinate; - minX: number; - minY: number; - maxX: number; - maxY: number; - boundingBox: [Coordinate, Coordinate]; -}; - -export type DimensionStatistics = CategoricalStats | NumericalStats | TemporalStats | SpatialStats; - -export type DimensionInfoType = { [label: string]: { [attribute: string]: DimensionType } }; - -export type DimensionMap = { - nodes: DimensionInfoType; - edges: DimensionInfoType; -}; - -export type ElementStats = { - dimension: DimensionType; - stats?: DimensionStatistics; -}; - -export type ElementStatisticsType = { - [label: string]: { [attribute: string]: ElementStats }; -}; - -export interface GraphStatistics { - nodes: ElementStatisticsType; - edges: ElementStatisticsType; -} - -export type CompressedElement = { - count?: number; - labels: string[]; - types: { - [label: string]: { - count: number; - avgDegreeIn?: number; - avgDegreeOut?: number; - attributes: { - [id: string]: { - dimension: DimensionType; - values?: any[]; - statistics?: any; - }; - }; - }; - }; -}; - -export type GraphMetadata = { - nodes: CompressedElement; - edges: CompressedElement; -}; diff --git a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts index a9f919a13..4f0870242 100755 --- a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts +++ b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts @@ -1,7 +1,6 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import type { RootState } from './store'; -import { getDimension, extractStatistics, GraphMetadata } from '../statistics'; -import { getGraphStatistics } from '../../statistics'; +import { getGraphStatistics, GraphStatistics } from '../../statistics'; export interface GraphQueryResultFromBackendPayload { queryID: string; @@ -50,7 +49,7 @@ export type Edge = { // Define a type for the slice state export type GraphQueryResult = { - metaData?: GraphMetadata; + metaData?: GraphStatistics; nodes: Node[]; edges: Edge[]; queryingBackend: boolean; @@ -79,11 +78,6 @@ export const graphQueryBackend2graphQuery = (payload: GraphQueryResultFromBacken let nodes = [...nodeIDs].map((nodeID) => payload.nodes.find((node) => node._id === nodeID) as unknown as Node); let edges = [...edgeIDs].map((edgeID) => payload.edges.find((edge) => edge._id === edgeID) as unknown as Edge); - const metaData: GraphMetadata = { - nodes: { labels: [], count: nodes.length, types: {} }, - edges: { labels: [], count: edges.length, types: {} }, - }; - nodes = nodes.map((node) => { let _node = { ...node }; // TODO!: only works for neo4j @@ -91,17 +85,6 @@ export const graphQueryBackend2graphQuery = (payload: GraphQueryResultFromBacken let innerLabels = node?.attributes?.labels as string[]; if (innerLabels?.length > 0) nodeType = innerLabels[0] as string; - if (!metaData.nodes.labels.includes(nodeType)) metaData.nodes.labels?.push(nodeType); - if (!metaData.nodes.types[nodeType]) metaData.nodes.types[nodeType] = { count: 0, attributes: {} }; - metaData.nodes.types[nodeType].count++; - - Object.entries(_node.attributes).forEach(([attributeId, attributeValue]) => { - if (!metaData.nodes.types[nodeType].attributes[attributeId]) { - const dimension = getDimension(attributeValue); - metaData.nodes.types[nodeType].attributes[attributeId] = { dimension, values: [], statistics: {} }; - } - metaData.nodes.types[nodeType].attributes[attributeId].values?.push(attributeValue); - }); _node.label = nodeType; return _node; @@ -113,23 +96,14 @@ export const graphQueryBackend2graphQuery = (payload: GraphQueryResultFromBacken let edgeType: string = edge._id.split('/')[0]; if (!_edge._id.includes('/')) edgeType = edge.attributes.Type as string; - if (!metaData.edges.labels?.includes(edgeType)) metaData.edges.labels?.push(edgeType); - if (!metaData.edges.types[edgeType]) metaData.edges.types[edgeType] = { count: 0, attributes: {} }; - metaData.edges.types[edgeType].count++; - - Object.entries(_edge.attributes).forEach(([attributeId, attributeValue]) => { - if (!metaData.edges.types[edgeType].attributes[attributeId]) { - const dimension = getDimension(attributeValue); - metaData.edges.types[edgeType].attributes[attributeId] = { dimension, values: [], statistics: {} }; - } - metaData.edges.types[edgeType].attributes[attributeId].values?.push(attributeValue); - }); _edge.label = edgeType; return _edge; }); - return { metaData: extractStatistics(metaData), nodes, edges }; + const metaData = getGraphStatistics(payload); + + return { metaData, nodes, edges }; }; export const graphQueryResultSlice = createSlice({ diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts index d55cbb791..9e7feb3fe 100644 --- a/libs/shared/lib/data-access/store/hooks.ts +++ b/libs/shared/lib/data-access/store/hooks.ts @@ -22,7 +22,7 @@ import { selectQuerybuilderHash, } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { activeSaveState, activeSaveStateAuthorization, SessionCacheI, sessionCacheState } from './sessionSlice'; -import { AuthSliceState, UseIsAuthorizedState, authState } from './authSlice'; +import { AuthSliceState, authState } from './authSlice'; import { visualizationState, VisState, visualizationActive } from './visualizationSlice'; import { ML, allMLEnabled, selectML } from './mlSlice'; import { @@ -37,12 +37,12 @@ import { import { AllLayoutAlgorithms } from '../../graph-layout'; import { QueryGraphEdgeHandle, QueryMultiGraph } from '../../querybuilder'; import { SchemaGraph, SchemaGraphInference, SchemaGraphStats } from '../../schema'; -import { GraphMetadata } from '../statistics'; import { SelectionStateI, FocusStateI, focusState, selectionState } from './interactionSlice'; import { VisualizationSettingsType } from '../../vis/common'; import { PolicyUsersState, selectPolicyState } from './authorizationUsersSlice'; import { PolicyResourcesState, selectResourcesPolicyState } from './authorizationResourcesSlice'; import { SaveStateAuthorizationHeaders, SaveStateI } from '..'; +import { GraphStatistics } from '../../statistics'; // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch: () => AppDispatch = useDispatch; @@ -51,7 +51,7 @@ export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector; /** Gives the graphQueryResult from the store */ export const useGraphQueryResult: () => GraphQueryResult = () => useAppSelector(selectGraphQueryResult); -export const useGraphQueryResultMeta: () => GraphMetadata | undefined = () => useAppSelector(selectGraphQueryResultMetaData); +export const useGraphQueryResultMeta: () => GraphStatistics | undefined = () => useAppSelector(selectGraphQueryResultMetaData); // Gives the schema export const useSchema: () => SchemaSliceI = () => useAppSelector(schema); diff --git a/libs/shared/lib/statistics/graphStatistics.ts b/libs/shared/lib/statistics/graphStatistics.ts index 53ead16bd..7f2db7998 100644 --- a/libs/shared/lib/statistics/graphStatistics.ts +++ b/libs/shared/lib/statistics/graphStatistics.ts @@ -8,8 +8,10 @@ const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphStatistics const n_nodes = nodes.length; const n_edges = edges.length; + const density = n_nodes < 2 ? 0 : (n_edges * 2) / (n_nodes * (n_nodes - 1)); + const metaData: GraphStatistics = { - topological: { density: (n_edges * 2) / (n_nodes * (n_nodes - 1)) || 0, self_loops: 0 }, + topological: { density, self_loops: 0 }, nodes: { labels: [], count: n_nodes, types: {} }, edges: { labels: [], count: n_edges, types: {} }, }; @@ -37,6 +39,7 @@ const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphStatistics }); }); + // Process edges edges.forEach((edge) => { const edgeType = getEdgeType(edge); if (!metaData.edges.labels.includes(edgeType)) { diff --git a/libs/shared/lib/statistics/index.ts b/libs/shared/lib/statistics/index.ts index e00f85d98..979311f96 100644 --- a/libs/shared/lib/statistics/index.ts +++ b/libs/shared/lib/statistics/index.ts @@ -1 +1,2 @@ export * from './graphStatistics'; +export * from './statistics.types'; diff --git a/libs/shared/lib/vis/common/types.ts b/libs/shared/lib/vis/common/types.ts index 3f2c302cb..118c0a933 100644 --- a/libs/shared/lib/vis/common/types.ts +++ b/libs/shared/lib/vis/common/types.ts @@ -3,9 +3,9 @@ import { GraphQueryResult } from '../../data-access/store/graphQueryResultSlice' import { ML } from '../../data-access/store/mlSlice'; import { SchemaGraph } from '../../schema'; import type { AppDispatch } from '../../data-access'; -import { GraphMetadata } from '../../data-access/statistics'; import { Node, Edge } from '../../data-access/store/graphQueryResultSlice'; import { Visualizations } from '../components/VisualizationPanel'; +import { GraphStatistics } from '../../statistics'; export type VisualizationSettingsType = { id: string; @@ -28,7 +28,7 @@ export type VisualizationPropTypes<T = {}> = { ml: ML; settings: T & VisualizationSettingsType; dispatch: AppDispatch; - graphMetadata: GraphMetadata; + graphMetadata: GraphStatistics; updateSettings: (newSettings: any) => void; handleHover: (val: any) => void; handleSelect: (selection?: { nodes?: Node[]; edges?: Edge[] }) => void; @@ -36,7 +36,7 @@ export type VisualizationPropTypes<T = {}> = { export type VisualizationSettingsPropTypes<T = {}> = { settings: T & VisualizationSettingsType; - graphMetadata: GraphMetadata; + graphMetadata: GraphStatistics; updateSettings: (val: Partial<T>) => void; }; diff --git a/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx b/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx index 8b7fad619..c7a336462 100644 --- a/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx +++ b/libs/shared/lib/vis/visualizations/arcplotvis/arcplotvis.tsx @@ -1,16 +1,15 @@ import { Input } from '@graphpolaris/shared/lib/components/inputs'; -import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { SettingsContainer } from '@graphpolaris/shared/lib/vis/components/config'; import { select, selectAll } from 'd3'; import React, { useEffect, useMemo, useRef } from 'react'; import { VISComponentType, VisualizationPropTypes } from '../../common'; import ArcVisNodes from './components/arcplotd3nodes'; - import { Edge, Node } from '@graphpolaris/shared/lib/data-access'; import { scaleOrdinal, schemeCategory10 } from 'd3'; import { ArcEdge, ArcNode } from './arcplotvis.types'; import ArcVisLine from './components/arcplotd3line'; import ArcVisLinks from './components/arcplotd3links'; +import { GraphStatistics } from '@graphpolaris/shared/lib/statistics'; export interface ArcVisProps { iconStyle: string; @@ -33,7 +32,7 @@ export const configStyle = { const margin = { top: 20, right: 20, bottom: 20, left: 20 }; -export const ArcPlotVis = React.memo(({ data, configuration }: VisualizationPropTypes) => { +export const ArcPlotVis = React.memo(({ data, settings }: VisualizationPropTypes) => { const svgRef = useRef<SVGSVGElement>(null); const [dimensions, setDimensions] = React.useState({ width: 0, height: 0 }); @@ -57,7 +56,7 @@ export const ArcPlotVis = React.memo(({ data, configuration }: VisualizationProp }, []); const getNodeOrdering = (node: Node) => { - switch (configuration.ordering) { + switch (settings.ordering) { case 'Name': return node.label.charCodeAt(0) * 10; case 'Group': @@ -108,7 +107,7 @@ export const ArcPlotVis = React.memo(({ data, configuration }: VisualizationProp }); return [dataNodes as ArcNode[], dataLinks as ArcEdge[]]; // MIB: no idea why typescript needs this cast so explicitly - }, [data, configuration, dimensions]); + }, [data, settings, dimensions]); const handleMouseEnterNodes = (event: React.MouseEvent<SVGGElement, MouseEvent>) => { const targetClassList = (event.currentTarget as SVGGElement).classList; @@ -193,8 +192,8 @@ export const ArcPlotVis = React.memo(({ data, configuration }: VisualizationProp nodes={dataNodes} handleMouseEnter={handleMouseEnterNodes} handleMouseOut={handleMouseOutNodes} - iconStyle={configuration.iconStyle} - drawLabels={configuration.labels} + iconStyle={settings.iconStyle} + drawLabels={settings.labels} /> } { @@ -212,7 +211,7 @@ const ArcPlotVisSettings = ({ updateSettings, }: { configuration: ArcVisProps; - graph: GraphMetadata; + graph: GraphStatistics; updateSettings: (val: any) => void; }) => { return ( @@ -236,10 +235,14 @@ const ArcPlotVisSettings = ({ ); }; +// remove ts ignore when added to Visualization Dict, because displayname needs to be key of it +// @ts-ignore export const ArcVisComponent: VISComponentType = { - displayName: 'Arc Plot', + displayName: 'ArcPlot', + description: '', component: ArcPlotVis, - settings: ArcPlotVisSettings, - configuration: configuration, + settingsComponent: ArcPlotVisSettings, + settings: configuration, + exportImage: () => {}, }; export default ArcVisComponent; diff --git a/libs/shared/lib/vis/visualizations/mapvis/components/MapSettings.tsx b/libs/shared/lib/vis/visualizations/mapvis/components/MapSettings.tsx index 066629a48..317245c62 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/components/MapSettings.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/components/MapSettings.tsx @@ -10,13 +10,16 @@ export const MapSettings = ({ settings, graphMetadata, updateSettings }: Visuali // For backwards compatibility with older saveStates, we migrate information from settings.nodes to settings.location // FIXME: this can be removed once all systems have updated their saveStates. if (!('location' in settings)) { - settings = JSON.parse(JSON.stringify(settings)); // Undo Object.preventExtensions() + settings = JSON.parse(JSON.stringify(settings)); // Undo Object.preventExtensions() settings.location = Object.entries(settings.nodes) - .map(([k, v]) => [k, { - lat: (v as any).lat, - lon: (v as any).lon - }]) - .reduce((obj, [k, v]) => ({...obj, [k as string]: v}), {}); + .map(([k, v]) => [ + k, + { + lat: (v as any).lat, + lon: (v as any).lon, + }, + ]) + .reduce((obj, [k, v]) => ({ ...obj, [k as string]: v }), {}); } const DataLayerSettings = settings.layer && layerSettings?.[settings.layer]; @@ -36,7 +39,7 @@ export const MapSettings = ({ settings, graphMetadata, updateSettings }: Visuali graphMetadata.nodes.labels.map((node) => [ node, Object.entries(graphMetadata.nodes.types[node].attributes) - .filter(([, value]) => value.dimension === 'numerical') + .filter(([, value]) => value.attributeType === 'number') .map(([key]) => key), ]), ); diff --git a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx index 51f05d351..89f2fb108 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx +++ b/libs/shared/lib/vis/visualizations/mapvis/layers/nodelink-layer/NodeLinkOptions.tsx @@ -6,6 +6,7 @@ import { LayerSettingsComponentType } from '../../mapvis.types'; import { nodeColorRGB } from '../../utils'; import { Accordion, AccordionBody, AccordionHead, AccordionItem } from '@graphpolaris/shared/lib/components/accordion'; import { isEqual } from 'lodash-es'; +import { CategoricalStats } from '@graphpolaris/shared/lib/statistics'; const defaultNodeSettings = (index: number) => ({ color: nodeColorRGB(index + 1), @@ -156,7 +157,7 @@ export function NodeLinkOptions({ [nodeType]: { ...nodeSettings, colorAttribute: String(val), - colorAttributeType: graphMetadata.nodes.types[nodeType].attributes[val].dimension, + colorAttributeType: graphMetadata.nodes.types[nodeType].attributes[val].attributeType, }, }, }) @@ -175,12 +176,13 @@ export function NodeLinkOptions({ /> </div> ) : ( - nodeSettings.colorAttributeType === 'categorical' && + nodeSettings.colorAttributeType === 'string' && nodeSettings.colorAttribute && ( <div> {( - graphMetadata.nodes.types[nodeType] as { attributes: { [key: string]: { values: string[] } } } - )?.attributes?.[nodeSettings.colorAttribute]?.values.map((attr: string) => ( + graphMetadata.nodes.types[nodeType]?.attributes?.[nodeSettings.colorAttribute] + ?.statistics as CategoricalStats + ).values.map((attr: string) => ( <div key={attr} className="flex items-center justify-between"> <p className="truncate w-18">{attr.length > 0 ? attr : 'Empty val'}</p> <ColorPicker diff --git a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts index fe4068e20..f8a5135c4 100644 --- a/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts +++ b/libs/shared/lib/vis/visualizations/mapvis/mapvis.types.ts @@ -2,7 +2,7 @@ import { CompositeLayer } from 'deck.gl'; import { VisualizationPropTypes, VisualizationSettingsType } from '../../common'; import { MapProps } from './mapvis'; import { Node as QueryNode } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice'; -import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; +import { GraphStatistics } from '@graphpolaris/shared/lib/statistics'; export type Coordinate = [number, number]; @@ -53,7 +53,7 @@ export type LayerSettingsType = { export type LayerSettingsComponentType<T = {}> = { settings: T & VisualizationSettingsType; - graphMetadata: GraphMetadata; + graphMetadata: GraphStatistics; spatialAttributes: { [k: string]: string[] }; updateSpatialAttribute: (label: string, attribute: 'lat' | 'lon', value: string) => void; updateLayerSettings: (val: Partial<LayerSettingsType>) => void; diff --git a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/configPanel/SemSubsConfigPanel.tsx b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/configPanel/SemSubsConfigPanel.tsx index 938906cd1..270b4fa28 100644 --- a/libs/shared/lib/vis/visualizations/semanticsubstratesvis/configPanel/SemSubsConfigPanel.tsx +++ b/libs/shared/lib/vis/visualizations/semanticsubstratesvis/configPanel/SemSubsConfigPanel.tsx @@ -2,13 +2,13 @@ import React, { useState, useEffect } from 'react'; import { Button } from '@graphpolaris/shared/lib/components/buttons'; import { Icon } from '@graphpolaris/shared/lib/components/icon'; import { DataFromPanel, DataPanelConfig } from '../components/types'; -import { GraphMetadata } from '@graphpolaris/shared/lib/data-access/statistics'; import { EntityPillSelector } from '@graphpolaris/shared/lib/components/selectors/entityPillSelector'; import { Input } from '@graphpolaris/shared/lib/components'; +import { CategoricalStats, GraphStatistics } from '@graphpolaris/shared/lib/statistics'; export type SemSubsConfigPanelProps = { dataFromPanel: DataFromPanel; - graphMetaData: GraphMetadata; + graphMetaData: GraphStatistics; colorNode: string; onUpdateData: (data: Partial<DataPanelConfig>) => void; onCollapse: (isOpen: boolean) => void; @@ -72,11 +72,12 @@ export const SemSubsConfigPanel: React.FC<SemSubsConfigPanelProps> = ({ categoricalKeys.push(undefined); for (const key in graphMetaData.nodes.types[data.entitySelected].attributes) { - const values = graphMetaData.nodes.types[data.entitySelected].attributes[key].values; + const stats = graphMetaData.nodes.types[data.entitySelected].attributes[key].statistics as CategoricalStats; + const values = stats?.values; if (values && values.length > 0 && values[0].toString() !== '[object Object]') { if ( graphMetaData.nodes.types[data.entitySelected].attributes.hasOwnProperty(key) && - graphMetaData.nodes.types[data.entitySelected].attributes[key].dimension === 'categorical' + graphMetaData.nodes.types[data.entitySelected].attributes[key].attributeType === 'string' ) { categoricalKeys.push(key as string); } @@ -100,7 +101,8 @@ export const SemSubsConfigPanel: React.FC<SemSubsConfigPanelProps> = ({ if (!data.entitySelected || !data.attributeSelected) return; if (data.attributeSelected && graphMetaData.nodes.types[data.entitySelected].attributes[data.attributeSelected]) { - let arrayAttributeValues = graphMetaData.nodes.types[data.entitySelected].attributes[data.attributeSelected].values; + let stats = graphMetaData.nodes.types[data.entitySelected].attributes[data.attributeSelected].statistics as CategoricalStats; + let arrayAttributeValues = stats?.values; if (arrayAttributeValues !== undefined) { if (arrayAttributeValues[0].toString() != '[object Object]') { setStateConfigPanelOptions((prevState) => ({ diff --git a/libs/shared/lib/vis/visualizations/vis1D/Vis1D.tsx b/libs/shared/lib/vis/visualizations/vis1D/Vis1D.tsx index 926beff6a..42f8391d8 100644 --- a/libs/shared/lib/vis/visualizations/vis1D/Vis1D.tsx +++ b/libs/shared/lib/vis/visualizations/vis1D/Vis1D.tsx @@ -100,7 +100,7 @@ const Vis1DSettings = ({ settings, graphMetadata, updateSettings }: Visualizatio if (graphMetadata && graphMetadata.nodes && graphMetadata.nodes.labels.length > 0 && settings.nodeLabel != '') { const newAttributeOptions = Object.entries(graphMetadata.nodes.types[settings.nodeLabel].attributes).map(([key, value]) => ({ name: key, - type: value.dimension, + type: value.attributeType, })); updateSettings({ attribute: newAttributeOptions[0].name }); // initialize the selected option for creating the dropdown and plots -- GitLab