Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Add UX support for HC detector #314

Merged
32 changes: 22 additions & 10 deletions public/pages/AnomalyCharts/containers/AnomaliesChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ import {
AnomalyHeatmapChart,
HeatmapCell,
} from '../containers/AnomalyHeatmapChart';
import {
getAnomalyGradeWording,
getConfidenceWording,
getFeatureBreakdownWording,
getFeatureDataWording,
} from '../utils/anomalyChartUtils';
import {
DATE_PICKER_QUICK_OPTIONS,
INITIAL_ANOMALY_SUMMARY,
Expand All @@ -59,8 +65,6 @@ interface AnomaliesChartProps {
dateRange: DateRange;
isLoading: boolean;
showAlerts?: boolean;
anomalyGradeSeriesName: string;
confidenceSeriesName: string;
detectorId: string;
detectorName: string;
detector?: Detector;
Expand Down Expand Up @@ -213,7 +217,7 @@ export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => {
unit={props.unit}
onHeatmapCellSelected={props.onHeatmapCellSelected}
/>,
props.showAlerts === undefined || !props.showAlerts
props.showAlerts !== true
? [
<EuiSpacer size="m" />,
<AnomalyOccurrenceChart
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AnomalyOccurrenceChart is only used by HC detector and AnomalyDetailChart is only used single entity detector?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and AnomalyOccurrenceChart contains AnomalyDetailChart. AnomalyOccurrenceChart is actually wrapper of AnomalyDetailChart, making AnomalyDetailChart a separate chart so that it can be easily re-used in multiple places

Expand All @@ -229,9 +233,13 @@ export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => {
bucketizedAnomalies={false}
anomalySummary={INITIAL_ANOMALY_SUMMARY}
isLoading={props.isLoading}
anomalyGradeSeriesName="Anomaly grade"
confidenceSeriesName="Confidence"
showAlerts={false}
anomalyGradeSeriesName={getAnomalyGradeWording(
props.showAlerts
)}
confidenceSeriesName={getConfidenceWording(
props.showAlerts
)}
showAlerts={props.showAlerts}
detectorId={props.detectorId}
detectorName={props.detectorName}
detector={props.detector}
Expand All @@ -248,7 +256,9 @@ export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => {
/>,
<EuiSpacer size="m" />,
<FeatureBreakDown
title="Sample feature breakdown"
title={getFeatureBreakdownWording(
props.showAlerts
)}
//@ts-ignore
detector={props.newDetector}
//@ts-ignore
Expand All @@ -259,7 +269,9 @@ export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => {
isLoading={props.isLoading}
//@ts-ignore
dateRange={props.zoomRange}
featureDataSeriesName="Sample feature output"
featureDataSeriesName={getFeatureDataWording(
props.showAlerts
)}
isHCDetector={props.isHCDetector}
selectedHeatmapCell={props.selectedHeatmapCell}
/>,
Expand All @@ -279,8 +291,8 @@ export const AnomaliesChart = React.memo((props: AnomaliesChartProps) => {
bucketizedAnomalies={props.bucketizedAnomalies}
anomalySummary={props.anomalySummary}
isLoading={props.isLoading}
anomalyGradeSeriesName="Anomaly grade"
confidenceSeriesName="Confidence"
anomalyGradeSeriesName={getAnomalyGradeWording(props.showAlerts)}
confidenceSeriesName={getConfidenceWording(props.showAlerts)}
showAlerts={props.showAlerts}
detectorId={props.detectorId}
detectorName={props.detectorName}
Expand Down
70 changes: 31 additions & 39 deletions public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ import {
convertAlerts,
disabledHistoryAnnotations,
generateAlertAnnotations,
getAnomalyGradeWording,
getAnomalyOccurrenceWording,
getAnomalySummary,
getConfidenceWording,
getLastAnomalyOccurrenceWording,
} from '../utils/anomalyChartUtils';
import {
ANOMALY_CHART_THEME,
Expand Down Expand Up @@ -169,18 +173,17 @@ export const AnomalyDetailsChart = React.memo(
}
if (
props.monitor &&
props.dateRange.startDate &&
props.dateRange &&
// only load alert stats for non HC detector
props.isHCDetector != undefined &&
!props.isHCDetector
props.isHCDetector !== true
) {
getMonitorAlerts(
props.monitor.id,
props.dateRange.startDate,
props.dateRange.endDate
);
}
}, [props.monitor, props.dateRange.startDate]);
}, [props.monitor, props.dateRange.startDate, props.dateRange.endDate]);

const anomalyChartTimeFormatter = niceTimeFormatter([
zoomRange.startDate,
Expand All @@ -192,60 +195,41 @@ export const AnomalyDetailsChart = React.memo(
handleZoomRangeChange(startDate, endDate);
};

const showLoader = useDelayedLoader(props.isLoading || isLoadingAlerts);
const isLoading = props.isLoading || isLoadingAlerts;
const showLoader = useDelayedLoader(isLoading);

return (
<React.Fragment>
<EuiFlexGroup style={{ padding: '20px' }}>
<EuiFlexItem>
<EuiStat
title={
props.isLoading || isLoadingAlerts
? '-'
: anomalySummary.anomalyOccurrence
}
description={
props.showAlerts
? 'Anomaly occurrences'
: 'Sample anomaly occurrences'
}
title={isLoading ? '-' : anomalySummary.anomalyOccurrence}
description={getAnomalyOccurrenceWording(props.showAlerts)}
titleSize="s"
/>
</EuiFlexItem>
<EuiFlexItem>
<AnomalyStatWithTooltip
isLoading={props.isLoading || isLoadingAlerts}
isLoading={isLoading}
minValue={anomalySummary.minAnomalyGrade}
maxValue={anomalySummary.maxAnomalyGrade}
description={
props.showAlerts ? 'Anomaly grade' : 'Sample anomaly grade'
}
description={getAnomalyGradeWording(props.showAlerts)}
tooltip="Indicates to what extent this data point is anomalous. The scale ranges from 0 to 1."
/>
</EuiFlexItem>
<EuiFlexItem>
<AnomalyStatWithTooltip
isLoading={props.isLoading || isLoadingAlerts}
isLoading={isLoading}
minValue={anomalySummary.minConfidence}
maxValue={anomalySummary.maxConfidence}
description={
props.showAlerts ? 'Confidence' : 'Sample confidence'
}
description={getConfidenceWording(props.showAlerts)}
tooltip="Indicates the level of confidence in the anomaly result."
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiStat
title={
props.isLoading || isLoadingAlerts
? ''
: anomalySummary.lastAnomalyOccurrence
}
description={
props.showAlerts
? 'Last anomaly occurrence'
: 'Last sample anomaly occurrence'
}
title={isLoading ? '' : anomalySummary.lastAnomalyOccurrence}
description={getLastAnomalyOccurrenceWording(props.showAlerts)}
titleSize="s"
/>
</EuiFlexItem>
Expand All @@ -255,7 +239,7 @@ export const AnomalyDetailsChart = React.memo(
monitor={props.monitor}
showAlertsFlyout={() => setShowAlertsFlyout(true)}
totalAlerts={totalAlerts}
isLoading={props.isLoading}
isLoading={isLoading}
/>
</EuiFlexItem>
) : null}
Expand All @@ -269,7 +253,7 @@ export const AnomalyDetailsChart = React.memo(
opacity: showLoader ? 0.2 : 1,
}}
>
{props.isLoading || isLoadingAlerts ? (
{isLoading ? (
<EuiFlexGroup
justifyContent="spaceAround"
style={{ paddingTop: '150px' }}
Expand Down Expand Up @@ -373,11 +357,19 @@ export const AnomalyDetailsChart = React.memo(
{showAlertsFlyout ? (
<AlertsFlyout
// @ts-ignore
detectorId={props.detectorId}
detectorId={get(props.detector, 'id', '')}
// @ts-ignore
detectorName={props.detectorName}
detectorInterval={get(props, 'detectorInterval', 1)}
unit={get(props, 'unit', 'Minutes')}
detectorName={get(props.detector, 'name', '')}
detectorInterval={get(
props.detector,
'detectionInterval.period.interval',
1
)}
unit={get(
props.detector,
'detectionInterval.period.unit',
'Minutes'
)}
monitor={props.monitor}
onClose={() => setShowAlertsFlyout(false)}
/>
Expand Down
40 changes: 8 additions & 32 deletions public/pages/AnomalyCharts/containers/AnomalyHeatmapChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
sortHeatmapPlotData,
filterHeatmapPlotDataByY,
} from '../utils/anomalyChartUtils';
import { MIN_IN_MILLI_SECS } from '../../../../server/utils/constants';

interface AnomalyHeatmapChartProps {
title: string;
Expand Down Expand Up @@ -126,7 +127,7 @@ export const AnomalyHeatmapChart = React.memo(
originalHeatmapData
);

const [sortByFeildValue, setSortByFeildValue] = useState(
const [sortByFieldValue, setSortByFieldValue] = useState(
SORT_BY_FIELD_OPTIONS[0].value
);

Expand Down Expand Up @@ -198,11 +199,7 @@ export const AnomalyHeatmapChart = React.memo(

const selectedStartDate =
selectedEndDate -
getHeatmapCellDateRangeInterval(
//@ts-ignore
heatmapData[0].x,
selectedCellIndices[1]
);
get(selectedHeatmapData, '[0].cellTimeInterval', MIN_IN_MILLI_SECS);
props.onHeatmapCellSelected({
dateRange: {
startDate: selectedStartDate,
Expand All @@ -214,27 +211,6 @@ export const AnomalyHeatmapChart = React.memo(
}
};

const getHeatmapCellDateRangeInterval = (
dates: string[],
index: number
) => {
if (dates.length < 2) {
// if only less than 2 dates in X axis, it means the props.dateRange is too small.
// we can just use props.dateRange interval for heatmap cell
return props.dateRange.endDate - props.dateRange.startDate;
}
let prevIndex = index;
let nextIndex = index + 1;
if (nextIndex >= dates.length) {
nextIndex = index;
prevIndex = index - 1;
}
return (
moment(dates[nextIndex], HEATMAP_X_AXIS_DATE_FORMAT).valueOf() -
moment(dates[prevIndex], HEATMAP_X_AXIS_DATE_FORMAT).valueOf()
);
};

const getColorPaletteFlexItem = (hexCode: string) => {
return (
<EuiFlexItem grow={false} style={{ margin: '0px' }}>
Expand All @@ -255,7 +231,7 @@ export const AnomalyHeatmapChart = React.memo(
const updateHeatmapPlotData = getAnomaliesHeatmapData(
props.anomalies,
props.dateRange,
sortByFeildValue,
sortByFieldValue,
displayTopEntityNum
);
setOriginalHeatmapData(updateHeatmapPlotData);
Expand All @@ -279,7 +255,7 @@ export const AnomalyHeatmapChart = React.memo(
const updateHeatmapPlotData = getAnomaliesHeatmapData(
props.anomalies,
props.dateRange,
sortByFeildValue,
sortByFieldValue,
displayTopEntityNum
);
setOriginalHeatmapData(updateHeatmapPlotData);
Expand All @@ -302,7 +278,7 @@ export const AnomalyHeatmapChart = React.memo(
let selectedHeatmapData = filterHeatmapPlotDataByY(
originalHeatmapData[0],
selectedYs,
sortByFeildValue
sortByFieldValue
);
selectedHeatmapData.opacity = 1;

Expand All @@ -317,7 +293,7 @@ export const AnomalyHeatmapChart = React.memo(
};

const handleSortByFieldChange = (value: any) => {
setSortByFeildValue(value);
setSortByFieldValue(value);
const sortedHeatmapData = sortHeatmapPlotData(
heatmapData[0],
value,
Expand Down Expand Up @@ -373,7 +349,7 @@ export const AnomalyHeatmapChart = React.memo(
<EuiFlexItem style={{ minWidth: 150 }}>
<EuiSuperSelect
options={SORT_BY_FIELD_OPTIONS}
valueOfSelected={sortByFeildValue}
valueOfSelected={sortByFieldValue}
onChange={(value) => handleSortByFieldChange(value)}
hasDividers
/>
Expand Down
21 changes: 11 additions & 10 deletions public/pages/AnomalyCharts/containers/AnomalyOccurrenceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import { Monitor, Detector, DateRange } from '../../../models/interfaces';
import { AnomalyDetailsChart } from './AnomalyDetailsChart';
import { HeatmapCell } from './AnomalyHeatmapChart';
import { filterWithHeatmapFilter } from '../../utils/anomalyResultUtils';
import { getAnomalySummary } from '../utils/anomalyChartUtils';
import {
getAnomalySummary,
getDateRangeWithSelectedHeatmapCell,
} from '../utils/anomalyChartUtils';

interface AnomalyOccurrenceChartProps {
onDateRangeChange(
Expand Down Expand Up @@ -79,24 +82,22 @@ export const AnomalyOccurrenceChart = React.memo(
}
};

const getDateRange = () => {
if (props.isHCDetector && props.selectedHeatmapCell) {
return props.selectedHeatmapCell.dateRange;
}
return props.dateRange;
};
return (
<ContentPanel title={props.title}>
<AnomalyDetailsChart
dateRange={getDateRange()}
dateRange={getDateRangeWithSelectedHeatmapCell(
props.dateRange,
props.isHCDetector,
props.selectedHeatmapCell
)}
onDateRangeChange={props.onDateRangeChange}
onZoomRangeChange={props.onZoomRangeChange}
anomalies={getAnomaliesForChart()}
bucketizedAnomalies={props.bucketizedAnomalies}
anomalySummary={getAnomalySummaryForChart()}
isLoading={props.isLoading}
anomalyGradeSeriesName="Anomaly grade"
confidenceSeriesName="Confidence"
anomalyGradeSeriesName={props.anomalyGradeSeriesName}
confidenceSeriesName={props.confidenceSeriesName}
showAlerts={props.showAlerts}
detectorId={props.detector ? props.detector.id : ''}
detectorName={props.detector ? props.detector.name : ''}
Expand Down
Loading