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

[ML] Data Frames Summary Stats Bar #43986

Merged
merged 8 commits into from
Aug 27, 2019
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import 'stat';
@import 'stats_bar';
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.stat {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest to make the class names prefixed with ml and not nested.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good - this will likely be fixed by the follow up to switch to using the Eui Stat component.

margin-right: $euiSizeS;

.stat-value {
font-weight: bold
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.mlStatsBar {
// SASSTODO: proper calcs
height: 42px;
padding: 14px;
background-color: $euiColorLightestShade;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also drop these styles by using EuiStat inside of an EuiFlexGroup.

7 changes: 7 additions & 0 deletions x-pack/legacy/plugins/ml/public/components/stats_bar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

export { StatsBar, TransformStatsBarStats } from './stats_bar';
24 changes: 24 additions & 0 deletions x-pack/legacy/plugins/ml/public/components/stats_bar/stat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { FC } from 'react';

export interface StatsBarStat {
label: string;
value: string | number;
show?: boolean;
}
interface StatProps {
stat: StatsBarStat;
}

export const Stat: FC<StatProps> = ({ stat }) => {
return (
<span className="stat">
<span>{stat.label}</span>: <span className="stat-value">{stat.value}</span>
</span>
);
};
46 changes: 46 additions & 0 deletions x-pack/legacy/plugins/ml/public/components/stats_bar/stats_bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { FC } from 'react';
import { Stat, StatsBarStat } from './stat';

interface JobStatsBarStats {
activeNodes: StatsBarStat;
total: StatsBarStat;
open: StatsBarStat;
failed: StatsBarStat;
closed: StatsBarStat;
activeDatafeeds: StatsBarStat;
}

export interface TransformStatsBarStats {
total: StatsBarStat;
batch: StatsBarStat;
continuous: StatsBarStat;
failed: StatsBarStat;
started: StatsBarStat;
}

type StatsBarStats = TransformStatsBarStats | JobStatsBarStats;
type StatsKey = keyof StatsBarStats;

interface StatsBarProps {
stats: StatsBarStats;
dataTestSub: string;
}

export const StatsBar: FC<StatsBarProps> = ({ stats, dataTestSub }) => {
const statsList = Object.keys(stats).map(k => stats[k as StatsKey]);
return (
<div className="mlStatsBar" data-test-subj={dataTestSub}>
{statsList
.filter((s: StatsBarStat) => s.show)
.map((s: StatsBarStat) => (
<Stat key={s.label} stat={s} />
))}
</div>
);
};

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

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

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,14 @@ import { DataFrameTransformList } from './transform_list';

describe('Data Frame: Transform List <DataFrameTransformList />', () => {
test('Minimal initialization', () => {
const wrapper = shallow(<DataFrameTransformList />);
const wrapper = shallow(
<DataFrameTransformList
isInitialized={true}
transforms={[]}
errorMessage={undefined}
transformsLoading={false}
/>
);

expect(wrapper).toMatchSnapshot();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,7 @@ import {
SortDirection,
} from '@elastic/eui';

import {
DataFrameTransformId,
moveToDataFrameWizard,
useRefreshTransformList,
} from '../../../../common';
import { DataFrameTransformId, moveToDataFrameWizard } from '../../../../common';
import { checkPermission } from '../../../../../privilege/check_privilege';
import { getTaskStateBadge } from './columns';
import { DeleteAction } from './action_delete';
Expand All @@ -39,11 +35,9 @@ import {
Query,
Clause,
} from './common';
import { getTransformsFactory } from '../../services/transform_service';
import { getColumns } from './columns';
import { ExpandedRow } from './expanded_row';
import { ProgressBar, TransformTable } from './transform_table';
import { useRefreshInterval } from './use_refresh_interval';

function getItemIdToExpandedRowMap(
itemIds: DataFrameTransformId[],
Expand All @@ -69,20 +63,28 @@ function stringMatch(str: string | undefined, substr: string) {
);
}

export const DataFrameTransformList: SFC = () => {
const [isInitialized, setIsInitialized] = useState(false);
interface Props {
isInitialized: boolean;
transforms: DataFrameTransformListRow[];
errorMessage: any;
transformsLoading: boolean;
}

export const DataFrameTransformList: SFC<Props> = ({
isInitialized,
transforms,
errorMessage,
transformsLoading,
}) => {
const [isLoading, setIsLoading] = useState(false);
const [blockRefresh, setBlockRefresh] = useState(false);
const [filterActive, setFilterActive] = useState(false);

const [transforms, setTransforms] = useState<DataFrameTransformListRow[]>([]);
const [filteredTransforms, setFilteredTransforms] = useState<DataFrameTransformListRow[]>([]);
const [expandedRowItemIds, setExpandedRowItemIds] = useState<DataFrameTransformId[]>([]);

const [transformSelection, setTransformSelection] = useState<DataFrameTransformListRow[]>([]);
const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(false);

const [errorMessage, setErrorMessage] = useState<any>(undefined);
const [searchError, setSearchError] = useState<any>(undefined);

const [pageIndex, setPageIndex] = useState(0);
Expand All @@ -96,20 +98,6 @@ export const DataFrameTransformList: SFC = () => {
!checkPermission('canPreviewDataFrame') ||
!checkPermission('canStartStopDataFrame');

const getTransforms = getTransformsFactory(
setTransforms,
setErrorMessage,
setIsInitialized,
blockRefresh
);
// Subscribe to the refresh observable to trigger reloading the transform list.
useRefreshTransformList({
isLoading: setIsLoading,
onRefresh: () => getTransforms(true),
});
// Call useRefreshInterval() after the subscription above is set up.
useRefreshInterval(setBlockRefresh);

const onQueryChange = ({ query, error }: { query: Query; error: any }) => {
if (error) {
setSearchError(error.message);
Expand Down Expand Up @@ -188,13 +176,13 @@ export const DataFrameTransformList: SFC = () => {
// Before the transforms have been loaded for the first time, display the loading indicator only.
// Otherwise a user would see 'No data frame transforms found' during the initial loading.
if (!isInitialized) {
return <ProgressBar isLoading={isLoading} />;
return <ProgressBar isLoading={isLoading || transformsLoading} />;
alvarezmelissa87 marked this conversation as resolved.
Show resolved Hide resolved
}

if (typeof errorMessage !== 'undefined') {
return (
<Fragment>
<ProgressBar isLoading={isLoading} />
<ProgressBar isLoading={isLoading || transformsLoading} />
<EuiCallOut
title={i18n.translate('xpack.ml.dataFrame.list.errorPromptTitle', {
defaultMessage: 'An error occurred getting the data frame transform list.',
Expand All @@ -211,7 +199,7 @@ export const DataFrameTransformList: SFC = () => {
if (transforms.length === 0) {
return (
<Fragment>
<ProgressBar isLoading={isLoading} />
<ProgressBar isLoading={isLoading || transformsLoading} />
<EuiEmptyPrompt
title={
<h2>
Expand Down Expand Up @@ -368,7 +356,7 @@ export const DataFrameTransformList: SFC = () => {

return (
<Fragment>
<ProgressBar isLoading={isLoading} />
<ProgressBar isLoading={isLoading || transformsLoading} />
<TransformTable
className="mlTransformTable"
columns={columns}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { FC } from 'react';
import { i18n } from '@kbn/i18n';
import { StatsBar, TransformStatsBarStats } from '../../../../../components/stats_bar';
import { DATA_FRAME_TRANSFORM_STATE, DATA_FRAME_MODE, DataFrameTransformListRow } from './common';

function createTranformStats(transformsList: DataFrameTransformListRow[]) {
const transformStats = {
total: {
label: i18n.translate('xpack.ml.dataFrame.statsBar.totalTransformsLabel', {
defaultMessage: 'Total transforms',
}),
value: 0,
show: true,
},
batch: {
label: i18n.translate('xpack.ml.dataFrame.statsBar.batchTransformsLabel', {
defaultMessage: 'Batch transforms',
}),
value: 0,
show: true,
},
continuous: {
label: i18n.translate('xpack.ml.dataFrame.statsBar.continuousTransformsLabel', {
defaultMessage: 'Continuous transforms',
}),
value: 0,
show: true,
},
failed: {
label: i18n.translate('xpack.ml.dataFrame.statsBar.failedTransformsLabel', {
defaultMessage: 'Failed transforms',
}),
value: 0,
show: false,
},
started: {
label: i18n.translate('xpack.ml.dataFrame.statsBar.startedTransformsLabel', {
defaultMessage: 'Started transforms',
}),
value: 0,
show: true,
},
};

if (transformsList === undefined) {
return transformStats;
}

let failedTransforms = 0;
let startedTransforms = 0;

transformsList.forEach(transform => {
if (transform.mode === DATA_FRAME_MODE.CONTINUOUS) {
transformStats.continuous.value++;
} else if (transform.mode === DATA_FRAME_MODE.BATCH) {
transformStats.batch.value++;
} else if (transform.stats.state === DATA_FRAME_TRANSFORM_STATE.FAILED) {
failedTransforms++;
} else if (transform.stats.state === DATA_FRAME_TRANSFORM_STATE.STARTED) {
startedTransforms++;
}
});

transformStats.total.value = transformsList.length;
transformStats.started.value = startedTransforms;

if (failedTransforms !== 0) {
transformStats.failed.value = failedTransforms;
transformStats.failed.show = true;
} else {
transformStats.failed.show = false;
}

return transformStats;
}

interface Props {
transformsList: DataFrameTransformListRow[];
}

export const TransformStatsBar: FC<Props> = ({ transformsList }) => {
const transformStats: TransformStatsBarStats = createTranformStats(transformsList);

return <StatsBar stats={transformStats} dataTestSub={'mlTransformStatsBar'} />;
};
Loading