Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hpcc 32244 custom metricoptions #18966

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
334 changes: 168 additions & 166 deletions esp/src/package-lock.json

Large diffs are not rendered by default.

32 changes: 17 additions & 15 deletions esp/src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,23 @@
"@fluentui/react-hooks": "8.8.10",
"@fluentui/react-icons-mdl2": "1.3.72",
"@fluentui/react-migration-v8-v9": "9.6.22",
"@hpcc-js/chart": "2.83.4",
"@hpcc-js/codemirror": "2.62.1",
"@hpcc-js/common": "2.71.18",
"@hpcc-js/comms": "2.94.1",
"@hpcc-js/chart": "2.84.1",
"@hpcc-js/codemirror": "2.63.0",
"@hpcc-js/common": "2.72.0",
"@hpcc-js/comms": "2.95.0",
"@hpcc-js/dataflow": "8.1.7",
"@hpcc-js/eclwatch": "2.74.8",
"@hpcc-js/graph": "2.85.16",
"@hpcc-js/html": "2.42.21",
"@hpcc-js/layout": "2.49.23",
"@hpcc-js/map": "2.77.22",
"@hpcc-js/other": "2.15.23",
"@hpcc-js/phosphor": "2.18.9",
"@hpcc-js/react": "2.53.17",
"@hpcc-js/tree": "2.40.18",
"@hpcc-js/util": "2.51.1",
"@hpcc-js/wasm": "2.18.0",
"@hpcc-js/eclwatch": "2.75.3",
"@hpcc-js/graph": "2.86.0",
"@hpcc-js/html": "2.43.0",
"@hpcc-js/layout": "2.50.1",
"@hpcc-js/map": "2.78.1",
"@hpcc-js/other": "2.16.1",
"@hpcc-js/phosphor": "2.19.1",
"@hpcc-js/react": "2.54.0",
"@hpcc-js/timeline": "2.53.0",
"@hpcc-js/tree": "2.41.0",
"@hpcc-js/util": "2.52.0",
"@hpcc-js/wasm": "2.18.1",
"@kubernetes/client-node": "0.20.0",
"clipboard": "2.0.11",
"d3-dsv": "3.0.1",
Expand All @@ -76,6 +77,7 @@
"react-hook-form": "7.51.2",
"react-hot-toast": "2.4.1",
"react-reflex": "4.2.6",
"react-singleton-hook": "3.4.0",
"react-sizeme": "3.0.2",
"universal-router": "9.2.0",
"xstyle": "0.3.3"
Expand Down
2 changes: 1 addition & 1 deletion esp/src/src-react/components/ECLArchive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const ECLArchive: React.FunctionComponent<ECLArchiveProps> = ({
return <HolyGrail fullscreen={fullscreen}
header={<CommandBar items={buttons} farItems={rightButtons} />}
main={
<DockPanel hideSingleTabs onDockPanelCreate={setDockpanel}>
<DockPanel hideSingleTabs onCreate={setDockpanel}>
<DockPanelItem key="scopesTable" title="Files" >
{ // Only render after archive is loaded (to ensure it "defaults to open") ---
archive?.modAttrs.length &&
Expand Down
11 changes: 10 additions & 1 deletion esp/src/src-react/components/Frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ import { useUserSession } from "../hooks/user";
const logger = scopedLogger("../components/Frame.tsx");
const envLogger = scopedLogger("environment");

const USER_COOKIE_CONSENT = "user_cookie_consent";

export function resetCookieConsent() {
const store = userKeyValStore();
return store.delete(USER_COOKIE_CONSENT);
}

const USER_COOKIE_CONSENT = "user_cookie_consent";

interface FrameProps {
}

Expand Down Expand Up @@ -111,7 +120,7 @@ export const Frame: React.FunctionComponent<FrameProps> = () => {
/>}
/>
<CookieConsent showCookieConsent={showCookieConsent} onApply={(n: boolean) => {
userKeyValStore().set("user_cookie_consent", n ? "1" : "0");
userKeyValStore().set(USER_COOKIE_CONSENT, n ? "1" : "0");
}} />
</ThemeProvider >
</FluentProvider >;
Expand Down
115 changes: 62 additions & 53 deletions esp/src/src-react/components/Metrics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import { bundleIcon, Folder20Filled, Folder20Regular, FolderOpen20Filled, Folder
import { Database } from "@hpcc-js/common";
import { WorkunitsServiceEx, IScope, splitMetric } from "@hpcc-js/comms";
import { CellFormatter, ColumnFormat, ColumnType, DBStore, RowType, Table } from "@hpcc-js/dgrid";
import { compare, scopedLogger } from "@hpcc-js/util";
import { scopedLogger } from "@hpcc-js/util";
import nlsHPCC from "src/nlsHPCC";
import { WUTimelineNoFetch } from "src/Timings";
import * as Utility from "src/Utility";
import { FetchStatus, useMetricsOptions, useWUQueryMetrics, MetricsOptions as MetricsOptionsT } from "../hooks/metrics";
import { FetchStatus, useMetricsViews, useWUQueryMetrics } from "../hooks/metrics";
import { HolyGrail } from "../layouts/HolyGrail";
import { AutosizeComponent, AutosizeHpccJSComponent } from "../layouts/HpccJSAdapter";
import { DockPanel, DockPanelItem, ResetableDockPanel } from "../layouts/DockPanel";
Expand Down Expand Up @@ -89,10 +89,10 @@ class TableEx extends Table {
}

_rawDataMap: { [id: number]: string } = {};
metrics(metrics: any[], options: MetricsOptionsT, timelineFilter: string, scopeFilter: string, matchCase: boolean): this {
metrics(metrics: any[], scopeTypes: string[], properties: string[], scopeFilter: string, matchCase: boolean): this {
this
.columns(["##"]) // Reset hash to force recalculation of default widths
.columns(["##", nlsHPCC.Type, "StdDevs", nlsHPCC.Scope, ...options.properties, "__StdDevs"])
.columns(["##", nlsHPCC.Type, "StdDevs", nlsHPCC.Scope, ...properties, "__StdDevs"])
.columnFormats([
new ColumnFormatEx()
.column("StdDevs")
Expand All @@ -106,19 +106,18 @@ class TableEx extends Table {
.data(metrics
.filter(m => this.scopeFilterFunc(m, scopeFilter, matchCase))
.filter(row => {
return (timelineFilter === "" || row.name?.indexOf(timelineFilter) === 0) &&
(options.scopeTypes.indexOf(row.type) >= 0);
return scopeTypes.indexOf(row.type) >= 0;
}).map((row, idx) => {
if (idx === 0) {
this._rawDataMap = {
0: "##", 1: "type", 2: "__StdDevs", 3: "name"
};
options.properties.forEach((p, idx2) => {
properties.forEach((p, idx2) => {
this._rawDataMap[4 + idx2] = p;
});
}
row.__hpcc_id = row.name;
return [idx, row.type, row.__StdDevs === 0 ? undefined : row.__StdDevs, row.name, ...options.properties.map(p => {
return [idx, row.type, row.__StdDevs === 0 ? undefined : row.__StdDevs, row.name, ...properties.map(p => {
return row.__groupedProps[p]?.Value ??
row.__groupedProps[p]?.Max ??
row.__groupedProps[p]?.Avg ??
Expand Down Expand Up @@ -158,6 +157,7 @@ class TableEx extends Table {
}

type SelectedMetricsSource = "" | "scopesTable" | "scopesSqlTable" | "metricGraphWidget" | "hotspot" | "reset";
const TIMELINE_FIXEDHEIGHT = 152;

interface MetricsProps {
wuid: string;
Expand All @@ -175,15 +175,13 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
selection
}) => {
const [_uiState, _setUIState] = React.useState({ ...defaultUIState });
const [timelineFilter, setTimelineFilter] = React.useState("");
const [selectedMetricsSource, setSelectedMetricsSource] = React.useState<SelectedMetricsSource>("");
const [selectedMetrics, setSelectedMetrics] = React.useState<IScope[]>([]);
const [selectedMetricsPtr, setSelectedMetricsPtr] = React.useState<number>(-1);
const [metrics, columns, _activities, _properties, _measures, _scopeTypes, fetchStatus, refresh] = useWUQueryMetrics(wuid, querySet, queryId);
const { viewIds, viewId, setViewId, view, updateView } = useMetricsViews();
const [showMetricOptions, setShowMetricOptions] = React.useState(false);
const [options, setOptions, saveOptions] = useMetricsOptions();
const [dockpanel, setDockpanel] = React.useState<ResetableDockPanel>();
const [showTimeline, setShowTimeline] = React.useState<boolean>(true);
const [trackSelection, setTrackSelection] = React.useState<boolean>(true);
const [fullscreen, setFullscreen] = React.useState<boolean>(false);
const [hotspots, setHotspots] = React.useState<string>("");
Expand Down Expand Up @@ -227,27 +225,31 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
.maxZoom(Number.MAX_SAFE_INTEGER)
);

const [scopeFilter, setScopeFilter] = React.useState("");
React.useEffect(() => {
timeline
.on("click", (row, col, sel) => {
setTimelineFilter(sel ? row[7].__hpcc_id : "");
if (sel) {
timeline.selection([]);
setSelectedMetricsSource("scopesTable");
pushUrl(`${parentUrl}/${row[7].Id}`);
setScopeFilter(`name:${row[7].__hpcc_id}`);
pushUrl(`${parentUrl}/${row[7].id}`);
}
}, true)
;
}, [parentUrl, timeline]);

React.useEffect(() => {
timeline
.scopes(metrics)
.lazyRender()
;
}, [metrics, timeline]);
if (view.showTimeline) {
timeline
.scopes(metrics)
.height(TIMELINE_FIXEDHEIGHT)
.lazyRender()
;
}
}, [metrics, timeline, view.showTimeline]);

// Scopes Table ---
const [scopeFilter, setScopeFilter] = React.useState("");
const onChangeScopeFilter = React.useCallback((event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
setScopeFilter(newValue || "");
}, []);
Expand All @@ -259,7 +261,7 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({

const scopesTable = useConst(() => new TableEx()
.multiSelect(true)
.metrics([], options, timelineFilter, scopeFilter, matchCase)
.metrics([], view.scopeTypes, view.properties, scopeFilter, matchCase)
.sortable(true)
);

Expand All @@ -274,24 +276,22 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
}, [scopesSelectionChanged, scopesTable]);

React.useEffect(() => {
if (!scopeFilter || scopeFilter.indexOf("name:") === 0) {
setScopeFilter(timelineFilter ? `name:${timelineFilter}` : "");
}
scopesTable
.metrics(metrics, options, timelineFilter, scopeFilter, matchCase)
.metrics(metrics, view.scopeTypes, view.properties, scopeFilter, matchCase)
.lazyRender()
;
}, [matchCase, metrics, options, scopeFilter, scopesTable, timelineFilter]);
}, [matchCase, metrics, scopeFilter, scopesTable, view.properties, view.scopeTypes]);

const updateScopesTable = React.useCallback((selection: IScope[]) => {
if (scopesTable?.renderCount() > 0) {
const prevSelection = scopesTable.selection().map(row => row.__lparam.id);
const newSelection = selection.map(row => row.id);
const diffs = compare(prevSelection, newSelection);
if (diffs.enter.length || diffs.exit.length) {
scopesTable.selection(scopesTable.data().filter(row => {
scopesTable.selection([]);
if (selection.length) {
const selRows = scopesTable.data().filter(row => {
return selection.indexOf(row[row.length - 1]) >= 0;
}));
});
scopesTable.render(() => {
scopesTable.selection(selRows);
});
}
}
}, [scopesTable]);
Expand Down Expand Up @@ -493,9 +493,9 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
}, [crossTabTable]);

React.useEffect(() => {
const dot = metricGraph.graphTpl(selectedLineage ? [selectedLineage] : [], options);
const dot = metricGraph.graphTpl(selectedLineage ? [selectedLineage] : [], view);
setDot(dot);
}, [metricGraph, options, selectedLineage]);
}, [metricGraph, view, selectedLineage]);

React.useEffect(() => {
let cancelled = false;
Expand All @@ -508,7 +508,7 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
}
}
setIsLayoutComplete(true);
});
}).catch(err => logger.error(err));
}
return () => {
cancelled = true;
Expand All @@ -533,13 +533,14 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
React.useEffect(() => {

// Update layout prior to unmount ---
if (dockpanel && options && saveOptions && setOptions) {
if (dockpanel && updateView) {
return () => {
setOptions({ ...options, layout: dockpanel.getLayout() });
saveOptions();
if (dockpanel && updateView) {
updateView({ layout: dockpanel.getLayout() });
}
};
}
}, [dockpanel, options, saveOptions, setOptions]);
}, [dockpanel, updateView]);

// Command Bar ---
const buttons = React.useMemo((): ICommandBarItemProps[] => [
Expand All @@ -559,19 +560,30 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
},
{ key: "divider_1", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
{
key: "timeline", text: nlsHPCC.Timeline, canCheck: true, checked: showTimeline, iconProps: { iconName: "TimelineProgress" },
key: "views", text: viewId, iconProps: { iconName: "View" },
subMenuProps: {
items: viewIds.map(v => ({
key: v, text: v, onClick: () => {
updateView({ layout: dockpanel.getLayout() });
setViewId(v);
}
}))
},
},
{
key: "timeline", text: nlsHPCC.Timeline, canCheck: true, checked: view.showTimeline, iconProps: { iconName: "TimelineProgress" },
onClick: () => {
setShowTimeline(!showTimeline);
updateView({ showTimeline: !view.showTimeline }, true);
}
},
{
key: "options", text: nlsHPCC.Options, iconProps: { iconName: "Settings" },
onClick: () => {
setOptions({ ...options, layout: dockpanel.layout() });
updateView({ layout: dockpanel.getLayout() });
setShowMetricOptions(true);
}
}
], [dockpanel, hotspots, onHotspot, options, refresh, setOptions, showTimeline, timeline]);
], [dockpanel, hotspots, onHotspot, refresh, setViewId, timeline, updateView, view.showTimeline, viewId, viewIds]);

const formatColumns = React.useMemo((): Utility.ColumnMap => {
const copyColumns: Utility.ColumnMap = {};
Expand Down Expand Up @@ -613,31 +625,28 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
}
}]
}
}, {
},
{ key: "divider_2", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
{
key: "fullscreen", title: nlsHPCC.MaximizeRestore, iconProps: { iconName: fullscreen ? "ChromeRestore" : "FullScreen" },
onClick: () => setFullscreen(!fullscreen)
}
], [dot, formatColumns, fullscreen, metrics, wuid]);

const setShowMetricOptionsHook = React.useCallback((show: boolean) => {
setShowMetricOptions(show);
scopesTable
.metrics(metrics, options, timelineFilter, scopeFilter, matchCase)
.render(() => {
updateScopesTable(selectedMetrics);
})
;
}, []);

}, [matchCase, metrics, options, scopeFilter, scopesTable, selectedMetrics, timelineFilter, updateScopesTable]);
console.log("View ID", viewId, view.scopeTypes);

