Index: lams_admin/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -ra9f95a26e562a58b55c99f2c18e253c151ef457a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_admin/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision a9f95a26e562a58b55c99f2c18e253c151ef457a) +++ lams_admin/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -501,6 +501,7 @@ label.upload.info =Uploaded file must be an Excel file and not exceed size of {0} errors.maxfilesize =Uploaded file exceeded maximum size: {0} error.attachment.not.xls =File is not an Excel .xls file. +sysadmin.maintain.session.count =logged in users sysadmin.maintain.session =Logged in users sysadmin.maintain.session.login =Login sysadmin.maintain.session.id =Session ID Index: lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java =================================================================== diff -u -re8a7110708b15579af2c6b31ac52a6da427fef6d -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java (.../ILamsCoreToolService.java) (revision e8a7110708b15579af2c6b31ac52a6da427fef6d) +++ lams_common/src/java/org/lamsfoundation/lams/tool/service/ILamsCoreToolService.java (.../ILamsCoreToolService.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -168,7 +168,7 @@ /** * Ask an activity to delete content entered by the given user, if exists. - * + * * @return whether the activity should be persisted afterwards */ boolean removeLearnerContent(Activity activity, User learner) throws ToolException; @@ -224,18 +224,17 @@ * @throws ToolException */ ToolOutput getOutputFromTool(String conditionName, ToolSession toolSession, Integer learnerId) throws ToolException; - + /** * Returns tool outputs for the entire activity. Note, some tools don't support this functionality and will always * return empty list. - * + * * @param conditionName * @param toolActivity * @return * @throws ToolException */ - List getOutputsFromTool(String conditionName, ToolActivity toolActivity) - throws ToolException; + List getOutputsFromTool(String conditionName, ToolActivity toolActivity) throws ToolException; /** * Ask a tool for a set of ToolOutputs, based on the given toolSessionId. @@ -274,26 +273,35 @@ */ SortedMap getOutputFromTool(List names, ToolSession toolSession, Integer learnerId) throws ToolException; - + /** - * Returns confidence levels indicated by learners when answering questions. By now this feature is supported only by Assessment and MCQ tools. - * + * Returns confidence levels indicated by learners when answering questions. By now this feature is supported only + * by Assessment and MCQ tools. + * * @param toolSession * @return */ List getConfidenceLevelsByToolSession(ToolSession toolSession); - + /** * Returns answers learners left for VSA questions in Assessment activity (together with according confidence * levels, if such option is turned on in Assessment). Currently only Assessment tool is capable of producing VSA * answers. - * + * * @param toolSession * @return */ Collection getVsaAnswersByToolSession(ToolSession toolSession); /** + * Returns true in case this tool is leader-aware and specified user is a leader in his group, false otherwise. + * + * @param toolSession + * @param learner + */ + boolean isUserLeaderInActivity(ToolSession toolSession, User user); + + /** * Notifies tool that the user is force completed. Currently it's been utilized only by leader aware tools, which * copy results from leader to non-leader. * @@ -314,7 +322,7 @@ /** * Calculates lesson's maximum possible mark by adding up all activities max marks. - * + * * @param lesson * @return */ @@ -441,7 +449,6 @@ String setupToolURLWithToolContent(ToolActivity activity, String toolURL); Object findToolService(Tool tool) throws NoSuchBeanDefinitionException; - - ToolCompletionStatus getCompletionStatusFromTool(User learner, Activity activity) - throws LamsToolServiceException; + + ToolCompletionStatus getCompletionStatusFromTool(User learner, Activity activity) throws LamsToolServiceException; } Index: lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java =================================================================== diff -u -r122ff0d8419be3fac72ddb842cbbce1cea01e542 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java (.../LamsCoreToolService.java) (revision 122ff0d8419be3fac72ddb842cbbce1cea01e542) +++ lams_common/src/java/org/lamsfoundation/lams/tool/service/LamsCoreToolService.java (.../LamsCoreToolService.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -158,8 +158,8 @@ // if haven't found an existing tool session then create one if (toolSession == null) { - if (LamsCoreToolService.log.isDebugEnabled()) { - LamsCoreToolService.log.debug("Creating tool session for [" + activity.getActivityId() + "," + if (log.isDebugEnabled()) { + log.debug("Creating tool session for [" + activity.getActivityId() + "," + activity.getTitle() + "] for learner [" + learner.getLogin() + "] lesson [" + lesson.getLessonId() + "," + lesson.getLessonName() + "]."); } @@ -203,7 +203,7 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to create tool session. ToolActivity " + activity; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } } @@ -228,7 +228,7 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to copy/update the tool content. ToolActivity " + toolActivity; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } @@ -242,14 +242,14 @@ if (toolContent == null) { String error = "The toolContentID " + toolContentId + " is not valid. No such record exists on the database."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } Tool tool = toolContent.getTool(); if (tool == null) { String error = "The tool for toolContentId " + toolContentId + " is missing."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } @@ -265,7 +265,7 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to copy the tool content. ToolContentId " + toolContentId; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } @@ -280,7 +280,7 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to delete the tool content. ToolActivity " + toolActivity; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } } @@ -295,7 +295,7 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to delete learner content. ToolActivity " + toolActivity; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } } else if (activity.isGroupingActivity()) { @@ -369,14 +369,14 @@ if (toolContent == null) { String error = "The toolContentID " + toolContentId + " is not valid. No such record exists on the database."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } Tool tool = toolContent.getTool(); if (tool == null) { String error = "The tool for toolContentId " + toolContentId + " is missing."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } @@ -386,12 +386,12 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to get the tool output definitions. ToolContentId " + toolContentId; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } catch (java.lang.AbstractMethodError e) { String message = "Tool " + tool.getToolDisplayName() + " doesn't support the getToolOutputDefinitions(toolContentId) method so no output definitions can be accessed."; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } @@ -406,14 +406,14 @@ if (toolContent == null) { String error = "The toolContentID " + inputToolContentId + " is not valid. No such record exists on the database."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } Tool tool = toolContent.getTool(); if (tool == null) { String error = "The tool for toolContentId " + inputToolContentId + " is missing."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } @@ -449,12 +449,12 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to get the tool output definitions. ToolContentId " + inputToolContentId; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } catch (java.lang.AbstractMethodError e) { String message = "Tool " + tool.getToolDisplayName() + " doesn't support the getSupportedToolOutputDefinitionClasses(definitionType) method so no output definitions can be accessed."; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } @@ -475,14 +475,14 @@ if (toolSession == null) { String error = "The toolSession is not valid. Unable to get the tool output"; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } Tool tool = toolSession.getToolActivity().getTool(); if (tool == null) { String error = "The tool for toolSession " + toolSession.getToolSessionId() + " is missing."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } @@ -493,12 +493,12 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to gt the tol output. toolSession " + toolSession.getToolSessionId(); - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } catch (java.lang.AbstractMethodError e) { String message = "Tool " + tool.getToolDisplayName() + " doesn't support the getToolOutput(name, toolSessionId, learnerId) method so no output definitions can be accessed."; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } } @@ -508,14 +508,14 @@ if (toolActivity == null) { String error = "The toolActivity is null. Unable to get tool outputs"; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } Tool tool = toolActivity.getTool(); if (tool == null) { String error = "The tool for toolActivity " + toolActivity.getActivityId() + " is missing."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } @@ -525,12 +525,12 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to grt the tol output. toolActivity " + toolActivity.getActivityId(); - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } catch (java.lang.AbstractMethodError e) { String message = "Tool " + tool.getToolDisplayName() + " doesn't support the getToolOutput(name, toolSessionId, learnerId) method so no output definitions can be accessed."; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } } @@ -579,14 +579,14 @@ if (toolSession == null) { String error = "The toolSession is null. Unable to get confidence levels"; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } Tool tool = toolSession.getToolActivity().getTool(); if (tool == null) { String error = "The tool for toolSession " + toolSession.getToolSessionId() + " is missing."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } @@ -596,29 +596,60 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to get the tool output. toolActivity " + toolSession.getToolActivity().getActivityId(); - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } catch (java.lang.AbstractMethodError e) { String message = "Tool " + tool.getToolDisplayName() + " doesn't support the getToolOutput(name, toolSessionId, learnerId) method so no output definitions can be accessed."; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } } + + @Override + public boolean isUserLeaderInActivity(ToolSession toolSession, User learner) throws ToolException { + if (toolSession == null) { + String error = "The toolSession is not valid. Unable to check whether user is a leader."; + log.error(error); + throw new DataMissingException(error); + } + Tool tool = toolSession.getToolActivity().getTool(); + if (tool == null) { + String error = "The tool for toolSession " + toolSession.getToolSessionId() + " is missing."; + log.error(error); + throw new DataMissingException(error); + } + try { + ToolSessionManager sessionManager = (ToolSessionManager) findToolService(tool); + //we get leader from the current tool rather than LeaderSelection tool in order to avoid making redundant calls to DB + return sessionManager.isUserGroupLeader(learner.getUserId().longValue(), toolSession.getToolSessionId()); + } catch (NoSuchBeanDefinitionException e) { + String message = "A tool which is defined in the database appears to missing from the classpath. Unable to check whether user is a leader. toolSession " + + toolSession.getToolSessionId(); + log.error(message, e); + throw new ToolException(message, e); + } catch (AbstractMethodError e) { + String message = "Tool " + tool.getToolDisplayName() + + " doesn't support the isUserLeaderInActivity(ToolSession, User) method so can't check whether user is a leader."; + log.error(message, e); + throw new ToolException(message, e); + } + } + @Override public void forceCompleteActivity(ToolSession toolSession, User learner) throws ToolException { if (toolSession == null) { String error = "The toolSession is not valid. Unable to force complete activity."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } Tool tool = toolSession.getToolActivity().getTool(); if (tool == null) { String error = "The tool for toolSession " + toolSession.getToolSessionId() + " is missing."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } @@ -628,12 +659,12 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to force complete activity. toolSession " + toolSession.getToolSessionId(); - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } catch (java.lang.AbstractMethodError e) { String message = "Tool " + tool.getToolDisplayName() + " doesn't support the forceCompleteUser(ToolSession toolSession, User learner) method so can't force complete learner."; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } } @@ -651,14 +682,14 @@ if (toolSession == null) { String error = "The toolSession is not valid. Unable to get the tool output"; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } Tool tool = toolSession.getToolActivity().getTool(); if (tool == null) { String error = "The tool for toolSession " + toolSession.getToolSessionId() + " is missing."; - LamsCoreToolService.log.error(error); + log.error(error); throw new DataMissingException(error); } @@ -669,12 +700,12 @@ } catch (NoSuchBeanDefinitionException e) { String message = "A tool which is defined in the database appears to missing from the classpath. Unable to gt the tol output. toolSession " + toolSession.getToolSessionId(); - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } catch (java.lang.AbstractMethodError e) { String message = "Tool " + tool.getToolDisplayName() + " doesn't support the getToolOutput(name, toolSessionId, learnerId) method so no output definitions can be accessed."; - LamsCoreToolService.log.error(message, e); + log.error(message, e); throw new ToolException(message, e); } } @@ -787,7 +818,7 @@ @Override public void deleteToolSession(ToolSession toolSession) { if (toolSession == null) { - LamsCoreToolService.log.error("deleteToolSession: unable to delete tool session as tool session is null."); + log.error("deleteToolSession: unable to delete tool session as tool session is null."); return; } @@ -798,10 +829,10 @@ try { sessionManager.removeToolSession(toolSession.getToolSessionId()); } catch (DataMissingException e) { - LamsCoreToolService.log.error("Unable to delete tool data for tool session " + log.error("Unable to delete tool data for tool session " + toolSession.getToolSessionId() + " as toolSession does not exist", e); } catch (ToolException e) { - LamsCoreToolService.log.error("Unable to delete tool data for tool session " + log.error("Unable to delete tool data for tool session " + toolSession.getToolSessionId() + " as tool threw an exception", e); } @@ -922,7 +953,7 @@ String error = "Unable to set up url as session does not exist. Activity " + (activity != null ? activity.getActivityId() + ":" + activity.getTitle() : "null") + " learner " + (learner != null ? learner.getUserId() + ":" + learner.getLogin() : "null"); - LamsCoreToolService.log.error(error); + log.error(error); throw new LamsToolServiceException(error); } Index: lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java =================================================================== diff -u -ra9f95a26e562a58b55c99f2c18e253c151ef457a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java (.../GradebookService.java) (revision a9f95a26e562a58b55c99f2c18e253c151ef457a) +++ lams_gradebook/src/java/org/lamsfoundation/lams/gradebook/service/GradebookService.java (.../GradebookService.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -49,8 +49,8 @@ import org.lamsfoundation.lams.gradebook.model.GradebookUserActivityArchive; import org.lamsfoundation.lams.gradebook.model.GradebookUserLessonArchive; import org.lamsfoundation.lams.gradebook.util.GBGridView; -import org.lamsfoundation.lams.gradebook.util.GradebookUtil; import org.lamsfoundation.lams.gradebook.util.LessonComparator; +import org.lamsfoundation.lams.integration.service.IIntegrationService; import org.lamsfoundation.lams.learning.service.ILearnerService; import org.lamsfoundation.lams.learningdesign.Activity; import org.lamsfoundation.lams.learningdesign.ActivityEvaluation; @@ -131,12 +131,11 @@ private ILessonDAO lessonDAO; private ILessonService lessonService; private IUserManagementService userService; - private IBaseDAO baseDAO; private IActivityDAO activityDAO; private MessageService messageService; private ILogEventService logEventService; private static ILearnerService learnerService; - + private IIntegrationService integrationService; private IOutcomeService outcomeService; @Override @@ -727,7 +726,6 @@ } } } - } catch (ToolException e) { logger.debug( "Runtime exception when attempted to get outputs for activity: " + toolActivity.getActivityId(), e); @@ -737,7 +735,6 @@ @Override public void updateGradebookUserActivityMark(Lesson lesson, User learner, Activity activity, Double mark, Boolean markedInGradebook, boolean isAuditLogRequired) { - GradebookUserActivity gradebookUserActivity = gradebookDAO .getGradebookUserDataForActivity(activity.getActivityId(), learner.getUserId()); @@ -748,19 +745,6 @@ gradebookUserActivity, gradebookUserLesson); } - private void updateUserActivityGradebookMark(Lesson lesson, User learner, Activity activity, Double mark, - Boolean markedInGradebook, boolean isAuditLogRequired) { - - GradebookUserActivity gradebookUserActivity = gradebookDAO - .getGradebookUserDataForActivity(activity.getActivityId(), learner.getUserId()); - - GradebookUserLesson gradebookUserLesson = gradebookDAO.getGradebookUserDataForLesson(lesson.getLessonId(), - learner.getUserId()); - - updateUserActivityGradebookMark(lesson, learner, activity, mark, markedInGradebook, isAuditLogRequired, - gradebookUserActivity, gradebookUserLesson); - } - @Override public boolean isWeightedMarks(Long lessonId) { Lesson lesson = lessonService.getLesson(lessonId); @@ -788,6 +772,9 @@ return evaluations; } + /* + * TODO Method is not in use. Remove it? + * private void updateUserActivityGradebookMark(Lesson lesson, Activity activity, User learner) { ToolSession toolSession = toolService.getToolSessionByLearner(learner, activity); @@ -815,7 +802,7 @@ // Only set the mark if it hasnt previously been set by a teacher if ((gradebookUserActivity == null) || !gradebookUserActivity.getMarkedInGradebook()) { - updateUserActivityGradebookMark(lesson, learner, toolActivity, outputDouble, false, false); + updateGradebookUserActivityMark(lesson, learner, toolActivity, outputDouble, false, false); } } } @@ -825,6 +812,7 @@ "Runtime exception when attempted to get outputs for activity: " + toolActivity.getActivityId(), e); } } + */ /** * It's the same method as above, it only also accepts gradebookUserActivity and gradebookUserLesson as parameters. @@ -870,6 +858,13 @@ logEventService.logEvent(LogEvent.TYPE_MARK_UPDATED, monitorUser.getUserID(), learner.getUserId(), lesson.getLessonId(), activity.getActivityId(), message); } + + //propagade mark to integration server, if the lesson has been finished by the learner + LearnerProgress learnerProgress = lessonService.getUserProgressForLesson(learner.getUserId(), + lesson.getLessonId()); + if (learnerProgress != null && learnerProgress.isComplete()) { + integrationService.pushMarkToLtiConsumer(learner, lesson, gradebookUserLesson.getMark()); + } } } @@ -941,7 +936,6 @@ Integer orgId = organisation.getOrganisationId(); if (organisation != null) { - List lessons = (view == GBGridView.MON_COURSE || view == GBGridView.LIST || view == GBGridView.MON_USER) ? gradebookDAO.getLessonsByGroupAndUser(isGroupManager ? null : viewer.getUserId(), true, @@ -1035,12 +1029,8 @@ } else { lessonRow.setSubGroup(""); } - } } - - } else { - logger.error("Request for gradebook grid with a null organisation"); } return lessonRows; @@ -1897,7 +1887,9 @@ } } - @Override + /* + * TODO Method is not in use. Remove it? + * public void updateActivityMark(Double mark, String feedback, Integer userID, Long toolSessionID, Boolean markedInGradebook) { ToolSession toolSession = toolService.getToolSessionById(toolSessionID); @@ -1909,12 +1901,13 @@ // If gradebook user activity is null or the mark is set by teacher or was set previously by user - save the // mark and feedback if ((gradebookUserActivity == null) || markedInGradebook || !gradebookUserActivity.getMarkedInGradebook()) { - updateUserActivityGradebookMark(toolSession.getLesson(), learner, activity, mark, markedInGradebook, + updateGradebookUserActivityMark(toolSession.getLesson(), learner, activity, mark, markedInGradebook, false); updateUserActivityGradebookFeedback(activity, learner, feedback); } } } + */ @Override public void removeActivityMark(Integer userID, Long toolSessionID) { @@ -1963,7 +1956,7 @@ logger.debug( "Removing activity and lesson entries for learner ID " + learnerId + " and lesson ID " + lessonId); } - Lesson lesson = getLessonService().getLesson(lessonId); + Lesson lesson = lessonService.getLesson(lessonId); List activities = getLessonActivitiesForLearner(lesson, learnerId); for (ToolActivity activity : activities) { GradebookUserActivity gradebookUserActivity = getGradebookUserActivity(activity.getActivityId(), learnerId); @@ -1983,7 +1976,7 @@ logger.debug("Archiving activity and lesson entries for learner ID " + learnerId + " and lesson ID " + lessonId + " with archive date " + archiveDate); } - Lesson lesson = getLessonService().getLesson(lessonId); + Lesson lesson = lessonService.getLesson(lessonId); List activities = getLessonActivitiesForLearner(lesson, learnerId); for (ToolActivity activity : activities) { GradebookUserActivity gradebookUserActivity = getGradebookUserActivity(activity.getActivityId(), learnerId); @@ -2553,18 +2546,10 @@ this.logEventService = logEventService; } - public ILamsCoreToolService getToolService() { - return toolService; - } - public void setToolService(ILamsCoreToolService toolService) { this.toolService = toolService; } - public IGradebookDAO getGradebookDAO() { - return gradebookDAO; - } - public void setGradebookDAO(IGradebookDAO gradebookDAO) { this.gradebookDAO = gradebookDAO; } @@ -2577,34 +2562,14 @@ this.lessonDAO = lessonDAO; } - public ILessonService getLessonService() { - return lessonService; - } - public void setLessonService(ILessonService lessonService) { this.lessonService = lessonService; } - public IUserManagementService getUserService() { - return userService; - } - public void setUserService(IUserManagementService userService) { this.userService = userService; } - public IBaseDAO getBaseDAO() { - return baseDAO; - } - - public void setBaseDAO(IBaseDAO baseDAO) { - this.baseDAO = baseDAO; - } - - public IActivityDAO getActivityDAO() { - return activityDAO; - } - public void setActivityDAO(IActivityDAO activityDAO) { this.activityDAO = activityDAO; } @@ -2620,4 +2585,8 @@ public void setOutcomeService(IOutcomeService outcomeService) { this.outcomeService = outcomeService; } + + public void setIntegrationService(IIntegrationService integrationService) { + this.integrationService = integrationService; + } } \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java =================================================================== diff -u -r76466ac35a7c584f60f7f9f40098d320a83dca7a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision 76466ac35a7c584f60f7f9f40098d320a83dca7a) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -43,8 +43,8 @@ private static final String FIND_LAST_BY_ASSESSMENT_AND_USER = "FROM " + AssessmentResult.class.getName() + " AS r WHERE r.user.userId =:userId AND r.assessment.uid=:assessmentUid AND r.latest=1"; - private static final String FIND_WHETHER_LAST_RESULT_FINISHED = "SELECT (r.finishDate IS NOT NULL) FROM " + AssessmentResult.class.getName() - + " AS r WHERE r.user.userId =:userId AND r.assessment.uid=:assessmentUid AND r.latest=1"; + private static final String FIND_WHETHER_LAST_RESULT_FINISHED = "SELECT COUNT(*) > 0 FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.user.userId =:userId AND r.assessment.uid=:assessmentUid AND r.latest=1 AND r.finishDate != null"; private static final String FIND_BY_ASSESSMENT_AND_USER_AND_FINISHED = "FROM " + AssessmentResult.class.getName() + " AS r WHERE r.user.userId = ? AND r.assessment.uid=? AND (r.finishDate != null) ORDER BY r.startDate ASC"; Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r20aa6cbca9fc96d341080e6ad39f82593443f792 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 20aa6cbca9fc96d341080e6ad39f82593443f792) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -32,7 +32,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -42,14 +41,17 @@ import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; +import java.util.UUID; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.poi.ss.usermodel.IndexedColors; import org.lamsfoundation.lams.confidencelevel.ConfidenceLevelDTO; +import org.lamsfoundation.lams.confidencelevel.VsaAnswerDTO; import org.lamsfoundation.lams.contentrepository.client.IToolContentHandler; import org.lamsfoundation.lams.events.IEventNotificationService; import org.lamsfoundation.lams.learning.service.ILearnerService; @@ -60,6 +62,12 @@ import org.lamsfoundation.lams.notebook.model.NotebookEntry; import org.lamsfoundation.lams.notebook.service.CoreNotebookConstants; import org.lamsfoundation.lams.notebook.service.ICoreNotebookService; +import org.lamsfoundation.lams.qb.model.QbCollection; +import org.lamsfoundation.lams.qb.model.QbOption; +import org.lamsfoundation.lams.qb.model.QbQuestion; +import org.lamsfoundation.lams.qb.model.QbQuestionUnit; +import org.lamsfoundation.lams.qb.model.QbToolQuestion; +import org.lamsfoundation.lams.qb.service.IQbService; import org.lamsfoundation.lams.rest.RestTags; import org.lamsfoundation.lams.rest.ToolRestManager; import org.lamsfoundation.lams.tool.ToolCompletionStatus; @@ -88,25 +96,22 @@ import org.lamsfoundation.lams.tool.assessment.model.Assessment; import org.lamsfoundation.lams.tool.assessment.model.AssessmentOptionAnswer; import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestion; -import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestionOption; import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestionResult; import org.lamsfoundation.lams.tool.assessment.model.AssessmentResult; import org.lamsfoundation.lams.tool.assessment.model.AssessmentSession; -import org.lamsfoundation.lams.tool.assessment.model.AssessmentUnit; import org.lamsfoundation.lams.tool.assessment.model.AssessmentUser; import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; import org.lamsfoundation.lams.tool.assessment.util.AnswerIntComparator; import org.lamsfoundation.lams.tool.assessment.util.AssessmentEscapeUtils; -import org.lamsfoundation.lams.tool.assessment.util.AssessmentQuestionResultComparator; import org.lamsfoundation.lams.tool.assessment.util.AssessmentSessionComparator; import org.lamsfoundation.lams.tool.assessment.util.SequencableComparator; import org.lamsfoundation.lams.tool.exception.DataMissingException; import org.lamsfoundation.lams.tool.exception.ToolException; +import org.lamsfoundation.lams.tool.service.ICommonAssessmentService; import org.lamsfoundation.lams.tool.service.ILamsToolService; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; -import org.lamsfoundation.lams.util.HashUtil; import org.lamsfoundation.lams.util.JsonUtil; import org.lamsfoundation.lams.util.MessageService; import org.lamsfoundation.lams.util.NumberUtil; @@ -122,8 +127,8 @@ /** * @author Andrey Balan */ -public class AssessmentServiceImpl - implements IAssessmentService, ToolContentManager, ToolSessionManager, ToolRestManager { +public class AssessmentServiceImpl implements IAssessmentService, ICommonAssessmentService, ToolContentManager, + ToolSessionManager, ToolRestManager { private static Logger log = Logger.getLogger(AssessmentServiceImpl.class.getName()); private AssessmentDAO assessmentDao; @@ -161,6 +166,8 @@ private IEventNotificationService eventNotificationService; + private IQbService qbService; + // ******************************************************************************* // Service method // ******************************************************************************* @@ -207,6 +214,7 @@ @Override public void copyAnswersFromLeader(AssessmentUser user, AssessmentUser leader) { + if ((user == null) || (leader == null) || user.getUid().equals(leader.getUid())) { return; } @@ -230,7 +238,7 @@ Set userQuestionResults = userResult.getQuestionResults(); for (AssessmentQuestionResult leaderQuestionResult : leaderQuestionResults) { AssessmentQuestionResult userQuestionResult = new AssessmentQuestionResult(); - userQuestionResult.setAssessmentQuestion(leaderQuestionResult.getAssessmentQuestion()); + userQuestionResult.setQbToolQuestion(leaderQuestionResult.getQbToolQuestion()); userQuestionResult.setAssessmentResult(userResult); userQuestionResults.add(userQuestionResult); @@ -255,13 +263,13 @@ Set userQuestionResults = userResult.getQuestionResults(); for (AssessmentQuestionResult leaderQuestionResult : leaderQuestionResults) { for (AssessmentQuestionResult userQuestionResult : userQuestionResults) { - if (userQuestionResult.getAssessmentQuestion().getUid() - .equals(leaderQuestionResult.getAssessmentQuestion().getUid())) { + if (userQuestionResult.getQbToolQuestion().getUid() + .equals(leaderQuestionResult.getQbToolQuestion().getUid())) { - userQuestionResult.setAnswerString(leaderQuestionResult.getAnswerString()); + userQuestionResult.setAnswer(leaderQuestionResult.getAnswer()); userQuestionResult.setAnswerFloat(leaderQuestionResult.getAnswerFloat()); userQuestionResult.setAnswerBoolean(leaderQuestionResult.getAnswerBoolean()); - userQuestionResult.setSubmittedOptionUid(leaderQuestionResult.getSubmittedOptionUid()); + userQuestionResult.setQbOption(leaderQuestionResult.getQbOption()); userQuestionResult.setMark(leaderQuestionResult.getMark()); userQuestionResult.setMaxMark(leaderQuestionResult.getMaxMark()); userQuestionResult.setPenalty(leaderQuestionResult.getPenalty()); @@ -417,46 +425,26 @@ @Override public void saveOrUpdateAssessment(Assessment assessment) { - //update questions' hashes in case questions' titles or descriptions got changed - for (AssessmentQuestion question : (Set) assessment.getQuestions()) { - String newHash = question.getQuestion() == null ? null : HashUtil.sha1(question.getQuestion()); - question.setQuestionHash(newHash); + for (QuestionReference reference : assessment.getQuestionReferences()) { + assessmentQuestionDao.saveObject(reference); } + for (AssessmentQuestion question : assessment.getQuestions()) { + assessmentQuestionDao.saveObject(question); + } + //store object in DB assessmentDao.saveObject(assessment); } @Override public void updateAssessmentQuestion(AssessmentQuestion question) { - //update question's hash in case question's title or description got changed - String newHash = question.getQuestion() == null ? null : HashUtil.sha1(question.getQuestion()); - question.setQuestionHash(newHash); - - //store object in DB assessmentQuestionDao.update(question); } @Override public void releaseFromCache(Object object) { assessmentDao.releaseFromCache(object); - - if (object instanceof AssessmentQuestion) { - AssessmentQuestion question = (AssessmentQuestion) object; - for (AssessmentQuestionOption option : question.getOptions()) { - assessmentDao.releaseFromCache(option); - } - for (AssessmentUnit unit : question.getUnits()) { - assessmentDao.releaseFromCache(unit); - } - } - - if (object instanceof QuestionReference) { - QuestionReference reference = (QuestionReference) object; - if (reference.getQuestion() != null) { - assessmentDao.releaseFromCache(reference.getQuestion()); - } - } } @Override @@ -495,8 +483,18 @@ } @Override - public void setAttemptStarted(Assessment assessment, AssessmentUser assessmentUser, Long toolSessionId) { - Set questions = assessment.getQuestions(); + public void setAttemptStarted(Assessment assessment, AssessmentUser assessmentUser, Long toolSessionId, + List> pagedQuestionDtos) { + //create list of all questions that user is going to answer (it will exclude random questions that user not going to answer) + Set questions = new TreeSet<>(); + if (pagedQuestionDtos != null) { + for (Set questionsForOnePage : pagedQuestionDtos) { + for (QuestionDTO questionDto : questionsForOnePage) { + AssessmentQuestion question = assessmentQuestionDao.getByUid(questionDto.getUid()); + questions.add(question); + } + } + } AssessmentResult lastResult = getLastAssessmentResult(assessment.getUid(), assessmentUser.getUserId()); if (lastResult != null) { @@ -506,14 +504,13 @@ //check all required questionResults exist, it can be missing in case of random question - create new one then Set questionResults = lastResult.getQuestionResults(); - Set updatedQuestionResults = new TreeSet<>( - new AssessmentQuestionResultComparator()); + Set updatedQuestionResults = new TreeSet<>(); for (AssessmentQuestion question : questions) { // get questionResult from DB instance of AssessmentResult AssessmentQuestionResult questionResult = null; for (AssessmentQuestionResult questionResultIter : questionResults) { - if (question.getUid().equals(questionResultIter.getAssessmentQuestion().getUid())) { + if (question.getUid().equals(questionResultIter.getQbToolQuestion().getUid())) { questionResult = questionResultIter; } } @@ -557,20 +554,19 @@ */ private AssessmentQuestionResult createQuestionResultObject(AssessmentQuestion question) { AssessmentQuestionResult questionResult = new AssessmentQuestionResult(); - questionResult.setAssessmentQuestion(question); + questionResult.setQbToolQuestion(question); // create optionAnswer for each option Set optionAnswers = questionResult.getOptionAnswers(); - for (AssessmentQuestionOption option : question.getOptions()) { + for (QbOption option : question.getQbQuestion().getQbOptions()) { AssessmentOptionAnswer optionAnswer = new AssessmentOptionAnswer(); optionAnswer.setOptionUid(option.getUid()); optionAnswers.add(optionAnswer); } return questionResult; } - - + @Override public void storeSingleMarkHedgingQuestion(Assessment assessment, Long userId, List> pagedQuestions, Long singleMarkHedgingQuestionUid) @@ -592,10 +588,10 @@ } } } - + AssessmentQuestionResult questionResult = storeUserAnswer(result, questionDto); questionResult.setFinishDate(new Date()); - + float mark = 0; for (OptionDTO optionDto : questionDto.getOptionDtos()) { if (optionDto.isCorrect()) { @@ -606,9 +602,9 @@ } } questionResult.setMark(mark); - questionResult.setMaxMark((float) questionDto.getGrade()); + questionResult.setMaxMark((float) questionDto.getMaxMark()); assessmentResultDao.saveObject(questionResult); - + //for displaying purposes calculate mark and set it to questionDto questionDto.setMark(mark); } @@ -636,13 +632,13 @@ int maximumGrade = 0; float grade = 0; - //sum up user grade and max grade for all questions + //sum up user mark and max mark for all questions for (Set questionsForOnePage : pagedQuestions) { for (QuestionDTO questionDto : questionsForOnePage) { // get questionResult from DB instance of AssessmentResult AssessmentQuestionResult questionResult = null; for (AssessmentQuestionResult questionResultIter : result.getQuestionResults()) { - if (questionDto.getUid().equals(questionResultIter.getAssessmentQuestion().getUid())) { + if (questionDto.getUid().equals(questionResultIter.getQbToolQuestion().getUid())) { questionResult = questionResultIter; } } @@ -652,7 +648,7 @@ } grade += questionResult.getMark(); - maximumGrade += questionDto.getGrade(); + maximumGrade += questionDto.getMaxMark(); } } @@ -676,7 +672,7 @@ // get questionResult from DB instance of AssessmentResult AssessmentQuestionResult questionResult = null; for (AssessmentQuestionResult questionResultIter : assessmentResult.getQuestionResults()) { - if (questionDto.getUid().equals(questionResultIter.getAssessmentQuestion().getUid())) { + if (questionDto.getUid().equals(questionResultIter.getQbToolQuestion().getUid())) { questionResult = questionResultIter; } } @@ -691,7 +687,7 @@ // store question answer values questionResult.setAnswerBoolean(questionDto.getAnswerBoolean()); questionResult.setAnswerFloat(questionDto.getAnswerFloat()); - questionResult.setAnswerString(questionDto.getAnswerString()); + questionResult.setAnswer(questionDto.getAnswer()); int j = 0; for (OptionDTO optionDto : questionDto.getOptionDtos()) { @@ -707,7 +703,7 @@ // store option answer values optionAnswer.setAnswerBoolean(optionDto.getAnswerBoolean()); optionAnswer.setAnswerInt(optionDto.getAnswerInt()); - if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_ORDERING) { + if (questionDto.getType() == QbQuestion.TYPE_ORDERING) { optionAnswer.setAnswerInt(j++); } } @@ -716,10 +712,10 @@ if (assessment.isEnableConfidenceLevels()) { questionResult.setConfidenceLevel(questionDto.getConfidenceLevel()); } - + return questionResult; } - + /** * * @return grade that user scored by answering that question @@ -728,102 +724,107 @@ QuestionDTO questionDto) { //calculate both mark and maxMark float mark = 0; - float maxMark = questionDto.getGrade(); - if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) { + float maxMark = questionDto.getMaxMark(); + if (questionDto.getType() == QbQuestion.TYPE_MULTIPLE_CHOICE) { boolean isMarkNullified = false; - float totalGrade = 0; + float optionMaxMark = 0; for (OptionDTO optionDto : questionDto.getOptionDtos()) { if (optionDto.getAnswerBoolean()) { - totalGrade += optionDto.getGrade(); - mark += optionDto.getGrade() * maxMark; + optionMaxMark += optionDto.getMaxMark(); + mark += optionDto.getMaxMark() * maxMark; // if option of "incorrect answer nullifies mark" is ON check if selected answer has a zero grade // and if so nullify question's mark - if (questionDto.isIncorrectAnswerNullifiesMark() && (optionDto.getGrade() == 0)) { + if (questionDto.isIncorrectAnswerNullifiesMark() && (optionDto.getMaxMark() == 0)) { isMarkNullified = true; } } } - // set answerTotalGrade to let jsp know whether the question was answered correctly/partly/incorrectly even if mark=0 - questionDto.setAnswerTotalGrade(totalGrade); + // set optionMaxMark to let jsp know whether the question was answered correctly/partly/incorrectly even if mark=0 + questionDto.setOptionMaxMark(optionMaxMark); if (isMarkNullified) { mark = 0; } - } else if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS) { + } else if (questionDto.getType() == QbQuestion.TYPE_MATCHING_PAIRS) { float maxMarkForCorrectAnswer = maxMark / questionDto.getOptionDtos().size(); for (OptionDTO optionDto : questionDto.getOptionDtos()) { if (optionDto.getAnswerInt() == optionDto.getUid()) { mark += maxMarkForCorrectAnswer; } } - } else if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER) { + } else if (questionDto.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS) { //clear previous answer - questionResult.setSubmittedOptionUid(null); + questionResult.setQbOption(null); for (OptionDTO optionDto : questionDto.getOptionDtos()) { + String[] optionAnswers = optionDto.getName().strip().split("\\r\\n"); + boolean isAnswerMatchedCurrentOption = false; + for (String optionAnswer : optionAnswers) { + optionAnswer = optionAnswer.strip(); - //prepare regex which takes into account only * special character - String regexWithOnlyAsteriskSymbolActive = "\\Q"; - String optionString = optionDto.getOptionString().trim(); - for (int i = 0; i < optionString.length(); i++) { - //everything in between \\Q and \\E are taken literally no matter which characters it contains - if (optionString.charAt(i) == '*') { - regexWithOnlyAsteriskSymbolActive += "\\E.*\\Q"; + //prepare regex which takes into account only * special character + String regexWithOnlyAsteriskSymbolActive = "\\Q"; + for (int i = 0; i < optionAnswer.length(); i++) { + //everything in between \\Q and \\E are taken literally no matter which characters it contains + if (optionAnswer.charAt(i) == '*') { + regexWithOnlyAsteriskSymbolActive += "\\E.*\\Q"; + } else { + regexWithOnlyAsteriskSymbolActive += optionAnswer.charAt(i); + } + } + regexWithOnlyAsteriskSymbolActive += "\\E"; + + //check whether answer matches regex + Pattern pattern; + if (questionDto.isCaseSensitive()) { + pattern = Pattern.compile(regexWithOnlyAsteriskSymbolActive); } else { - regexWithOnlyAsteriskSymbolActive += optionString.charAt(i); + pattern = Pattern.compile(regexWithOnlyAsteriskSymbolActive, + java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.UNICODE_CASE); } + if (questionDto.getAnswer() != null && pattern.matcher(questionDto.getAnswer().strip()).matches()) { + isAnswerMatchedCurrentOption = true; + break; + } } - regexWithOnlyAsteriskSymbolActive += "\\E"; - //check whether answer matches regex - Pattern pattern; - if (questionDto.isCaseSensitive()) { - pattern = Pattern.compile(regexWithOnlyAsteriskSymbolActive); - } else { - pattern = Pattern.compile(regexWithOnlyAsteriskSymbolActive, - java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.UNICODE_CASE); - } - boolean isAnswerMatchedCurrentOption = (questionDto.getAnswerString() != null) - ? pattern.matcher(questionDto.getAnswerString().trim()).matches() - : false; - if (isAnswerMatchedCurrentOption) { - mark = optionDto.getGrade() * maxMark; - questionResult.setSubmittedOptionUid(optionDto.getUid()); + mark = optionDto.getMaxMark() * maxMark; + QbOption qbOption = qbService.getOptionByUid(optionDto.getUid()); + questionResult.setQbOption(qbOption); break; } } - } else if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_NUMERICAL) { - String answerString = questionDto.getAnswerString(); - if (answerString != null) { + } else if (questionDto.getType() == QbQuestion.TYPE_NUMERICAL) { + String answer = questionDto.getAnswer(); + if (answer != null) { for (OptionDTO optionDto : questionDto.getOptionDtos()) { boolean isAnswerMatchedCurrentOption = false; try { - float answerFloat = Float.valueOf(questionDto.getAnswerString()); - isAnswerMatchedCurrentOption = ((answerFloat >= (optionDto.getOptionFloat() + float answerFloat = Float.valueOf(questionDto.getAnswer()); + isAnswerMatchedCurrentOption = ((answerFloat >= (optionDto.getNumericalOption() - optionDto.getAcceptedError())) - && (answerFloat <= (optionDto.getOptionFloat() + optionDto.getAcceptedError()))); + && (answerFloat <= (optionDto.getNumericalOption() + optionDto.getAcceptedError()))); } catch (Exception e) { } if (!isAnswerMatchedCurrentOption) { - for (AssessmentUnit unit : questionDto.getUnits()) { - String regex = ".*" + unit.getUnit() + "$"; + for (QbQuestionUnit unit : questionDto.getUnits()) { + String regex = ".*" + unit.getName() + "$"; Pattern pattern = Pattern.compile(regex, java.util.regex.Pattern.CASE_INSENSITIVE | java.util.regex.Pattern.UNICODE_CASE); - if (pattern.matcher(answerString).matches()) { - String answerFloatStr = answerString.substring(0, - answerString.length() - unit.getUnit().length()); + if (pattern.matcher(answer).matches()) { + String answerFloatStr = answer.substring(0, answer.length() - unit.getName().length()); try { float answerFloat = Float.valueOf(answerFloatStr); answerFloat = answerFloat / unit.getMultiplier(); - isAnswerMatchedCurrentOption = ((answerFloat >= (optionDto.getOptionFloat() + isAnswerMatchedCurrentOption = ((answerFloat >= (optionDto.getNumericalOption() - optionDto.getAcceptedError())) - && (answerFloat <= (optionDto.getOptionFloat() + && (answerFloat <= (optionDto.getNumericalOption() + optionDto.getAcceptedError()))); if (isAnswerMatchedCurrentOption) { break; @@ -834,33 +835,34 @@ } } if (isAnswerMatchedCurrentOption) { - mark = optionDto.getGrade() * maxMark; - questionResult.setSubmittedOptionUid(optionDto.getUid()); + mark = optionDto.getMaxMark() * maxMark; + QbOption qbOption = qbService.getOptionByUid(optionDto.getUid()); + questionResult.setQbOption(qbOption); break; } } } - } else if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_TRUE_FALSE) { + } else if (questionDto.getType() == QbQuestion.TYPE_TRUE_FALSE) { if ((questionDto.getAnswerBoolean() == questionDto.getCorrectAnswer()) - && (questionDto.getAnswerString() != null)) { + && (questionDto.getAnswer() != null)) { mark = maxMark; } - } else if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_ORDERING) { + } else if (questionDto.getType() == QbQuestion.TYPE_ORDERING) { float maxMarkForCorrectAnswer = maxMark / questionDto.getOptionDtos().size(); - TreeSet correctOptionSet = new TreeSet<>(new SequencableComparator()); - Set originalOptions = questionResult.getAssessmentQuestion().getQuestionDTO().getOptionDtos(); - correctOptionSet.addAll(originalOptions); - ArrayList correctOptionList = new ArrayList<>(correctOptionSet); + + Set originalOptions = (new QuestionDTO(questionResult.getQbToolQuestion())).getOptionDtos(); + ArrayList optionsInCorrectOrder = new ArrayList<>(originalOptions); + int i = 0; for (OptionDTO optionDto : questionDto.getOptionDtos()) { - if (optionDto.getUid().equals(correctOptionList.get(i++).getUid())) { + if (optionDto.getUid().equals(optionsInCorrectOrder.get(i++).getUid())) { mark += maxMarkForCorrectAnswer; } } - } else if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING) { + } else if (questionDto.getType() == QbQuestion.TYPE_MARK_HEDGING) { for (OptionDTO optionDto : questionDto.getOptionDtos()) { if (optionDto.isCorrect()) { //if hedgingMark is a default '-1', change it to '0' @@ -870,21 +872,21 @@ } } } - + //total mark can't be more than maxMark if (mark > maxMark) { mark = maxMark; - // in case options have negative grades (<0), their total mark can't be less than -maxMark + // in case options have negative marks (<0), their total mark can't be less than -maxMark } else if (mark < -maxMark) { mark = -maxMark; } // calculate penalty if (mark > 0) { // calculate number of wrong answers - int numberWrongAnswers = assessmentQuestionResultDao.getNumberWrongAnswersDoneBefore(assessmentUid, - userId, questionDto.getUid()); + int numberWrongAnswers = assessmentQuestionResultDao.getNumberWrongAnswersDoneBefore(assessmentUid, userId, + questionDto.getUid()); // calculate penalty itself float penalty = questionDto.getPenaltyFactor() * numberWrongAnswers; @@ -903,7 +905,7 @@ questionResult.setMark(mark); questionResult.setMaxMark(maxMark); } - + @Override public void loadupLastAttempt(Long assessmentUid, Long userId, List> pagedQuestionDtos) { //get the latest result (it can be unfinished one) @@ -924,28 +926,28 @@ //load last finished results for hedging type of questions (in order to prevent retry) Set questionResults = lastResult.getQuestionResults(); - if ((questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING) - && (lastResult.getFinishDate() == null) && (lastFinishedResult != null)) { + if ((questionDto.getType() == QbQuestion.TYPE_MARK_HEDGING) && (lastResult.getFinishDate() == null) + && (lastFinishedResult != null)) { questionResults = lastFinishedResult.getQuestionResults(); } for (AssessmentQuestionResult questionResult : questionResults) { - if (questionDto.getUid().equals(questionResult.getAssessmentQuestion().getUid())) { + if (questionDto.getUid().equals(questionResult.getQbToolQuestion().getUid())) { loadupQuestionResultIntoQuestionDto(questionDto, questionResult); break; } } } } } - + /** * Loads up all information from questionResult into questionDto. */ private void loadupQuestionResultIntoQuestionDto(QuestionDTO questionDto, AssessmentQuestionResult questionResult) { questionDto.setAnswerBoolean(questionResult.getAnswerBoolean()); questionDto.setAnswerFloat(questionResult.getAnswerFloat()); - questionDto.setAnswerString(questionResult.getAnswerString()); + questionDto.setAnswer(questionResult.getAnswer()); questionDto.setMark(questionResult.getMark()); questionDto.setResponseSubmitted(questionResult.getFinishDate() != null); questionDto.setPenalty(questionResult.getPenalty()); @@ -963,7 +965,7 @@ } //sort ordering type of question in order to show how learner has sorted them - if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_ORDERING) { + if (questionDto.getType() == QbQuestion.TYPE_ORDERING) { //don't sort ordering type of questions that haven't been submitted to not break their shuffled order boolean isOptionAnswersNeverSubmitted = true; @@ -980,15 +982,15 @@ } } - // set answerTotalGrade to let jsp know whether the question was answered correctly/partly/incorrectly even if mark=0 - if (questionDto.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) { - float totalGrade = 0; + // set optionMaxMark to let jsp know whether the question was answered correctly/partly/incorrectly even if mark=0 + if (questionDto.getType() == QbQuestion.TYPE_MULTIPLE_CHOICE) { + float optionMaxMark = 0; for (OptionDTO optionDto : questionDto.getOptionDtos()) { if (optionDto.getAnswerBoolean()) { - totalGrade += optionDto.getGrade(); + optionMaxMark += optionDto.getMaxMark(); } } - questionDto.setAnswerTotalGrade(totalGrade); + questionDto.setOptionMaxMark(optionMaxMark); } } @@ -1054,8 +1056,8 @@ } @Override - public int countAttemptsPerOption(Long optionUid) { - return assessmentResultDao.countAttemptsPerOption(optionUid); + public int countAttemptsPerOption(Long toolContentId, Long optionUid) { + return assessmentResultDao.countAttemptsPerOption(toolContentId, optionUid); } @Override @@ -1078,7 +1080,7 @@ public int getAssessmentResultCount(Long assessmentUid, Long userId) { return assessmentResultDao.getAssessmentResultCount(assessmentUid, userId); } - + @Override public boolean isAssessmentAttempted(Long assessmentUid) { return assessmentResultDao.isAssessmentAttempted(assessmentUid); @@ -1095,8 +1097,8 @@ } @Override - public Float getQuestionResultMark(Long assessmentUid, Long userId, int questionSequenceId) { - return assessmentQuestionResultDao.getQuestionResultMark(assessmentUid, userId, questionSequenceId); + public Float getQuestionResultMark(Long assessmentUid, Long userId, int questionDisplayOrder) { + return assessmentQuestionResultDao.getQuestionResultMark(assessmentUid, userId, questionDisplayOrder); } @Override @@ -1163,7 +1165,7 @@ //finish non-leader sessionUser.setSessionFinished(true); assessmentUserDao.saveObject(user); - + //copy answers from leader to non-leaders copyAnswersFromLeader(sessionUser, session.getGroupLeader()); }); @@ -1252,8 +1254,7 @@ Set questionResults = lastFinishedResult.getQuestionResults(); //prepare list of the questions to display in user master detail table, filtering out questions that aren't supposed to be answered - SortedSet questionResultsToDisplay = new TreeSet<>( - new AssessmentQuestionResultComparator()); + SortedSet questionResultsToDisplay = new TreeSet<>(); //in case there is at least one random question - we need to show all questions if (assessment.hasRandomQuestion()) { questionResultsToDisplay.addAll(questionResults); @@ -1262,7 +1263,7 @@ } else { for (QuestionReference reference : questionReferences) { for (AssessmentQuestionResult questionResult : questionResults) { - if (reference.getQuestion().getUid().equals(questionResult.getAssessmentQuestion().getUid())) { + if (reference.getQuestion().getUid().equals(questionResult.getQbToolQuestion().getUid())) { questionResultsToDisplay.add(questionResult); } } @@ -1307,7 +1308,7 @@ //otherwise show only questions from the question list } else { - for (QuestionReference reference : (Set) assessment.getQuestionReferences()) { + for (QuestionReference reference : assessment.getQuestionReferences()) { questions.add(reference.getQuestion()); } } @@ -1321,7 +1322,7 @@ List questionResults = new ArrayList<>(); for (AssessmentResult result : results) { for (AssessmentQuestionResult questionResult : result.getQuestionResults()) { - if (question.getUid().equals(questionResult.getAssessmentQuestion().getUid())) { + if (question.getUid().equals(questionResult.getQbToolQuestion().getUid())) { // for displaying purposes only (no saving occurs) questionResult.setFinishDate(result.getFinishDate()); @@ -1344,14 +1345,178 @@ @Override public QuestionSummary getQuestionSummary(Long contentId, Long questionUid) { - QuestionSummary questionSummary = new QuestionSummary(); AssessmentQuestion question = assessmentQuestionDao.getByUid(questionUid); - questionSummary.setQuestion(question); + QbQuestion qbQuestion = question.getQbQuestion(); + List allQuestionResults = assessmentQuestionResultDao + .getQuestionResultsByQuestionUid(questionUid); + QuestionSummary questionSummary = new QuestionSummary(question); + + //prepare extra data for VSA type of questions, so teachers can allocate answers into groups + if (question.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS) { + //find all questionResults that are not allocated into groups yet + List notAllocatedQuestionResults = new ArrayList<>(); + for (AssessmentQuestionResult questionResult : allQuestionResults) { + String answer = questionResult.getAnswer(); + + boolean isAnswerAllocated = false; + for (QbOption option : qbQuestion.getQbOptions()) { + String[] alternatives = option.getName().split("\r\n"); + for (String alternative : alternatives) { + if (answer != null && alternative.equals(answer)) { + isAnswerAllocated = true; + break; + } + } + } + + if (!isAnswerAllocated) { + notAllocatedQuestionResults.add(questionResult); + } + } + questionSummary.setNotAllocatedQuestionResults(notAllocatedQuestionResults); + + //check is it a TBL case, i.e. only two option groups available, one has 0%, second - 100% + boolean isCompatibleWithTbl = qbQuestion.isVsaAndCompatibleWithTbl(); + questionSummary.setTbl(isCompatibleWithTbl); + } + return questionSummary; } @Override + public void allocateAnswerToOption(Long questionUid, Long targetOptionUid, Long previousOptionUid, + Long questionResultUid) { + AssessmentQuestion assessmentQuestion = assessmentQuestionDao.getByUid(questionUid); + QbQuestion qbQuestion = assessmentQuestion.getQbQuestion(); + AssessmentQuestionResult questionRes = assessmentQuestionResultDao + .getAssessmentQuestionResultByUid(questionResultUid); + String answer = questionRes.getAnswer(); + + //adding + if (previousOptionUid.equals(-1L)) { + for (QbOption targetOption : qbQuestion.getQbOptions()) { + if (targetOption.getUid().equals(targetOptionUid)) { + String name = targetOption.getName(); + name += "\r\n" + answer; + targetOption.setName(name); + assessmentDao.saveObject(targetOption); + break; + } + } + + } + //removing + else if (targetOptionUid.equals(-1L)) { + for (QbOption previousOption : qbQuestion.getQbOptions()) { + if (previousOption.getUid().equals(previousOptionUid)) { + String name = previousOption.getName(); + String[] alternatives = name.split("\r\n"); + + String nameWithoutUserAnswer = ""; + for (String alternative : alternatives) { + if (!alternative.equals(answer)) { + nameWithoutUserAnswer += alternative + "\r\n"; + } + } + if (nameWithoutUserAnswer.length() > 2) { + previousOption.setName(nameWithoutUserAnswer.substring(0, nameWithoutUserAnswer.length() - 2)); + assessmentDao.saveObject(previousOption); + } + break; + } + } + + } + //reshuffling inside the same container - do nothing + else if (targetOptionUid.equals(previousOptionUid)) { + + } + //moving from one to another + else { + for (QbOption targetOption : qbQuestion.getQbOptions()) { + if (targetOption.getUid().equals(targetOptionUid)) { + String name = targetOption.getName(); + name += "\r\n" + answer; + targetOption.setName(name); + assessmentDao.saveObject(targetOption); + break; + } + } + + for (QbOption previousOption : qbQuestion.getQbOptions()) { + if (previousOption.getUid().equals(previousOptionUid)) { + String name = previousOption.getName(); + String[] alternatives = name.split("\r\n"); + + String nameWithoutUserAnswer = ""; + for (String alternative : alternatives) { + if (!alternative.equals(answer)) { + nameWithoutUserAnswer += alternative + "\r\n"; + } + } + if (nameWithoutUserAnswer.length() > 2) { + nameWithoutUserAnswer = nameWithoutUserAnswer.substring(0, nameWithoutUserAnswer.length() - 2); + } + previousOption.setName(nameWithoutUserAnswer); + assessmentDao.saveObject(previousOption); + break; + } + } + } + assessmentResultDao.flush(); + + //recalculate marks for all lessons in all cases except for reshuffling inside the same container + if (!targetOptionUid.equals(previousOptionUid)) { + + // get all finished user results + List assessmentResults = assessmentResultDao + .getAssessmentResultsByQbQuestion(qbQuestion.getUid()); + + //stores userId->lastFinishedAssessmentResult + Map lastFinishedAssessmentResults = new LinkedHashMap<>(); + for (AssessmentResult assessmentResult : assessmentResults) { + Long userId = assessmentResult.getUser().getUserId(); + lastFinishedAssessmentResults.put(userId, assessmentResult); + } + + for (AssessmentResult assessmentResult : assessmentResults) { + AssessmentUser user = assessmentResult.getUser(); + float assessmentMark = assessmentResult.getGrade(); + int assessmentMaxMark = assessmentResult.getMaximumGrade(); + + for (AssessmentQuestionResult questionResult : assessmentResult.getQuestionResults()) { + if (questionResult.getQbQuestion().getUid().equals(qbQuestion.getUid())) { + Float oldQuestionAnswerMark = questionResult.getMark(); + int oldResultMaxMark = questionResult.getMaxMark() == null ? 0 + : questionResult.getMaxMark().intValue(); + + //actually recalculate marks + QuestionDTO questionDto = new QuestionDTO(assessmentQuestion); + questionDto.setMaxMark(oldResultMaxMark); + loadupQuestionResultIntoQuestionDto(questionDto, questionResult); + calculateAnswerMark(assessmentResult.getAssessment().getUid(), user.getUserId(), questionResult, + questionDto); + assessmentQuestionResultDao.saveObject(questionResult); + + float newQuestionAnswerMark = questionResult.getMark(); + assessmentMark += newQuestionAnswerMark - oldQuestionAnswerMark; + break; + } + } + + // store new mark and maxMark if they were changed + AssessmentResult lastFinishedAssessmentResult = lastFinishedAssessmentResults.get(user.getUserId()); + storeAssessmentResultMarkAndMaxMark(assessmentResult, lastFinishedAssessmentResult, assessmentMark, + assessmentMaxMark, user); + } + + //recalculate marks in all Scratchie activities, that use modified QbQuestion + toolService.recalculateScratchieMarksForVsaQuestion(qbQuestion.getUid()); + } + } + + @Override public Map getQuestionSummaryForExport(Assessment assessment) { Map questionSummaries = new LinkedHashMap<>(); @@ -1388,10 +1553,9 @@ sessionIdToUsersMap.put(sessionId, users); } - for (AssessmentQuestion question : (Set) assessment.getQuestions()) { + for (AssessmentQuestion question : assessment.getQuestions()) { Long questionUid = question.getUid(); - QuestionSummary questionSummary = new QuestionSummary(); - questionSummary.setQuestion(question); + QuestionSummary questionSummary = new QuestionSummary(question); List> questionResults = new ArrayList<>(); @@ -1406,10 +1570,10 @@ AssessmentQuestionResult questionResult = null; if (assessmentResult == null) { questionResult = new AssessmentQuestionResult(); - questionResult.setAssessmentQuestion(question); + questionResult.setQbToolQuestion(question); } else { for (AssessmentQuestionResult dbQuestionResult : assessmentResult.getQuestionResults()) { - if (dbQuestionResult.getAssessmentQuestion().getUid().equals(questionUid)) { + if (dbQuestionResult.getQbToolQuestion().getUid().equals(questionUid)) { questionResult = dbQuestionResult; break; } @@ -1447,9 +1611,8 @@ } @Override - public List exportSummary(Assessment assessment, List sessionDtos, - boolean showUserNames) { - List sheets = new LinkedList(); + public List exportSummary(Assessment assessment, List sessionDtos, boolean showUserNames) { + List sheets = new LinkedList<>(); // -------------- First tab: Summary ---------------------------------------------------- if (showUserNames) { @@ -1519,16 +1682,16 @@ ExcelRow minMaxRow = summarySheet.initRow(); minMaxRow.addCell(getMessage("label.number.learners"), true); minMaxRow.addCell(totalNumEntries); - + minMaxRow = summarySheet.initRow(); minMaxRow.addCell(getMessage("label.lowest.mark"), true); minMaxRow.addCell((double) minGrade); - + minMaxRow = summarySheet.initRow(); minMaxRow.addCell(getMessage("label.highest.mark"), true); minMaxRow.addCell((double) maxGrade); summarySheet.addEmptyRow(); - + ExcelRow binSummaryRow = summarySheet.initRow(); binSummaryRow.addCell(getMessage("label.authoring.basic.list.header.mark"), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); @@ -1587,10 +1750,9 @@ ExcelCell.BORDER_STYLE_BOTTOM_THIN); questionTitleRow.addCell(getMessage("label.authoring.basic.penalty.factor"), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - questionTitleRow.addCell(getMessage("label.monitoring.question.summary.default.mark"), - true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - questionTitleRow.addCell(getMessage("label.export.user.id"), true, + questionTitleRow.addCell(getMessage("label.monitoring.question.summary.default.mark"), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); + questionTitleRow.addCell(getMessage("label.export.user.id"), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); if (showUserNames) { questionTitleRow.addCell(getMessage("label.monitoring.user.summary.user.name"), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); @@ -1601,23 +1763,21 @@ ExcelCell.BORDER_STYLE_BOTTOM_THIN); questionTitleRow.addCell(getMessage("label.authoring.basic.option.answer"), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - questionTitleRow.addCell(getMessage("label.export.time.taken"), true, - ExcelCell.BORDER_STYLE_BOTTOM_THIN); - questionTitleRow.addCell(getMessage("label.export.mark"), true, - ExcelCell.BORDER_STYLE_BOTTOM_THIN); + questionTitleRow.addCell(getMessage("label.export.time.taken"), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); + questionTitleRow.addCell(getMessage("label.export.mark"), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); int questionNumber = 1; for (AssessmentQuestion question : questions) { ExcelRow questionTitle = questionSummarySheet.initRow(); - questionTitle.addCell( - getMessage("label.monitoring.question.summary.question") + " " + questionNumber++, true); + questionTitle.addCell(getMessage("label.monitoring.question.summary.question") + " " + questionNumber++, + true); // set up the summary table data for the top of the question area. - boolean doSummaryTable = question.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE - || question.getType() == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER - || question.getType() == AssessmentConstants.QUESTION_TYPE_NUMERICAL - || question.getType() == AssessmentConstants.QUESTION_TYPE_TRUE_FALSE; + boolean doSummaryTable = question.getType() == QbQuestion.TYPE_MULTIPLE_CHOICE + || question.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS + || question.getType() == QbQuestion.TYPE_NUMERICAL + || question.getType() == QbQuestion.TYPE_TRUE_FALSE; // For MC, Numeric & Short Answer Key is optionUid, Value is number of answers // For True/False Key 0 is false and Key 1 is true Map summaryOfAnswers = new HashMap<>(); @@ -1632,28 +1792,23 @@ ArrayList questionSummaryTabTemp = new ArrayList<>(); //add question title row - if (question.getType() == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING) { - Set options = question.getOptions(); + if (question.getType() == QbQuestion.TYPE_MARK_HEDGING) { + List options = question.getQbQuestion().getQbOptions(); // question row title ExcelRow hedgeQuestionTitleRow = new ExcelRow(); - hedgeQuestionTitleRow.addCell( - getMessage("label.monitoring.question.summary.question"), true); - hedgeQuestionTitleRow.addCell(getMessage("label.authoring.basic.list.header.type"), - true); - hedgeQuestionTitleRow.addCell(getMessage("label.authoring.basic.penalty.factor"), - true); - hedgeQuestionTitleRow.addCell( - getMessage("label.monitoring.question.summary.default.mark"), true); + hedgeQuestionTitleRow.addCell(getMessage("label.monitoring.question.summary.question"), true); + hedgeQuestionTitleRow.addCell(getMessage("label.authoring.basic.list.header.type"), true); + hedgeQuestionTitleRow.addCell(getMessage("label.authoring.basic.penalty.factor"), true); + hedgeQuestionTitleRow.addCell(getMessage("label.monitoring.question.summary.default.mark"), true); hedgeQuestionTitleRow.addCell(getMessage("label.export.user.id"), true); if (showUserNames) { - hedgeQuestionTitleRow.addCell( - getMessage("label.monitoring.user.summary.user.name"), true); + hedgeQuestionTitleRow.addCell(getMessage("label.monitoring.user.summary.user.name"), true); } hedgeQuestionTitleRow.addCell(getMessage("label.export.date.attempted"), true); hedgeQuestionTitleRow.addCell(getMessage("label.export.time.attempted"), true); - for (AssessmentQuestionOption option : options) { - ExcelCell cell = hedgeQuestionTitleRow.addCell( - option.getOptionString().replaceAll("\\<.*?\\>", ""), true); + for (QbOption option : options) { + ExcelCell cell = hedgeQuestionTitleRow.addCell(option.getName().replaceAll("\\<.*?\\>", ""), + true); if (option.isCorrect()) { cell.setColor(IndexedColors.GREEN); } @@ -1676,11 +1831,11 @@ for (List resultList : allResultsForQuestion) { for (AssessmentQuestionResult questionResult : resultList) { ExcelRow userResultRow = new ExcelRow(); - userResultRow.addCell(questionResult.getAssessmentQuestion().getTitle()); - userResultRow.addCell( - getQuestionTypeLanguageLabel(questionResult.getAssessmentQuestion().getType())); - userResultRow.addCell( - Float.valueOf(questionResult.getAssessmentQuestion().getPenaltyFactor())); + userResultRow.addCell(questionResult.getQbQuestion().getName()); + userResultRow.addCell(AssessmentServiceImpl + .getQuestionTypeLanguageLabel(questionResult.getQbQuestion().getType())); + userResultRow.addCell(Float.valueOf(questionResult.getQbQuestion().getPenaltyFactor())); + Float maxMark = (questionResult.getMaxMark() == null) ? 0 : Float.valueOf(questionResult.getMaxMark()); userResultRow.addCell(maxMark); @@ -1690,17 +1845,17 @@ } else { userResultRow.addCell(questionResult.getUser().getUserId()); } - + //date and time ExcelCell dateCell = userResultRow.addCell(questionResult.getFinishDate()); dateCell.setDataFormat(ExcelCell.CELL_FORMAT_DATE); ExcelCell timeCell = userResultRow.addCell(questionResult.getFinishDate()); timeCell.setDataFormat(ExcelCell.CELL_FORMAT_TIME); //answer - if (question.getType() == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING) { + if (question.getType() == QbQuestion.TYPE_MARK_HEDGING) { Set optionAnswers = questionResult.getOptionAnswers(); - for (AssessmentQuestionOption option : question.getOptions()) { + for (QbOption option : question.getQbQuestion().getQbOptions()) { for (AssessmentOptionAnswer optionAnswer : optionAnswers) { if (option.getUid().equals(optionAnswer.getOptionUid())) { userResultRow.addCell(optionAnswer.getAnswerInt()); @@ -1807,9 +1962,9 @@ } else { AssessmentQuestion question = questionReference.getQuestion(); - title = question.getTitle(); - questionType = getQuestionTypeLanguageLabel(question.getType()); - penaltyFactor = question.getPenaltyFactor(); + title = question.getQbQuestion().getName(); + questionType = AssessmentServiceImpl.getQuestionTypeLanguageLabel(question.getType()); + penaltyFactor = question.getQbQuestion().getPenaltyFactor(); QuestionSummary questionSummary = questionSummaries.get(question.getUid()); if (questionSummary != null) { @@ -1818,7 +1973,7 @@ } } - int maxGrade = questionReference.getDefaultGrade(); + int maxGrade = questionReference.getMaxMark(); totalGradesPossible += maxGrade; ExcelRow questCellRow = userSummarySheet.initRow(); @@ -1860,20 +2015,17 @@ if (showUserNames) { ExcelRow userTitleRow = userSummarySheet.initRow(); userTitleRow.addCell(getMessage("label.export.user.id"), true); - userTitleRow.addCell(getMessage("label.monitoring.user.summary.user.name"), - true); + userTitleRow.addCell(getMessage("label.monitoring.user.summary.user.name"), true); userTitleRow.addCell(getMessage("label.export.date.attempted"), true); - userTitleRow.addCell(getMessage("label.monitoring.question.summary.question"), - true); + userTitleRow.addCell(getMessage("label.monitoring.question.summary.question"), true); userTitleRow.addCell(getMessage("label.authoring.basic.option.answer"), true); userTitleRow.addCell(getMessage("label.export.mark"), true); } else { ExcelRow userTitleRow = userSummarySheet.initRow(); userTitleRow.addCell(getMessage("label.export.user.id"), true); userTitleRow.addCell(getMessage("label.export.date.attempted"), true); - userTitleRow.addCell(getMessage("label.monitoring.question.summary.question"), - true); + userTitleRow.addCell(getMessage("label.monitoring.question.summary.question"), true); userTitleRow.addCell(getMessage("label.authoring.basic.option.answer"), true); userTitleRow.addCell(getMessage("label.export.mark"), true); } @@ -1890,8 +2042,9 @@ } else { userResultRow.addCell(assessmentUser.getUserId()); } + userResultRow.addCell(assessmentResult.getStartDate()); - userResultRow.addCell(questionResult.getAssessmentQuestion().getTitle()); + userResultRow.addCell(questionResult.getQbQuestion().getName()); userResultRow.addCell( AssessmentEscapeUtils.printResponsesForExcelExport(questionResult)); userResultRow.addCell(questionResult.getMark()); @@ -1912,27 +2065,29 @@ return sheets; } - private ExcelRow startSummaryTable(AssessmentQuestion question, Map summaryOfAnswers, - Long trueKey, Long falseKey) { + private ExcelRow startSummaryTable(AssessmentQuestion question, Map summaryOfAnswers, Long trueKey, + Long falseKey) { ExcelRow summaryTableRow; int i = 0; - if (question.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE - || question.getType() == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER - || question.getType() == AssessmentConstants.QUESTION_TYPE_NUMERICAL) { + if (question.getType() == QbQuestion.TYPE_MULTIPLE_CHOICE + || question.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS + || question.getType() == QbQuestion.TYPE_NUMERICAL) { + summaryTableRow = new ExcelRow(); - for (AssessmentQuestionOption option : question.getOptions()) { + List options = question.getQbQuestion().getQbOptions(); + for (QbOption option : options) { summaryOfAnswers.put(option.getUid(), 0); StringBuilder bldr = new StringBuilder(getMessage("label.authoring.basic.option.answer")).append(" ") .append(i + 1).append(" - "); - if (question.getType() == AssessmentConstants.QUESTION_TYPE_NUMERICAL) { - bldr.append(option.getOptionFloat()).append(" +- ").append(option.getAcceptedError()); + if (question.getType() == QbQuestion.TYPE_NUMERICAL) { + bldr.append(option.getNumericalOption()).append(" +- ").append(option.getAcceptedError()); } else { - bldr.append(option.getOptionString().replaceAll("\\<.*?\\>", "")); + bldr.append(option.getName().replaceAll("\\<.*?\\>", "")); } summaryTableRow.addCell(bldr.toString()); i++; } - if (question.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) { + if (question.getType() == QbQuestion.TYPE_MULTIPLE_CHOICE) { summaryTableRow.addCell(getMessage("label.not.answered")); } else { summaryTableRow.addCell(getMessage("label.other")); @@ -1950,7 +2105,7 @@ private Integer updateSummaryCounts(AssessmentQuestion question, AssessmentQuestionResult questionResult, Map summaryOfAnswers, Integer summaryNACount) { - if (question.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) { + if (question.getType() == QbQuestion.TYPE_MULTIPLE_CHOICE) { boolean foundOption = false; Set optionAnswers = questionResult.getOptionAnswers(); if (optionAnswers != null) { @@ -1972,24 +2127,25 @@ if (!foundOption) { summaryNACount++; } - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER - || question.getType() == AssessmentConstants.QUESTION_TYPE_NUMERICAL) { - Long submittedUid = questionResult.getSubmittedOptionUid(); - if (submittedUid != null) { - Integer currentCount = summaryOfAnswers.get(submittedUid); + } else if (question.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS + || question.getType() == QbQuestion.TYPE_NUMERICAL) { + Long submittedOptionUid = questionResult.getQbOption() == null ? null + : questionResult.getQbOption().getUid(); + if (submittedOptionUid != null) { + Integer currentCount = summaryOfAnswers.get(submittedOptionUid); if (currentCount == null) { log.error( "Assessment Export: Unable to count answer in summary, refers to an unexpected option. QuestionResult " - + questionResult.getUid() + " submittedOptionUid " + submittedUid + " question " + + questionResult.getUid() + " chosen optionUid " + submittedOptionUid + " question " + question.getUid()); } else { - summaryOfAnswers.put(submittedUid, currentCount + 1); + summaryOfAnswers.put(submittedOptionUid, currentCount + 1); } } else { summaryNACount++; } - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_TRUE_FALSE) { - if (questionResult.getAnswerString() == null) { + } else if (question.getType() == QbQuestion.TYPE_TRUE_FALSE) { + if (questionResult.getAnswer() == null) { summaryNACount++; } else { long key = questionResult.getAnswerBoolean() ? 1 : 0; @@ -2007,57 +2163,56 @@ for (int value : summaryOfAnswers.values()) { total += value; } - if (question.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE - || question.getType() == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER - || question.getType() == AssessmentConstants.QUESTION_TYPE_NUMERICAL) { - for (AssessmentQuestionOption option : question.getOptions()) { + if (question.getType() == QbQuestion.TYPE_MULTIPLE_CHOICE + || question.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS + || question.getType() == QbQuestion.TYPE_NUMERICAL) { + for (QbOption option : question.getQbQuestion().getQbOptions()) { Double optionPercentage = (double) summaryOfAnswers.get(option.getUid()) / total; ExcelCell optionCell = summaryTableRow.addPercentageCell(optionPercentage); - if (option.getGrade() > 0) { + if (option.getMaxMark() > 0) { optionCell.setColor(IndexedColors.GREEN); } } - + } else { Double correctPercentage = (double) summaryOfAnswers.get(trueKey) / total; ExcelCell correctCell = summaryTableRow.addPercentageCell(correctPercentage); - + Double wrongPercentage = (double) summaryOfAnswers.get(falseKey) / total; ExcelCell wrongCell = summaryTableRow.addPercentageCell(wrongPercentage); - - if (question.getCorrectAnswer()) { + + if (question.getQbQuestion().getCorrectAnswer()) { correctCell.setColor(IndexedColors.GREEN); } else { wrongCell.setColor(IndexedColors.GREEN); } } - Double summaryNAPercentage = (double) summaryNACount / total; summaryTableRow.addPercentageCell(summaryNAPercentage); - + return summaryTableRow; } /** * Used only for excell export (for getUserSummaryData() method). */ - private String getQuestionTypeLanguageLabel(short type) { + public static String getQuestionTypeLanguageLabel(int type) { switch (type) { - case AssessmentConstants.QUESTION_TYPE_ESSAY: + case QbQuestion.TYPE_ESSAY: return "Essay"; - case AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS: + case QbQuestion.TYPE_MATCHING_PAIRS: return "Matching Pairs"; - case AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE: + case QbQuestion.TYPE_MULTIPLE_CHOICE: return "Multiple Choice"; - case AssessmentConstants.QUESTION_TYPE_NUMERICAL: + case QbQuestion.TYPE_NUMERICAL: return "Numerical"; - case AssessmentConstants.QUESTION_TYPE_ORDERING: + case QbQuestion.TYPE_ORDERING: return "Ordering"; - case AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER: + case QbQuestion.TYPE_VERY_SHORT_ANSWERS: return "Short Answer"; - case AssessmentConstants.QUESTION_TYPE_TRUE_FALSE: + case QbQuestion.TYPE_TRUE_FALSE: return "True/False"; - case AssessmentConstants.QUESTION_TYPE_MARK_HEDGING: + case QbQuestion.TYPE_MARK_HEDGING: return "Mark Hedging"; default: return null; @@ -2066,15 +2221,15 @@ @Override public void changeQuestionResultMark(Long questionResultUid, float newMark) { - AssessmentQuestionResult questionAnswer = assessmentQuestionResultDao + AssessmentQuestionResult questionResult = assessmentQuestionResultDao .getAssessmentQuestionResultByUid(questionResultUid); - float oldMark = questionAnswer.getMark(); - AssessmentResult assessmentResult = questionAnswer.getAssessmentResult(); - float totalMark = (assessmentResult.getGrade() - oldMark) + newMark; + float oldMark = questionResult.getMark(); + AssessmentResult assessmentResult = questionResult.getAssessmentResult(); + float assessmentMark = (assessmentResult.getGrade() - oldMark) + newMark; Long toolSessionId = assessmentResult.getSessionId(); Assessment assessment = assessmentResult.getAssessment(); - Long questionUid = questionAnswer.getAssessmentQuestion().getUid(); + Long questionUid = questionResult.getQbToolQuestion().getUid(); // When changing a mark for user and isUseSelectLeaderToolOuput is true, the mark should be propagated to all // students within the group @@ -2106,15 +2261,16 @@ assessmentQuestionResultDao.saveObject(lastAssessmentQuestionResult); AssessmentResult result = lastAssessmentQuestionResult.getAssessmentResult(); - result.setGrade(totalMark); + result.setGrade(assessmentMark); assessmentResultDao.saveObject(result); // propagade changes to Gradebook - toolService.updateActivityMark(Double.valueOf(totalMark), null, userId.intValue(), toolSessionId, false); + toolService.updateActivityMark(Double.valueOf(assessmentMark), null, userId.intValue(), toolSessionId, + false); // records mark change with audit service logEventService.logMarkChange(userId, user.getLoginName(), assessment.getContentId(), "" + oldMark, - "" + totalMark); + "" + assessmentMark); } } @@ -2128,43 +2284,41 @@ // create list of modified questions List modifiedQuestions = new ArrayList<>(); for (AssessmentQuestion oldQuestion : oldQuestions) { - - if (AssessmentConstants.QUESTION_TYPE_ESSAY == oldQuestion.getType() - || AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS == oldQuestion.getType()) { + + if (QbQuestion.TYPE_ESSAY == oldQuestion.getType() + || QbQuestion.TYPE_MATCHING_PAIRS == oldQuestion.getType() + || QbQuestion.TYPE_ORDERING == oldQuestion.getType()) { continue; } for (AssessmentQuestion newQuestion : newQuestions) { - if (oldQuestion.getUid().equals(newQuestion.getUid())) { + if (oldQuestion.getDisplayOrder() == newQuestion.getDisplayOrder()) { boolean isQuestionModified = false; // title or question is different - do nothing. Also question grade can't be changed - - //AssessmentConstants.QUESTION_TYPE_TRUE_FALSE - if (oldQuestion.getCorrectAnswer() != newQuestion.getCorrectAnswer()) { + + //QbQuestion.TYPE_TRUE_FALSE + if (oldQuestion.getQbQuestion().getCorrectAnswer() != newQuestion.getQbQuestion() + .getCorrectAnswer()) { isQuestionModified = true; } // options are different - Set oldOptions = oldQuestion.getOptions(); - Set newOptions = newQuestion.getOptions(); - for (AssessmentQuestionOption oldOption : oldOptions) { - for (AssessmentQuestionOption newOption : newOptions) { - if (oldOption.getUid().equals(newOption.getUid())) { + List oldOptions = oldQuestion.getQbQuestion().getQbOptions(); + List newOptions = newQuestion.getQbQuestion().getQbOptions(); + for (QbOption oldOption : oldOptions) { + for (QbOption newOption : newOptions) { + if (oldOption.getDisplayOrder() == newOption.getDisplayOrder()) { - //ordering - if (((oldQuestion.getType() == AssessmentConstants.QUESTION_TYPE_ORDERING) - && (oldOption.getSequenceId() != newOption.getSequenceId())) - //short answer - || ((oldQuestion.getType() == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER) - && !StringUtils.equals(oldOption.getOptionString(), - newOption.getOptionString())) + //short answer + if (((oldQuestion.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS) + && !StringUtils.equals(oldOption.getName(), newOption.getName())) //numbering - || (oldOption.getOptionFloat() != newOption.getOptionFloat()) + || (oldOption.getNumericalOption() != newOption.getNumericalOption()) || (oldOption.getAcceptedError() != newOption.getAcceptedError()) //option grade - || (oldOption.getGrade() != newOption.getGrade()) + || (oldOption.getMaxMark() != newOption.getMaxMark()) //changed correct option || (oldOption.isCorrect() != newOption.isCorrect())) { isQuestionModified = true; @@ -2184,13 +2338,13 @@ } // create list of references with modified grades. - // modifiedReferences holds pairs newReference -> oldReference.getDefaultGrade() + // modifiedReferences holds pairs newReference -> oldReference.getMaxMark() Map modifiedReferences = new HashMap<>(); for (QuestionReference oldReference : oldReferences) { for (QuestionReference newReference : newReferences) { if (oldReference.getUid().equals(newReference.getUid()) - && (oldReference.getDefaultGrade() != newReference.getDefaultGrade())) { - modifiedReferences.put(newReference, oldReference.getDefaultGrade()); + && (oldReference.getMaxMark() != newReference.getMaxMark())) { + modifiedReferences.put(newReference, oldReference.getMaxMark()); } } } @@ -2219,50 +2373,82 @@ float assessmentMark = assessmentResult.getGrade(); int assessmentMaxMark = assessmentResult.getMaximumGrade(); Set questionResults = assessmentResult.getQuestionResults(); - + // [+] if the question is modified for (AssessmentQuestionResult questionResult : questionResults) { - AssessmentQuestion question = questionResult.getAssessmentQuestion(); + QbToolQuestion oldQuestion = questionResult.getQbToolQuestion(); + Float oldQuestionAnswerMark = questionResult.getMark(); + int oldResultMaxMark = questionResult.getMaxMark() == null ? 0 + : questionResult.getMaxMark().intValue(); //check whether according question was modified for (AssessmentQuestion modifiedQuestion : modifiedQuestions) { - if (question.getUid().equals(modifiedQuestion.getUid())) { - Float oldQuestionAnswerMark = questionResult.getMark(); + if (oldQuestion.getDisplayOrder() == modifiedQuestion.getDisplayOrder()) { + //update questionResult's qbQuestion with the new one + questionResult.setQbToolQuestion(modifiedQuestion); + //update questionResult's qbOption - it seems to be redundant, as it's done in loadupQuestionResultIntoQuestionDto() +// for (QbOption newOption : modifiedQuestion.getQbQuestion().getQbOptions()) { +// if (questionResult.getQbOption().getDisplayOrder() == newOption.getDisplayOrder()) { +// questionResult.setQbOption(newOption); +// break; +// } +// } + //update questionResult's optionAnswers + for (AssessmentOptionAnswer oldOptionAnswer : questionResult.getOptionAnswers()) { + + //find according old qbOption + QbOption oldOption = null; + for (QbOption oldOptionIter : oldQuestion.getQbQuestion().getQbOptions()) { + if (oldOptionIter.getUid().equals(oldOptionAnswer.getOptionUid())) { + oldOption = oldOptionIter; + } + } + + //update + for (QbOption newOption : modifiedQuestion.getQbQuestion().getQbOptions()) { + if (oldOption.getDisplayOrder() == newOption.getDisplayOrder()) { + oldOptionAnswer.setOptionUid(newOption.getUid()); + break; + } + } + } + //actually recalculate marks - QuestionDTO questionDto = question.getQuestionDTO(); - questionDto.setGrade(questionResult.getMaxMark().intValue()); - loadupQuestionResultIntoQuestionDto(questionDto, questionResult); - calculateAnswerMark(assessmentUid, user.getUserId(), questionResult, questionDto); + QuestionDTO modifiedQuestionDto = new QuestionDTO(modifiedQuestion); + modifiedQuestionDto.setMaxMark(oldResultMaxMark); + loadupQuestionResultIntoQuestionDto(modifiedQuestionDto, questionResult); + calculateAnswerMark(assessmentUid, user.getUserId(), questionResult, + modifiedQuestionDto); assessmentQuestionResultDao.saveObject(questionResult); - + float newQuestionAnswerMark = questionResult.getMark(); assessmentMark += newQuestionAnswerMark - oldQuestionAnswerMark; break; } } } - + // [+] if the question reference mark is modified - for (AssessmentQuestionResult questionResult:questionResults) { - Long questionUid = questionResult.getAssessmentQuestion().getUid(); - + for (AssessmentQuestionResult questionResult : questionResults) { + Long questionUid = questionResult.getQbToolQuestion().getUid(); + for (QuestionReference modifiedReference : modifiedReferences.keySet()) { if (!modifiedReference.isRandomQuestion() && questionUid.equals(modifiedReference.getQuestion().getUid())) { - int newReferenceGrade = modifiedReference.getDefaultGrade(); - int oldReferenceGrade = modifiedReferences.get(modifiedReference); + int newReferenceMaxMark = modifiedReference.getMaxMark(); + int oldReferenceMaxMark = modifiedReferences.get(modifiedReference); // update question answer's mark Float oldQuestionAnswerMark = questionResult.getMark(); - float newQuestionAnswerMark = (oldQuestionAnswerMark * newReferenceGrade) - / oldReferenceGrade; + float newQuestionAnswerMark = (oldQuestionAnswerMark * newReferenceMaxMark) + / oldReferenceMaxMark; questionResult.setMark(newQuestionAnswerMark); - questionResult.setMaxMark((float) newReferenceGrade); + questionResult.setMaxMark((float) newReferenceMaxMark); assessmentQuestionResultDao.saveObject(questionResult); assessmentMark += newQuestionAnswerMark - oldQuestionAnswerMark; - assessmentMaxMark += newReferenceGrade - oldReferenceGrade; + assessmentMaxMark += newReferenceMaxMark - oldReferenceMaxMark; break; } } @@ -2272,7 +2458,7 @@ ArrayList nonRandomQuestionResults = new ArrayList<>(); for (AssessmentQuestionResult questionResult : questionResults) { for (QuestionReference reference : newReferences) { - if (!reference.isRandomQuestion() && questionResult.getAssessmentQuestion().getUid() + if (!reference.isRandomQuestion() && questionResult.getQbToolQuestion().getUid() .equals(reference.getQuestion().getUid())) { nonRandomQuestionResults.add(questionResult); } @@ -2284,27 +2470,28 @@ // [+] if the question reference mark is modified (in case of random question references) for (QuestionReference modifiedReference : modifiedReferences.keySet()) { - // in case of random question reference - search for the answer with the same maxmark (it does not matter to which random reference this question belong originally as the only thing that differentiate those references is defaultGrade) + // in case of random question reference - search for the answer with the same maxmark (it does not matter to which random reference this question belong originally as the only thing that differentiate those references is maxMark) if (modifiedReference.isRandomQuestion()) { for (AssessmentQuestionResult randomQuestionResult : randomQuestionResults) { - int newReferenceGrade = modifiedReference.getDefaultGrade(); - int oldReferenceGrade = modifiedReferences.get(modifiedReference); + int newReferenceMaxMark = modifiedReference.getMaxMark(); + int oldReferenceMaxMark = modifiedReferences.get(modifiedReference); - if (randomQuestionResult.getMaxMark().intValue() == oldReferenceGrade) { + if (randomQuestionResult.getMaxMark() != null + && randomQuestionResult.getMaxMark().intValue() == oldReferenceMaxMark) { // update question answer's mark Float oldQuestionResultMark = randomQuestionResult.getMark(); - float newQuestionResultMark = (oldQuestionResultMark * newReferenceGrade) - / oldReferenceGrade; + float newQuestionResultMark = (oldQuestionResultMark * newReferenceMaxMark) + / oldReferenceMaxMark; randomQuestionResult.setMark(newQuestionResultMark); - randomQuestionResult.setMaxMark((float) newReferenceGrade); + randomQuestionResult.setMaxMark((float) newReferenceMaxMark); assessmentQuestionResultDao.saveObject(randomQuestionResult); nonRandomQuestionResults.add(randomQuestionResult); assessmentMark += newQuestionResultMark - oldQuestionResultMark; - assessmentMaxMark += newReferenceGrade - oldReferenceGrade; + assessmentMaxMark += newReferenceMaxMark - oldReferenceMaxMark; break; } } @@ -2313,30 +2500,38 @@ } // store new mark and maxMark if they were changed - if ((assessmentResult.getGrade() != assessmentMark) - || (assessmentResult.getMaximumGrade() != assessmentMaxMark)) { + storeAssessmentResultMarkAndMaxMark(assessmentResult, lastFinishedAssessmentResult, assessmentMark, + assessmentMaxMark, user); + } + } + } + } - // marks can't be below zero - assessmentMark = (assessmentMark < 0) ? 0 : assessmentMark; - assessmentMaxMark = (assessmentMaxMark < 0) ? 0 : assessmentMaxMark; + /** + * Store new mark and maxMark if they were changed + */ + private void storeAssessmentResultMarkAndMaxMark(AssessmentResult assessmentResult, + AssessmentResult lastFinishedAssessmentResult, float newAssessmentMark, int newAssessmentMaxMark, + AssessmentUser user) { + // store new mark and maxMark if they were changed + if ((assessmentResult.getGrade() != newAssessmentMark) + || (assessmentResult.getMaximumGrade() != newAssessmentMaxMark)) { - assessmentResult.setGrade(assessmentMark); - assessmentResult.setMaximumGrade(assessmentMaxMark); - assessmentResultDao.saveObject(assessmentResult); + // marks can't be below zero + newAssessmentMark = (newAssessmentMark < 0) ? 0 : newAssessmentMark; + newAssessmentMaxMark = (newAssessmentMaxMark < 0) ? 0 : newAssessmentMaxMark; - // if this is the last finished assessment result - propagade total mark to Gradebook - if (lastFinishedAssessmentResult != null - && lastFinishedAssessmentResult.getUid().equals(assessmentResult.getUid())) { - toolService.updateActivityMark(Double.valueOf(assessmentMark), null, - user.getUserId().intValue(), toolSessionId, false); - } - } + assessmentResult.setGrade(newAssessmentMark); + assessmentResult.setMaximumGrade(newAssessmentMaxMark); + assessmentResultDao.saveObject(assessmentResult); - } - + // if this is the last finished assessment result - propagade total mark to Gradebook + if (lastFinishedAssessmentResult != null + && lastFinishedAssessmentResult.getUid().equals(assessmentResult.getUid())) { + toolService.updateActivityMark(Double.valueOf(newAssessmentMark), null, user.getUserId().intValue(), + user.getSession().getSessionId(), false); } } - } @Override @@ -2353,7 +2548,7 @@ public void auditLogStartEditingActivityInMonitor(long toolContentID) { toolService.auditLogStartEditingActivityInMonitor(toolContentID); } - + @Override public boolean isLastActivity(Long toolSessionId) { return toolService.isLastActivity(toolSessionId); @@ -2543,6 +2738,10 @@ } toolContentObj = Assessment.newInstance(toolContentObj, toolContentId); + for (AssessmentQuestion assessmentQuestion : toolContentObj.getQuestions()) { + qbService.prepareQuestionForExport(assessmentQuestion.getQbQuestion()); + } + try { exportContentService.exportToolContent(toolContentId, toolContentObj, assessmentToolContentHandler, rootPath); @@ -2569,8 +2768,7 @@ // reset it to new toolContentId toolContentObj.setContentId(toolContentId); - AssessmentUser user = assessmentUserDao.getUserCreatedAssessment(newUserUid.longValue(), - toolContentId); + AssessmentUser user = assessmentUserDao.getUserCreatedAssessment(newUserUid.longValue(), toolContentId); if (user == null) { user = new AssessmentUser(); UserDTO sysUser = ((User) userManagementService.findById(User.class, newUserUid)).getUserDTO(); @@ -2582,6 +2780,26 @@ } toolContentObj.setCreatedBy(user); + long publicQbCollectionUid = qbService.getPublicCollection().getUid(); + + // we need to save QB questions and options first + for (AssessmentQuestion assessmentQuestion : toolContentObj.getQuestions()) { + QbQuestion qbQuestion = assessmentQuestion.getQbQuestion(); + + // try to match the question to an existing QB question in DB + QbQuestion existingQuestion = qbService.getQuestionByUUID(qbQuestion.getUuid()); + if (existingQuestion == null) { + // none found, create a new QB question + qbService.insertQuestion(qbQuestion); + qbService.addQuestionToCollection(publicQbCollectionUid, qbQuestion.getQuestionId(), false); + } else { + // found, use the existing one + assessmentQuestion.setQbQuestion(existingQuestion); + } + + assessmentDao.insert(assessmentQuestion); + } + saveOrUpdateAssessment(toolContentObj); } catch (ImportToolContentException e) { throw new ToolException(e); @@ -2753,7 +2971,7 @@ @Override public List getConfidenceLevels(Long toolSessionId) { - List confidenceLevelDtos = new ArrayList(); + List confidenceLevelDtos = new ArrayList<>(); if (toolSessionId == null) { return confidenceLevelDtos; } @@ -2772,44 +2990,45 @@ //fill in question's and user answer's hashes for (AssessmentQuestionResult questionResult : assessmentResult.getQuestionResults()) { - AssessmentQuestion question = questionResult.getAssessmentQuestion(); + QbQuestion qbQuestion = questionResult.getQbQuestion(); - List answers = new LinkedList(); + List answers = new LinkedList<>(); + List optionUids = new LinkedList<>(); - if (question.getType() == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) { + if (qbQuestion.getType() == QbQuestion.TYPE_MULTIPLE_CHOICE) { - for (AssessmentQuestionOption option : question.getOptions()) { + for (QbOption option : qbQuestion.getQbOptions()) { for (AssessmentOptionAnswer optionAnswer : questionResult.getOptionAnswers()) { if (optionAnswer.getAnswerBoolean() && (optionAnswer.getOptionUid().equals(option.getUid()))) { - answers.add(option.getOptionString()); + optionUids.add(option.getUid()); } } } - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS) { + } else if (qbQuestion.getType() == QbQuestion.TYPE_MATCHING_PAIRS) { - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_SHORT_ANSWER) { - answers.add(questionResult.getAnswerString()); + } else if (qbQuestion.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS) { + answers.add(questionResult.getAnswer()); - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_NUMERICAL) { - answers.add(questionResult.getAnswerString()); + } else if (qbQuestion.getType() == QbQuestion.TYPE_NUMERICAL) { + answers.add(questionResult.getAnswer()); - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_TRUE_FALSE) { - if (questionResult.getAnswerString() != null) { + } else if (qbQuestion.getType() == QbQuestion.TYPE_TRUE_FALSE) { + if (questionResult.getAnswer() != null) { answers.add("" + questionResult.getAnswerBoolean()); } - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_ESSAY) { - answers.add(questionResult.getAnswerString()); + } else if (qbQuestion.getType() == QbQuestion.TYPE_ESSAY) { + answers.add(questionResult.getAnswer()); - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_ORDERING) { + } else if (qbQuestion.getType() == QbQuestion.TYPE_ORDERING) { - } else if (question.getType() == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING) { + } else if (qbQuestion.getType() == QbQuestion.TYPE_MARK_HEDGING) { } - for (String answer : answers) { + for (Long optionUid : optionUids) { ConfidenceLevelDTO confidenceLevelDto = new ConfidenceLevelDTO(); confidenceLevelDto.setUserId(user.getUserId().intValue()); String userName = StringUtils.isBlank(user.getFirstName()) @@ -2818,11 +3037,21 @@ confidenceLevelDto.setUserName(userName); confidenceLevelDto.setPortraitUuid(portraitUuid); confidenceLevelDto.setLevel(questionResult.getConfidenceLevel()); - confidenceLevelDto.setQuestion(question.getQuestion()); - confidenceLevelDto.setAnswer(answer); + confidenceLevelDto.setQbQuestionUid(qbQuestion.getUid()); + confidenceLevelDto.setQbOptionUid(optionUid); confidenceLevelDtos.add(confidenceLevelDto); } + for (String answer : answers) { +// ConfidenceLevelDTO confidenceLevelDto = new ConfidenceLevelDTO(); +// confidenceLevelDto.setUserId(userId.intValue()); +// confidenceLevelDto.setPortraitUuid(portraitUuid); +// confidenceLevelDto.setLevel(questionResult.getConfidenceLevel()); +// confidenceLevelDto.setQbQuestionUid(qbQuestion.getUid()); +// confidenceLevelDto.setQbOptionUid(optionUid); +// +// confidenceLevelDtos.add(confidenceLevelDto); + } } } @@ -2831,6 +3060,78 @@ } @Override + public Collection getVsaAnswers(Long toolSessionId) { + if (toolSessionId == null) { + return new ArrayList<>(); + } + + Map uid_answerToVsaAnswerDtoMap = new LinkedHashMap<>(); + + Assessment assessment = getAssessmentBySessionId(toolSessionId); + //in case Assessment is leader aware return all leaders confidences, otherwise - confidences from the users from the same group as requestor + List assessmentResultsAndPortraits = assessment.isUseSelectLeaderToolOuput() + ? assessmentResultDao.getLeadersLastFinishedAssessmentResults(assessment.getContentId()) + : assessmentResultDao.getLastFinishedAssessmentResultsBySession(toolSessionId); + + for (Object[] assessmentResultsAndPortraitIter : assessmentResultsAndPortraits) { + AssessmentResult assessmentResult = (AssessmentResult) assessmentResultsAndPortraitIter[0]; + Long portraitUuid = assessmentResultsAndPortraitIter[1] == null ? null + : ((Number) assessmentResultsAndPortraitIter[1]).longValue(); + AssessmentUser user = assessmentResult.getUser(); + + //fill in question's and user answer's hashes + for (AssessmentQuestionResult questionResult : assessmentResult.getQuestionResults()) { + QbQuestion qbQuestion = questionResult.getQbQuestion(); + + if (qbQuestion.getType() == QbQuestion.TYPE_VERY_SHORT_ANSWERS) { + //uid_answer should be unique in the final list + String uid_answer = qbQuestion.getUid() + "_" + questionResult.getAnswer(); + + //find VsaAnswerDTO in the map, or create the new one if it doesn't exist + VsaAnswerDTO vsaAnswerDTO; + if (uid_answerToVsaAnswerDtoMap.containsKey(uid_answer)) { + vsaAnswerDTO = uid_answerToVsaAnswerDtoMap.get(uid_answer); + + } else { + vsaAnswerDTO = new VsaAnswerDTO(); + vsaAnswerDTO.setQbQuestionUid(qbQuestion.getUid()); + vsaAnswerDTO.setAnswer(questionResult.getAnswer()); + vsaAnswerDTO.setCorrect(questionResult.getMark() > 0); + vsaAnswerDTO.setUserId(user.getUserId()); + uid_answerToVsaAnswerDtoMap.put(uid_answer, vsaAnswerDTO); + + //find and store optionUid + for (QbOption option : qbQuestion.getQbOptions()) { + for (AssessmentOptionAnswer optionAnswer : questionResult.getOptionAnswers()) { + if (optionAnswer.getAnswerBoolean() + && (optionAnswer.getOptionUid().equals(option.getUid()))) { + Long optionUid = option.getUid(); + vsaAnswerDTO.setQbOptionUid(optionUid); + break; + } + } + } + } + + ConfidenceLevelDTO confidenceLevelDto = new ConfidenceLevelDTO(); + confidenceLevelDto.setUserId(user.getUserId().intValue()); + String userName = StringUtils.isBlank(user.getFirstName()) + && StringUtils.isBlank(user.getLastName()) ? user.getLoginName() + : user.getFirstName() + " " + user.getLastName(); + confidenceLevelDto.setUserName(userName); + confidenceLevelDto.setPortraitUuid(portraitUuid); + confidenceLevelDto.setLevel(questionResult.getConfidenceLevel()); + + vsaAnswerDTO.getConfidenceLevels().add(confidenceLevelDto); + } + } + + } + + return uid_answerToVsaAnswerDtoMap.values(); + } + + @Override public void forceCompleteUser(Long toolSessionId, User user) { Long userId = user.getUserId().longValue(); @@ -2839,16 +3140,16 @@ return; } Assessment assessment = session.getAssessment(); - + AssessmentUser assessmentUser = getUserByIDAndSession(userId, toolSessionId); // create user if he hasn't accessed this activity yet if (assessmentUser == null) { assessmentUser = new AssessmentUser(user.getUserDTO(), session); createUser(assessmentUser); - - setAttemptStarted(assessment, assessmentUser, toolSessionId); + + setAttemptStarted(assessment, assessmentUser, toolSessionId, null); } - + //finalize the latest result, if it's still active AssessmentResult lastAssessmentResult = getLastAssessmentResult(assessment.getUid(), userId); if (lastAssessmentResult != null && lastAssessmentResult.getFinishDate() == null) { @@ -2869,11 +3170,12 @@ //copy answers from leader to non-leaders copyAnswersFromLeader(sessionUser, groupLeader); }); - + } else { assessmentUser.setSessionFinished(true); assessmentUserDao.saveObject(user); } + } @Override @@ -2923,6 +3225,10 @@ this.eventNotificationService = eventNotificationService; } + public void setQbService(IQbService qbService) { + this.qbService = qbService; + } + public AssessmentOutputFactory getAssessmentOutputFactory() { return assessmentOutputFactory; } @@ -2982,7 +3288,7 @@ * "answerFloat", "displayOrder" (Integer), "grade" (Integer). * * The references entry should be a ArrayNode containing JSON objects, which in turn must contain "displayOrder" - * (Integer), "questionDisplayOrder" (Integer - to match to the question). It may also have "defaultGrade" (Integer) + * (Integer), "questionDisplayOrder" (Integer - to match to the question). It may also have "maxMark" (Integer) * and "randomQuestion" (Boolean) * * @throws IOException @@ -3046,76 +3352,107 @@ assessment.setCreatedBy(assessmentUser); // **************************** Set the question bank ********************* + QbCollection collection = qbService.getUserPrivateCollection(userID); + Set collectionUUIDs = collection == null ? new HashSet<>() + : qbService.getCollectionQuestions(collection.getUid()).stream().filter(q -> q.getUuid() != null) + .collect(Collectors.mapping(q -> q.getUuid().toString(), Collectors.toSet())); ArrayNode questions = JsonUtil.optArray(toolContentJSON, "questions"); Set newQuestionSet = assessment.getQuestions(); // the Assessment constructor will set up the collection for (JsonNode questionJSONData : questions) { + boolean addToCollection = false; + AssessmentQuestion question = new AssessmentQuestion(); - short type = JsonUtil.optInt(questionJSONData, "type").shortValue(); - question.setType(type); - question.setTitle(questionJSONData.get(RestTags.QUESTION_TITLE).asText()); - question.setQuestion(questionJSONData.get(RestTags.QUESTION_TEXT).asText()); - question.setSequenceId(JsonUtil.optInt(questionJSONData, RestTags.DISPLAY_ORDER)); + Integer type = JsonUtil.optInt(questionJSONData, "type"); + question.setToolContentId(toolContentID); + question.setDisplayOrder(JsonUtil.optInt(questionJSONData, RestTags.DISPLAY_ORDER)); - question.setAllowRichEditor( - JsonUtil.optBoolean(questionJSONData, RestTags.ALLOW_RICH_TEXT_EDITOR, Boolean.FALSE)); - question.setAnswerRequired(JsonUtil.optBoolean(questionJSONData, "answerRequired", Boolean.FALSE)); - question.setCaseSensitive(JsonUtil.optBoolean(questionJSONData, "caseSensitive", Boolean.FALSE)); - question.setCorrectAnswer(JsonUtil.optBoolean(questionJSONData, "correctAnswer", Boolean.FALSE)); - question.setDefaultGrade(JsonUtil.optInt(questionJSONData, "defaultGrade", 1)); - question.setFeedback(JsonUtil.optString(questionJSONData, "feedback")); - question.setFeedbackOnCorrect(JsonUtil.optString(questionJSONData, "feedbackOnCorrect")); - question.setFeedbackOnIncorrect(JsonUtil.optString(questionJSONData, "feedbackOnIncorrect")); - question.setFeedbackOnPartiallyCorrect(JsonUtil.optString(questionJSONData, "feedbackOnPartiallyCorrect")); - question.setGeneralFeedback(JsonUtil.optString(questionJSONData, "generalFeedback", "")); - question.setMaxWordsLimit(JsonUtil.optInt(questionJSONData, "maxWordsLimit", 0)); - question.setMinWordsLimit(JsonUtil.optInt(questionJSONData, "minWordsLimit", 0)); - question.setMultipleAnswersAllowed( - JsonUtil.optBoolean(questionJSONData, "multipleAnswersAllowed", Boolean.FALSE)); - question.setIncorrectAnswerNullifiesMark( - JsonUtil.optBoolean(questionJSONData, "incorrectAnswerNullifiesMark", Boolean.FALSE)); - question.setPenaltyFactor(JsonUtil.optDouble(questionJSONData, "penaltyFactor", 0.0).floatValue()); - // question.setUnits(units); Needed for numerical type question + QbQuestion qbQuestion = null; + String uuid = JsonUtil.optString(questionJSONData, RestTags.QUESTION_UUID); - if ((type == AssessmentConstants.QUESTION_TYPE_MATCHING_PAIRS) - || (type == AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE) - || (type == AssessmentConstants.QUESTION_TYPE_NUMERICAL) - || (type == AssessmentConstants.QUESTION_TYPE_MARK_HEDGING)) { + // try to match the question to an existing QB question in DB + if (StringUtils.isNotBlank(uuid)) { + qbQuestion = qbService.getQuestionByUUID(UUID.fromString(uuid)); + } - if (!questionJSONData.has(RestTags.ANSWERS)) { - throw new IOException("REST Authoring is missing answers for a question of type " + type + ". Data:" - + toolContentJSON); - } + if (qbQuestion == null) { + addToCollection = collection != null; - Set optionList = new LinkedHashSet<>(); - ArrayNode optionsData = JsonUtil.optArray(questionJSONData, RestTags.ANSWERS); - for (JsonNode answerData : optionsData) { - AssessmentQuestionOption option = new AssessmentQuestionOption(); - option.setSequenceId(JsonUtil.optInt(answerData, RestTags.DISPLAY_ORDER)); - option.setGrade(answerData.get("grade").floatValue()); - option.setCorrect(JsonUtil.optBoolean(answerData, "correct", false)); - option.setAcceptedError(JsonUtil.optDouble(answerData, "acceptedError", 0.0).floatValue()); - option.setFeedback(JsonUtil.optString(answerData, "feedback")); - option.setOptionString(JsonUtil.optString(answerData, RestTags.ANSWER_TEXT)); - option.setOptionFloat(JsonUtil.optDouble(answerData, "answerFloat", 0.0).floatValue()); - // option.setQuestion(question); can't find the use for this field yet! - optionList.add(option); + qbQuestion = new QbQuestion(); + qbQuestion.setQuestionId(qbService.generateNextQuestionId()); + qbQuestion.setType(type); + qbQuestion.setName(JsonUtil.optString(questionJSONData, RestTags.QUESTION_TITLE)); + qbQuestion.setDescription(JsonUtil.optString(questionJSONData, RestTags.QUESTION_TEXT)); + + qbQuestion.setAllowRichEditor( + JsonUtil.optBoolean(questionJSONData, RestTags.ALLOW_RICH_TEXT_EDITOR, Boolean.FALSE)); + qbQuestion.setAnswerRequired(JsonUtil.optBoolean(questionJSONData, "answerRequired", Boolean.FALSE)); + qbQuestion.setCaseSensitive(JsonUtil.optBoolean(questionJSONData, "caseSensitive", Boolean.FALSE)); + qbQuestion.setCorrectAnswer(JsonUtil.optBoolean(questionJSONData, "correctAnswer", Boolean.FALSE)); + qbQuestion.setMaxMark(JsonUtil.optInt(questionJSONData, "maxMark", 1)); + qbQuestion.setFeedback(JsonUtil.optString(questionJSONData, "feedback")); + qbQuestion.setFeedbackOnCorrect(JsonUtil.optString(questionJSONData, "feedbackOnCorrect")); + qbQuestion.setFeedbackOnIncorrect(JsonUtil.optString(questionJSONData, "feedbackOnIncorrect")); + qbQuestion.setFeedbackOnPartiallyCorrect( + JsonUtil.optString(questionJSONData, "feedbackOnPartiallyCorrect")); + qbQuestion.setMaxWordsLimit(JsonUtil.optInt(questionJSONData, "maxWordsLimit", 0)); + qbQuestion.setMinWordsLimit(JsonUtil.optInt(questionJSONData, "minWordsLimit", 0)); + qbQuestion.setMultipleAnswersAllowed( + JsonUtil.optBoolean(questionJSONData, "multipleAnswersAllowed", Boolean.FALSE)); + qbQuestion.setIncorrectAnswerNullifiesMark( + JsonUtil.optBoolean(questionJSONData, "incorrectAnswerNullifiesMark", Boolean.FALSE)); + qbQuestion.setPenaltyFactor(JsonUtil.optDouble(questionJSONData, "penaltyFactor", 0.0).floatValue()); + + assessmentDao.insert(qbQuestion); + + if ((type == QbQuestion.TYPE_MATCHING_PAIRS) || (type == QbQuestion.TYPE_MULTIPLE_CHOICE) + || (type == QbQuestion.TYPE_NUMERICAL) || (type == QbQuestion.TYPE_MARK_HEDGING)) { + + if (!questionJSONData.has(RestTags.ANSWERS)) { + throw new IOException("REST Authoring is missing answers for a question of type " + type + + ". Data:" + toolContentJSON); + } + + List optionList = new ArrayList<>(); + ArrayNode optionsData = JsonUtil.optArray(questionJSONData, RestTags.ANSWERS); + for (JsonNode answerData : optionsData) { + QbOption option = new QbOption(); + option.setQbQuestion(qbQuestion); + option.setDisplayOrder(JsonUtil.optInt(answerData, RestTags.DISPLAY_ORDER)); + Double grade = JsonUtil.optDouble(answerData, "grade"); + option.setMaxMark(grade == null ? 0 : grade.floatValue()); + option.setAcceptedError(JsonUtil.optDouble(answerData, "acceptedError", 0.0).floatValue()); + option.setFeedback(JsonUtil.optString(answerData, "feedback")); + option.setName(JsonUtil.optString(answerData, RestTags.ANSWER_TEXT)); + option.setNumericalOption(JsonUtil.optDouble(answerData, "answerFloat", 0.0).floatValue()); + // option.setQuestion(question); can't find the use for this field yet! + optionList.add(option); + } + qbQuestion.setQbOptions(optionList); } - question.setOptions(optionList); + } else if (collection != null && !collectionUUIDs.contains(uuid)) { + addToCollection = true; } + // question.setUnits(units); Needed for numerical type question + question.setQbQuestion(qbQuestion); checkType(question.getType()); newQuestionSet.add(question); + + // all questions need to end up in user's private collection + if (addToCollection) { + qbService.addQuestionToCollection(collection.getUid(), qbQuestion.getQuestionId(), false); + collectionUUIDs.add(uuid); + } } // **************************** Now set up the references to the questions in the bank ********************* ArrayNode references = JsonUtil.optArray(toolContentJSON, "references"); Set newReferenceSet = assessment.getQuestionReferences(); // the Assessment constructor will set up the - ;// collection + // collection for (JsonNode referenceJSONData : references) { QuestionReference reference = new QuestionReference(); - reference.setType((short) 0); - reference.setDefaultGrade(JsonUtil.optInt(referenceJSONData, "defaultGrade", 1)); + reference.setMaxMark(JsonUtil.optInt(referenceJSONData, "maxMark", 1)); reference.setSequenceId(JsonUtil.optInt(referenceJSONData, RestTags.DISPLAY_ORDER)); AssessmentQuestion matchingQuestion = matchQuestion(newQuestionSet, JsonUtil.optInt(referenceJSONData, "questionDisplayOrder")); @@ -3125,7 +3462,6 @@ } reference.setQuestion(matchingQuestion); reference.setRandomQuestion(JsonUtil.optBoolean(referenceJSONData, "randomQuestion", Boolean.FALSE)); - reference.setTitle(null); newReferenceSet.add(reference); } @@ -3137,7 +3473,7 @@ AssessmentQuestion matchQuestion(Set newReferenceSet, Integer displayOrder) { if (displayOrder != null) { for (AssessmentQuestion question : newReferenceSet) { - if (displayOrder.equals(question.getSequenceId())) { + if (displayOrder.equals(question.getDisplayOrder())) { return question; } } @@ -3146,18 +3482,12 @@ } // TODO Implement REST support for all types and then remove checkType method - void checkType(short type) throws IOException { - if ((type != AssessmentConstants.QUESTION_TYPE_ESSAY) - && (type != AssessmentConstants.QUESTION_TYPE_MULTIPLE_CHOICE)) { + void checkType(Integer type) throws IOException { + if ((type != QbQuestion.TYPE_ESSAY) && (type != QbQuestion.TYPE_MULTIPLE_CHOICE)) { throw new IOException( "Assessment Tool does not support REST Authoring for anything but Essay Type and Multiple Choice. Found type " + type); } - // public static final short QUESTION_TYPE_MATCHING_PAIRS = 2; - // public static final short QUESTION_TYPE_SHORT_ANSWER = 3; - // public static final short QUESTION_TYPE_NUMERICAL = 4; - // public static final short QUESTION_TYPE_TRUE_FALSE = 5; - // public static final short QUESTION_TYPE_ORDERING = 7; } @Override @@ -3168,7 +3498,7 @@ @Override public void notifyLearnersOnAnswerDisclose(long toolContentId) { List sessions = assessmentSessionDao.getByContentId(toolContentId); - Set userIds = new HashSet(); + Set userIds = new HashSet<>(); for (AssessmentSession session : sessions) { for (AssessmentUser user : session.getAssessmentUsers()) { userIds.add(user.getUserId().intValue()); Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java =================================================================== diff -u -ra9f95a26e562a58b55c99f2c18e253c151ef457a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision a9f95a26e562a58b55c99f2c18e253c151ef457a) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -61,7 +61,7 @@ * @param toolSessionId * @return */ - boolean isUserGroupLeader(AssessmentUser user, Long toolSessionId); + boolean isUserGroupLeader(Long userId, Long toolSessionId); /** * Set specified user as a leader. Also the previous leader (if any) is marked as non-leader. Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java =================================================================== diff -u -r76466ac35a7c584f60f7f9f40098d320a83dca7a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java (.../LearningController.java) (revision 76466ac35a7c584f60f7f9f40098d320a83dca7a) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java (.../LearningController.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -192,7 +192,7 @@ } sessionMap.put(AssessmentConstants.ATTR_GROUP_LEADER, groupLeader); - boolean isUserLeader = service.isUserGroupLeader(user, toolSessionId); + boolean isUserLeader = service.isUserGroupLeader(user.getUserId(), toolSessionId); sessionMap.put(AssessmentConstants.ATTR_IS_USER_LEADER, isUserLeader); Set questionReferences = new TreeSet<>(new SequencableComparator()); Index: lams_tool_assessment/web/pages/learning/learning.jsp =================================================================== diff -u -r269c13324c6bb998631af858dc8091ad3102ef78 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision 269c13324c6bb998631af858dc8091ad3102ef78) +++ lams_tool_assessment/web/pages/learning/learning.jsp (.../learning.jsp) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -195,31 +195,30 @@ //autosave feature + + function learnerAutosave(){ + if (isWaitingForConfirmation) return; + + //copy value from CKEditor (only available in essay type of questions) to textarea before ajax submit + $("textarea[id^='question']").each(function() { + var ckeditorData = CKEDITOR.instances[this.name].getData(); + //skip out empty values + this.value = ((ckeditorData == null) || (ckeditorData.replace(/ | |
|\s|

|<\/p>|\xa0/g, "").length == 0)) ? "" : ckeditorData; + }); + + //ajax form submit + $('#answers').ajaxSubmit({ + url: "?sessionMapID=${sessionMapID}&date=" + new Date().getTime(), + success: function() { + $.jGrowl( + " ", + { life: 2000, closeTemplate: '' } + ); + } + }); + } var autosaveInterval = "30000"; // 30 seconds interval - window.setInterval( - function(){ - if (isWaitingForConfirmation) return; - - //copy value from CKEditor (only available in essay type of questions) to textarea before ajax submit - $("textarea[id^='question']").each(function() { - var ckeditorData = CKEDITOR.instances[this.name].getData(); - //skip out empty values - this.value = ((ckeditorData == null) || (ckeditorData.replace(/ | |
|\s|

