Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • graphpolaris/frontend-v2
  • rijkheere/frontend-v-2-reordering-paoh
2 results
Show changes
Commits on Source (6)
Showing
with 46 additions and 19 deletions
......@@ -6,6 +6,7 @@ import { store } from '@graphpolaris/shared/lib/data-access/store';
import App from './app/App';
import { createRoot } from 'react-dom/client';
import './main.css';
import { ErrorBoundary } from '@graphpolaris/shared/lib/components/errorBoundary';
if (import.meta.env.SENTRY_ENABLED) {
Sentry.init({
......@@ -25,10 +26,12 @@ if (domNode) {
root.render(
<Provider store={store}>
<Router>
<Routes>
<Route path="/" element={<App load={undefined} />}></Route>
<Route path="/fraud" element={<App load="5bdf3354-673f-4dec-b6a0-196e67cd211c" />}></Route>
</Routes>
<ErrorBoundary fallback={<div>Oops! Something went wrong. Please try again.</div>}>
<Routes>
<Route path="/" element={<App load={undefined} />}></Route>
<Route path="/fraud" element={<App load="5bdf3354-673f-4dec-b6a0-196e67cd211c" />}></Route>
</Routes>
</ErrorBoundary>
</Router>
</Provider>,
);
......
......@@ -13,6 +13,7 @@ const IconMap: Record<SchemaAttributeTypes, string> = {
duration: 'icon-[carbon--calendar]',
number: 'icon-[carbon--string-integer]',
location: 'icon-[carbon--location]',
array: 'icon-[ic--baseline-data-array]',
};
function getDataTypeIcon(data_type?: SchemaAttributeTypes): string {
......
......@@ -448,7 +448,7 @@ export const CheckboxInput = ({ label, value, options, onChange, tooltip }: Chec
export const BooleanInput = ({ label, value, onChange, tooltip, info, size, required, className }: BooleanProps) => {
return (
<Tooltip>
<TooltipTrigger className={className + 'w-full flex justify-between'}>
<TooltipTrigger className={className + ' w-full flex justify-between'}>
{label && (
<label className="label p-0">
<span
......
......@@ -76,6 +76,14 @@ export const ContextMenu = (props: {
}
function removeNode() {
if (!props.node) return;
const connectedLogicPills = graphologyGraph.neighbors(props.node.id);
connectedLogicPills.forEach((pill) => {
const attributes = graphologyGraph.getNodeAttributes(pill);
if (attributes.type === 'logic') {
graphologyGraph.dropNode(pill);
}
});
graphologyGraph.dropNode(props.node.id);
dispatch(setQuerybuilderGraphology(graphologyGraph));
props.onClose();
......
......@@ -19,11 +19,7 @@ import { EntityPill } from '@graphpolaris/shared/lib/components/pills';
import { Handle, Position, useUpdateNodeInternals } from 'reactflow';
import { NodeAttribute, SchemaReactflowEntityNode, toHandleId } from '../../../model';
import { PillAttributes } from '../../pillattributes/PillAttributes';
import { DropdownTrigger, DropdownContainer, DropdownItemContainer, DropdownItem } from '@graphpolaris/shared/lib/components/dropdowns';
import { PopoverContext } from '@graphpolaris/shared/lib/components/layout/Popover';
import { useDispatch } from 'react-redux';
import { isEqual } from 'lodash-es';
import { getDataTypeIcon } from '@graphpolaris/shared/lib/components/DataTypeIcon';
import { uniqBy } from 'lodash-es';
/**
* Component to render an entity flow element
......@@ -43,6 +39,7 @@ export const QueryEntityPill = React.memo((node: SchemaReactflowEntityNode) => {
[graph],
);
const uniqueAttributes = useMemo(() => uniqBy(data.attributes, (attr) => attr.handleData.attributeName), [data.attributes]);
const unionType = useQuerybuilderUnionTypes()[node.id];
return (
......@@ -75,7 +72,7 @@ export const QueryEntityPill = React.memo((node: SchemaReactflowEntityNode) => {
{data?.attributes && (
<PillAttributes
node={node}
attributes={data.attributes}
attributes={uniqueAttributes}
attributeEdges={attributeEdges.map((edge) => edge?.attributes)}
unionType={unionType}
/>
......
......@@ -29,7 +29,8 @@ export type SchemaAttributeTypes =
| 'datetime'
| 'duration'
| 'number'
| 'location';
| 'location'
| 'array';
export type DimensionType = 'categorical' | 'numerical' | 'temporal' | 'spatial';
......
......@@ -10,12 +10,14 @@ const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphStatistics
const density = n_nodes < 2 ? 0 : (n_edges * 2) / (n_nodes * (n_nodes - 1));
// general nodes and edges statistics
const metaData: GraphStatistics = {
topological: { density, self_loops: 0 },
nodes: { labels: [], count: n_nodes, types: {} },
edges: { labels: [], count: n_edges, types: {} },
};
// attributes based statistics
nodes.forEach((node) => {
const nodeType = getNodeLabel(node);
if (!metaData.nodes.labels.includes(nodeType)) {
......@@ -25,7 +27,6 @@ const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphStatistics
if (!metaData.nodes.types[nodeType]) {
metaData.nodes.types[nodeType] = { count: 0, attributes: {} };
}
metaData.nodes.types[nodeType].count++;
Object.entries(node.attributes).forEach(([attributeId, attributeValue]) => {
......@@ -34,7 +35,6 @@ const getGraphStatistics = (graph: GraphQueryResultFromBackend): GraphStatistics
if (!metaData.nodes.types[nodeType].attributes[attributeId]) {
metaData.nodes.types[nodeType].attributes[attributeId] = { attributeType, statistics: initializeStatistics(attributeType) };
}
updateStatistics(metaData.nodes.types[nodeType].attributes[attributeId], attributeValue);
});
});
......
......@@ -67,6 +67,7 @@ type CategoricalStats = {
uniqueItems: number;
values: string[];
mode: string;
count: number;
};
type TemporalStats = {
......@@ -77,6 +78,7 @@ type TemporalStats = {
type ArrayStats = {
length: number;
count: number;
};
type ObjectStats = {
......
......@@ -12,10 +12,11 @@ import type { ArrayStats, BooleanStats, CategoricalStats, NumericalStats, Tempor
describe('updateArrayStats', () => {
it('should update the length of the array', () => {
const stats: ArrayStats = { length: 0 };
const stats: ArrayStats = { length: 0, count: 0 };
const value = [1, 2, 3];
updateArrayStats(stats, value);
expect(stats.length).toBe(3);
expect(stats.count).toBe(1);
});
});
......@@ -37,7 +38,7 @@ describe('updateBooleanStats', () => {
describe('updateCategoricalStats', () => {
it('should update mode and unique items count', () => {
const stats: CategoricalStats = { uniqueItems: 0, values: [], mode: '' };
const stats: CategoricalStats = { uniqueItems: 0, values: [], mode: '', count: 0 };
updateCategoricalStats(stats, 'apple');
updateCategoricalStats(stats, 'banana');
updateCategoricalStats(stats, 'apple');
......@@ -45,6 +46,7 @@ describe('updateCategoricalStats', () => {
expect(stats.values).toEqual(['apple', 'banana', 'apple']);
expect(stats.uniqueItems).toBe(2);
expect(stats.mode).toBe('apple');
expect(stats.count).toBe(3);
});
});
......@@ -86,7 +88,7 @@ describe('updateObjectStats', () => {
describe('initializeStatistics', () => {
it('should initialize statistics for string type', () => {
const stats = initializeStatistics('string');
expect(stats).toEqual({ uniqueItems: 0, values: [], mode: '' });
expect(stats).toEqual({ uniqueItems: 0, values: [], mode: '', count: 0 });
});
it('should initialize statistics for boolean type', () => {
......@@ -96,7 +98,7 @@ describe('initializeStatistics', () => {
it('should initialize statistics for number type', () => {
const stats = initializeStatistics('number');
expect(stats).toEqual({ min: Infinity, max: -Infinity, average: 0 });
expect(stats).toEqual({ min: Infinity, max: -Infinity, average: 0, count: 0 });
});
it('should throw an error for an unknown type', () => {
......
......@@ -2,6 +2,7 @@ import { ArrayStats } from '../../statistics.types';
const updateArrayStats = (stats: ArrayStats, value: any[]) => {
stats.length = value.length;
stats.count++;
};
export { updateArrayStats };
......@@ -11,6 +11,7 @@ const updateCategoricalStats = (stats: CategoricalStats, value: string | boolean
frequencyMap[val] = (frequencyMap[val] || 0) + 1;
});
stats.mode = Object.keys(frequencyMap).reduce((a, b) => (frequencyMap[a] > frequencyMap[b] ? a : b));
stats.count++;
};
export { updateCategoricalStats };
......@@ -7,6 +7,7 @@ const initializeStatistics = <T extends AttributeType>(type: T): AttributeTypeSt
uniqueItems: 0,
values: [],
mode: '',
count: 0,
} as unknown as AttributeTypeStats<T>;
case 'boolean':
return {
......@@ -18,6 +19,7 @@ const initializeStatistics = <T extends AttributeType>(type: T): AttributeTypeSt
min: Infinity,
max: -Infinity,
average: 0,
count: 0,
} as unknown as AttributeTypeStats<T>;
case 'date':
case 'time':
......@@ -31,6 +33,7 @@ const initializeStatistics = <T extends AttributeType>(type: T): AttributeTypeSt
case 'array':
return {
length: 0,
count: 0,
} as unknown as AttributeTypeStats<T>;
case 'object':
return {
......
......@@ -1124,14 +1124,22 @@ const PaohSettings = ({ settings, graphMetadata, updateSettings }: Visualization
label="Row jump sensitivity"
value={settings.rowJumpAmount}
onChange={(val) => updateSettings({ rowJumpAmount: val })}
containerClassName="pt-2"
/>
<Input
type="number"
label="Column jump sensitivity"
value={settings.colJumpAmount}
onChange={(val) => updateSettings({ colJumpAmount: val })}
containerClassName="pt-2"
/>
<Input
type="boolean"
label="Merge Data"
value={settings.mergeData}
onChange={(val) => updateSettings({ mergeData: val })}
className="pt-2"
/>
<Input type="boolean" label="Merge Data" value={settings.mergeData} onChange={(val) => updateSettings({ mergeData: val })} />
</div>
</SettingsContainer>
);
......