Skip to content

Commit 83241b8

Browse files
committed
SAK-52275 Assignments PeerReview Schedule a retry after detecting an error in reviewer allocation
1 parent 27007d3 commit 83241b8

File tree

1 file changed

+105
-1
lines changed

1 file changed

+105
-1
lines changed

assignment/impl/src/java/org/sakaiproject/assignment/impl/AssignmentPeerAssessmentServiceImpl.java

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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.info("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

Comments
 (0)