diff --git a/x-pack/plugins/ml/common/constants/categorization_job.ts b/x-pack/plugins/ml/common/constants/categorization_job.ts new file mode 100644 index 00000000000000..c1c65e4bf15b85 --- /dev/null +++ b/x-pack/plugins/ml/common/constants/categorization_job.ts @@ -0,0 +1,80 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { VALIDATION_RESULT } from '../types/categories'; + +export const NUMBER_OF_CATEGORY_EXAMPLES = 5; +export const CATEGORY_EXAMPLES_SAMPLE_SIZE = 1000; +export const CATEGORY_EXAMPLES_WARNING_LIMIT = 0.75; +export const CATEGORY_EXAMPLES_ERROR_LIMIT = 0.02; + +export const VALID_TOKEN_COUNT = 3; +export const MEDIAN_LINE_LENGTH_LIMIT = 400; +export const NULL_COUNT_PERCENT_LIMIT = 0.75; + +export enum CATEGORY_EXAMPLES_VALIDATION_STATUS { + VALID = 'valid', + PARTIALLY_VALID = 'partially_valid', + INVALID = 'invalid', +} + +export const VALIDATION_CHECK_DESCRIPTION = { + [VALIDATION_RESULT.NO_EXAMPLES]: i18n.translate( + 'xpack.ml.models.jobService.categorization.messages.validNoDataFound', + { + defaultMessage: 'Examples were successfully loaded.', + } + ), + [VALIDATION_RESULT.FAILED_TO_TOKENIZE]: i18n.translate( + 'xpack.ml.models.jobService.categorization.messages.validFailureToGetTokens', + { + defaultMessage: 'The examples loaded were tokenized successfully.', + } + ), + [VALIDATION_RESULT.TOKEN_COUNT]: i18n.translate( + 'xpack.ml.models.jobService.categorization.messages.validTokenLength', + { + defaultMessage: + 'More than {tokenCount} tokens per example were found in over {percentage}% of the examples loaded.', + values: { + percentage: Math.floor(CATEGORY_EXAMPLES_WARNING_LIMIT * 100), + tokenCount: VALID_TOKEN_COUNT, + }, + } + ), + [VALIDATION_RESULT.MEDIAN_LINE_LENGTH]: i18n.translate( + 'xpack.ml.models.jobService.categorization.messages.validMedianLineLength', + { + defaultMessage: + 'The median line length of the examples loaded was less than {medianCharCount} characters.', + values: { + medianCharCount: MEDIAN_LINE_LENGTH_LIMIT, + }, + } + ), + [VALIDATION_RESULT.NULL_VALUES]: i18n.translate( + 'xpack.ml.models.jobService.categorization.messages.validNullValues', + { + defaultMessage: 'Less than {percentage}% of the examples loaded were null.', + values: { + percentage: Math.floor(100 - NULL_COUNT_PERCENT_LIMIT * 100), + }, + } + ), + [VALIDATION_RESULT.TOO_MANY_TOKENS]: i18n.translate( + 'xpack.ml.models.jobService.categorization.messages.validTooManyTokens', + { + defaultMessage: 'Less than 10000 tokens were found in total in the examples loaded.', + } + ), + [VALIDATION_RESULT.INSUFFICIENT_PRIVILEGES]: i18n.translate( + 'xpack.ml.models.jobService.categorization.messages.validUserPrivileges', + { + defaultMessage: 'The user has sufficient privileges to perform the checks.', + } + ), +}; diff --git a/x-pack/plugins/ml/common/constants/new_job.ts b/x-pack/plugins/ml/common/constants/new_job.ts index 862fa72d11fdb7..751413bb6485a0 100644 --- a/x-pack/plugins/ml/common/constants/new_job.ts +++ b/x-pack/plugins/ml/common/constants/new_job.ts @@ -25,15 +25,3 @@ export const DEFAULT_RARE_BUCKET_SPAN = '1h'; export const DEFAULT_QUERY_DELAY = '60s'; export const SHARED_RESULTS_INDEX_NAME = 'shared'; - -// Categorization -export const NUMBER_OF_CATEGORY_EXAMPLES = 5; -export const CATEGORY_EXAMPLES_SAMPLE_SIZE = 1000; -export const CATEGORY_EXAMPLES_WARNING_LIMIT = 0.75; -export const CATEGORY_EXAMPLES_ERROR_LIMIT = 0.02; - -export enum CATEGORY_EXAMPLES_VALIDATION_STATUS { - VALID = 'valid', - PARTIALLY_VALID = 'partially_valid', - INVALID = 'invalid', -} diff --git a/x-pack/plugins/ml/common/types/categories.ts b/x-pack/plugins/ml/common/types/categories.ts index 862ad8e194a0b2..5d4c3eab53ee88 100644 --- a/x-pack/plugins/ml/common/types/categories.ts +++ b/x-pack/plugins/ml/common/types/categories.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../constants/new_job'; +import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../constants/categorization_job'; export type CategoryId = number; @@ -39,12 +39,12 @@ export interface CategoryFieldExample { } export enum VALIDATION_RESULT { + NO_EXAMPLES, + FAILED_TO_TOKENIZE, + TOO_MANY_TOKENS, TOKEN_COUNT, MEDIAN_LINE_LENGTH, NULL_VALUES, - NO_EXAMPLES, - TOO_MANY_TOKENS, - FAILED_TO_TOKENIZE, INSUFFICIENT_PRIVILEGES, } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts index 7407a43aa9d5e0..95fd9df892cab9 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts @@ -16,8 +16,8 @@ import { CREATED_BY_LABEL, DEFAULT_BUCKET_SPAN, DEFAULT_RARE_BUCKET_SPAN, - CATEGORY_EXAMPLES_VALIDATION_STATUS, } from '../../../../../../common/constants/new_job'; +import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../../common/constants/categorization_job'; import { ML_JOB_AGGREGATION } from '../../../../../../common/constants/aggregation_types'; import { CategorizationAnalyzer, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts index 8f6b16c407fb66..82e5e15a24d5c6 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_validator/job_validator.ts @@ -16,7 +16,7 @@ import { JobCreator, JobCreatorType, isCategorizationJobCreator } from '../job_c import { populateValidationMessages, checkForExistingJobAndGroupIds } from './util'; import { ExistingJobsAndGroups } from '../../../../services/job_service'; import { cardinalityValidator, CardinalityValidatorResult } from './validators'; -import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../../common/constants/new_job'; +import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../../common/constants/categorization_job'; // delay start of validation to allow the user to make changes // e.g. if they are typing in a new value, try not to validate diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts index 8f3a56b6b2b903..de550f61858e66 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/categorization_examples_loader.ts @@ -11,7 +11,7 @@ import { ml } from '../../../../services/ml_api_service'; import { NUMBER_OF_CATEGORY_EXAMPLES, CATEGORY_EXAMPLES_VALIDATION_STATUS, -} from '../../../../../../common/constants/new_job'; +} from '../../../../../../common/constants/categorization_job'; export class CategorizationExamplesLoader { private _jobCreator: CategorizationJobCreator; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/additional_section.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/additional_section.tsx index dd287d10ab2c85..75856d5276fdfe 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/additional_section.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/job_details_step/components/additional_section/additional_section.tsx @@ -5,11 +5,17 @@ */ import React, { FC, Fragment } from 'react'; +import { i18n } from '@kbn/i18n'; import { EuiAccordion, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { CalendarsSelection } from './components/calendars'; import { CustomUrlsSelection } from './components/custom_urls'; -const ButtonContent = Additional settings; +const buttonContent = i18n.translate( + 'xpack.ml.newJob.wizard.jobDetailsStep.additionalSectionButton', + { + defaultMessage: 'Additional settings', + } +); interface Props { additionalExpanded: boolean; @@ -22,7 +28,7 @@ export const AdditionalSection: FC = ({ additionalExpanded, setAdditional = ({ advancedExpanded, setAdvancedExpand = ({ overallValidStatus, validationChecks, @@ -66,6 +84,10 @@ export const ExamplesValidCallout: FC = ({ ))} {analyzerUsed} + + + + ); }; @@ -96,3 +118,28 @@ const AnalyzerUsed: FC<{ categorizationAnalyzer: CategorizationAnalyzer }> = ({ ); }; + +const AllValidationChecks: FC<{ validationChecks: FieldExampleCheck[] }> = ({ + validationChecks, +}) => { + const list: EuiListGroupItemProps[] = Object.keys(VALIDATION_CHECK_DESCRIPTION).map((k, i) => { + const failedCheck = validationChecks.find(vc => vc.id === i); + if ( + failedCheck !== undefined && + failedCheck?.valid !== CATEGORY_EXAMPLES_VALIDATION_STATUS.VALID + ) { + return { + iconType: 'cross', + label: failedCheck.message, + size: 's', + }; + } + return { + iconType: 'check', + label: VALIDATION_CHECK_DESCRIPTION[i as VALIDATION_RESULT], + size: 's', + }; + }); + + return ; +}; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx index 411f6e898bd486..f5c3e90d63418d 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection.tsx @@ -18,7 +18,7 @@ import { CategoryFieldExample, FieldExampleCheck, } from '../../../../../../../../../common/types/categories'; -import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../../../../../common/constants/new_job'; +import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../../../../../common/constants/categorization_job'; import { LoadingWrapper } from '../../../charts/loading_wrapper'; interface Props { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx index 3bade07250b464..227c93dc2d86ba 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/top_categories.tsx @@ -11,7 +11,7 @@ import { JobCreatorContext } from '../../../job_creator_context'; import { CategorizationJobCreator } from '../../../../../common/job_creator'; import { Results } from '../../../../../common/results_loader'; import { ml } from '../../../../../../../services/ml_api_service'; -import { NUMBER_OF_CATEGORY_EXAMPLES } from '../../../../../../../../../common/constants/new_job'; +import { NUMBER_OF_CATEGORY_EXAMPLES } from '../../../../../../../../../common/constants/categorization_job'; export const TopCategories: FC = () => { const { jobCreator: jc, resultsLoader } = useContext(JobCreatorContext); diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts index bcceffb14123eb..16e25067fd91e3 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts @@ -17,7 +17,7 @@ import { CategoryFieldExample, FieldExampleCheck, } from '../../../../common/types/categories'; -import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../common/constants/new_job'; +import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../common/constants/categorization_job'; import { Category } from '../../../../common/types/categories'; export const jobs = { diff --git a/x-pack/plugins/ml/server/models/job_service/new_job/categorization/examples.ts b/x-pack/plugins/ml/server/models/job_service/new_job/categorization/examples.ts index ea2c71b04f56d2..b209dc56815637 100644 --- a/x-pack/plugins/ml/server/models/job_service/new_job/categorization/examples.ts +++ b/x-pack/plugins/ml/server/models/job_service/new_job/categorization/examples.ts @@ -6,7 +6,7 @@ import { chunk } from 'lodash'; import { SearchResponse } from 'elasticsearch'; -import { CATEGORY_EXAMPLES_SAMPLE_SIZE } from '../../../../../common/constants/new_job'; +import { CATEGORY_EXAMPLES_SAMPLE_SIZE } from '../../../../../common/constants/categorization_job'; import { Token, CategorizationAnalyzer, diff --git a/x-pack/plugins/ml/server/models/job_service/new_job/categorization/validation_results.ts b/x-pack/plugins/ml/server/models/job_service/new_job/categorization/validation_results.ts index 34e63eabb405ef..e3b37fffa9c770 100644 --- a/x-pack/plugins/ml/server/models/job_service/new_job/categorization/validation_results.ts +++ b/x-pack/plugins/ml/server/models/job_service/new_job/categorization/validation_results.ts @@ -6,10 +6,13 @@ import { i18n } from '@kbn/i18n'; import { + VALID_TOKEN_COUNT, + MEDIAN_LINE_LENGTH_LIMIT, + NULL_COUNT_PERCENT_LIMIT, CATEGORY_EXAMPLES_VALIDATION_STATUS, CATEGORY_EXAMPLES_ERROR_LIMIT, CATEGORY_EXAMPLES_WARNING_LIMIT, -} from '../../../../../common/constants/new_job'; +} from '../../../../../common/constants/categorization_job'; import { FieldExampleCheck, CategoryFieldExample, @@ -17,10 +20,6 @@ import { } from '../../../../../common/types/categories'; import { getMedianStringLength } from '../../../../../common/util/string_utils'; -const VALID_TOKEN_COUNT = 3; -const MEDIAN_LINE_LENGTH_LIMIT = 400; -const NULL_COUNT_PERCENT_LIMIT = 0.75; - export class ValidationResults { private _results: FieldExampleCheck[] = []; @@ -187,7 +186,6 @@ export class ValidationResults { valid: CATEGORY_EXAMPLES_VALIDATION_STATUS.PARTIALLY_VALID, message, }); - return; } } diff --git a/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts b/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts index ba7b9c31ad64cc..aab7a65a7c1223 100644 --- a/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts +++ b/x-pack/test/api_integration/apis/ml/categorization_field_examples.ts @@ -96,7 +96,7 @@ export default ({ getService }: FtrProviderContext) => { exampleLength: 5, validationChecks: [ { - id: 0, + id: 3, valid: 'valid', message: '1000 field values analyzed, 95% contain 3 or more tokens.', }, @@ -117,12 +117,12 @@ export default ({ getService }: FtrProviderContext) => { exampleLength: 5, validationChecks: [ { - id: 1, + id: 4, valid: 'partially_valid', message: 'The median length for the field values analyzed is over 400 characters.', }, { - id: 4, + id: 2, valid: 'invalid', message: 'Tokenization of field value examples has failed due to more than 10000 tokens being found in a sample of 50 values.', @@ -144,12 +144,12 @@ export default ({ getService }: FtrProviderContext) => { exampleLength: 5, validationChecks: [ { - id: 0, + id: 3, valid: 'valid', message: '250 field values analyzed, 95% contain 3 or more tokens.', }, { - id: 2, + id: 5, valid: 'partially_valid', message: 'More than 75% of field values are null.', }, @@ -170,12 +170,12 @@ export default ({ getService }: FtrProviderContext) => { exampleLength: 5, validationChecks: [ { - id: 0, + id: 3, valid: 'valid', message: '500 field values analyzed, 100% contain 3 or more tokens.', }, { - id: 1, + id: 4, valid: 'partially_valid', message: 'The median length for the field values analyzed is over 400 characters.', }, @@ -196,7 +196,7 @@ export default ({ getService }: FtrProviderContext) => { exampleLength: 0, validationChecks: [ { - id: 3, + id: 0, valid: 'invalid', message: 'No examples for this field could be found. Please ensure the selected date range contains data.', @@ -218,7 +218,7 @@ export default ({ getService }: FtrProviderContext) => { exampleLength: 5, validationChecks: [ { - id: 0, + id: 3, valid: 'invalid', message: '1000 field values analyzed, 0% contain 3 or more tokens.', }, @@ -242,7 +242,7 @@ export default ({ getService }: FtrProviderContext) => { exampleLength: 5, validationChecks: [ { - id: 0, + id: 3, valid: 'valid', message: '1000 field values analyzed, 100% contain 3 or more tokens.', }, @@ -263,7 +263,7 @@ export default ({ getService }: FtrProviderContext) => { exampleLength: 5, validationChecks: [ { - id: 0, + id: 3, valid: 'partially_valid', message: '1000 field values analyzed, 50% contain 3 or more tokens.', }, diff --git a/x-pack/test/functional/apps/machine_learning/anomaly_detection/categorization_job.ts b/x-pack/test/functional/apps/machine_learning/anomaly_detection/categorization_job.ts index 80f020f66c0ed4..9fa53d6e546ba6 100644 --- a/x-pack/test/functional/apps/machine_learning/anomaly_detection/categorization_job.ts +++ b/x-pack/test/functional/apps/machine_learning/anomaly_detection/categorization_job.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../plugins/ml/common/constants/new_job'; +import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../../plugins/ml/common/constants/categorization_job'; // eslint-disable-next-line import/no-default-export export default function({ getService }: FtrProviderContext) { diff --git a/x-pack/test/functional/services/machine_learning/job_wizard_categorization.ts b/x-pack/test/functional/services/machine_learning/job_wizard_categorization.ts index 2f4162c0cb60a8..97d45701a2685d 100644 --- a/x-pack/test/functional/services/machine_learning/job_wizard_categorization.ts +++ b/x-pack/test/functional/services/machine_learning/job_wizard_categorization.ts @@ -6,7 +6,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../plugins/ml/common/constants/new_job'; +import { CATEGORY_EXAMPLES_VALIDATION_STATUS } from '../../../../plugins/ml/common/constants/categorization_job'; export function MachineLearningJobWizardCategorizationProvider({ getService }: FtrProviderContext) { const comboBox = getService('comboBox');