Skip to content

Commit

Permalink
[ML] Data Frame Analytics creation wizard: add validation step (Part …
Browse files Browse the repository at this point in the history
…1) (#93478) (#93869)

* wip: create validationStep component

* wip: trainingPercent check, analysisFields check. Step details

* move validation check to server

* handle no training percent in validation

* move callout component to shared dir

* use shared Callout component in AD val and update message headings

* update types

* adds functional tests for validation

* adds api integration test for validate endpoint

* consolidate messages for depvar and fields

* fix accessibility test

* update license

* update validation messages

* update types in validation model

* add jobValidationReturnType
  • Loading branch information
alvarezmelissa87 authored Mar 6, 2021
1 parent ad63751 commit 46e7ec0
Show file tree
Hide file tree
Showing 28 changed files with 947 additions and 101 deletions.
17 changes: 17 additions & 0 deletions x-pack/plugins/ml/common/constants/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,16 @@
* 2.0.
*/

export interface CalloutMessage {
id: string;
heading: string;
status: VALIDATION_STATUS;
text: string;
url?: string;
}

export type ValidateAnalyticsJobResponse = CalloutMessage[];

export enum VALIDATION_STATUS {
ERROR = 'error',
INFO = 'info',
Expand All @@ -17,3 +27,10 @@ export const SKIP_BUCKET_SPAN_ESTIMATION = true;
export const ALLOWED_DATA_UNITS = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];

export const JOB_ID_MAX_LENGTH = 64;

// Data Frame Analytics
export const TRAINING_DOCS_UPPER = 200000;
export const TRAINING_DOCS_LOWER = 200;
export const INCLUDED_FIELDS_THRESHOLD = 100;
export const MINIMUM_NUM_FIELD_FOR_CHECK = 25;
export const FRACTION_EMPTY_LIMIT = 0.3;
4 changes: 2 additions & 2 deletions x-pack/plugins/ml/common/types/data_frame_analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ export interface OutlierAnalysis {

interface Regression {
dependent_variable: string;
training_percent?: number;
training_percent: number;
num_top_feature_importance_values?: number;
prediction_field_name?: string;
}

interface Classification {
class_assignment_objective?: string;
dependent_variable: string;
training_percent?: number;
training_percent: number;
num_top_classes?: number;
num_top_feature_importance_values?: number;
prediction_field_name?: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { FC } from 'react';
import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { CalloutMessage, VALIDATION_STATUS } from '../../../../common/constants/validation';

export const defaultIconType = 'questionInCircle';

const statusToEuiColor = (status: VALIDATION_STATUS) => {
switch (status) {
case VALIDATION_STATUS.INFO:
return 'primary';
case VALIDATION_STATUS.ERROR:
return 'danger';
default:
return status;
}
};

export const statusToEuiIconType = (status: VALIDATION_STATUS) => {
switch (status) {
case VALIDATION_STATUS.INFO:
return 'iInCircle';
case VALIDATION_STATUS.ERROR:
return 'cross';
case VALIDATION_STATUS.SUCCESS:
return 'check';
case VALIDATION_STATUS.WARNING:
return 'alert';
default:
return status;
}
};

const Link: FC<{ url: string }> = ({ url }) => (
<EuiLink href={url} target="_BLANK">
<FormattedMessage id="xpack.ml.validateJob.learnMoreLinkText" defaultMessage="Learn more" />
</EuiLink>
);

const Message: FC<Pick<CalloutMessage, 'text' | 'url'>> = ({ text, url }) => (
<>
{text} {url && <Link url={url} />}
</>
);

export const Callout: FC<CalloutMessage> = ({ heading, status, text, url }) => (
<>
<EuiCallOut
data-test-subj={'mlValidationCallout'}
// @ts-ignore
color={statusToEuiColor(status)}
size="s"
title={heading || <Message text={text} url={url} />}
iconType={status ? statusToEuiIconType(status) : defaultIconType}
>
{heading && <Message text={text} url={url} />}
</EuiCallOut>
<EuiSpacer size="m" />
</>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { Callout, statusToEuiIconType } from './callout';
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ import React, { Component, Fragment } from 'react';

import {
EuiButton,
EuiCallOut,
EuiLink,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
EuiSpacer,
EuiText,
EuiFlexGroup,
EuiFlexItem,
Expand All @@ -31,6 +29,7 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { getDocLinks } from '../../util/dependency_cache';

import { VALIDATION_STATUS } from '../../../../common/constants/validation';
import { Callout, statusToEuiIconType } from '../callout';
import { getMostSevereMessageStatus } from '../../../../common/util/validation_utils';
import { toastNotificationServiceProvider } from '../../services/toast_notification_service';
import { withKibana } from '../../../../../../../src/plugins/kibana_react/public';
Expand All @@ -49,77 +48,20 @@ const getDefaultState = () => ({
title: '',
});

const statusToEuiColor = (status) => {
switch (status) {
case VALIDATION_STATUS.INFO:
return 'primary';
break;
case VALIDATION_STATUS.ERROR:
return 'danger';
break;
default:
return status;
}
};

const statusToEuiIconType = (status) => {
switch (status) {
case VALIDATION_STATUS.INFO:
return 'iInCircle';
break;
case VALIDATION_STATUS.ERROR:
return 'cross';
break;
case VALIDATION_STATUS.SUCCESS:
return 'check';
break;
case VALIDATION_STATUS.WARNING:
return 'alert';
break;
default:
return status;
}
};

const Link = ({ url }) => (
<EuiLink href={url} target="_BLANK">
<FormattedMessage id="xpack.ml.validateJob.learnMoreLinkText" defaultMessage="Learn more" />
</EuiLink>
);
Link.propTypes = {
url: PropTypes.string.isRequired,
};

// Message is its own component so it can be passed
// as the "title" prop in the Callout component.
const Message = ({ message }) => (
<React.Fragment>
{message.text} {message.url && <Link url={message.url} />}
</React.Fragment>
);
Message.propTypes = {
message: PropTypes.shape({
text: PropTypes.string,
url: PropTypes.string,
}),
};

const MessageList = ({ messages, idFilterList }) => {
const callouts = messages
.filter((m) => idFilterList.includes(m.id) === false)
.map((m, i) => <Callout key={`${m.id}_${i}`} message={m} />);
.map((m, i) => <Callout key={`${m.id}_${i}`} {...m} />);

// there could be no error or success messages due to the
// idFilterList being applied. so rather than showing nothing,
// show a message saying all passed
const allPassedCallout = (
<Callout
message={{
text: i18n.translate('xpack.ml.validateJob.allPassed', {
defaultMessage: 'All validation checks passed successfully',
}),
status: VALIDATION_STATUS.SUCCESS,
}}
text={i18n.translate('xpack.ml.validateJob.allPassed', {
defaultMessage: 'All validation checks passed successfully',
})}
status={VALIDATION_STATUS.SUCCESS}
/>
);

Expand All @@ -130,27 +72,6 @@ MessageList.propTypes = {
idFilterList: PropTypes.array,
};

const Callout = ({ message }) => (
<React.Fragment>
<EuiCallOut
color={statusToEuiColor(message.status)}
size="s"
title={message.heading || <Message message={message} />}
iconType={statusToEuiIconType(message.status)}
>
{message.heading && <Message message={message} />}
</EuiCallOut>
<EuiSpacer size="m" />
</React.Fragment>
);
Callout.propTypes = {
message: PropTypes.shape({
status: PropTypes.string,
text: PropTypes.string,
url: PropTypes.string,
}),
};

const LoadingSpinner = () => (
<EuiFlexGroup justifyContent="spaceAround" alignItems="center">
<EuiFlexItem grow={false}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ export const getTrainingPercent = (
analysis: AnalysisConfig
):
| RegressionAnalysis['regression']['training_percent']
| ClassificationAnalysis['classification']['training_percent'] => {
| ClassificationAnalysis['classification']['training_percent']
| undefined => {
let trainingPercent;

if (isRegressionAnalysis(analysis)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ export interface ListItems {
description: string | JSX.Element;
}

export const AdvancedStepDetails: FC<{ setCurrentStep: any; state: State }> = ({
setCurrentStep,
state,
}) => {
export const AdvancedStepDetails: FC<{
setCurrentStep: React.Dispatch<React.SetStateAction<ANALYTICS_STEPS>>;
state: State;
}> = ({ setCurrentStep, state }) => {
const { form, isJobCreated } = state;
const {
computeFeatureInfluence,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { ANALYTICS_STEPS } from '../../page';
const MAX_INCLUDES_LENGTH = 5;

interface Props {
setCurrentStep: React.Dispatch<React.SetStateAction<any>>;
setCurrentStep: React.Dispatch<React.SetStateAction<ANALYTICS_STEPS>>;
state: State;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ export interface ListItems {
description: string | JSX.Element;
}

export const DetailsStepDetails: FC<{ setCurrentStep: any; state: State }> = ({
setCurrentStep,
state,
}) => {
export const DetailsStepDetails: FC<{
setCurrentStep: React.Dispatch<React.SetStateAction<ANALYTICS_STEPS>>;
state: State;
}> = ({ setCurrentStep, state }) => {
const { form, isJobCreated } = state;
const { description, jobId, destinationIndex, resultsField } = form;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
<ContinueButton
isDisabled={isStepInvalid}
onClick={() => {
setCurrentStep(ANALYTICS_STEPS.CREATE);
setCurrentStep(ANALYTICS_STEPS.VALIDATION);
}}
/>
</Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { ConfigurationStep } from './configuration_step/index';
export { AdvancedStep } from './advanced_step/index';
export { DetailsStep } from './details_step/index';
export { CreateStep } from './create_step/index';
export { ValidationStepWrapper } from './validation_step/index';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { ValidationStepWrapper } from './validation_step_wrapper';
Loading

0 comments on commit 46e7ec0

Please sign in to comment.