[ML] Adding bucket span estimator to new wizards (#43288)

* [ML] Adding bucket span estimator to new wizards

* disabling next button when estimating

* fixing population
This commit is contained in:
James Gowdy 2019-08-14 20:09:22 +01:00 committed by GitHub
parent c65b9752cb
commit f4b0a9c962
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 147 additions and 10 deletions

View file

@ -95,11 +95,19 @@ export class JobCreator {
return agg !== undefined ? agg : null;
}
public get aggregations(): Aggregation[] {
return this._aggs;
}
public getField(index: number): Field | null {
const field = this._fields[index];
return field !== undefined ? field : null;
}
public get fields(): Field[] {
return this._fields;
}
public set bucketSpan(bucketSpan: BucketSpan) {
this._job_config.analysis_config.bucket_span = bucketSpan;
}

View file

@ -5,12 +5,18 @@
*/
import React, { FC, useContext, useEffect, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { BucketSpanInput } from './bucket_span_input';
import { JobCreatorContext } from '../../../job_creator_context';
import { Description } from './description';
import { BucketSpanEstimator } from '../bucket_span_estimator';
export const BucketSpan: FC = () => {
interface Props {
setIsValid: (proceed: boolean) => void;
}
export const BucketSpan: FC<Props> = ({ setIsValid }) => {
const {
jobCreator,
jobCreatorUpdate,
@ -20,6 +26,7 @@ export const BucketSpan: FC = () => {
} = useContext(JobCreatorContext);
const [bucketSpan, setBucketSpan] = useState(jobCreator.bucketSpan);
const [validation, setValidation] = useState(jobValidator.bucketSpan);
const [estimating, setEstimating] = useState(false);
useEffect(() => {
jobCreator.bucketSpan = bucketSpan;
@ -34,13 +41,25 @@ export const BucketSpan: FC = () => {
setValidation(jobValidator.bucketSpan);
}, [jobValidatorUpdated]);
useEffect(() => {
setIsValid(estimating === false);
}, [estimating]);
return (
<Description validation={validation}>
<BucketSpanInput
setBucketSpan={setBucketSpan}
bucketSpan={bucketSpan}
isInvalid={validation.valid === false}
/>
<EuiFlexGroup gutterSize="s">
<EuiFlexItem>
<BucketSpanInput
setBucketSpan={setBucketSpan}
bucketSpan={bucketSpan}
isInvalid={validation.valid === false}
disabled={estimating}
/>
</EuiFlexItem>
<EuiFlexItem>
<BucketSpanEstimator setEstimating={setEstimating} />
</EuiFlexItem>
</EuiFlexGroup>
</Description>
);
};

View file

@ -11,11 +11,13 @@ interface Props {
bucketSpan: string;
setBucketSpan: (bs: string) => void;
isInvalid: boolean;
disabled: boolean;
}
export const BucketSpanInput: FC<Props> = ({ bucketSpan, setBucketSpan, isInvalid }) => {
export const BucketSpanInput: FC<Props> = ({ bucketSpan, setBucketSpan, isInvalid, disabled }) => {
return (
<EuiFieldText
disabled={disabled}
placeholder="Bucket span"
value={bucketSpan}
onChange={e => setBucketSpan(e.target.value)}

View file

@ -0,0 +1,28 @@
/*
* 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, useEffect } from 'react';
import { EuiButton } from '@elastic/eui';
import { useEstimateBucketSpan, ESTIMATE_STATUS } from './estimate_bucket_span';
interface Props {
setEstimating(estimating: boolean): void;
}
export const BucketSpanEstimator: FC<Props> = ({ setEstimating }) => {
const { status, estimateBucketSpan } = useEstimateBucketSpan();
useEffect(() => {
setEstimating(status === ESTIMATE_STATUS.RUNNING);
}, [status]);
return (
<EuiButton disabled={status === ESTIMATE_STATUS.RUNNING} onClick={estimateBucketSpan}>
Estimate bucket span
</EuiButton>
);
};

View file

@ -0,0 +1,70 @@
/*
* 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 { useContext, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
import { JobCreatorContext } from '../../../job_creator_context';
import { EVENT_RATE_FIELD_ID } from '../../../../../../../../common/types/fields';
import { isMultiMetricJobCreator, isPopulationJobCreator } from '../../../../../common/job_creator';
import { ml } from '../../../../../../../services/ml_api_service';
import { useKibanaContext } from '../../../../../../../contexts/kibana';
export enum ESTIMATE_STATUS {
NOT_RUNNING,
RUNNING,
}
export function useEstimateBucketSpan() {
const { jobCreator, jobCreatorUpdate } = useContext(JobCreatorContext);
const kibanaContext = useKibanaContext();
const [status, setStatus] = useState(ESTIMATE_STATUS.NOT_RUNNING);
const data = {
aggTypes: jobCreator.aggregations.map(a => a.dslName),
duration: {
start: jobCreator.start,
end: jobCreator.end,
},
fields: jobCreator.fields.map(f => (f.id === EVENT_RATE_FIELD_ID ? null : f.id)),
index: kibanaContext.currentIndexPattern.title,
query: kibanaContext.combinedQuery,
splitField:
(isMultiMetricJobCreator(jobCreator) || isPopulationJobCreator(jobCreator)) &&
jobCreator.splitField !== null
? jobCreator.splitField.id
: undefined,
timeField: kibanaContext.currentIndexPattern.timeFieldName,
};
async function estimateBucketSpan() {
setStatus(ESTIMATE_STATUS.RUNNING);
const { name, error, message } = await ml.estimateBucketSpan(data);
setStatus(ESTIMATE_STATUS.NOT_RUNNING);
if (error === true) {
let text = '';
if (message !== undefined) {
if (typeof message === 'object') {
text = message.msg || JSON.stringify(message);
} else {
text = message;
}
}
toastNotifications.addDanger({
title: i18n.translate('xpack.ml.newJob.wizard.estimateBucketSpanError', {
defaultMessage: `Bucket span estimation error`,
}),
text,
});
} else {
jobCreator.bucketSpan = name;
jobCreatorUpdate();
}
}
return { status, estimateBucketSpan };
}

View file

@ -0,0 +1,6 @@
/*
* 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 { BucketSpanEstimator } from './bucket_span_estimator';

View file

@ -45,7 +45,7 @@ export const MultiMetricSettings: FC<Props> = ({ isActive, setIsValid }) => {
</EuiFlexGroup>
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem>
<BucketSpan />
<BucketSpan setIsValid={setIsValid} />
</EuiFlexItem>
<EuiFlexItem />
</EuiFlexGroup>

View file

@ -36,7 +36,7 @@ export const PopulationSettings: FC<Props> = ({ isActive, setIsValid }) => {
<Fragment>
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem>
<BucketSpan />
<BucketSpan setIsValid={setIsValid} />
</EuiFlexItem>
<EuiFlexItem>
<Influencers />

View file

@ -51,7 +51,7 @@ export const SingleMetricSettings: FC<Props> = ({ isActive, setIsValid }) => {
<Fragment>
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem>
<BucketSpan />
<BucketSpan setIsValid={setIsValid} />
</EuiFlexItem>
</EuiFlexGroup>
<EuiFlexGroup>

View file

@ -121,6 +121,10 @@ declare interface Ml {
end: number
): Promise<{ progress: number; isRunning: boolean }>;
};
estimateBucketSpan(
data: object
): Promise<{ name: string; ms: number; error?: boolean; message?: { msg: string } | string }>;
}
declare const ml: Ml;