From 2a0ca5dc789e80009cc590c8e3b89e166709b7d9 Mon Sep 17 00:00:00 2001 From: Mykhailo <90206262+Morphenoed@users.noreply.github.com> Date: Fri, 17 Oct 2025 22:23:52 +0200 Subject: [PATCH 1/4] Fix slug (url version of GH Assignment name) used to query GH API for student repository --- .../service/code_assignment/GithubClassroom.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 8123f7e..9c71936 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 @@ -1,6 +1,7 @@ package de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment; import com.google.gson.*; +import com.github.slugify.Slugify; import de.unistuttgart.iste.meitrex.assignment_service.exception.ExternalPlatformConnectionException; import de.unistuttgart.iste.meitrex.assignment_service.persistence.entity.assignment.ExternalCodeAssignmentEntity; import de.unistuttgart.iste.meitrex.assignment_service.persistence.repository.AssignmentRepository; @@ -513,7 +514,8 @@ public String findRepository(final String assignmentName, final String organizat String token = queryTokenResponse.getAccessToken(); String githubUsername = queryTokenResponse.getExternalUserId(); - String slug = assignmentName.toLowerCase().replaceAll("\\s+", "-"); + final Slugify slg = Slugify.builder().build(); + String slug = slg.slugify(assignmentName); String repoName = slug + "-" + githubUsername; HttpRequest request = HttpRequest.newBuilder() From 71017befb25ec9ae966bffb29681927bdd57f291 Mon Sep 17 00:00:00 2001 From: Mykhailo <90206262+Morphenoed@users.noreply.github.com> Date: Fri, 17 Oct 2025 22:24:41 +0200 Subject: [PATCH 2/4] add missing slugify dependency --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index c4e89d8..5b01f5c 100644 --- a/build.gradle +++ b/build.gradle @@ -115,6 +115,7 @@ dependencies { implementation 'de.unistuttgart.iste.meitrex:course_service:0.1.0' implementation 'de.unistuttgart.iste.meitrex:user_service:0.1.0' implementation 'com.google.code.gson:gson:2.13.1' + implementation 'com.github.slugify:slugify:3.0.7' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-graphql' implementation 'org.springframework.boot:spring-boot-starter-validation' From 4c5c84e787169bd8723de3ca50c21849d5a05651 Mon Sep 17 00:00:00 2001 From: MP-DG <57503145+MP-DG@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:57:52 +0100 Subject: [PATCH 3/4] added more logging --- .../controller/AssignmentController.java | 2 + .../service/GradingService.java | 43 ++++++++++++++++--- .../code_assignment/GithubClassroom.java | 29 ++++++++++++- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/controller/AssignmentController.java b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/controller/AssignmentController.java index c38f4b3..7d1bfd8 100644 --- a/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/controller/AssignmentController.java +++ b/src/main/java/de/unistuttgart/iste/meitrex/assignment_service/controller/AssignmentController.java @@ -54,6 +54,8 @@ public ExternalCourse getExternalCourse(@Argument final UUID courseId, @ContextV @QueryMapping public List getGradingsForAssignment(@Argument final UUID assessmentId, @ContextValue final LoggedInUser currentUser) { + log.info("[GRADING-FLOW] GraphQL query received: getGradingsForAssignment for assessmentId={}, userId={}", + assessmentId, currentUser.getId()); return gradingService.getGradingsForAssignment(assessmentId, currentUser); } 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..fa23699 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 @@ -69,13 +69,17 @@ public class GradingService { private final ExternalCourseRepository externalCourseRepository; public List getGradingsForAssignment(final UUID assignmentId, final LoggedInUser currentUser) { + log.info("[GRADING-FLOW] Starting getGradingsForAssignment for assignmentId={}, userId={}", + assignmentId, currentUser.getId()); + final AssignmentEntity assignment = assignmentService.requireAssignmentExists(assignmentId); LoggedInUser.CourseMembership courseMembership = currentUser.getCourseMemberships().stream() .filter(membership -> membership.getCourseId().equals(assignment.getCourseId())).findFirst() .orElseThrow(() -> new NoAccessToCourseException(assignment.getCourseId(), "User is not a member of the course.")); - + if (assignment.getAssignmentType() == AssignmentType.CODE_ASSIGNMENT){ + log.info("[GRADING-FLOW] Processing CODE_ASSIGNMENT type"); List gradings = new ArrayList<>(); // Always get the current student's grading so that they see it in student's view (even if the user is a tutor/admin) @@ -189,10 +193,14 @@ private List getCodeAssignmentGradingForAdmin(final AssignmentEntity as private List getCodeAssignmentGradingForStudent(final AssignmentEntity assignment, final LoggedInUser currentUser) { + log.info("[GRADING-FLOW] >>> getCodeAssignmentGradingForStudent START - assignmentId={}, studentId={}", + assignment.getId(), currentUser.getId()); + final GradingEntity.PrimaryKey pk = new GradingEntity.PrimaryKey(assignment.getId(), currentUser.getId()); GradingEntity gradingEntity = gradingRepository.findById(pk).orElse(null); - + if (gradingEntity == null) { + log.info("[GRADING-FLOW] Creating new grading entity for first-time access"); gradingEntity = GradingEntity.builder().primaryKey(pk).build(); CodeAssignmentGradingMetadataEntity metadata = CodeAssignmentGradingMetadataEntity.builder() @@ -207,6 +215,7 @@ private List getCodeAssignmentGradingForStudent(final AssignmentEntity if (gradingEntity.getCodeAssignmentGradingMetadata() == null || gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink() == null) { + log.info("[GRADING-FLOW] Repository link not found, attempting to find student repository"); try { String assignmentName = contentServiceClient.queryContentsOfCourse(currentUser.getId(), assignment.getCourseId()).stream() .filter(assignmentDto -> assignmentDto.getId().equals(assignment.getId())) @@ -214,25 +223,32 @@ private List getCodeAssignmentGradingForStudent(final AssignmentEntity .orElseThrow(() -> new EntityNotFoundException("Assignment with externalId %s not found".formatted(assignment.getExternalId()))) .getMetadata().getName(); - String courseTitle = courseServiceClient.queryCourseById(assignment.getCourseId()).getTitle(); + 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(); + log.info("[GRADING-FLOW] Calling findRepository with assignmentName={}, organizationName={}", + assignmentName, organizationName); String repoLink = codeAssessmentProvider.findRepository(assignmentName, organizationName, currentUser); + log.info("[GRADING-FLOW] Repository link found: {}", repoLink != null ? repoLink : "NULL"); 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()); + log.error("[GRADING-FLOW] ERROR: Failed to find repository for assignment {} and student {}: {}", assignment.getId(), currentUser.getId(), e.toString()); } } if (gradingEntity.getCodeAssignmentGradingMetadata() != null && gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink() != null) { + log.info("[GRADING-FLOW] Repository link exists: {}", + gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink()); ExternalGrading externalGrading; try { externalGrading = codeAssessmentProvider.syncGradeForStudent(gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink(), currentUser); + log.info("[GRADING-FLOW] syncGradeForStudent completed - achievedPoints={}, totalPoints={}, status={}", + externalGrading.achievedPoints(), externalGrading.totalPoints(), externalGrading.status()); } catch (ExternalPlatformConnectionException | UserServiceConnectionException e) { - log.error("Failed to sync student grade for assignment {} and student {}: {}", assignment.getId(), currentUser.getId(), e.toString()); + log.error("[GRADING-FLOW] 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)); } @@ -250,24 +266,37 @@ private List getCodeAssignmentGradingForStudent(final AssignmentEntity assignment.setTotalCredits(externalGrading.totalPoints()); } + log.info("[GRADING-FLOW] Attempting to fetch student code from repository"); try { if (codeAssessmentProvider instanceof de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.GithubClassroom githubClassroom) { + log.info("[GRADING-FLOW] Calling fetchStudentCode for repoLink={}", + gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink()); + de.unistuttgart.iste.meitrex.assignment_service.service.code_assignment.StudentCodeSubmission codeSubmission = githubClassroom.fetchStudentCode(gradingEntity.getCodeAssignmentGradingMetadata().getRepoLink(), currentUser); + log.info("[GRADING-FLOW] fetchStudentCode completed successfully - files count: {}, commit: {}", + codeSubmission.getFiles().size(), codeSubmission.getCommitSha()); codeSubmission.setAssignmentId(assignment.getId()); codeSubmission.setCourseId(assignment.getCourseId()); + log.info("[GRADING-FLOW] Publishing StudentCodeSubmittedEvent"); publishStudentCodeSubmittedEvent(codeSubmission); + log.info("[GRADING-FLOW] StudentCodeSubmittedEvent published successfully"); + } else { + log.warn("[GRADING-FLOW] Code assessment provider is not GithubClassroom instance: {}", + codeAssessmentProvider.getClass().getName()); } } catch (ExternalPlatformConnectionException | UserServiceConnectionException e) { - log.error("Failed to fetch student code for assignment {} and student {}: {}", - assignment.getId(), currentUser.getId(), e.toString()); + log.error("[GRADING-FLOW] ERROR: Failed to fetch student code for assignment {} and student {}: {}", + assignment.getId(), currentUser.getId(), e.toString(), e); } } + log.info("[GRADING-FLOW] Saving grading entity to database"); gradingEntity = gradingRepository.save(gradingEntity); + log.info("[GRADING-FLOW] <<< getCodeAssignmentGradingForStudent END - returning grading"); return List.of(assignmentMapper.gradingEntityToDto(gradingEntity)); } 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 7e85e34..1afda55 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 @@ -511,6 +511,8 @@ private String extractGradingTableAsHtml(String logs) { @Override public String findRepository(final String assignmentName, final String organizationName, final LoggedInUser currentUser) throws ExternalPlatformConnectionException, UserServiceConnectionException { + log.info("[GITHUB-API] >>> findRepository START - assignmentName={}, organizationName={}, userId={}", + assignmentName, organizationName, currentUser.getId()); try { AccessToken queryTokenResponse = userServiceClient.queryAccessToken(currentUser, NAME); String token = queryTokenResponse.getAccessToken(); @@ -519,6 +521,7 @@ public String findRepository(final String assignmentName, final String organizat final Slugify slg = Slugify.builder().build(); String slug = slg.slugify(assignmentName); String repoName = slug + "-" + githubUsername; + log.info("[GITHUB-API] Constructed repository name: {}", repoName); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(basePath + "/repos/" + organizationName + "/" + repoName)) @@ -529,6 +532,7 @@ public String findRepository(final String assignmentName, final String organizat .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + log.info("[GITHUB-API] GitHub API response status: {}", response.statusCode()); if (response.statusCode() == 404) { return null; @@ -543,8 +547,10 @@ public String findRepository(final String assignmentName, final String organizat } catch (InterruptedException e) { Thread.currentThread().interrupt(); + log.error("[GITHUB-API] Interrupted while fetching student repo link"); throw new ExternalPlatformConnectionException("Interrupted while fetching student repo link", e); } catch (IOException e) { + log.error("[GITHUB-API] IOException while fetching student repo link"); throw new ExternalPlatformConnectionException("Error fetching student repo link", e); } @@ -613,6 +619,7 @@ public ExternalCourse getExternalCourse(final String courseTitle, final LoggedIn */ public StudentCodeSubmission fetchStudentCode(String repoLink, LoggedInUser currentUser) throws ExternalPlatformConnectionException, UserServiceConnectionException { + log.info("[GITHUB-API] >>> fetchStudentCode START - repoLink={}, userId={}", repoLink, currentUser.getId()); try { AccessToken tokenResponse = userServiceClient.queryAccessToken(currentUser, NAME); String token = tokenResponse.getAccessToken(); @@ -620,10 +627,12 @@ public StudentCodeSubmission fetchStudentCode(String repoLink, LoggedInUser curr URI uri = URI.create(repoLink); String[] parts = uri.getPath().split("/"); if (parts.length < 3) { + log.error("[GITHUB-API] Invalid repo URL format: {}, parts count: {}", repoLink, parts.length); throw new ExternalPlatformConnectionException("Invalid repo URL: " + repoLink); } String owner = parts[1]; String repo = parts[2]; + log.info("[GITHUB-API] Parsed repository - owner={}, repo={}", owner, repo); HttpRequest repoRequest = HttpRequest.newBuilder() .uri(URI.create(basePath + "/repos/" + owner + "/" + repo)) @@ -634,7 +643,11 @@ public StudentCodeSubmission fetchStudentCode(String repoLink, LoggedInUser curr .build(); HttpResponse repoResponse = client.send(repoRequest, HttpResponse.BodyHandlers.ofString()); + log.info("[GITHUB-API] Repository info response status: {}", repoResponse.statusCode()); + if (repoResponse.statusCode() != 200) { + log.error("[GITHUB-API] Failed to fetch repository info: status={}, body={}", + repoResponse.statusCode(), repoResponse.body()); throw new ExternalPlatformConnectionException("Failed to fetch repository info: " + repoResponse.body()); } @@ -650,7 +663,11 @@ public StudentCodeSubmission fetchStudentCode(String repoLink, LoggedInUser curr .build(); HttpResponse commitResponse = client.send(commitRequest, HttpResponse.BodyHandlers.ofString()); + log.info("[GITHUB-API] Commit info response status: {}", commitResponse.statusCode()); + if (commitResponse.statusCode() != 200) { + log.error("[GITHUB-API] Failed to fetch commit info: status={}, body={}", + commitResponse.statusCode(), commitResponse.body()); throw new ExternalPlatformConnectionException("Failed to fetch commit info: " + commitResponse.body()); } @@ -662,7 +679,9 @@ public StudentCodeSubmission fetchStudentCode(String repoLink, LoggedInUser curr OffsetDateTime commitDate = OffsetDateTime.parse(commitDateStr); Map files = new HashMap<>(); + log.info("[GITHUB-API] Starting recursive file fetch from repository"); fetchFilesRecursively(owner, repo, defaultBranch, "", token, files); + log.info("[GITHUB-API] Finished fetching files - total files retrieved: {}", files.size()); return StudentCodeSubmission.builder() .studentId(currentUser.getId()) @@ -675,9 +694,14 @@ public StudentCodeSubmission fetchStudentCode(String repoLink, LoggedInUser curr } catch (InterruptedException e) { Thread.currentThread().interrupt(); + log.error("[GITHUB-API] Interrupted while fetching student code from {}", repoLink); throw new ExternalPlatformConnectionException("Interrupted while fetching student code", e); } catch (IOException e) { + log.error("[GITHUB-API] IOException while fetching student code from {}", repoLink); throw new ExternalPlatformConnectionException("Error fetching student code", e); + } catch (Exception e) { + log.error("[GITHUB-API] Unexpected error while fetching student code from {}", repoLink); + throw new ExternalPlatformConnectionException("Unexpected error fetching student code: " + e.getMessage()); } } @@ -706,7 +730,8 @@ private void fetchFilesRecursively(String owner, String repo, String branch, Str HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() != 200) { - log.warn("Failed to fetch contents for path '{}': {}", path, response.body()); + log.warn("[GITHUB-API] Failed to fetch contents for path '{}': status={}, body={}", + path, response.statusCode(), response.body()); return; } @@ -729,7 +754,7 @@ private void fetchFilesRecursively(String owner, String repo, String branch, Str if (fileResponse.statusCode() == 200) { files.put(itemPath, fileResponse.body()); } else { - log.warn("Failed to fetch file content for: {}", itemPath); + log.warn("[GITHUB-API] Failed to fetch file content for: {}, status={}", itemPath, fileResponse.statusCode()); } } else if ("dir".equals(type)) { fetchFilesRecursively(owner, repo, branch, itemPath, token, files); 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 4/4] Initial plan