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