diff --git a/src/main/java/de/tum/in/www1/artemis/service/CourseScoreCalculationService.java b/src/main/java/de/tum/in/www1/artemis/service/CourseScoreCalculationService.java index a8eac7e14031..1211fbcf9653 100644 --- a/src/main/java/de/tum/in/www1/artemis/service/CourseScoreCalculationService.java +++ b/src/main/java/de/tum/in/www1/artemis/service/CourseScoreCalculationService.java @@ -230,12 +230,16 @@ public CourseForDashboardDTO getScoresAndParticipationResults(Course course, Gra Map scoresPerExerciseType = calculateCourseScoresPerExerciseType(course, gradedStudentParticipations, userId, plagiarismCases); // Get participation results (used in course-statistics.component). - List participationResults = new ArrayList<>(); + List participationResults = new ArrayList<>(); for (StudentParticipation studentParticipation : gradedStudentParticipations) { if (studentParticipation.getResults() != null && !studentParticipation.getResults().isEmpty()) { - Result participationResult = getResultForParticipation(studentParticipation, studentParticipation.getIndividualDueDate()); - participationResult.setParticipation(studentParticipation); + Result result = getResultForParticipation(studentParticipation, studentParticipation.getIndividualDueDate()); + var participationResult = new ParticipationResultDTO(result.getScore(), result.isRated(), studentParticipation.getId()); participationResults.add(participationResult); + // this line is an important workaround. It prevents that the whole tree + // "result -> participation -> exercise -> course -> exercises -> studentParticipations -> submissions -> results" is sent again to the client which is useless + // TODO: in the future, we need a better solution to prevent this + studentParticipation.setExercise(null); } } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/CourseForDashboardDTO.java b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/CourseForDashboardDTO.java index cc1c848192f1..b1ab83e8873b 100644 --- a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/CourseForDashboardDTO.java +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/CourseForDashboardDTO.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import de.tum.in.www1.artemis.domain.Course; -import de.tum.in.www1.artemis.domain.Result; /** * Returned by the for-dashboard resources. @@ -23,5 +22,5 @@ */ @JsonInclude(JsonInclude.Include.NON_EMPTY) public record CourseForDashboardDTO(Course course, CourseScoresDTO totalScores, CourseScoresDTO textScores, CourseScoresDTO programmingScores, CourseScoresDTO modelingScores, - CourseScoresDTO fileUploadScores, CourseScoresDTO quizScores, List participationResults) { + CourseScoresDTO fileUploadScores, CourseScoresDTO quizScores, List participationResults) { } diff --git a/src/main/java/de/tum/in/www1/artemis/web/rest/dto/ParticipationResultDTO.java b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/ParticipationResultDTO.java new file mode 100644 index 000000000000..7549af09689b --- /dev/null +++ b/src/main/java/de/tum/in/www1/artemis/web/rest/dto/ParticipationResultDTO.java @@ -0,0 +1,7 @@ +package de.tum.in.www1.artemis.web.rest.dto; + +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public record ParticipationResultDTO(Double score, Boolean rated, Long participationId) { +} diff --git a/src/main/webapp/app/course/course-scores/scores-storage.service.ts b/src/main/webapp/app/course/course-scores/scores-storage.service.ts index ca04bc0ba8f5..450e37895ae3 100644 --- a/src/main/webapp/app/course/course-scores/scores-storage.service.ts +++ b/src/main/webapp/app/course/course-scores/scores-storage.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; +import { ParticipationResultDTO } from 'app/course/manage/course-for-dashboard-dto'; import { ScoresPerExerciseType } from 'app/entities/exercise.model'; -import { Result } from 'app/entities/result.model'; import { CourseScores } from 'app/course/course-scores/course-scores'; /** @@ -24,7 +24,7 @@ export class ScoresStorageService { /** * This map stores the {@link Result} object for each {@link Participation} of the currently logged-in user. The number is the id of the participation. */ - private storedParticipationResults: Map = new Map(); + private storedParticipationResults: Map = new Map(); getStoredTotalScores(courseId: number): CourseScores | undefined { return this.storedTotalScores.get(courseId); @@ -42,13 +42,13 @@ export class ScoresStorageService { this.storedScoresPerExerciseType.set(courseId, scoresPerExerciseType); } - getStoredParticipationResult(participationId: number): Result | undefined { + getStoredParticipationResult(participationId: number): ParticipationResultDTO | undefined { return this.storedParticipationResults.get(participationId); } - setStoredParticipationResults(participationResults?: Result[]): void { - for (const result of participationResults ?? []) { - this.storedParticipationResults.set(result.participation!.id!, result); + setStoredParticipationResults(participationResults?: ParticipationResultDTO[]): void { + for (const participationResult of participationResults ?? []) { + this.storedParticipationResults.set(participationResult.participationId, participationResult); } } } diff --git a/src/main/webapp/app/course/manage/course-for-dashboard-dto.ts b/src/main/webapp/app/course/manage/course-for-dashboard-dto.ts index 7e088b33864d..67cc0c6bed68 100644 --- a/src/main/webapp/app/course/manage/course-for-dashboard-dto.ts +++ b/src/main/webapp/app/course/manage/course-for-dashboard-dto.ts @@ -1,6 +1,5 @@ import { Course } from 'app/entities/course.model'; import { CourseScores } from 'app/course/course-scores/course-scores'; -import { Result } from 'app/entities/result.model'; export class CourseForDashboardDTO { course: Course; @@ -13,7 +12,14 @@ export class CourseForDashboardDTO { fileUploadScores: CourseScores; quizScores: CourseScores; - participationResults: Result[]; + participationResults: ParticipationResultDTO[]; constructor() {} } + +export class ParticipationResultDTO { + score?: number; + rated?: boolean; + participationId: number; + constructor() {} +} diff --git a/src/main/webapp/app/overview/course-statistics/course-statistics.component.ts b/src/main/webapp/app/overview/course-statistics/course-statistics.component.ts index eb300095e249..ff5917b43052 100644 --- a/src/main/webapp/app/overview/course-statistics/course-statistics.component.ts +++ b/src/main/webapp/app/overview/course-statistics/course-statistics.component.ts @@ -5,6 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { Color, ScaleType } from '@swimlane/ngx-charts'; import { CourseScores } from 'app/course/course-scores/course-scores'; import { ScoresStorageService } from 'app/course/course-scores/scores-storage.service'; +import { ParticipationResultDTO } from 'app/course/manage/course-for-dashboard-dto'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { Course } from 'app/entities/course.model'; import { Exercise, ExerciseType, IncludedInOverallScore, ScoresPerExerciseType } from 'app/entities/exercise.model'; @@ -12,7 +13,6 @@ import { GradeDTO } from 'app/entities/grade-step.model'; import { GradeType } from 'app/entities/grading-scale.model'; import { InitializationState } from 'app/entities/participation/participation.model'; import { StudentParticipation } from 'app/entities/participation/student-participation.model'; -import { Result } from 'app/entities/result.model'; import { GraphColors } from 'app/entities/statistics.model'; import { GradingSystemService } from 'app/grading-system/grading-system.service'; import { BarControlConfiguration, BarControlConfigurationProvider } from 'app/shared/tab-bar/tab-bar'; @@ -326,7 +326,7 @@ export class CourseStatisticsComponent implements OnInit, OnDestroy, AfterViewIn } else { exercise.studentParticipations.forEach((participation: StudentParticipation) => { if (participation.id && participation.results?.length) { - const participationResult: Result | undefined = this.scoresStorageService.getStoredParticipationResult(participation.id); + const participationResult: ParticipationResultDTO | undefined = this.scoresStorageService.getStoredParticipationResult(participation.id); if (participationResult?.rated) { const roundedParticipationScore = roundValueSpecifiedByCourseSettings(participationResult.score!, this.course); const cappedParticipationScore = Math.min(roundedParticipationScore, 100); diff --git a/src/test/javascript/spec/component/course/course-management.service.spec.ts b/src/test/javascript/spec/component/course/course-management.service.spec.ts index 41d6d0fa6ec2..225dc898fd96 100644 --- a/src/test/javascript/spec/component/course/course-management.service.spec.ts +++ b/src/test/javascript/spec/component/course/course-management.service.spec.ts @@ -21,11 +21,10 @@ import { MockRouter } from '../../helpers/mocks/mock-router'; import { MockSyncStorage } from '../../helpers/mocks/service/mock-sync-storage.service'; import { MockTranslateService } from '../../helpers/mocks/service/mock-translate.service'; import { OnlineCourseConfiguration } from 'app/entities/online-course-configuration.model'; -import { CourseForDashboardDTO } from 'app/course/manage/course-for-dashboard-dto'; +import { CourseForDashboardDTO, ParticipationResultDTO } from 'app/course/manage/course-for-dashboard-dto'; import { CourseScores } from 'app/course/course-scores/course-scores'; import { ScoresStorageService } from 'app/course/course-scores/scores-storage.service'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; -import { Result } from 'app/entities/result.model'; describe('Course Management Service', () => { let courseManagementService: CourseManagementService; @@ -48,7 +47,7 @@ describe('Course Management Service', () => { let courseForDashboard: CourseForDashboardDTO; let courseScores: CourseScores; let scoresPerExerciseType: ScoresPerExerciseType; - let participationResult: Result; + let participationResult: ParticipationResultDTO; let onlineCourseConfiguration: OnlineCourseConfiguration; let exercises: Exercise[]; let returnedFromService: any; @@ -96,10 +95,8 @@ describe('Course Management Service', () => { courseForDashboard.quizScores = courseScores; courseForDashboard.textScores = courseScores; courseForDashboard.fileUploadScores = courseScores; - participationResult = new Result(); - const participation = new StudentParticipation(); - participation.id = 432; - participationResult.participation = participation; + participationResult = new ParticipationResultDTO(); + participationResult.participationId = 432; courseForDashboard.participationResults = [participationResult]; scoresPerExerciseType = new Map(); diff --git a/src/test/javascript/spec/component/overview/course-statistics/course-statistics.component.spec.ts b/src/test/javascript/spec/component/overview/course-statistics/course-statistics.component.spec.ts index 0e105bcae3bb..298bc6cf7fef 100644 --- a/src/test/javascript/spec/component/overview/course-statistics/course-statistics.component.spec.ts +++ b/src/test/javascript/spec/component/overview/course-statistics/course-statistics.component.spec.ts @@ -7,6 +7,7 @@ import { BarChartModule, PieChartModule } from '@swimlane/ngx-charts'; import { CourseScores } from 'app/course/course-scores/course-scores'; import { ScoresStorageService } from 'app/course/course-scores/scores-storage.service'; import { DueDateStat } from 'app/course/dashboards/due-date-stat.model'; +import { ParticipationResultDTO } from 'app/course/manage/course-for-dashboard-dto'; import { CourseStorageService } from 'app/course/manage/course-storage.service'; import { Course } from 'app/entities/course.model'; import { ExerciseCategory } from 'app/entities/exercise-category.model'; @@ -15,7 +16,6 @@ import { FileUploadExercise } from 'app/entities/file-upload-exercise.model'; import { ModelingExercise } from 'app/entities/modeling-exercise.model'; import { ProgrammingExercise } from 'app/entities/programming-exercise.model'; import { QuizExercise } from 'app/entities/quiz/quiz-exercise.model'; -import { Result } from 'app/entities/result.model'; import { TreeviewModule } from 'app/exercises/programming/shared/code-editor/treeview/treeview.module'; import { CourseCompetenciesComponent } from 'app/overview/course-competencies/course-competencies.component'; import { CourseStatisticsComponent, NgxExercise } from 'app/overview/course-statistics/course-statistics.component'; @@ -360,7 +360,7 @@ describe('CourseStatisticsComponent', () => { const courseToAdd = { ...course }; courseToAdd.exercises = [programmingExercise, quizExercise, ...modelingExercises, fileUploadExercise]; jest.spyOn(courseStorageService, 'getCourse').mockReturnValue(courseToAdd); - const mockParticipationResult: Result = { rated: true, score: 100 }; + const mockParticipationResult: ParticipationResultDTO = { rated: true, score: 100, participationId: 1 }; jest.spyOn(scoresStorageService, 'getStoredParticipationResult').mockReturnValue(mockParticipationResult); fixture.detectChanges(); comp.ngOnInit(); @@ -384,7 +384,7 @@ describe('CourseStatisticsComponent', () => { const courseToAdd = { ...course }; courseToAdd.exercises = [...modelingExercises]; jest.spyOn(courseStorageService, 'getCourse').mockReturnValue(courseToAdd); - const mockParticipationResult: Result = { rated: true, score: 100 }; + const mockParticipationResult: ParticipationResultDTO = { rated: true, score: 100, participationId: 1 }; jest.spyOn(scoresStorageService, 'getStoredParticipationResult').mockReturnValue(mockParticipationResult); fixture.detectChanges(); comp.ngOnInit(); @@ -480,7 +480,7 @@ describe('CourseStatisticsComponent', () => { }); it('should filter optional exercises correctly', () => { - const mockParticipationResult: Result = { rated: true, score: 100 }; + const mockParticipationResult: ParticipationResultDTO = { rated: true, score: 100, participationId: 1 }; jest.spyOn(scoresStorageService, 'getStoredParticipationResult').mockReturnValue(mockParticipationResult); comp.toggleNotIncludedInScoreExercises(); diff --git a/src/test/javascript/spec/service/scores-storage.service.spec.ts b/src/test/javascript/spec/service/scores-storage.service.spec.ts index 060d5595e09f..9aee460f9a59 100644 --- a/src/test/javascript/spec/service/scores-storage.service.spec.ts +++ b/src/test/javascript/spec/service/scores-storage.service.spec.ts @@ -12,10 +12,10 @@ describe('ScoresStorageService', () => { participation2.id = 2; scoresStorageService.setStoredParticipationResults([ - { successful: true, participation: participation1 }, - { successful: false, participation: participation2 }, + { score: 100, participationId: participation1.id }, + { score: 0, participationId: participation2.id }, ]); - expect(scoresStorageService.getStoredParticipationResult(1)).toEqual({ successful: true, participation: participation1 }); + expect(scoresStorageService.getStoredParticipationResult(1)).toEqual({ score: 100, participationId: participation1.id }); // Should return undefined for an unknown participation id. expect(scoresStorageService.getStoredParticipationResult(3)).toBeUndefined(); }); @@ -24,7 +24,7 @@ describe('ScoresStorageService', () => { const scoresStorageService = new ScoresStorageService(); const participation = new StudentParticipation(); participation.id = 234; - scoresStorageService.setStoredParticipationResults([{ successful: true, participation }]); + scoresStorageService.setStoredParticipationResults([{ score: 100, participationId: participation.id }]); expect(scoresStorageService.getStoredParticipationResult(1)).toBeUndefined(); }); });