/* * SPDX-License-Identifier: Apache-2.0 * * The OpenSearch Contributors require contributions made to * this file be licensed under the Apache-2.0 license or a * compatible open source license. * * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ import { Chart, Axis, LineSeries, RectAnnotation, niceTimeFormatter, Position, Settings, ScaleType, LineAnnotation, AnnotationDomainType, } from '@elastic/charts'; import { EuiText, EuiLink, EuiButton, EuiIcon } from '@elastic/eui'; import React, { useState, Fragment } from 'react'; import ContentPanel from '../../../../components/ContentPanel/ContentPanel'; import { useDelayedLoader } from '../../../../hooks/useDelayedLoader'; import { FeatureAggregationData, FeatureAttributes, DateRange, FEATURE_TYPE, Schedule, EntityData, } from '../../../../models/interfaces'; import { darkModeEnabled } from '../../../../utils/opensearchDashboardsUtils'; import { prepareDataForChart, getFeatureMissingDataAnnotations, flattenData, convertToEntityString, } from '../../../utils/anomalyResultUtils'; import { CodeModal } from '../../../DetectorConfig/components/CodeModal/CodeModal'; import { CHART_FIELDS, CHART_COLORS, FEATURE_CHART_THEME, } from '../../utils/constants'; import { get } from 'lodash'; import { ENTITY_COLORS } from '../../../DetectorResults/utils/constants'; interface FeatureChartProps { feature: FeatureAttributes; featureData: FeatureAggregationData[][]; annotations: any[][]; isLoading: boolean; dateRange: DateRange; featureType: FEATURE_TYPE; field?: string; aggregationMethod?: string; aggregationQuery?: string; featureDataSeriesName: string; edit?: boolean; onEdit?(): void; detectorInterval: Schedule; showFeatureMissingDataPointAnnotation?: boolean; detectorEnabledTime?: number; rawFeatureData: FeatureAggregationData[][]; entityData: EntityData[][]; isHCDetector?: boolean; windowDelay: Schedule; } export const FeatureChart = (props: FeatureChartProps) => { const getDisabledChartBackground = () => darkModeEnabled() ? '#25262E' : '#F0F0F0'; const [showCustomExpression, setShowCustomExpression] = useState(false); const timeFormatter = niceTimeFormatter([ props.dateRange.startDate, props.dateRange.endDate, ]); const showLoader = useDelayedLoader(props.isLoading); const featureDescription = () => ( {props.featureType === FEATURE_TYPE.SIMPLE ? ( Field: {props.field} Aggregation method: {props.aggregationMethod} State: {props.feature.featureEnabled ? 'Enabled' : 'Disabled'} ) : ( Custom expression:{' '} setShowCustomExpression(true)}> View code State: {props.feature.featureEnabled ? 'Enabled' : 'Disabled'} )} ); // return undefined if featureMissingDataPointAnnotationStartDate is missing // OR it is even behind the specified date range const getFeatureMissingAnnotationDateRange = ( dateRange: DateRange, featureMissingDataPointAnnotationStartDate?: number ) => { if ( featureMissingDataPointAnnotationStartDate && dateRange.endDate > featureMissingDataPointAnnotationStartDate ) { return { startDate: Math.max( dateRange.startDate, featureMissingDataPointAnnotationStartDate ), endDate: dateRange.endDate, }; } return undefined; }; const featureData = prepareDataForChart( props.featureData, props.dateRange ) as FeatureAggregationData[][]; const multipleTimeSeries = get(featureData, 'length', 0) > 1; return ( Edit feature ) : null } >
{/** * Charts may show stale data even after feature data is changed. * By setting the key prop here, the chart will re-mount whenever the * feature data has changed. */} {/** * props.annotations is 2-dimensional, and contains an array of annotations * per time series to show on the charts. We flatten all anomaly annotations into * a 1-D array, and show on all enabled feature line charts. * * Note that feature attribution is supported on the backend as of 1.2 - future * frontend improvements may change the data model of generated annotations, * and thus show different annotations per feature chart (currently all annotations * shown equally across all enabled feature charts for a given detector). */} {props.feature.featureEnabled ? ( ) : null} {props.feature.featureEnabled && props.showFeatureMissingDataPointAnnotation && props.detectorEnabledTime ? [ } style={{ line: { stroke: 'red', strokeWidth: 1, opacity: 0.8 }, }} />, ] : null} { // Add each set of feature data as a separate time series } {featureData.map( (featureTimeSeries: FeatureAggregationData[], index) => { const timeSeriesList: any[] = []; const seriesKey = props.isHCDetector ? `${props.featureDataSeriesName} (${convertToEntityString( props.entityData[index], ', ' )})` : props.featureDataSeriesName; timeSeriesList.push( ); if ( featureTimeSeries.map((item: FeatureAggregationData) => { if (item.hasOwnProperty('expectedValue')) { timeSeriesList.push( ); } }) ) return timeSeriesList; } )} {showCustomExpression ? ( true} closeModal={() => setShowCustomExpression(false)} /> ) : null}
); };