@@ -189,7 +189,24 @@ private List<Grading> getCodeAssignmentGradingForAdmin(final AssignmentEntity as
189189
190190
191191 private List <Grading > getCodeAssignmentGradingForStudent (final AssignmentEntity assignment , final LoggedInUser currentUser ) {
192- final GradingEntity .PrimaryKey pk = new GradingEntity .PrimaryKey (assignment .getId (), currentUser .getId ());
192+ GradingEntity gradingEntity = ensureGradingEntityExists (assignment .getId (), currentUser .getId ());
193+
194+ findAndSetRepositoryLinkIfMissing (gradingEntity , assignment , currentUser );
195+
196+ if (hasRepositoryLink (gradingEntity )) {
197+ syncAndUpdateGrading (gradingEntity , assignment , currentUser );
198+ }
199+
200+ gradingEntity = gradingRepository .save (gradingEntity );
201+ return List .of (assignmentMapper .gradingEntityToDto (gradingEntity ));
202+ }
203+
204+ /**
205+ * Ensures a grading entity exists for the given assignment and student.
206+ * Creates a new entity with metadata if it doesn't exist.
207+ */
208+ private GradingEntity ensureGradingEntityExists (final UUID assignmentId , final UUID studentId ) {
209+ final GradingEntity .PrimaryKey pk = new GradingEntity .PrimaryKey (assignmentId , studentId );
193210 GradingEntity gradingEntity = gradingRepository .findById (pk ).orElse (null );
194211
195212 if (gradingEntity == null ) {
@@ -205,97 +222,148 @@ private List<Grading> getCodeAssignmentGradingForStudent(final AssignmentEntity
205222 gradingEntity .setCodeAssignmentGradingMetadata (metadata );
206223 }
207224
208- if ( gradingEntity . getCodeAssignmentGradingMetadata () == null ||
209- gradingEntity . getCodeAssignmentGradingMetadata (). getRepoLink () == null ) {
210- try {
211- String assignmentName = contentServiceClient . queryContentsOfCourse ( currentUser . getId (), assignment . getCourseId ()). stream ()
212- . filter ( assignmentDto -> assignmentDto . getId (). equals ( assignment . getId ()))
213- . findFirst ()
214- . orElseThrow (() -> new EntityNotFoundException ( "Assignment with externalId %s not found" . formatted ( assignment . getExternalId ())))
215- . getMetadata (). getName ();
216-
217- String courseTitle = courseServiceClient . queryCourseById ( assignment . getCourseId ()). getTitle ();
218- // no isPresent check, since if we are here, the external course must exist
219- String organizationName = externalCourseRepository . findById ( courseTitle ). get (). getOrganizationName ();
220-
221- String repoLink = codeAssessmentProvider . findRepository ( assignmentName , organizationName , currentUser );
222- gradingEntity . getCodeAssignmentGradingMetadata (). setRepoLink ( repoLink );
223- } catch ( ExternalPlatformConnectionException | UserServiceConnectionException |
224- ContentServiceConnectionException | CourseServiceConnectionException e ) {
225- log . error ( "Failed to find repository for assignment {} and student {}: {}" , assignment . getId (), currentUser . getId (), e . toString ());
226- }
225+ return gradingEntity ;
226+ }
227+
228+ /**
229+ * Checks if the grading entity has a repository link set.
230+ */
231+ private boolean hasRepositoryLink ( final GradingEntity gradingEntity ) {
232+ return gradingEntity . getCodeAssignmentGradingMetadata () != null &&
233+ gradingEntity . getCodeAssignmentGradingMetadata (). getRepoLink () != null ;
234+ }
235+
236+ /**
237+ * Finds and sets the repository link for the student if it's missing.
238+ */
239+ private void findAndSetRepositoryLinkIfMissing ( final GradingEntity gradingEntity ,
240+ final AssignmentEntity assignment ,
241+ final LoggedInUser currentUser ) {
242+ if ( hasRepositoryLink ( gradingEntity )) {
243+ return ;
227244 }
228245
229- if (gradingEntity .getCodeAssignmentGradingMetadata () != null &&
230- gradingEntity .getCodeAssignmentGradingMetadata ().getRepoLink () != null ) {
231- ExternalGrading externalGrading ;
232- try {
233- externalGrading = codeAssessmentProvider .syncGradeForStudent (gradingEntity .getCodeAssignmentGradingMetadata ().getRepoLink (), currentUser );
234- } catch (ExternalPlatformConnectionException | UserServiceConnectionException e ) {
235- log .error ("Failed to sync student grade for assignment {} and student {}: {}" , assignment .getId (), currentUser .getId (), e .toString ());
236- gradingEntity = gradingRepository .save (gradingEntity );
237- return List .of (assignmentMapper .gradingEntityToDto (gradingEntity ));
238- }
246+ try {
247+ String assignmentName = contentServiceClient .queryContentsOfCourse (currentUser .getId (), assignment .getCourseId ()).stream ()
248+ .filter (assignmentDto -> assignmentDto .getId ().equals (assignment .getId ()))
249+ .findFirst ()
250+ .orElseThrow (() -> new EntityNotFoundException ("Assignment with externalId %s not found" .formatted (assignment .getExternalId ())))
251+ .getMetadata ().getName ();
239252
240- if (externalGrading .achievedPoints () != null ){
241- gradingEntity .setAchievedCredits (externalGrading .achievedPoints ());
242- }
243- gradingEntity .setDate (externalGrading .date ());
253+ String courseTitle = courseServiceClient .queryCourseById (assignment .getCourseId ()).getTitle ();
254+ String organizationName = externalCourseRepository .findById (courseTitle ).get ().getOrganizationName ();
244255
245- CodeAssignmentGradingMetadataEntity metadata = gradingEntity .getCodeAssignmentGradingMetadata ();
246- metadata .setStatus (externalGrading .status ());
247- metadata .setFeedbackTableHtml (externalGrading .tableHtml ());
256+ String repoLink = codeAssessmentProvider .findRepository (assignmentName , organizationName , currentUser );
257+ gradingEntity .getCodeAssignmentGradingMetadata ().setRepoLink (repoLink );
258+ } catch (ExternalPlatformConnectionException | UserServiceConnectionException |
259+ ContentServiceConnectionException | CourseServiceConnectionException e ) {
260+ log .error ("Failed to find repository for assignment {} and student {}: {}" ,
261+ assignment .getId (), currentUser .getId (), e .toString ());
262+ }
263+ }
248264
249- if (assignment .getTotalCredits () == null || (externalGrading .totalPoints () != null && externalGrading .totalPoints () > assignment .getTotalCredits ())) {
250- assignment .setTotalCredits (externalGrading .totalPoints ());
265+ /**
266+ * Syncs grading from external system, updates the grading entity, and handles code submission events.
267+ */
268+ private void syncAndUpdateGrading (final GradingEntity gradingEntity ,
269+ final AssignmentEntity assignment ,
270+ final LoggedInUser currentUser ) {
271+ ExternalGrading externalGrading ;
272+ try {
273+ externalGrading = codeAssessmentProvider .syncGradeForStudent (
274+ gradingEntity .getCodeAssignmentGradingMetadata ().getRepoLink (), currentUser );
275+ } catch (ExternalPlatformConnectionException | UserServiceConnectionException e ) {
276+ log .error ("Failed to sync student grade for assignment {} and student {}: {}" ,
277+ assignment .getId (), currentUser .getId (), e .toString ());
278+ return ;
279+ }
280+
281+ updateGradingFromExternalGrading (gradingEntity , externalGrading , assignment );
282+ handleCodeSubmissionEvent (gradingEntity , externalGrading , assignment , currentUser );
283+ }
284+
285+ /**
286+ * Updates the grading entity with data from external grading.
287+ */
288+ private void updateGradingFromExternalGrading (final GradingEntity gradingEntity ,
289+ final ExternalGrading externalGrading ,
290+ final AssignmentEntity assignment ) {
291+ if (externalGrading .achievedPoints () != null ) {
292+ gradingEntity .setAchievedCredits (externalGrading .achievedPoints ());
293+ }
294+ gradingEntity .setDate (externalGrading .date ());
295+
296+ CodeAssignmentGradingMetadataEntity metadata = gradingEntity .getCodeAssignmentGradingMetadata ();
297+ metadata .setStatus (externalGrading .status ());
298+ metadata .setFeedbackTableHtml (externalGrading .tableHtml ());
299+
300+ if (assignment .getTotalCredits () == null ||
301+ (externalGrading .totalPoints () != null && externalGrading .totalPoints () > assignment .getTotalCredits ())) {
302+ assignment .setTotalCredits (externalGrading .totalPoints ());
303+ }
304+ }
305+
306+ /**
307+ * Handles code submission event publishing with commit-based deduplication.
308+ */
309+ private void handleCodeSubmissionEvent (final GradingEntity gradingEntity ,
310+ final ExternalGrading externalGrading ,
311+ final AssignmentEntity assignment ,
312+ final LoggedInUser currentUser ) {
313+ CodeAssignmentGradingMetadataEntity metadata = gradingEntity .getCodeAssignmentGradingMetadata ();
314+ String lastProcessedCommit = metadata .getLastProcessedCommitSha ();
315+ String currentCommit = externalGrading .commitSha ();
316+
317+ if (!shouldSendCodeSubmissionEvent (lastProcessedCommit , currentCommit , assignment .getId (), currentUser .getId ())) {
318+ return ;
319+ }
320+
321+ try {
322+ if (codeAssessmentProvider instanceof de .unistuttgart .iste .meitrex .assignment_service .service .code_assignment .GithubClassroom githubClassroom ) {
323+ de .unistuttgart .iste .meitrex .assignment_service .service .code_assignment .StudentCodeSubmission codeSubmission =
324+ githubClassroom .fetchStudentCode (metadata .getRepoLink (), currentUser );
325+
326+ codeSubmission .setAssignmentId (assignment .getId ());
327+ codeSubmission .setCourseId (assignment .getCourseId ());
328+
329+ publishStudentCodeSubmittedEvent (codeSubmission );
330+
331+ metadata .setLastProcessedCommitSha (currentCommit != null ? currentCommit : "NO_COMMIT_SHA_PROCESSED" );
251332 }
252-
253- String lastProcessedCommit = metadata .getLastProcessedCommitSha ();
254- String currentCommit = externalGrading .commitSha ();
255- boolean shouldSendEvent = false ;
256-
257- if (currentCommit != null ) {
258- if (lastProcessedCommit == null || lastProcessedCommit .equals ("NO_COMMIT_SHA_PROCESSED" )) {
259- shouldSendEvent = true ;
260- } else if (!currentCommit .equals (lastProcessedCommit )) {
261- shouldSendEvent = true ;
262- } else {
263- log .debug ("Skipping code submission event. Commit {} already processed for student {} on assignment {}" ,
264- currentCommit , currentUser .getId (), assignment .getId ());
265- }
333+ } catch (ExternalPlatformConnectionException | UserServiceConnectionException e ) {
334+ log .error ("Failed to fetch student code for assignment {} and student {}: {}" ,
335+ assignment .getId (), currentUser .getId (), e .toString ());
336+ }
337+ }
338+
339+ /**
340+ * Determines whether a code submission event should be sent based on commit SHA comparison.
341+ */
342+ private boolean shouldSendCodeSubmissionEvent (final String lastProcessedCommit ,
343+ final String currentCommit ,
344+ final UUID assignmentId ,
345+ final UUID studentId ) {
346+ if (currentCommit != null ) {
347+ if (lastProcessedCommit == null || lastProcessedCommit .equals ("NO_COMMIT_SHA_PROCESSED" )) {
348+ return true ;
349+ } else if (!currentCommit .equals (lastProcessedCommit )) {
350+ return true ;
266351 } else {
267- if (lastProcessedCommit == null || lastProcessedCommit .equals ("NO_COMMIT_SHA_PROCESSED" )) {
268- shouldSendEvent = true ;
269- log .warn ("No commit SHA available for assignment {} and student {}, sending event without commit tracking" ,
270- assignment .getId (), currentUser .getId ());
271- } else {
272- log .debug ("No commit SHA and event already sent once for student {} on assignment {}, skipping" ,
273- currentUser .getId (), assignment .getId ());
274- }
352+ log .debug ("Skipping code submission event. Commit {} already processed for student {} on assignment {}" ,
353+ currentCommit , studentId , assignmentId );
354+ return false ;
275355 }
276-
277- if (shouldSendEvent ) {
278- try {
279- if (codeAssessmentProvider instanceof de .unistuttgart .iste .meitrex .assignment_service .service .code_assignment .GithubClassroom githubClassroom ) {
280- de .unistuttgart .iste .meitrex .assignment_service .service .code_assignment .StudentCodeSubmission codeSubmission =
281- githubClassroom .fetchStudentCode (gradingEntity .getCodeAssignmentGradingMetadata ().getRepoLink (), currentUser );
282-
283- codeSubmission .setAssignmentId (assignment .getId ());
284- codeSubmission .setCourseId (assignment .getCourseId ());
285-
286- publishStudentCodeSubmittedEvent (codeSubmission );
287-
288- metadata .setLastProcessedCommitSha (currentCommit != null ? currentCommit : "NO_COMMIT_SHA_PROCESSED" );
289- }
290- } catch (ExternalPlatformConnectionException | UserServiceConnectionException e ) {
291- log .error ("Failed to fetch student code for assignment {} and student {}: {}" ,
292- assignment .getId (), currentUser .getId (), e .toString ());
293- }
356+ } else {
357+ if (lastProcessedCommit == null || lastProcessedCommit .equals ("NO_COMMIT_SHA_PROCESSED" )) {
358+ log .warn ("No commit SHA available for assignment {} and student {}, sending event without commit tracking" ,
359+ assignmentId , studentId );
360+ return true ;
361+ } else {
362+ log .debug ("No commit SHA and event already sent once for student {} on assignment {}, skipping" ,
363+ studentId , assignmentId );
364+ return false ;
294365 }
295366 }
296-
297- gradingEntity = gradingRepository .save (gradingEntity );
298- return List .of (assignmentMapper .gradingEntityToDto (gradingEntity ));
299367 }
300368
301369 /**
0 commit comments