Skip to content

Commit

Permalink
next
Browse files Browse the repository at this point in the history
  • Loading branch information
vitshev committed Jun 4, 2024
1 parent 4860f31 commit 538aa27
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 312 deletions.
2 changes: 1 addition & 1 deletion packages/ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 41 additions & 12 deletions packages/ui/src/ui/components/StatisticTable/StatisticTable.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import * as React from 'react';
import {useCallback} from 'react';
import {useSelector} from 'react-redux';
import cn from 'bem-cn-lite';

import Icon from '../Icon/Icon';
import hammer from '../../common/hammer';
import {JobStatistic} from '../../types/job';
import ErrorBoundary from '../ErrorBoundary/ErrorBoundary';
import ElementsTableRaw from '../ElementsTable/ElementsTable';
import Icon from '../Icon/Icon';
import Toolbar from './Toolbar';
import {getFontFamilies} from '../../store/selectors/settings-ts';

import hammer from '../../common/hammer';
import Toolbar from './Toolbar';
import {getMinWidth} from './get-min-width';
import {filterJobStatisticTree, prepareJobStatistic} from './prepare-job-statistic';

import './Statistics.scss';

Expand Down Expand Up @@ -101,7 +105,7 @@ export function ExpandedCell({
return {minWidth, paddingLeft: (item?.level || 0) * LEVEL_OFFSET};
}, [item.level, minWidth]);

