From 180ad2eeafa8ad0a80f1093be3935a9afab71fa0 Mon Sep 17 00:00:00 2001 From: Adrian Fish Date: Thu, 19 Oct 2023 15:59:16 +0100 Subject: [PATCH] SAK-47987 grader. Add peer review information to submission https://sakaiproject.atlassian.net/browse/SAK-47987 --- .../AssignmentEntityProvider.java | 59 +++++++++++++++++++ .../src/webapp/WEB-INF/applicationContext.xml | 1 + .../bundle/src/main/bundle/grader.properties | 5 ++ .../js/grader/sakai-grader-rendering-mixin.js | 29 +++++++++ .../src/main/frontend/js/grader/submission.js | 2 + 5 files changed, 96 insertions(+) diff --git a/assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java b/assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java index 340954e68ca2..0d2de600676b 100644 --- a/assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java +++ b/assignment/tool/src/java/org/sakaiproject/assignment/entityproviders/AssignmentEntityProvider.java @@ -34,6 +34,7 @@ import org.apache.commons.fileupload.FileItem; import org.sakaiproject.assignment.api.AssignmentConstants; +import org.sakaiproject.assignment.api.AssignmentPeerAssessmentService; import org.sakaiproject.assignment.api.AssignmentReferenceReckoner; import org.sakaiproject.assignment.api.AssignmentService; import org.sakaiproject.assignment.api.ContentReviewResult; @@ -74,6 +75,7 @@ import org.sakaiproject.time.api.UserTimeService; import org.sakaiproject.timesheet.api.TimeSheetEntry; import org.sakaiproject.tool.api.SessionManager; +import org.sakaiproject.tool.api.ToolManager; import org.sakaiproject.user.api.User; import org.sakaiproject.user.api.UserDirectoryService; import org.sakaiproject.lti.api.LTIService; @@ -91,6 +93,7 @@ public class AssignmentEntityProvider extends AbstractEntityProvider implements private static ResourceLoader rb = new ResourceLoader("assignment"); + private AssignmentPeerAssessmentService assignmentPeerAssessmentService; private AssignmentService assignmentService; private AssignmentToolUtils assignmentToolUtils; private ContentHostingService contentHostingService; @@ -99,6 +102,7 @@ public class AssignmentEntityProvider extends AbstractEntityProvider implements private SecurityService securityService; private SessionManager sessionManager; private SiteService siteService; + private ToolManager toolManager; private AssignmentSupplementItemService assignmentSupplementItemService; private GradingService gradingService; private ServerConfigurationService serverConfigurationService; @@ -763,6 +767,61 @@ private Map submissionToMap(Set activeSubmitters, Assign submission.put("previewableAttachments", previewableAttachments); } } + + //peer review + if (assignment.getAllowPeerAssessment() + && assignment.getPeerAssessmentStudentReview() + && assignmentService.isPeerAssessmentClosed(assignment)) { + List reviews = assignmentPeerAssessmentService.getPeerAssessmentItems(as.getId(), assignment.getScaleFactor()); + if (reviews != null) { + List completedReviews = new ArrayList<>(); + for (PeerAssessmentItem review : reviews) { + if (!review.getRemoved() && (review.getScore() != null || (StringUtils.isNotBlank(review.getComment())))) { + //only show peer reviews that have either a score or a comment saved + if (assignment.getPeerAssessmentAnonEval()) { + //annonymous eval + review.setAssessorDisplayName(rb.getFormattedMessage("gen.reviewer.countReview", completedReviews.size() + 1)); + } else { + //need to set the assessor's display name + try { + if (assignment.getIsGroup()) { + String siteId = toolManager.getCurrentPlacement().getContext(); + Site site = siteService.getSite(siteId); + review.setAssessorDisplayName(site.getGroup(review.getId().getAssessorUserId()).getTitle()); + } else { + review.setAssessorDisplayName(userDirectoryService.getUser(review.getId().getAssessorUserId()).getDisplayName()); + } + } catch (IdUnusedException | UserNotDefinedException e) { + //reviewer doesn't exist or one of userId/groupId/siteId is wrong + log.error(e.getMessage(), e); + //set a default one: + review.setAssessorDisplayName(rb.getFormattedMessage("gen.reviewer.countReview", completedReviews.size() + 1)); + } + } + // get attachments for peer review item + List attachments = assignmentPeerAssessmentService.getPeerAssessmentAttachments(review.getId().getSubmissionId(), review.getId().getAssessorUserId()); + if (attachments != null && !attachments.isEmpty()) { + List attachmentRefList = new ArrayList<>(); + for (PeerAssessmentAttachment attachment : attachments) { + try { + Reference ref = entityManager.newReference(contentHostingService.getReference(attachment.getResourceId())); + attachmentRefList.add(ref); + } catch (Exception e) { + log.warn(e.getMessage(), e); + } + } + if (!attachmentRefList.isEmpty()) + review.setAttachmentRefList(attachmentRefList); + } + completedReviews.add(review); + } + } + if (completedReviews.size() > 0) { + submission.put("peerReviews", completedReviews); + } + } + } + } List> submitters diff --git a/assignment/tool/src/webapp/WEB-INF/applicationContext.xml b/assignment/tool/src/webapp/WEB-INF/applicationContext.xml index 037c5d866851..02efce1a3811 100644 --- a/assignment/tool/src/webapp/WEB-INF/applicationContext.xml +++ b/assignment/tool/src/webapp/WEB-INF/applicationContext.xml @@ -18,6 +18,7 @@ + diff --git a/webcomponents/bundle/src/main/bundle/grader.properties b/webcomponents/bundle/src/main/bundle/grader.properties index 6b160af79d91..ade8c8b38539 100644 --- a/webcomponents/bundle/src/main/bundle/grader.properties +++ b/webcomponents/bundle/src/main/bundle/grader.properties @@ -1,6 +1,11 @@ +attachment=Attachment done=Done +grade=Grade: no_submission=No submission no_submission_for=No submission for +peer_reviews=Peer Reviews +reviewer_comments=Reviewer Comments +reviewer_attachments=Reviewer Attachments grading_rubric_tooltip=Grade this submission using a rubric rubric=Rubric add_feedback_tooltip=Write, or record, some feedback for this student diff --git a/webcomponents/tool/src/main/frontend/js/grader/sakai-grader-rendering-mixin.js b/webcomponents/tool/src/main/frontend/js/grader/sakai-grader-rendering-mixin.js index 81e89765e3dc..be79795f3a7d 100644 --- a/webcomponents/tool/src/main/frontend/js/grader/sakai-grader-rendering-mixin.js +++ b/webcomponents/tool/src/main/frontend/js/grader/sakai-grader-rendering-mixin.js @@ -183,6 +183,35 @@ export const graderRenderingMixin = Base => class extends Base { ` : ""} `} + ${this.gradable.allowPeerAssessment && this.submission.peerReviews?.length > 0 ? html` +
+

${this.i18n.peer_reviews}

+ ${this.submission.peerReviews.map(pr => html` + +
+
${pr.assessorDisplayName}
+
+
+
+ ${this.i18n.grade} + ${pr.scoreDisplay} +
+
${this.i18n.reviewer_comments}
+
${unsafeHTML(pr.comment)}
+ ${pr.attachmentRefList && pr.attachmentRefList.length > 0 ? html` +
${this.i18n.reviewer_attachments}
+ ${pr.attachmentRefList.map((ref, i) => html` + + `)} + ` : ""} +
+
+
+ `)} +
+ ` : "" } ` : ""} `; diff --git a/webcomponents/tool/src/main/frontend/js/grader/submission.js b/webcomponents/tool/src/main/frontend/js/grader/submission.js index 50bd405755f5..9063580368e7 100644 --- a/webcomponents/tool/src/main/frontend/js/grader/submission.js +++ b/webcomponents/tool/src/main/frontend/js/grader/submission.js @@ -19,6 +19,8 @@ class Submission { this.hasHistory = this.history.grades || this.history.comments; } + this.peerReviews = init.peerReviews; + this.hasRubricEvaluation = init.hasRubricEvaluation; this.showExtension = true;