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