[ML] severity cell with multi-bucket impact support (#46002)
* [ML] severity cell with multi-bucket impact support * [ML] fix type check * [ML] fix PR remarks * [ML] replace css classes with Eui components * [ML] severity cell with multi-bucket impact support * [ML] fix type check * [ML] fix PR remarks * [ML] replace css classes with Eui components * [ML] set props for eui flex
This commit is contained in:
parent
1786364104
commit
999e2188af
|
@ -4,15 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// Thresholds for indicating the impact of multi-bucket features in an anomaly.
|
||||
// As a rule-of-thumb, a threshold value T corresponds to the multi-bucket probability
|
||||
// being 1000^(T/5) times smaller than the single bucket probability.
|
||||
// So, for example, for HIGH it is 63 times smaller.
|
||||
/**
|
||||
* Thresholds for indicating the impact of multi-bucket features in an anomaly.
|
||||
* As a rule-of-thumb, a threshold value T corresponds to the multi-bucket probability
|
||||
* being 1000^(T/5) times smaller than the single bucket probability.
|
||||
* So, for example, for HIGH it is 63 times smaller.
|
||||
*/
|
||||
export const MULTI_BUCKET_IMPACT = {
|
||||
HIGH: 3,
|
||||
MEDIUM: 2,
|
||||
LOW: 1,
|
||||
NONE: -5
|
||||
NONE: -5,
|
||||
};
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
import {
|
||||
EuiButtonIcon,
|
||||
EuiHealth,
|
||||
EuiLink,
|
||||
} from '@elastic/eui';
|
||||
|
||||
|
@ -31,12 +30,13 @@ import { InfluencersCell } from './influencers_cell';
|
|||
import { LinksMenu } from './links_menu';
|
||||
import { checkPermission } from '../../privilege/check_privilege';
|
||||
import { mlFieldFormatService } from '../../services/field_format_service';
|
||||
import { getSeverityColor, isRuleSupported } from '../../../common/util/anomaly_utils';
|
||||
import { isRuleSupported } from '../../../common/util/anomaly_utils';
|
||||
import { formatValue } from '../../formatters/format_value';
|
||||
import {
|
||||
INFLUENCERS_LIMIT,
|
||||
ANOMALIES_TABLE_TABS
|
||||
} from './anomalies_table_constants';
|
||||
import { SeverityCell } from './severity_cell';
|
||||
|
||||
function renderTime(date, aggregationInterval) {
|
||||
if (aggregationInterval === 'hour') {
|
||||
|
@ -101,11 +101,7 @@ export function getColumns(
|
|||
name: i18n.translate('xpack.ml.anomaliesTable.severityColumnName', {
|
||||
defaultMessage: 'severity',
|
||||
}),
|
||||
render: (score) => (
|
||||
<EuiHealth color={getSeverityColor(score)} compressed="true">
|
||||
{score >= 1 ? Math.floor(score) : '< 1'}
|
||||
</EuiHealth>
|
||||
),
|
||||
render: (score, item) => <SeverityCell score={score} multiBucketImpact={item.source.multi_bucket_impact} />,
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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 { SeverityCell } from './severity_cell';
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 from 'react';
|
||||
import { cleanup, render } from 'react-testing-library';
|
||||
import { SeverityCell } from './severity_cell';
|
||||
|
||||
describe('SeverityCell', () => {
|
||||
afterEach(cleanup);
|
||||
|
||||
test('should render a single-bucket marker with rounded severity score', () => {
|
||||
const props = {
|
||||
score: 75.2,
|
||||
multiBucketImpact: -2,
|
||||
};
|
||||
const { container } = render(<SeverityCell {...props} />);
|
||||
expect(container.textContent).toBe('75');
|
||||
const svgEl = container.getElementsByTagName('svg').item(0);
|
||||
expect(svgEl && svgEl.style.fill).toBe('#fe5050');
|
||||
});
|
||||
|
||||
test('should render a multi-bucket marker with low severity score', () => {
|
||||
const props = {
|
||||
score: 0.8,
|
||||
multiBucketImpact: 4,
|
||||
};
|
||||
const { container } = render(<SeverityCell {...props} />);
|
||||
expect(container.textContent).toBe('< 1');
|
||||
const svgEl = container.getElementsByTagName('svg').item(0);
|
||||
expect(svgEl && svgEl.getAttribute('fill')).toBe('#d2e9f7');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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, memo } from 'react';
|
||||
import { EuiHealth, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { MULTI_BUCKET_IMPACT } from '../../../../common/constants/multi_bucket_impact';
|
||||
import { getSeverityColor } from '../../../../common/util/anomaly_utils';
|
||||
|
||||
interface SeverityCellProps {
|
||||
/**
|
||||
* Severity score.
|
||||
*/
|
||||
score: number;
|
||||
/**
|
||||
* Multi-bucket impact score from –5 to 5.
|
||||
* Anomalies with a multi-bucket impact value of greater than or equal
|
||||
* to 2 are indicated with a plus shaped symbol in the cell.
|
||||
*/
|
||||
multiBucketImpact: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders anomaly severity score with single or multi-bucket impact marker.
|
||||
*/
|
||||
export const SeverityCell: FC<SeverityCellProps> = memo(({ score, multiBucketImpact }) => {
|
||||
const severity = score >= 1 ? Math.floor(score) : '< 1';
|
||||
const color = getSeverityColor(score);
|
||||
const isMultiBucket = multiBucketImpact >= MULTI_BUCKET_IMPACT.MEDIUM;
|
||||
return isMultiBucket ? (
|
||||
<EuiFlexGroup gutterSize="xs" alignItems="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<svg width="16" height="16" viewBox="-2 -2 20 20" fill={color}>
|
||||
<path
|
||||
d="M-6.708203932499369,-2.23606797749979H-2.23606797749979V-6.708203932499369H2.23606797749979V-2.23606797749979H6.708203932499369V2.23606797749979H2.23606797749979V6.708203932499369H-2.23606797749979V2.23606797749979H-6.708203932499369Z"
|
||||
transform="translate(8,8)"
|
||||
></path>
|
||||
</svg>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>{severity}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
) : (
|
||||
<EuiHealth color={color}>{severity}</EuiHealth>
|
||||
);
|
||||
});
|
|
@ -32,7 +32,7 @@ export interface State {
|
|||
jobId: DataFrameAnalyticsId;
|
||||
jobIdExists: boolean;
|
||||
jobIdEmpty: boolean;
|
||||
jobIdInvalidMaxLength?: boolean;
|
||||
jobIdInvalidMaxLength: boolean;
|
||||
jobIdValid: boolean;
|
||||
sourceIndex: EsIndexName;
|
||||
sourceIndexNameEmpty: boolean;
|
||||
|
@ -66,6 +66,7 @@ export const getInitialState = (): State => ({
|
|||
jobId: '',
|
||||
jobIdExists: false,
|
||||
jobIdEmpty: true,
|
||||
jobIdInvalidMaxLength: false,
|
||||
jobIdValid: false,
|
||||
sourceIndex: '',
|
||||
sourceIndexNameEmpty: true,
|
||||
|
|
Loading…
Reference in a new issue