const toggleItemAndTreeState = useCallback(() => {
const toggleItemAndTreeState = React.useCallback(() => {
if (!itemState.empty) {
toggleItemState();
}
Expand Down Expand Up @@ -182,22 +186,47 @@ const prepareTableProps = ({visibleColumns}: {visibleColumns: VisibleColumns}) =
};
};

const useJobStatisticTable = ({
statistic,
fontFamilies,
}: {
statistic: JobStatistic;
fontFamilies: {regular: string; monospace: string};
}) => {
const [filter, setFilter] = React.useState('');
const [treeState, setTreeState] = React.useState('expanded');

const tree = React.useMemo(() => prepareJobStatistic(statistic), [statistic]);
const items = React.useMemo(() => filterJobStatisticTree(tree, filter), [tree, filter]);
const minWidth = React.useMemo(() => getMinWidth(items, fontFamilies), [fontFamilies, items]);
const onFilterChange = (value: string) => setFilter(value);

return {
minWidth,
items,
treeState,
setTreeState,
onFilterChange,
};
};

export function StatisticTable({
items,
virtual,
minWidth,
onFilterChange,
visibleColumns,
fixedHeader,
statistic,
}: {
virtual?: boolean;
fixedHeader?: boolean;
items: TreeItem[];
minWidth?: number;
onFilterChange(text: string): void;
statistic: JobStatistic;
visibleColumns: Array<'avg' | 'min' | 'max' | 'sum' | 'count' | 'last'>;
}) {
const [treeState, setTreeState] = React.useState('expanded');
const fontFamilies = useSelector(getFontFamilies);
const {items, minWidth, treeState, setTreeState, onFilterChange} = useJobStatisticTable({
statistic,
fontFamilies,
});

const templates = React.useMemo(
() =>
({
Expand Down
27 changes: 27 additions & 0 deletions packages/ui/src/ui/components/StatisticTable/get-min-width.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {LEVEL_OFFSET} from './StatisticTable';

export function createCanvasContext(font: string) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx!.font = font;
return ctx!;
}

export function mesureRowWidth(ctx: CanvasRenderingContext2D, text = '') {
const {width} = ctx.measureText(text);
return Math.max(1, Math.ceil(width));
}

export const getMinWidth = (items: any[], fontFamilies: {regular: string; monospace: string}) => {
let res = 300;
const ctx = createCanvasContext(
`14px / 20.02px "${fontFamilies.regular}", "Helvetica Neue", Arial, Helvetica, sans-serif`,
);

for (const item of items) {
const iconsWidth = item.isLeafNode ? 20 : 20 * 2;
const width = mesureRowWidth(ctx, item.attributes.name);
res = Math.max(res, width + iconsWidth + item.level * LEVEL_OFFSET);
}
return Math.round(res * 1.05);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import hammer from '../../common/hammer';
import {JobStatistic, JobTree, MetricsEntry} from '../../types/job';
import {prepareMetricsTree} from '../../utils/job/tabs/statistics';

export function filterJobStatisticTree(tree: JobTree, currentFilter = '') {
const filteredTree = hammer.treeList.filterTree(
tree,
(entry: MetricsEntry) => entry.name.indexOf(currentFilter) !== -1,
true,
);

const sortedTree = hammer.treeList.sortTree(
filteredTree,
{field: 'name', asc: true},
{
name: {
get: function (entry: MetricsEntry) {
return entry.name;
},
},
},
);

return hammer.treeList.flattenTree(sortedTree);
}

export const prepareJobStatistic = (statistic: JobStatistic) => {
return prepareMetricsTree(statistic);
};
1 change: 0 additions & 1 deletion packages/ui/src/ui/constants/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export const LOAD_JOB_SPECIFICATION_REQUEST = 'JOB:GENERAL:LOAD_JOB_SPECIFICATIO
export const LOAD_JOB_SPECIFICATION_SUCCESS = 'JOB:GENERAL:LOAD_JOB_SPECIFICATION_SUCCESS';
export const LOAD_JOB_SPECIFICATION_FAILURE = 'JOB:GENERAL:LOAD_JOB_SPECIFICATION_FAILURE';
export const LOAD_JOB_SPECIFICATION_CANCELLED = 'JOB:GENERAL:LOAD_JOB_SPECIFICATION_CANCELLED';
export const CHANGE_FILTER = 'JOB:STATISTICS:CHANGE_FILTER';
export const CHANGE_OMIT_NODE_DIRECTORY = 'JOB:SPECIFICATION:CHANGE_OMIT_NODE_DIRECTORY';
export const CHANGE_OMIT_INPUT_TABLES_SPECS = 'JOB:SPECIFICATION:CHANGE_OMIT_INPUT_TABLES_SPECS';
export const CHANGE_OMIT_OUTPUT_TABLES_SPECS = 'JOB:SPECIFICATION:CHANGE_OMIT_OUTPUT_TABLES_SPECS';
Expand Down
14 changes: 3 additions & 11 deletions packages/ui/src/ui/pages/job/tabs/Statistics/Statistics.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import * as React from 'react';
import {useSelector} from 'react-redux';
import {changeFilter} from '../../../../store/actions/job/statistics';
import {
getJobStatisticsMetricMinWidth,
getTreeItems,
} from '../../../../store/selectors/job/statistics';
import {JobTreeItem} from '../../../../types/job';
import {getRawStatistic} from '../../../../store/selectors/job/statistics';
import {StatisticTable} from '../../../../components/StatisticTable/StatisticTable';

export default function Statistics() {
const items: JobTreeItem[] = useSelector(getTreeItems);
const minWidth = useSelector(getJobStatisticsMetricMinWidth);
const statistic = useSelector(getRawStatistic);

return (
<StatisticTable
items={items}
minWidth={minWidth}
onFilterChange={changeFilter}
statistic={statistic}
visibleColumns={['min', 'max', 'last', 'sum', 'count']}
/>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import {useSelector} from 'react-redux';
import React from 'react';

import {getProgressYQLStatisticsFiltered} from '../../module/query/selectors';
import {changeProgressYQLStatisticsFilter} from '../../module/query/actions';
import {getProgressYQLStatistics} from '../../module/query/selectors';
import {StatisticTable} from '../../../../components/StatisticTable/StatisticTable';

import './index.scss';

export function YQLStatisticsTable() {
const items = useSelector(getProgressYQLStatisticsFiltered);
const statistics = useSelector(getProgressYQLStatistics);

return (
<StatisticTable
fixedHeader
virtual={false}
items={items}
statistic={statistics}
visibleColumns={['min', 'max', 'avg', 'sum', 'count']}
onFilterChange={changeProgressYQLStatisticsFilter}
/>
);
}
17 changes: 0 additions & 17 deletions packages/ui/src/ui/pages/query-tracker/module/query/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,20 +303,3 @@ export function setDraftQueryACO({
);
};
}

export const PROGRESS_YQL_STATISTIC_CHANGE_FILTER_TEXT =
'query-tracker/PROGRESS_YQL_STATISTIC:CHANGE_FILTER_TEXT';

export type ChangeProgressYQLStatisticFilterTextAction = ActionD<
typeof PROGRESS_YQL_STATISTIC_CHANGE_FILTER_TEXT,
{filter: string}
>;

export function changeProgressYQLStatisticsFilter(
filter: string,
): ChangeProgressYQLStatisticFilterTextAction {
return {
type: PROGRESS_YQL_STATISTIC_CHANGE_FILTER_TEXT,
data: {filter},
};
}
23 changes: 1 addition & 22 deletions packages/ui/src/ui/pages/query-tracker/module/query/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {DraftQuery, QueryItem} from '../api';
import {QueryEngine} from '../engines';
import {
ChangeProgressYQLStatisticFilterTextAction,
PROGRESS_YQL_STATISTIC_CHANGE_FILTER_TEXT,
REQUEST_QUERY,
RequestQueryAction,
SET_QUERY,
Expand Down Expand Up @@ -39,11 +37,6 @@ export interface QueryState {
cliqueLoading: boolean;
cliqueMap: Record<string, {alias: string; yt_operation_id?: string}[]>;
state: 'init' | 'loading' | 'ready' | 'error';
filters?: {
progressYQLStatistic?: {
text: string;
};
};
}

const initialQueryDraftState: QueryState['draft'] = {
Expand Down Expand Up @@ -80,7 +73,6 @@ export function reducer(state = initState, action: Actions): QueryState {
},
dirtySinceLastSubmit: false,
state: 'ready',
filters: {},
};
}
case UPDATE_QUERY: {
Expand All @@ -91,7 +83,6 @@ export function reducer(state = initState, action: Actions): QueryState {
...state.draft,
error: action.data?.error,
},
filters: {},
};
}
case REQUEST_QUERY: {
Expand Down Expand Up @@ -153,17 +144,6 @@ export function reducer(state = initState, action: Actions): QueryState {
draft: {...state.draft, access_control_object},
};
}

case PROGRESS_YQL_STATISTIC_CHANGE_FILTER_TEXT: {
return {
...state,
filters: {
progressYQLStatistic: {
text: action.data.filter,
},
},
};
}
}

return state;
Expand All @@ -179,5 +159,4 @@ type Actions =
| SetQueryReadyAction
| UpdateACOQueryAction
| SetQueryClusterClique
| SetQueryCliqueLoading
| ChangeProgressYQLStatisticFilterTextAction;
| SetQueryCliqueLoading;
79 changes: 3 additions & 76 deletions packages/ui/src/ui/pages/query-tracker/module/query/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import forOwn_ from 'lodash/forOwn';
import isNumber from 'lodash/isNumber';
import {createSelector} from 'reselect';
import {RootState} from '../../../../store/reducers';
import {QTRequestOptions, QueryStatus} from '../api';
Expand All @@ -12,14 +11,7 @@ import {QTEditorError} from '../types/editor';
import {YTError} from '../../../../types';
import {isYTError} from '../../../../../shared/utils';
import {getQueryResults} from '../query_result/selectors';
import {
FieldTree,
fieldTreeForEach,
fieldTreeSome,
filterFieldTree,
} from '../../../../common/hammer/field-tree';
import {ValueOf} from '../../../../../@types/types';
import {JobTreeItem, LeafStatistic} from '../../../../types/job';
import {JobStatistic} from '../../../../types/job';

const QT_STAGE = getQueryTrackerStage();
const getState = (state: RootState) => state.queryTracker.query;
Expand Down Expand Up @@ -113,71 +105,6 @@ export const getCurrentQueryACO = (state: RootState) =>
export const getCurrentDraftQueryACO = (state: RootState) =>
state.queryTracker.query?.draft?.access_control_object || DEFAULT_QUERY_ACO;

const isSummaryItem = (v: ValueOf<FieldTree<LeafStatistic>>): v is LeafStatistic => {
if (typeof v === 'string' || isNumber(v)) {
return true;
}

return ['count', 'sum', 'min', 'avg', 'max'].some((key) => key in v);
};

const getProgressYQLStatistics = (state: RootState) => {
return state.queryTracker?.query?.queryItem?.progress?.yql_statistics;
export const getProgressYQLStatistics = (state: RootState) => {
return state.queryTracker?.query?.queryItem?.progress?.yql_statistics as JobStatistic;
};

export const getProgressYQLStatisticsFilterText = (state: RootState) =>
state.queryTracker?.query?.filters?.progressYQLStatistic?.text ?? '';

const filterProgressYQLStatisticsTree = createSelector(
[getProgressYQLStatistics, getProgressYQLStatisticsFilterText],
(tree, filterText: string) => {
if (!filterText) {
return tree;
}

const checkByName = !filterText.length
? () => true
: (path: Array<string>) => {
const pathText = path.join('/');
return pathText.indexOf(filterText) !== -1;
};

return filterFieldTree(
tree ?? {},
isSummaryItem,
(path, tree) => {
if (checkByName(path)) {
return true;
}
return tree && fieldTreeSome(tree, isSummaryItem, checkByName, path.slice());
},
(items) => items,
);
},
);

export const getProgressYQLStatisticsFiltered = createSelector(
[filterProgressYQLStatisticsTree],
(tree) => {
const res: Array<JobTreeItem> = [];
fieldTreeForEach<LeafStatistic>(tree ?? {}, isSummaryItem, (path, _tree, item) => {
const isLeafNode = Boolean(item);

res.push({
level: path.length - 1,
attributes: {
prefix: path.join('/'),
name: path[path.length - 1],
path: path.join('/'),
value: item,
},
children: [],
isLeafNode,
name: path.join('/'),
_initedBy: '',
key: '',
});
});
return res;
},
);
Loading

0 comments on commit 538aa27

Please sign in to comment.