Skip to content

Commit

Permalink
[ML] Fix Anomaly Explorer charts time range to obey time picker range (
Browse files Browse the repository at this point in the history
…#80317)

* [ML] Fix Anomaly Explorer charts time range to obey time picker range

* [ML] Fix explorer_charts_container_service Jest tests
  • Loading branch information
peteharverson authored Oct 14, 2020
1 parent d508fef commit e88cf10
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { get, each, find, sortBy, map, reduce } from 'lodash';

import { buildConfig } from './explorer_chart_config_builder';
import { chartLimits, getChartType } from '../../util/chart_utils';
import { getTimefilter } from '../../util/dependency_cache';

import { getEntityFieldList } from '../../../../common/util/anomaly_utils';
import {
Expand Down Expand Up @@ -50,8 +51,8 @@ const MAX_CHARTS_PER_ROW = 4;
export const anomalyDataChange = function (
chartsContainerWidth,
anomalyRecords,
earliestMs,
latestMs,
selectedEarliestMs,
selectedLatestMs,
severity = 0
) {
const data = getDefaultChartsData();
Expand Down Expand Up @@ -83,8 +84,8 @@ export const anomalyDataChange = function (
const chartWidth = Math.floor(chartsContainerWidth / chartsPerRow);
const { chartRange, tooManyBuckets } = calculateChartRange(
seriesConfigs,
earliestMs,
latestMs,
selectedEarliestMs,
selectedLatestMs,
chartWidth,
recordsToPlot,
data.timeFieldName
Expand Down Expand Up @@ -408,8 +409,8 @@ export const anomalyDataChange = function (
chartData: processedData[i],
plotEarliest: chartRange.min,
plotLatest: chartRange.max,
selectedEarliest: earliestMs,
selectedLatest: latestMs,
selectedEarliest: selectedEarliestMs,
selectedLatest: selectedLatestMs,
chartLimits: USE_OVERALL_CHART_LIMITS ? overallChartLimits : chartLimits(processedData[i]),
}));
explorerService.setCharts({ ...data });
Expand Down Expand Up @@ -561,19 +562,21 @@ function processRecordsForDisplay(anomalyRecords) {

function calculateChartRange(
seriesConfigs,
earliestMs,
latestMs,
selectedEarliestMs,
selectedLatestMs,
chartWidth,
recordsToPlot,
timeFieldName
) {
let tooManyBuckets = false;
// Calculate the time range for the charts.
// Fit in as many points in the available container width plotted at the job bucket span.
const midpointMs = Math.ceil((earliestMs + latestMs) / 2);
const midpointMs = Math.ceil((selectedEarliestMs + selectedLatestMs) / 2);
const maxBucketSpanMs = Math.max.apply(null, map(seriesConfigs, 'bucketSpanSeconds')) * 1000;

const pointsToPlotFullSelection = Math.ceil((latestMs - earliestMs) / maxBucketSpanMs);
const pointsToPlotFullSelection = Math.ceil(
(selectedLatestMs - selectedEarliestMs) / maxBucketSpanMs
);

// Optimally space points 5px apart.
const optimumPointSpacing = 5;
Expand All @@ -583,9 +586,12 @@ function calculateChartRange(
// at optimal point spacing.
const plotPoints = Math.max(optimumNumPoints, pointsToPlotFullSelection);
const halfPoints = Math.ceil(plotPoints / 2);
const timefilter = getTimefilter();
const bounds = timefilter.getActiveBounds();

let chartRange = {
min: midpointMs - halfPoints * maxBucketSpanMs,
max: midpointMs + halfPoints * maxBucketSpanMs,
min: Math.max(midpointMs - halfPoints * maxBucketSpanMs, bounds.min.valueOf()),
max: Math.min(midpointMs + halfPoints * maxBucketSpanMs, bounds.max.valueOf()),
};

if (plotPoints > CHART_MAX_POINTS) {
Expand Down Expand Up @@ -615,8 +621,8 @@ function calculateChartRange(

if (maxMs - minMs < maxTimeSpan) {
// Expand out to cover as much as the requested time span as possible.
minMs = Math.max(earliestMs, minMs - maxTimeSpan);
maxMs = Math.min(latestMs, maxMs + maxTimeSpan);
minMs = Math.max(selectedEarliestMs, minMs - maxTimeSpan);
maxMs = Math.min(selectedLatestMs, maxMs + maxTimeSpan);
}

chartRange = { min: minMs, max: maxMs };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import mockSeriesPromisesResponse from './__mocks__/mock_series_promises_respons
//
// 'call anomalyChangeListener with actual series config'
// This test uses the standard mocks and uses the data as is provided via the mock files.
// The mocked services check for values in the data (e.g. 'mock-job-id', 'farequore-2017')
// The mocked services check for values in the data (e.g. 'mock-job-id', 'farequote-2017')
// and return the mock data from the files.
//
// 'filtering should skip values of null'
Expand Down Expand Up @@ -88,14 +88,41 @@ jest.mock('../../util/string_utils', () => ({
},
}));

jest.mock('../../util/dependency_cache', () => {
const dateMath = require('@elastic/datemath');
let _time = undefined;
const timefilter = {
setTime: (time) => {
_time = time;
},
getActiveBounds: () => {
return {
min: dateMath.parse(_time.from),
max: dateMath.parse(_time.to),
};
},
};
return {
getTimefilter: () => timefilter,
};
});

jest.mock('../explorer_dashboard_service', () => ({
explorerService: {
setCharts: jest.fn(),
},
}));

import moment from 'moment';
import { anomalyDataChange, getDefaultChartsData } from './explorer_charts_container_service';
import { explorerService } from '../explorer_dashboard_service';
import { getTimefilter } from '../../util/dependency_cache';

const timefilter = getTimefilter();
timefilter.setTime({
from: moment(1486425600000).toISOString(), // Feb 07 2017
to: moment(1486857600000).toISOString(), // Feb 12 2017
});

describe('explorerChartsContainerService', () => {
afterEach(() => {
Expand Down

0 comments on commit e88cf10

Please sign in to comment.