Skip to content

Commit

Permalink
[ML] Adding post create job options (#43205)
Browse files Browse the repository at this point in the history
* [ML] Adding post create job options

* adding toasts

* fixing toast string

* tweaking continue job function

* updating ids

* removing ts-ignore
  • Loading branch information
jgowdyelastic authored Aug 14, 2019
1 parent 6da05e0 commit 4acfc04
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ class CreateWatchFlyoutUI extends Component {
}
}

closeFlyout = () => {
this.setState({ isFlyoutVisible: false });
closeFlyout = (watchCreated = false) => {
this.setState({ isFlyoutVisible: false }, ()=>{
if (typeof this.props.flyoutHidden === 'function') {
this.props.flyoutHidden(watchCreated);
}
});
}

showFlyout = (jobId) => {
Expand All @@ -107,7 +111,7 @@ class CreateWatchFlyoutUI extends Component {
mlCreateWatchService.createNewWatch(this.state.jobId)
.then((resp) => {
toastNotifications.addSuccess(getSuccessToast(resp.id, resp.url, intl));
this.closeFlyout();
this.closeFlyout(true);
})
.catch((error) => {
toastNotifications.addDanger(intl.formatMessage({
Expand Down Expand Up @@ -194,6 +198,7 @@ class CreateWatchFlyoutUI extends Component {
CreateWatchFlyoutUI.propTypes = {
setShowFunction: PropTypes.func.isRequired,
unsetShowFunction: PropTypes.func.isRequired,
flyoutHidden: PropTypes.func,
};

export const CreateWatchFlyout = injectI18n(CreateWatchFlyoutUI);
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,6 @@ export class JobsListView extends Component {
<CreateWatchFlyout
setShowFunction={this.setShowCreateWatchFlyoutFunction}
unsetShowFunction={this.unsetShowCreateWatchFlyoutFunction}
compile={this.props.compile}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,12 @@ export class JobCreator {
return this._subscribers;
}

public async createAndStartJob() {
public async createAndStartJob(): Promise<JobRunner> {
try {
await this.createJob();
await this.createDatafeed();
await this.startDatafeed();
const jobRunner = await this.startDatafeed();
return jobRunner;
} catch (error) {
throw error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export class MultiMetricJobCreator extends JobCreator {
public cloneFromExistingJob(job: Job, datafeed: Datafeed) {
this._overrideConfigs(job, datafeed);
this.jobId = '';
this.createdBy = CREATED_BY_LABEL.MULTI_METRIC;
const detectors = getRichDetectors(job.analysis_config.detectors);

this.removeAllDetectors();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export class PopulationJobCreator extends JobCreator {
public cloneFromExistingJob(job: Job, datafeed: Datafeed) {
this._overrideConfigs(job, datafeed);
this.jobId = '';
this.createdBy = CREATED_BY_LABEL.POPULATION;
const detectors = getRichDetectors(job.analysis_config.detectors);

this.removeAllDetectors();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export class SingleMetricJobCreator extends JobCreator {
public cloneFromExistingJob(job: Job, datafeed: Datafeed) {
this._overrideConfigs(job, datafeed);
this.jobId = '';
this.createdBy = CREATED_BY_LABEL.SINGLE_METRIC;
const detectors = getRichDetectors(job.analysis_config.detectors);

this.removeAllDetectors();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,20 @@ export class JobRunner {
// start the datafeed and then start polling for progress
// the complete percentage is added to an observable
// so all pre-subscribed listeners can follow along.
public async startDatafeed(): Promise<void> {
private async _startDatafeed(
start: number | undefined,
end: number | undefined,
pollProgress: boolean
): Promise<boolean> {
try {
await this.openJob();
await mlJobService.startDatafeed(this._datafeedId, this._jobId, this._start, this._end);
const { started } = await mlJobService.startDatafeed(
this._datafeedId,
this._jobId,
start,
end
);

this._datafeedState = DATAFEED_STATE.STARTED;
this._percentageComplete = 0;

Expand All @@ -87,12 +97,25 @@ export class JobRunner {
};
// wait for the first check to run and then return success.
// all subsequent checks will update the observable
await check();
if (pollProgress === true) {
await check();
}
return started;
} catch (error) {
throw error;
}
}

public async startDatafeed() {
return await this._startDatafeed(this._start, this._end, true);
}

public async startDatafeedInRealTime(continueJob: boolean) {
// if continuing a job, set the start to be the end date
const start = continueJob ? this._end : this._start;
return await this._startDatafeed(start, undefined, false);
}

public async getProgress(): Promise<{ progress: Progress; isRunning: boolean }> {
return await ml.jobs.getLookBackProgress(this._jobId, this._start, this._end);
}
Expand Down
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 { PostSaveOptions } from './post_save_options';
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* 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, useContext, useState } from 'react';
import { toastNotifications } from 'ui/notify';
import { EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { JobRunner } from '../../../../../common/job_runner';

// @ts-ignore
import { CreateWatchFlyout } from '../../../../../../jobs_list/components/create_watch_flyout';
import { JobCreatorContext } from '../../../../components/job_creator_context';
import { DATAFEED_STATE } from '../../../../../../../../common/constants/states';

interface Props {
jobRunner: JobRunner | null;
}

type ShowFlyout = (jobId: string) => void;

export const PostSaveOptions: FC<Props> = ({ jobRunner }) => {
const { jobCreator } = useContext(JobCreatorContext);
const [datafeedState, setDatafeedState] = useState(DATAFEED_STATE.STOPPED);
const [watchFlyoutVisible, setWatchFlyoutVisible] = useState(false);
const [watchCreated, setWatchCreated] = useState(false);

function setShowCreateWatchFlyoutFunction(showFlyout: ShowFlyout) {
showFlyout(jobCreator.jobId);
}

function flyoutHidden(jobCreated: boolean) {
setWatchFlyoutVisible(false);
setWatchCreated(jobCreated);
}

function unsetShowCreateWatchFlyoutFunction() {
setWatchFlyoutVisible(false);
}

async function startJobInRealTime() {
setDatafeedState(DATAFEED_STATE.STARTING);
if (jobRunner !== null) {
try {
const started = await jobRunner.startDatafeedInRealTime(true);
setDatafeedState(started === true ? DATAFEED_STATE.STARTED : DATAFEED_STATE.STOPPED);
toastNotifications.addSuccess({
title: i18n.translate('xpack.ml.newJob.wizard.startJobInRealTimeSuccess', {
defaultMessage: `Job {jobId} started`,
values: { jobId: jobCreator.jobId },
}),
});
} catch (error) {
setDatafeedState(DATAFEED_STATE.STOPPED);
toastNotifications.addDanger({
title: i18n.translate('xpack.ml.newJob.wizard.startJobInRealTimeError', {
defaultMessage: `Error starting job`,
}),
text: error.message,
});
}
}
}

return (
<Fragment>
&emsp;
<EuiButton
isDisabled={
datafeedState === DATAFEED_STATE.STARTING || datafeedState === DATAFEED_STATE.STARTED
}
onClick={startJobInRealTime}
data-test-subj="mlButtonUseFullData3"
>
<FormattedMessage
id="xpack.ml.newJob.wizard.startJobInRealTime"
defaultMessage="Start job running in real time"
/>
</EuiButton>
&emsp;
<EuiButton
isDisabled={
datafeedState === DATAFEED_STATE.STOPPED ||
datafeedState === DATAFEED_STATE.STARTING ||
watchCreated === true
}
onClick={() => setWatchFlyoutVisible(true)}
data-test-subj="mlButtonUseFullData"
>
<FormattedMessage id="xpack.ml.newJob.wizard.createWatch" defaultMessage="Create watch" />
</EuiButton>
{datafeedState === DATAFEED_STATE.STARTED && watchFlyoutVisible && (
<CreateWatchFlyout
setShowFunction={setShowCreateWatchFlyoutFunction}
unsetShowFunction={unsetShowCreateWatchFlyoutFunction}
flyoutHidden={flyoutHidden}
/>
)}
</Fragment>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,35 @@ import { toastNotifications } from 'ui/notify';
import { WizardNav } from '../wizard_nav';
import { WIZARD_STEPS, StepProps } from '../step_types';
import { JobCreatorContext } from '../job_creator_context';
import { JobRunner } from '../../../common/job_runner';
import { mlJobService } from '../../../../../services/job_service';
import { JsonFlyout } from './json_flyout';
import { isSingleMetricJobCreator } from '../../../common/job_creator';
import { JobDetails } from './job_details';
import { DetectorChart } from './detector_chart';
import { JobProgress } from './components/job_progress';
import { PostSaveOptions } from './components/post_save_options';

export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) => {
const { jobCreator, jobValidator, jobValidatorUpdated, resultsLoader } = useContext(
JobCreatorContext
);
const [progress, setProgress] = useState(resultsLoader.progress);
const [showJsonFlyout, setShowJsonFlyout] = useState(false);
const [creatingJob, setCreatingJob] = useState(false);
const [isValid, setIsValid] = useState(jobValidator.validationSummary.basic);
const [jobRunner, setJobRunner] = useState<JobRunner | null>(null);

useEffect(() => {
jobCreator.subscribeToProgress(setProgress);
}, []);

async function start() {
setShowJsonFlyout(false);
setCreatingJob(true);
try {
await jobCreator.createAndStartJob();
const jr = await jobCreator.createAndStartJob();
setJobRunner(jr);
} catch (error) {
// catch and display all job creation errors
toastNotifications.addDanger({
Expand All @@ -42,6 +48,7 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
}),
text: error.message,
});
setCreatingJob(false);
}
}

Expand Down Expand Up @@ -79,13 +86,16 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
<Fragment>
<EuiButton
onClick={start}
isDisabled={progress > 0}
disabled={isValid === false}
isDisabled={creatingJob === true || isValid === false}
data-test-subj="mlJobWizardButtonCreateJob"
>
Create job
</EuiButton>
&emsp;
</Fragment>
)}
{creatingJob === false && (
<Fragment>
<EuiButtonEmpty
size="s"
onClick={toggleJsonFlyout}
Expand All @@ -97,14 +107,18 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
{showJsonFlyout && (
<JsonFlyout closeFlyout={() => setShowJsonFlyout(false)} jobCreator={jobCreator} />
)}
&emsp;
</Fragment>
)}
{progress > 0 && (
<Fragment>
<EuiButton onClick={viewResults} data-test-subj="mlJobWizardButtonViewResults">
View results
</EuiButton>
{progress === 100 && (
<Fragment>
<PostSaveOptions jobRunner={jobRunner} />
</Fragment>
)}
</Fragment>
)}
</Fragment>
Expand Down
7 changes: 6 additions & 1 deletion x-pack/legacy/plugins/ml/public/services/job_service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ declare interface JobService {
cloneJob(job: any): any;
openJob(jobId: string): Promise<any>;
saveNewDatafeed(datafeedConfig: any, jobId: string): Promise<any>;
startDatafeed(datafeedId: string, jobId: string, start: number, end: number): Promise<any>;
startDatafeed(
datafeedId: string,
jobId: string,
start: number | undefined,
end: number | undefined
): Promise<any>;
createResultsUrl(jobId: string[], start: number, end: number, location: string): string;
getJobAndGroupIds(): ExistingJobsAndGroups;
}
Expand Down

0 comments on commit 4acfc04

Please sign in to comment.