Skip to content

Commit

Permalink
[ML] Hide annotation strip
Browse files Browse the repository at this point in the history
  • Loading branch information
qn895 committed Nov 2, 2020
1 parent f2253c0 commit e12c721
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ export const annotations = {

getAnnotations(obj: {
jobIds: string[];
earliestMs: number;
latestMs: number;
earliestMs: number | null;
latestMs: number | null;
maxAnnotations: number;
fields: FieldToBucket[];
detectorIndex: number;
entities: any[];
fields?: FieldToBucket[];
detectorIndex?: number;
entities?: any[];
}) {
const body = JSON.stringify(obj);
return http<GetAnnotationsResponse>({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import d3 from 'd3';

import React from 'react';
import { Annotation } from '../../../../../common/types/annotations';
import { CombinedJob } from '../../../../../common/types/anomaly_detection_jobs';
import { ChartTooltipService } from '../../../components/chart_tooltip';
Expand All @@ -22,3 +23,30 @@ interface State {
export interface TimeseriesChart extends React.Component<Props, State> {
focusXScale: d3.scale.Ordinal<{}, number>;
}

interface TimeseriesChartProps {
annotation: object;
autoZoomDuration: number;
bounds: object;
contextAggregationInterval: object;
contextChartData: any[];
contextForecastData: any[];
contextChartSelected: any;
detectorIndex: number;
focusAggregationInterval: object;
focusAnnotationData: Annotation[];
focusChartData: any[];
focusForecastData: any[];
modelPlotEnabled: boolean;
renderFocusChartOnly: boolean;
selectedJob: CombinedJob;
showForecast: boolean;
showModelBounds: boolean;
svgWidth: number;
swimlaneData: any[];
zoomFrom: object;
zoomTo: object;
zoomFromFocusLoaded: object;
zoomToFocusLoaded: object;
tooltipService: object;
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,13 @@ const anomalyGrayScale = d3.scale
.domain([3, 25, 50, 75, 100])
.range(['#dce7ed', '#b0c5d6', '#b1a34e', '#b17f4e', '#c88686']);

function getSvgHeight() {
function getSvgHeight(showAnnotations) {
const adjustedAnnotationHeight = showAnnotations ? annotationHeight : 0;
return (
focusHeight +
contextChartHeight +
swimlaneHeight +
annotationHeight +
adjustedAnnotationHeight +
chartSpacing +
margin.top +
margin.bottom
Expand Down Expand Up @@ -261,6 +262,7 @@ class TimeseriesChartIntl extends Component {
modelPlotEnabled,
selectedJob,
svgWidth,
showAnnotations,
} = this.props;

const createFocusChart = this.createFocusChart.bind(this);
Expand All @@ -269,7 +271,7 @@ class TimeseriesChartIntl extends Component {
const focusYAxis = this.focusYAxis;
const focusYScale = this.focusYScale;

const svgHeight = getSvgHeight();
const svgHeight = getSvgHeight(showAnnotations);

// Clear any existing elements from the visualization,
// then build the svg elements for the bubble chart.
Expand Down Expand Up @@ -1027,7 +1029,9 @@ class TimeseriesChartIntl extends Component {
.domain([chartLimits.min, chartLimits.max]);

const borders = cxtGroup.append('g').attr('class', 'axis');
const brushChartHeight = cxtChartHeight + swlHeight + annotationHeight;
const brushChartHeight = showAnnotations
? cxtChartHeight + swlHeight + annotationHeight
: cxtChartHeight + swlHeight;

// Add borders left and right.
borders.append('line').attr('x1', 0).attr('y1', 0).attr('x2', 0).attr('y2', brushChartHeight);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { FC, useEffect, useState, useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { MlTooltipComponent } from '../../../components/chart_tooltip';
import { TimeseriesChart } from './timeseries_chart';
import { CombinedJob } from '../../../../../common/types/anomaly_detection_jobs';
import { ml } from '../../../services/ml_api_service';
import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../../common/constants/search';
import { extractErrorMessage } from '../../../../../common/util/errors';
import { Annotation } from '../../../../../common/types/annotations';
import { useNotifications } from '../../../contexts/kibana';

interface TimeSeriesChartWithTooltipsProps {
bounds: any;
selectedDetectorIndex: number;
renderFocusChartOnly: boolean;
selectedJob: CombinedJob;
showAnnotations: boolean;
showForecast: boolean;
showModelBounds: boolean;
chartProps: any;
lastRefresh: number;
}
export const TimeSeriesChartWithTooltips: FC<TimeSeriesChartWithTooltipsProps> = ({
bounds,
selectedDetectorIndex,
renderFocusChartOnly,
selectedJob,
showAnnotations,
showForecast,
showModelBounds,
chartProps,
lastRefresh,
}) => {
const { toasts: toastNotifications } = useNotifications();

const [annotationData, setAnnotationData] = useState<Annotation[]>([]);

const showAnnotationErrorToastNotification = useCallback((error?: string) => {
toastNotifications.addDanger({
title: i18n.translate('xpack.ml.timeSeriesExplorer.annotationsErrorTitle', {
defaultMessage: 'An error occurred fetching annotations',
}),
...(error ? { text: extractErrorMessage(error) } : {}),
});
}, []);

useEffect(() => {
let unmounted = false;
/**
* Loads the full list of annotations for job without any aggs or time boundaries
* used to indicate existence of annotations that are beyond the selected time
* in the time series brush area
*/
const loadAnnotations = async (jobId: string) => {
try {
const resp = await ml.annotations.getAnnotations({
jobIds: [jobId],
earliestMs: null,
latestMs: null,
maxAnnotations: ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE,
});
if (!unmounted) {
if (Array.isArray(resp.annotations[jobId])) {
setAnnotationData(resp.annotations[jobId]);
} else {
showAnnotationErrorToastNotification();
}
}
} catch (error) {
showAnnotationErrorToastNotification(error);
}
};

loadAnnotations(selectedJob.job_id);

return () => {
unmounted = false;
};
}, [selectedJob.job_id, selectedDetectorIndex, lastRefresh]);

return (
<div className="ml-timeseries-chart" data-test-subj="mlSingleMetricViewerChart">
<MlTooltipComponent>
{(tooltipService) => (
<TimeseriesChart
{...chartProps}
annotationData={annotationData}
bounds={bounds}
detectorIndex={selectedDetectorIndex}
renderFocusChartOnly={renderFocusChartOnly}
selectedJob={selectedJob}
showAnnotations={showAnnotations}
showForecast={showForecast}
showModelBounds={showModelBounds}
tooltipService={tooltipService}
/>
)}
</MlTooltipComponent>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { FC } from 'react';

import { getDateFormatTz, TimeRangeBounds } from '../explorer/explorer_utils';
import { TimeRangeBounds } from '../explorer/explorer_utils';

declare const TimeSeriesExplorer: FC<{
appStateHandler: (action: string, payload: any) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ import {
import { getToastNotifications } from '../util/dependency_cache';
import { ResizeChecker } from '../../../../../../src/plugins/kibana_utils/public';

import {
ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE,
ANOMALIES_TABLE_DEFAULT_QUERY_SIZE,
} from '../../../common/constants/search';
import { ANOMALIES_TABLE_DEFAULT_QUERY_SIZE } from '../../../common/constants/search';
import {
isModelPlotEnabled,
isModelPlotChartableForDetector,
Expand All @@ -50,12 +47,10 @@ import {
import { AnnotationFlyout } from '../components/annotations/annotation_flyout';
import { AnnotationsTable } from '../components/annotations/annotations_table';
import { AnomaliesTable } from '../components/anomalies_table/anomalies_table';
import { MlTooltipComponent } from '../components/chart_tooltip';
import { ForecastingModal } from './components/forecasting_modal/forecasting_modal';
import { LoadingIndicator } from '../components/loading_indicator/loading_indicator';
import { SelectInterval } from '../components/controls/select_interval/select_interval';
import { SelectSeverity } from '../components/controls/select_severity/select_severity';
import { TimeseriesChart } from './components/timeseries_chart/timeseries_chart';
import { TimeseriesexplorerNoChartData } from './components/timeseriesexplorer_no_chart_data';
import { TimeSeriesExplorerPage } from './timeseriesexplorer_page';

Expand Down Expand Up @@ -84,9 +79,9 @@ import {
getFocusData,
} from './timeseriesexplorer_utils';
import { ANOMALY_DETECTION_DEFAULT_TIME_RANGE } from '../../../common/constants/settings';
import { extractErrorMessage } from '../../../common/util/errors';
import { getControlsForDetector } from './get_controls_for_detector';
import { SeriesControls } from './components/series_controls';
import { TimeSeriesChartWithTooltips } from './components/timeseries_chart/timeseries_chart_with_tooltip';

// Used to indicate the chart is being plotted across
// all partition field values, where the cardinality of the field cannot be
Expand Down Expand Up @@ -119,8 +114,6 @@ function getTimeseriesexplorerDefaultState() {
dataNotChartable: false,
entitiesLoading: false,
entityValues: {},
annotationError: undefined,
annotationData: [],
focusAnnotationData: [],
focusAggregations: {},
focusAggregationInterval: {},
Expand Down Expand Up @@ -385,29 +378,6 @@ export class TimeSeriesExplorer extends React.Component {
);
};

/**
* Loads the full list of annotations for job without any aggs or time boundaries
* used to indicate existence of annotations that are beyond the selected time
* in the time series brush area
*/
loadAnnotations = (jobId) => {
ml.annotations
.getAnnotations({
jobIds: [jobId],
earliestMs: null,
latestMs: null,
maxAnnotations: ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE,
})
.then((resp) => {
if (!this.unmounted && resp?.success === true && Array.isArray(resp.annotations[jobId])) {
this.setState({ annotationData: resp.annotations[jobId] ?? [] });
}
})
.catch((error) => {
this.setState({ annotationData: [], annotationError: extractErrorMessage(error) });
});
};

/**
* Loads available entity values.
* @param {Array} entities - Entity controls configuration
Expand Down Expand Up @@ -933,7 +903,6 @@ export class TimeSeriesExplorer extends React.Component {
previousProps.selectedForecastId !== this.props.selectedForecastId ||
previousProps.selectedJobId !== this.props.selectedJobId;
this.loadSingleMetricData(fullRefresh);
this.loadAnnotations(this.props.selectedJobId);
}

if (previousProps === undefined) {
Expand Down Expand Up @@ -981,7 +950,6 @@ export class TimeSeriesExplorer extends React.Component {
contextForecastData,
dataNotChartable,
focusAggregationInterval,
annotationData,
focusAnnotationError,
focusAnnotationData,
focusAggregations,
Expand Down Expand Up @@ -1013,7 +981,6 @@ export class TimeSeriesExplorer extends React.Component {
contextForecastData,
contextAggregationInterval,
swimlaneData,
annotationData,
focusAnnotationData,
focusChartData,
focusForecastData,
Expand Down Expand Up @@ -1219,23 +1186,17 @@ export class TimeSeriesExplorer extends React.Component {
</EuiFlexItem>
)}
</EuiFlexGroup>
<div className="ml-timeseries-chart" data-test-subj="mlSingleMetricViewerChart">
<MlTooltipComponent>
{(tooltipService) => (
<TimeseriesChart
{...chartProps}
bounds={bounds}
detectorIndex={selectedDetectorIndex}
renderFocusChartOnly={renderFocusChartOnly}
selectedJob={selectedJob}
showAnnotations={showAnnotations}
showForecast={showForecast}
showModelBounds={showModelBounds}
tooltipService={tooltipService}
/>
)}
</MlTooltipComponent>
</div>
<TimeSeriesChartWithTooltips
chartProps={chartProps}
bounds={bounds}
detectorIndex={selectedDetectorIndex}
renderFocusChartOnly={renderFocusChartOnly}
selectedJob={selectedJob}
showAnnotations={showAnnotations}
showForecast={showForecast}
showModelBounds={showModelBounds}
lastRefresh={lastRefresh}
/>
{focusAnnotationError !== undefined && (
<>
<EuiTitle
Expand Down

0 comments on commit e12c721

Please sign in to comment.