mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
- The tooltip styling, code and features have been updated to be more in line with Elastic Charts tooltips. This prepares us should we want to use Elastic Charts' tooltip as is at some point. - jQuery is no longer used in mlChartTooltipService. - The tooltip content is no longer raw HTML, a mostly Elastic Charts compatible data structure is now passed around by mlChartTooltipService.
This commit is contained in:
parent
90788a90b3
commit
809a0efb93
20 changed files with 505 additions and 377 deletions
|
@ -1,31 +1,54 @@
|
|||
@import '@elastic/eui/src/components/tool_tip/variables';
|
||||
@import '@elastic/eui/src/components/tool_tip/mixins';
|
||||
|
||||
.mlChartTooltip {
|
||||
@include euiToolTipStyle('s');
|
||||
@include euiFontSizeXS;
|
||||
position: absolute;
|
||||
border: 2px solid $euiColorDarkestShade;
|
||||
border-radius: 5px;
|
||||
padding: 7px 5px;
|
||||
color: $euiColorEmptyShade;
|
||||
background-color: $euiColorDarkestShade;
|
||||
font-family: $euiFontFamily;
|
||||
font-size: $euiFontSizeXS;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
white-space: nowrap;
|
||||
z-index: 1;
|
||||
transition: opacity 0.2s;
|
||||
z-index: 20;
|
||||
max-width: 500px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0;
|
||||
transition: opacity $euiAnimSpeedNormal;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
max-width: 512px;
|
||||
|
||||
hr {
|
||||
margin-top: $euiSizeXS;
|
||||
margin-bottom: $euiSizeXS;
|
||||
border: none;
|
||||
height: 1px;
|
||||
background-color: $euiColorMediumShade;
|
||||
&__list {
|
||||
margin: $euiSizeXS;
|
||||
}
|
||||
|
||||
&__header {
|
||||
@include euiToolTipTitle;
|
||||
padding: $euiSizeXS ($euiSizeXS * 2);
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: flex;
|
||||
padding: 3px;
|
||||
box-sizing: border-box;
|
||||
border-left: $euiSizeXS solid transparent;
|
||||
}
|
||||
|
||||
&__label {
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
min-width: 1px;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
&__value {
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
font-weight: $euiFontWeightBold;
|
||||
text-align: right;
|
||||
font-feature-settings: 'tnum';
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
&__rowHighlighted {
|
||||
background-color: transparentize($euiColorGhost, 0.9);
|
||||
}
|
||||
|
||||
&--hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mlChartTooltip--noTransition {
|
||||
transition: opacity 0s;
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<div class="mlChartTooltip euiText"></div>
|
|
@ -1,24 +0,0 @@
|
|||
/*
|
||||
* 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 template from './chart_tooltip.html';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml');
|
||||
|
||||
import { mlChartTooltipService } from './chart_tooltip_service';
|
||||
|
||||
module.directive('mlChartTooltip', function () {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
template,
|
||||
link: function (scope, element) {
|
||||
mlChartTooltipService.element = element;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 classNames from 'classnames';
|
||||
import React, { useRef, FC } from 'react';
|
||||
import { TooltipValueFormatter } from '@elastic/charts';
|
||||
|
||||
// TODO: Below import is temporary, use `react-use` lib instead.
|
||||
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
|
||||
import { useObservable } from '../../../../../../../src/plugins/kibana_react/public/util/use_observable';
|
||||
|
||||
import { chartTooltip$, mlChartTooltipService, ChartTooltipValue } from './chart_tooltip_service';
|
||||
|
||||
const renderHeader = (headerData?: ChartTooltipValue, formatter?: TooltipValueFormatter) => {
|
||||
if (!headerData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return formatter ? formatter(headerData) : headerData.name;
|
||||
};
|
||||
|
||||
export const ChartTooltip: FC = () => {
|
||||
const chartTooltipElement = useRef(null);
|
||||
|
||||
mlChartTooltipService.element = chartTooltipElement.current;
|
||||
|
||||
const chartTooltipState = useObservable(chartTooltip$);
|
||||
|
||||
if (chartTooltipState === undefined || !chartTooltipState.isTooltipVisible) {
|
||||
return <div className="mlChartTooltip mlChartTooltip--hidden" ref={chartTooltipElement} />;
|
||||
}
|
||||
|
||||
const { tooltipData, tooltipHeaderFormatter, tooltipPosition } = chartTooltipState;
|
||||
|
||||
return (
|
||||
<div
|
||||
className="mlChartTooltip"
|
||||
style={{ transform: tooltipPosition.transform }}
|
||||
ref={chartTooltipElement}
|
||||
>
|
||||
{tooltipData.length > 0 && tooltipData[0].skipHeader === undefined && (
|
||||
<div className="mlChartTooltip__header">
|
||||
{renderHeader(tooltipData[0], tooltipHeaderFormatter)}
|
||||
</div>
|
||||
)}
|
||||
{tooltipData.length > 1 && (
|
||||
<div className="mlChartTooltip__list">
|
||||
{tooltipData
|
||||
.slice(1)
|
||||
.map(({ name, value, color, isHighlighted, seriesKey, yAccessor }) => {
|
||||
const classes = classNames('mlChartTooltip__item', {
|
||||
/* eslint @typescript-eslint/camelcase:0 */
|
||||
echTooltip__rowHighlighted: isHighlighted,
|
||||
});
|
||||
return (
|
||||
<div
|
||||
key={`${seriesKey}--${yAccessor}`}
|
||||
className={classes}
|
||||
style={{
|
||||
borderLeftColor: color,
|
||||
}}
|
||||
>
|
||||
<span className="mlChartTooltip__label">{name}</span>
|
||||
<span className="mlChartTooltip__value">{value}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
39
x-pack/legacy/plugins/ml/public/components/chart_tooltip/chart_tooltip_service.d.ts
vendored
Normal file
39
x-pack/legacy/plugins/ml/public/components/chart_tooltip/chart_tooltip_service.d.ts
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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 { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { TooltipValue, TooltipValueFormatter } from '@elastic/charts';
|
||||
|
||||
export interface ChartTooltipValue extends TooltipValue {
|
||||
skipHeader?: boolean;
|
||||
}
|
||||
|
||||
interface ChartTooltipState {
|
||||
isTooltipVisible: boolean;
|
||||
tooltipData: ChartTooltipValue[];
|
||||
tooltipHeaderFormatter?: TooltipValueFormatter;
|
||||
tooltipPosition: { transform: string };
|
||||
}
|
||||
|
||||
export declare const chartTooltip$: BehaviorSubject<ChartTooltipState>;
|
||||
|
||||
interface ToolTipOffset {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
interface MlChartTooltipService {
|
||||
element: HTMLElement | null;
|
||||
show: (
|
||||
tooltipData: ChartTooltipValue[],
|
||||
target: HTMLElement | null,
|
||||
offset: ToolTipOffset
|
||||
) => void;
|
||||
hide: () => void;
|
||||
}
|
||||
|
||||
export declare const mlChartTooltipService: MlChartTooltipService;
|
|
@ -4,86 +4,76 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import $ from 'jquery';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
const doc = document.documentElement;
|
||||
const FADE_TIMEOUT_MS = 200;
|
||||
|
||||
const chartTooltipDefaultState = {
|
||||
isTooltipVisible: false,
|
||||
tooltipData: [],
|
||||
tooltipPosition: { transform: 'translate(0px, 0px)' }
|
||||
};
|
||||
|
||||
export const chartTooltip$ = new BehaviorSubject(chartTooltipDefaultState);
|
||||
|
||||
export const mlChartTooltipService = {
|
||||
element: null,
|
||||
fadeTimeout: null,
|
||||
visible: false
|
||||
};
|
||||
|
||||
mlChartTooltipService.show = function (contents, target, offset = { x: 0, y: 0 }) {
|
||||
mlChartTooltipService.show = function (tooltipData, target, offset = { x: 0, y: 0 }) {
|
||||
if (this.element === null || typeof target === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.visible = true;
|
||||
// if a previous fade out was happening, stop it
|
||||
if (this.fadeTimeout !== null) {
|
||||
clearTimeout(this.fadeTimeout);
|
||||
// side bar width
|
||||
const euiNavDrawer = document.getElementsByClassName('euiNavDrawer');
|
||||
|
||||
if (euiNavDrawer.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// populate the tooltip contents
|
||||
this.element.html(contents);
|
||||
// enable the tooltip to render it in the DOM
|
||||
// so the correct `tooltipWidth` gets returned.
|
||||
const tooltipState = {
|
||||
...chartTooltipDefaultState,
|
||||
isTooltipVisible: true,
|
||||
tooltipData,
|
||||
};
|
||||
chartTooltip$.next(tooltipState);
|
||||
|
||||
// side bar width
|
||||
const navOffset = $('.euiNavDrawer').width() || 0; // Offset by width of side navbar
|
||||
const contentWidth = $('body').width() - navOffset;
|
||||
const tooltipWidth = this.element.width();
|
||||
const navOffset = euiNavDrawer[0].clientWidth; // Offset by width of side navbar
|
||||
const contentWidth = document.body.clientWidth - navOffset;
|
||||
const tooltipWidth = this.element.clientWidth;
|
||||
|
||||
const pos = target.getBoundingClientRect();
|
||||
let left = (pos.left + (offset.x) + 4) - navOffset;
|
||||
let left = pos.left + offset.x + 4 - navOffset;
|
||||
if (left + tooltipWidth > contentWidth) {
|
||||
// the tooltip is hanging off the side of the page,
|
||||
// so move it to the other side of the target
|
||||
const markerWidthAdjustment = 22;
|
||||
const markerWidthAdjustment = 10;
|
||||
left = left - (tooltipWidth + offset.x + markerWidthAdjustment);
|
||||
}
|
||||
|
||||
// Calculate top offset
|
||||
const scrollTop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
|
||||
const topNavHeightAdjustment = 75;
|
||||
const top = pos.top + (offset.y) + scrollTop - topNavHeightAdjustment;
|
||||
const topNavHeightAdjustment = 190;
|
||||
const top = pos.top + offset.y + scrollTop - topNavHeightAdjustment;
|
||||
|
||||
this.element.css({
|
||||
left,
|
||||
top,
|
||||
opacity: '0.9',
|
||||
display: 'block'
|
||||
// render the tooltip with adjusted position.
|
||||
chartTooltip$.next({
|
||||
...tooltipState,
|
||||
tooltipPosition: { transform: `translate(${left}px, ${top}px)` }
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// When selecting multiple cells using dargSelect, we need to quickly
|
||||
// When selecting multiple cells using dragSelect, we need to quickly
|
||||
// hide the tooltip with `noTransition`, otherwise, if the mouse pointer
|
||||
// enters the tooltip while dragging, it will cancel selecting multiple
|
||||
// swimlane cells which we'd like to avoid of course.
|
||||
mlChartTooltipService.hide = function (noTransition = false) {
|
||||
if (this.element === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.visible = false;
|
||||
|
||||
if (noTransition) {
|
||||
this.element.addClass('mlChartTooltip--noTransition');
|
||||
this.element.css({ opacity: '0', display: 'none' });
|
||||
this.element.removeClass('mlChartTooltip--noTransition');
|
||||
return;
|
||||
}
|
||||
|
||||
this.element.css({ opacity: '0' });
|
||||
|
||||
// after the fade out transition has finished, set the display to
|
||||
// none so it doesn't block any mouse events underneath it.
|
||||
this.fadeTimeout = setTimeout(() => {
|
||||
if (this.visible === false) {
|
||||
this.element.css('display', 'none');
|
||||
}
|
||||
this.fadeTimeout = null;
|
||||
}, FADE_TIMEOUT_MS);
|
||||
mlChartTooltipService.hide = function () {
|
||||
chartTooltip$.next({
|
||||
...chartTooltipDefaultState,
|
||||
isTooltipVisible: false
|
||||
});
|
||||
};
|
||||
|
|
|
@ -4,6 +4,5 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import './chart_tooltip';
|
||||
export { mlChartTooltipService } from './chart_tooltip_service';
|
||||
export { ChartTooltip } from './chart_tooltip';
|
|
@ -19,7 +19,6 @@ import {
|
|||
ScaleType,
|
||||
Settings,
|
||||
TooltipValueFormatter,
|
||||
TooltipValue,
|
||||
} from '@elastic/charts';
|
||||
|
||||
import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
|
@ -28,6 +27,7 @@ import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
|
|||
import { MetricDistributionChartTooltipHeader } from './metric_distribution_chart_tooltip_header';
|
||||
import { useUiChromeContext } from '../../../../../contexts/ui/use_ui_chrome_context';
|
||||
import { kibanaFieldFormat } from '../../../../../formatters/kibana_field_format';
|
||||
import { ChartTooltipValue } from '../../../../../components/chart_tooltip/chart_tooltip_service';
|
||||
|
||||
export interface MetricDistributionChartData {
|
||||
x: number;
|
||||
|
@ -60,7 +60,7 @@ export const MetricDistributionChart: FC<Props> = ({ width, height, chartData, f
|
|||
const themeName = IS_DARK_THEME ? darkTheme : lightTheme;
|
||||
const AREA_SERIES_COLOR = themeName.euiColorVis1;
|
||||
|
||||
const headerFormatter: TooltipValueFormatter = (tooltipData: TooltipValue) => {
|
||||
const headerFormatter: TooltipValueFormatter = (tooltipData: ChartTooltipValue) => {
|
||||
const xValue = tooltipData.value;
|
||||
const chartPoint: MetricDistributionChartData | undefined = chartData.find(
|
||||
data => data.x === xValue
|
||||
|
|
|
@ -33,6 +33,7 @@ import {
|
|||
ExplorerNoJobsFound,
|
||||
ExplorerNoResultsFound,
|
||||
} from './components';
|
||||
import { ChartTooltip } from '../components/chart_tooltip';
|
||||
import { ExplorerSwimlane } from './explorer_swimlane';
|
||||
import { KqlFilterBar } from '../components/kql_filter_bar';
|
||||
import { formatHumanReadableDateTime } from '../util/date_utils';
|
||||
|
@ -1145,6 +1146,7 @@ export const Explorer = injectI18n(injectObservablesAsProps(
|
|||
|
||||
return (
|
||||
<ExplorerPage jobSelectorProps={jobSelectorProps}>
|
||||
<ChartTooltip />
|
||||
<div className="results-container">
|
||||
|
||||
{noInfluencersConfigured === false &&
|
||||
|
|
|
@ -21,7 +21,7 @@ import moment from 'moment';
|
|||
// because it won't work with the jest tests
|
||||
import { formatHumanReadableDateTime } from '../../util/date_utils';
|
||||
import { formatValue } from '../../formatters/format_value';
|
||||
import { getSeverityWithLow } from '../../../common/util/anomaly_utils';
|
||||
import { getSeverityColor, getSeverityWithLow } from '../../../common/util/anomaly_utils';
|
||||
import {
|
||||
getChartType,
|
||||
getTickValues,
|
||||
|
@ -30,7 +30,6 @@ import {
|
|||
} from '../../util/chart_utils';
|
||||
import { LoadingIndicator } from '../../components/loading_indicator/loading_indicator';
|
||||
import { TimeBuckets } from '../../util/time_buckets';
|
||||
import { mlEscape } from '../../util/string_utils';
|
||||
import { mlFieldFormatService } from '../../services/field_format_service';
|
||||
import { mlChartTooltipService } from '../../components/chart_tooltip/chart_tooltip_service';
|
||||
import { severity$ } from '../../components/controls/select_severity/select_severity';
|
||||
|
@ -407,72 +406,98 @@ export const ExplorerChartDistribution = injectI18n(class ExplorerChartDistribut
|
|||
// Show the time and metric values in the tooltip.
|
||||
// Uses date, value, upper, lower and anomalyScore (optional) marker properties.
|
||||
const formattedDate = formatHumanReadableDateTime(marker.date);
|
||||
let contents = `${formattedDate}<br/><hr/>`;
|
||||
const tooltipData = [{ name: formattedDate }];
|
||||
const seriesKey = config.detectorLabel;
|
||||
|
||||
if (_.has(marker, 'entity')) {
|
||||
contents += `<div>${marker.entity}</div>`;
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.entityLabel',
|
||||
defaultMessage: 'entity'
|
||||
}),
|
||||
value: marker.entity,
|
||||
seriesKey
|
||||
});
|
||||
}
|
||||
|
||||
if (_.has(marker, 'anomalyScore')) {
|
||||
const score = parseInt(marker.anomalyScore);
|
||||
const displayScore = (score > 0 ? score : '< 1');
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.anomalyScoreLabel',
|
||||
defaultMessage: 'anomaly score: {displayScore}'
|
||||
}, { displayScore });
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.anomalyScoreLabel',
|
||||
defaultMessage: 'anomaly score'
|
||||
}),
|
||||
value: displayScore,
|
||||
color: getSeverityColor(score),
|
||||
seriesKey,
|
||||
yAccessor: 'anomaly_score'
|
||||
});
|
||||
if (chartType !== CHART_TYPE.EVENT_DISTRIBUTION) {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.valueLabel',
|
||||
defaultMessage: '{br}value: {value}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
value: formatValue(marker.value, config.functionDescription, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.valueLabel',
|
||||
defaultMessage: 'value'
|
||||
}),
|
||||
value: formatValue(marker.value, config.functionDescription, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'value'
|
||||
});
|
||||
if (typeof marker.numberOfCauses === 'undefined' || marker.numberOfCauses === 1) {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.typicalLabel',
|
||||
defaultMessage: '{br}typical: {typicalValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
typicalValue: formatValue(marker.typical, config.functionDescription, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.typicalLabel',
|
||||
defaultMessage: 'typical'
|
||||
}),
|
||||
value: formatValue(marker.typical, config.functionDescription, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'typical'
|
||||
});
|
||||
}
|
||||
if (typeof marker.byFieldName !== 'undefined' && _.has(marker, 'numberOfCauses')) {
|
||||
const numberOfCauses = marker.numberOfCauses;
|
||||
const byFieldName = mlEscape(marker.byFieldName);
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel',
|
||||
defaultMessage:
|
||||
'{br} { numberOfCauses, plural, one {# unusual {byFieldName} value} other {#{plusSign} unusual {byFieldName} values}}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
numberOfCauses,
|
||||
byFieldName,
|
||||
// Maximum of 10 causes are stored in the record, so '10' may mean more than 10.
|
||||
plusSign: numberOfCauses < 10 ? '' : '+',
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel',
|
||||
defaultMessage:
|
||||
'{ numberOfCauses, plural, one {# unusual {byFieldName} value} other {#{plusSign} unusual {byFieldName} values}}'
|
||||
}, {
|
||||
numberOfCauses: marker.numberOfCauses,
|
||||
byFieldName: marker.byFieldName,
|
||||
// Maximum of 10 causes are stored in the record, so '10' may mean more than 10.
|
||||
plusSign: marker.numberOfCauses < 10 ? '' : '+',
|
||||
}),
|
||||
seriesKey,
|
||||
yAccessor: 'numberOfCauses'
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (chartType !== CHART_TYPE.EVENT_DISTRIBUTION) {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel',
|
||||
defaultMessage: 'value: {value}'
|
||||
}, {
|
||||
value: formatValue(marker.value, config.functionDescription, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel',
|
||||
defaultMessage: 'value'
|
||||
}),
|
||||
value: formatValue(marker.value, config.functionDescription, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'value'
|
||||
});
|
||||
}
|
||||
|
||||
if (_.has(marker, 'scheduledEvents')) {
|
||||
contents += '<div><hr/>' + intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.scheduledEventsLabel',
|
||||
defaultMessage: 'Scheduled events:{br}{scheduledEventsValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
scheduledEventsValue: marker.scheduledEvents.map(mlEscape).join('<br/>')
|
||||
}) + '</div>';
|
||||
marker.scheduledEvents.forEach((scheduledEvent, i) => {
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.scheduledEventsLabel',
|
||||
defaultMessage: 'scheduled event{counter}'
|
||||
}, { counter: marker.scheduledEvents.length > 1 ? ` #${i + 1}` : '' }),
|
||||
value: scheduledEvent,
|
||||
seriesKey,
|
||||
yAccessor: `scheduled_events_${i + 1}`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
mlChartTooltipService.show(contents, circle, {
|
||||
mlChartTooltipService.show(tooltipData, circle, {
|
||||
x: LINE_CHART_ANOMALY_RADIUS * 2,
|
||||
y: 0
|
||||
});
|
||||
|
|
|
@ -22,6 +22,7 @@ import moment from 'moment';
|
|||
import { formatHumanReadableDateTime } from '../../util/date_utils';
|
||||
import { formatValue } from '../../formatters/format_value';
|
||||
import {
|
||||
getSeverityColor,
|
||||
getSeverityWithLow,
|
||||
getMultiBucketImpactLabel,
|
||||
} from '../../../common/util/anomaly_utils';
|
||||
|
@ -340,23 +341,32 @@ export const ExplorerChartSingleMetric = injectI18n(class ExplorerChartSingleMet
|
|||
// Show the time and metric values in the tooltip.
|
||||
// Uses date, value, upper, lower and anomalyScore (optional) marker properties.
|
||||
const formattedDate = formatHumanReadableDateTime(marker.date);
|
||||
let contents = formattedDate + '<br/><hr/>';
|
||||
const tooltipData = [{ name: formattedDate }];
|
||||
const seriesKey = config.detectorLabel;
|
||||
|
||||
if (_.has(marker, 'anomalyScore')) {
|
||||
const score = parseInt(marker.anomalyScore);
|
||||
const displayScore = (score > 0 ? score : '< 1');
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.anomalyScoreLabel',
|
||||
defaultMessage: 'anomaly score: {displayScore}'
|
||||
}, { displayScore });
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.anomalyScoreLabel',
|
||||
defaultMessage: 'anomaly score'
|
||||
}),
|
||||
value: displayScore,
|
||||
color: getSeverityColor(score),
|
||||
seriesKey,
|
||||
yAccessor: 'anomaly_score'
|
||||
});
|
||||
|
||||
if (showMultiBucketAnomalyTooltip(marker) === true) {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.multiBucketImpactLabel',
|
||||
defaultMessage: '{br}multi-bucket impact: {multiBucketImpactLabel}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
multiBucketImpactLabel: getMultiBucketImpactLabel(marker.multiBucketImpact)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.multiBucketImpactLabel',
|
||||
defaultMessage: 'multi-bucket impact'
|
||||
}),
|
||||
value: getMultiBucketImpactLabel(marker.multiBucketImpact),
|
||||
seriesKey,
|
||||
yAccessor: 'multi_bucket_impact'
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -366,64 +376,74 @@ export const ExplorerChartSingleMetric = injectI18n(class ExplorerChartSingleMet
|
|||
if (_.has(marker, 'actual') && config.functionDescription !== 'rare') {
|
||||
// Display the record actual in preference to the chart value, which may be
|
||||
// different depending on the aggregation interval of the chart.
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.actualLabel',
|
||||
defaultMessage: '{br}actual: {actualValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
actualValue: formatValue(marker.actual, config.functionDescription, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.actualLabel',
|
||||
defaultMessage: 'actual'
|
||||
}),
|
||||
value: formatValue(marker.actual, config.functionDescription, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'actual'
|
||||
});
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.typicalLabel',
|
||||
defaultMessage: '{br}typical: {typicalValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
typicalValue: formatValue(marker.typical, config.functionDescription, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.typicalLabel',
|
||||
defaultMessage: 'typical'
|
||||
}),
|
||||
value: formatValue(marker.typical, config.functionDescription, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'typical'
|
||||
});
|
||||
} else {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.valueLabel',
|
||||
defaultMessage: '{br}value: {value}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
value: formatValue(marker.value, config.functionDescription, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.valueLabel',
|
||||
defaultMessage: 'value'
|
||||
}),
|
||||
value: formatValue(marker.value, config.functionDescription, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'value'
|
||||
});
|
||||
if (_.has(marker, 'byFieldName') && _.has(marker, 'numberOfCauses')) {
|
||||
const numberOfCauses = marker.numberOfCauses;
|
||||
const byFieldName = mlEscape(marker.byFieldName);
|
||||
intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.unusualByFieldValuesLabel',
|
||||
defaultMessage:
|
||||
'{br} { numberOfCauses, plural, one {# unusual {byFieldName} value} other {#{plusSign} unusual {byFieldName} values}}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
numberOfCauses,
|
||||
byFieldName,
|
||||
// Maximum of 10 causes are stored in the record, so '10' may mean more than 10.
|
||||
plusSign: numberOfCauses < 10 ? '' : '+',
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel',
|
||||
defaultMessage:
|
||||
'{ numberOfCauses, plural, one {# unusual {byFieldName} value} other {#{plusSign} unusual {byFieldName} values}}'
|
||||
}, {
|
||||
numberOfCauses: marker.numberOfCauses,
|
||||
byFieldName: marker.byFieldName,
|
||||
// Maximum of 10 causes are stored in the record, so '10' may mean more than 10.
|
||||
plusSign: marker.numberOfCauses < 10 ? '' : '+',
|
||||
}),
|
||||
seriesKey,
|
||||
yAccessor: 'numberOfCauses'
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.valueWithoutAnomalyScoreLabel',
|
||||
defaultMessage: 'value: {value}'
|
||||
}, {
|
||||
value: formatValue(marker.value, config.functionDescription, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.valueWithoutAnomalyScoreLabel',
|
||||
defaultMessage: 'value'
|
||||
}),
|
||||
value: formatValue(marker.value, config.functionDescription, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'value'
|
||||
});
|
||||
}
|
||||
|
||||
if (_.has(marker, 'scheduledEvents')) {
|
||||
contents += '<br/><hr/>' + intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.scheduledEventsLabel',
|
||||
defaultMessage: 'Scheduled events:{br}{scheduledEventsValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
scheduledEventsValue: marker.scheduledEvents.map(mlEscape).join('<br/>')
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.explorer.singleMetricChart.scheduledEventsLabel',
|
||||
defaultMessage: 'Scheduled events'
|
||||
}),
|
||||
value: marker.scheduledEvents.map(mlEscape).join('<br/>')
|
||||
});
|
||||
}
|
||||
|
||||
mlChartTooltipService.show(contents, circle, {
|
||||
mlChartTooltipService.show(tooltipData, circle, {
|
||||
x: LINE_CHART_ANOMALY_RADIUS * 2,
|
||||
y: 0
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ import { subscribeAppStateToObservable } from '../util/app_state_utils';
|
|||
|
||||
import { APP_STATE_ACTION, EXPLORER_ACTION } from './explorer_constants';
|
||||
|
||||
const template = `<ml-chart-tooltip /><ml-explorer-react-wrapper class="ml-explorer" data-test-subj="mlPageAnomalyExplorer" />`;
|
||||
const template = `<ml-explorer-react-wrapper class="ml-explorer" data-test-subj="mlPageAnomalyExplorer" />`;
|
||||
|
||||
uiRoutes
|
||||
.when('/explorer/?', {
|
||||
|
|
|
@ -309,17 +309,23 @@ export const ExplorerSwimlane = injectI18n(class ExplorerSwimlane extends React.
|
|||
|
||||
// Display date using same format as Kibana visualizations.
|
||||
const formattedDate = formatHumanReadableDateTime(time * 1000);
|
||||
let contents = `${formattedDate}<br/><hr/>`;
|
||||
const tooltipData = [{ name: formattedDate }];
|
||||
|
||||
if (swimlaneData.fieldName !== undefined) {
|
||||
contents += `${mlEscape(swimlaneData.fieldName)}: ${mlEscape(laneLabel)}<br/><hr/>`;
|
||||
tooltipData.push({ name: swimlaneData.fieldName, value: laneLabel, seriesKey: laneLabel, yAccessor: 'fieldName' });
|
||||
}
|
||||
contents += intl.formatMessage(
|
||||
{ id: 'xpack.ml.explorer.swimlane.maxAnomalyScoreLabel', defaultMessage: 'Max anomaly score: {displayScore}' },
|
||||
{ displayScore }
|
||||
);
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage(
|
||||
{ id: 'xpack.ml.explorer.swimlane.maxAnomalyScoreLabel', defaultMessage: 'Max anomaly score' },
|
||||
),
|
||||
value: displayScore,
|
||||
color: colorScore(displayScore),
|
||||
seriesKey: laneLabel,
|
||||
yAccessor: 'anomaly_score'
|
||||
});
|
||||
|
||||
const offsets = (target.className === 'sl-cell-inner' ? { x: 0, y: 0 } : { x: 2, y: 1 });
|
||||
mlChartTooltipService.show(contents, target, {
|
||||
mlChartTooltipService.show(tooltipData, target, {
|
||||
x: target.offsetWidth - offsets.x,
|
||||
y: 10 + offsets.y
|
||||
});
|
||||
|
@ -355,9 +361,9 @@ export const ExplorerSwimlane = injectI18n(class ExplorerSwimlane extends React.
|
|||
if (swimlaneData.fieldName !== undefined) {
|
||||
d3.select(this)
|
||||
.on('mouseover', label => {
|
||||
mlChartTooltipService.show(`${mlEscape(swimlaneData.fieldName)}: ${mlEscape(label)}`, this, {
|
||||
mlChartTooltipService.show([{ skipHeader: true }, { name: swimlaneData.fieldName, value: label }], this, {
|
||||
x: laneLabelWidth,
|
||||
y: 20
|
||||
y: 8
|
||||
});
|
||||
})
|
||||
.on('mouseout', () => {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<ml-nav-menu name="new_job_population" />
|
||||
<ml-chart-tooltip></ml-chart-tooltip>
|
||||
<ml-new-job-population>
|
||||
<ml-message-bar></ml-message-bar>
|
||||
<div ng-controller="MlCreatePopulationJob" class="population-job-container">
|
||||
|
|
|
@ -11,15 +11,12 @@
|
|||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import d3 from 'd3';
|
||||
import angular from 'angular';
|
||||
import moment from 'moment';
|
||||
|
||||
import { formatHumanReadableDateTime } from '../../../../../util/date_utils';
|
||||
import { TimeBuckets } from '../../../../../util/time_buckets';
|
||||
import { numTicksForDateFormat } from '../../../../../util/chart_utils';
|
||||
import { mlEscape } from '../../../../../util/string_utils';
|
||||
import { mlChartTooltipService } from '../../../../../components/chart_tooltip/chart_tooltip_service';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
|
@ -230,19 +227,8 @@ module.directive('mlPopulationJobChart', function () {
|
|||
|
||||
function showTooltip(data, el) {
|
||||
scope;
|
||||
let contents = '';
|
||||
const formattedDate = formatHumanReadableDateTime(data.date);
|
||||
contents += `${formattedDate}<br/><hr/>`;
|
||||
contents += `${mlEscape(scope.overFieldName)}: ${mlEscape(data.label)}<br/>`;
|
||||
contents += i18n.translate('xpack.ml.newJob.simple.population.chartTooltipValueLabel', {
|
||||
defaultMessage: 'Value: {dataValue}',
|
||||
values: {
|
||||
dataValue: scope.chartData.fieldFormat !== undefined
|
||||
? scope.chartData.fieldFormat.convert(data.value, 'text')
|
||||
: parseInt(data.value)
|
||||
}
|
||||
});
|
||||
mlChartTooltipService.show(contents, el, {
|
||||
// Deprecated code which will be removed soon, tooltip content not migrated.
|
||||
mlChartTooltipService.show([], el, {
|
||||
x: 5,
|
||||
y: 10
|
||||
});
|
||||
|
|
|
@ -1281,27 +1281,36 @@ const TimeseriesChartIntl = injectI18n(class TimeseriesChart extends React.Compo
|
|||
} = this.props;
|
||||
|
||||
const fieldFormat = this.fieldFormat;
|
||||
const seriesKey = 'single_metric_viewer';
|
||||
|
||||
// Show the time and metric values in the tooltip.
|
||||
// Uses date, value, upper, lower and anomalyScore (optional) marker properties.
|
||||
const formattedDate = formatHumanReadableDateTimeSeconds(marker.date);
|
||||
let contents = formattedDate + '<br/><hr/>';
|
||||
const tooltipData = [{ name: formattedDate }];
|
||||
|
||||
if (_.has(marker, 'anomalyScore')) {
|
||||
const score = parseInt(marker.anomalyScore);
|
||||
const displayScore = (score > 0 ? score : '< 1');
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.anomalyScoreLabel',
|
||||
defaultMessage: 'anomaly score: {displayScore}{br}'
|
||||
}, { displayScore, br: '<br />' });
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.anomalyScoreLabel',
|
||||
defaultMessage: 'anomaly score'
|
||||
}),
|
||||
value: displayScore,
|
||||
color: anomalyColorScale(score),
|
||||
seriesKey,
|
||||
yAccessor: 'anomaly_score'
|
||||
});
|
||||
|
||||
if (showMultiBucketAnomalyTooltip(marker) === true) {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.multiBucketImpactLabel',
|
||||
defaultMessage: 'multi-bucket impact: {multiBucketImpactLabel}{br}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
multiBucketImpactLabel: getMultiBucketImpactLabel(marker.multiBucketImpact)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.multiBucketImpactLabel',
|
||||
defaultMessage: 'multi-bucket impact'
|
||||
}),
|
||||
value: getMultiBucketImpactLabel(marker.multiBucketImpact),
|
||||
seriesKey,
|
||||
yAccessor: 'multi_bucket_impact'
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1312,120 +1321,158 @@ const TimeseriesChartIntl = injectI18n(class TimeseriesChart extends React.Compo
|
|||
if (_.has(marker, 'actual') && marker.function !== 'rare') {
|
||||
// Display the record actual in preference to the chart value, which may be
|
||||
// different depending on the aggregation interval of the chart.
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.actualLabel',
|
||||
defaultMessage: 'actual: {actualValue}'
|
||||
}, {
|
||||
actualValue: formatValue(marker.actual, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.actualLabel',
|
||||
defaultMessage: 'actual'
|
||||
}),
|
||||
value: formatValue(marker.actual, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'actual'
|
||||
});
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.typicalLabel',
|
||||
defaultMessage: '{br}typical: {typicalValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
typicalValue: formatValue(marker.typical, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.typicalLabel',
|
||||
defaultMessage: 'typical'
|
||||
}),
|
||||
value: formatValue(marker.typical, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'typical'
|
||||
});
|
||||
} else {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.valueLabel',
|
||||
defaultMessage: 'value: {value}'
|
||||
}, {
|
||||
value: formatValue(marker.value, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.valueLabel',
|
||||
defaultMessage: 'value'
|
||||
}),
|
||||
value: formatValue(marker.value, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'value'
|
||||
});
|
||||
if (_.has(marker, 'byFieldName') && _.has(marker, 'numberOfCauses')) {
|
||||
const numberOfCauses = marker.numberOfCauses;
|
||||
// If numberOfCauses === 1, won't go into this block as actual/typical copied to top level fields.
|
||||
const byFieldName = mlEscape(marker.byFieldName);
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.moreThanOneUnusualByFieldValuesLabel',
|
||||
defaultMessage: '{br} {numberOfCauses}{plusSign} unusual {byFieldName} values'
|
||||
}, {
|
||||
br: '<br />',
|
||||
numberOfCauses,
|
||||
byFieldName,
|
||||
// Maximum of 10 causes are stored in the record, so '10' may mean more than 10.
|
||||
plusSign: numberOfCauses < 10 ? '' : '+'
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.moreThanOneUnusualByFieldValuesLabel',
|
||||
defaultMessage: '{numberOfCauses}{plusSign} unusual {byFieldName} values'
|
||||
}, {
|
||||
numberOfCauses,
|
||||
byFieldName,
|
||||
// Maximum of 10 causes are stored in the record, so '10' may mean more than 10.
|
||||
plusSign: numberOfCauses < 10 ? '' : '+'
|
||||
}),
|
||||
seriesKey,
|
||||
yAccessor: 'numberOfCauses'
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.valueLabel',
|
||||
defaultMessage: 'value: {value}'
|
||||
}, {
|
||||
value: formatValue(marker.value, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.valueLabel',
|
||||
defaultMessage: 'value'
|
||||
}),
|
||||
value: formatValue(marker.value, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'value'
|
||||
});
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.upperBoundsLabel',
|
||||
defaultMessage: '{br}upper bounds: {upperBoundsValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
upperBoundsValue: formatValue(marker.upper, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.upperBoundsLabel',
|
||||
defaultMessage: 'upper bounds'
|
||||
}),
|
||||
value: formatValue(marker.upper, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'upper_bounds'
|
||||
});
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.lowerBoundsLabel',
|
||||
defaultMessage: '{br}lower bounds: {lowerBoundsValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
lowerBoundsValue: formatValue(marker.lower, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.lowerBoundsLabel',
|
||||
defaultMessage: 'lower bounds'
|
||||
}),
|
||||
value: formatValue(marker.lower, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'lower_bounds'
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// TODO - need better formatting for small decimals.
|
||||
if (_.get(marker, 'isForecast', false) === true) {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.predictionLabel',
|
||||
defaultMessage: 'prediction: {predictionValue}'
|
||||
}, {
|
||||
predictionValue: formatValue(marker.value, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.predictionLabel',
|
||||
defaultMessage: 'prediction'
|
||||
}),
|
||||
value: formatValue(marker.value, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'prediction'
|
||||
});
|
||||
} else {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.valueLabel',
|
||||
defaultMessage: 'value: {value}'
|
||||
}, {
|
||||
value: formatValue(marker.value, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.valueLabel',
|
||||
defaultMessage: 'value'
|
||||
}),
|
||||
value: formatValue(marker.value, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'value'
|
||||
});
|
||||
}
|
||||
|
||||
if (modelPlotEnabled === true) {
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.upperBoundsLabel',
|
||||
defaultMessage: '{br}upper bounds: {upperBoundsValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
upperBoundsValue: formatValue(marker.upper, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.upperBoundsLabel',
|
||||
defaultMessage: 'upper bounds'
|
||||
}),
|
||||
value: formatValue(marker.upper, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'upper_bounds'
|
||||
});
|
||||
contents += intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.lowerBoundsLabel',
|
||||
defaultMessage: '{br}lower bounds: {lowerBoundsValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
lowerBoundsValue: formatValue(marker.lower, marker.function, fieldFormat)
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.lowerBoundsLabel',
|
||||
defaultMessage: 'lower bounds'
|
||||
}),
|
||||
value: formatValue(marker.lower, marker.function, fieldFormat),
|
||||
seriesKey,
|
||||
yAccessor: 'lower_bounds'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (_.has(marker, 'scheduledEvents')) {
|
||||
contents += '<br/><hr/>' + intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.scheduledEventsLabel',
|
||||
defaultMessage: 'Scheduled events:{br}{scheduledEventsValue}'
|
||||
}, {
|
||||
br: '<br />',
|
||||
scheduledEventsValue: marker.scheduledEvents.map(mlEscape).join('<br/>')
|
||||
marker.scheduledEvents.forEach((scheduledEvent, i) => {
|
||||
tooltipData.push({
|
||||
name: intl.formatMessage({
|
||||
id: 'xpack.ml.timeSeriesExplorer.timeSeriesChart.scheduledEventsLabel',
|
||||
defaultMessage: 'scheduled event{counter}'
|
||||
}, { counter: marker.scheduledEvents.length > 1 ? ` #${i + 1}` : '' }),
|
||||
value: scheduledEvent,
|
||||
seriesKey,
|
||||
yAccessor: `scheduled_events_${i + 1}`
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (mlAnnotationsEnabled && _.has(marker, 'annotation')) {
|
||||
contents = mlEscape(marker.annotation);
|
||||
contents += `<br />${moment(marker.timestamp).format('MMMM Do YYYY, HH:mm')}`;
|
||||
tooltipData.length = 0;
|
||||
tooltipData.push({
|
||||
name: marker.annotation
|
||||
});
|
||||
let timespan = moment(marker.timestamp).format('MMMM Do YYYY, HH:mm');
|
||||
|
||||
if (typeof marker.end_timestamp !== 'undefined') {
|
||||
contents += ` - ${moment(marker.end_timestamp).format('MMMM Do YYYY, HH:mm')}`;
|
||||
timespan += ` - ${moment(marker.end_timestamp).format('MMMM Do YYYY, HH:mm')}`;
|
||||
}
|
||||
tooltipData.push({
|
||||
name: timespan
|
||||
});
|
||||
}
|
||||
|
||||
mlChartTooltipService.show(contents, circle, {
|
||||
mlChartTooltipService.show(tooltipData, circle, {
|
||||
x: LINE_CHART_ANOMALY_RADIUS * 2,
|
||||
y: 0
|
||||
});
|
||||
|
|
|
@ -42,6 +42,7 @@ import {
|
|||
mlFunctionToESAggregation,
|
||||
} from '../../common/util/job_utils';
|
||||
|
||||
import { ChartTooltip } from '../components/chart_tooltip';
|
||||
import { jobSelectServiceFactory, setGlobalState, getSelectedJobIds } from '../components/job_selector/job_select_service_utils';
|
||||
import { AnnotationFlyout } from '../components/annotations/annotation_flyout';
|
||||
import { AnnotationsTable } from '../components/annotations/annotations_table';
|
||||
|
@ -1041,6 +1042,7 @@ export class TimeSeriesExplorer extends React.Component {
|
|||
|
||||
return (
|
||||
<TimeSeriesExplorerPage jobSelectorProps={jobSelectorProps} loading={loading} resizeRef={this.resizeRef}>
|
||||
<ChartTooltip />
|
||||
<div className="series-controls" data-test-subj="mlSingleMetricViewerSeriesControls">
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
|
|
@ -18,7 +18,7 @@ import { getSingleMetricViewerBreadcrumbs } from './breadcrumbs';
|
|||
|
||||
uiRoutes
|
||||
.when('/timeseriesexplorer/?', {
|
||||
template: '<ml-chart-tooltip /><ml-time-series-explorer data-test-subj="mlPageSingleMetricViewer" />',
|
||||
template: '<ml-time-series-explorer data-test-subj="mlPageSingleMetricViewer" />',
|
||||
k7Breadcrumbs: getSingleMetricViewerBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkFullLicense,
|
||||
|
|
|
@ -6022,12 +6022,6 @@
|
|||
"xpack.ml.explorer.charts.tooManyBucketsDescription": "この選択は、表示するバケット数が多すぎます。ダッシュボードは短い時間範囲で表示するのが最適です。",
|
||||
"xpack.ml.explorer.charts.viewLabel": "表示",
|
||||
"xpack.ml.explorer.createNewJobLinkText": "新規ジョブを作成",
|
||||
"xpack.ml.explorer.distributionChart.anomalyScoreLabel": "異常スコア: {displayScore}",
|
||||
"xpack.ml.explorer.distributionChart.scheduledEventsLabel": "予定イベント:{br}{scheduledEventsValue}",
|
||||
"xpack.ml.explorer.distributionChart.typicalLabel": "{br}通常: {typicalValue}",
|
||||
"xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel": "{br} { numberOfCauses, plural, one {# 異常な {byFieldName} の値} other {#{plusSign} 異常な {byFieldName} の値}}",
|
||||
"xpack.ml.explorer.distributionChart.valueLabel": "{br} 値: {value}",
|
||||
"xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel": "値: {value}",
|
||||
"xpack.ml.explorer.fetchingSuggestionsErrorMessage": "提案の取得中にエラーが発生しました",
|
||||
"xpack.ml.explorer.intervalLabel": "間隔",
|
||||
"xpack.ml.explorer.invalidKuerySyntaxErrorMessage": "無効な Kuery 構文",
|
||||
|
@ -6045,17 +6039,8 @@
|
|||
"xpack.ml.explorer.overallLabel": "全体",
|
||||
"xpack.ml.explorer.overallSwimlaneUnfilteredLabel": "{label} (フィルタリングなし)",
|
||||
"xpack.ml.explorer.severityThresholdLabel": "深刻度のしきい値",
|
||||
"xpack.ml.explorer.singleMetricChart.actualLabel": "{br}実際: {actualValue}",
|
||||
"xpack.ml.explorer.singleMetricChart.anomalyScoreLabel": "異常スコア: {displayScore}",
|
||||
"xpack.ml.explorer.singleMetricChart.multiBucketImpactLabel": "{br}複数バケットの影響: {multiBucketImpactLabel}",
|
||||
"xpack.ml.explorer.singleMetricChart.scheduledEventsLabel": "予定イベント:{br}{scheduledEventsValue}",
|
||||
"xpack.ml.explorer.singleMetricChart.typicalLabel": "{br}通常: {typicalValue}",
|
||||
"xpack.ml.explorer.singleMetricChart.unusualByFieldValuesLabel": "{br} { numberOfCauses, plural, one {# 異常な {byFieldName} の値} other {#{plusSign} 異常な {byFieldName} の値}}",
|
||||
"xpack.ml.explorer.singleMetricChart.valueLabel": "{br} 値: {value}",
|
||||
"xpack.ml.explorer.singleMetricChart.valueWithoutAnomalyScoreLabel": "値: {value}",
|
||||
"xpack.ml.explorer.sortedByMaxAnomalyScoreForTimeFormattedLabel": "({viewByLoadedForTimeFormatted} の最高異常スコアで分類)",
|
||||
"xpack.ml.explorer.sortedByMaxAnomalyScoreLabel": "(最高異常スコアで分類)",
|
||||
"xpack.ml.explorer.swimlane.maxAnomalyScoreLabel": "最高異常スコア: {displayScore}",
|
||||
"xpack.ml.explorer.topInfuencersTitle": "トップ影響因子",
|
||||
"xpack.ml.explorer.tryWideningTimeSelectionLabel": "時間範囲を広げるか、さらに過去に遡ってみてください",
|
||||
"xpack.ml.explorer.viewByLabel": "表示方式",
|
||||
|
@ -6784,7 +6769,6 @@
|
|||
"xpack.ml.newJob.simple.population.chart.splitDataLabel": "データを分割",
|
||||
"xpack.ml.newJob.simple.population.chart.splitFieldPlaceholder": "フィールドを選択",
|
||||
"xpack.ml.newJob.simple.population.chartIntervalLabel": "チャート間隔: {interval}",
|
||||
"xpack.ml.newJob.simple.population.chartTooltipValueLabel": "値: {dataValue}",
|
||||
"xpack.ml.newJob.simple.population.couldNotOpenJobErrorMessage": "ジョブを開けませんでした:",
|
||||
"xpack.ml.newJob.simple.population.couldNotStartDatafeedErrorMessage": "データフィードを開始できませんでした",
|
||||
"xpack.ml.newJob.simple.population.createJobButtonAriaLabel": "ジョブの作成",
|
||||
|
@ -7146,27 +7130,13 @@
|
|||
"xpack.ml.timeSeriesExplorer.showForecastLabel": "予測を表示",
|
||||
"xpack.ml.timeSeriesExplorer.showModelBoundsLabel": "モデルバウンドを表示",
|
||||
"xpack.ml.timeSeriesExplorer.singleTimeSeriesAnalysisTitle": "{functionLabel} の単独時系列分析",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.actualLabel": "実際: {actualValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.addedAnnotationNotificationMessage": "ID {jobId} のジョブに注釈が追加されました。",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.anomalyScoreLabel": "異常スコア: {displayScore}{br}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.deletedAnnotationNotificationMessage": "ID {jobId} のジョブの注釈が削除されました。",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithCreatingAnnotationNotificationErrorMessage": "ID {jobId} のジョブの注釈を作成中にエラーが発生しました: {error}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithDeletingAnnotationNotificationErrorMessage": "ID {jobId} のジョブの注釈を削除中にエラーが発生しました: {error}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithUpdatingAnnotationNotificationErrorMessage": "ID {jobId} のジョブの注釈を更新中にエラーが発生しました: {error}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.modelBoundsNotAvailableLabel": "モデルバウンドが利用できません",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.lowerBoundsLabel": "{br}下のバウンド: {lowerBoundsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.upperBoundsLabel": "{br}上のバウンド: {upperBoundsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.valueLabel": "値: {value}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.moreThanOneUnusualByFieldValuesLabel": "{br} {numberOfCauses}{plusSign} 異常な {byFieldName} 値",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.multiBucketImpactLabel": "複数バケットの影響: {multiBucketImpactLabel}{br}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.scheduledEventsLabel": "予定イベント:{br}{scheduledEventsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.typicalLabel": "{br}通常: {typicalValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.updatedAnnotationNotificationMessage": "ID {jobId} のジョブの注釈が更新されました。",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.valueLabel": "値: {value}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.predictionLabel": "予測: {predictionValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.valueLabel": "値: {value}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.lowerBoundsLabel": "{br}下のバウンド: {lowerBoundsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.upperBoundsLabel": "{br}上のバウンド: {upperBoundsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomAggregationIntervalLabel": "(集約間隔: {focusAggInt}、バケットスパン: {bucketSpan})",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomGroupAggregationIntervalLabel": "(集約間隔: 、バケットスパン: )",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomLabel": "ズーム:",
|
||||
|
|
|
@ -6024,12 +6024,6 @@
|
|||
"xpack.ml.explorer.charts.tooManyBucketsDescription": "此选项包含太多要显示的时段。最好设置一个较短的时间范围来查看仪表板。",
|
||||
"xpack.ml.explorer.charts.viewLabel": "查看",
|
||||
"xpack.ml.explorer.createNewJobLinkText": "创建新作业",
|
||||
"xpack.ml.explorer.distributionChart.anomalyScoreLabel": "异常分数:{displayScore}",
|
||||
"xpack.ml.explorer.distributionChart.scheduledEventsLabel": "计划的事件:{br}{scheduledEventsValue}",
|
||||
"xpack.ml.explorer.distributionChart.typicalLabel": "{br}典型:{typicalValue}",
|
||||
"xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel": "{br} { numberOfCauses, plural, one {# 个异常 {byFieldName} 值} other {#{plusSign} 个异常 {byFieldName} 值}}",
|
||||
"xpack.ml.explorer.distributionChart.valueLabel": "{br}值:{value}",
|
||||
"xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel": "值:{value}",
|
||||
"xpack.ml.explorer.fetchingSuggestionsErrorMessage": "获取建议时出错",
|
||||
"xpack.ml.explorer.intervalLabel": "时间间隔",
|
||||
"xpack.ml.explorer.invalidKuerySyntaxErrorMessage": "kuery 语法无效",
|
||||
|
@ -6047,17 +6041,8 @@
|
|||
"xpack.ml.explorer.overallLabel": "总体",
|
||||
"xpack.ml.explorer.overallSwimlaneUnfilteredLabel": "{label}(未筛选)",
|
||||
"xpack.ml.explorer.severityThresholdLabel": "严重性阈值",
|
||||
"xpack.ml.explorer.singleMetricChart.actualLabel": "{br}实际:{actualValue}",
|
||||
"xpack.ml.explorer.singleMetricChart.anomalyScoreLabel": "异常分数:{displayScore}",
|
||||
"xpack.ml.explorer.singleMetricChart.multiBucketImpactLabel": "{br}多桶影响:{multiBucketImpactLabel}",
|
||||
"xpack.ml.explorer.singleMetricChart.scheduledEventsLabel": "计划的事件:{br}{scheduledEventsValue}",
|
||||
"xpack.ml.explorer.singleMetricChart.typicalLabel": "{br}典型:{typicalValue}",
|
||||
"xpack.ml.explorer.singleMetricChart.unusualByFieldValuesLabel": "{br} { numberOfCauses, plural, one {# 个异常 {byFieldName} 值} other {#{plusSign} 个异常 {byFieldName} 值}}",
|
||||
"xpack.ml.explorer.singleMetricChart.valueLabel": "{br}值:{value}",
|
||||
"xpack.ml.explorer.singleMetricChart.valueWithoutAnomalyScoreLabel": "值:{value}",
|
||||
"xpack.ml.explorer.sortedByMaxAnomalyScoreForTimeFormattedLabel": "(按 {viewByLoadedForTimeFormatted} 的异常分数最大值排序)",
|
||||
"xpack.ml.explorer.sortedByMaxAnomalyScoreLabel": "(按异常分数最大值排序)",
|
||||
"xpack.ml.explorer.swimlane.maxAnomalyScoreLabel": "异常分数最大值:{displayScore}",
|
||||
"xpack.ml.explorer.topInfuencersTitle": "顶级影响因素",
|
||||
"xpack.ml.explorer.tryWideningTimeSelectionLabel": "请尝试扩大时间选择范围或进一步向前追溯",
|
||||
"xpack.ml.explorer.viewByLabel": "查看者",
|
||||
|
@ -6786,7 +6771,6 @@
|
|||
"xpack.ml.newJob.simple.population.chart.splitDataLabel": "分割数据:",
|
||||
"xpack.ml.newJob.simple.population.chart.splitFieldPlaceholder": "选择字段",
|
||||
"xpack.ml.newJob.simple.population.chartIntervalLabel": "图表时间间隔:{interval}",
|
||||
"xpack.ml.newJob.simple.population.chartTooltipValueLabel": "值:{dataValue}",
|
||||
"xpack.ml.newJob.simple.population.couldNotOpenJobErrorMessage": "无法打开作业:",
|
||||
"xpack.ml.newJob.simple.population.couldNotStartDatafeedErrorMessage": "无法开始数据馈送:",
|
||||
"xpack.ml.newJob.simple.population.createJobButtonAriaLabel": "创建作业",
|
||||
|
@ -7148,27 +7132,13 @@
|
|||
"xpack.ml.timeSeriesExplorer.showForecastLabel": "显示预测",
|
||||
"xpack.ml.timeSeriesExplorer.showModelBoundsLabel": "显示模型边界",
|
||||
"xpack.ml.timeSeriesExplorer.singleTimeSeriesAnalysisTitle": "{functionLabel} 的单时间序列分析",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.actualLabel": "实际:{actualValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.addedAnnotationNotificationMessage": "已为 ID {jobId} 的作业添加注释。",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.anomalyScoreLabel": "异常分数:{displayScore}{br}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.deletedAnnotationNotificationMessage": "已为 ID {jobId} 的作业删除注释。",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithCreatingAnnotationNotificationErrorMessage": "为 ID {jobId} 的作业创建注释时发生错误:{error}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithDeletingAnnotationNotificationErrorMessage": "为 ID {jobId} 的作业删除注释时发生错误:{error}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.errorWithUpdatingAnnotationNotificationErrorMessage": "为 ID {jobId} 的作业更新注释时发生错误:{error}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.modelBoundsNotAvailableLabel": "模型边界不可用",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.lowerBoundsLabel": "{br}下边界:{lowerBoundsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.upperBoundsLabel": "{br}上边界:{upperBoundsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.valueLabel": "值:{value}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.moreThanOneUnusualByFieldValuesLabel": "{br} {numberOfCauses}{plusSign} 异常 {byFieldName} 值",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.multiBucketImpactLabel": "多存储桶影响:{multiBucketImpactLabel}{br}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.scheduledEventsLabel": "计划的事件:{br}{scheduledEventsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.typicalLabel": "{br}典型:{typicalValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.updatedAnnotationNotificationMessage": "已为 ID {jobId} 的作业更新注释。",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.valueLabel": "值:{value}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.predictionLabel": "预测:{predictionValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.valueLabel": "值:{value}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.lowerBoundsLabel": "{br}下边界:{lowerBoundsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.upperBoundsLabel": "{br}上边界:{upperBoundsValue}",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomAggregationIntervalLabel": "(聚合时间间隔:{focusAggInt},存储桶跨度:{bucketSpan})",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomGroupAggregationIntervalLabel": "(聚合时间间隔:,存储桶跨度:)",
|
||||
"xpack.ml.timeSeriesExplorer.timeSeriesChart.zoomLabel": "缩放:",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue