mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[ML] translate anomalies table
This commit is contained in:
parent
7d9b110972
commit
a944705110
7 changed files with 246 additions and 67 deletions
|
@ -23,6 +23,8 @@ import {
|
|||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import { getColumns } from './anomalies_table_columns';
|
||||
|
||||
import { AnomalyDetails } from './anomaly_details';
|
||||
|
@ -158,7 +160,12 @@ class AnomaliesTable extends Component {
|
|||
<EuiFlexGroup justifyContent="spaceAround">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText>
|
||||
<h4>No matching anomalies found</h4>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.noMatchingAnomaliesFoundTitle"
|
||||
defaultMessage="No matching anomalies found"
|
||||
/>
|
||||
</h4>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'ngreact';
|
|||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { injectI18nProvider } from '@kbn/i18n/react';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import { AnomaliesTable } from './anomalies_table';
|
||||
|
@ -17,7 +18,7 @@ module.directive('mlAnomaliesTable', function ($injector) {
|
|||
const reactDirective = $injector.get('reactDirective');
|
||||
|
||||
return reactDirective(
|
||||
AnomaliesTable,
|
||||
injectI18nProvider(AnomaliesTable),
|
||||
[
|
||||
['filter', { watchDepth: 'reference' }],
|
||||
['tableData', { watchDepth: 'reference' }]
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import {
|
||||
EuiDescriptionList,
|
||||
|
@ -113,21 +115,33 @@ function getDetailsItems(anomaly, examples, filter) {
|
|||
let timeDesc = `${formatHumanReadableDateTimeSeconds(anomalyTime)}`;
|
||||
if (source.bucket_span !== undefined) {
|
||||
const anomalyEndTime = anomalyTime + (source.bucket_span * 1000);
|
||||
timeDesc += ` to ${formatHumanReadableDateTimeSeconds(anomalyEndTime)}`;
|
||||
timeDesc = i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.anomalyTimeRangeLabel', {
|
||||
defaultMessage: '{anomalyTime} to {anomalyEndTime}',
|
||||
values: {
|
||||
anomalyTime: formatHumanReadableDateTimeSeconds(anomalyTime),
|
||||
anomalyEndTime: formatHumanReadableDateTimeSeconds(anomalyEndTime),
|
||||
}
|
||||
});
|
||||
}
|
||||
items.push({
|
||||
title: 'time',
|
||||
title: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.timeTitle', {
|
||||
defaultMessage: 'time',
|
||||
}),
|
||||
description: timeDesc
|
||||
});
|
||||
|
||||
items.push({
|
||||
title: 'function',
|
||||
title: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.functionTitle', {
|
||||
defaultMessage: 'function',
|
||||
}),
|
||||
description: (source.function !== 'metric') ? source.function : source.function_description
|
||||
});
|
||||
|
||||
if (source.field_name !== undefined) {
|
||||
items.push({
|
||||
title: 'fieldName',
|
||||
title: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.fieldNameTitle', {
|
||||
defaultMessage: 'fieldName',
|
||||
}),
|
||||
description: source.field_name
|
||||
});
|
||||
}
|
||||
|
@ -135,33 +149,43 @@ function getDetailsItems(anomaly, examples, filter) {
|
|||
const functionDescription = source.function_description || '';
|
||||
if (anomaly.actual !== undefined && showActualForFunction(functionDescription) === true) {
|
||||
items.push({
|
||||
title: 'actual',
|
||||
title: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.actualTitle', {
|
||||
defaultMessage: 'actual',
|
||||
}),
|
||||
description: formatValue(anomaly.actual, source.function)
|
||||
});
|
||||
}
|
||||
|
||||
if (anomaly.typical !== undefined && showTypicalForFunction(functionDescription) === true) {
|
||||
items.push({
|
||||
title: 'typical',
|
||||
title: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.typicalTitle', {
|
||||
defaultMessage: 'typical',
|
||||
}),
|
||||
description: formatValue(anomaly.typical, source.function)
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: 'job ID',
|
||||
title: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.jobIdTitle', {
|
||||
defaultMessage: 'job ID',
|
||||
}),
|
||||
description: anomaly.jobId
|
||||
});
|
||||
|
||||
if (source.multi_bucket_impact !== undefined &&
|
||||
source.multi_bucket_impact >= MULTI_BUCKET_IMPACT.LOW) {
|
||||
items.push({
|
||||
title: 'multi-bucket impact',
|
||||
title: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.multiBucketImpactTitle', {
|
||||
defaultMessage: 'multi-bucket impact',
|
||||
}),
|
||||
description: getMultiBucketImpactLabel(source.multi_bucket_impact)
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: 'probability',
|
||||
title: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.probabilityTitle', {
|
||||
defaultMessage: 'probability',
|
||||
}),
|
||||
description: source.probability
|
||||
});
|
||||
|
||||
|
@ -169,9 +193,22 @@ function getDetailsItems(anomaly, examples, filter) {
|
|||
// will already have been added for display.
|
||||
if (causes.length > 1) {
|
||||
causes.forEach((cause, index) => {
|
||||
const title = (index === 0) ? `${cause.entityName} values` : '';
|
||||
let description = `${cause.entityValue} (actual ${formatValue(cause.actual, source.function)}, `;
|
||||
description += `typical ${formatValue(cause.typical, source.function)}, probability ${cause.probability})`;
|
||||
const title = (index === 0) ? i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.causeValuesTitle', {
|
||||
defaultMessage: '{causeEntityName} values',
|
||||
values: {
|
||||
causeEntityName: cause.entityName,
|
||||
}
|
||||
}) : '';
|
||||
const description = i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.causeValuesDescription', {
|
||||
defaultMessage: '{causeEntityValue} (actual {actualValue}, ' +
|
||||
'typical {typicalValue}, probability {probabilityValue})',
|
||||
values: {
|
||||
causeEntityValue: cause.entityValue,
|
||||
actualValue: formatValue(cause.actual, source.function),
|
||||
typicalValue: formatValue(cause.typical, source.function),
|
||||
probabilityValue: cause.probability,
|
||||
}
|
||||
});
|
||||
items.push({ title, description });
|
||||
});
|
||||
}
|
||||
|
@ -190,7 +227,9 @@ export class AnomalyDetails extends Component {
|
|||
if (this.props.examples !== undefined && this.props.examples.length > 0) {
|
||||
this.tabs = [{
|
||||
id: 'Details',
|
||||
name: 'Details',
|
||||
name: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.detailsTitle', {
|
||||
defaultMessage: 'Details',
|
||||
}),
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className="ml-anomalies-table-details">
|
||||
|
@ -204,7 +243,9 @@ export class AnomalyDetails extends Component {
|
|||
},
|
||||
{
|
||||
id: 'Category examples',
|
||||
name: 'Category examples',
|
||||
name: i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.categoryExamplesTitle', {
|
||||
defaultMessage: 'Category examples',
|
||||
}),
|
||||
content: (
|
||||
<Fragment>
|
||||
{this.renderCategoryExamples()}
|
||||
|
@ -289,28 +330,58 @@ export class AnomalyDetails extends Component {
|
|||
const anomaly = this.props.anomaly;
|
||||
const source = anomaly.source;
|
||||
|
||||
let anomalyDescription = `${getSeverity(anomaly.severity)} anomaly in ${anomaly.detector}`;
|
||||
let anomalyDescription = i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.anomalyInLabel', {
|
||||
defaultMessage: '{anomalySeverity} anomaly in {anomalyDetector}',
|
||||
values: {
|
||||
anomalySeverity: getSeverity(anomaly.severity),
|
||||
anomalyDetector: anomaly.detector,
|
||||
}
|
||||
});
|
||||
if (anomaly.entityName !== undefined) {
|
||||
anomalyDescription += ` found for ${anomaly.entityName} ${anomaly.entityValue}`;
|
||||
anomalyDescription += i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.foundForLabel', {
|
||||
defaultMessage: ' found for {anomalyEntityName} {anomalyEntityValue}',
|
||||
values: {
|
||||
anomalyEntityName: anomaly.entityName,
|
||||
anomalyEntityValue: anomaly.entityValue,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ((source.partition_field_name !== undefined) &&
|
||||
(source.partition_field_name !== anomaly.entityName)) {
|
||||
anomalyDescription += ` detected in ${source.partition_field_name}`;
|
||||
anomalyDescription += ` ${source.partition_field_value}`;
|
||||
anomalyDescription += i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.detectedInLabel', {
|
||||
defaultMessage: ' detected in {sourcePartitionFieldName} {sourcePartitionFieldValue}',
|
||||
values: {
|
||||
sourcePartitionFieldName: source.partition_field_name,
|
||||
sourcePartitionFieldValue: source.partition_field_value,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check for a correlatedByFieldValue in the source which will be present for multivariate analyses
|
||||
// where the record is anomalous due to relationship with another 'by' field value.
|
||||
let mvDescription = undefined;
|
||||
if (source.correlated_by_field_value !== undefined) {
|
||||
mvDescription = `multivariate correlations found in ${source.by_field_name}; `;
|
||||
mvDescription += `${source.by_field_value} is considered anomalous given ${source.correlated_by_field_value}`;
|
||||
mvDescription = i18n.translate('xpack.ml.anomaliesTable.anomalyDetails.multivariateDescription', {
|
||||
defaultMessage: 'multivariate correlations found in {sourceByFieldName}; ' +
|
||||
'{sourceByFieldValue} is considered anomalous given {sourceCorrelatedByFieldValue}',
|
||||
values: {
|
||||
sourceByFieldName: source.by_field_name,
|
||||
sourceByFieldValue: source.by_field_value,
|
||||
sourceCorrelatedByFieldValue: source.correlated_by_field_value,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<EuiText size="xs">
|
||||
<h4>Description</h4>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.anomalyDetails.descriptionTitle"
|
||||
defaultMessage="Description"
|
||||
/>
|
||||
</h4>
|
||||
{anomalyDescription}
|
||||
</EuiText>
|
||||
{(mvDescription !== undefined) &&
|
||||
|
@ -329,13 +400,29 @@ export class AnomalyDetails extends Component {
|
|||
<React.Fragment>
|
||||
<EuiText size="xs">
|
||||
{this.props.isAggregatedData === true ? (
|
||||
<h4>Details on highest severity anomaly</h4>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.anomalyDetails.detailsOnHighestSeverityAnomalyTitle"
|
||||
defaultMessage="Details on highest severity anomaly"
|
||||
/>
|
||||
</h4>
|
||||
) : (
|
||||
<h4>Anomaly details</h4>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.anomalyDetails.anomalyDetailsTitle"
|
||||
defaultMessage="Anomaly details"
|
||||
/>
|
||||
</h4>
|
||||
)}
|
||||
{isInterimResult === true &&
|
||||
<React.Fragment>
|
||||
<EuiIcon type="alert"/><span className="interim-result">Interim result</span>
|
||||
<EuiIcon type="alert"/>
|
||||
<span className="interim-result">
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.anomalyDetails.interimResultLabel"
|
||||
defaultMessage="Interim result"
|
||||
/>
|
||||
</span>
|
||||
</React.Fragment>
|
||||
}
|
||||
</EuiText>
|
||||
|
@ -379,7 +466,12 @@ export class AnomalyDetails extends Component {
|
|||
<React.Fragment>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="xs">
|
||||
<h4>Influencers</h4>
|
||||
<h4>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.anomalyDetails.influencersTitle"
|
||||
defaultMessage="Influencers"
|
||||
/>
|
||||
</h4>
|
||||
</EuiText>
|
||||
<EuiDescriptionList
|
||||
type="column"
|
||||
|
@ -390,14 +482,21 @@ export class AnomalyDetails extends Component {
|
|||
<EuiLink
|
||||
onClick={() => this.toggleAllInfluencers()}
|
||||
>
|
||||
and {othersCount} more
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.anomalyDetails.anomalyDescriptionListMoreLinkText"
|
||||
defaultMessage="and {othersCount} more"
|
||||
values={{ othersCount }}
|
||||
/>
|
||||
</EuiLink>
|
||||
}
|
||||
{numToDisplay > (this.props.influencersLimit + 1) &&
|
||||
<EuiLink
|
||||
onClick={() => this.toggleAllInfluencers()}
|
||||
>
|
||||
show less
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.anomalyDetails.anomalyDescriptionShowLessLinkText"
|
||||
defaultMessage="show less"
|
||||
/>
|
||||
</EuiLink>
|
||||
}
|
||||
</React.Fragment>
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
EuiIcon,
|
||||
EuiToolTip
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
/*
|
||||
* Component for rendering a detector cell in the anomalies table, displaying the
|
||||
|
@ -21,7 +22,12 @@ export function DetectorCell({ detectorDescription, numberOfRules }) {
|
|||
let rulesIcon;
|
||||
if (numberOfRules !== undefined && numberOfRules > 0) {
|
||||
rulesIcon = (
|
||||
<EuiToolTip content="rules have been configured for this detector">
|
||||
<EuiToolTip
|
||||
content={<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.detectorCell.rulesConfiguredTooltip"
|
||||
defaultMessage="rules have been configured for this detector"
|
||||
/>}
|
||||
>
|
||||
<EuiIcon
|
||||
type="controlsHorizontal"
|
||||
className="detector-rules-icon"
|
||||
|
|
|
@ -12,44 +12,61 @@ import {
|
|||
EuiButtonIcon,
|
||||
EuiToolTip
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
|
||||
/*
|
||||
* Component for rendering an entity cell in the anomalies table, displaying the value
|
||||
* of the 'partition', 'by' or 'over' field, and optionally links for adding or removing
|
||||
* a filter on this entity.
|
||||
*/
|
||||
export function EntityCell({ entityName, entityValue, filter }) {
|
||||
export const EntityCell = injectI18n(function EntityCell({ entityName, entityValue, filter, intl }) {
|
||||
const valueText = (entityName !== 'mlcategory') ? entityValue : `mlcategory ${entityValue}`;
|
||||
return (
|
||||
<React.Fragment>
|
||||
{valueText}
|
||||
{filter !== undefined && entityName !== undefined && entityValue !== undefined &&
|
||||
<React.Fragment>
|
||||
<EuiToolTip content="Add filter">
|
||||
<EuiToolTip
|
||||
content={<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.entityCell.addFilterTooltip"
|
||||
defaultMessage="Add filter"
|
||||
/>}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
size="xs"
|
||||
className="filter-button"
|
||||
onClick={() => filter(entityName, entityValue, '+')}
|
||||
iconType="plusInCircle"
|
||||
aria-label="Add filter"
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.entityCell.addFilterAriaLabel',
|
||||
defaultMessage: 'Add filter'
|
||||
})}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
<EuiToolTip content="Remove filter">
|
||||
<EuiToolTip
|
||||
content={<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.entityCell.removeFilterTooltip"
|
||||
defaultMessage="Remove filter"
|
||||
/>}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
size="xs"
|
||||
className="filter-button"
|
||||
onClick={() => filter(entityName, entityValue, '-')}
|
||||
iconType="minusInCircle"
|
||||
aria-label="Remove filter"
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.entityCell.removeFilterAriaLabel',
|
||||
defaultMessage: 'Remove filter'
|
||||
})}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</React.Fragment>
|
||||
}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
EntityCell.propTypes = {
|
||||
EntityCell.WrappedComponent.propTypes = {
|
||||
entityName: PropTypes.string,
|
||||
entityValue: PropTypes.any,
|
||||
filter: PropTypes.func
|
||||
|
|
|
@ -10,6 +10,8 @@ import PropTypes from 'prop-types';
|
|||
import React, { Component } from 'react';
|
||||
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
|
||||
/*
|
||||
* Component for rendering a list of record influencers inside a cell in the anomalies table.
|
||||
|
@ -60,7 +62,13 @@ export class InfluencersCell extends Component {
|
|||
<EuiLink
|
||||
onClick={() => this.toggleAllInfluencers()}
|
||||
>
|
||||
and {othersCount} more
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.influencersCell.moreInfluencersLinkText"
|
||||
defaultMessage="and {othersCount} more"
|
||||
values={{
|
||||
othersCount,
|
||||
}}
|
||||
/>
|
||||
</EuiLink>
|
||||
</div>
|
||||
);
|
||||
|
@ -70,7 +78,10 @@ export class InfluencersCell extends Component {
|
|||
<EuiLink
|
||||
onClick={() => this.toggleAllInfluencers()}
|
||||
>
|
||||
show less
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.influencersCell.showLessInfluencersLinkText"
|
||||
defaultMessage="show less"
|
||||
/>
|
||||
</EuiLink>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
EuiContextMenuItem,
|
||||
EuiPopover
|
||||
} from '@elastic/eui';
|
||||
import { injectI18n, FormattedMessage } from '@kbn/i18n/react';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
|
@ -36,7 +37,16 @@ import { replaceStringTokens } from '../../util/string_utils';
|
|||
/*
|
||||
* Component for rendering the links menu inside a cell in the anomalies table.
|
||||
*/
|
||||
export class LinksMenu extends Component {
|
||||
export const LinksMenu = injectI18n(class LinksMenu extends Component {
|
||||
static propTypes = {
|
||||
anomaly: PropTypes.object.isRequired,
|
||||
showViewSeriesLink: PropTypes.bool,
|
||||
isAggregatedData: PropTypes.bool,
|
||||
interval: PropTypes.string,
|
||||
timefilter: PropTypes.object.isRequired,
|
||||
showRuleEditorFlyout: PropTypes.func
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
|
@ -47,7 +57,7 @@ export class LinksMenu extends Component {
|
|||
}
|
||||
|
||||
openCustomUrl = (customUrl) => {
|
||||
const { anomaly, interval, isAggregatedData } = this.props;
|
||||
const { anomaly, interval, isAggregatedData, intl } = this.props;
|
||||
|
||||
console.log('Anomalies Table - open customUrl for record:', anomaly);
|
||||
|
||||
|
@ -112,8 +122,12 @@ export class LinksMenu extends Component {
|
|||
|
||||
}).catch((resp) => {
|
||||
console.log('openCustomUrl(): error loading categoryDefinition:', resp);
|
||||
toastNotifications.addDanger(
|
||||
`Unable to open link as an error occurred loading details on category ID ${categoryId}`);
|
||||
toastNotifications.addDanger(intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.linksMenu.unableToOpenLinkErrorMessage',
|
||||
defaultMessage: 'Unable to open link as an error occurred loading details on category ID {categoryId}'
|
||||
}, {
|
||||
categoryId,
|
||||
}));
|
||||
});
|
||||
|
||||
} else {
|
||||
|
@ -126,6 +140,7 @@ export class LinksMenu extends Component {
|
|||
};
|
||||
|
||||
viewSeries = () => {
|
||||
const { intl } = this.props;
|
||||
const record = this.props.anomaly.source;
|
||||
const bounds = this.props.timefilter.getActiveBounds();
|
||||
const from = bounds.min.toISOString(); // e.g. 2016-02-08T16:00:00.000Z
|
||||
|
@ -160,7 +175,10 @@ export class LinksMenu extends Component {
|
|||
jobIds: [record.job_id]
|
||||
},
|
||||
refreshInterval: {
|
||||
display: 'Off',
|
||||
display: intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.linksMenu.offLabel',
|
||||
defaultMessage: 'Off'
|
||||
}),
|
||||
pause: false,
|
||||
value: 0
|
||||
},
|
||||
|
@ -196,6 +214,7 @@ export class LinksMenu extends Component {
|
|||
}
|
||||
|
||||
viewExamples = () => {
|
||||
const { intl } = this.props;
|
||||
const categoryId = this.props.anomaly.entityValue;
|
||||
const record = this.props.anomaly.source;
|
||||
const indexPatterns = getIndexPatterns();
|
||||
|
@ -203,8 +222,12 @@ export class LinksMenu extends Component {
|
|||
const job = mlJobService.getJob(this.props.anomaly.jobId);
|
||||
if (job === undefined) {
|
||||
console.log(`viewExamples(): no job found with ID: ${this.props.anomaly.jobId}`);
|
||||
toastNotifications.addDanger(
|
||||
`Unable to view examples as no details could be found for job ID ${this.props.anomaly.jobId}`);
|
||||
toastNotifications.addDanger(intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.linksMenu.unableToViewExamplesErrorMessage',
|
||||
defaultMessage: 'Unable to view examples as no details could be found for job ID {jobId}'
|
||||
}, {
|
||||
jobId: this.props.anomaly.jobId,
|
||||
}));
|
||||
return;
|
||||
}
|
||||
const categorizationFieldName = job.analysis_config.categorization_field_name;
|
||||
|
@ -274,7 +297,10 @@ export class LinksMenu extends Component {
|
|||
// Use rison to build the URL .
|
||||
const _g = rison.encode({
|
||||
refreshInterval: {
|
||||
display: 'Off',
|
||||
display: intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.linksMenu.offLabel',
|
||||
defaultMessage: 'Off'
|
||||
}),
|
||||
pause: false,
|
||||
value: 0
|
||||
},
|
||||
|
@ -308,8 +334,12 @@ export class LinksMenu extends Component {
|
|||
|
||||
}).catch((resp) => {
|
||||
console.log('viewExamples(): error loading categoryDefinition:', resp);
|
||||
toastNotifications.addDanger(
|
||||
`Unable to view examples as an error occurred loading details on category ID ${categoryId}`);
|
||||
toastNotifications.addDanger(intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.linksMenu.loadingDetailsErrorMessage',
|
||||
defaultMessage: 'Unable to view examples as an error occurred loading details on category ID {categoryId}'
|
||||
}, {
|
||||
categoryId,
|
||||
}));
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -317,9 +347,14 @@ export class LinksMenu extends Component {
|
|||
function error() {
|
||||
console.log(`viewExamples(): error finding type of field ${categorizationFieldName} in indices:`,
|
||||
datafeedIndices);
|
||||
toastNotifications.addDanger(
|
||||
`Unable to view examples of documents with mlcategory ${categoryId} ` +
|
||||
`as no mapping could be found for the categorization field ${categorizationFieldName}`);
|
||||
toastNotifications.addDanger(intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.linksMenu.noMappingCouldBeFoundErrorMessage',
|
||||
defaultMessage: 'Unable to view examples of documents with mlcategory {categoryId} ' +
|
||||
'as no mapping could be found for the categorization field {categorizationFieldName}'
|
||||
}, {
|
||||
categoryId,
|
||||
categorizationFieldName,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -336,7 +371,7 @@ export class LinksMenu extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { anomaly, showViewSeriesLink } = this.props;
|
||||
const { anomaly, showViewSeriesLink, intl } = this.props;
|
||||
const canConfigureRules = (isRuleSupported(anomaly.source) && checkPermission('canUpdateJob'));
|
||||
|
||||
const button = (
|
||||
|
@ -345,7 +380,10 @@ export class LinksMenu extends Component {
|
|||
color="text"
|
||||
onClick={this.onButtonClick}
|
||||
iconType="gear"
|
||||
aria-label="Select action"
|
||||
aria-label={intl.formatMessage({
|
||||
id: 'xpack.ml.anomaliesTable.linksMenu.selectActionAriaLabel',
|
||||
defaultMessage: 'Select action',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -371,7 +409,10 @@ export class LinksMenu extends Component {
|
|||
icon="popout"
|
||||
onClick={() => { this.closePopover(); this.viewSeries(); }}
|
||||
>
|
||||
View series
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.linksMenu.viewSeriesLabel"
|
||||
defaultMessage="View series"
|
||||
/>
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
}
|
||||
|
@ -383,7 +424,10 @@ export class LinksMenu extends Component {
|
|||
icon="popout"
|
||||
onClick={() => { this.closePopover(); this.viewExamples(); }}
|
||||
>
|
||||
View examples
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.linksMenu.viewExamplesLabel"
|
||||
defaultMessage="View examples"
|
||||
/>
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
}
|
||||
|
@ -395,7 +439,10 @@ export class LinksMenu extends Component {
|
|||
icon="controlsHorizontal"
|
||||
onClick={() => { this.closePopover(); this.props.showRuleEditorFlyout(anomaly); }}
|
||||
>
|
||||
Configure rules
|
||||
<FormattedMessage
|
||||
id="xpack.ml.anomaliesTable.linksMenu.configureRulesLabel"
|
||||
defaultMessage="Configure rules"
|
||||
/>
|
||||
</EuiContextMenuItem>
|
||||
);
|
||||
}
|
||||
|
@ -415,13 +462,4 @@ export class LinksMenu extends Component {
|
|||
</EuiPopover>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LinksMenu.propTypes = {
|
||||
anomaly: PropTypes.object.isRequired,
|
||||
showViewSeriesLink: PropTypes.bool,
|
||||
isAggregatedData: PropTypes.bool,
|
||||
interval: PropTypes.string,
|
||||
timefilter: PropTypes.object.isRequired,
|
||||
showRuleEditorFlyout: PropTypes.func
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue