From b49359ec4a6d626a79de37bc5d68e97664d6844f Mon Sep 17 00:00:00 2001 From: Jason Qiu Date: Sat, 19 Aug 2023 15:56:07 +0800 Subject: [PATCH] [#12544] Rubric Question Statistics: Handle empty weights (#12545) * Use NO_VALUE to represent empty weights * Skip over null weights * Clean up calculations * Standardize naming * Fix null weight display * Add explanation for empty weights * Fix downloaded result empty weight display * Fix order of assertEquals * Handle null conversion to NO_VALUE when input out of focus * Revert back to using null value * Fix display when all chosen weights are empty * Standardize display for empty values * Fix failing tests --- .../InstructorFeedbackEditPage.java | 4 +- ...-question-edit-details-form.component.html | 5 +- .../rubric-question-statistics-calculation.ts | 108 +++++++++++++----- .../rubric-question-statistics.component.ts | 38 ++++-- .../test-data/rubricQuestionResponses.json | 2 + .../session-result-csv.service.spec.ts.snap | 18 +-- .../feedback-rubric-question-details.impl.ts | 28 +++-- 7 files changed, 144 insertions(+), 59 deletions(-) diff --git a/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java index 032e0fbc66f..4c0eda460ee 100644 --- a/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java +++ b/src/e2e/java/teammates/e2e/pageobjects/InstructorFeedbackEditPage.java @@ -741,8 +741,8 @@ public void verifyRubricQuestionDetails(int questionNum, FeedbackRubricQuestionD for (int i = 0; i < numSubQn; i++) { List rubricWeights = getRubricWeights(questionNum, i + 2); for (int j = 0; j < numChoices; j++) { - assertEquals(rubricWeights.get(j).getAttribute("value"), - getDoubleString(weights.get(i).get(j))); + assertEquals(getDoubleString(weights.get(i).get(j)), + rubricWeights.get(j).getAttribute("value")); } } } else { diff --git a/src/web/app/components/question-types/question-edit-details-form/rubric-question-edit-details-form.component.html b/src/web/app/components/question-types/question-edit-details-form/rubric-question-edit-details-form.component.html index c6055bb8dce..5a7965a949f 100644 --- a/src/web/app/components/question-types/question-edit-details-form/rubric-question-edit-details-form.component.html +++ b/src/web/app/components/question-types/question-edit-details-form/rubric-question-edit-details-form.component.html @@ -2,7 +2,7 @@
@@ -49,7 +49,8 @@ (ngModelChange)="triggerRubricDescriptionChange($event, i, j)" rows="3" [disabled]="!isEditable"> + [ngModel]="model.rubricWeightsForEachCell[i][j]" + (ngModelChange)="triggerRubricWeightChange($event, i, j)"> diff --git a/src/web/app/components/question-types/question-statistics/question-statistics-calculation/rubric-question-statistics-calculation.ts b/src/web/app/components/question-types/question-statistics/question-statistics-calculation/rubric-question-statistics-calculation.ts index 9bc68fd288c..2a5e15dc865 100644 --- a/src/web/app/components/question-types/question-statistics/question-statistics-calculation/rubric-question-statistics-calculation.ts +++ b/src/web/app/components/question-types/question-statistics/question-statistics-calculation/rubric-question-statistics-calculation.ts @@ -3,7 +3,7 @@ import { FeedbackRubricQuestionDetails, FeedbackRubricResponseDetails, } from '../../../../../types/api-output'; -import { RUBRIC_ANSWER_NOT_CHOSEN } from '../../../../../types/feedback-response-details'; +import { NO_VALUE, RUBRIC_ANSWER_NOT_CHOSEN } from '../../../../../types/feedback-response-details'; import { QuestionStatistics } from '../question-statistics'; /** @@ -18,6 +18,7 @@ export interface PerRecipientStats { percentages: number[][]; percentagesAverage: number[]; weightsAverage: number[]; + areSubQuestionChosenWeightsAllNull: boolean[]; subQuestionTotalChosenWeight: number[]; subQuestionWeightAverage: number[]; overallWeightedSum: number; @@ -113,6 +114,7 @@ export class RubricQuestionStatisticsCalculation percentages: [], percentagesAverage: [], weightsAverage: [], + areSubQuestionChosenWeightsAllNull: this.subQuestions.map(() => true), subQuestionTotalChosenWeight: this.subQuestions.map(() => 0), subQuestionWeightAverage: [], }; @@ -122,8 +124,12 @@ export class RubricQuestionStatisticsCalculation continue; } this.perRecipientStatsMap[response.recipientEmail || response.recipient].answers[i][subAnswer] += 1; - this.perRecipientStatsMap[response.recipientEmail || response.recipient].subQuestionTotalChosenWeight[i] += - +this.weights[i][subAnswer].toFixed(5); + if (this.weights[i][subAnswer] !== null) { + this.perRecipientStatsMap[response.recipientEmail || response.recipient].subQuestionTotalChosenWeight[i] += + +this.weights[i][subAnswer].toFixed(5); + this.perRecipientStatsMap[ + response.recipientEmail || response.recipient].areSubQuestionChosenWeightsAllNull[i] = false; + } } } @@ -131,29 +137,54 @@ export class RubricQuestionStatisticsCalculation const perRecipientStats: PerRecipientStats = this.perRecipientStatsMap[recipient]; // Answers sum = number of answers in each column - perRecipientStats.answersSum = this.calculateAnswersSum(perRecipientStats.answers); + perRecipientStats.answersSum = this.sumValidValuesByColumn(perRecipientStats.answers); perRecipientStats.percentages = this.calculatePercentages(perRecipientStats.answers); perRecipientStats.percentagesAverage = this.calculatePercentagesAverage(perRecipientStats.answersSum); + perRecipientStats.subQuestionTotalChosenWeight = + perRecipientStats.subQuestionTotalChosenWeight.map((val: number, i: number) => + (perRecipientStats.areSubQuestionChosenWeightsAllNull[i] ? NO_VALUE : val)); perRecipientStats.subQuestionWeightAverage = this.calculateSubQuestionWeightAverage(perRecipientStats.answers); perRecipientStats.weightsAverage = this.calculateWeightsAverage(this.weights); - // Overall weighted sum = sum of total chosen weight for all sub questions - perRecipientStats.overallWeightedSum = - +(perRecipientStats.subQuestionTotalChosenWeight.reduce((a, b) => a + b)).toFixed(2); - // Overall weighted average = overall weighted sum / total number of responses - perRecipientStats.overallWeightAverage = +(perRecipientStats.overallWeightedSum - / this.calculateNumResponses(perRecipientStats.answersSum)).toFixed(2); + perRecipientStats.overallWeightedSum = this.calculateOverallWeightedSum( + perRecipientStats.areSubQuestionChosenWeightsAllNull, perRecipientStats.subQuestionTotalChosenWeight); + // Overall weighted average = overall weighted sum / total number of responses with non-null weights + perRecipientStats.overallWeightAverage = perRecipientStats.overallWeightedSum === NO_VALUE + ? NO_VALUE + : +(perRecipientStats.overallWeightedSum + / this.calculateNumResponses(this.countResponsesByRowWithValidWeight(perRecipientStats.answers))) + .toFixed(2); } } + // Number of responses for each sub question with non-null weights + private countResponsesByRowWithValidWeight(answers: number[][]): number[] { + const sums: number[] = []; + for (let r: number = 0; r < answers.length; r += 1) { + let sum: number = 0; + for (let c: number = 0; c < answers[0].length; c += 1) { + if (this.weights[r][c] === null) { + continue; + } + sum += answers[r][c]; + } + sums[r] = sum; + } + return sums; + } + private calculateSubQuestionWeightAverage(answers: number[][]): number[] { - const sums: number[] = answers.map((weightedAnswers: number[]) => - weightedAnswers.reduce((a: number, b: number) => a + b, 0)); + const sums: number[] = this.countResponsesByRowWithValidWeight(answers); return answers.map((subQuestionAnswer: number[], subQuestionIdx: number): number => { - const weightAverage: number = sums[subQuestionIdx] === 0 ? 0 - : subQuestionAnswer.reduce((prevValue: number, currValue: number, currentIndex: number): number => - prevValue + currValue * this.weights[subQuestionIdx][currentIndex], 0) / sums[subQuestionIdx]; + if (sums[subQuestionIdx] === 0) { + return NO_VALUE; + } + const weightAverage: number = + subQuestionAnswer.reduce((prevValue: number, currValue: number, currentIndex: number): number => + (this.weights[subQuestionIdx][currentIndex] === null + ? prevValue + : prevValue + currValue * this.weights[subQuestionIdx][currentIndex]), 0) / sums[subQuestionIdx]; return +weightAverage.toFixed(2); }); } @@ -176,27 +207,40 @@ export class RubricQuestionStatisticsCalculation return percentages; } - // Calculate sum of answers for each column - private calculateAnswersSum(answers: number[][]): number[] { + // Calculate sum of non-null values for each column + private sumValidValuesByColumn(matrix: number[][]): number[] { const sums: number[] = []; - for (let i: number = 0; i < answers[0].length; i += 1) { + for (let c: number = 0; c < matrix[0].length; c += 1) { let sum: number = 0; - for (let j: number = 0; j < answers.length; j += 1) { - sum += answers[j][i]; + for (let r: number = 0; r < matrix.length; r += 1) { + sum += matrix[r][c] === null ? 0 : matrix[r][c]; } - sums[i] = sum; + sums[c] = sum; } return sums; } - // Calculate weight average for each column + // Count number of non-null values for each column + private countValidValuesByColumn(matrix: number[][]): number[] { + const counts: number[] = []; + for (let c: number = 0; c < matrix[0].length; c += 1) { + let count: number = 0; + for (let r: number = 0; r < matrix.length; r += 1) { + count += matrix[r][c] === null ? 0 : 1; + } + counts[c] = count; + } + return counts; + } + + // Calculate non-null weight average for each column private calculateWeightsAverage(weights: number[][]): number[] { - // Calculate sum of weights for each column - const sums: number[] = this.calculateAnswersSum(weights); + const sums: number[] = this.sumValidValuesByColumn(weights); + const counts: number[] = this.countValidValuesByColumn(weights); const averages: number[] = []; - // Divide each weight sum by number of weights + // Divide each weight sum by number of non-null weights for (let i: number = 0; i < sums.length; i += 1) { - averages[i] = +(sums[i] / weights.length).toFixed(2); + averages[i] = counts[i] ? +(sums[i] / counts[i]).toFixed(2) : NO_VALUE; } return averages; } @@ -217,4 +261,16 @@ export class RubricQuestionStatisticsCalculation private calculateNumResponses(answersSum: number[]): number { return answersSum.reduce((a, b) => a + b); } + + // Overall weighted sum is sum of total chosen non-null weight for all sub questions + private calculateOverallWeightedSum(areChosenWeightsAllNull: boolean[], totalChosenWeights: number[]): number { + if (areChosenWeightsAllNull.every(Boolean)) { + return NO_VALUE; + } + let sum: number = 0; + for (const totalChosenWeight of totalChosenWeights) { + sum += totalChosenWeight === NO_VALUE ? 0 : totalChosenWeight; + } + return +(sum).toFixed(2); + } } diff --git a/src/web/app/components/question-types/question-statistics/rubric-question-statistics.component.ts b/src/web/app/components/question-types/question-statistics/rubric-question-statistics.component.ts index 852058648f4..e7fe3c03886 100644 --- a/src/web/app/components/question-types/question-statistics/rubric-question-statistics.component.ts +++ b/src/web/app/components/question-types/question-statistics/rubric-question-statistics.component.ts @@ -1,6 +1,7 @@ import { Component, OnChanges } from '@angular/core'; import { StringHelper } from '../../../../services/string-helper'; import { DEFAULT_RUBRIC_QUESTION_DETAILS } from '../../../../types/default-question-structs'; +import { NO_VALUE } from '../../../../types/feedback-response-details'; import { SortBy } from '../../../../types/sort-properties'; import { ColumnData, SortableTableCellData } from '../../sortable-table/sortable-table.component'; import { @@ -54,21 +55,29 @@ export class RubricQuestionStatisticsComponent extends RubricQuestionStatisticsC return { value: `${this.percentagesExcludeSelf[questionIndex][choiceIndex]}%` + ` (${this.answersExcludeSelf[questionIndex][choiceIndex]})` - + `${this.isWeightStatsVisible ? ` [${this.weights[questionIndex][choiceIndex]}]` : ''}`, + + `${this.isWeightStatsVisible + ? ` [${this.getDisplayWeight(this.weights[questionIndex][choiceIndex])}]` + : ''}`, }; } return { value: `${this.percentages[questionIndex][choiceIndex]}%` + ` (${this.answers[questionIndex][choiceIndex]})` - + `${this.isWeightStatsVisible ? ` [${this.weights[questionIndex][choiceIndex]}]` : ''}`, + + `${this.isWeightStatsVisible + ? ` [${this.getDisplayWeight(this.weights[questionIndex][choiceIndex])}]` + : ''}`, }; }), ]; if (this.isWeightStatsVisible) { if (this.excludeSelf) { - currRow.push({ value: this.subQuestionWeightAverageExcludeSelf[questionIndex] }); + currRow.push({ + value: this.getDisplayWeight(this.subQuestionWeightAverageExcludeSelf[questionIndex]), + }); } else { - currRow.push({ value: this.subQuestionWeightAverage[questionIndex] }); + currRow.push({ + value: this.getDisplayWeight(this.subQuestionWeightAverage[questionIndex]), + }); } } @@ -103,11 +112,11 @@ export class RubricQuestionStatisticsComponent extends RubricQuestionStatisticsC return { value: `${perRecipientStats.percentages[questionIndex][choiceIndex]}%` + ` (${perRecipientStats.answers[questionIndex][choiceIndex]})` - + ` [${this.weights[questionIndex][choiceIndex]}]`, + + ` [${this.getDisplayWeight(this.weights[questionIndex][choiceIndex])}]`, }; }), - { value: perRecipientStats.subQuestionTotalChosenWeight[questionIndex] }, - { value: perRecipientStats.subQuestionWeightAverage[questionIndex] }, + { value: this.getDisplayWeight(perRecipientStats.subQuestionTotalChosenWeight[questionIndex]) }, + { value: this.getDisplayWeight(perRecipientStats.subQuestionWeightAverage[questionIndex]) }, ]); }); }); @@ -124,6 +133,9 @@ export class RubricQuestionStatisticsComponent extends RubricQuestionStatisticsC this.perRecipientOverallRowsData = []; Object.values(this.perRecipientStatsMap).forEach((perRecipientStats: PerRecipientStats) => { + const perCriterionAverage: string = + perRecipientStats.subQuestionWeightAverage.map((val: number) => + this.getDisplayWeight(val)).toString(); this.perRecipientOverallRowsData.push([ { value: perRecipientStats.recipientTeam }, { value: perRecipientStats.recipientName }, @@ -132,13 +144,17 @@ export class RubricQuestionStatisticsComponent extends RubricQuestionStatisticsC return { value: `${perRecipientStats.percentagesAverage[choiceIndex]}%` + ` (${perRecipientStats.answersSum[choiceIndex]})` - + ` [${perRecipientStats.weightsAverage[choiceIndex]}]`, + + ` [${this.getDisplayWeight(perRecipientStats.weightsAverage[choiceIndex])}]`, }; }), - { value: perRecipientStats.overallWeightedSum }, - { value: perRecipientStats.overallWeightAverage }, - { value: perRecipientStats.subQuestionWeightAverage.toString() }, + { value: this.getDisplayWeight(perRecipientStats.overallWeightedSum) }, + { value: this.getDisplayWeight(perRecipientStats.overallWeightAverage) }, + { value: perCriterionAverage }, ]); }); } + + private getDisplayWeight(weight: number): any { + return weight === null || weight === NO_VALUE ? '-' : weight; + } } diff --git a/src/web/app/components/question-types/question-statistics/test-data/rubricQuestionResponses.json b/src/web/app/components/question-types/question-statistics/test-data/rubricQuestionResponses.json index e6623a372fc..e746e887694 100644 --- a/src/web/app/components/question-types/question-statistics/test-data/rubricQuestionResponses.json +++ b/src/web/app/components/question-types/question-statistics/test-data/rubricQuestionResponses.json @@ -70,6 +70,7 @@ "recipientEmail": "alice@gmail.com", "recipientName": "Alice", "recipientTeam": "Team 1", + "areSubQuestionChosenWeightsAllNull": [false, false, false], "subQuestionTotalChosenWeight": [1, 1, 0.8], "subQuestionWeightAverage": [0.5, 0.5, 0.4], "weightsAverage": [0.23, 0.77], @@ -88,6 +89,7 @@ "recipientEmail": "bob@gmail.com", "recipientName": "Bob", "recipientTeam": "Team 2", + "areSubQuestionChosenWeightsAllNull": [false, false, false], "subQuestionTotalChosenWeight": [0.4, 1, 0.8], "subQuestionWeightAverage": [0.2, 0.5, 0.4], "weightsAverage": [0.23, 0.77], diff --git a/src/web/services/__snapshots__/session-result-csv.service.spec.ts.snap b/src/web/services/__snapshots__/session-result-csv.service.spec.ts.snap index 88b9cf5793b..b6690556117 100644 --- a/src/web/services/__snapshots__/session-result-csv.service.spec.ts.snap +++ b/src/web/services/__snapshots__/session-result-csv.service.spec.ts.snap @@ -387,7 +387,7 @@ Team,Recipient Name,Recipient Email,Sub Question,Yes,No,Total,Average \\"Team 1.1
'\\"\\"\\",student2 In Course1,student2InCourse1@gmail.tmt,b) This student has tried his/her best.,100% (1) [1.25],0% (0) [-1.7],1.25,1.25 \\"Team 1.1'\\"\\"\\",student3 In Course1,student3InCourse1@gmail.tmt,a) This student has done a good job.,100% (1) [1.25],0% (0) [-1.7],1.25,1.25 \\"Team 1.1'\\"\\"\\",student3 In Course1,student3InCourse1@gmail.tmt,b) This student has tried his/her best.,100% (1) [1.25],0% (0) [-1.7],1.25,1.25 -\\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,a) This student has done a good job.,0% (0) [1.25],0% (0) [-1.7],0,0 +\\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,a) This student has done a good job.,0% (0) [1.25],0% (0) [-1.7],-,- \\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,b) This student has tried his/her best.,100% (1) [1.25],0% (0) [-1.7],1.25,1.25 Per Recipient Statistics (Overall) @@ -395,7 +395,7 @@ Team,Recipient Name,Recipient Email,Yes,No,Total,Average,Per Criterion Average \\"Team 1.1'\\"\\"\\",\\"student1 In Course1'\\"\\"\\",student1InCourse1@gmail.tmt,50% (1) [1.25],50% (1) [-1.7],-0.45,-0.23,\\"1.25,-1.7\\" \\"Team 1.1'\\"\\"\\",student2 In Course1,student2InCourse1@gmail.tmt,50% (1) [1.25],50% (1) [-1.7],-0.45,-0.23,\\"-1.7,1.25\\" \\"Team 1.1'\\"\\"\\",student3 In Course1,student3InCourse1@gmail.tmt,100% (2) [1.25],0% (0) [-1.7],2.5,1.25,\\"1.25,1.25\\" -\\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,100% (1) [1.25],0% (0) [-1.7],1.25,1.25,\\"0,1.25\\" +\\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,100% (1) [1.25],0% (0) [-1.7],1.25,1.25,\\"-,1.25\\" Team,Giver's Name,Giver's Email,Recipient's Team,Recipient's Name,Recipient's Email,Sub Question,Choice Value,Choice Number,Giver's Comment @@ -427,22 +427,22 @@ Question 2,Please choose the best choice for the following sub-questions. Only f Summary Statistics ,Yes,No,Average a) This student has done a good job.,66.67% (2) [1.25],33.33% (1) [1],1.17 -b) This student has tried his/her best.,0% (0) [1.25],0% (0) [1],0 +b) This student has tried his/her best.,0% (0) [1.25],0% (0) [1],- Per Recipient Statistics (Per Criterion) Team,Recipient Name,Recipient Email,Sub Question,Yes,No,Total,Average \\"Team 1.1'\\"\\"\\",student2 In Course1,student2InCourse1@gmail.tmt,a) This student has done a good job.,100% (1) [1.25],0% (0) [1],1.25,1.25 -\\"Team 1.1'\\"\\"\\",student2 In Course1,student2InCourse1@gmail.tmt,b) This student has tried his/her best.,0% (0) [1.25],0% (0) [1],0,0 +\\"Team 1.1'\\"\\"\\",student2 In Course1,student2InCourse1@gmail.tmt,b) This student has tried his/her best.,0% (0) [1.25],0% (0) [1],-,- \\"Team 1.1'\\"\\"\\",student3 In Course1,student3InCourse1@gmail.tmt,a) This student has done a good job.,0% (0) [1.25],100% (1) [1],1,1 -\\"Team 1.1'\\"\\"\\",student3 In Course1,student3InCourse1@gmail.tmt,b) This student has tried his/her best.,0% (0) [1.25],0% (0) [1],0,0 +\\"Team 1.1'\\"\\"\\",student3 In Course1,student3InCourse1@gmail.tmt,b) This student has tried his/her best.,0% (0) [1.25],0% (0) [1],-,- \\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,a) This student has done a good job.,100% (1) [1.25],0% (0) [1],1.25,1.25 -\\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,b) This student has tried his/her best.,0% (0) [1.25],0% (0) [1],0,0 +\\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,b) This student has tried his/her best.,0% (0) [1.25],0% (0) [1],-,- Per Recipient Statistics (Overall) Team,Recipient Name,Recipient Email,Yes,No,Total,Average,Per Criterion Average -\\"Team 1.1'\\"\\"\\",student2 In Course1,student2InCourse1@gmail.tmt,100% (1) [1.25],0% (0) [1],1.25,1.25,\\"1.25,0\\" -\\"Team 1.1'\\"\\"\\",student3 In Course1,student3InCourse1@gmail.tmt,0% (0) [1.25],100% (1) [1],1,1,\\"1,0\\" -\\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,100% (1) [1.25],0% (0) [1],1.25,1.25,\\"1.25,0\\" +\\"Team 1.1'\\"\\"\\",student2 In Course1,student2InCourse1@gmail.tmt,100% (1) [1.25],0% (0) [1],1.25,1.25,\\"1.25,-\\" +\\"Team 1.1'\\"\\"\\",student3 In Course1,student3InCourse1@gmail.tmt,0% (0) [1.25],100% (1) [1],1,1,\\"1,-\\" +\\"Team 1.1'\\"\\"\\",student4 In Course1,student4InCourse1@gmail.tmt,100% (1) [1.25],0% (0) [1],1.25,1.25,\\"1.25,-\\" Team,Giver's Name,Giver's Email,Recipient's Team,Recipient's Name,Recipient's Email,Sub Question,Choice Value,Choice Number,Giver's Comment diff --git a/src/web/types/question-details-impl/feedback-rubric-question-details.impl.ts b/src/web/types/question-details-impl/feedback-rubric-question-details.impl.ts index 1049e57744d..7cb611879e5 100644 --- a/src/web/types/question-details-impl/feedback-rubric-question-details.impl.ts +++ b/src/web/types/question-details-impl/feedback-rubric-question-details.impl.ts @@ -7,6 +7,7 @@ import { FeedbackQuestionType, FeedbackRubricQuestionDetails, QuestionOutput, } from '../api-output'; +import { NO_VALUE } from '../feedback-response-details'; import { AbstractFeedbackQuestionDetails } from './abstract-feedback-question-details'; /** @@ -64,11 +65,13 @@ export class FeedbackRubricQuestionDetailsImpl extends AbstractFeedbackQuestionD ...statsCalculation.choices.map((_: string, choiceIndex: number) => { return `${statsCalculation.percentages[questionIndex][choiceIndex]}% \ (${statsCalculation.answers[questionIndex][choiceIndex]}) \ -${statsCalculation.hasWeights ? `[${statsCalculation.weights[questionIndex][choiceIndex]}]` : ''}`; +${statsCalculation.hasWeights + ? `[${this.getDisplayWeight(statsCalculation.weights[questionIndex][choiceIndex])}]` + : ''}`; }), ]; if (statsCalculation.hasWeights) { - currRow.push(String(statsCalculation.subQuestionWeightAverage[questionIndex])); + currRow.push(String(this.getDisplayWeight(statsCalculation.subQuestionWeightAverage[questionIndex]))); } statsRows.push(currRow); }); @@ -103,10 +106,10 @@ ${statsCalculation.hasWeights ? `[${statsCalculation.weights[questionIndex][choi ...statsCalculation.choices.map((_: string, choiceIndex: number) => { return `${perRecipientStats.percentages[questionIndex][choiceIndex]}% \ (${perRecipientStats.answers[questionIndex][choiceIndex]}) \ -[${statsCalculation.weights[questionIndex][choiceIndex]}]`; +[${this.getDisplayWeight(statsCalculation.weights[questionIndex][choiceIndex])}]`; }), - String(perRecipientStats.subQuestionTotalChosenWeight[questionIndex]), - String(perRecipientStats.subQuestionWeightAverage[questionIndex]), + String(this.getDisplayWeight(perRecipientStats.subQuestionTotalChosenWeight[questionIndex])), + String(this.getDisplayWeight(perRecipientStats.subQuestionWeightAverage[questionIndex])), ]); }); }); @@ -128,6 +131,9 @@ ${statsCalculation.hasWeights ? `[${statsCalculation.weights[questionIndex][choi .sort((a: PerRecipientStats, b: PerRecipientStats) => a.recipientTeam.localeCompare(b.recipientTeam) || a.recipientName.localeCompare(b.recipientName)) .forEach((perRecipientStats: PerRecipientStats) => { + const perCriterionAverage: string = + perRecipientStats.subQuestionWeightAverage.map((val: number) => + this.getDisplayWeight(val)).toString(); statsRows.push([ perRecipientStats.recipientTeam, perRecipientStats.recipientName, @@ -135,11 +141,11 @@ ${statsCalculation.hasWeights ? `[${statsCalculation.weights[questionIndex][choi ...statsCalculation.choices.map((_: string, choiceIndex: number) => { return `${perRecipientStats.percentagesAverage[choiceIndex]}% \ (${perRecipientStats.answersSum[choiceIndex]}) \ -[${perRecipientStats.weightsAverage[choiceIndex]}]`; +[${this.getDisplayWeight(perRecipientStats.weightsAverage[choiceIndex])}]`; }), - String(perRecipientStats.overallWeightedSum), - String(perRecipientStats.overallWeightAverage), - String(perRecipientStats.subQuestionWeightAverage.toString()), + String(this.getDisplayWeight(perRecipientStats.overallWeightedSum)), + String(this.getDisplayWeight(perRecipientStats.overallWeightAverage)), + String(perCriterionAverage), ]); }); @@ -153,4 +159,8 @@ ${statsCalculation.hasWeights ? `[${statsCalculation.weights[questionIndex][choi isInstructorCommentsOnResponsesAllowed(): boolean { return true; } + + private getDisplayWeight(weight: number): any { + return weight === null || weight === NO_VALUE ? '-' : weight; + } }