From 9cc16c81bc405c83e31d7852109cadd662fa6961 Mon Sep 17 00:00:00 2001 From: mp-dg <57503145+MP-DG@users.noreply.github.com> Date: Thu, 15 Jan 2026 23:22:47 +0100 Subject: [PATCH 1/4] Bugfix: No longer sends multiple StudentCodeSubmittedEvents on page reload --- .../CodeAssignmentGradingMetadataEntity.java | 3 ++ .../service/GradingService.java | 53 ++++++++++++++----- .../code_assignment/ExternalGrading.java | 9 +++- .../code_assignment/GithubClassroom.java | 7 +-- ...odeGradingForAssignmentForStudentTest.java | 5 +- .../GradingServiceEventPublishingTest.java | 15 ------ 6 files changed, 58 insertions(+), 34 deletions(-) diff --git a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/persistence/entity/grading/CodeAssignmentGradingMetadataEntity.java b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/persistence/entity/grading/CodeAssignmentGradingMetadataEntity.java index ca73ff6..f21d49f 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/persistence/entity/grading/CodeAssignmentGradingMetadataEntity.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/persistence/entity/grading/CodeAssignmentGradingMetadataEntity.java @@ -37,4 +37,7 @@ public class CodeAssignmentGradingMetadataEntity { @Column(nullable = true, columnDefinition = "TEXT") private String feedbackTableHtml; + + @Column(nullable = true) + private String lastProcessedCommitSha; } \ No newline at end of file diff --git a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java index c3a640b..d00b0a9 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java @@ -250,20 +250,47 @@ private List getCodeAssignmentGradingForStudent(final AssignmentEntity assignment.setTotalCredits(externalGrading.totalPoints()); } - try { - if (codeAssessmentProvider instanceof de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.GithubClassroom githubClassroom) { - de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.StudentCodeSubmission codeSubmission = - githubClassroom.fetchStudentCode(gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink(), currentUser); - - - codeSubmission.setAssignmentId(assignment.getId()); - codeSubmission.setCourseId(assignment.getCourseId()); - - publishStudentCodeSubmittedEvent(codeSubmission); + String lastProcessedCommit = metadata.getLastProcessedCommitSha(); + String currentCommit = externalGrading.commitSha(); + boolean shouldSendEvent = false; + + if (currentCommit != null) { + if (lastProcessedCommit == null || lastProcessedCommit.equals("NO_COMMIT_SHA_PROCESSED")) { + shouldSendEvent = true; + } else if (!currentCommit.equals(lastProcessedCommit)) { + shouldSendEvent = true; + } else { + log.debug("Skipping code submission event. Commit {} already processed for student {} on assignment {}", + currentCommit, currentUser.getId(), assignment.getId()); + } + } else { + if (lastProcessedCommit == null || lastProcessedCommit.equals("NO_COMMIT_SHA_PROCESSED")) { + shouldSendEvent = true; + log.warn("No commit SHA available for assignment {} and student {}, sending event without commit tracking", + assignment.getId(), currentUser.getId()); + } else { + log.debug("No commit SHA and event already sent once for student {} on assignment {}, skipping", + currentUser.getId(), assignment.getId()); + } + } + + if (shouldSendEvent) { + try { + if (codeAssessmentProvider instanceof de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.GithubClassroom githubClassroom) { + de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.StudentCodeSubmission codeSubmission = + githubClassroom.fetchStudentCode(gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink(), currentUser); + + codeSubmission.setAssignmentId(assignment.getId()); + codeSubmission.setCourseId(assignment.getCourseId()); + + publishStudentCodeSubmittedEvent(codeSubmission); + + metadata.setLastProcessedCommitSha(currentCommit != null ? currentCommit : "NO_COMMIT_SHA_PROCESSED"); + } + } catch (ExternalPlatformConnectionException | UserServiceConnectionException e) { + log.error("Failed to fetch student code for assignment {} and student {}: {}", + assignment.getId(), currentUser.getId(), e.toString()); } - } catch (ExternalPlatformConnectionException | UserServiceConnectionException e) { - log.error("Failed to fetch student code for assignment {} and student {}: {}", - assignment.getId(), currentUser.getId(), e.toString()); } } diff --git a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/code_assignment/ExternalGrading.java b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/code_assignment/ExternalGrading.java index 3497d87..218ba84 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/code_assignment/ExternalGrading.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/code_assignment/ExternalGrading.java @@ -2,5 +2,12 @@ import java.time.OffsetDateTime; -public record ExternalGrading(String externalUsername, String status, OffsetDateTime date, String tableHtml, Double achievedPoints, Double totalPoints) { +public record ExternalGrading( + String externalUsername, + String status, + OffsetDateTime date, + String tableHtml, + Double achievedPoints, + Double totalPoints, + String commitSha) { } \ No newline at end of file diff --git a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/code_assignment/GithubClassroom.java b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/code_assignment/GithubClassroom.java index b5ed338..178ec0c 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/code_assignment/GithubClassroom.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/code_assignment/GithubClassroom.java @@ -280,7 +280,7 @@ public List syncGrades(final String externalAssignmentId, final } } - gradings.add(new ExternalGrading(username, null, submissionDate, null, achieved, total)); + gradings.add(new ExternalGrading(username, null, submissionDate, null, achieved, total, null)); } return gradings; @@ -333,9 +333,10 @@ public ExternalGrading syncGradeForStudent(String repoLink, LoggedInUser current String status = run.get("status").getAsString(); String logsUrl = run.get("logs_url").getAsString(); String lastlyTested = run.get("updated_at").getAsString(); + String commitSha = run.has("head_sha") ? run.get("head_sha").getAsString() : null; if (!status.equals("completed")){ - return new ExternalGrading(null, status, OffsetDateTime.parse(lastlyTested), null, null, null); + return new ExternalGrading(null, status, OffsetDateTime.parse(lastlyTested), null, null, null, commitSha); } // Download logs @@ -377,7 +378,7 @@ public ExternalGrading syncGradeForStudent(String repoLink, LoggedInUser current double totalPoints = Double.parseDouble(matcher.group(1)); double maxPoints = Double.parseDouble(matcher.group(2)); String tableHtml = extractGradingTableAsHtml(logs); - return new ExternalGrading(null, status, OffsetDateTime.parse(lastlyTested), tableHtml, totalPoints, maxPoints); + return new ExternalGrading(null, status, OffsetDateTime.parse(lastlyTested), tableHtml, totalPoints, maxPoints, commitSha); } else { throw new ExternalPlatformConnectionException("Could not find totalPoints/maxPoints in logs."); } diff --git a/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/api/QueryGetCodeGradingForAssignmentForStudentTest.java b/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/api/QueryGetCodeGradingForAssignmentForStudentTest.java index 9d2c29c..e84fe86 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/api/QueryGetCodeGradingForAssignmentForStudentTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/api/QueryGetCodeGradingForAssignmentForStudentTest.java @@ -116,7 +116,7 @@ void testStudentGetsCodeAssignmentGradingSynced(GraphQlTester tester) throws Ext ); ExternalGrading externalGrading = new ExternalGrading("ext-user", gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink(), OffsetDateTime.now(), - "feedback
", 42.0, 60.0); + "feedback
", 42.0, 60.0, "test-commit-sha"); when(codeAssessmentProvider.findRepository(eq(assignment.getExternalId()), any(), any())).thenReturn(gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink()); @@ -227,7 +227,8 @@ void testStudentGetsCodeAssignmentGradingCreated(GraphQlTester tester) OffsetDateTime.now().truncatedTo(ChronoUnit.SECONDS), "feedback
", 35.0, - 50.0 + 50.0, + "test-commit-sha-2" ); when(codeAssessmentProvider.syncGradeForStudent(eq("https://github.com/user/repo"), any())) .thenReturn(externalGrading); diff --git a/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingServiceEventPublishingTest.java b/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingServiceEventPublishingTest.java index 8793e85..71ddc0e 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingServiceEventPublishingTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingServiceEventPublishingTest.java @@ -42,11 +42,9 @@ void setUp() { @Test void testPublishStudentCodeSubmittedEvent_Success() throws Exception { - // Arrange String repoUrl = "https://github.com/org/repo-student"; StudentCodeSubmission codeSubmission = createMockCodeSubmission(repoUrl); - // Create the event that would be published StudentCodeSubmittedEvent event = StudentCodeSubmittedEvent.builder() .studentId(codeSubmission.getStudentId()) .assignmentId(codeSubmission.getAssignmentId()) @@ -58,10 +56,8 @@ void testPublishStudentCodeSubmittedEvent_Success() throws Exception { .branch(codeSubmission.getBranch()) .build(); - // Act topicPublisher.notifyStudentCodeSubmitted(event); - // Assert ArgumentCaptor eventCaptor = ArgumentCaptor.forClass(StudentCodeSubmittedEvent.class); verify(topicPublisher, times(1)).notifyStudentCodeSubmitted(eventCaptor.capture()); @@ -80,7 +76,6 @@ void testPublishStudentCodeSubmittedEvent_Success() throws Exception { @Test void testCodeSubmissionEventContent_AllFieldsPresent() { - // Arrange String repoUrl = "https://github.com/test/repo"; String commitSha = "abc123def456"; OffsetDateTime commitTime = OffsetDateTime.now(); @@ -89,7 +84,6 @@ void testCodeSubmissionEventContent_AllFieldsPresent() { Map files = new HashMap<>(); files.put("Test.java", "public class Test {}"); - // Act StudentCodeSubmittedEvent event = StudentCodeSubmittedEvent.builder() .studentId(studentId) .assignmentId(assignmentId) @@ -101,7 +95,6 @@ void testCodeSubmissionEventContent_AllFieldsPresent() { .branch(branch) .build(); - // Assert assertEquals(studentId, event.getStudentId()); assertEquals(assignmentId, event.getAssignmentId()); assertEquals(courseId, event.getCourseId()); @@ -114,7 +107,6 @@ void testCodeSubmissionEventContent_AllFieldsPresent() { @Test void testCodeSubmission_MultipleFiles() { - // Arrange StudentCodeSubmission codeSubmission = StudentCodeSubmission.builder() .studentId(studentId) .assignmentId(assignmentId) @@ -126,7 +118,6 @@ void testCodeSubmission_MultipleFiles() { .files(createMultipleFiles()) .build(); - // Act StudentCodeSubmittedEvent event = StudentCodeSubmittedEvent.builder() .studentId(codeSubmission.getStudentId()) .assignmentId(codeSubmission.getAssignmentId()) @@ -138,7 +129,6 @@ void testCodeSubmission_MultipleFiles() { .branch(codeSubmission.getBranch()) .build(); - // Assert assertNotNull(event.getFiles()); assertEquals(5, event.getFiles().size()); assertTrue(event.getFiles().containsKey("src/Main.java")); @@ -150,7 +140,6 @@ void testCodeSubmission_MultipleFiles() { @Test void testCodeSubmission_EmptyFiles() { - // Arrange StudentCodeSubmission codeSubmission = StudentCodeSubmission.builder() .studentId(studentId) .assignmentId(assignmentId) @@ -162,7 +151,6 @@ void testCodeSubmission_EmptyFiles() { .files(new HashMap<>()) .build(); - // Act StudentCodeSubmittedEvent event = StudentCodeSubmittedEvent.builder() .studentId(codeSubmission.getStudentId()) .assignmentId(codeSubmission.getAssignmentId()) @@ -174,14 +162,12 @@ void testCodeSubmission_EmptyFiles() { .branch(codeSubmission.getBranch()) .build(); - // Assert assertNotNull(event.getFiles()); assertTrue(event.getFiles().isEmpty()); } @Test void testCodeSubmission_VerifyMetadata() { - // Arrange String repoUrl = "https://github.com/student/assignment-repo"; String commitSha = "1234567890abcdef"; OffsetDateTime timestamp = OffsetDateTime.parse("2025-12-15T10:30:00Z"); @@ -198,7 +184,6 @@ void testCodeSubmission_VerifyMetadata() { .files(Map.of("Main.java", "code")) .build(); - // Assert assertEquals(repoUrl, submission.getRepositoryUrl()); assertEquals(commitSha, submission.getCommitSha()); assertEquals(timestamp, submission.getCommitTimestamp()); From d0124fd5ede7164cc0aff3c81e2327ff619b1828 Mon Sep 17 00:00:00 2001 From: mp-dg <57503145+MP-DG@users.noreply.github.com> Date: Fri, 16 Jan 2026 00:07:19 +0100 Subject: [PATCH 2/4] Refactored getCodeAssignmentGradingForStudent --- .../service/GradingService.java | 232 +++++++++++------- 1 file changed, 150 insertions(+), 82 deletions(-) diff --git a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java index d00b0a9..334e32b 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java @@ -189,7 +189,24 @@ private List getCodeAssignmentGradingForAdmin(final AssignmentEntity as private List getCodeAssignmentGradingForStudent(final AssignmentEntity assignment, final LoggedInUser currentUser) { - final GradingEntity.PrimaryKey pk = new GradingEntity.PrimaryKey(assignment.getId(), currentUser.getId()); + GradingEntity gradingEntity = ensureGradingEntityExists(assignment.getId(), currentUser.getId()); + + findAndSetRepositoryLinkIfMissing(gradingEntity, assignment, currentUser); + + if (hasRepositoryLink(gradingEntity)) { + syncAndUpdateGrading(gradingEntity, assignment, currentUser); + } + + gradingEntity = gradingRepository.save(gradingEntity); + return List.of(assignmentMapper.gradingEntityToDto(gradingEntity)); + } + + /** + * Ensures a grading entity exists for the given assignment and student. + * Creates a new entity with metadata if it doesn't exist. + */ + private GradingEntity ensureGradingEntityExists(final UUID assignmentId, final UUID studentId) { + final GradingEntity.PrimaryKey pk = new GradingEntity.PrimaryKey(assignmentId, studentId); GradingEntity gradingEntity = gradingRepository.findById(pk).orElse(null); if (gradingEntity == null) { @@ -205,97 +222,148 @@ private List getCodeAssignmentGradingForStudent(final AssignmentEntity gradingEntity.setCodeAssignmentGradingMetadata(metadata); } - if (gradingEntity.getCodeAssignmentGradingMetadata() == null || - gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink() == null) { - try { - String assignmentName = contentServiceClient.queryContentsOfCourse(currentUser.getId(), assignment.getCourseId()).stream() - .filter(assignmentDto -> assignmentDto.getId().equals(assignment.getId())) - .findFirst() - .orElseThrow(() -> new EntityNotFoundException("Assignment with externalId %s not found".formatted(assignment.getExternalId()))) - .getMetadata().getName(); - - String courseTitle = courseServiceClient.queryCourseById(assignment.getCourseId()).getTitle(); - // no isPresent check, since if we are here, the external course must exist - String organizationName = externalCourseRepository.findById(courseTitle).get().getOrganizationName(); - - String repoLink = codeAssessmentProvider.findRepository(assignmentName, organizationName, currentUser); - gradingEntity.getCodeAssignmentGradingMetadata().setRepoLink(repoLink); - } catch (ExternalPlatformConnectionException | UserServiceConnectionException | - ContentServiceConnectionException | CourseServiceConnectionException e) { - log.error("Failed to find repository for assignment {} and student {}: {}", assignment.getId(), currentUser.getId(), e.toString()); - } + return gradingEntity; + } + + /** + * Checks if the grading entity has a repository link set. + */ + private boolean hasRepositoryLink(final GradingEntity gradingEntity) { + return gradingEntity.getCodeAssignmentGradingMetadata() != null && + gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink() != null; + } + + /** + * Finds and sets the repository link for the student if it's missing. + */ + private void findAndSetRepositoryLinkIfMissing(final GradingEntity gradingEntity, + final AssignmentEntity assignment, + final LoggedInUser currentUser) { + if (hasRepositoryLink(gradingEntity)) { + return; } - if (gradingEntity.getCodeAssignmentGradingMetadata() != null && - gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink() != null) { - ExternalGrading externalGrading; - try { - externalGrading = codeAssessmentProvider.syncGradeForStudent(gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink(), currentUser); - } catch (ExternalPlatformConnectionException | UserServiceConnectionException e) { - log.error("Failed to sync student grade for assignment {} and student {}: {}", assignment.getId(), currentUser.getId(), e.toString()); - gradingEntity = gradingRepository.save(gradingEntity); - return List.of(assignmentMapper.gradingEntityToDto(gradingEntity)); - } + try { + String assignmentName = contentServiceClient.queryContentsOfCourse(currentUser.getId(), assignment.getCourseId()).stream() + .filter(assignmentDto -> assignmentDto.getId().equals(assignment.getId())) + .findFirst() + .orElseThrow(() -> new EntityNotFoundException("Assignment with externalId %s not found".formatted(assignment.getExternalId()))) + .getMetadata().getName(); - if (externalGrading.achievedPoints() != null){ - gradingEntity.setAchievedCredits(externalGrading.achievedPoints()); - } - gradingEntity.setDate(externalGrading.date()); + String courseTitle = courseServiceClient.queryCourseById(assignment.getCourseId()).getTitle(); + String organizationName = externalCourseRepository.findById(courseTitle).get().getOrganizationName(); - CodeAssignmentGradingMetadataEntity metadata = gradingEntity.getCodeAssignmentGradingMetadata(); - metadata.setStatus(externalGrading.status()); - metadata.setFeedbackTableHtml(externalGrading.tableHtml()); + String repoLink = codeAssessmentProvider.findRepository(assignmentName, organizationName, currentUser); + gradingEntity.getCodeAssignmentGradingMetadata().setRepoLink(repoLink); + } catch (ExternalPlatformConnectionException | UserServiceConnectionException | + ContentServiceConnectionException | CourseServiceConnectionException e) { + log.error("Failed to find repository for assignment {} and student {}: {}", + assignment.getId(), currentUser.getId(), e.toString()); + } + } - if (assignment.getTotalCredits() == null || (externalGrading.totalPoints() != null && externalGrading.totalPoints() > assignment.getTotalCredits())) { - assignment.setTotalCredits(externalGrading.totalPoints()); + /** + * Syncs grading from external system, updates the grading entity, and handles code submission events. + */ + private void syncAndUpdateGrading(final GradingEntity gradingEntity, + final AssignmentEntity assignment, + final LoggedInUser currentUser) { + ExternalGrading externalGrading; + try { + externalGrading = codeAssessmentProvider.syncGradeForStudent( + gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink(), currentUser); + } catch (ExternalPlatformConnectionException | UserServiceConnectionException e) { + log.error("Failed to sync student grade for assignment {} and student {}: {}", + assignment.getId(), currentUser.getId(), e.toString()); + return; + } + + updateGradingFromExternalGrading(gradingEntity, externalGrading, assignment); + handleCodeSubmissionEvent(gradingEntity, externalGrading, assignment, currentUser); + } + + /** + * Updates the grading entity with data from external grading. + */ + private void updateGradingFromExternalGrading(final GradingEntity gradingEntity, + final ExternalGrading externalGrading, + final AssignmentEntity assignment) { + if (externalGrading.achievedPoints() != null) { + gradingEntity.setAchievedCredits(externalGrading.achievedPoints()); + } + gradingEntity.setDate(externalGrading.date()); + + CodeAssignmentGradingMetadataEntity metadata = gradingEntity.getCodeAssignmentGradingMetadata(); + metadata.setStatus(externalGrading.status()); + metadata.setFeedbackTableHtml(externalGrading.tableHtml()); + + if (assignment.getTotalCredits() == null || + (externalGrading.totalPoints() != null && externalGrading.totalPoints() > assignment.getTotalCredits())) { + assignment.setTotalCredits(externalGrading.totalPoints()); + } + } + + /** + * Handles code submission event publishing with commit-based deduplication. + */ + private void handleCodeSubmissionEvent(final GradingEntity gradingEntity, + final ExternalGrading externalGrading, + final AssignmentEntity assignment, + final LoggedInUser currentUser) { + CodeAssignmentGradingMetadataEntity metadata = gradingEntity.getCodeAssignmentGradingMetadata(); + String lastProcessedCommit = metadata.getLastProcessedCommitSha(); + String currentCommit = externalGrading.commitSha(); + + if (!shouldSendCodeSubmissionEvent(lastProcessedCommit, currentCommit, assignment.getId(), currentUser.getId())) { + return; + } + + try { + if (codeAssessmentProvider instanceof de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.GithubClassroom githubClassroom) { + de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.StudentCodeSubmission codeSubmission = + githubClassroom.fetchStudentCode(metadata.getRepoLink(), currentUser); + + codeSubmission.setAssignmentId(assignment.getId()); + codeSubmission.setCourseId(assignment.getCourseId()); + + publishStudentCodeSubmittedEvent(codeSubmission); + + metadata.setLastProcessedCommitSha(currentCommit != null ? currentCommit : "NO_COMMIT_SHA_PROCESSED"); } - - String lastProcessedCommit = metadata.getLastProcessedCommitSha(); - String currentCommit = externalGrading.commitSha(); - boolean shouldSendEvent = false; - - if (currentCommit != null) { - if (lastProcessedCommit == null || lastProcessedCommit.equals("NO_COMMIT_SHA_PROCESSED")) { - shouldSendEvent = true; - } else if (!currentCommit.equals(lastProcessedCommit)) { - shouldSendEvent = true; - } else { - log.debug("Skipping code submission event. Commit {} already processed for student {} on assignment {}", - currentCommit, currentUser.getId(), assignment.getId()); - } + } catch (ExternalPlatformConnectionException | UserServiceConnectionException e) { + log.error("Failed to fetch student code for assignment {} and student {}: {}", + assignment.getId(), currentUser.getId(), e.toString()); + } + } + + /** + * Determines whether a code submission event should be sent based on commit SHA comparison. + */ + private boolean shouldSendCodeSubmissionEvent(final String lastProcessedCommit, + final String currentCommit, + final UUID assignmentId, + final UUID studentId) { + if (currentCommit != null) { + if (lastProcessedCommit == null || lastProcessedCommit.equals("NO_COMMIT_SHA_PROCESSED")) { + return true; + } else if (!currentCommit.equals(lastProcessedCommit)) { + return true; } else { - if (lastProcessedCommit == null || lastProcessedCommit.equals("NO_COMMIT_SHA_PROCESSED")) { - shouldSendEvent = true; - log.warn("No commit SHA available for assignment {} and student {}, sending event without commit tracking", - assignment.getId(), currentUser.getId()); - } else { - log.debug("No commit SHA and event already sent once for student {} on assignment {}, skipping", - currentUser.getId(), assignment.getId()); - } + log.debug("Skipping code submission event. Commit {} already processed for student {} on assignment {}", + currentCommit, studentId, assignmentId); + return false; } - - if (shouldSendEvent) { - try { - if (codeAssessmentProvider instanceof de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.GithubClassroom githubClassroom) { - de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.StudentCodeSubmission codeSubmission = - githubClassroom.fetchStudentCode(gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink(), currentUser); - - codeSubmission.setAssignmentId(assignment.getId()); - codeSubmission.setCourseId(assignment.getCourseId()); - - publishStudentCodeSubmittedEvent(codeSubmission); - - metadata.setLastProcessedCommitSha(currentCommit != null ? currentCommit : "NO_COMMIT_SHA_PROCESSED"); - } - } catch (ExternalPlatformConnectionException | UserServiceConnectionException e) { - log.error("Failed to fetch student code for assignment {} and student {}: {}", - assignment.getId(), currentUser.getId(), e.toString()); - } + } else { + if (lastProcessedCommit == null || lastProcessedCommit.equals("NO_COMMIT_SHA_PROCESSED")) { + log.warn("No commit SHA available for assignment {} and student {}, sending event without commit tracking", + assignmentId, studentId); + return true; + } else { + log.debug("No commit SHA and event already sent once for student {} on assignment {}, skipping", + studentId, assignmentId); + return false; } } - - gradingEntity = gradingRepository.save(gradingEntity); - return List.of(assignmentMapper.gradingEntityToDto(gradingEntity)); } /** From f7eec336acacc03832e2c7c2c12ea20570e51558 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 15 Jan 2026 23:13:22 +0000 Subject: [PATCH 3/4] Initial plan From 55de8c23c74a71f55e70ec1b22a746471116a625 Mon Sep 17 00:00:00 2001 From: mp-dg <57503145+MP-DG@users.noreply.github.com> Date: Fri, 16 Jan 2026 00:21:32 +0100 Subject: [PATCH 4/4] fixed failing tests introduced by someone else --- .../meitrex/assignment_service/service/GradingService.java | 4 ++++ .../api/MutationLogAssignmentCompletedTest.java | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java index 334e32b..b7666a5 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/service/GradingService.java @@ -188,6 +188,9 @@ private List getCodeAssignmentGradingForAdmin(final AssignmentEntity as } + /** + * Returns the grading for the current user on the given code assignment. + */ private List getCodeAssignmentGradingForStudent(final AssignmentEntity assignment, final LoggedInUser currentUser) { GradingEntity gradingEntity = ensureGradingEntityExists(assignment.getId(), currentUser.getId()); @@ -621,6 +624,7 @@ private void logGradingImported(final GradingEntity gradingEntity) { .contentId(assignmentEntity.getAssessmentId()) .hintsUsed(0) .success(success) + .contentType(ContentProgressedEvent.ContentType.ASSIGNMENT) .timeToComplete(null) .correctness(correctness) .responses(responses) diff --git a/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/api/MutationLogAssignmentCompletedTest.java b/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/api/MutationLogAssignmentCompletedTest.java index ebb68cb..e836947 100644 --- a/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/api/MutationLogAssignmentCompletedTest.java +++ b/src/test/java/de/unistuttgart/iste/meitrex/assignment_service/api/MutationLogAssignmentCompletedTest.java @@ -125,6 +125,7 @@ void testValidLogAssignmentCompletedSuccessful(final GraphQlTester tester) { .correctness(35.0/50.0) .hintsUsed(0) .success(true) + .contentType(ContentProgressedEvent.ContentType.ASSIGNMENT) .responses(responses) .build(); @@ -207,6 +208,7 @@ void testValidLogAssignmentCompletedNotSuccessful(final GraphQlTester tester) { .correctness(15.0/50.0) .hintsUsed(0) .success(false) + .contentType(ContentProgressedEvent.ContentType.ASSIGNMENT) .responses(responses) .build(); @@ -294,6 +296,7 @@ void testValidLogAssignmentCompletedNewPercentage(final GraphQlTester tester) { .correctness(35.0/50.0) .hintsUsed(0) .success(false) + .contentType(ContentProgressedEvent.ContentType.ASSIGNMENT) .responses(responses) .build(); @@ -382,6 +385,7 @@ void testValidLogAssignmentCompletedZeroCredits(final GraphQlTester tester) { .correctness(1.0f) .hintsUsed(0) .success(true) + .contentType(ContentProgressedEvent.ContentType.ASSIGNMENT) .responses(responses) .build();