@@ -105,15 +105,43 @@ public void removeScheduledPeerReview(String assignmentId) {
105105 * Method called by the scheduledInvocationManager
106106 */
107107 public void execute (String opaqueContext ) {
108+ log .info ("Starting peer assessment assignment process for assignment: {}" , opaqueContext );
108109 Session session = sessionManager .getCurrentSession ();
109110 session .setUserEid ("admin" );
110111 session .setUserId ("admin" );
112+
113+ boolean success = false ;
114+ int totalReviewsAssigned = 0 ;
115+ int totalSubmissions = 0 ;
116+
111117 try {
118+ if (StringUtils .isBlank (opaqueContext )) {
119+ log .error ("Peer assessment job called with null or empty assignment ID" );
120+ return ;
121+ }
122+
112123 //for group assignments, we need to have a user ID, otherwise, an exception is thrown:
113124 Assignment assignment = assignmentService .getAssignment (opaqueContext );
125+ if (assignment == null ) {
126+ log .error ("Assignment not found with ID: {}" , opaqueContext );
127+ return ;
128+ }
129+
114130 if (assignment .getAllowPeerAssessment () && !assignment .getDraft ()) {
115131 int numOfReviews = assignment .getPeerAssessmentNumberReviews ();
132+ if (numOfReviews <= 0 ) {
133+ log .error ("Invalid number of reviews configured: {} for assignment {}" , numOfReviews , opaqueContext );
134+ return ;
135+ }
136+ log .debug ("Number of reviews per submission: {}" , numOfReviews );
137+
116138 Set <AssignmentSubmission > submissions = assignmentService .getSubmissions (assignment );
139+ if (submissions == null || submissions .isEmpty ()) {
140+ log .warn ("No submissions found for assignment: {}" , opaqueContext );
141+ return ;
142+ }
143+ log .debug ("Total submissions found: {}" , submissions .size ());
144+
117145 //keep a map of submission ids to look up possible existing peer assessments
118146 Map <String , AssignmentSubmission > submissionIdMap = new HashMap <>();
119147 //keep track of who has been assigned an assessment
@@ -146,6 +174,8 @@ public void execute(String opaqueContext) {
146174 && (CollectionUtils .containsAny (submitterIdsList , submitterIds ))) {
147175 submissionIdMap .put (s .getId (), s );
148176 submitterIds .forEach (submitterId -> assignedAssessmentsMap .computeIfAbsent (submitterId , k -> new HashMap <>()));
177+ totalSubmissions ++;
178+ log .debug ("Added submission {} for review assignment" , s .getId ());
149179 }
150180 }
151181
@@ -248,7 +278,10 @@ public void execute(String opaqueContext) {
248278 //update this submission user's count:
249279 assignedCount ++;
250280 studentAssessorsMap .put (submitterId , assignedCount );
281+ totalReviewsAssigned ++;
282+ log .debug ("Assigned reviewer {} to submission {} (review count: {})" , lowestAssignedAssessor , submissionId , assignedCount );
251283 } else {
284+ log .warn ("Could not find available reviewer for submission {} (submitter: {}), breaking assignment loop" , submissionId , submitterId );
252285 break ;
253286 }
254287 }
@@ -258,10 +291,35 @@ public void execute(String opaqueContext) {
258291 for (PeerAssessmentItem item : newItems ) {
259292 getHibernateTemplate ().saveOrUpdate (item );
260293 }
294+ success = true ;
295+ } else {
296+ log .debug ("No new peer assessment items to save" );
297+ // Check if existing items cover all submissions
298+ success = validatePeerAssessmentCoverage (assignment , submissionIdMap , numOfReviews );
299+ }
300+
301+ log .debug ("Peer assessment assignment completed for {}: {} submissions processed, {} reviews assigned, success: {}" , opaqueContext , totalSubmissions , totalReviewsAssigned , success );
302+ if (!success && totalReviewsAssigned == 0 && totalSubmissions > 0 ) {
303+ log .warn ("No reviews were assigned for assignment {} with {} submissions. Scheduling retry." , opaqueContext , totalSubmissions );
304+ scheduleRetry (assignment );
261305 }
306+ } else {
307+ log .warn ("Assignment {} does not allow peer assessment or is in draft mode, skipping" , opaqueContext );
262308 }
263309 } catch (Exception e ) {
264- log .error ("Assignments peer review submission/assessor config" , e );
310+ log .error ("Error during peer assessment assignment process for assignment {}: {}" , opaqueContext , e .getMessage (), e );
311+
312+ try {
313+ if (!StringUtils .isBlank (opaqueContext )) {
314+ Assignment assignment = assignmentService .getAssignment (opaqueContext );
315+ if (assignment != null && assignment .getAllowPeerAssessment () && !assignment .getDraft ()) {
316+ log .debug ("Scheduling retry for failed peer assessment assignment: {}" , opaqueContext );
317+ scheduleRetry (assignment );
318+ }
319+ }
320+ } catch (Exception retryException ) {
321+ log .error ("Failed to schedule retry for assignment {}: {}" , opaqueContext , retryException .getMessage ());
322+ }
265323 } finally {
266324 session .clear ();
267325 session .setUserEid (null );
@@ -529,4 +587,50 @@ public SecurityAdvice isAllowed(String userId, String function, String reference
529587 return saved ;
530588 }
531589
590+ /**
591+ * Validates that peer assessments have been properly assigned
592+ */
593+ private boolean validatePeerAssessmentCoverage (Assignment assignment , Map <String , AssignmentSubmission > submissionIdMap , int numOfReviews ) {
594+ try {
595+ List <PeerAssessmentItem > allItems = getPeerAssessmentItemsByAssignmentId (assignment .getId (), assignment .getScaleFactor ());
596+ Map <String , Integer > submissionReviewCounts = new HashMap <>();
597+ // Count reviews per submission
598+ for (PeerAssessmentItem item : allItems ) {
599+ if (!item .getRemoved ()) {
600+ submissionReviewCounts .merge (item .getId ().getSubmissionId (), 1 , Integer ::sum );
601+ }
602+ }
603+ // Check if all submissions have adequate reviews
604+ int underReviewedSubmissions = 0 ;
605+ for (String submissionId : submissionIdMap .keySet ()) {
606+ int reviewCount = submissionReviewCounts .getOrDefault (submissionId , 0 );
607+ if (reviewCount < numOfReviews ) {
608+ underReviewedSubmissions ++;
609+ log .warn ("Submission {} has only {} reviews (expected: {})" , submissionId , reviewCount , numOfReviews );
610+ }
611+ }
612+ if (underReviewedSubmissions > 0 ) {
613+ log .error ("{} submissions are under-reviewed for assignment {}" , underReviewedSubmissions , assignment .getId ());
614+ return false ;
615+ }
616+ return true ;
617+ } catch (Exception e ) {
618+ log .error ("Error validating peer assessment coverage for assignment {}: {}" , assignment .getId (), e .getMessage (), e );
619+ return false ;
620+ }
621+ }
622+
623+ /**
624+ * Schedules a retry of the peer assessment assignment after a delay
625+ */
626+ private void scheduleRetry (Assignment assignment ) {
627+ try {
628+ // Schedule retry in 5 minutes
629+ Instant retryTime = Instant .now ().plusSeconds (300 );
630+ log .debug ("Scheduling peer assessment retry for assignment {} at {}" , assignment .getId (), retryTime );
631+ scheduledInvocationManager .createDelayedInvocation (retryTime , "org.sakaiproject.assignment.api.AssignmentPeerAssessmentService" , assignment .getId ());
632+ } catch (Exception e ) {
633+ log .error ("Failed to schedule retry for assignment {}: {}" , assignment .getId (), e .getMessage (), e );
634+ }
635+ }
532636}
0 commit comments