From 5793fbd38788bcf88cdca6b87975f35c5f76581f Mon Sep 17 00:00:00 2001 From: CeciliaLiu8 Date: Thu, 4 Apr 2024 13:07:05 -0500 Subject: [PATCH] MAT-6819 multiple populations --- package-lock.json | 17 +- package.json | 2 +- src/controllers/export.controller.spec.ts | 1 + src/controllers/export.controller.ts | 2 + src/main.ts | 5 +- src/services/export.service.spec.ts | 65 +++++++ src/services/export.service.ts | 197 +++++++++++++++++++--- 7 files changed, 249 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4a9eecc..0f50035 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.1", "license": "UNLICENSED", "dependencies": { - "@madie/madie-models": "^1.3.49", + "@madie/madie-models": "^1.3.51", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/jwt": "^10.2.0", @@ -1751,9 +1751,9 @@ } }, "node_modules/@madie/madie-models": { - "version": "1.3.49", - "resolved": "https://registry.npmjs.org/@madie/madie-models/-/madie-models-1.3.49.tgz", - "integrity": "sha512-OJZMGtamG0AUWAjQwGiDYp10EKAB9GnOtnD7QswzMhPifNfrbLW1wdV3dxkskovjmW4btHwz3NJ1EoUi+wStmg==" + "version": "1.3.51", + "resolved": "https://registry.npmjs.org/@madie/madie-models/-/madie-models-1.3.51.tgz", + "integrity": "sha512-6TrTYVN49u0/mPhXpLrrRaY6xV+lUXH6mNen4yppWUD1PKKxD9jDEBV2PZn5wV8/pOL+2i30fVnABMsez0srNw==" }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.4", @@ -3934,7 +3934,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -4948,7 +4947,6 @@ "version": "4.19.2", "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dev": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -4990,7 +4988,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "dependencies": { "ms": "2.0.0" } @@ -4998,14 +4995,12 @@ "node_modules/express/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" }, "node_modules/external-editor": { "version": "3.1.0", diff --git a/package.json b/package.json index 6df56da..46381e3 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test:e2e": "JWT_SECRET=ThisIsMySecret jest --config ./test/jest-e2e.json" }, "dependencies": { - "@madie/madie-models": "^1.3.49", + "@madie/madie-models": "^1.3.51", "@nestjs/common": "^10.3.5", "@nestjs/core": "^10.3.5", "@nestjs/jwt": "^10.2.0", diff --git a/src/controllers/export.controller.spec.ts b/src/controllers/export.controller.spec.ts index 1c83e04..858f25a 100644 --- a/src/controllers/export.controller.spec.ts +++ b/src/controllers/export.controller.spec.ts @@ -15,6 +15,7 @@ describe('exportController', () => { groupNumber: '1', testCaseExecutionResults: [ { + testCaseId: 'testCaseId', populations: [], notes: '', last: 'testSeries1', diff --git a/src/controllers/export.controller.ts b/src/controllers/export.controller.ts index 7c3adcf..2d435a9 100644 --- a/src/controllers/export.controller.ts +++ b/src/controllers/export.controller.ts @@ -3,6 +3,7 @@ import { Response, Request } from 'express'; import { ExportService } from '../services/export.service'; import { AuthGuard } from '../auth/auth.guard'; import { TestCaseExcelExportDto } from '@madie/madie-models'; +import { log } from 'console'; @Controller('excel') @UseGuards(AuthGuard) @@ -18,6 +19,7 @@ export class ExportController { async getExcelFile(@Req() req: Request, @Res() res: Response) { const testCaseGroupDtos: TestCaseExcelExportDto[] = req.body.testCaseExcelExportDtos; + log('request -> ' + JSON.stringify(testCaseGroupDtos)); const buffer = await this.exportService.generateXlsx(testCaseGroupDtos); res.send(buffer); } diff --git a/src/main.ts b/src/main.ts index a0399f6..3c21f71 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,7 +2,10 @@ import { NestFactory } from '@nestjs/core'; import { ExportModule } from './export.module'; export async function bootstrap() { - const app = await NestFactory.create(ExportModule); + const app = await NestFactory.create(ExportModule, { + logger: ['error', 'log'], + }); + app.enableCors(); await app.listen(3000); } bootstrap(); diff --git a/src/services/export.service.spec.ts b/src/services/export.service.spec.ts index 87067c2..8d06357 100644 --- a/src/services/export.service.spec.ts +++ b/src/services/export.service.spec.ts @@ -10,11 +10,13 @@ describe('ExcelService', () => { groupNumber: '1', testCaseExecutionResults: [ { + testCaseId: 'testCaseId1', populations: [ { name: 'initialPopulation', expected: 1, actual: 2, + pass: false, }, ], notes: '', @@ -42,6 +44,43 @@ describe('ExcelService', () => { actual: 'FUNCTION', }, ], + stratifications: [ + { + testCaseId: 'testCaseId1', + stratId: 'stratId1', + stratName: 'PopSet1 Stratification 1', + stratificationDtos: [ + { + id: 'stratId1', + name: 'STRAT', + expected: 11, + actual: 0, + pass: false, + }, + { + id: 'f0b3c08d-1164-48d8-bc71-aed87499099f', + name: 'initialPopulation', + expected: 11, + actual: 0, + pass: false, + }, + { + id: 'f0b3c08d-1164-48d8-bc71-aed87499099f', + name: 'denominator', + expected: 11, + actual: 0, + pass: false, + }, + { + id: 'f0b3c08d-1164-48d8-bc71-aed87499099f', + name: 'numerator', + expected: 11, + actual: 0, + pass: false, + }, + ], + }, + ], }, ], }; @@ -180,5 +219,31 @@ describe('ExcelService', () => { '1 - Population Criteria Section', ); expect(populationCriteria1WorkSheet).not.toBe(null); + + const strat1WorkSheet = workbook.getWorksheet( + '2 - PopSet1 Stratification 1', + ); + expect(strat1WorkSheet).not.toBe(null); + expect(strat1WorkSheet.getRows.length).toBe(2); + expect(strat1WorkSheet.getCell(1, 1).value).toBe('Expected'); + expect(strat1WorkSheet.getCell(1, 5).value).toBe('Actual'); + + expect(strat1WorkSheet.getCell(2, 1).value).toBe('STRAT'); + expect(strat1WorkSheet.getCell(2, 2).value).toBe('initialPopulation'); + expect(strat1WorkSheet.getCell(2, 3).value).toBe('denominator'); + expect(strat1WorkSheet.getCell(2, 4).value).toBe('numerator'); + expect(strat1WorkSheet.getCell(2, 5).value).toBe('STRAT'); + expect(strat1WorkSheet.getCell(2, 6).value).toBe('initialPopulation'); + expect(strat1WorkSheet.getCell(2, 7).value).toBe('denominator'); + expect(strat1WorkSheet.getCell(2, 8).value).toBe('numerator'); + + expect(strat1WorkSheet.getCell(3, 1).value).toBe(11); + expect(strat1WorkSheet.getCell(3, 2).value).toBe(11); + expect(strat1WorkSheet.getCell(3, 3).value).toBe(11); + expect(strat1WorkSheet.getCell(3, 4).value).toBe(11); + expect(strat1WorkSheet.getCell(3, 5).value).toBe(0); + expect(strat1WorkSheet.getCell(3, 6).value).toBe(0); + expect(strat1WorkSheet.getCell(3, 7).value).toBe(0); + expect(strat1WorkSheet.getCell(3, 8).value).toBe(0); }); }); diff --git a/src/services/export.service.ts b/src/services/export.service.ts index d0c2ce9..229b8c6 100644 --- a/src/services/export.service.ts +++ b/src/services/export.service.ts @@ -14,6 +14,8 @@ import { TestCaseExecutionResultDto, TestCaseExcelExportDto, PopulationDto, + StratificationDto, + GroupedStratificationDto, } from '@madie/madie-models'; @Injectable() @@ -28,14 +30,33 @@ export class ExportService { this.generateKeyWorksheet(keyWorkSheet); //Generate other worksheets as needed - const groupNumber = testCaseExcelExportDtos[0].groupNumber; - const populationWorksheet = workbook.addWorksheet( - `${groupNumber} - Population Criteria Section`, - ); - this.generatePopulationWorksheet( - populationWorksheet, - testCaseExcelExportDtos[0], - ); + let index: number = 1; + testCaseExcelExportDtos.forEach((testCaseExcelExportDto) => { + const populationWorksheet = workbook.addWorksheet( + `${index} - Population Criteria Section`, + ); + this.generatePopulationWorksheet( + populationWorksheet, + testCaseExcelExportDto, + ); + index += 1; + }); + + testCaseExcelExportDtos.forEach((testCaseExcelExportDto) => { + testCaseExcelExportDto.testCaseExecutionResults.forEach((result) => { + result.stratifications?.forEach((stratification) => { + const stratificationWorksheet = workbook.addWorksheet( + `${index} - ${stratification.stratName}`, + ); + this.generateStratificationWorksheet( + stratificationWorksheet, + testCaseExcelExportDto, + stratification, + ); + index += 1; + }); + }); + }); // Return final workbook return workbook.xlsx.writeBuffer() as Promise; @@ -144,6 +165,9 @@ export class ExportService { let firstRow = []; let headerRow = []; const testCasesData = []; + //failed test cases will have red text font + const failedIndexes = []; + let index: number = 3; testCaseGroupDto.testCaseExecutionResults.forEach( (result: TestCaseExecutionResultDto) => { const firstRowData = []; @@ -151,14 +175,24 @@ export class ExportService { const testCaseData = []; const populations: PopulationDto[] = this.getPopulations(testCaseGroupDto); + + const numValues: number = populations?.length; + this.getFirstRowData(firstRowData, numValues); + populations?.forEach((population) => { - firstRowData.push('Expected', 'Actual'); - headerRowData.push(population.name, population.name); - this.populateTestCaseExpectedAndActual( + headerRowData.push(population.name); + this.populatePopExpected( testCaseData, - result, population, + result, + index, + failedIndexes, ); + index += 1; + }); + populations?.forEach((population) => { + headerRowData.push(population.name); + this.populatePopActual(testCaseData, population, result); }); this.populateTestCase(testCaseData, result); @@ -178,9 +212,47 @@ export class ExportService { testCasesData.forEach((testCaseData) => { worksheet.addRow(testCaseData); }); + failedIndexes.forEach((index) => { + worksheet.getRow(index).font = { color: { argb: 'ff0000' } }; + }); this.adjustColumnWidth(worksheet); } + private populatePopExpected( + testCaseData, + populationDto: PopulationDto, + result, + index: number, + failedIndexes: number[], + ) { + let foundPopulation: PopulationDto = null; + result.populations?.forEach((currentPopulation) => { + if (currentPopulation.name === populationDto.name) { + foundPopulation = currentPopulation; + } + }); + if (foundPopulation?.expected !== foundPopulation?.actual) { + if (failedIndexes.indexOf(index) === -1) { + //if not in the array + failedIndexes.push(index); + } + } + testCaseData.push(foundPopulation?.expected); + } + private populatePopActual( + testCaseData, + populationDto: PopulationDto, + result, + ) { + let foundPopulation: PopulationDto = null; + result.populations?.forEach((currentPopulation) => { + if (currentPopulation.name === populationDto.name) { + foundPopulation = currentPopulation; + } + }); + testCaseData.push(foundPopulation?.actual); + } + private getPopulations = (testCaseExcelExportDto: TestCaseExcelExportDto) => { let populations: PopulationDto[] = []; testCaseExcelExportDto.testCaseExecutionResults?.forEach( @@ -193,21 +265,6 @@ export class ExportService { return populations; }; - private populateTestCaseExpectedAndActual( - testCaseData, - result: TestCaseExecutionResultDto, - population: PopulationDto, - ) { - let foundPopulation: PopulationDto = null; - result.populations?.forEach((currentPopulation) => { - if (currentPopulation.name === population.name) { - foundPopulation = currentPopulation; - } - }); - testCaseData.push(foundPopulation?.expected, foundPopulation?.actual); - return foundPopulation; - } - private populateFirstRow(worksheet, firstRowData) { worksheet.addRow(firstRowData); const firstRow = worksheet.getRow(1); @@ -297,4 +354,90 @@ export class ExportService { column.height = 40; }); } + + public generateStratificationWorksheet( + worksheet: ExcelJS.Worksheet, + testCaseGroupDto: TestCaseExcelExportDto, + groupedStratificationDto: GroupedStratificationDto, + ) { + let firstRow = []; + let headerRow = []; + const testCasesData = []; + + //failed test cases will have red text font + const failedIndexes = []; + let index: number = 3; + + testCaseGroupDto.testCaseExecutionResults.forEach( + (result: TestCaseExecutionResultDto) => { + const firstRowData = []; + const headerRowData = []; + const testCaseData = []; + + const numValues: number = + groupedStratificationDto.stratificationDtos?.length; + this.getFirstRowData(firstRowData, numValues); + + groupedStratificationDto.stratificationDtos?.forEach((strat) => { + headerRowData.push(strat.name); + this.populateStratExpected(testCaseData, strat, index, failedIndexes); + index += 1; + }); + + groupedStratificationDto.stratificationDtos?.forEach((strat) => { + headerRowData.push(strat.name); + this.populateStratActual(testCaseData, strat); + }); + + this.populateTestCase(testCaseData, result); + testCasesData.push(testCaseData); + firstRow = firstRowData; + headerRow = headerRowData; + }, + ); + this.populateFirstRow(worksheet, firstRow); + + this.populateHeaderRow( + worksheet, + headerRow, + testCaseGroupDto.testCaseExecutionResults[0], + ); + + testCasesData.forEach((testCaseData) => { + worksheet.addRow(testCaseData); + }); + failedIndexes.forEach((index) => { + worksheet.getRow(index).font = { color: { argb: 'ff0000' } }; + }); + this.adjustColumnWidth(worksheet); + } + + private populateStratExpected( + testCaseData, + stratificationDto: StratificationDto, + index: number, + failedIndexes, + ) { + testCaseData.push(stratificationDto?.expected); + if (stratificationDto?.expected !== stratificationDto?.actual) { + if (failedIndexes.indexOf(index) === -1) { + //if not in the array + failedIndexes.push(index); + } + } + } + private populateStratActual( + testCaseData, + stratificationDto: StratificationDto, + ) { + testCaseData.push(stratificationDto?.actual); + } + + private getFirstRowData(firstRowData, numValues: number) { + firstRowData.push('Expected'); + for (let i = 0; i < numValues - 1; i++) { + firstRowData.push(' '); + } + firstRowData.push('Actual'); + } }