From a0e37589ee69ef62053636ce3d4562c3934c5c5a Mon Sep 17 00:00:00 2001
From: Raphael Stief <118574504+rstief@users.noreply.github.com>
Date: Tue, 3 Oct 2023 09:52:44 +0200
Subject: [PATCH] Grading: Fix course statistics to show correct numbers in
tooltips (#7159)
---
.../exercise-scores-chart.component.html | 4 +-
.../exercise-scores-chart.component.ts | 63 +++++++++++--------
.../exercise-scores-chart.component.spec.ts | 10 +--
3 files changed, 44 insertions(+), 33 deletions(-)
diff --git a/src/main/webapp/app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component.html b/src/main/webapp/app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component.html
index 7390a72a5907..02a0bccffc96 100644
--- a/src/main/webapp/app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component.html
+++ b/src/main/webapp/app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component.html
@@ -46,12 +46,12 @@
{{ 'artemisApp.exercise-scores-chart.title' | artemisTranslate
>
{{ model.name }}
- {{ model.series }}: {{ Math.max(model.value - 1, 0) }} %
+ {{ model.series }}: {{ Math.max(model.value, 0) }} %
{{ model[0].name }}
- {{ entry.series }}: {{ Math.max(entry.value - 1, 0) }}%
+ {{ entry.series }}: {{ Math.max(entry.value, 0) }}%
{{ 'artemisApp.exercise-scores-chart.exerciseType' | artemisTranslate }}
{{ 'artemisApp.exercise-scores-chart.' + model[0].exerciseType.toLowerCase() | artemisTranslate }} {
const extraInformation = {
- exerciseId: exerciseScoreDTO.exerciseId,
+ exerciseId: exerciseScoreDTO.exerciseId!,
exerciseType: exerciseScoreDTO.exerciseType,
};
// adapt the y-axis max
@@ -132,9 +148,9 @@ export class ExerciseScoresChartComponent implements AfterViewInit, OnChanges {
round(exerciseScoreDTO.maxScoreAchieved!),
this.maxScale,
);
- scoreSeries.push({ name: exerciseScoreDTO.exerciseTitle, value: round(exerciseScoreDTO.scoreOfStudent!) + 1, ...extraInformation });
- averageSeries.push({ name: exerciseScoreDTO.exerciseTitle, value: round(exerciseScoreDTO.averageScoreAchieved!) + 1, ...extraInformation });
- bestScoreSeries.push({ name: exerciseScoreDTO.exerciseTitle, value: round(exerciseScoreDTO.maxScoreAchieved!) + 1, ...extraInformation });
+ scoreSeries.push({ name: exerciseScoreDTO.exerciseTitle!, value: round(exerciseScoreDTO.scoreOfStudent!), ...extraInformation });
+ averageSeries.push({ name: exerciseScoreDTO.exerciseTitle!, value: round(exerciseScoreDTO.averageScoreAchieved!), ...extraInformation });
+ bestScoreSeries.push({ name: exerciseScoreDTO.exerciseTitle!, value: round(exerciseScoreDTO.maxScoreAchieved!), ...extraInformation });
});
const studentScore = { name: this.yourScoreLabel, series: scoreSeries };
@@ -144,7 +160,7 @@ export class ExerciseScoresChartComponent implements AfterViewInit, OnChanges {
this.ngxData.push(averageScore);
this.ngxData.push(bestScore);
this.ngxData = [...this.ngxData];
- this.backUpData = [...this.ngxData];
+ this.backUpData = cloneDeep(this.ngxData);
}
/**
@@ -153,34 +169,29 @@ export class ExerciseScoresChartComponent implements AfterViewInit, OnChanges {
* If the users click on an entry in the legend, the corresponding line disappears or reappears depending on its previous state
* @param data the event sent by the framework
*/
- onSelect(data: any): void {
- // delegate to the corresponding exercise if chart node is clicked
- if (data.exerciseId) {
- this.navigateToExercise(data.exerciseId);
- } else {
- // if a legend label is clicked, the corresponding line has to disappear or reappear
- const name = JSON.parse(JSON.stringify(data)) as string;
+ onSelect(data: ChartNode | string): void {
+ if (typeof data === 'string') {
+ // if a legend label is clicked, the visibility of the corresponding line is toggled
+ const name: string = data;
// find the affected line in the dataset
const index = this.ngxData.findIndex((dataPack: any) => {
const dataName = dataPack.name as string;
return dataName === name;
});
- // check whether the line is currently displayed
if (this.ngxColor.domain[index] !== 'rgba(255,255,255,0)') {
- const placeHolder = cloneDeep(this.ngxData[index]);
- placeHolder.series.forEach((piece: any) => {
- piece.value = 0;
- });
- // exchange actual line with all-zero line and make color transparent
- this.ngxData[index] = placeHolder;
+ //if the line is displayed, remove its values and make it transparent
+ this.ngxData[index].series = [];
this.ngxColor.domain[index] = 'rgba(255,255,255,0)';
} else {
- // if the line is currently hidden, the color and the values are reset
+ // if the line is currently hidden, the values and the color are reset
+ this.ngxData[index].series = cloneDeep(this.backUpData[index].series);
this.ngxColor.domain[index] = this.colorBase[index];
- this.ngxData[index] = this.backUpData[index];
}
// trigger a chart update
this.ngxData = [...this.ngxData];
+ } else {
+ // if a chart node is clicked, navigate to the corresponding exercise
+ this.navigateToExercise(data.exerciseId);
}
}
diff --git a/src/test/javascript/spec/component/overview/course-statistics/visualizations/exercise-scores-chart.component.spec.ts b/src/test/javascript/spec/component/overview/course-statistics/visualizations/exercise-scores-chart.component.spec.ts
index 4cac347eb88d..76416a42e4f9 100644
--- a/src/test/javascript/spec/component/overview/course-statistics/visualizations/exercise-scores-chart.component.spec.ts
+++ b/src/test/javascript/spec/component/overview/course-statistics/visualizations/exercise-scores-chart.component.spec.ts
@@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateService } from '@ngx-translate/core';
import { AlertService } from 'app/core/util/alert.service';
import { MockDirective, MockModule, MockPipe, MockProvider } from 'ng-mocks';
-import { ExerciseScoresChartComponent } from 'app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component';
+import { ChartNode, ExerciseScoresChartComponent } from 'app/overview/visualizations/exercise-scores-chart/exercise-scores-chart.component';
import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
import { of } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
@@ -136,11 +136,11 @@ describe('ExerciseScoresChartComponent', () => {
component.onSelect(legendClickEvent);
expect(component.ngxColor.domain[2]).toBe('rgba(255,255,255,0)');
- expect(component.ngxData[2].series.map((exercise: any) => exercise.value)).toEqual([0, 0]);
+ expect(component.ngxData[2].series).toEqual([]);
component.onSelect(legendClickEvent);
expect(component.ngxColor.domain[2]).toBe(GraphColors.GREEN);
- expect(component.ngxData[2].series.map((exercise: any) => exercise.value)).toEqual([61, 71]);
+ expect(component.ngxData[2].series.map((exercise: any) => exercise.value)).toEqual([60, 70]);
});
it('should react correct if chart point is clicked', () => {
@@ -150,7 +150,7 @@ describe('ExerciseScoresChartComponent', () => {
setUpServiceAndStartComponent([firstExercise, secondExercise]);
const routingService = TestBed.inject(ArtemisNavigationUtilService);
const routingStub = jest.spyOn(routingService, 'routeInNewTab');
- const pointClickEvent = { exerciseId: 2 };
+ const pointClickEvent: ChartNode = { exerciseType: '', name: '', series: '', value: 0, exerciseId: 2 };
component.onSelect(pointClickEvent);
@@ -177,7 +177,7 @@ describe('ExerciseScoresChartComponent', () => {
});
function validateStructureOfDataPoint(dataPoint: any, exerciseScoresDTO: ExerciseScoresDTO, score: number) {
- const expectedStructure = { name: exerciseScoresDTO.exerciseTitle, value: score + 1, exerciseId: exerciseScoresDTO.exerciseId, exerciseType: exerciseScoresDTO.exerciseType };
+ const expectedStructure = { name: exerciseScoresDTO.exerciseTitle, value: score, exerciseId: exerciseScoresDTO.exerciseId, exerciseType: exerciseScoresDTO.exerciseType };
expect(dataPoint).toEqual(expectedStructure);
}