Skip to content

Commit 1bd8f41

Browse files
authored
[ML] Add indicator if there are stopped partitions in categorization job wizard (#75709)
1 parent c31acce commit 1bd8f41

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

x-pack/plugins/ml/public/application/jobs/new_job/common/results_loader/results_loader.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ export class ResultsLoader {
126126
this._results$.next(this._results);
127127
}
128128

129+
public get results$() {
130+
return this._results$;
131+
}
132+
129133
public subscribeToResults(func: ResultsSubscriber) {
130134
return this._results$.subscribe(func);
131135
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import React, { FC, useContext, useEffect, useState, useMemo, useCallback } from 'react';
8+
import { EuiBasicTable, EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui';
9+
import { FormattedMessage } from '@kbn/i18n/react';
10+
import { i18n } from '@kbn/i18n';
11+
import { from } from 'rxjs';
12+
import { switchMap, takeWhile, tap } from 'rxjs/operators';
13+
import { JobCreatorContext } from '../../../job_creator_context';
14+
import { CategorizationJobCreator } from '../../../../../common/job_creator';
15+
import { ml } from '../../../../../../../services/ml_api_service';
16+
import { extractErrorProperties } from '../../../../../../../../../common/util/errors';
17+
18+
const NUMBER_OF_PREVIEW = 5;
19+
export const CategoryStoppedPartitions: FC = () => {
20+
const { jobCreator: jc, resultsLoader } = useContext(JobCreatorContext);
21+
const jobCreator = jc as CategorizationJobCreator;
22+
const [tableRow, setTableRow] = useState<Array<{ partitionName: string }>>([]);
23+
const [stoppedPartitionsError, setStoppedPartitionsError] = useState<string | undefined>();
24+
25+
const columns = useMemo(
26+
() => [
27+
{
28+
field: 'partitionName',
29+
name: i18n.translate(
30+
'xpack.ml.newJob.wizard.pickFieldsStep.stoppedPartitionsPreviewColumnName',
31+
{
32+
defaultMessage: 'Stopped partition names',
33+
}
34+
),
35+
render: (partition: any) => (
36+
<EuiText size="s">
37+
<code>{partition}</code>
38+
</EuiText>
39+
),
40+
},
41+
],
42+
[]
43+
);
44+
45+
const loadCategoryStoppedPartitions = useCallback(async () => {
46+
try {
47+
const { jobs } = await ml.results.getCategoryStoppedPartitions([jobCreator.jobId]);
48+
49+
if (
50+
!Array.isArray(jobs) && // if jobs is object of jobId: [partitions]
51+
Array.isArray(jobs[jobCreator.jobId]) &&
52+
jobs[jobCreator.jobId].length > 0
53+
) {
54+
return jobs[jobCreator.jobId];
55+
}
56+
} catch (e) {
57+
const error = extractErrorProperties(e);
58+
// might get 404 because job has not been created yet and that's ok
59+
if (error.statusCode !== 404) {
60+
setStoppedPartitionsError(error.message);
61+
}
62+
}
63+
}, [jobCreator.jobId]);
64+
65+
useEffect(() => {
66+
// only need to run this check if jobCreator.perPartitionStopOnWarn is turned on
67+
if (jobCreator.perPartitionCategorization && jobCreator.perPartitionStopOnWarn) {
68+
// subscribe to result updates
69+
const resultsSubscription = resultsLoader.results$
70+
.pipe(
71+
switchMap(() => {
72+
return from(loadCategoryStoppedPartitions());
73+
}),
74+
tap((results) => {
75+
if (Array.isArray(results)) {
76+
setTableRow(
77+
results.slice(0, NUMBER_OF_PREVIEW).map((partitionName) => ({
78+
partitionName,
79+
}))
80+
);
81+
}
82+
}),
83+
takeWhile((results) => {
84+
return !results || (Array.isArray(results) && results.length <= NUMBER_OF_PREVIEW);
85+
})
86+
)
87+
.subscribe();
88+
return () => resultsSubscription.unsubscribe();
89+
}
90+
}, []);
91+
92+
return (
93+
<>
94+
{stoppedPartitionsError && (
95+
<>
96+
<EuiSpacer />
97+
<EuiCallOut
98+
color={'danger'}
99+
size={'s'}
100+
title={
101+
<FormattedMessage
102+
id="xpack.ml.newJob.wizard.pickFieldsStep.stoppedPartitionsErrorCallout"
103+
defaultMessage="An error occurred while fetching list of stopped partitions."
104+
/>
105+
}
106+
/>
107+
</>
108+
)}
109+
{Array.isArray(tableRow) && tableRow.length > 0 && (
110+
<>
111+
<EuiSpacer />
112+
<div>
113+
<FormattedMessage
114+
id="xpack.ml.newJob.wizard.pickFieldsStep.categorizationStoppedPartitionsTitle"
115+
defaultMessage="Stopped partitions"
116+
/>
117+
</div>
118+
<EuiSpacer size={'s'} />
119+
<EuiCallOut
120+
color={'warning'}
121+
size={'s'}
122+
title={
123+
<FormattedMessage
124+
id="xpack.ml.newJob.wizard.pickFieldsStep.stoppedPartitionsExistCallout"
125+
defaultMessage="Per-partition categorization and stop_on_warn settings are enabled. Some partitions in job '{jobId}' are unsuitable for categorization and have been excluded from further categorization or anomaly detection analysis."
126+
values={{
127+
jobId: jobCreator.jobId,
128+
}}
129+
/>
130+
}
131+
/>
132+
<EuiBasicTable columns={columns} items={tableRow} />
133+
</>
134+
)}
135+
</>
136+
);
137+
};

x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/categorization_view/metric_selection_summary.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Results, Anomaly } from '../../../../../common/results_loader';
1111
import { LineChartPoint } from '../../../../../common/chart_loader';
1212
import { EventRateChart } from '../../../charts/event_rate_chart';
1313
import { TopCategories } from './top_categories';
14+
import { CategoryStoppedPartitions } from './category_stopped_partitions';
1415

1516
const DTR_IDX = 0;
1617

@@ -73,6 +74,7 @@ export const CategorizationDetectorsSummary: FC = () => {
7374
fadeChart={jobIsRunning}
7475
/>
7576
<TopCategories />
77+
<CategoryStoppedPartitions />
7678
</>
7779
);
7880
};

0 commit comments

Comments
 (0)