24
24
* permissions and limitations under the License.
25
25
*/
26
26
27
- import React , { useState } from 'react' ;
28
-
27
+ import React , { useState , useEffect } from 'react' ;
29
28
import moment from 'moment' ;
30
29
import Plotly , { PlotData } from 'plotly.js-dist' ;
31
30
import plotComponentFactory from 'react-plotly.js/factory' ;
@@ -54,13 +53,25 @@ import {
54
53
filterHeatmapPlotDataByY ,
55
54
getEntitytAnomaliesHeatmapData ,
56
55
} from '../utils/anomalyChartUtils' ;
57
- import { MIN_IN_MILLI_SECS } from '../../../../server/utils/constants' ;
58
- import { EntityAnomalySummaries } from '../../../../server/models/interfaces' ;
56
+ import {
57
+ MIN_IN_MILLI_SECS ,
58
+ ENTITY_LIST_DELIMITER ,
59
+ } from '../../../../server/utils/constants' ;
60
+ import {
61
+ EntityAnomalySummaries ,
62
+ Entity ,
63
+ } from '../../../../server/models/interfaces' ;
64
+ import { HEATMAP_CHART_Y_AXIS_WIDTH } from '../utils/constants' ;
65
+ import {
66
+ convertToEntityList ,
67
+ convertToCategoryFieldString ,
68
+ } from '../../utils/anomalyResultUtils' ;
59
69
60
70
interface AnomalyHeatmapChartProps {
61
- title : string ;
62
71
detectorId : string ;
63
72
detectorName : string ;
73
+ detectorTaskProgress ?: number ;
74
+ isHistorical ?: boolean ;
64
75
anomalies ?: any [ ] ;
65
76
dateRange : DateRange ;
66
77
isLoading : boolean ;
@@ -72,11 +83,13 @@ interface AnomalyHeatmapChartProps {
72
83
heatmapDisplayOption ?: HeatmapDisplayOption ;
73
84
entityAnomalySummaries ?: EntityAnomalySummaries [ ] ;
74
85
isNotSample ?: boolean ;
86
+ categoryField ?: string [ ] ;
75
87
}
76
88
77
89
export interface HeatmapCell {
78
90
dateRange : DateRange ;
79
- entityValue : string ;
91
+ entityList : Entity [ ] ;
92
+ modelId ?: string ;
80
93
}
81
94
82
95
export interface HeatmapDisplayOption {
@@ -123,14 +136,15 @@ export const AnomalyHeatmapChart = React.memo(
123
136
//@ts -ignore
124
137
individualEntities = inputHeatmapData [ 0 ] . y . filter (
125
138
//@ts -ignore
126
- ( entityValue ) => entityValue && entityValue . trim ( ) . length > 0
139
+ ( entityListAsString ) =>
140
+ entityListAsString && entityListAsString . trim ( ) . length > 0
127
141
) ;
128
142
}
129
143
const individualEntityOptions = [ ] as any [ ] ;
130
144
//@ts -ignore
131
- individualEntities . forEach ( ( entityValue ) => {
145
+ individualEntities . forEach ( ( entityListAsString : string ) => {
132
146
individualEntityOptions . push ( {
133
- label : entityValue ,
147
+ label : entityListAsString . replace ( ENTITY_LIST_DELIMITER , ', ' ) ,
134
148
} ) ;
135
149
} ) ;
136
150
@@ -165,7 +179,7 @@ export const AnomalyHeatmapChart = React.memo(
165
179
getEntitytAnomaliesHeatmapData (
166
180
props . dateRange ,
167
181
props . entityAnomalySummaries ,
168
- props . heatmapDisplayOption . entityOption . value
182
+ props . heatmapDisplayOption ? .entityOption . value
169
183
)
170
184
: // use anomalies data in case of sample result
171
185
getAnomaliesHeatmapData (
@@ -184,7 +198,7 @@ export const AnomalyHeatmapChart = React.memo(
184
198
AnomalyHeatmapSortType
185
199
> (
186
200
props . isNotSample
187
- ? props . heatmapDisplayOption . sortType
201
+ ? props . heatmapDisplayOption ? .sortType
188
202
: SORT_BY_FIELD_OPTIONS [ 0 ] . value
189
203
) ;
190
204
@@ -215,9 +229,25 @@ export const AnomalyHeatmapChart = React.memo(
215
229
return false ;
216
230
} ;
217
231
232
+ // Custom hook to refresh all of the heatmap data when running a historical task
233
+ useEffect ( ( ) => {
234
+ if ( props . isHistorical ) {
235
+ const updateHeatmapPlotData = getAnomaliesHeatmapData (
236
+ props . anomalies ,
237
+ props . dateRange ,
238
+ sortByFieldValue ,
239
+ get ( COMBINED_OPTIONS . options [ 0 ] , 'value' )
240
+ ) ;
241
+ setOriginalHeatmapData ( updateHeatmapPlotData ) ;
242
+ setHeatmapData ( updateHeatmapPlotData ) ;
243
+ setNumEntities ( updateHeatmapPlotData [ 0 ] . y . length ) ;
244
+ setEntityViewOptions ( getViewEntityOptions ( updateHeatmapPlotData ) ) ;
245
+ }
246
+ } , [ props . detectorTaskProgress ] ) ;
247
+
218
248
const handleHeatmapClick = ( event : Plotly . PlotMouseEvent ) => {
219
249
const selectedCellIndices = get ( event , 'points[0].pointIndex' , [ ] ) ;
220
- const selectedEntity = get ( event , 'points[0].y' , '' ) ;
250
+ const selectedEntityString = get ( event , 'points[0].y' , '' ) ;
221
251
if ( ! isEmpty ( selectedCellIndices ) ) {
222
252
let anomalyCount = get ( event , 'points[0].text' , 0 ) ;
223
253
if (
@@ -262,7 +292,11 @@ export const AnomalyHeatmapChart = React.memo(
262
292
startDate : selectedStartDate ,
263
293
endDate : selectedEndDate ,
264
294
} ,
265
- entityValue : selectedEntity ,
295
+ entityList : convertToEntityList (
296
+ selectedEntityString ,
297
+ get ( props , 'categoryField' , [ ] ) ,
298
+ ENTITY_LIST_DELIMITER
299
+ ) ,
266
300
} as HeatmapCell ) ;
267
301
}
268
302
}
@@ -345,7 +379,7 @@ export const AnomalyHeatmapChart = React.memo(
345
379
346
380
setNumEntities ( nonCombinedOptions . length ) ;
347
381
const selectedYs = nonCombinedOptions . map ( ( option ) =>
348
- get ( option , 'label' , '' )
382
+ get ( option , 'label' , '' ) . replace ( ', ' , ENTITY_LIST_DELIMITER )
349
383
) ;
350
384
351
385
let selectedHeatmapData = filterHeatmapPlotDataByY (
@@ -407,40 +441,53 @@ export const AnomalyHeatmapChart = React.memo(
407
441
</ EuiFlexItem >
408
442
</ EuiFlexGroup >
409
443
< EuiFlexGroup style = { { padding : '0px' } } >
410
- < EuiFlexItem grow = { false } style = { { minWidth : '80px' } } >
411
- < EuiFlexGroup alignItems = "center" justifyContent = "flexEnd" >
412
- < EuiText textAlign = "right" >
413
- < h4 > { props . title } </ h4 >
414
- </ EuiText >
415
- </ EuiFlexGroup >
416
- </ EuiFlexItem >
417
444
< EuiFlexItem style = { { paddingLeft : '5px' } } >
418
445
< EuiFlexGroup
419
446
style = { { padding : '0px' } }
420
447
justifyContent = "spaceBetween"
421
448
>
422
- < EuiFlexItem grow = { false } style = { { marginLeft : '0px' } } >
423
- < EuiFlexGroup gutterSize = "s" alignItems = "center" >
424
- < EuiFlexItem style = { { minWidth : 300 } } >
425
- < EuiComboBox
426
- placeholder = "Select options"
427
- options = { entityViewOptions }
428
- selectedOptions = { currentViewOptions }
429
- onChange = { ( selectedOptions ) =>
430
- handleViewEntityOptionsChange ( selectedOptions )
431
- }
432
- />
433
- </ EuiFlexItem >
434
- < EuiFlexItem style = { { minWidth : 150 } } >
435
- < EuiSuperSelect
436
- options = { SORT_BY_FIELD_OPTIONS }
437
- valueOfSelected = { sortByFieldValue }
438
- onChange = { ( value ) => handleSortByFieldChange ( value ) }
439
- hasDividers
440
- />
441
- </ EuiFlexItem >
442
- </ EuiFlexGroup >
443
- </ EuiFlexItem >
449
+ < EuiFlexGroup
450
+ direction = "column"
451
+ justifyContent = "spaceBetween"
452
+ style = { { padding : '12px' , marginBottom : '6px' } }
453
+ >
454
+ < EuiFlexItem style = { { marginBottom : '0px' } } >
455
+ < EuiText >
456
+ < h4 >
457
+ View by:
458
+ < b >
459
+ { convertToCategoryFieldString (
460
+ get ( props , 'categoryField' , [ ] ) as string [ ] ,
461
+ ', '
462
+ ) }
463
+ </ b >
464
+ </ h4 >
465
+ </ EuiText >
466
+ </ EuiFlexItem >
467
+
468
+ < EuiFlexItem grow = { false } >
469
+ < EuiFlexGroup gutterSize = "s" alignItems = "center" >
470
+ < EuiFlexItem style = { { minWidth : 300 } } >
471
+ < EuiComboBox
472
+ placeholder = "Select options"
473
+ options = { entityViewOptions }
474
+ selectedOptions = { currentViewOptions }
475
+ onChange = { ( selectedOptions ) =>
476
+ handleViewEntityOptionsChange ( selectedOptions )
477
+ }
478
+ />
479
+ </ EuiFlexItem >
480
+ < EuiFlexItem style = { { minWidth : 150 } } >
481
+ < EuiSuperSelect
482
+ options = { SORT_BY_FIELD_OPTIONS }
483
+ valueOfSelected = { sortByFieldValue }
484
+ onChange = { ( value ) => handleSortByFieldChange ( value ) }
485
+ hasDividers
486
+ />
487
+ </ EuiFlexItem >
488
+ </ EuiFlexGroup >
489
+ </ EuiFlexItem >
490
+ </ EuiFlexGroup >
444
491
< EuiFlexItem grow = { false } >
445
492
< EuiFlexGroup alignItems = "center" >
446
493
< EuiFlexItem >
@@ -542,6 +589,15 @@ export const AnomalyHeatmapChart = React.memo(
542
589
showline : true ,
543
590
showgrid : false ,
544
591
fixedrange : true ,
592
+ automargin : true ,
593
+ tickmode : 'array' ,
594
+ tickvals : heatmapData [ 0 ] . y ,
595
+ ticktext : heatmapData [ 0 ] . y . map ( ( label : string ) =>
596
+ label . length <= HEATMAP_CHART_Y_AXIS_WIDTH
597
+ ? label
598
+ : label . substring ( 0 , HEATMAP_CHART_Y_AXIS_WIDTH - 3 ) +
599
+ '...'
600
+ ) ,
545
601
} ,
546
602
margin : {
547
603
l : 100 ,
0 commit comments