]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/blob
607a3b7d51ad7221334e35b639f475b5f9b8e8a5
[ceph-ci.git] /
1 import { Component, Input, ViewChild, OnChanges, SimpleChanges } from '@angular/core';
2
3 import { CssHelper } from '~/app/shared/classes/css-helper';
4 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
5 import { DimlessBinaryPerSecondPipe } from '~/app/shared/pipes/dimless-binary-per-second.pipe';
6 import { FormatterService } from '~/app/shared/services/formatter.service';
7 import { BaseChartDirective } from 'ng2-charts';
8 import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe';
9 import { NumberFormatterService } from '~/app/shared/services/number-formatter.service';
10 import 'chartjs-adapter-moment';
11
12 @Component({
13   selector: 'cd-dashboard-area-chart',
14   templateUrl: './dashboard-area-chart.component.html',
15   styleUrls: ['./dashboard-area-chart.component.scss']
16 })
17 export class DashboardAreaChartComponent implements OnChanges {
18   @ViewChild(BaseChartDirective) chart: BaseChartDirective;
19
20   @Input()
21   chartTitle: string;
22   @Input()
23   maxValue?: number;
24   @Input()
25   dataUnits: string;
26   @Input()
27   dataArray?: Array<Array<[number, string]>>; // Array of query results
28   @Input()
29   labelsArray?: string[] = []; // Array of chart labels
30   @Input()
31   decimals?: number = 1;
32   @Input()
33   truncateLabel = false;
34
35   currentDataUnits: string;
36   currentData: number;
37   maxConvertedValue?: number;
38   maxConvertedValueUnits?: string;
39
40   chartDataUnits: string;
41   chartData: any = { dataset: [] };
42   options: any = {};
43   currentChartData: any = {};
44
45   chartColors: any[] = [
46     [
47       this.cssHelper.propertyValue('chart-color-strong-blue'),
48       this.cssHelper.propertyValue('chart-color-translucent-blue')
49     ],
50     [
51       this.cssHelper.propertyValue('chart-color-orange'),
52       this.cssHelper.propertyValue('chart-color-translucent-orange')
53     ],
54     [
55       this.cssHelper.propertyValue('chart-color-green'),
56       this.cssHelper.propertyValue('chart-color-translucent-green')
57     ],
58     [
59       this.cssHelper.propertyValue('chart-color-cyan'),
60       this.cssHelper.propertyValue('chart-color-translucent-cyan')
61     ],
62     [
63       this.cssHelper.propertyValue('chart-color-purple'),
64       this.cssHelper.propertyValue('chart-color-translucent-purple')
65     ],
66     [
67       this.cssHelper.propertyValue('chart-color-red'),
68       this.cssHelper.propertyValue('chart-color-translucent-red')
69     ]
70   ];
71
72   public chartAreaBorderPlugin: any[] = [
73     {
74       beforeDraw(chart: any) {
75         if (!chart.options.plugins.borderArea) {
76           return;
77         }
78         const {
79           ctx,
80           chartArea: { left, top, width, height }
81         } = chart;
82         ctx.save();
83         ctx.strokeStyle = chart.options.plugins.chartAreaBorder.borderColor;
84         ctx.lineWidth = chart.options.plugins.chartAreaBorder.borderWidth;
85         ctx.setLineDash(chart.options.plugins.chartAreaBorder.borderDash || []);
86         ctx.lineDashOffset = chart.options.plugins.chartAreaBorder.borderDashOffset;
87         ctx.strokeRect(left, top, width, height);
88         ctx.restore();
89       }
90     }
91   ];
92
93   constructor(
94     private cssHelper: CssHelper,
95     private dimlessBinary: DimlessBinaryPipe,
96     private dimlessBinaryPerSecond: DimlessBinaryPerSecondPipe,
97     private dimlessPipe: DimlessPipe,
98     private formatter: FormatterService,
99     private numberFormatter: NumberFormatterService
100   ) {
101     this.options = {
102       plugins: {
103         legend: {
104           display: false
105         },
106         tooltip: {
107           mode: 'index',
108           external: function (tooltipModel: any) {
109             tooltipModel.tooltip.x = 10;
110             tooltipModel.tooltip.y = 0;
111           }.bind(this),
112           intersect: false,
113           displayColors: true,
114           backgroundColor: this.cssHelper.propertyValue('chart-color-tooltip-background'),
115           callbacks: {
116             title: function (tooltipItem: any): any {
117               return tooltipItem[0].xLabel;
118             },
119             label: (context: any) => {
120               return (
121                 ' ' +
122                 context.dataset.label +
123                 ' - ' +
124                 context.formattedValue +
125                 ' ' +
126                 this.chartDataUnits
127               );
128             }
129           }
130         },
131         borderArea: true,
132         chartAreaBorder: {
133           borderColor: this.cssHelper.propertyValue('chart-color-slight-dark-gray'),
134           borderWidth: 1
135         }
136       },
137       responsive: true,
138       maintainAspectRatio: false,
139       animation: false,
140       elements: {
141         point: {
142           radius: 0
143         }
144       },
145       hover: {
146         intersect: false
147       },
148       scales: {
149         x: {
150           display: false,
151           type: 'time',
152           grid: {
153             display: false
154           },
155           time: {
156             tooltipFormat: 'DD/MM/YYYY - HH:mm:ss'
157           }
158         },
159         y: {
160           afterFit: (scaleInstance: any) => (scaleInstance.width = 100),
161           grid: {
162             display: false
163           },
164           beginAtZero: true,
165           ticks: {
166             maxTicksLimit: 4
167           }
168         }
169       }
170     };
171   }
172
173   ngOnChanges(changes: SimpleChanges): void {
174     this.updateChartData(changes);
175   }
176
177   ngAfterViewInit() {
178     this.updateChartData(null);
179   }
180
181   private updateChartData(changes: SimpleChanges): void {
182     for (let index = 0; index < this.labelsArray.length; index++) {
183       const colorIndex = index % this.chartColors.length;
184       this.chartData.dataset[index] = {
185         label: '',
186         data: [],
187         tension: 0.2,
188         pointBackgroundColor: this.chartColors[colorIndex][0],
189         backgroundColor: this.chartColors[colorIndex][1],
190         borderColor: this.chartColors[colorIndex][0],
191         borderWidth: 1,
192         fill: {
193           target: 'origin'
194         }
195       };
196       this.chartData.dataset[index].label = this.labelsArray[index];
197     }
198
199     this.setChartTicks();
200
201     if (this.dataArray && this.dataArray.length && this.dataArray[0] && this.dataArray[0].length) {
202       this.dataArray = changes?.dataArray?.currentValue || this.dataArray;
203       this.currentChartData = this.chartData;
204       for (let index = 0; index < this.dataArray.length; index++) {
205         this.chartData.dataset[index].data = this.formatData(this.dataArray[index]);
206         let currentDataValue = this.dataArray?.[index]?.[this.dataArray[index]?.length - 1]
207           ? this.dataArray[index][this.dataArray[index]?.length - 1][1]
208           : 0;
209         if (currentDataValue) {
210           [
211             this.currentChartData.dataset[index]['currentData'],
212             this.currentChartData.dataset[index]['currentDataUnits']
213           ] = this.convertUnits(currentDataValue).split(' ');
214           [this.maxConvertedValue, this.maxConvertedValueUnits] = this.convertUnits(
215             this.maxValue
216           ).split(' ');
217         }
218       }
219     }
220
221     if (this.chart) {
222       this.chart.chart.update();
223     }
224   }
225
226   private formatData(array: Array<any>): any {
227     let formattedData = {};
228     formattedData = array?.map((data: any) => ({
229       x: data[0] * 1000,
230       y: Number(this.convertToChartDataUnits(data[1]).replace(/[^\d,.]+/g, ''))
231     }));
232     return formattedData;
233   }
234
235   private convertToChartDataUnits(data: any): any {
236     let dataWithUnits: string = '';
237     if (this.chartDataUnits !== null) {
238       if (this.dataUnits === 'B') {
239         dataWithUnits = this.numberFormatter.formatBytesFromTo(
240           data,
241           this.dataUnits,
242           this.chartDataUnits,
243           this.decimals
244         );
245       } else if (this.dataUnits === 'B/s') {
246         dataWithUnits = this.numberFormatter.formatBytesPerSecondFromTo(
247           data,
248           this.dataUnits,
249           this.chartDataUnits,
250           this.decimals
251         );
252       } else if (this.dataUnits === 'ms') {
253         dataWithUnits = this.numberFormatter.formatSecondsFromTo(
254           data,
255           this.dataUnits,
256           this.chartDataUnits,
257           this.decimals
258         );
259       } else {
260         dataWithUnits = this.numberFormatter.formatUnitlessFromTo(
261           data,
262           this.dataUnits,
263           this.chartDataUnits,
264           this.decimals
265         );
266       }
267     }
268     return dataWithUnits;
269   }
270
271   private convertUnits(data: any): any {
272     let dataWithUnits: string = '';
273     if (this.dataUnits === 'B') {
274       dataWithUnits = this.dimlessBinary.transform(data, this.decimals);
275     } else if (this.dataUnits === 'B/s') {
276       dataWithUnits = this.dimlessBinaryPerSecond.transform(data, this.decimals);
277     } else if (this.dataUnits === 'ms') {
278       dataWithUnits = this.formatter.format_number(data, 1000, ['ms', 's'], this.decimals);
279     } else {
280       dataWithUnits = this.dimlessPipe.transform(data, this.decimals);
281     }
282     return dataWithUnits;
283   }
284
285   private setChartTicks() {
286     if (!this.chart) {
287       this.chartDataUnits = '';
288       return;
289     }
290
291     let maxValue = 0;
292     let maxValueDataUnits = '';
293
294     const allDataValues = this.dataArray.reduce((array: string[], data) => {
295       return array.concat(data?.map((values: [number, string]) => values[1]));
296     }, []);
297
298     maxValue = Math.max(...allDataValues.map(Number));
299     [maxValue, maxValueDataUnits] = this.convertUnits(maxValue).split(' ');
300
301     const yAxesTicks = this.chart.chart.options.scales.y;
302     yAxesTicks.ticks.callback = (value: any) => {
303       if (value === 0) {
304         return null;
305       }
306       if (!maxValueDataUnits) {
307         return `${value}`;
308       }
309       return `${value} ${maxValueDataUnits}`;
310     };
311     this.chartDataUnits = maxValueDataUnits || '';
312     this.chart.chart.update();
313   }
314 }