mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Fix Single Metric Viewer chart failing to load if no points during calendar event (#130000)
* [ML] Fix Single Metric Viewer chart load if no points during calendar event * [ML] Type fix
This commit is contained in:
parent
b7dc8f0e9c
commit
8afa2c30fd
5 changed files with 76 additions and 15 deletions
|
@ -131,7 +131,6 @@
|
|||
stroke-width: 1px;
|
||||
stroke: $euiColorDarkShade;
|
||||
fill: $euiColorLightShade;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.forecast {
|
||||
|
|
|
@ -667,7 +667,8 @@ class TimeseriesChartIntl extends Component {
|
|||
return d.lower;
|
||||
}
|
||||
}
|
||||
return metricValue;
|
||||
// metricValue is undefined for scheduled events when there is no source data.
|
||||
return metricValue || 0;
|
||||
});
|
||||
yMax = d3.max(combinedData, (d) => {
|
||||
let metricValue = d.value;
|
||||
|
@ -675,7 +676,8 @@ class TimeseriesChartIntl extends Component {
|
|||
// If an anomaly coincides with a gap in the data, use the anomaly actual value.
|
||||
metricValue = Array.isArray(d.actual) ? d.actual[0] : d.actual;
|
||||
}
|
||||
return d.upper !== undefined ? Math.max(metricValue, d.upper) : metricValue;
|
||||
// metricValue is undefined for scheduled events when there is no source data.
|
||||
return d.upper !== undefined ? Math.max(metricValue, d.upper) : metricValue || 0;
|
||||
});
|
||||
|
||||
if (yMax === yMin) {
|
||||
|
@ -701,6 +703,7 @@ class TimeseriesChartIntl extends Component {
|
|||
// TODO needs revisiting to be a more robust normalization
|
||||
yMax += Math.abs(yMax - yMin) * ((maxLevel + 1) / 5);
|
||||
}
|
||||
|
||||
this.focusYScale.domain([yMin, yMax]);
|
||||
} else {
|
||||
// Display 10 unlabelled ticks.
|
||||
|
@ -835,6 +838,10 @@ class TimeseriesChartIntl extends Component {
|
|||
scheduledEventMarkers
|
||||
.enter()
|
||||
.append('rect')
|
||||
.on('mouseover', function (d) {
|
||||
showFocusChartTooltip(d, this);
|
||||
})
|
||||
.on('mouseout', () => hideFocusChartTooltip())
|
||||
.attr('width', LINE_CHART_ANOMALY_RADIUS * 2)
|
||||
.attr('height', SCHEDULED_EVENT_SYMBOL_HEIGHT)
|
||||
.attr('class', 'scheduled-event-marker')
|
||||
|
@ -844,7 +851,10 @@ class TimeseriesChartIntl extends Component {
|
|||
// Update all markers to new positions.
|
||||
scheduledEventMarkers
|
||||
.attr('x', (d) => this.focusXScale(d.date) - LINE_CHART_ANOMALY_RADIUS)
|
||||
.attr('y', (d) => this.focusYScale(d.value) - 3);
|
||||
.attr('y', (d) => {
|
||||
const focusYValue = this.focusYScale(d.value);
|
||||
return isNaN(focusYValue) ? -focusHeight - 3 : focusYValue - 3;
|
||||
});
|
||||
|
||||
// Plot any forecast data in scope.
|
||||
if (focusForecastData !== undefined) {
|
||||
|
@ -1652,7 +1662,7 @@ class TimeseriesChartIntl extends Component {
|
|||
valueAccessor: 'prediction',
|
||||
});
|
||||
} else {
|
||||
if (marker.value !== undefined) {
|
||||
if (marker.value !== undefined && marker.value !== null) {
|
||||
tooltipData.push({
|
||||
label: i18n.translate(
|
||||
'xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.valueLabel',
|
||||
|
|
|
@ -148,7 +148,11 @@ export function getFocusData(
|
|||
modelPlotEnabled,
|
||||
functionDescription
|
||||
);
|
||||
focusChartData = processScheduledEventsForChart(focusChartData, scheduledEvents);
|
||||
focusChartData = processScheduledEventsForChart(
|
||||
focusChartData,
|
||||
scheduledEvents,
|
||||
focusAggregationInterval
|
||||
);
|
||||
|
||||
const refreshFocusData: FocusData = {
|
||||
scheduledEvents,
|
||||
|
|
|
@ -21,7 +21,11 @@ export function processDataForFocusAnomalies(
|
|||
functionDescription: any
|
||||
): any;
|
||||
|
||||
export function processScheduledEventsForChart(chartData: any, scheduledEvents: any): any;
|
||||
export function processScheduledEventsForChart(
|
||||
chartData: any,
|
||||
scheduledEvents: any,
|
||||
aggregationInterval: any
|
||||
): any;
|
||||
|
||||
export function findNearestChartPointToTime(chartData: any, time: any): any;
|
||||
|
||||
|
|
|
@ -205,16 +205,44 @@ export function processDataForFocusAnomalies(
|
|||
|
||||
// Adds a scheduledEvents property to any points in the chart data set
|
||||
// which correspond to times of scheduled events for the job.
|
||||
export function processScheduledEventsForChart(chartData, scheduledEvents) {
|
||||
export function processScheduledEventsForChart(chartData, scheduledEvents, aggregationInterval) {
|
||||
if (scheduledEvents !== undefined) {
|
||||
const timesToAddPointsFor = [];
|
||||
|
||||
// Iterate through the scheduled events making sure we have a chart point for each event.
|
||||
const intervalMs = aggregationInterval.asMilliseconds();
|
||||
let lastChartDataPointTime = undefined;
|
||||
if (chartData !== undefined && chartData.length > 0) {
|
||||
lastChartDataPointTime = chartData[chartData.length - 1].date.getTime();
|
||||
}
|
||||
|
||||
// In case there's no chart data/sparse data during these scheduled events
|
||||
// ensure we add chart points at every aggregation interval for these scheduled events.
|
||||
let sortRequired = false;
|
||||
each(scheduledEvents, (events, time) => {
|
||||
const chartPoint = findNearestChartPointToTime(chartData, time);
|
||||
if (chartPoint !== undefined) {
|
||||
// Note if the scheduled event coincides with an absence of the underlying metric data,
|
||||
// we don't worry about plotting the event.
|
||||
chartPoint.scheduledEvents = events;
|
||||
const exactChartPoint = findChartPointForScheduledEvent(chartData, +time);
|
||||
|
||||
if (exactChartPoint !== undefined) {
|
||||
exactChartPoint.scheduledEvents = events;
|
||||
} else {
|
||||
const timeToAdd = Math.floor(time / intervalMs) * intervalMs;
|
||||
if (timesToAddPointsFor.indexOf(timeToAdd) === -1 && timeToAdd !== lastChartDataPointTime) {
|
||||
const pointToAdd = {
|
||||
date: new Date(timeToAdd),
|
||||
value: null,
|
||||
scheduledEvents: events,
|
||||
};
|
||||
|
||||
chartData.push(pointToAdd);
|
||||
sortRequired = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Sort chart data by time if extra points were added at the end of the array for scheduled events.
|
||||
if (sortRequired === true) {
|
||||
chartData.sort((a, b) => a.date.getTime() - b.date.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
return chartData;
|
||||
|
@ -240,12 +268,12 @@ export function findNearestChartPointToTime(chartData, time) {
|
|||
// grab the current and previous items and compare the time differences
|
||||
let foundItem;
|
||||
for (let i = 0; i < chartData.length; i++) {
|
||||
const itemTime = chartData[i].date.getTime();
|
||||
const itemTime = chartData[i]?.date?.getTime();
|
||||
if (itemTime > time) {
|
||||
const item = chartData[i];
|
||||
const previousItem = chartData[i - 1];
|
||||
|
||||
const diff1 = Math.abs(time - previousItem.date.getTime());
|
||||
const diff1 = Math.abs(time - previousItem?.date?.getTime());
|
||||
const diff2 = Math.abs(time - itemTime);
|
||||
|
||||
// foundItem should be the item with a date closest to bucketTime
|
||||
|
@ -300,6 +328,22 @@ export function findChartPointForAnomalyTime(chartData, anomalyTime, aggregation
|
|||
return chartPoint;
|
||||
}
|
||||
|
||||
export function findChartPointForScheduledEvent(chartData, eventTime) {
|
||||
let chartPoint;
|
||||
if (chartData === undefined) {
|
||||
return chartPoint;
|
||||
}
|
||||
|
||||
for (let i = 0; i < chartData.length; i++) {
|
||||
if (chartData[i].date.getTime() === eventTime) {
|
||||
chartPoint = chartData[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return chartPoint;
|
||||
}
|
||||
|
||||
export function calculateAggregationInterval(bounds, bucketsTarget, jobs, selectedJob) {
|
||||
// Aggregation interval used in queries should be a function of the time span of the chart
|
||||
// and the bucket span of the selected job(s).
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue