From f32111c49182a4dc5034843e8806e9e0560932b4 Mon Sep 17 00:00:00 2001 From: Ludek Novy <13610612+ludeknovy@users.noreply.github.com> Date: Sun, 27 Feb 2022 22:08:16 +0100 Subject: [PATCH] Download request stats as excel (#236) --- package-lock.json | 98 +++++++++++++++++++ package.json | 2 + src/app/_services/excel.service.ts | 27 +++++ src/app/item-detail/item-detail.module.ts | 4 +- .../request-stats-compare.component.html | 11 ++- .../request-stats-compare.component.spec.ts | 5 +- .../request-stats-compare.component.ts | 9 ++ .../stats-compare/stats-compare.component.css | 5 - .../stats-compare.component.html | 2 +- 9 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 src/app/_services/excel.service.ts diff --git a/package-lock.json b/package-lock.json index 4f2e99d8..7fda0269 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3470,6 +3470,14 @@ "regex-parser": "^2.2.11" } }, + "adler-32": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.0.tgz", + "integrity": "sha512-f5nltvjl+PRUh6YNfUstRaXwJxtfnKEWhAWWlmKvh+Y3J2+98a0KKVYDEhz6NdKGqswLhjNGznxfSsZGOvOd9g==", + "requires": { + "printj": "~1.2.2" + } + }, "adm-zip": { "version": "0.4.16", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", @@ -4392,6 +4400,23 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "cfb": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.1.tgz", + "integrity": "sha512-wT2ScPAFGSVy7CY+aauMezZBnNrfnaLSrxHUHdea+Td/86vrk6ZquggV+ssBR88zNs0OnBkL2+lf9q0K+zVGzQ==", + "requires": { + "adler-32": "~1.3.0", + "crc-32": "~1.2.0", + "printj": "~1.3.0" + }, + "dependencies": { + "printj": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz", + "integrity": "sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg==" + } + } + }, "chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", @@ -4625,6 +4650,11 @@ } } }, + "codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==" + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -4929,6 +4959,22 @@ "yaml": "^1.10.0" } }, + "crc-32": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.1.tgz", + "integrity": "sha512-Dn/xm/1vFFgs3nfrpEVScHoIslO9NZRITWGz/1E/St6u4xw99vfZzVkW0OSnzx2h9egej9xwMCEut6sqwokM/w==", + "requires": { + "exit-on-epipe": "~1.0.1", + "printj": "~1.3.1" + }, + "dependencies": { + "printj": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.3.1.tgz", + "integrity": "sha512-GA3TdL8szPK4AQ2YnOe/b+Y1jUFwmmGMMK/qbY7VcE3Z7FU8JstbKiKRzO6CIiAKPhTO8m01NoQ0V5f3jc4OGg==" + } + } + }, "critters": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.12.tgz", @@ -6449,6 +6495,11 @@ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", "dev": true }, + "exit-on-epipe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz", + "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==" + }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -6715,6 +6766,11 @@ "flat-cache": "^3.0.4" } }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -6849,6 +6905,11 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" }, + "frac": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz", + "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==" + }, "fragment-cache": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", @@ -11834,6 +11895,11 @@ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==" }, + "printj": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/printj/-/printj-1.2.3.tgz", + "integrity": "sha512-sanczS6xOJOg7IKDvi4sGOUOe7c1tsEzjwlLFH/zgwx/uyImVM9/rgBkc8AfiQa/Vg54nRd8mkm9yI7WV/O+WA==" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -13496,6 +13562,14 @@ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", "dev": true }, + "ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "requires": { + "frac": "~1.1.2" + } + }, "sshpk": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", @@ -15006,6 +15080,16 @@ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==" }, + "wmf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz", + "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==" + }, + "word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==" + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -15048,6 +15132,20 @@ "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", "dev": true }, + "xlsx": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.2.tgz", + "integrity": "sha512-BWLS+GO5yg5Hnro8mpbNkZq/a+dZ8689otFuHmb9wgCtiMpL+I9dpc+Sans6K9yYxTLEZ235Kr/JkmoTEMunzQ==", + "requires": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + } + }, "xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", diff --git a/package.json b/package.json index 9c9d6848..25511816 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "chart.js": "^2.9.3", "core-js": "^2.5.4", "deepmerge": "^4.2.2", + "file-saver": "^2.0.5", "highcharts": "^9.2.2", "highcharts-angular": "^2.10.0", "html2canvas": "^1.4.1", @@ -42,6 +43,7 @@ "node-sass": "^4.14.1", "rxjs": "^6.0.0", "tslib": "^2.0.0", + "xlsx": "^0.18.2", "zone.js": "~0.11.4" }, "devDependencies": { diff --git a/src/app/_services/excel.service.ts b/src/app/_services/excel.service.ts new file mode 100644 index 00000000..e69cc0a6 --- /dev/null +++ b/src/app/_services/excel.service.ts @@ -0,0 +1,27 @@ +import { Injectable } from "@angular/core"; +import * as FileSaver from "file-saver"; +import * as XLSX from "xlsx"; + +const EXCEL_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"; +const EXCEL_EXTENSION = ".xlsx"; + +@Injectable() +export class ExcelService { + + + public exportAsExcelFile(json: unknown[], excelFileName: string): void { + + const myworksheet: XLSX.WorkSheet = XLSX.utils.json_to_sheet(json); + const myworkbook: XLSX.WorkBook = { Sheets: { "data": myworksheet }, SheetNames: ["data"] }; + const excelBuffer: BlobPart = XLSX.write(myworkbook, { bookType: "xlsx", type: "array" }); + this.saveAsExcelFile(excelBuffer, excelFileName); + } + + private saveAsExcelFile(buffer: BlobPart, fileName: string): void { + const data: Blob = new Blob([buffer], { + type: EXCEL_TYPE + }); + FileSaver.saveAs(data, fileName + EXCEL_EXTENSION); + } + +} \ No newline at end of file diff --git a/src/app/item-detail/item-detail.module.ts b/src/app/item-detail/item-detail.module.ts index 1de54fed..5aac1082 100644 --- a/src/app/item-detail/item-detail.module.ts +++ b/src/app/item-detail/item-detail.module.ts @@ -24,6 +24,7 @@ import { CreateNewShareLinkComponent } from "./share/create-new-share-link/creat import { MonitoringStatsComponent } from "./monitoring-stats/monitoring-stats.component"; import { DataTableModule } from "@pascalhonegger/ng-datatable"; import { RoleModule } from "../_directives/role.module"; +import { ExcelService } from "../_services/excel.service"; const routes: Routes = [ { @@ -41,6 +42,7 @@ const routes: Routes = [ { CommonModule, NgbModule, RouterModule.forRoot(routes), DataTableModule, SharedItemModule, SharedModule, HighchartsChartModule, ReactiveFormsModule, FormsModule, RoleModule ], - exports: [] + exports: [], + providers: [ExcelService] }) export class ItemDetailModule { } diff --git a/src/app/item-detail/request-stats/request-stats-compare.component.html b/src/app/item-detail/request-stats/request-stats-compare.component.html index 0728a30a..0c154d53 100644 --- a/src/app/item-detail/request-stats/request-stats-compare.component.html +++ b/src/app/item-detail/request-stats/request-stats-compare.component.html @@ -22,12 +22,15 @@
Request Statistics diff --git a/src/app/item-detail/request-stats/request-stats-compare.component.spec.ts b/src/app/item-detail/request-stats/request-stats-compare.component.spec.ts index e72f2117..10fe75ea 100644 --- a/src/app/item-detail/request-stats/request-stats-compare.component.spec.ts +++ b/src/app/item-detail/request-stats/request-stats-compare.component.spec.ts @@ -5,6 +5,7 @@ import { NgbModule } from "@ng-bootstrap/ng-bootstrap"; import { DataTableModule } from "@pascalhonegger/ng-datatable"; import { HighchartsChartModule } from "highcharts-angular"; import { ToastrModule } from "ngx-toastr"; +import { ExcelService } from "src/app/_services/excel.service"; import { LabelTrendComponent } from "../label-trend/label-trend.component"; import { StatsCompareComponent } from "../stats-compare/stats-compare.component"; import { LabelHealthComponent } from "./label-health/label-health.component"; @@ -30,8 +31,8 @@ describe("RequestStatsCompareComponent", () => { HighchartsChartModule, HttpClientModule, RouterTestingModule, - - ] + ], + providers: [ExcelService] }) .compileComponents(); })); diff --git a/src/app/item-detail/request-stats/request-stats-compare.component.ts b/src/app/item-detail/request-stats/request-stats-compare.component.ts index b659905e..5ad75113 100644 --- a/src/app/item-detail/request-stats/request-stats-compare.component.ts +++ b/src/app/item-detail/request-stats/request-stats-compare.component.ts @@ -6,6 +6,7 @@ import { ItemsApiService } from "src/app/items-api.service"; import { ToastrService } from "ngx-toastr"; import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; import html2canvas from "html2canvas"; +import { ExcelService } from "src/app/_services/excel.service"; @@ -38,6 +39,7 @@ export class RequestStatsCompareComponent implements OnInit, OnDestroy { private toastr: ToastrService, private analyzeChartService: AnalyzeChartService, private modalService: NgbModal, + private excelService: ExcelService, ) { } @@ -272,4 +274,11 @@ export class RequestStatsCompareComponent implements OnInit, OnDestroy { this.downloadLink.nativeElement.click(); }); } + + downloadAsXLXS() { + const dataToBeSaved = this.labelsData.map(({ + n0: p90, n5: p95, n9: p99, label: label, statusCodes: statusCodes, responseMessageFailures, ...rest }) => + ({ label, p90, p95, p99, ...rest })) + this.excelService.exportAsExcelFile(dataToBeSaved, `request-stats-${this.params.id}`) + } } diff --git a/src/app/item-detail/stats-compare/stats-compare.component.css b/src/app/item-detail/stats-compare/stats-compare.component.css index ba3d58cd..9902f928 100644 --- a/src/app/item-detail/stats-compare/stats-compare.component.css +++ b/src/app/item-detail/stats-compare/stats-compare.component.css @@ -21,8 +21,3 @@ thead { white-space: nowrap; overflow: hidden; } - -.compare-desc { - font-size: 13px; - font-weight: 400; -} diff --git a/src/app/item-detail/stats-compare/stats-compare.component.html b/src/app/item-detail/stats-compare/stats-compare.component.html index 803aded0..2799a4d1 100644 --- a/src/app/item-detail/stats-compare/stats-compare.component.html +++ b/src/app/item-detail/stats-compare/stats-compare.component.html @@ -100,4 +100,4 @@ - +