package fi.otavanopisto.muikku.plugins.evaluation; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import javax.inject.Inject; import org.apache.commons.lang3.StringUtils; import fi.otavanopisto.muikku.i18n.LocaleController; import fi.otavanopisto.muikku.model.base.Tag; import fi.otavanopisto.muikku.model.users.UserEntity; import fi.otavanopisto.muikku.model.workspace.WorkspaceEntity; import fi.otavanopisto.muikku.plugins.communicator.CommunicatorController; import fi.otavanopisto.muikku.plugins.communicator.model.CommunicatorMessageCategory; import fi.otavanopisto.muikku.plugins.evaluation.dao.SupplementationRequestDAO; import fi.otavanopisto.muikku.plugins.evaluation.dao.WorkspaceMaterialEvaluationDAO; import fi.otavanopisto.muikku.plugins.evaluation.model.SupplementationRequest; import fi.otavanopisto.muikku.plugins.evaluation.model.WorkspaceMaterialEvaluation; import fi.otavanopisto.muikku.plugins.workspace.ContentNode; import fi.otavanopisto.muikku.plugins.workspace.WorkspaceMaterialController; import fi.otavanopisto.muikku.plugins.workspace.WorkspaceMaterialException; import fi.otavanopisto.muikku.plugins.workspace.WorkspaceMaterialReplyController; import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterial; import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialAssignmentType; import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialReply; import fi.otavanopisto.muikku.plugins.workspace.model.WorkspaceMaterialReplyState; import fi.otavanopisto.muikku.schooldata.GradingController; import fi.otavanopisto.muikku.schooldata.WorkspaceController; import fi.otavanopisto.muikku.schooldata.WorkspaceEntityController; import fi.otavanopisto.muikku.schooldata.entity.GradingScale; import fi.otavanopisto.muikku.schooldata.entity.GradingScaleItem; import fi.otavanopisto.muikku.schooldata.entity.Workspace; import fi.otavanopisto.muikku.schooldata.entity.WorkspaceAssessmentRequest; import fi.otavanopisto.muikku.servlet.BaseUrl; import fi.otavanopisto.muikku.users.UserEntityController; public class EvaluationController { @Inject @BaseUrl private String baseUrl; @Inject private WorkspaceMaterialReplyController workspaceMaterialReplyController; @Inject private WorkspaceController workspaceController; @Inject private WorkspaceEntityController workspaceEntityController; @Inject private WorkspaceMaterialController workspaceMaterialController; @Inject private WorkspaceMaterialEvaluationDAO workspaceMaterialEvaluationDAO; @Inject private SupplementationRequestDAO supplementationRequestDAO; @Inject private GradingController gradingController; @Inject private UserEntityController userEntityController; @Inject private LocaleController localeController; @Inject private CommunicatorController communicatorController; public SupplementationRequest createSupplementationRequest(Long userEntityId, Long studentEntityId, Long workspaceEntityId, Long workspaceMaterialId, Date requestDate, String requestText) { SupplementationRequest supplementationRequest = supplementationRequestDAO.createSupplementationRequest( userEntityId, studentEntityId, workspaceEntityId, workspaceMaterialId, requestDate, requestText); // For workspace supplementation requests, mark respective workspace assessment requests as handled if (studentEntityId != null && workspaceEntityId != null) { markWorkspaceAssessmentRequestsAsHandled(studentEntityId, workspaceEntityId); } handleSupplementationNotifications(supplementationRequest); return supplementationRequest; } public WorkspaceMaterialEvaluation createWorkspaceMaterialEvaluation(UserEntity student, WorkspaceMaterial workspaceMaterial, GradingScale gradingScale, GradingScaleItem grade, UserEntity assessor, Date evaluated, String verbalAssessment) { WorkspaceMaterialEvaluation evaluation = workspaceMaterialEvaluationDAO.create(student.getId(), workspaceMaterial.getId(), gradingScale.getIdentifier(), gradingScale.getSchoolDataSource(), grade.getIdentifier(), grade.getSchoolDataSource(), assessor.getId(), evaluated, verbalAssessment); WorkspaceMaterialReply reply = workspaceMaterialReplyController.findWorkspaceMaterialReplyByWorkspaceMaterialAndUserEntity(workspaceMaterial, student); WorkspaceMaterialReplyState state = grade.isPassingGrade() ? WorkspaceMaterialReplyState.PASSED : WorkspaceMaterialReplyState.FAILED; if (reply == null) { workspaceMaterialReplyController.createWorkspaceMaterialReply(workspaceMaterial, state, student); } else { workspaceMaterialReplyController.updateWorkspaceMaterialReply(reply, state); } // #1669: Notification on non-passing grade on an assignment if (state == WorkspaceMaterialReplyState.FAILED) { notifyOfFailedAssignment(assessor, student, workspaceMaterial, grade, verbalAssessment); } return evaluation; } public void deleteSupplementationRequest(SupplementationRequest supplementationRequest) { supplementationRequestDAO.archive(supplementationRequest); } public void deleteWorkspaceMaterialEvaluation(WorkspaceMaterialEvaluation evaluation) { if (evaluation != null) { workspaceMaterialEvaluationDAO.delete(evaluation); } } public SupplementationRequest findSupplementationRequestByStudentAndWorkspace(Long studentEntityId, Long workspaceEntityId) { return supplementationRequestDAO.findByStudentAndWorkspace(studentEntityId, workspaceEntityId); } public SupplementationRequest findSupplementationRequestByStudentAndWorkspaceAndArchived(Long studentEntityId, Long workspaceEntityId, Boolean archived) { return supplementationRequestDAO.findByStudentAndWorkspaceAndArchived(studentEntityId, workspaceEntityId, archived); } public SupplementationRequest findSupplementationRequestByStudentAndWorkspaceMaterial(Long studentEntityId, Long workspaceMaterialId) { return supplementationRequestDAO.findByStudentAndWorkspaceMaterial(studentEntityId, workspaceMaterialId); } public SupplementationRequest findSupplementationRequestByStudentAndWorkspaceMaterialAndArchived(Long studentEntityId, Long workspaceMaterialId, Boolean archived) { return supplementationRequestDAO.findByStudentAndWorkspaceMaterialAndArchived(studentEntityId, workspaceMaterialId, archived); } public WorkspaceMaterialEvaluation findWorkspaceMaterialEvaluation(Long id) { return workspaceMaterialEvaluationDAO.findById(id); } public WorkspaceMaterialEvaluation findWorkspaceMaterialEvaluationByWorkspaceMaterialAndStudent(WorkspaceMaterial workspaceMaterial, UserEntity student) { return workspaceMaterialEvaluationDAO.findByWorkspaceMaterialIdAndStudentEntityId(workspaceMaterial.getId(), student.getId()); } public List<WorkspaceMaterialEvaluation> listWorkspaceMaterialEvaluationsByWorkspaceMaterialId(Long workspaceMaterialId){ return workspaceMaterialEvaluationDAO.listByWorkspaceMaterialId(workspaceMaterialId); } public SupplementationRequest updateSupplementationRequest(SupplementationRequest supplementationRequest, Long userEntityId, Date requestDate, String requestText) { supplementationRequest = supplementationRequestDAO.updateSupplementationRequest( supplementationRequest, userEntityId, supplementationRequest.getStudentEntityId(), supplementationRequest.getWorkspaceEntityId(), supplementationRequest.getWorkspaceMaterialId(), requestDate, requestText, Boolean.FALSE); // For workspace supplementation requests, mark respective workspace assessment requests as handled if (supplementationRequest.getStudentEntityId() != null && supplementationRequest.getWorkspaceEntityId() != null) { markWorkspaceAssessmentRequestsAsHandled(supplementationRequest.getStudentEntityId(), supplementationRequest.getWorkspaceEntityId()); } handleSupplementationNotifications(supplementationRequest); return supplementationRequest; } public WorkspaceMaterialEvaluation updateWorkspaceMaterialEvaluation(WorkspaceMaterialEvaluation workspaceMaterialEvaluation, GradingScale gradingScale, GradingScaleItem grade, UserEntity assessor, Date evaluated, String verbalAssessment) { workspaceMaterialEvaluationDAO.updateGradingScaleIdentifier(workspaceMaterialEvaluation, gradingScale.getIdentifier()); workspaceMaterialEvaluationDAO.updateGradingScaleSchoolDataSource(workspaceMaterialEvaluation, gradingScale.getSchoolDataSource()); workspaceMaterialEvaluationDAO.updateVerbalAssessment(workspaceMaterialEvaluation, verbalAssessment); workspaceMaterialEvaluationDAO.updateGradeIdentifier(workspaceMaterialEvaluation, grade.getIdentifier()); workspaceMaterialEvaluationDAO.updateGradeSchoolDataSource(workspaceMaterialEvaluation, grade.getSchoolDataSource()); workspaceMaterialEvaluationDAO.updateAssessorEntityId(workspaceMaterialEvaluation, assessor.getId()); workspaceMaterialEvaluationDAO.updateEvaluated(workspaceMaterialEvaluation, evaluated); WorkspaceMaterial workspaceMaterial = workspaceMaterialController.findWorkspaceMaterialById(workspaceMaterialEvaluation.getWorkspaceMaterialId()); UserEntity student = userEntityController.findUserEntityById(workspaceMaterialEvaluation.getStudentEntityId()); WorkspaceMaterialReply reply = workspaceMaterialReplyController.findWorkspaceMaterialReplyByWorkspaceMaterialAndUserEntity(workspaceMaterial, student); WorkspaceMaterialReplyState state = grade.isPassingGrade() ? WorkspaceMaterialReplyState.PASSED : WorkspaceMaterialReplyState.FAILED; if (reply == null) { workspaceMaterialReplyController.createWorkspaceMaterialReply(workspaceMaterial, state, student); } else { workspaceMaterialReplyController.updateWorkspaceMaterialReply(reply, state); } // #1669: Notification on non-passing grade on an assignment if (state == WorkspaceMaterialReplyState.FAILED) { notifyOfFailedAssignment(assessor, student, workspaceMaterial, grade, verbalAssessment); } return workspaceMaterialEvaluation; } public List<ContentNode> getAssignmentContentNodes(WorkspaceEntity workspaceEntity, boolean processHtml) throws WorkspaceMaterialException { List<ContentNode> result = new ArrayList<>(); addAssignmentNodes(workspaceMaterialController.listVisibleEvaluableWorkspaceMaterialsAsContentNodes(workspaceEntity, processHtml), result); return result; } private void addAssignmentNodes(List<ContentNode> contentNodes, List<ContentNode> result) { for (ContentNode contentNode : contentNodes) { if (contentNode.getAssignmentType() == WorkspaceMaterialAssignmentType.EVALUATED) { result.add(contentNode); } else { addAssignmentNodes(contentNode.getChildren(), result); } } } /** * Marks the assessment requests of student <code>userEntityId</code> in workspace * <code>workspaceEntityId</code> as handled. * * @param userEntityId Student id (in Muikku) * @param workspaceEntityId Workspace id (in Muikku) */ private void markWorkspaceAssessmentRequestsAsHandled(Long userEntityId, Long workspaceEntityId) { // TODO Performance bottleneck? UserEntity userEntity = userEntityController.findUserEntityById(userEntityId); WorkspaceEntity workspaceEntity = workspaceEntityController.findWorkspaceEntityById(workspaceEntityId); // List assessment requests (from Pyramus) List<WorkspaceAssessmentRequest> assessmentRequests = gradingController.listWorkspaceAssessmentRequests( workspaceEntity.getDataSource().getIdentifier(), workspaceEntity.getIdentifier(), userEntity.getDefaultIdentifier()); // Mark each assessment request as handled (to Pyramus) for (WorkspaceAssessmentRequest assessmentRequest : assessmentRequests) { gradingController.updateWorkspaceAssessmentRequest( assessmentRequest.getSchoolDataSource(), assessmentRequest.getIdentifier(), assessmentRequest.getWorkspaceUserIdentifier(), assessmentRequest.getWorkspaceUserSchoolDataSource(), workspaceEntity.getIdentifier(), userEntity.getDefaultIdentifier(), assessmentRequest.getRequestText(), assessmentRequest.getDate(), assessmentRequest.getArchived(), Boolean.TRUE); // handled } } /** * Handles all notification messages related to the given supplementation request. * * @param supplementationRequest Supplementation request */ private void handleSupplementationNotifications(SupplementationRequest supplementationRequest) { Long teacherEntityId = supplementationRequest.getUserEntityId(); Long studentEntityId = supplementationRequest.getStudentEntityId(); Long workspaceEntityId = supplementationRequest.getWorkspaceEntityId(); Long workspaceMaterialId = supplementationRequest.getWorkspaceMaterialId(); String requestText = supplementationRequest.getRequestText(); // If the supplementation request is for an assignment, also mark student's reply as INCOMPLETE if (studentEntityId != null && workspaceMaterialId != null) { UserEntity teacher = userEntityController.findUserEntityById(teacherEntityId); UserEntity student = userEntityController.findUserEntityById(studentEntityId); WorkspaceMaterial workspaceMaterial = workspaceMaterialController.findWorkspaceMaterialById(workspaceMaterialId); if (student != null && workspaceMaterial != null) { WorkspaceMaterialReply reply = workspaceMaterialReplyController.findWorkspaceMaterialReplyByWorkspaceMaterialAndUserEntity(workspaceMaterial, student); if (reply == null) { workspaceMaterialReplyController.createWorkspaceMaterialReply(workspaceMaterial, WorkspaceMaterialReplyState.INCOMPLETE, student); } else { workspaceMaterialReplyController.updateWorkspaceMaterialReply(reply, WorkspaceMaterialReplyState.INCOMPLETE); } } // Send notification of an incomplete assignment notifyOfIncompleteAssignment(teacher, student, workspaceMaterial, requestText); } else if (studentEntityId != null && workspaceEntityId != null) { // Send notification of an incomplete workspace UserEntity teacher = userEntityController.findUserEntityById(teacherEntityId); UserEntity student = userEntityController.findUserEntityById(studentEntityId); WorkspaceEntity workspaceEntity = workspaceEntityController.findWorkspaceEntityById(workspaceEntityId); notifyOfIncompleteWorkspace(teacher, student, workspaceEntity, requestText); } } private void notifyOfFailedAssignment(UserEntity assessor, UserEntity student, WorkspaceMaterial workspaceMaterial, GradingScaleItem grade, String verbalAssessment) { Locale locale = student.getLocale() == null ? new Locale("fi") : new Locale(student.getLocale()); // Workspace Long workspaceEntityId = workspaceMaterialController.getWorkspaceEntityId(workspaceMaterial); WorkspaceEntity workspaceEntity = workspaceEntityController.findWorkspaceEntityById(workspaceEntityId); Workspace workspace = workspaceController.findWorkspace(workspaceEntity); String workspaceUrl = String.format("%s/workspace/%s/materials", baseUrl, workspaceEntity.getUrlName()); String workspaceName = workspace.getName(); if (!StringUtils.isBlank(workspace.getNameExtension())) { workspaceName = String.format("%s (%s)", workspaceName, workspace.getNameExtension()); } // Notification String assignmentName = workspaceMaterial.getTitle(); String gradeName = grade.getName(); String notificationCaption = localeController.getText(locale, "plugin.evaluation.assignmentFailed.notificationCaption"); String notificationText = localeController.getText(locale, "plugin.evaluation.assignmentFailed.notificationText"); notificationCaption = MessageFormat.format(notificationCaption, gradeName); notificationText = MessageFormat.format(notificationText, assignmentName, workspaceUrl, workspaceName, gradeName, verbalAssessment == null ? "" : verbalAssessment); // Communicator message CommunicatorMessageCategory category = communicatorController.persistCategory("assessments"); communicatorController.createMessage( communicatorController.createMessageId(), assessor, Arrays.asList(student), null, null, null, category, notificationCaption, notificationText, Collections.<Tag>emptySet()); } private void notifyOfIncompleteAssignment(UserEntity assessor, UserEntity student, WorkspaceMaterial workspaceMaterial, String verbalAssessment) { Locale locale = student.getLocale() == null ? new Locale("fi") : new Locale(student.getLocale()); // Workspace Long workspaceEntityId = workspaceMaterialController.getWorkspaceEntityId(workspaceMaterial); WorkspaceEntity workspaceEntity = workspaceEntityController.findWorkspaceEntityById(workspaceEntityId); Workspace workspace = workspaceController.findWorkspace(workspaceEntity); String workspaceUrl = String.format("%s/workspace/%s/materials", baseUrl, workspaceEntity.getUrlName()); String workspaceName = workspace.getName(); if (!StringUtils.isBlank(workspace.getNameExtension())) { workspaceName = String.format("%s (%s)", workspaceName, workspace.getNameExtension()); } // Notification String assignmentName = workspaceMaterial.getTitle(); String notificationCaption = localeController.getText(locale, "plugin.evaluation.assignmentIncomplete.notificationCaption"); String notificationText = localeController.getText(locale, "plugin.evaluation.assignmentIncomplete.notificationText"); notificationText = MessageFormat.format(notificationText, assignmentName, workspaceUrl, workspaceName, verbalAssessment == null ? "" : verbalAssessment); // Communicator message CommunicatorMessageCategory category = communicatorController.persistCategory("assessments"); communicatorController.createMessage( communicatorController.createMessageId(), assessor, Arrays.asList(student), null, null, null, category, notificationCaption, notificationText, Collections.<Tag>emptySet()); } private void notifyOfIncompleteWorkspace(UserEntity teacher, UserEntity student, WorkspaceEntity workspaceEntity, String verbalAssessment) { // Workspace Workspace workspace = workspaceController.findWorkspace(workspaceEntity); String workspaceUrl = String.format("%s/workspace/%s/materials", baseUrl, workspaceEntity.getUrlName()); String workspaceName = workspace.getName(); if (!StringUtils.isBlank(workspace.getNameExtension())) { workspaceName = String.format("%s (%s)", workspaceName, workspace.getNameExtension()); } Locale locale = userEntityController.getLocale(student); CommunicatorMessageCategory category = communicatorController.persistCategory("assessments"); communicatorController.createMessage( communicatorController.createMessageId(), teacher, Arrays.asList(student), null, null, null, category, localeController.getText(locale, "plugin.evaluation.workspaceIncomplete.notificationCaption"), localeController.getText(locale, "plugin.evaluation.workspaceIncomplete.notificationText", new Object[] {workspaceUrl, workspaceName, verbalAssessment}), Collections.<Tag>emptySet()); } }