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