From 4665d61c22bc8ddae5cc24b582d8b5fc7b0cd52e Mon Sep 17 00:00:00 2001 From: Johannes Brandenburger <79154528+johannesbrandenburger@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:59:25 +0100 Subject: [PATCH] fill feedback analytics --- .../mobilelearning/helper/Analytics.java | 82 ++++++++- .../models/QuestionWrapper.java | 10 ++ .../socket/LiveFeedbackSocketTest.java | 170 ++++++++++++++++++ 3 files changed, 257 insertions(+), 5 deletions(-) diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/Analytics.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/Analytics.java index 6c122696..98b30d4b 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/Analytics.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/helper/Analytics.java @@ -1,9 +1,81 @@ package de.htwg_konstanz.mobilelearning.helper; +import java.util.List; +import java.util.ArrayList; + public class Analytics { - public Float avg; - public Float min; - public Float max; - public Float median; - public Float count; + public Double avg; + public Double min; + public Double max; + public Double median; + public Integer count; + + public Analytics() { + this.avg = 0.0; + this.min = 0.0; + this.max = 0.0; + this.median = 0.0; + this.count = 0; + } + + public void update(List values) { + + // reset values + if (values == null || values.size() == 0) { + this.avg = 0.0; + this.min = 0.0; + this.max = 0.0; + this.median = 0.0; + this.count = 0; + return; + } + + // convert values to double + List valuesAsDouble = new ArrayList(); + values.forEach(valueAsString -> { + try { + valuesAsDouble.add(Double.parseDouble(valueAsString)); + } catch (NumberFormatException e) { + // ignore + } + }); + + // if everything is a string, update the count and return + if (valuesAsDouble.size() == 0) { + this.count = values.size(); + return; + } + + // update + this.count = values.size(); + this.min = valuesAsDouble.stream().mapToDouble(Double::doubleValue).min().orElse(0.0); + this.max = valuesAsDouble.stream().mapToDouble(Double::doubleValue).max().orElse(0.0); + this.avg = valuesAsDouble.stream().mapToDouble(Double::doubleValue).average().orElse(0.0); + if (valuesAsDouble.size() % 2 == 0) { + this.median = (valuesAsDouble.get(valuesAsDouble.size() / 2) + valuesAsDouble.get(valuesAsDouble.size() / 2 - 1)) / 2; + } else { + this.median = valuesAsDouble.get(valuesAsDouble.size() / 2); + } + } + + public Double getAvg() { + return this.avg; + } + + public Double getMin() { + return this.min; + } + + public Double getMax() { + return this.max; + } + + public Double getMedian() { + return this.median; + } + + public Integer getCount() { + return this.count; + } + } diff --git a/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/QuestionWrapper.java b/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/QuestionWrapper.java index 2a4f8814..709892de 100644 --- a/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/QuestionWrapper.java +++ b/backend/src/main/java/de/htwg_konstanz/mobilelearning/models/QuestionWrapper.java @@ -55,9 +55,19 @@ public Boolean addResult(Result result) { } this.results.add(result); + + // update analytics + List values = new ArrayList(); + this.results.forEach(r -> { if (r.values != null) { values.addAll(r.values); } }); + this.analytics.update(values); + return true; } + public Analytics getAnalytics() { + return analytics; + } + public void setQuestionContent(Question question) { if (question == null) { this.questionContent = null; diff --git a/backend/src/test/java/de/htwg_konstanz/mobilelearning/services/feedback/socket/LiveFeedbackSocketTest.java b/backend/src/test/java/de/htwg_konstanz/mobilelearning/services/feedback/socket/LiveFeedbackSocketTest.java index d6c69694..91267142 100644 --- a/backend/src/test/java/de/htwg_konstanz/mobilelearning/services/feedback/socket/LiveFeedbackSocketTest.java +++ b/backend/src/test/java/de/htwg_konstanz/mobilelearning/services/feedback/socket/LiveFeedbackSocketTest.java @@ -535,6 +535,176 @@ public void participateInFeedbackForm() { studentSession2.close(); studentSession3.close(); + // check via a get request if the participants are still in the feedback form + Assertions.assertEquals(3, courseService.getCourse(courseId).getFeedbackForms().get(0).getParticipants().size()); + FeedbackForm feedbackFormFromGet = given().header("Authorization", "Bearer " + prof.getJwt()).get("/course/" + courseId + "/feedback/form/" + formId).then().statusCode(200).extract().body().as(FeedbackForm.class); + Assertions.assertEquals(3, feedbackFormFromGet.getParticipants().size()); + + } catch (Exception e) { + System.out.println(e); + Assertions.fail(e.getMessage()); + } + } + + @Test + public void testAnalytics() { + + // create & get courses + List courses = Helper.createCourse("Prof-1"); + Course course = courses.get(0); + + // make 1 prof and 3 students + MockUser prof = Helper.createMockUser("Prof-1"); + MockUser student1 = Helper.createMockUser("Student-1"); + MockUser student2 = Helper.createMockUser("Student-2"); + MockUser student3 = Helper.createMockUser("Student-3"); + MockUser student4 = Helper.createMockUser("Student-4"); + + // call the get courses endpoint for each user to update the course-user relation + given().header("Authorization", "Bearer " + prof.getJwt()).when().get("/course").then().statusCode(200); + given().header("Authorization", "Bearer " + student1.getJwt()).when().get("/course").then().statusCode(200); + given().header("Authorization", "Bearer " + student2.getJwt()).when().get("/course").then().statusCode(200); + given().header("Authorization", "Bearer " + student3.getJwt()).when().get("/course").then().statusCode(200); + given().header("Authorization", "Bearer " + student4.getJwt()).when().get("/course").then().statusCode(200); + + // get course and feedback form id + String courseId = course.getId().toString(); + String formId = course.getFeedbackForms().get(0).getId().toString(); + + // try catch block to handle exceptions of websocket connection + try { + + // create websocket clients + SocketClient profClient = new SocketClient(); + SocketClient studentClient1 = new SocketClient(); + SocketClient studentClient2 = new SocketClient(); + SocketClient studentClient3 = new SocketClient(); + SocketClient studentClient4 = new SocketClient(); + + // connect the prof to the feedback form + Session profSession = ContainerProvider.getWebSocketContainer().connectToServer( + profClient, + URI.create("ws://localhost:8081/course/" + courseId + "/feedback/form/" + formId + "/subscribe/" + prof.getId() + "/" + prof.getJwt()) + ); + Thread.sleep(100); + Assertions.assertTrue(profSession.isOpen()); + + // set the form status to "WAITING" and check if it was set + profClient.sendMessage(""" + { + "action": "CHANGE_FORM_STATUS", + "formStatus": "WAITING" + } + """); + Thread.sleep(100); + Assertions.assertEquals("WAITING", courseService.getCourse(courseId).getFeedbackForms().get(0).getStatus().toString()); + Assertions.assertEquals(0, courseService.getCourse(courseId).getFeedbackForms().get(0).getParticipants().size()); + + // connect the students to the feedback form + Session studentSession1 = ContainerProvider.getWebSocketContainer().connectToServer( + studentClient1, + URI.create("ws://localhost:8081/course/" + courseId + "/feedback/form/" + formId + "/subscribe/" + student1.getId() + "/" + student1.getJwt()) + ); + Thread.sleep(100); + Assertions.assertTrue(studentSession1.isOpen()); + Assertions.assertEquals(1, courseService.getCourse(courseId).getFeedbackForms().get(0).getParticipants().size()); + + Session studentSession2 = ContainerProvider.getWebSocketContainer().connectToServer( + studentClient2, + URI.create("ws://localhost:8081/course/" + courseId + "/feedback/form/" + formId + "/subscribe/" + student2.getId() + "/" + student2.getJwt()) + ); + Thread.sleep(100); + Assertions.assertTrue(studentSession2.isOpen()); + Assertions.assertEquals(2, courseService.getCourse(courseId).getFeedbackForms().get(0).getParticipants().size()); + Session studentSession3 = ContainerProvider.getWebSocketContainer().connectToServer( + studentClient3, + URI.create("ws://localhost:8081/course/" + courseId + "/feedback/form/" + formId + "/subscribe/" + student3.getId() + "/" + student3.getJwt()) + ); + Thread.sleep(100); + Assertions.assertTrue(studentSession3.isOpen()); + Assertions.assertEquals(3, courseService.getCourse(courseId).getFeedbackForms().get(0).getParticipants().size()); + Thread.sleep(100); + Session studentSession4 = ContainerProvider.getWebSocketContainer().connectToServer( + studentClient4, + URI.create("ws://localhost:8081/course/" + courseId + "/feedback/form/" + formId + "/subscribe/" + student4.getId() + "/" + student4.getJwt()) + ); + Thread.sleep(100); + Assertions.assertTrue(studentSession4.isOpen()); + Assertions.assertEquals(4, courseService.getCourse(courseId).getFeedbackForms().get(0).getParticipants().size()); + + // send results to the feedback form (1, 2, 3, 3) + String questionId = course.getFeedbackForms().get(0).getQuestions().get(0).getId().toString(); + studentClient1.sendMessage(String.format(""" + { + "action": "ADD_RESULT", + "resultElementId": %s, + "resultValues": [1] + } + """, questionId)); + Thread.sleep(100); + studentClient2.sendMessage(String.format(""" + { + "action": "ADD_RESULT", + "resultElementId": %s, + "resultValues": [2] + } + """, questionId)); + Thread.sleep(100); + studentClient3.sendMessage(String.format(""" + { + "action": "ADD_RESULT", + "resultElementId": %s, + "resultValues": [3] + } + """, questionId)); + Thread.sleep(100); + studentClient4.sendMessage(String.format(""" + { + "action": "ADD_RESULT", + "resultElementId": %s, + "resultValues": [3] + } + """, questionId)); + Thread.sleep(100); + + + // change the form status to "STARTED" and check if it was set + profClient.sendMessage(""" + { + "action": "CHANGE_FORM_STATUS", + "formStatus": "STARTED" + } + """); + Thread.sleep(100); + Assertions.assertEquals("STARTED", courseService.getCourse(courseId).getFeedbackForms().get(0).getStatus().toString()); + + // close the websocket connections + profSession.close(); + studentSession1.close(); + studentSession2.close(); + studentSession3.close(); + + // check the analytics (avg, min, max, median, count) + Response response = given() + .header("Authorization", "Bearer " + prof.getJwt()) + .pathParam("courseId", courseId) + .pathParam("formId", formId) + .queryParam("analytics", true) + .when() + .get("/course/{courseId}/feedback/form/{formId}"); + FeedbackForm feedbackForm = response + .then() + .statusCode(200) + .extract() + .body() + .as(FeedbackForm.class); + + Assertions.assertEquals(1, feedbackForm.getQuestions().get(0).getAnalytics().getMin()); + Assertions.assertEquals(3, feedbackForm.getQuestions().get(0).getAnalytics().getMax()); + Assertions.assertEquals(2.5, feedbackForm.getQuestions().get(0).getAnalytics().getMedian()); + Assertions.assertEquals(4, feedbackForm.getQuestions().get(0).getAnalytics().getCount()); + Assertions.assertEquals(2.25, feedbackForm.getQuestions().get(0).getAnalytics().getAvg()); + } catch (Exception e) { System.out.println(e);