return <HolyGrail fullscreen={fullscreen}
header={<>
<CommandBar items={buttons} farItems={rightButtons} />
<AutosizeHpccJSComponent widget={timeline} fixedHeight={"160px"} padding={4} hidden={!showTimeline} />
<AutosizeHpccJSComponent widget={timeline} fixedHeight={`${TIMELINE_FIXEDHEIGHT + 8}px`} padding={4} hidden={!view.showTimeline} />
</>}
main={
<ErrorBoundary>
<DockPanel layout={options?.layout} onDockPanelCreate={setDockpanel}>
<DockPanel layout={view?.layout} onCreate={setDockpanel}>
<DockPanelItem key="scopesTable" title={nlsHPCC.Metrics}>
<HolyGrail
header={<Stack horizontal>
Expand All @@ -650,7 +659,7 @@ export const Metrics: React.FunctionComponent<MetricsProps> = ({
/>
</DockPanelItem>
<DockPanelItem key="metricsSql" title={nlsHPCC.MetricsSQL} location="tab-after" relativeTo="scopesTable">
<MetricsSQL defaultSql={options.sql} scopes={metrics} onSelectionChanged={selection => scopesSelectionChanged("scopesSqlTable", selection)}></MetricsSQL>
<MetricsSQL defaultSql={view.sql} scopes={metrics} onSelectionChanged={selection => scopesSelectionChanged("scopesSqlTable", selection)}></MetricsSQL>
</DockPanelItem>
<DockPanelItem key="metricGraph" title={nlsHPCC.Graph} location="split-right" relativeTo="scopesTable" >
<HolyGrail
Expand Down
Loading
Loading