|<\/p>|\xa0/g, "").length == 0)) ? "" : ckeditorData; - }); - - //ajax form submit - $('#answers').ajaxSubmit({ - url: "?sessionMapID=${sessionMapID}&date=" + new Date().getTime(), - success: function() { - $.jGrowl( - " ", - { life: 2000, closeTemplate: '' } - ); - } - }); - }, - autosaveInterval - ); + window.setInterval(learnerAutosave, autosaveInterval); //check if we came back due to failed answers' validation (missing required question's answer or min words limit not reached) Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dao/IMcUsrAttemptDAO.java =================================================================== diff -u -rb436dbb8397cdcbacaab50eec68b6427a5c6e1e9 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dao/IMcUsrAttemptDAO.java (.../IMcUsrAttemptDAO.java) (revision b436dbb8397cdcbacaab50eec68b6427a5c6e1e9) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dao/IMcUsrAttemptDAO.java (.../IMcUsrAttemptDAO.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -101,7 +101,7 @@ /** * Get the highest attempt order for a user for a particular questionDescription */ - McUsrAttempt getUserAttemptByQuestion(Long queUsrUid, Long mcQueContentId); + McUsrAttempt getUserAttemptByQuestion(Long userUid, Long mcQueContentId); /** * Get the highest attempt order for a all users in a session for a particular questionDescription Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dao/hibernate/McUsrAttemptDAO.java =================================================================== diff -u -rb436dbb8397cdcbacaab50eec68b6427a5c6e1e9 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dao/hibernate/McUsrAttemptDAO.java (.../McUsrAttemptDAO.java) (revision b436dbb8397cdcbacaab50eec68b6427a5c6e1e9) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/dao/hibernate/McUsrAttemptDAO.java (.../McUsrAttemptDAO.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -120,9 +120,9 @@ @Override @SuppressWarnings("unchecked") - public McUsrAttempt getUserAttemptByQuestion(final Long queUsrUid, final Long mcQueContentId) { + public McUsrAttempt getUserAttemptByQuestion(final Long userUid, final Long mcQueContentId) { List userAttemptList = getSessionFactory().getCurrentSession() - .createQuery(LOAD_PARTICULAR_QUESTION_ATTEMPT).setParameter("queUsrUid", queUsrUid) + .createQuery(LOAD_PARTICULAR_QUESTION_ATTEMPT).setParameter("queUsrUid", userUid) .setParameter("mcQueContentId", mcQueContentId).list(); if (userAttemptList.size() > 1) { throw new RuntimeException("There are more than 1 latest questionDescription attempt"); Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java =================================================================== diff -u -ra9f95a26e562a58b55c99f2c18e253c151ef457a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision a9f95a26e562a58b55c99f2c18e253c151ef457a) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/service/McService.java (.../McService.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -1662,6 +1662,14 @@ return confidenceLevelDtos; } + + @Override + public boolean isUserGroupLeader(Long userId, Long toolSessionId) { + McSession session = getMcSessionById(toolSessionId); + McQueUsr mcUser = getMcUserBySession(userId, session.getUid()); + + return (session != null) && (mcUser != null) && session.isUserGroupLeader(mcUser); + } @Override public void forceCompleteUser(Long toolSessionId, User user) { @@ -1673,30 +1681,44 @@ } McContent content = session.getMcContent(); - // copy answers only in case leader aware feature is ON - if (content.isUseSelectLeaderToolOuput()) { + McQueUsr mcUser = getMcUserBySession(userId, session.getUid()); + // create user if he hasn't accessed this activity yet + if (mcUser == null) { + String userName = user.getLogin(); + String fullName = user.getFirstName() + " " + user.getLastName(); + mcUser = new McQueUsr(userId, userName, fullName, session); + mcUserDAO.saveMcUser(mcUser); + } + + //finalize the latest result, if it's still active + if (!mcUser.isResponseFinalised()) { - McQueUsr mcUser = getMcUserBySession(userId, session.getUid()); - // create user if he hasn't accessed this activity yet - if (mcUser == null) { - - String userName = user.getLogin(); - String fullName = user.getFirstName() + " " + user.getLastName(); - mcUser = new McQueUsr(userId, userName, fullName, session); - mcUserDAO.saveMcUser(mcUser); + //calculate total learner mark + int learnerMark = 0; + for (McQueContent question : content.getMcQueContents()) { + McUsrAttempt attempt = mcUsrAttemptDAO.getUserAttemptByQuestion(mcUser.getUid(), question.getUid()); + learnerMark += attempt == null ? 0 : attempt.getMark(); } - McQueUsr groupLeader = session.getGroupLeader(); + Integer numberOfAttempts = mcUser.getNumberOfAttempts() + 1; + mcUser.setNumberOfAttempts(numberOfAttempts); + mcUser.setLastAttemptTotalMark(learnerMark); + mcUser.setResponseFinalised(true); + updateMcQueUsr(mcUser); + } - // check if leader has submitted answers - if ((groupLeader != null) && groupLeader.isResponseFinalised()) { + //if this is a leader finishes, complete all non-leaders as well, also copy leader results to them + McQueUsr groupLeader = checkLeaderSelectToolForSessionLeader(mcUser, toolSessionId); + if (session.isUserGroupLeader(mcUser)) { + session.getMcQueUsers().forEach(sessionUser -> { + //finish non-leader + sessionUser.setResponseFinalised(true); + updateMcQueUsr(sessionUser); - // we need to make sure specified user has the same scratches as a leader - copyAnswersFromLeader(mcUser, groupLeader); - } - + //copy answers from leader to non-leaders + copyAnswersFromLeader(sessionUser, groupLeader); + }); } - } @Override @@ -2012,7 +2034,6 @@ * * Retries are controlled by lockWhenFinished, which defaults to true (no retries). */ - @SuppressWarnings("unchecked") @Override public void createRestToolContent(Integer userID, Long toolContentID, ObjectNode toolContentJSON) { Index: lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McLearningController.java =================================================================== diff -u -ra9f95a26e562a58b55c99f2c18e253c151ef457a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McLearningController.java (.../McLearningController.java) (revision a9f95a26e562a58b55c99f2c18e253c151ef457a) +++ lams_tool_lamc/src/java/org/lamsfoundation/lams/tool/mc/web/controller/McLearningController.java (.../McLearningController.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -284,10 +284,6 @@ return "learning/AnswersContent"; } - /** - * ActionForward endLearning(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse - * response) - */ @RequestMapping("/endLearning") public String endLearning(@ModelAttribute McLearningForm mcLearningForm, HttpServletRequest request, HttpServletResponse response) throws IOException { @@ -394,20 +390,20 @@ String toolSessionID = request.getParameter(AttributeNames.PARAM_TOOL_SESSION_ID); McSession session = mcService.getMcSessionById(new Long(toolSessionID)); String toolContentId = session.getMcContent().getMcContentId().toString(); - McContent mcContent = mcService.getMcContent(new Long(toolContentId)); + McContent content = mcService.getMcContent(new Long(toolContentId)); List answers = McLearningController.parseLearnerAnswers(mcLearningForm, request, - mcContent.isQuestionsSequenced()); + content.isQuestionsSequenced()); Map learnerConfidenceLevels = null; - if (mcContent.isEnableConfidenceLevels()) { + if (content.isEnableConfidenceLevels()) { learnerConfidenceLevels = parseLearnerConfidenceLevels(mcLearningForm, request, - mcContent.isQuestionsSequenced()); + content.isQuestionsSequenced()); } - if (mcContent.isQuestionsSequenced()) { + if (content.isQuestionsSequenced()) { sessionMap.put(McAppConstants.QUESTION_AND_CANDIDATE_ANSWERS_KEY, answers); - if (mcContent.isEnableConfidenceLevels()) { + if (content.isEnableConfidenceLevels()) { sessionMap.put(McAppConstants.CONFIDENCE_LEVELS_KEY, learnerConfidenceLevels); } } @@ -422,7 +418,7 @@ } /* process the answers */ - List answerDtos = buildAnswerDtos(answers, learnerConfidenceLevels, mcContent, request); + List answerDtos = buildAnswerDtos(answers, learnerConfidenceLevels, content, request); mcService.saveUserAttempt(user, answerDtos); //calculate total learner mark @@ -436,6 +432,18 @@ user.setLastAttemptTotalMark(learnerMark); user.setResponseFinalised(true); mcService.updateMcQueUsr(user); + + //if this is a leader finishes, complete all non-leaders as well, also copy leader results to them + if (content.isUseSelectLeaderToolOuput() && session.isUserGroupLeader(user)) { + session.getMcQueUsers().forEach(sessionUser -> { + //finish non-leader + sessionUser.setResponseFinalised(true); + mcService.updateMcQueUsr(user); + + //copy answers from leader to non-leaders + mcService.copyAnswersFromLeader(sessionUser, session.getGroupLeader()); + }); + } return viewAnswers(mcLearningForm, request, response); } @@ -567,7 +575,7 @@ } mcGeneralLearnerFlowDTO.setMapFeedbackContent(mapFeedbackContent); - McQueUsr user = getCurrentUser(toolSessionID); + McQueUsr user = getSpecifiedUser(toolSessionID, mcLearningForm.getUserID().intValue()); Long toolContentUID = mcContent.getUid(); Index: lams_tool_lamc/web/learning/AnswersContent.jsp =================================================================== diff -u -r9ca39edffd0c57ba7deeb38316f5a0f7b0d8fe44 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_lamc/web/learning/AnswersContent.jsp (.../AnswersContent.jsp) (revision 9ca39edffd0c57ba7deeb38316f5a0f7b0d8fe44) +++ lams_tool_lamc/web/learning/AnswersContent.jsp (.../AnswersContent.jsp) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -37,17 +37,17 @@ //autoSaveAnswers if hasEditRight if (${hasEditRight}) { var interval = "30000"; // = 30 seconds - window.setInterval( - function(){ + window.setInterval(learnerAutosave, interval); + + function learnerAutosave(){ //ajax form submit $('#mcLearningForm').ajaxSubmit({ url: "" + new Date().getTime(), success: function() { $.growlUI(' '); } }); - }, interval - ); + } } function submitNextQuestionSelected() { Index: lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/qaApplicationContext.xml =================================================================== diff -u -rf49dd9f6b076828f464fc954bb9c3dc0c87e8b37 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/qaApplicationContext.xml (.../qaApplicationContext.xml) (revision f49dd9f6b076828f464fc954bb9c3dc0c87e8b37) +++ lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/qaApplicationContext.xml (.../qaApplicationContext.xml) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -100,6 +100,7 @@ PROPAGATION_REQUIRED,-QaApplicationException PROPAGATION_REQUIRED,-QaApplicationException PROPAGATION_REQUIRED,-QaApplicationException + PROPAGATION_REQUIRED, -QaApplicationException Index: lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/service/IQaService.java =================================================================== diff -u -rc4e8969c8dd14ae2428a6cfd6bb941dfe5cf479a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/service/IQaService.java (.../IQaService.java) (revision c4e8969c8dd14ae2428a6cfd6bb941dfe5cf479a) +++ lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/service/IQaService.java (.../IQaService.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -52,7 +52,7 @@ * @param toolSessionId * @return */ - boolean isUserGroupLeader(QaQueUsr user, Long toolSessionId); + boolean isUserGroupLeader(Long userId, Long toolSessionId); /** * Set specified user as a leader. Also the previous leader (if any) is marked as non-leader. @@ -125,8 +125,6 @@ void removeQuestion(QaQueContent qaQuestion); - void createUserResponse(QaUsrResp qaUsrResp); - void updateUserResponse(QaUsrResp resp); QaUsrResp getResponseById(Long responseId); @@ -201,6 +199,8 @@ Long createNotebookEntry(Long id, Integer idType, String signature, Integer userID, String entry); NotebookEntry getEntry(Long id, Integer idType, String signature, Integer userID); + + String finishToolSession(Long toolSessionID, Long userID); /** * Get the LAMS audit service. Needed as the web layer controls the staff updating of an answer, so the log entry Index: lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/service/QaService.java =================================================================== diff -u -re784910cb85086f0951f780d5dd64ed7a039ba5a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/service/QaService.java (.../QaService.java) (revision e784910cb85086f0951f780d5dd64ed7a039ba5a) +++ lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/service/QaService.java (.../QaService.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -119,12 +119,11 @@ private Random generator = new Random(); @Override - public boolean isUserGroupLeader(QaQueUsr user, Long toolSessionId) { - + public boolean isUserGroupLeader(Long userId, Long toolSessionId) { QaSession session = this.getSessionById(toolSessionId); QaQueUsr groupLeader = session.getGroupLeader(); - boolean isUserLeader = (groupLeader != null) && user.getUid().equals(groupLeader.getUid()); + boolean isUserLeader = (groupLeader != null) && userId.equals(groupLeader.getQueUsrId()); return isUserLeader; } @@ -149,7 +148,7 @@ User leaderDto = (User) getUserManagementService().findById(User.class, leaderUserId.intValue()); String userName = leaderDto.getLogin(); String fullName = leaderDto.getFirstName() + " " + leaderDto.getLastName(); - leader = new QaQueUsr(leaderUserId, userName, fullName, qaSession, new TreeSet()); + leader = new QaQueUsr(leaderUserId, userName, fullName, qaSession, new TreeSet<>()); qaQueUsrDAO.createUsr(user); } @@ -177,14 +176,14 @@ if (response == null) { response = new QaUsrResp(leaderResponse.getAnswer(), leaderResponse.getAnswerAutosaved(), leaderResponse.getAttemptTime(), "", question, user, true); - createUserResponse(response); + qaUsrRespDAO.createUserResponse(response); // if it's been changed by the leader } else if (leaderResponse.getAttemptTime().compareTo(response.getAttemptTime()) != 0) { response.setAnswer(leaderResponse.getAnswer()); response.setAttemptTime(leaderResponse.getAttemptTime()); response.setTimezone(""); - updateUserResponse(response); + qaUsrRespDAO.updateUserResponse(response); } } } @@ -321,7 +320,7 @@ response = isAutosave ? new QaUsrResp(null, newAnswer, new Date(System.currentTimeMillis()), "", question, user, true) : new QaUsrResp(newAnswer, null, new Date(System.currentTimeMillis()), "", question, user, true); - createUserResponse(response); + qaUsrRespDAO.createUserResponse(response); // if answer has changed } else if (!newAnswer.equals(response.getAnswer())) { @@ -334,16 +333,11 @@ response.setAttemptTime(new Date(System.currentTimeMillis())); response.setTimezone(""); - updateUserResponse(response); + qaUsrRespDAO.updateUserResponse(response); } } @Override - public void createUserResponse(QaUsrResp qaUsrResp) { - qaUsrRespDAO.createUserResponse(qaUsrResp); - } - - @Override public void updateUser(QaQueUsr qaQueUsr) { qaQueUsrDAO.updateUsr(qaQueUsr); } @@ -682,7 +676,6 @@ @Override public void createToolSession(Long toolSessionId, String toolSessionName, Long toolContentID) throws ToolException { - if (toolSessionId == null) { logger.error("toolSessionId is null"); throw new ToolException("toolSessionId is missing"); @@ -697,7 +690,7 @@ if (qaSession == null) { try { qaSession = new QaSession(toolSessionId, new Date(System.currentTimeMillis()), QaSession.INCOMPLETE, - toolSessionName, qaContent, new TreeSet()); + toolSessionName, qaContent, new TreeSet<>()); qaSessionDAO.createSession(qaSession); } catch (Exception e) { logger.error("Error creating new toolsession in the db"); @@ -730,7 +723,6 @@ @Override public String leaveToolSession(Long toolSessionId, Long learnerId) throws DataMissingException, ToolException { - if (toolSessionId == null) { logger.error("toolSessionId is null"); throw new DataMissingException("toolSessionId is missing"); @@ -752,7 +744,6 @@ } catch (DataAccessException e) { throw new ToolException("Exception occured when user is leaving tool session: " + e); } - } @Override @@ -847,30 +838,45 @@ } QaContent content = session.getQaContent(); - // copy answers only in case leader aware feature is ON - if (content.isUseSelectLeaderToolOuput()) { + QaQueUsr qaUser = getUserByIdAndSession(userId, toolSessionId); + // create user if he hasn't accessed this activity yet + if (qaUser == null) { + String userName = user.getLogin(); + String fullName = user.getFirstName() + " " + user.getLastName(); + qaUser = new QaQueUsr(userId, userName, fullName, session, new TreeSet<>()); + qaQueUsrDAO.createUsr(qaUser); + } - QaQueUsr qaUser = getUserByIdAndSession(userId, toolSessionId); - // create user if he hasn't accessed this activity yet - if (qaUser == null) { + //finalize the latest result, if it's still active + content.getQaQueContents().forEach(question -> { - String userName = user.getLogin(); - String fullName = user.getFirstName() + " " + user.getLastName(); - qaUser = new QaQueUsr(userId, userName, fullName, session, new TreeSet()); - qaQueUsrDAO.createUsr(qaUser); + QaUsrResp response = getResponseByUserAndQuestion(userId, question.getUid()); + if (response != null && response.getAnswer() == null && response.getAnswerAutosaved() != null) { + response.setAnswer(response.getAnswerAutosaved()); + response.setAnswerAutosaved(null); + qaUsrRespDAO.updateUserResponse(response); } + }); - QaQueUsr groupLeader = session.getGroupLeader(); + //if this is a leader finishes, complete all non-leaders as well, also copy leader results to them + QaQueUsr groupLeader = checkLeaderSelectToolForSessionLeader(qaUser, toolSessionId); + if (isUserGroupLeader(userId, toolSessionId)) { + session.getQaQueUsers().forEach(sessionUser -> { + //finish users + sessionUser.setResponseFinalized(true); + sessionUser.setLearnerFinished(true); + updateUser(sessionUser); - // check if leader has submitted answers - if ((groupLeader != null) && groupLeader.isResponseFinalized()) { + //copy answers from leader to non-leaders + copyAnswersFromLeader(sessionUser, groupLeader); + }); - // we need to make sure specified user has the same scratches as a leader - copyAnswersFromLeader(qaUser, groupLeader); - } - + } else { + //finish user + qaUser.setResponseFinalized(true); + qaUser.setLearnerFinished(true); + updateUser(qaUser); } - } @Override @@ -1108,6 +1114,32 @@ return new ToolCompletionStatus(ToolCompletionStatus.ACTIVITY_ATTEMPTED, startDate, null); } } + + @Override + public String finishToolSession(Long toolSessionID, Long userID) { + QaQueUsr user = getUserByIdAndSession(userID, toolSessionID); + user.setLearnerFinished(true); + updateUser(user); + + //if this is a leader finishes, complete all non-leaders as well, also copy leader results to them + QaSession session = user.getQaSession(); + QaQueUsr groupLeader = checkLeaderSelectToolForSessionLeader(user, toolSessionID); + if (isUserGroupLeader(userID, toolSessionID)) { + session.getQaQueUsers().forEach(sessionUser -> { + //finish users + sessionUser.setResponseFinalized(true); + sessionUser.setLearnerFinished(true); + updateUser(user); + + //copy answers from leader to non-leaders + copyAnswersFromLeader(sessionUser, groupLeader); + }); + } + + //return nextActivityUrl + return leaveToolSession(toolSessionID, userID); + } + // ****************** REST methods ************************* /** Index: lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/web/controller/QaLearningController.java =================================================================== diff -u -rc4e8969c8dd14ae2428a6cfd6bb941dfe5cf479a -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/web/controller/QaLearningController.java (.../QaLearningController.java) (revision c4e8969c8dd14ae2428a6cfd6bb941dfe5cf479a) +++ lams_tool_laqa/src/java/org/lamsfoundation/lams/tool/qa/web/controller/QaLearningController.java (.../QaLearningController.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -71,12 +71,14 @@ import org.lamsfoundation.lams.web.util.SessionMap; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.WebApplicationContext; import com.fasterxml.jackson.databind.node.ArrayNode; @@ -194,7 +196,7 @@ || (mode != null) && mode.equals(ToolAccessMode.TEACHER.toString())); sessionMap.put(ATTR_GROUP_LEADER, groupLeader); - boolean isUserLeader = qaService.isUserGroupLeader(user, new Long(toolSessionID)); + boolean isUserLeader = qaService.isUserGroupLeader(user.getQueUsrId(), new Long(toolSessionID)); boolean lockWhenFinished = qaContent.isLockWhenFinished(); sessionMap.put(ATTR_IS_USER_LEADER, isUserLeader); sessionMap.put(AttributeNames.ATTR_MODE, mode); @@ -392,10 +394,6 @@ /** * validates the learning mode parameters - * - * @param request - * @param mapping - * @return ActionForward */ protected void validateParameters(HttpServletRequest request, @ModelAttribute("qaLearningForm") QaLearningForm qaLearningForm) { @@ -443,14 +441,6 @@ /** * submits users responses - * - * @param mapping - * @param form - * @param request - * @param response - * @return - * @throws IOException - * @throws ServletException */ @RequestMapping("/submitAnswersContent") public String submitAnswersContent(@ModelAttribute("qaLearningForm") QaLearningForm qaLearningForm, @@ -578,17 +568,9 @@ /** * auto saves responses - * - * @param mapping - * @param form - * @param request - * @param response - * @return - * @throws IOException - * @throws ServletException */ @RequestMapping("/autoSaveAnswers") - @ResponseBody + @ResponseStatus(HttpStatus.OK) public void autoSaveAnswers(@ModelAttribute("qaLearningForm") QaLearningForm qaLearningForm, HttpServletRequest request) throws IOException, ServletException { String toolSessionID = request.getParameter(AttributeNames.PARAM_TOOL_SESSION_ID); @@ -627,14 +609,6 @@ /** * enables retaking the activity - * - * @param mapping - * @param form - * @param request - * @param response - * @return - * @throws IOException - * @throws ServletException */ @RequestMapping("/redoQuestions") public String redoQuestions(@ModelAttribute("qaLearningForm") QaLearningForm qaLearningForm, @@ -684,8 +658,6 @@ * answers screen, otherwise goes straight to the reflection screen (if any). * * @return Learner Report for a session - * @throws IOException - * @throws ServletException */ @RequestMapping("/storeAllResults") public String storeAllResults(@ModelAttribute("qaLearningForm") QaLearningForm qaLearningForm, @@ -864,12 +836,6 @@ /** * Get the answer from the form and copy into DTO. Set up the next question. If the current question is required and * the answer is blank, then just persist the error and don't change questions. - * - * @param form - * @param request - * @param generalLearnerFlowDTO - * @param getNextQuestion - * @return */ private Object[] storeSequentialAnswer(QaLearningForm qaLearningForm, HttpServletRequest request, GeneralLearnerFlowDTO generalLearnerFlowDTO, boolean getNextQuestion) { @@ -1031,7 +997,6 @@ public String endLearning(@ModelAttribute("qaLearningForm") QaLearningForm qaLearningForm, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, ToolException { - LearningUtil.saveFormRequestData(request, qaLearningForm); String toolSessionID = request.getParameter(AttributeNames.PARAM_TOOL_SESSION_ID); @@ -1040,44 +1005,22 @@ String userID = request.getParameter("userID"); qaLearningForm.setUserID(userID); - QaSession qaSession = qaService.getSessionById(new Long(toolSessionID).longValue()); - - QaQueUsr qaQueUsr = qaService.getUserByIdAndSession(new Long(userID), qaSession.getQaSessionId()); - qaQueUsr.setLearnerFinished(true); - qaService.updateUser(qaQueUsr); - /* * The learner is done with the tool session. The tool needs to clean-up. */ HttpSession ss = SessionManager.getSession(); /* get back login user DTO */ UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); - qaSession.setSession_end_date(new Date(System.currentTimeMillis())); - qaSession.setSession_status(QaAppConstants.COMPLETED); - qaService.updateSession(qaSession); - String sessionMapID = qaLearningForm.getSessionMapID(); // request.getSession().removeAttribute(sessionMapID); qaLearningForm.setSessionMapID(sessionMapID); - - String nextActivityUrl = qaService.leaveToolSession(new Long(toolSessionID), - new Long(user.getUserID().longValue())); + String nextActivityUrl = qaService.finishToolSession(Long.valueOf(toolSessionID), user.getUserID().longValue()); response.sendRedirect(nextActivityUrl); return null; } - /** - * - * @param qaLearningForm - * @param request - * @param response - * @return - * @throws IOException - * @throws ServletException - * @throws ToolException - */ @RequestMapping("/submitReflection") public String submitReflection(@ModelAttribute("qaLearningForm") QaLearningForm qaLearningForm, HttpServletRequest request, HttpServletResponse response) @@ -1118,15 +1061,6 @@ return endLearning(qaLearningForm, request, response); } - /** - * - * @param qaLearningForm - * @param request - * @return - * @throws IOException - * @throws ServletException - * @throws ToolException - */ @RequestMapping("/forwardtoReflection") public String forwardtoReflection(@ModelAttribute("qaLearningForm") QaLearningForm qaLearningForm, HttpServletRequest request) throws IOException, ServletException, ToolException { Index: lams_tool_laqa/web/learning/AnswersContent.jsp =================================================================== diff -u -rb4eeced9a946a0a62d6b51698addd3db798c0498 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_laqa/web/learning/AnswersContent.jsp (.../AnswersContent.jsp) (revision b4eeced9a946a0a62d6b51698addd3db798c0498) +++ lams_tool_laqa/web/learning/AnswersContent.jsp (.../AnswersContent.jsp) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -120,26 +120,26 @@ if (${hasEditRight}) { var interval = "30000"; // = 30 seconds - window.setInterval( - function(){ - //fire onchange event for textareas/ckeditors - if (${generalLearnerFlowDTO.allowRichEditor}) { - for ( instance in CKEDITOR.instances ) { - CKEDITOR.instances[instance].updateElement(); - } - } else { - $("textarea[name$=__textarea]").change(); - } - - //ajax form submit - $('#qaLearningForm').ajaxSubmit({ - url: '' + new Date().getTime(), - success: function() { - $.growlUI(' '); - } - }); - }, interval - ); + window.setInterval(learnerAutosave, interval); + + function learnerAutosave(){ + //fire onchange event for textareas/ckeditors + if (${generalLearnerFlowDTO.allowRichEditor}) { + for ( instance in CKEDITOR.instances ) { + CKEDITOR.instances[instance].updateElement(); + } + } else { + $("textarea[name$=__textarea]").change(); + } + + //ajax form submit + $('#qaLearningForm').ajaxSubmit({ + url: '' + new Date().getTime(), + success: function() { + $.growlUI(' '); + } + }); + } } //min words counter Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java =================================================================== diff -u -r40f2ba218df565d53c58eb97ca60c409887faa63 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision 40f2ba218df565d53c58eb97ca60c409887faa63) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -302,6 +302,14 @@ } @Override + public boolean isUserGroupLeader(Long userId, Long toolSessionId) { + ScratchieSession session = getScratchieSessionBySessionId(toolSessionId); + ScratchieUser groupLeader = session.getGroupLeader(); + + return (groupLeader != null) && userId.equals(groupLeader.getUserId()); + } + + @Override public ScratchieUser checkLeaderSelectToolForSessionLeader(ScratchieUser user, Long toolSessionId) { if ((user == null) || (toolSessionId == null)) { return null; @@ -379,7 +387,7 @@ List users = this.getUsersBySession(sessionId); for (ScratchieUser user : users) { - toolService.updateActivityMark(new Double(newMark), null, user.getUserId().intValue(), + toolService.updateActivityMark(newMark.doubleValue(), null, user.getUserId().intValue(), user.getSession().getSessionId(), false); // record mark change with audit service @@ -481,7 +489,7 @@ if (isPropagateToGradebook) { List users = getUsersBySession(sessionId); for (ScratchieUser user : users) { - toolService.updateActivityMark(new Double(mark), null, user.getUserId().intValue(), + toolService.updateActivityMark(Double.valueOf(mark), null, user.getUserId().intValue(), user.getSession().getSessionId(), false); } } @@ -614,6 +622,19 @@ user.setSessionFinished(true); scratchieUserDao.saveObject(user); + //if this is a leader finishes, complete all non-leaders as well + boolean isUserGroupLeader = user.getSession().isUserGroupLeader(user.getUid()); + if (isUserGroupLeader) { + getUsersBySession(toolSessionId).forEach(sessionUser -> { + //finish non-leader + sessionUser.setSessionFinished(true); + scratchieUserDao.saveObject(user); + + // as long as there is no individual results in Scratchie tool (but rather one for entire group) there is no + // need to copyAnswersFromLeader() + }); + } + nextUrl = this.leaveToolSession(toolSessionId, userId); } catch (DataMissingException e) { throw new ScratchieApplicationException(e); @@ -1306,7 +1327,7 @@ } row.addCell(isFirstChoice, color); } - row.addCell(new Integer(numberOfFirstChoiceEvents)); + row.addCell(Integer.valueOf(numberOfFirstChoiceEvents)); double percentage = (numberOfItems == 0) ? 0 : (double) numberOfFirstChoiceEvents / numberOfItems; row.addPercentageCell(percentage); } @@ -1369,7 +1390,7 @@ } row.addCell(itemDto.getOptionsSequence(), color); } - row.addCell(new Integer(numberOfFirstChoiceEvents)); + row.addCell(Integer.valueOf(numberOfFirstChoiceEvents)); double percentage = (numberOfItems == 0) ? 0 : (double) numberOfFirstChoiceEvents / numberOfItems; row.addPercentageCell(percentage); @@ -1461,8 +1482,8 @@ color = IndexedColors.RED; } row.addCell(isFirstChoice, color); - row.addCell(new Long(attempts), color); - Long mark = (itemDto.getUserMark() == -1) ? null : new Long(itemDto.getUserMark()); + row.addCell(Integer.valueOf(attempts), color); + Long mark = (itemDto.getUserMark() == -1) ? null : Long.valueOf(itemDto.getUserMark()); row.addCell(mark); } } @@ -1486,8 +1507,8 @@ for (ScratchieUser user : summary.getUsers()) { row = researchAndAnalysisSheet.initRow(); row.addCell(user.getFirstName() + " " + user.getLastName()); - row.addCell(new Long(summary.getTotalAttempts())); - Long mark = (summary.getTotalAttempts() == 0) ? null : new Long(summary.getMark()); + row.addCell(Long.valueOf(summary.getTotalAttempts())); + Long mark = (summary.getTotalAttempts() == 0) ? null : Long.valueOf(summary.getMark()); row.addCell(mark); row.addCell(summary.getSessionName()); } @@ -1539,7 +1560,7 @@ row.addCell(answerTitle, color); for (int numberAttempts : optionDto.getAttempts()) { - row.addCell(new Long(numberAttempts)); + row.addCell(Integer.valueOf(numberAttempts)); } } researchAndAnalysisSheet.addEmptyRow(); @@ -1562,7 +1583,7 @@ row = researchAndAnalysisSheet.initRow(); row.addEmptyCell(); for (int i = 0; i < optionDtos.size(); i++) { - row.addCell(new Long(i + 1)); + row.addCell(Integer.valueOf(i + 1)); } for (OptionDTO optionDto : optionDtos) { @@ -1575,7 +1596,7 @@ row.addCell(optionTitle); for (int numberAttempts : optionDto.getAttempts()) { - row.addCell(new Long(numberAttempts)); + row.addCell(Integer.valueOf(numberAttempts)); } } @@ -1604,7 +1625,7 @@ Long attempts = (long) scratchieAnswerVisitDao.getLogCountTotal(sessionId); row.addCell(attempts); row.addCell(getMessage("label.mark") + ":"); - row.addCell(new Long(session.getMark())); + row.addCell(Long.valueOf(session.getMark())); row = researchAndAnalysisSheet.initRow(); row.addCell(getMessage("label.team.leader") + session.getSessionName()); @@ -1619,7 +1640,7 @@ item.getUid()); for (ScratchieAnswerVisitLog log : logs) { row = researchAndAnalysisSheet.initRow(); - row.addCell(new Long(i++)); + row.addCell(Integer.valueOf(i++)); String answerDescr = removeHtmlMarkup(log.getQbOption().getName()); row.addCell(answerDescr); row.addCell(fullDateFormat.format(log.getAccessDate())); @@ -1682,7 +1703,7 @@ // group name row.addCell(summary.getSessionName()); // question number - row.addCell(new Long(questionCount++)); + row.addCell(Integer.valueOf(questionCount++)); // question title row.addCell(itemDto.getTitle()); @@ -1709,9 +1730,9 @@ } row.addCell(isFirstChoice); // attempts - row.addCell(new Long(attempts)); + row.addCell(Integer.valueOf(attempts)); // mark - Object mark = (itemDto.getUserMark() == -1) ? "" : new Long(itemDto.getUserMark()); + Object mark = (itemDto.getUserMark() == -1) ? "" : Long.valueOf(itemDto.getUserMark()); row.addCell(mark); // options selected @@ -2305,17 +2326,31 @@ return; } - // as long as leader aware feature is always ON - copy answers from leader to non-leader user - ScratchieUser scratchieUser = scratchieUserDao.getUserByUserIDAndSessionID(userId, toolSessionId); // create user if he hasn't accessed this activity yet if (scratchieUser == null) { scratchieUser = new ScratchieUser(user.getUserDTO(), session); createUser(scratchieUser); } - // as long as there is no individual results in Scratchie tool (but rather one for entire group) there is no - // need to copyAnswersFromLeader() + checkLeaderSelectToolForSessionLeader(scratchieUser, toolSessionId); + //if this is a leader finishes, complete all non-leaders as well + boolean isUserGroupLeader = session.isUserGroupLeader(scratchieUser.getUid()); + if (isUserGroupLeader) { + getUsersBySession(toolSessionId).forEach(sessionUser -> { + //finish users + sessionUser.setSessionFinished(true); + scratchieUserDao.saveObject(user); + + // as long as there is no individual results in Scratchie tool (but rather one for entire group) there is no + // need to copyAnswersFromLeader() + }); + + } else { + //finish user + scratchieUser.setSessionFinished(true); + scratchieUserDao.saveObject(scratchieUser); + } } /* =================================================================================== */ Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningController.java =================================================================== diff -u -r8d982bb83bb4040e0eba0076df8ab05ff715f2e9 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningController.java (.../LearningController.java) (revision 8d982bb83bb4040e0eba0076df8ab05ff715f2e9) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/controller/LearningController.java (.../LearningController.java) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -100,32 +100,31 @@ * Read scratchie data from database and put them into HttpSession. */ @RequestMapping("/start") - public String start(HttpServletRequest request, HttpServletResponse response) + private String start(HttpServletRequest request, HttpServletResponse response, @RequestParam Long toolSessionID) throws ScratchieApplicationException { ToolAccessMode mode = WebUtil.readToolAccessModeParam(request, AttributeNames.PARAM_MODE, true); - final Long toolSessionId = new Long(request.getParameter(ScratchieConstants.PARAM_TOOL_SESSION_ID)); - ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionId); + ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionID); // get back the scratchie and item list and display them on page - final Scratchie scratchie = scratchieService.getScratchieBySessionId(toolSessionId); + final Scratchie scratchie = scratchieService.getScratchieBySessionId(toolSessionID); boolean isReflectOnActivity = scratchie.isReflectOnActivity(); final ScratchieUser user; if ((mode != null) && mode.isTeacher()) { // monitoring mode - user is specified in URL // scratchieUser may be null if the user was force completed. - user = getSpecifiedUser(toolSessionId, WebUtil.readIntParam(request, AttributeNames.PARAM_USER_ID, false)); + user = getSpecifiedUser(toolSessionID, WebUtil.readIntParam(request, AttributeNames.PARAM_USER_ID, false)); } else { - user = getCurrentUser(toolSessionId); + user = getCurrentUser(toolSessionID); } - ScratchieUser groupLeader = scratchieService.checkLeaderSelectToolForSessionLeader(user, toolSessionId); + ScratchieUser groupLeader = scratchieService.checkLeaderSelectToolForSessionLeader(user, toolSessionID); // forwards to the leaderSelection page if (groupLeader == null) { // get group users and store it to request as DTO objects - List groupUsers = scratchieService.getUsersBySession(toolSessionId); + List groupUsers = scratchieService.getUsersBySession(toolSessionID); List groupUserDtos = new ArrayList<>(); for (ScratchieUser groupUser : groupUsers) { User groupUserDto = new User(); @@ -134,7 +133,7 @@ groupUserDtos.add(groupUserDto); } request.setAttribute(ScratchieConstants.ATTR_GROUP_USERS, groupUserDtos); - request.setAttribute(ScratchieConstants.PARAM_TOOL_SESSION_ID, toolSessionId); + request.setAttribute(ScratchieConstants.PARAM_TOOL_SESSION_ID, toolSessionID); request.setAttribute(ScratchieConstants.ATTR_SCRATCHIE, scratchie); request.setAttribute(AttributeNames.ATTR_MODE, mode); return "pages/learning/waitforleader"; @@ -148,7 +147,7 @@ // get notebook entry NotebookEntry notebookEntry = null; if (isReflectOnActivity && (groupLeader != null)) { - notebookEntry = scratchieService.getEntry(toolSessionId, CoreNotebookConstants.NOTEBOOK_TOOL, + notebookEntry = scratchieService.getEntry(toolSessionID, CoreNotebookConstants.NOTEBOOK_TOOL, ScratchieConstants.TOOL_SIGNATURE, groupLeader.getUserId().intValue()); } String entryText = (notebookEntry == null) ? null : notebookEntry.getEntry(); @@ -165,7 +164,7 @@ sessionMap.put(ScratchieConstants.ATTR_IS_USER_LEADER, isUserLeader); boolean isUserFinished = (user != null) && user.isSessionFinished(); sessionMap.put(ScratchieConstants.ATTR_USER_FINISHED, isUserFinished); - sessionMap.put(AttributeNames.PARAM_TOOL_SESSION_ID, toolSessionId); + sessionMap.put(AttributeNames.PARAM_TOOL_SESSION_ID, toolSessionID); sessionMap.put(AttributeNames.ATTR_MODE, mode); sessionMap.put(ScratchieConstants.ATTR_IS_BURNING_QUESTIONS_ENABLED, scratchie.isBurningQuestionsEnabled()); // reflection information @@ -188,7 +187,7 @@ return "pages/learning/definelater"; } - sessionMap.put(AttributeNames.ATTR_IS_LAST_ACTIVITY, scratchieService.isLastActivity(toolSessionId)); + sessionMap.put(AttributeNames.ATTR_IS_LAST_ACTIVITY, scratchieService.isLastActivity(toolSessionID)); // check if there is submission deadline Date submissionDeadline = scratchie.getSubmissionDeadline(); @@ -208,7 +207,7 @@ } } - storeItemsToSessionMap(toolSessionId, scratchie, sessionMap, mode.isTeacher()); + storeItemsToSessionMap(toolSessionID, scratchie, sessionMap, mode.isTeacher()); sessionMap.put(ScratchieConstants.ATTR_SCRATCHIE, scratchie); // calculate max score @@ -355,8 +354,8 @@ * Record in DB that leader has scratched specified option. And return whether scratchie option is correct or not. */ @RequestMapping("/recordItemScratched") - @ResponseBody - public String recordItemScratched(HttpServletRequest request, HttpServletResponse response) + @ResponseStatus(HttpStatus.OK) + private void recordItemScratched(HttpServletRequest request, HttpServletResponse response) throws IOException, ScratchieApplicationException { SessionMap sessionMap = getSessionMap(request); final Long toolSessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID); @@ -368,19 +367,19 @@ ScratchieUser leader = getCurrentUser(toolSessionId); // only leader is allowed to scratch options if (!toolSession.isUserGroupLeader(leader.getUid())) { - return null; + return; } // check option is belong to current session Set optionUids = (Set) sessionMap.get(ScratchieConstants.ATTR_OPTION_UIDS); if (!optionUids.contains(optionUid)) { - return null; + return; } // Return whether option is correct or not QbOption option = scratchieService.getQbOptionByUid(optionUid); if (option == null) { - return null; + return; } ObjectNode ObjectNode = JsonNodeFactory.instance.objectNode(); @@ -395,11 +394,8 @@ } }, "LAMS_recordItemScratched_thread"); recordItemScratchedThread.start(); - - response.setContentType("application/json;charset=utf-8"); - return ObjectNode.toString(); } - + /** * Record in DB that leader has provided this answer. And return whether it's correct or not. */ @@ -411,20 +407,20 @@ final Long toolSessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID); final Long itemUid = NumberUtils.createLong(request.getParameter(ScratchieConstants.PARAM_ITEM_UID)); final String answer = request.getParameter("answer"); - + ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionId); ScratchieUser leader = getCurrentUser(toolSessionId); ScratchieItem item = scratchieService.getScratchieItemByUid(itemUid); final boolean isCaseSensitive = item.getQbQuestion().isCaseSensitive(); - + // only leader is allowed to answer if (!toolSession.isUserGroupLeader(leader.getUid())) { return null; } - + // return whether option is correct or not boolean isAnswerCorrect = ScratchieServiceImpl.isItemUnraveledByAnswers(item, List.of(answer)); - + // return whether such answer was already logged (and also the answer hash which was logged previously) int loggedAnswerHash = -1; //1) search for the answer in logs stored in SessionMap @@ -449,7 +445,7 @@ loggedAnswerHash = log.getAnswer().hashCode(); } } - + ObjectNode objectNode = JsonNodeFactory.instance.objectNode(); objectNode.put("isAnswerCorrect", isAnswerCorrect); objectNode.put("loggedAnswerHash", loggedAnswerHash); @@ -475,15 +471,15 @@ String userAnswer = WebUtil.readStrParam(request, "term", true); ScratchieItem item = scratchieService.getScratchieItemByUid(itemUid); QbQuestion qbQuestion = item.getQbQuestion(); - + ArrayNode responseJSON = JsonNodeFactory.instance.arrayNode(); if (StringUtils.isNotBlank(userAnswer)) { userAnswer = userAnswer.trim(); userAnswer = qbQuestion.isCaseSensitive() ? userAnswer : userAnswer.toLowerCase(); - + for (QbOption option : qbQuestion.getQbOptions()) { - //filter out options not starting with 'term' and containing '*' + //filter out options not starting with 'term' and containing '*' String optionTitle = qbQuestion.isCaseSensitive() ? option.getName() : option.getName().toLowerCase(); int i = 0; for (String optionAnswer : optionTitle.split("\\r\\n")) { @@ -506,8 +502,7 @@ */ @RequestMapping("/launchTimeLimit") @ResponseStatus(HttpStatus.OK) - public void launchTimeLimit(HttpServletRequest request) - throws ScratchieApplicationException, SchedulerException { + private void launchTimeLimit(HttpServletRequest request) throws ScratchieApplicationException, SchedulerException { SessionMap sessionMap = getSessionMap(request); final Long toolSessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID); ScratchieSession toolSession = scratchieService.getScratchieSessionBySessionId(toolSessionId); @@ -596,22 +591,22 @@ */ @RequestMapping("/editBurningQuestion") @ResponseStatus(HttpStatus.OK) - public void editBurningQuestion(HttpServletRequest request) { - - if (!StringUtils.isEmpty(request.getParameter(ScratchieConstants.ATTR_ITEM_UID)) - && !StringUtils.isEmpty(request.getParameter(ScratchieConstants.PARAM_SESSION_ID))) { - - Long itemUid = WebUtil.readLongParam(request, ScratchieConstants.ATTR_ITEM_UID) == 0 ? null - : WebUtil.readLongParam(request, ScratchieConstants.ATTR_ITEM_UID); - Long sessionId = WebUtil.readLongParam(request, ScratchieConstants.PARAM_SESSION_ID); - String question = request.getParameter(ScratchieConstants.ATTR_BURNING_QUESTION_PREFIX); - scratchieService.saveBurningQuestion(sessionId, itemUid, question); + private void editBurningQuestion(HttpServletRequest request) { + if (StringUtils.isEmpty(request.getParameter(ScratchieConstants.ATTR_ITEM_UID)) + || StringUtils.isEmpty(request.getParameter(ScratchieConstants.PARAM_SESSION_ID))) { + return; } + + Long itemUid = WebUtil.readLongParam(request, ScratchieConstants.ATTR_ITEM_UID) == 0 ? null + : WebUtil.readLongParam(request, ScratchieConstants.ATTR_ITEM_UID); + Long sessionId = WebUtil.readLongParam(request, ScratchieConstants.PARAM_SESSION_ID); + String question = request.getParameter(ScratchieConstants.ATTR_BURNING_QUESTION_PREFIX); + scratchieService.saveBurningQuestion(sessionId, itemUid, question); } @RequestMapping("/like") - @ResponseBody - public String like(HttpServletRequest request, HttpServletResponse response) + @ResponseStatus(HttpStatus.OK) + private void like(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, ScratchieApplicationException { SessionMap sessionMap = getSessionMap(request); final Long sessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID); @@ -622,20 +617,20 @@ ScratchieUser leader = this.getCurrentUser(sessionId); // only leader is allowed to scratch options if (!toolSession.isUserGroupLeader(leader.getUid())) { - return null; + return; } boolean added = scratchieService.addLike(burningQuestionUid, sessionId); ObjectNode ObjectNode = JsonNodeFactory.instance.objectNode(); ObjectNode.put("added", added); response.setContentType("application/json;charset=utf-8"); - return ObjectNode.toString(); + response.getWriter().print(ObjectNode); } @RequestMapping("/removeLike") - @ResponseBody - public String removeLike(HttpServletRequest request, HttpServletResponse response) + @ResponseStatus(HttpStatus.OK) + private void removeLike(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException, ScratchieApplicationException { SessionMap sessionMap = getSessionMap(request); final Long sessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID); @@ -646,15 +641,15 @@ ScratchieUser leader = this.getCurrentUser(sessionId); // only leader is allowed to scratch options if (!toolSession.isUserGroupLeader(leader.getUid())) { - return null; + return; } - + scratchieService.removeLike(burningQuestionUid, sessionId); ObjectNode ObjectNode = JsonNodeFactory.instance.objectNode(); ObjectNode.put("added", true); response.setContentType("application/json;charset=utf-8"); - return ObjectNode.toString(); + response.getWriter().print(ObjectNode); } /** @@ -682,8 +677,10 @@ */ @RequestMapping("/autosaveBurningQuestions") @ResponseStatus(HttpStatus.OK) - public void autosaveBurningQuestions(HttpServletRequest request) throws ScratchieApplicationException { - SessionMap sessionMap = getSessionMap(request); + private void autosaveBurningQuestions(HttpServletRequest request) throws ScratchieApplicationException { + String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID); + SessionMap sessionMap = (SessionMap) request.getSession() + .getAttribute(sessionMapID); final Long sessionId = (Long) sessionMap.get(AttributeNames.PARAM_TOOL_SESSION_ID); // only leader is allowed to submit burning questions @@ -831,7 +828,7 @@ } return scratchieUser; } - + @SuppressWarnings("unchecked") private SessionMap getSessionMap(HttpServletRequest request) { String sessionMapID = WebUtil.readStrParam(request, ScratchieConstants.ATTR_SESSION_MAP_ID); Index: lams_tool_scratchie/web/pages/learning/learning.jsp =================================================================== diff -u -r8d982bb83bb4040e0eba0076df8ab05ff715f2e9 -r02ce40b60524aa33d326fbda824dcd43f566ab94 --- lams_tool_scratchie/web/pages/learning/learning.jsp (.../learning.jsp) (revision 8d982bb83bb4040e0eba0076df8ab05ff715f2e9) +++ lams_tool_scratchie/web/pages/learning/learning.jsp (.../learning.jsp) (revision 02ce40b60524aa33d326fbda824dcd43f566ab94) @@ -312,23 +312,22 @@ //autosave feature var autosaveInterval = "60000"; // 60 seconds interval - window.setInterval( - function(){ - if (isWaitingForConfirmation) return; - - //ajax form submit - $('#burning-questions').ajaxSubmit({ - url: "learning/autosaveBurningQuestions.do?sessionMapID=${sessionMapID}&date=" + new Date().getTime(), - success: function() { - $.jGrowl( - " ", - { life: 2000, closeTemplate: '' } - ); - } - }); - }, - autosaveInterval - ); + window.setInterval(learnerAutosave, autosaveInterval); + + function learnerAutosave(){ + if (isWaitingForConfirmation) return; + + //ajax form submit + $('#burning-questions').ajaxSubmit({ + url: "learning/autosaveBurningQuestions.do?sessionMapID=${sessionMapID}&date=" + new Date().getTime(), + success: function() { + $.jGrowl( + " ", + { life: 2000, closeTemplate: '' } + ); + } + }); + } function finish(isTimelimitExpired) {