mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 11:05:39 -04:00
[ML] DF Analytics creation wizard: show link to results (#74025)
* show view results card once job complete * update types * update types and move css to own file
This commit is contained in:
parent
8759646576
commit
3fb77fb546
10 changed files with 201 additions and 86 deletions
|
@ -1,3 +1,4 @@
|
||||||
@import 'pages/analytics_exploration/components/regression_exploration/index';
|
@import 'pages/analytics_exploration/components/regression_exploration/index';
|
||||||
@import 'pages/analytics_management/components/analytics_list/index';
|
@import 'pages/analytics_management/components/analytics_list/index';
|
||||||
@import 'pages/analytics_management/components/create_analytics_button/index';
|
@import 'pages/analytics_management/components/create_analytics_button/index';
|
||||||
|
@import 'pages/analytics_creation/components/index';
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
.dfAnalyticsCreationWizard__card {
|
||||||
|
width: 300px;
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { FC, Fragment } from 'react';
|
import React, { FC, Fragment } from 'react';
|
||||||
import { EuiCard, EuiHorizontalRule, EuiIcon } from '@elastic/eui';
|
import { EuiCard, EuiIcon } from '@elastic/eui';
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { useNavigateToPath } from '../../../../../contexts/kibana';
|
import { useNavigateToPath } from '../../../../../contexts/kibana';
|
||||||
|
|
||||||
|
@ -18,10 +18,8 @@ export const BackToListPanel: FC = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<EuiHorizontalRule />
|
|
||||||
<EuiCard
|
<EuiCard
|
||||||
// @ts-ignore
|
className="dfAnalyticsCreationWizard__card"
|
||||||
style={{ width: '300px' }}
|
|
||||||
icon={<EuiIcon size="xxl" type="list" />}
|
icon={<EuiIcon size="xxl" type="list" />}
|
||||||
title={i18n.translate('xpack.ml.dataframe.analytics.create.analyticsListCardTitle', {
|
title={i18n.translate('xpack.ml.dataframe.analytics.create.analyticsListCardTitle', {
|
||||||
defaultMessage: 'Data Frame Analytics',
|
defaultMessage: 'Data Frame Analytics',
|
||||||
|
|
|
@ -18,8 +18,7 @@ import { i18n } from '@kbn/i18n';
|
||||||
import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form';
|
import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form';
|
||||||
import { Messages } from '../shared';
|
import { Messages } from '../shared';
|
||||||
import { ANALYTICS_STEPS } from '../../page';
|
import { ANALYTICS_STEPS } from '../../page';
|
||||||
import { BackToListPanel } from '../back_to_list_panel';
|
import { CreateStepFooter } from '../create_step_footer';
|
||||||
import { ProgressStats } from './progress_stats';
|
|
||||||
|
|
||||||
interface Props extends CreateAnalyticsFormProps {
|
interface Props extends CreateAnalyticsFormProps {
|
||||||
step: ANALYTICS_STEPS;
|
step: ANALYTICS_STEPS;
|
||||||
|
@ -28,7 +27,7 @@ interface Props extends CreateAnalyticsFormProps {
|
||||||
export const CreateStep: FC<Props> = ({ actions, state, step }) => {
|
export const CreateStep: FC<Props> = ({ actions, state, step }) => {
|
||||||
const { createAnalyticsJob, startAnalyticsJob } = actions;
|
const { createAnalyticsJob, startAnalyticsJob } = actions;
|
||||||
const { isAdvancedEditorValidJson, isJobCreated, isJobStarted, isValid, requestMessages } = state;
|
const { isAdvancedEditorValidJson, isJobCreated, isJobStarted, isValid, requestMessages } = state;
|
||||||
const { jobId } = state.form;
|
const { jobId, jobType } = state.form;
|
||||||
|
|
||||||
const [checked, setChecked] = useState<boolean>(true);
|
const [checked, setChecked] = useState<boolean>(true);
|
||||||
const [showProgress, setShowProgress] = useState<boolean>(false);
|
const [showProgress, setShowProgress] = useState<boolean>(false);
|
||||||
|
@ -86,8 +85,9 @@ export const CreateStep: FC<Props> = ({ actions, state, step }) => {
|
||||||
)}
|
)}
|
||||||
<EuiSpacer size="s" />
|
<EuiSpacer size="s" />
|
||||||
<Messages messages={requestMessages} />
|
<Messages messages={requestMessages} />
|
||||||
{isJobCreated === true && showProgress && <ProgressStats jobId={jobId} />}
|
{isJobCreated === true && (
|
||||||
{isJobCreated === true && <BackToListPanel />}
|
<CreateStepFooter jobId={jobId} jobType={jobType!} showProgress={showProgress} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,38 +4,43 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { FC, useState, useEffect } from 'react';
|
import React, { FC, useEffect, useState } from 'react';
|
||||||
import {
|
import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui';
|
||||||
EuiCallOut,
|
|
||||||
EuiFlexGroup,
|
|
||||||
EuiFlexItem,
|
|
||||||
EuiProgress,
|
|
||||||
EuiSpacer,
|
|
||||||
EuiText,
|
|
||||||
} from '@elastic/eui';
|
|
||||||
import { i18n } from '@kbn/i18n';
|
import { i18n } from '@kbn/i18n';
|
||||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
|
||||||
import {
|
import {
|
||||||
getDataFrameAnalyticsProgressPhase,
|
getDataFrameAnalyticsProgressPhase,
|
||||||
DATA_FRAME_TASK_STATE,
|
DATA_FRAME_TASK_STATE,
|
||||||
} from '../../../analytics_management/components/analytics_list/common';
|
} from '../../../analytics_management/components/analytics_list/common';
|
||||||
import { isGetDataFrameAnalyticsStatsResponseOk } from '../../../analytics_management/services/analytics_service/get_analytics';
|
import { isGetDataFrameAnalyticsStatsResponseOk } from '../../../analytics_management/services/analytics_service/get_analytics';
|
||||||
|
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||||
import { ml } from '../../../../../services/ml_api_service';
|
import { ml } from '../../../../../services/ml_api_service';
|
||||||
import { DataFrameAnalyticsId } from '../../../../common/analytics';
|
import { BackToListPanel } from '../back_to_list_panel';
|
||||||
|
import { ViewResultsPanel } from '../view_results_panel';
|
||||||
|
import { ProgressStats } from './progress_stats';
|
||||||
|
import { ANALYSIS_CONFIG_TYPE } from '../../../../common/analytics';
|
||||||
|
|
||||||
export const PROGRESS_REFRESH_INTERVAL_MS = 1000;
|
export const PROGRESS_REFRESH_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
export const ProgressStats: FC<{ jobId: DataFrameAnalyticsId }> = ({ jobId }) => {
|
interface Props {
|
||||||
const [initialized, setInitialized] = useState<boolean>(false);
|
jobId: string;
|
||||||
const [failedJobMessage, setFailedJobMessage] = useState<string | undefined>(undefined);
|
jobType: ANALYSIS_CONFIG_TYPE;
|
||||||
const [currentProgress, setCurrentProgress] = useState<
|
showProgress: boolean;
|
||||||
| {
|
}
|
||||||
|
|
||||||
|
export interface AnalyticsProgressStats {
|
||||||
currentPhase: number;
|
currentPhase: number;
|
||||||
progress: number;
|
progress: number;
|
||||||
totalPhases: number;
|
totalPhases: number;
|
||||||
}
|
}
|
||||||
| undefined
|
|
||||||
>(undefined);
|
export const CreateStepFooter: FC<Props> = ({ jobId, jobType, showProgress }) => {
|
||||||
|
const [initialized, setInitialized] = useState<boolean>(false);
|
||||||
|
const [failedJobMessage, setFailedJobMessage] = useState<string | undefined>(undefined);
|
||||||
|
const [jobFinished, setJobFinished] = useState<boolean>(false);
|
||||||
|
const [currentProgress, setCurrentProgress] = useState<AnalyticsProgressStats | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
services: { notifications },
|
services: { notifications },
|
||||||
|
@ -77,6 +82,7 @@ export const ProgressStats: FC<{ jobId: DataFrameAnalyticsId }> = ({ jobId }) =>
|
||||||
jobStats.state === DATA_FRAME_TASK_STATE.STOPPED
|
jobStats.state === DATA_FRAME_TASK_STATE.STOPPED
|
||||||
) {
|
) {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
|
setJobFinished(true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
|
@ -95,62 +101,26 @@ export const ProgressStats: FC<{ jobId: DataFrameAnalyticsId }> = ({ jobId }) =>
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [initialized]);
|
}, [initialized]);
|
||||||
|
|
||||||
if (currentProgress === undefined) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<EuiFlexGroup direction="column">
|
||||||
<EuiSpacer />
|
|
||||||
{failedJobMessage !== undefined && (
|
|
||||||
<>
|
|
||||||
<EuiCallOut
|
|
||||||
data-test-subj="analyticsWizardProgressCallout"
|
|
||||||
title={i18n.translate(
|
|
||||||
'xpack.ml.dataframe.analytics.create.analyticsProgressCalloutTitle',
|
|
||||||
{
|
|
||||||
defaultMessage: 'Job failed',
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
color={'danger'}
|
|
||||||
iconType={'alert'}
|
|
||||||
size="s"
|
|
||||||
>
|
|
||||||
<p>{failedJobMessage}</p>
|
|
||||||
</EuiCallOut>
|
|
||||||
<EuiSpacer size="s" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<EuiText size="m">
|
|
||||||
<strong>
|
|
||||||
{i18n.translate('xpack.ml.dataframe.analytics.create.analyticsProgressTitle', {
|
|
||||||
defaultMessage: 'Progress',
|
|
||||||
})}
|
|
||||||
</strong>
|
|
||||||
</EuiText>
|
|
||||||
<EuiSpacer size="s" />
|
|
||||||
<EuiFlexGroup alignItems="center">
|
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiText size="s">
|
{showProgress && (
|
||||||
<strong>
|
<ProgressStats currentProgress={currentProgress} failedJobMessage={failedJobMessage} />
|
||||||
{i18n.translate('xpack.ml.dataframe.analytics.create.analyticsProgressPhaseTitle', {
|
)}
|
||||||
defaultMessage: 'Phase',
|
|
||||||
})}{' '}
|
|
||||||
{currentProgress.currentPhase}/{currentProgress.totalPhases}
|
|
||||||
</strong>
|
|
||||||
</EuiText>
|
|
||||||
</EuiFlexItem>
|
|
||||||
<EuiFlexItem style={{ width: '400px' }} grow={false}>
|
|
||||||
<EuiProgress
|
|
||||||
value={currentProgress.progress}
|
|
||||||
max={100}
|
|
||||||
color="primary"
|
|
||||||
size="l"
|
|
||||||
data-test-subj="mlAnalyticsCreationWizardProgress"
|
|
||||||
/>
|
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
<EuiFlexItem grow={false}>
|
<EuiFlexItem grow={false}>
|
||||||
<EuiText size="s">{`${currentProgress.progress}%`}</EuiText>
|
<EuiHorizontalRule />
|
||||||
|
<EuiFlexGroup>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<BackToListPanel />
|
||||||
|
</EuiFlexItem>
|
||||||
|
{jobFinished === true && (
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<ViewResultsPanel jobId={jobId} analysisType={jobType} />
|
||||||
|
</EuiFlexItem>
|
||||||
|
)}
|
||||||
|
</EuiFlexGroup>
|
||||||
</EuiFlexItem>
|
</EuiFlexItem>
|
||||||
</EuiFlexGroup>
|
</EuiFlexGroup>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -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 { CreateStepFooter } from './create_step_footer';
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
EuiCallOut,
|
||||||
|
EuiFlexGroup,
|
||||||
|
EuiFlexItem,
|
||||||
|
EuiProgress,
|
||||||
|
EuiSpacer,
|
||||||
|
EuiText,
|
||||||
|
} from '@elastic/eui';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { AnalyticsProgressStats } from './create_step_footer';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
currentProgress?: AnalyticsProgressStats;
|
||||||
|
failedJobMessage: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProgressStats: FC<Props> = ({ currentProgress, failedJobMessage }) => {
|
||||||
|
if (currentProgress === undefined) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<EuiSpacer />
|
||||||
|
{failedJobMessage !== undefined && (
|
||||||
|
<>
|
||||||
|
<EuiCallOut
|
||||||
|
data-test-subj="analyticsWizardProgressCallout"
|
||||||
|
title={i18n.translate(
|
||||||
|
'xpack.ml.dataframe.analytics.create.analyticsProgressCalloutTitle',
|
||||||
|
{
|
||||||
|
defaultMessage: 'Job failed',
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
color={'danger'}
|
||||||
|
iconType={'alert'}
|
||||||
|
size="s"
|
||||||
|
>
|
||||||
|
<p>{failedJobMessage}</p>
|
||||||
|
</EuiCallOut>
|
||||||
|
<EuiSpacer size="s" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<EuiText size="m">
|
||||||
|
<strong>
|
||||||
|
{i18n.translate('xpack.ml.dataframe.analytics.create.analyticsProgressTitle', {
|
||||||
|
defaultMessage: 'Progress',
|
||||||
|
})}
|
||||||
|
</strong>
|
||||||
|
</EuiText>
|
||||||
|
<EuiSpacer size="s" />
|
||||||
|
<EuiFlexGroup alignItems="center">
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiText size="s">
|
||||||
|
<strong>
|
||||||
|
{i18n.translate('xpack.ml.dataframe.analytics.create.analyticsProgressPhaseTitle', {
|
||||||
|
defaultMessage: 'Phase',
|
||||||
|
})}{' '}
|
||||||
|
{currentProgress.currentPhase}/{currentProgress.totalPhases}
|
||||||
|
</strong>
|
||||||
|
</EuiText>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem style={{ width: '400px' }} grow={false}>
|
||||||
|
<EuiProgress
|
||||||
|
value={currentProgress.progress}
|
||||||
|
max={100}
|
||||||
|
color="primary"
|
||||||
|
size="l"
|
||||||
|
data-test-subj="mlAnalyticsCreationWizardProgress"
|
||||||
|
/>
|
||||||
|
</EuiFlexItem>
|
||||||
|
<EuiFlexItem grow={false}>
|
||||||
|
<EuiText size="s">{`${currentProgress.progress}%`}</EuiText>
|
||||||
|
</EuiFlexItem>
|
||||||
|
</EuiFlexGroup>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
|
@ -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 { ViewResultsPanel } from './view_results_panel';
|
|
@ -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, Fragment } from 'react';
|
||||||
|
import { EuiCard, EuiIcon } from '@elastic/eui';
|
||||||
|
import { i18n } from '@kbn/i18n';
|
||||||
|
import { useNavigateToPath } from '../../../../../contexts/kibana';
|
||||||
|
import { getResultsUrl } from '../../../analytics_management/components/analytics_list/common';
|
||||||
|
import { ANALYSIS_CONFIG_TYPE } from '../../../../common/analytics';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
jobId: string;
|
||||||
|
analysisType: ANALYSIS_CONFIG_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ViewResultsPanel: FC<Props> = ({ jobId, analysisType }) => {
|
||||||
|
const navigateToPath = useNavigateToPath();
|
||||||
|
|
||||||
|
const redirectToAnalyticsManagementPage = async () => {
|
||||||
|
const path = getResultsUrl(jobId, analysisType);
|
||||||
|
await navigateToPath(path);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<EuiCard
|
||||||
|
className="dfAnalyticsCreationWizard__card"
|
||||||
|
icon={<EuiIcon size="xxl" type="tableDensityNormal" />}
|
||||||
|
title={i18n.translate('xpack.ml.dataframe.analytics.create.viewResultsCardTitle', {
|
||||||
|
defaultMessage: 'View Results',
|
||||||
|
})}
|
||||||
|
description={i18n.translate(
|
||||||
|
'xpack.ml.dataframe.analytics.create.viewResultsCardDescription',
|
||||||
|
{
|
||||||
|
defaultMessage: 'View results for the analytics job.',
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
onClick={redirectToAnalyticsManagementPage}
|
||||||
|
data-test-subj="analyticsWizardViewResultsCard"
|
||||||
|
/>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
|
@ -130,6 +130,6 @@ export function isCompletedAnalyticsJob(stats: DataFrameAnalyticsStats) {
|
||||||
return stats.state === DATA_FRAME_TASK_STATE.STOPPED && progress === 100;
|
return stats.state === DATA_FRAME_TASK_STATE.STOPPED && progress === 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getResultsUrl(jobId: string, analysisType: string) {
|
export function getResultsUrl(jobId: string, analysisType: ANALYSIS_CONFIG_TYPE | string) {
|
||||||
return `#/data_frame_analytics/exploration?_g=(ml:(jobId:${jobId},analysisType:${analysisType}))`;
|
return `#/data_frame_analytics/exploration?_g=(ml:(jobId:${jobId},analysisType:${analysisType}))`;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue