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