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 0000000000000..c1c65e4bf15b8
--- /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 862fa72d11fdb..751413bb6485a 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 862ad8e194a0b..5d4c3eab53ee8 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 7407a43aa9d5e..95fd9df892cab 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 8f6b16c407fb6..82e5e15a24d5c 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 8f3a56b6b2b90..de550f61858e6 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 dd287d10ab2c8..75856d5276fdf 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 411f6e898bd48..f5c3e90d63418 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 3bade07250b46..227c93dc2d86b 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 bcceffb14123e..16e25067fd91e 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 ea2c71b04f56d..b209dc5681563 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 34e63eabb405e..e3b37fffa9c77 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 ba7b9c31ad64c..aab7a65a7c122 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 80f020f66c0ed..9fa53d6e546ba 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 2f4162c0cb60a..97d45701a2685 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');