diff --git a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomApiMappings.kt b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomApiMappings.kt index b6ac131..5392e25 100644 --- a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomApiMappings.kt +++ b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomApiMappings.kt @@ -79,7 +79,10 @@ fun Classroom.toResponse(): ClassroomResponse = * * @return a detailed classroom response DTO. */ -fun Classroom.toDetailResponse(pupilDisplayNames: Map = emptyMap()): ClassroomDetailResponse = +fun Classroom.toDetailResponse( + pupilDisplayNames: Map = emptyMap(), + pupilProgressByUserId: Map = emptyMap(), +): ClassroomDetailResponse = ClassroomDetailResponse( id = id, title = title, @@ -98,10 +101,13 @@ fun Classroom.toDetailResponse(pupilDisplayNames: Map = emptyMap() pupilMemberships.mapNotNull { membership -> val pupil = membership.pupil ?: return@mapNotNull null val pupilId = pupil.id ?: return@mapNotNull null + val progress = pupilProgressByUserId[pupilId] ClassroomPupilResponse( userId = pupilId, displayName = pupilDisplayNames[pupilId] ?: pupil.username ?: "Unknown", + currentLevel = progress?.currentLevel ?: 1, + completedStopsCount = progress?.completedStopsCount ?: 0, ) }, ) diff --git a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomController.kt b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomController.kt index ee2edb4..b058501 100644 --- a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomController.kt +++ b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomController.kt @@ -108,7 +108,7 @@ class ClassroomController( @AuthenticationPrincipal currentUser: CurrentUser, ): ClassroomDetailResponse = classroomService.getClassroomById(id, currentUser.subject).let { detail -> - detail.classroom.toDetailResponse(detail.pupilDisplayNames) + detail.classroom.toDetailResponse(detail.pupilDisplayNames, detail.pupilProgressByUserId) } /** diff --git a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomPupilResponse.kt b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomPupilResponse.kt index 5186022..b2cde7b 100644 --- a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomPupilResponse.kt +++ b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/api/ClassroomPupilResponse.kt @@ -9,4 +9,6 @@ package edu.ntnu.idi.idatt.backend.classroom.api data class ClassroomPupilResponse( val userId: Long, val displayName: String, + val currentLevel: Int, + val completedStopsCount: Int, ) diff --git a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/application/ClassroomDetail.kt b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/application/ClassroomDetail.kt index b61adf4..babb7c2 100644 --- a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/application/ClassroomDetail.kt +++ b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/application/ClassroomDetail.kt @@ -1,14 +1,17 @@ package edu.ntnu.idi.idatt.backend.classroom.application import edu.ntnu.idi.idatt.backend.classroom.domain.Classroom +import edu.ntnu.idi.idatt.backend.game.gameProgress.domain.PupilGameProgress /** * Classroom detail model enriched with presentation data owned by other domains. * * @property classroom classroom entity after access checks have passed. * @property pupilDisplayNames display names keyed by pupil user id. + * @property pupilProgressByUserId classroom-scoped pupil progress keyed by pupil user id. */ data class ClassroomDetail( val classroom: Classroom, val pupilDisplayNames: Map, + val pupilProgressByUserId: Map, ) diff --git a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/application/ClassroomService.kt b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/application/ClassroomService.kt index 1dd935b..b69cd23 100644 --- a/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/application/ClassroomService.kt +++ b/src/main/kotlin/edu/ntnu/idi/idatt/backend/classroom/application/ClassroomService.kt @@ -269,9 +269,17 @@ class ClassroomService( ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Classroom not found") val pupilIds = classroom.pupilMemberships.mapNotNull { membership -> membership.pupil?.id } + val resolvedClassroomId = requireNotNull(classroom.id) { "Classroom is missing an id" } return ClassroomDetail( classroom = classroom, pupilDisplayNames = userProfileService.findPupilDisplayNames(pupilIds), + pupilProgressByUserId = + pupilIds + .associateWith { pupilId -> + pupilGameProgressService.getPupilProgress(pupilId, resolvedClassroomId) + }.mapNotNull { (pupilId, progress) -> + progress?.let { pupilId to it } + }.toMap(), ) } diff --git a/src/test/kotlin/edu/ntnu/idi/idatt/backend/classroom/ClassroomIntegrationTests.kt b/src/test/kotlin/edu/ntnu/idi/idatt/backend/classroom/ClassroomIntegrationTests.kt index aa1f966..a96701a 100644 --- a/src/test/kotlin/edu/ntnu/idi/idatt/backend/classroom/ClassroomIntegrationTests.kt +++ b/src/test/kotlin/edu/ntnu/idi/idatt/backend/classroom/ClassroomIntegrationTests.kt @@ -289,6 +289,15 @@ class ClassroomIntegrationTests { val classroom = createClassroom("DETAIL1") val pupil = userRepository.save(activePupil(username = "detailuser")) pupilProfileRepository.save(PupilProfile(user = pupil, displayName = "detail", currentClassroom = classroom)) + pupilGameProgressRepository.save( + PupilGameProgress( + id = PupilGameProgressId(pupilUserId = requireNotNull(pupil.id), classroomId = requireNotNull(classroom.id)), + pupil = pupil, + classroom = classroom, + currentLevel = 4, + completedStopsCount = 2, + ), + ) val token = pupilAccessToken(username = "detailuser", password = "secret123") mockMvc @@ -308,6 +317,9 @@ class ClassroomIntegrationTests { status { isOk() } jsonPath("$.id") { value(classroom.id.toInt()) } jsonPath("$.title") { value("Class DETAIL1") } + jsonPath("$.pupils[0].displayName") { value("detail") } + jsonPath("$.pupils[0].currentLevel") { value(4) } + jsonPath("$.pupils[0].completedStopsCount") { value(2) } } } diff --git a/src/test/kotlin/edu/ntnu/idi/idatt/backend/medals/MedalEventListenerTests.kt b/src/test/kotlin/edu/ntnu/idi/idatt/backend/medals/MedalEventListenerTests.kt index 88f27c8..c74e246 100644 --- a/src/test/kotlin/edu/ntnu/idi/idatt/backend/medals/MedalEventListenerTests.kt +++ b/src/test/kotlin/edu/ntnu/idi/idatt/backend/medals/MedalEventListenerTests.kt @@ -24,7 +24,7 @@ class MedalEventListenerTests { val classroom = classroom() Mockito.`when`(currentUserResolver.requireUser("1")).thenReturn(pupil) - Mockito.`when`(classroomService.getClassroomById(2L, "1")).thenReturn(ClassroomDetail(classroom, emptyMap())) + Mockito.`when`(classroomService.getClassroomById(2L, "1")).thenReturn(ClassroomDetail(classroom, emptyMap(), emptyMap())) listener.onMapStopCompleted(MapStopCompletedEvent(pupilUserId = 1L, classroomId = 2L, stopId = 3L)) @@ -41,7 +41,7 @@ class MedalEventListenerTests { val classroom = classroom() Mockito.`when`(currentUserResolver.requireUser("1")).thenReturn(pupil) - Mockito.`when`(classroomService.getClassroomById(2L, "1")).thenReturn(ClassroomDetail(classroom, emptyMap())) + Mockito.`when`(classroomService.getClassroomById(2L, "1")).thenReturn(ClassroomDetail(classroom, emptyMap(), emptyMap())) listener.onMapStopCompleted(MapStopCompletedEvent(pupilUserId = 1L, classroomId = 2L, stopId = 3L)) Mockito.verify(currentUserResolver).requireUser("1")