Index: lams_monitoring/lams_monitoring.eml =================================================================== diff -u -r21c7529af602718ef4a963e75e902c964f986831 -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_monitoring/lams_monitoring.eml (.../lams_monitoring.eml) (revision 21c7529af602718ef4a963e75e902c964f986831) +++ lams_monitoring/lams_monitoring.eml (.../lams_monitoring.eml) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -1,17 +1,25 @@ - + + + + + + + + + Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentResultDAO.java =================================================================== diff -u -rb8e5c281efce81034ea2675a610ba3ea8faa41fb -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentResultDAO.java (.../AssessmentResultDAO.java) (revision b8e5c281efce81034ea2675a610ba3ea8faa41fb) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentResultDAO.java (.../AssessmentResultDAO.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -22,13 +22,13 @@ package org.lamsfoundation.lams.tool.assessment.dao; -import java.util.List; -import java.util.Map; - import org.lamsfoundation.lams.tool.assessment.dto.AssessmentUserDTO; import org.lamsfoundation.lams.tool.assessment.model.AssessmentResult; import org.lamsfoundation.lams.tool.assessment.model.AssessmentUser; +import java.util.List; +import java.util.Map; + public interface AssessmentResultDAO extends DAO { List getAssessmentResults(Long assessmentUid, Long userId); @@ -81,8 +81,6 @@ */ List getLastFinishedAssessmentResults(Long contentId); - int countLastFinishedAssessmentResults(long contentId); - List getLastFinishedAssessmentResultsBySession(Long sessionId); /** @@ -107,5 +105,7 @@ */ int countAttemptsPerOption(Long toolContentId, Long optionUid, boolean finishedAttemptsOnly); - Map> getAnsweredQuestionsByUsers(long toolContentId); + Map> getAnsweredQuestionsByUsersForCompletionChart(long toolContentId); + + List getLearnersWithFinishedCurrentAttemptForCompletionChart(Long contentId); } \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentUserDAO.java =================================================================== diff -u -r6cda19702eb2790fece3c55bddb223147417af5d -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentUserDAO.java (.../AssessmentUserDAO.java) (revision 6cda19702eb2790fece3c55bddb223147417af5d) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/AssessmentUserDAO.java (.../AssessmentUserDAO.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -23,12 +23,12 @@ package org.lamsfoundation.lams.tool.assessment.dao; -import java.util.List; - import org.lamsfoundation.lams.tool.assessment.dto.AssessmentUserDTO; import org.lamsfoundation.lams.tool.assessment.model.AssessmentUser; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import java.util.List; + public interface AssessmentUserDAO extends DAO { AssessmentUser getUserByUserIDAndSessionID(Long userID, Long sessionId); @@ -46,8 +46,12 @@ int getCountUsersBySession(Long sessionId, String searchString); - int getCountUsersByContentId(Long contentId); + int getCountLearnersByContentId(Long contentId); + List getLearnersByContentId(Long contentId); + + List getLearnersByContentIdForCompletionChart(Long contentId); + List getPagedUsersBySessionAndQuestion(Long sessionId, Long questionUid, int page, int size, String sortBy, String sortOrder, String searchString, IUserManagementService userManagementService); Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java =================================================================== diff -u -rf0ff6bc30e8fa7e5d4201b7b9571194845ad3a83 -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision f0ff6bc30e8fa7e5d4201b7b9571194845ad3a83) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -22,11 +22,6 @@ package org.lamsfoundation.lams.tool.assessment.dao.hibernate; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.apache.commons.lang.StringUtils; import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; @@ -45,27 +40,32 @@ import org.lamsfoundation.lams.usermanagement.User; import org.springframework.stereotype.Repository; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + @Repository public class AssessmentResultDAOHibernate extends LAMSBaseDAO implements AssessmentResultDAO { - private static final String FIND_LAST_BY_ASSESSMENT = "FROM " + AssessmentResult.class.getName() - + " AS r WHERE r.assessment.uid=:assessmentUid AND r.latest=1"; + private static final String FIND_LAST_BY_ASSESSMENT = + "FROM " + AssessmentResult.class.getName() + " AS r WHERE r.assessment.uid=:assessmentUid AND r.latest=1"; 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 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_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"; private static final String FIND_LAST_FINISHED_BY_ASSESSMENT_AND_USER = "FROM " + AssessmentResult.class.getName() + " AS r WHERE r.user.userId = :userId AND r.assessment.uid=:assessmentUid AND (r.finishDate != null) AND r.latest=1"; - private static final String FIND_BY_SESSION_AND_USER = "FROM " + AssessmentResult.class.getName() - + " AS r WHERE r.user.userId = ? AND r.sessionId=?"; + private static final String FIND_BY_SESSION_AND_USER = + "FROM " + AssessmentResult.class.getName() + " AS r WHERE r.user.userId = ? AND r.sessionId=?"; private static final String FIND_BY_SESSION_AND_USER_AND_FINISHED = "FROM " + AssessmentResult.class.getName() + " AS r WHERE r.user.userId = ? AND r.sessionId=? AND (r.finishDate != null) ORDER BY r.startDate ASC"; @@ -76,73 +76,101 @@ private static final String FIND_LAST_FINISHED_RESULTS_BY_CONTENT_ID = "FROM " + AssessmentResult.class.getName() + " AS r WHERE r.assessment.contentId = :contentId AND (r.finishDate != null) AND r.latest = 1"; - private static final String FIND_ASSESSMENT_RESULT_COUNT_BY_ASSESSMENT_AND_USER = "select COUNT(*) FROM " - + AssessmentResult.class.getName() - + " AS r WHERE r.user.userId=? AND r.assessment.uid=? AND (r.finishDate != null)"; + private static final String FIND_ASSESSMENT_RESULT_COUNT_BY_ASSESSMENT_AND_USER = + "select COUNT(*) FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.user.userId=? AND r.assessment.uid=? AND (r.finishDate != null)"; - private static final String IS_ASSESSMENT_RESULT_EXIST_BY_ASSESSMENT = "select COUNT(*) > 0 FROM " - + AssessmentResult.class.getName() + " AS r WHERE r.assessment.uid=:assessmentUid"; + private static final String IS_ASSESSMENT_RESULT_EXIST_BY_ASSESSMENT = + "select COUNT(*) > 0 FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.assessment.uid=:assessmentUid"; private static final String LAST_ASSESSMENT_RESULT_GRADE = "select r.grade FROM " + AssessmentResult.class.getName() + " AS r WHERE r.user.userId=:userId AND r.assessment.uid=:assessmentUid AND (r.finishDate != null) AND r.latest=1"; - private static final String LAST_ASSESSMENT_RESULT_GRADES_BY_CONTENT_ID = "select r.user.userId, r.grade FROM " - + AssessmentResult.class.getName() - + " AS r WHERE r.assessment.contentId=? AND (r.finishDate != null) AND r.latest=1"; + private static final String LAST_ASSESSMENT_RESULT_GRADES_BY_CONTENT_ID = + "select r.user.userId, r.grade FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.assessment.contentId=? AND (r.finishDate != null) AND r.latest=1"; - private static final String BEST_SCORE_BY_SESSION_AND_USER = "SELECT MAX(r.grade) FROM " - + AssessmentResult.class.getName() - + " AS r WHERE r.user.userId = :userId AND r.sessionId=:sessionId AND (r.finishDate != null)"; + private static final String BEST_SCORE_BY_SESSION_AND_USER = + "SELECT MAX(r.grade) FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.user.userId = :userId AND r.sessionId=:sessionId AND (r.finishDate != null)"; - private static final String BEST_SCORES_BY_CONTENT_ID = "SELECT r.user.userId, MAX(r.grade) FROM " - + AssessmentResult.class.getName() - + " AS r WHERE r.assessment.contentId=? AND (r.finishDate != null) GROUP BY r.user.userId"; + private static final String BEST_SCORES_BY_CONTENT_ID = + "SELECT r.user.userId, MAX(r.grade) FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.assessment.contentId=? AND (r.finishDate != null) GROUP BY r.user.userId"; - private static final String FIRST_SCORE_BY_SESSION_AND_USER = "SELECT r.grade FROM " - + AssessmentResult.class.getName() - + " AS r WHERE r.user.userId = :userId AND r.sessionId=:sessionId AND (r.finishDate != null) ORDER BY r.startDate ASC"; + private static final String FIRST_SCORE_BY_SESSION_AND_USER = + "SELECT r.grade FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.user.userId = :userId AND r.sessionId=:sessionId AND (r.finishDate != null) ORDER BY r.startDate ASC"; - private static final String AVERAGE_SCORE_BY_SESSION_AND_USER = "SELECT AVG(r.grade) FROM " - + AssessmentResult.class.getName() - + " AS r WHERE r.user.userId = :userId AND r.sessionId=:sessionId AND (r.finishDate != null)"; + private static final String AVERAGE_SCORE_BY_SESSION_AND_USER = + "SELECT AVG(r.grade) FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.user.userId = :userId AND r.sessionId=:sessionId AND (r.finishDate != null)"; - private static final String AVERAGE_SCORES_BY_CONTENT_ID = "SELECT r.user.userId, AVG(r.grade) FROM " - + AssessmentResult.class.getName() - + " AS r WHERE r.assessment.contentId=? AND (r.finishDate != null) GROUP BY r.user.userId"; + private static final String AVERAGE_SCORES_BY_CONTENT_ID = + "SELECT r.user.userId, AVG(r.grade) FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.assessment.contentId=? AND (r.finishDate != null) GROUP BY r.user.userId"; - private static final String FIND_LAST_ASSESSMENT_RESULT_TIME_TAKEN = "select UNIX_TIMESTAMP(r.finishDate) - UNIX_TIMESTAMP(r.startDate) FROM " - + AssessmentResult.class.getName() - + " AS r WHERE r.user.userId=? AND r.assessment.uid=? AND (r.finishDate != null) AND r.latest=1"; + private static final String FIND_LAST_ASSESSMENT_RESULT_TIME_TAKEN = + "select UNIX_TIMESTAMP(r.finishDate) - UNIX_TIMESTAMP(r.startDate) FROM " + AssessmentResult.class.getName() + + " AS r WHERE r.user.userId=? AND r.assessment.uid=? AND (r.finishDate != null) AND r.latest=1"; private static final String FIND_BY_UID = "FROM " + AssessmentResult.class.getName() + " AS r WHERE r.uid = ?"; - private static final String ANSWERED_QUESTIONS_BY_USER = "SELECT user_id, portrait_uuid, user_name, group_name, SUM(IF(" - + " (type = 1 AND answer_boolean = 1) OR" - + " (type = 2 AND answer_int <> -1) OR" - + " ((type BETWEEN 3 AND 6) AND (answer IS NOT NULL AND TRIM(answer) <> '')) OR" - + " (type = 7 AND mark > 0) OR " - + " (type = 8 AND answer_int > 0)" - + " ,1, 0)) AS answered_question_count FROM" - + " (SELECT u.user_id, BIN_TO_UUID(u.portrait_uuid) AS portrait_uuid, CONCAT(u.first_name, ' ', u.last_name) AS user_name," - + " IF(a.use_select_leader_tool_ouput, s.session_name, NULL) AS group_name, qbq.type, qr.mark, qbta.answer, oa.answer_boolean, oa.answer_int" - + " FROM tl_laasse10_assessment AS a" - + " JOIN tl_laasse10_assessment_result AS ar ON a.uid = ar.assessment_uid" - + " JOIN tl_laasse10_user AS au ON ar.user_uid = au.uid" - + " JOIN lams_user AS u USING (user_id)" - + " JOIN tl_laasse10_session AS s USING (session_id)" - + " JOIN tl_laasse10_question_result AS qr ON ar.uid = qr.result_uid" - + " LEFT JOIN lams_qb_tool_answer AS qbta ON qbta.answer_uid = qr.uid" - + " LEFT JOIN lams_qb_tool_question AS qbtq USING (tool_question_uid)" - + " LEFT JOIN lams_qb_question AS qbq ON qbq.uid = qbtq.qb_question_uid" - + " LEFT JOIN tl_laasse10_option_answer AS oa" - + " ON oa.question_result_uid = qr.uid" - + " AND ((qbq.type = 7 AND qr.mark > 0) OR (qbta.answer IS NOT NULL AND TRIM(qbta.answer) <> '')" - + " OR oa.answer_boolean IS NULL OR oa.answer_boolean = 1 OR (qbq.type = 2 AND answer_int <> -1))" - + " WHERE ar.latest = 1" - + " AND (a.use_select_leader_tool_ouput = 0 OR s.group_leader_uid = ar.user_uid)" - + " AND a.content_id = :toolContentId" - + " GROUP BY qr.uid, u.user_id) AS answered_questions GROUP BY user_id ORDER BY user_name"; + private static final String ANSWERED_QUESTIONS_BY_USER = + "SELECT user_id, portrait_uuid, user_name, group_name, SUM(IF((type = 1 AND answer_boolean = 1) OR" + + " (type = 2 AND answer_int <> -1) OR" + + " ((type BETWEEN 3 AND 6) AND (answer IS NOT NULL AND TRIM(answer) <> '')) OR" + + " (type = 7 AND mark > 0) OR " + + " (type = 8 AND answer_int > 0)" + + " ,1, 0)) AS answered_question_count FROM" + + " (SELECT u.user_id, BIN_TO_UUID(u.portrait_uuid) AS portrait_uuid, CONCAT(u.first_name, ' ', u.last_name) AS user_name," + + " IF(a.use_select_leader_tool_ouput, s.session_name, NULL) AS group_name, qbq.type, qr.mark, qbta.answer, oa.answer_boolean, oa.answer_int" + + " FROM tl_laasse10_assessment AS a" + + " JOIN tl_laasse10_assessment_result AS ar ON a.uid = ar.assessment_uid" + + " JOIN tl_laasse10_user AS au ON ar.user_uid = au.uid" + + " JOIN lams_user AS u USING (user_id)" + + " JOIN tl_laasse10_session AS s USING (session_id)" + + " JOIN tl_laasse10_question_result AS qr ON ar.uid = qr.result_uid" + + " LEFT JOIN lams_qb_tool_answer AS qbta ON qbta.answer_uid = qr.uid" + + " LEFT JOIN lams_qb_tool_question AS qbtq USING (tool_question_uid)" + + " LEFT JOIN lams_qb_question AS qbq ON qbq.uid = qbtq.qb_question_uid" + + " LEFT JOIN tl_laasse10_option_answer AS oa" + + " ON oa.question_result_uid = qr.uid" + + " AND ((qbq.type = 7 AND qr.mark > 0) OR (qbta.answer IS NOT NULL AND TRIM(qbta.answer) <> '')" + + " OR oa.answer_boolean IS NULL OR oa.answer_boolean = 1 OR (qbq.type = 2 AND answer_int <> -1))" + + " WHERE ar.latest = 1" + + " AND (a.use_select_leader_tool_ouput = 0 OR s.group_leader_uid = ar.user_uid)" + + " AND a.content_id = :toolContentId" + + " GROUP BY qr.uid, u.user_id) AS answered_questions GROUP BY user_id ORDER BY user_name"; + private static final String FINISHED_LEARNERS_FOR_COMPLETION_CHART = + "SELECT user_id, portrait_uuid, user_name, group_name, SUM(IF((type = 1 AND answer_boolean = 1) OR" + + " (type = 2 AND answer_int <> -1) OR" + + " ((type BETWEEN 3 AND 6) AND (answer IS NOT NULL AND TRIM(answer) <> '')) OR" + + " (type = 7 AND mark > 0) OR " + + " (type = 8 AND answer_int > 0)" + + " ,1, 0)) AS answered_question_count FROM" + + " (SELECT u.user_id, BIN_TO_UUID(u.portrait_uuid) AS portrait_uuid, CONCAT(u.first_name, ' ', u.last_name) AS user_name," + + " IF(a.use_select_leader_tool_ouput, s.session_name, NULL) AS group_name, qbq.type, qr.mark, qbta.answer, oa.answer_boolean, oa.answer_int" + + " FROM tl_laasse10_assessment AS a" + + " JOIN tl_laasse10_assessment_result AS ar ON a.uid = ar.assessment_uid" + + " JOIN tl_laasse10_user AS au ON ar.user_uid = au.uid" + + " JOIN lams_user AS u USING (user_id)" + + " JOIN tl_laasse10_session AS s USING (session_id)" + + " JOIN tl_laasse10_question_result AS qr ON ar.uid = qr.result_uid" + + " LEFT JOIN lams_qb_tool_answer AS qbta ON qbta.answer_uid = qr.uid" + + " LEFT JOIN lams_qb_tool_question AS qbtq USING (tool_question_uid)" + + " LEFT JOIN lams_qb_question AS qbq ON qbq.uid = qbtq.qb_question_uid" + + " LEFT JOIN tl_laasse10_option_answer AS oa" + + " ON oa.question_result_uid = qr.uid" + + " AND ((qbq.type = 7 AND qr.mark > 0) OR (qbta.answer IS NOT NULL AND TRIM(qbta.answer) <> '')" + + " OR oa.answer_boolean IS NULL OR oa.answer_boolean = 1 OR (qbq.type = 2 AND answer_int <> -1))" + + " WHERE ar.latest = 1" + + " AND (a.use_select_leader_tool_ouput = 0 OR s.group_leader_uid = ar.user_uid)" + + " AND a.content_id = :toolContentId" + + " GROUP BY qr.uid, u.user_id) AS answered_questions GROUP BY user_id ORDER BY user_name"; + @Override @SuppressWarnings("unchecked") public List getAssessmentResults(Long assessmentUid, Long userId) { @@ -170,13 +198,15 @@ + " AS a JOIN a.learningDesign.lessons AS l " + "WHERE qr.assessmentResult.uid = r.uid AND a.toolContentId = r.assessment.contentId " + "AND (l.organisation.organisationId = :organisationId OR " - + " l.organisation.parentOrganisation.organisationId = :organisationId" - + (parentOrganisation == null ? "" + + " l.organisation.parentOrganisation.organisationId = :organisationId" + ( + parentOrganisation == null + ? "" : " OR l.organisation.organisationId = :parentOrganisationId OR " + "l.organisation.parentOrganisation.organisationId = :parentOrganisationId") - + ") AND qr.qbToolQuestion.qbQuestion.uid = :qbQuestionUid AND " - + (qbToolQuestion.getQbQuestion().isExactMatch() ? "TRIM(qr.answer)" - : "REGEXP_REPLACE(qr.answer, '" + QbUtils.VSA_ANSWER_NORMALISE_SQL_REG_EXP + "', '')") + + ") AND qr.qbToolQuestion.qbQuestion.uid = :qbQuestionUid AND " + (qbToolQuestion.getQbQuestion() + .isExactMatch() + ? "TRIM(qr.answer)" + : "REGEXP_REPLACE(qr.answer, '" + QbUtils.VSA_ANSWER_NORMALISE_SQL_REG_EXP + "', '')") + " = :answer ORDER BY r.startDate ASC"; Query q = getSession().createQuery(FIND_BY_QBQUESTION, AssessmentResult.class); @@ -272,12 +302,12 @@ @Override public List getFirstTotalScoresByContentId(Long toolContentId) { - final String FIRST_SCORES_BY_CONTENT_ID = "SELECT user.user_id, res.grade " - + "FROM tl_laasse10_assessment_result AS res " - + "JOIN tl_laasse10_user AS user ON res.user_uid = user.uid " - + "JOIN tl_laasse10_assessment AS assess ON res.assessment_uid = assess.uid AND assess.content_id = :contentId " - + "INNER JOIN (SELECT user_uid, MIN(start_date) AS startDate FROM tl_laasse10_assessment_result WHERE finish_date IS NOT NULL GROUP BY user_uid) firstRes " - + "ON (res.user_uid = firstRes.user_uid AND res.start_date = firstRes.startDate) GROUP BY res.user_uid"; + final String FIRST_SCORES_BY_CONTENT_ID = + "SELECT user.user_id, res.grade " + "FROM tl_laasse10_assessment_result AS res " + + "JOIN tl_laasse10_user AS user ON res.user_uid = user.uid " + + "JOIN tl_laasse10_assessment AS assess ON res.assessment_uid = assess.uid AND assess.content_id = :contentId " + + "INNER JOIN (SELECT user_uid, MIN(start_date) AS startDate FROM tl_laasse10_assessment_result WHERE finish_date IS NOT NULL GROUP BY user_uid) firstRes " + + "ON (res.user_uid = firstRes.user_uid AND res.start_date = firstRes.startDate) GROUP BY res.user_uid"; NativeQuery query = getSession().createNativeQuery(FIRST_SCORES_BY_CONTENT_ID); query.setParameter("contentId", toolContentId); @@ -328,25 +358,19 @@ } @Override - public int countLastFinishedAssessmentResults(long contentId) { - return ((Long) getSession().createQuery("SELECT COUNT(*) " + FIND_LAST_FINISHED_RESULTS_BY_CONTENT_ID) - .setParameter("contentId", contentId).uniqueResult()).intValue(); - } - - @Override public List getLastFinishedAssessmentResultsBySession(Long sessionId) { - final String FIND_LAST_FINISHED_RESULTS_BY_SESSION_ID = "SELECT r, u.portraitUuid FROM " - + AssessmentResult.class.getName() + " AS r, " + User.class.getName() - + " as u WHERE r.sessionId=? AND (r.finishDate != null) AND r.latest=1 AND u.userId=r.user.userId"; + final String FIND_LAST_FINISHED_RESULTS_BY_SESSION_ID = + "SELECT r, u.portraitUuid FROM " + AssessmentResult.class.getName() + " AS r, " + User.class.getName() + + " as u WHERE r.sessionId=? AND (r.finishDate != null) AND r.latest=1 AND u.userId=r.user.userId"; return doFind(FIND_LAST_FINISHED_RESULTS_BY_SESSION_ID, new Object[] { sessionId }); } @Override public List getLeadersLastFinishedAssessmentResults(Long contentId) { - final String FIND_LAST_FINISHED_RESULTS_BY_SESSION_ID = "SELECT r, u.portraitUuid FROM " - + AssessmentResult.class.getName() + " AS r, " + User.class.getName() - + " as u WHERE r.user=r.user.session.groupLeader AND r.assessment.contentId=? AND (r.finishDate != null) AND r.latest=1 AND u.userId=r.user.userId"; + final String FIND_LAST_FINISHED_RESULTS_BY_SESSION_ID = + "SELECT r, u.portraitUuid FROM " + AssessmentResult.class.getName() + " AS r, " + User.class.getName() + + " as u WHERE r.user=r.user.session.groupLeader AND r.assessment.contentId=? AND (r.finishDate != null) AND r.latest=1 AND u.userId=r.user.userId"; return doFind(FIND_LAST_FINISHED_RESULTS_BY_SESSION_ID, new Object[] { contentId }); } @@ -362,6 +386,13 @@ } @Override + public List getLearnersWithFinishedCurrentAttemptForCompletionChart(Long contentId) { + List results = getSession().createNativeQuery(FINISHED_LEARNERS_FOR_COMPLETION_CHART) + .setParameter("toolContentId", contentId).getResultList(); + return results; + } + + @Override public boolean isAssessmentAttempted(Long assessmentUid) { Query q = getSession().createQuery(IS_ASSESSMENT_RESULT_EXIST_BY_ASSESSMENT, Boolean.class); q.setParameter("assessmentUid", assessmentUid); @@ -396,15 +427,13 @@ @Override @SuppressWarnings("unchecked") - public Map> getAnsweredQuestionsByUsers(long toolContentId) { + public Map> getAnsweredQuestionsByUsersForCompletionChart(long toolContentId) { List results = getSession().createNativeQuery(ANSWERED_QUESTIONS_BY_USER) .setParameter("toolContentId", toolContentId).getResultList(); - return results.stream() - .collect(Collectors.groupingBy(r -> ((Number) r[4]).intValue(), - Collectors.mapping( - r -> new String[] { r[0].toString(), r[1] == null ? null : r[1].toString(), - r[2] == null ? "" : r[2].toString(), r[3] == null ? null : r[3].toString() }, - Collectors.toList()))); + return results.stream().collect(Collectors.groupingBy(r -> ((Number) r[4]).intValue(), Collectors.mapping( + r -> new String[] { r[0].toString(), r[1] == null ? null : r[1].toString(), + r[2] == null ? "" : r[2].toString(), r[3] == null ? null : r[3].toString() }, + Collectors.toList()))); } private List convertResultsToAssessmentUserDTOList(List list) { Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java =================================================================== diff -u -r3248d9808412f06775532829889bb22b76da3a9e -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java (.../AssessmentUserDAOHibernate.java) (revision 3248d9808412f06775532829889bb22b76da3a9e) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java (.../AssessmentUserDAOHibernate.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -23,11 +23,6 @@ package org.lamsfoundation.lams.tool.assessment.dao.hibernate; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import org.hibernate.query.NativeQuery; import org.hibernate.query.Query; import org.lamsfoundation.lams.dao.hibernate.LAMSBaseDAO; @@ -37,13 +32,20 @@ import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.springframework.stereotype.Repository; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + @Repository public class AssessmentUserDAOHibernate extends LAMSBaseDAO implements AssessmentUserDAO { - private static final String FIND_BY_USER_ID_SESSION_ID = "from " + AssessmentUser.class.getName() - + " as u where u.userId =? and u.session.sessionId=?"; - private static final String FIND_BY_SESSION_ID = "from " + AssessmentUser.class.getName() - + " as u where u.session.sessionId=?"; + private static final String FIND_BY_USER_ID_SESSION_ID = + "from " + AssessmentUser.class.getName() + " as u where u.userId =? and u.session.sessionId=?"; + private static final String FIND_BY_SESSION_ID = + "from " + AssessmentUser.class.getName() + " as u where u.session.sessionId=?"; + private static final String FIND_LEARNERS_BY_CONTENT_ID = "FROM " + AssessmentUser.class.getName() + " user" + + " WHERE user.session.assessment.contentId = :contentId "; private static final String LOAD_MARKS_FOR_SESSION = "SELECT grade FROM tl_laasse10_assessment_result " + " WHERE finish_date IS NOT NULL AND latest = 1 AND session_id = :sessionId"; @@ -57,6 +59,16 @@ + " JOIN tl_laasse10_assessment a ON r.assessment_uid = a.uid " + " WHERE r.finish_date IS NOT NULL AND r.latest = 1 AND a.content_id = :toolContentId"; + private static final String FIND_LEARNERS_BY_CONTENT_ID_FOR_COMPLETION_CHART = + "SELECT u.user_id, BIN_TO_UUID(u.portrait_uuid) AS portrait_uuid, " + + " CONCAT(u.first_name, ' ', u.last_name) AS user_name," + + " IF(a.use_select_leader_tool_ouput, s.session_name, NULL) AS group_name" + + " FROM tl_laasse10_assessment AS a" + + " JOIN tl_laasse10_session AS s ON s.assessment_uid = a.uid" + + " JOIN tl_laasse10_user AS au ON au.session_uid = s.uid" + + " JOIN lams_user AS u USING (user_id)" + + " WHERE a.content_id = :toolContentId ORDER BY user_name"; + @SuppressWarnings("rawtypes") @Override public AssessmentUser getUserByUserIDAndSessionID(Long userID, Long sessionId) { @@ -70,8 +82,8 @@ @SuppressWarnings("rawtypes") @Override public AssessmentUser getUserCreatedAssessment(Long userId, Long contentId) { - final String FIND_BY_USER_ID_CONTENT_ID = "from " + AssessmentUser.class.getName() - + " as u where u.userId =? and u.assessment.contentId=?"; + final String FIND_BY_USER_ID_CONTENT_ID = + "from " + AssessmentUser.class.getName() + " as u where u.userId =? and u.assessment.contentId=?"; List list = doFind(FIND_BY_USER_ID_CONTENT_ID, new Object[] { userId, contentId }); if (list == null || list.size() == 0) { @@ -194,30 +206,43 @@ } @Override - public int getCountUsersByContentId(Long contentId) { - final String LOAD_USERS_ORDERED_BY_NAME = "SELECT COUNT(*) FROM " + AssessmentUser.class.getName() + " user" - + " WHERE user.session.assessment.contentId = :contentId "; - - Query query = getSession().createQuery(LOAD_USERS_ORDERED_BY_NAME, Number.class); + public int getCountLearnersByContentId(Long contentId) { + Query query = getSession().createQuery("SELECT COUNT(*) " + FIND_LEARNERS_BY_CONTENT_ID, Number.class); query.setParameter("contentId", contentId); return query.uniqueResult().intValue(); } + @Override + public List getLearnersByContentId(Long contentId) { + Query query = getSession().createQuery( + FIND_LEARNERS_BY_CONTENT_ID + " ORDER BY firstName, lastName", AssessmentUser.class); + query.setParameter("contentId", contentId); + return query.list(); + } + + @Override + public List getLearnersByContentIdForCompletionChart(Long contentId) { + return getSession().createNativeQuery(FIND_LEARNERS_BY_CONTENT_ID_FOR_COMPLETION_CHART) + .setParameter("toolContentId", contentId).getResultList(); + } + private static String LOAD_USERS_ORDERED_BY_SESSION_QUESTION_SELECT = "SELECT DISTINCT question_result.uid, user.last_name, user.first_name, user.login_name, question_result.mark"; private static String LOAD_USERS_ORDERED_BY_SESSION_QUESTION_FROM = " FROM tl_laasse10_user user"; - private static String LOAD_USERS_ORDERED_BY_SESSION_QUESTION_JOIN = " INNER JOIN tl_laasse10_session session" - + " ON user.session_uid=session.uid" + + private static String LOAD_USERS_ORDERED_BY_SESSION_QUESTION_JOIN = + " INNER JOIN tl_laasse10_session session" + " ON user.session_uid=session.uid" + - " LEFT OUTER JOIN tl_laasse10_assessment_result result " + " ON result.user_uid = user.uid" - + " AND result.finish_date IS NOT NULL" + " AND result.latest = 1" + + " LEFT OUTER JOIN tl_laasse10_assessment_result result " + " ON result.user_uid = user.uid" + + " AND result.finish_date IS NOT NULL" + " AND result.latest = 1" + - " INNER JOIN lams_qb_tool_answer qbToolAnswer " + " ON qbToolAnswer.tool_question_uid = :questionUid " + + " INNER JOIN lams_qb_tool_answer qbToolAnswer " + + " ON qbToolAnswer.tool_question_uid = :questionUid " + - " INNER JOIN tl_laasse10_question_result question_result " + " ON result.uid=question_result.result_uid" - + " AND question_result.uid = qbToolAnswer.answer_uid" + + " INNER JOIN tl_laasse10_question_result question_result " + + " ON result.uid=question_result.result_uid" + + " AND question_result.uid = qbToolAnswer.answer_uid" + - " WHERE session.session_id = :sessionId " - + " AND (CONCAT(user.last_name, ' ', user.first_name) LIKE CONCAT('%', :searchString, '%')) "; + " WHERE session.session_id = :sessionId " + + " AND (CONCAT(user.last_name, ' ', user.first_name) LIKE CONCAT('%', :searchString, '%')) "; private static String LOAD_USERS_ORDERED_ORDER_BY_RESULT = "ORDER BY question_result.mark "; @SuppressWarnings("unchecked") Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r56aa4c936a358e32b0cbc287465f90fdfb9a90d5 -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 56aa4c936a358e32b0cbc287465f90fdfb9a90d5) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -37,6 +37,7 @@ import org.lamsfoundation.lams.contentrepository.client.IToolContentHandler; import org.lamsfoundation.lams.events.IEventNotificationService; import org.lamsfoundation.lams.learning.service.ILearnerService; +import org.lamsfoundation.lams.learningdesign.Group; import org.lamsfoundation.lams.learningdesign.Grouping; import org.lamsfoundation.lams.learningdesign.ToolActivity; import org.lamsfoundation.lams.learningdesign.service.ExportToolContentException; @@ -316,26 +317,78 @@ } @Override - public int getCountUsersByContentId(Long contentId) { - return assessmentUserDao.getCountUsersByContentId(contentId); + public int getCountLearnersByContentId(Long contentId) { + return assessmentUserDao.getCountLearnersByContentId(contentId); } + @Override + public List getLearnersByContentId(Long contentId) { + return assessmentUserDao.getLearnersByContentId(contentId); + } + + @Override + public ArrayNode getLearnersByContentIdJson(Long contentId) { + ArrayNode result = JsonNodeFactory.instance.arrayNode(); + List learnersDetails = assessmentUserDao.getLearnersByContentIdForCompletionChart(contentId); + for (Object[] learnerDetails : learnersDetails) { + ObjectNode learner = JsonNodeFactory.instance.objectNode(); + learner.put("id", learnerDetails[0].toString()); + learner.put("portraitUuid", learnerDetails[1] == null ? null : learnerDetails[1].toString()); + learner.put("name", learnerDetails[2] == null ? "?" : learnerDetails[2].toString()); + learner.put("group", learnerDetails[3] == null ? null : learnerDetails[3].toString()); + result.add(learner); + } + return result; + } + /** * How many learners can possibly access this activity */ @Override - public int getCountLessonLearnersByContentId(long contentId) { + public ArrayNode getLessonLearnersByContentIdJson(long contentId) { long lessonId = lessonService.getLessonByToolContentId(contentId).getLessonId(); - return lessonService.getCountLessonLearners(lessonId, null); + List learners = lessonService.getLessonLearners(lessonId, null, null, null, true); + + Grouping grouping = getGrouping(contentId); + ArrayNode result = JsonNodeFactory.instance.arrayNode(); + for (User learner : learners) { + ObjectNode learnerJson = JsonNodeFactory.instance.objectNode(); + learnerJson.put("id", learner.getUserId()); + learnerJson.put("name", learner.getFullName()); + learnerJson.put("portraitUuid", + learner.getPortraitUuid() == null ? null : learner.getPortraitUuid().toString()); + if (grouping != null) { + Group group = grouping.getGroupBy(learner); + if (group != null) { + learnerJson.put("group", group.getGroupName()); + } + } + result.add(learnerJson); + } + + return result; + } /** - * How many learners have already finished answering questions. They are either on results page or left the activity + * Learners have already finished answering questions. They are either on results page or left the activity * completely. */ @Override - public int getCountLearnersWithFinishedCurrentAttempt(long contentId) { - return assessmentResultDao.countLastFinishedAssessmentResults(contentId); + public ArrayNode getLearnersWithFinishedCurrentAttemptJson(long contentId) { + List learnersDetails = assessmentResultDao.getLearnersWithFinishedCurrentAttemptForCompletionChart( + contentId); + ArrayNode result = JsonNodeFactory.instance.arrayNode(); + for (Object[] learnerDetails : learnersDetails) { + ObjectNode learner = JsonNodeFactory.instance.objectNode(); + learner.put("id", learnerDetails[0].toString()); + learner.put("portraitUuid", learnerDetails[1] == null ? null : learnerDetails[1].toString()); + learner.put("name", learnerDetails[2] == null ? "?" : learnerDetails[2].toString()); + learner.put("group", learnerDetails[3] == null ? null : learnerDetails[3].toString()); + result.add(learner); + } + + return result; } @Override @@ -4018,21 +4071,36 @@ } @Override - public Map> getAnsweredQuestionsByUsers(long toolContentId) { - Map> answeredQuestions = assessmentResultDao.getAnsweredQuestionsByUsers(toolContentId); + public Map getAnsweredQuestionsByUsersJson(long toolContentId) { + Map> answeredQuestions = assessmentResultDao.getAnsweredQuestionsByUsersForCompletionChart( + toolContentId); + Map result = new TreeMap<>(); if (answeredQuestions.isEmpty()) { - return answeredQuestions; + return result; } Assessment assessment = getAssessmentByContentId(toolContentId); int questionCount = assessment.getQuestions().size(); // list all question counts, from 0 to maximum possible questions - Map> result = new TreeMap<>(); + for (int i = 0; i <= questionCount; i++) { - result.put(i, List.of()); + ArrayNode learnersJson = JsonNodeFactory.instance.arrayNode(); + result.put(i, learnersJson); + + List learnersDetails = answeredQuestions.get(i); + if (learnersDetails != null) { + for (String[] learnerDetails : learnersDetails) { + ObjectNode learner = JsonNodeFactory.instance.objectNode(); + learner.put("id", learnerDetails[0]); + learner.put("portraitUuid", learnerDetails[1] == null ? null : learnerDetails[1]); + learner.put("name", learnerDetails[2] == null ? "?" : learnerDetails[2]); + learner.put("group", learnerDetails[3] == null ? null : learnerDetails[3]); + learner.put("answeredQuestionCount", learnerDetails[4]); + learnersJson.add(learner); + } + } } - result.putAll(answeredQuestions); return result; } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java =================================================================== diff -u -r8bcf5f3b87aa5b9bcead80b6c2c06a5982513b41 -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 8bcf5f3b87aa5b9bcead80b6c2c06a5982513b41) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/IAssessmentService.java (.../IAssessmentService.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -23,33 +23,22 @@ package org.lamsfoundation.lams.tool.assessment.service; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.lamsfoundation.lams.learningdesign.Grouping; +import org.lamsfoundation.lams.notebook.model.NotebookEntry; +import org.lamsfoundation.lams.tool.assessment.dto.*; +import org.lamsfoundation.lams.tool.assessment.model.*; +import org.lamsfoundation.lams.tool.service.ICommonToolService; +import org.lamsfoundation.lams.usermanagement.User; +import org.lamsfoundation.lams.util.excel.ExcelSheet; + import java.lang.reflect.InvocationTargetException; import java.time.LocalDateTime; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; -import org.lamsfoundation.lams.learningdesign.Grouping; -import org.lamsfoundation.lams.notebook.model.NotebookEntry; -import org.lamsfoundation.lams.tool.assessment.dto.AssessmentResultDTO; -import org.lamsfoundation.lams.tool.assessment.dto.AssessmentUserDTO; -import org.lamsfoundation.lams.tool.assessment.dto.GradeStatsDTO; -import org.lamsfoundation.lams.tool.assessment.dto.QuestionDTO; -import org.lamsfoundation.lams.tool.assessment.dto.QuestionSummary; -import org.lamsfoundation.lams.tool.assessment.dto.ReflectDTO; -import org.lamsfoundation.lams.tool.assessment.dto.UserSummary; -import org.lamsfoundation.lams.tool.assessment.model.Assessment; -import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestion; -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.AssessmentUser; -import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; -import org.lamsfoundation.lams.tool.service.ICommonToolService; -import org.lamsfoundation.lams.usermanagement.User; -import org.lamsfoundation.lams.util.excel.ExcelSheet; - /** * Interface that defines the contract that all ShareAssessment service provider must follow. * @@ -110,12 +99,16 @@ int getCountUsersBySession(Long sessionId, String searchString); - int getCountUsersByContentId(Long contentId); + int getCountLearnersByContentId(Long contentId); - int getCountLessonLearnersByContentId(long contentId); + List getLearnersByContentId(Long contentId); - int getCountLearnersWithFinishedCurrentAttempt(long contentId); + ArrayNode getLearnersByContentIdJson(Long contentId); + ArrayNode getLessonLearnersByContentIdJson(long contentId); + + ArrayNode getLearnersWithFinishedCurrentAttemptJson(long contentId); + List getPagedUsersBySessionAndQuestion(Long sessionId, Long questionUid, int page, int size, String sortBy, String sortOrder, String searchString); @@ -138,6 +131,7 @@ Assessment getDefaultContent(Long contentId) throws AssessmentApplicationException; // ********** for user methods ************* + /** * Create a new user in database. */ @@ -234,8 +228,7 @@ * @param userId * @param pagedQuestions * @param isAutosave - * indicates whether it's autosave request - * + * indicates whether it's autosave request * @return whether storing results is allowed, false otherwise */ boolean storeUserAnswers(Assessment assessment, Long userId, List> pagedQuestions, @@ -429,23 +422,21 @@ List exportSummary(Assessment assessment, long toolContentId); /** - * Gets the basic statistics for the grades for the Leaders when an Assessment is done using - * Group Leaders. So the averages, etc are for the whole Assessment, not for a Group. + * Gets the basic statistics for the grades for the Leaders when an Assessment is done using Group Leaders. So the + * averages, etc are for the whole Assessment, not for a Group. */ GradeStatsDTO getStatsDtoForLeaders(Long contentId); /** * Prepares data for the marks summary graph on the statistics page - * */ List getMarksArray(Long sessionId); List getMarksArrayByContentId(Long toolContentId); /** - * Prepares data for the marks summary graph on the statistics page, using the grades for the Leaders - * when an Assessment is done using Group Leaders. So the grades are for the whole Assessment, not for a Group. - * + * Prepares data for the marks summary graph on the statistics page, using the grades for the Leaders when an + * Assessment is done using Group Leaders. So the grades are for the whole Assessment, not for a Group. */ List getMarksArrayForLeaders(Long contentId); @@ -499,8 +490,8 @@ AssessmentQuestion getAssessmentQuestionByUid(Long questionUid); /** - * Sends a websocket command to learners who have assessment results open - * to refresh page because new data is available + * Sends a websocket command to learners who have assessment results open to refresh page because new data is + * available */ void notifyLearnersOnAnswerDisclose(long toolContentId); @@ -514,7 +505,7 @@ List getPossibleIndividualTimeLimitUsers(long toolContentId, String searchString); - Map> getAnsweredQuestionsByUsers(long toolContentId); + Map getAnsweredQuestionsByUsersJson(long toolContentId); void changeLeaderForGroup(long toolSessionId, long leaderUserId); } \ No newline at end of file Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java =================================================================== diff -u -r1dbecfa7a6a96832fa3c1578a4ac977b424a2b9d -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 1dbecfa7a6a96832fa3c1578a4ac977b424a2b9d) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -23,32 +23,10 @@ package org.lamsfoundation.lams.tool.assessment.web.controller; -import java.io.IOException; -import java.security.InvalidParameterException; -import java.time.LocalDateTime; -import java.time.OffsetDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Date; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.function.Function; -import java.util.stream.Collectors; - -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.lamsfoundation.lams.learningdesign.Group; @@ -63,34 +41,14 @@ import org.lamsfoundation.lams.rating.model.RatingCriteria; import org.lamsfoundation.lams.rating.service.IRatingService; import org.lamsfoundation.lams.tool.assessment.AssessmentConstants; -import org.lamsfoundation.lams.tool.assessment.dto.AssessmentResultDTO; -import org.lamsfoundation.lams.tool.assessment.dto.AssessmentUserDTO; -import org.lamsfoundation.lams.tool.assessment.dto.GradeStatsDTO; -import org.lamsfoundation.lams.tool.assessment.dto.OptionDTO; -import org.lamsfoundation.lams.tool.assessment.dto.QuestionDTO; -import org.lamsfoundation.lams.tool.assessment.dto.QuestionSummary; -import org.lamsfoundation.lams.tool.assessment.dto.ReflectDTO; -import org.lamsfoundation.lams.tool.assessment.dto.UserSummary; -import org.lamsfoundation.lams.tool.assessment.dto.UserSummaryItem; -import org.lamsfoundation.lams.tool.assessment.model.Assessment; -import org.lamsfoundation.lams.tool.assessment.model.AssessmentQuestion; -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.AssessmentUser; -import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; +import org.lamsfoundation.lams.tool.assessment.dto.*; +import org.lamsfoundation.lams.tool.assessment.model.*; import org.lamsfoundation.lams.tool.assessment.service.IAssessmentService; import org.lamsfoundation.lams.tool.assessment.util.AssessmentEscapeUtils; 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.CommonConstants; -import org.lamsfoundation.lams.util.Configuration; -import org.lamsfoundation.lams.util.ConfigurationKeys; -import org.lamsfoundation.lams.util.DateUtil; -import org.lamsfoundation.lams.util.JsonUtil; -import org.lamsfoundation.lams.util.NumberUtil; -import org.lamsfoundation.lams.util.WebUtil; +import org.lamsfoundation.lams.util.*; import org.lamsfoundation.lams.util.excel.ExcelSheet; import org.lamsfoundation.lams.util.excel.ExcelUtil; import org.lamsfoundation.lams.web.session.SessionManager; @@ -101,17 +59,21 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.*; import org.springframework.web.util.HtmlUtils; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.ObjectNode; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.security.InvalidParameterException; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; @Controller @RequestMapping("/monitoring") @@ -229,7 +191,7 @@ if (displayStudentChoices) { request.setAttribute("maxOptionsInQuestion", maxOptionsInQuestion); - int totalNumberOfUsers = service.getCountUsersByContentId(contentId); + int totalNumberOfUsers = service.getCountLearnersByContentId(contentId); Set questionDtos = new TreeSet<>(); for (AssessmentQuestion question : assessment.getQuestions()) { @@ -264,12 +226,12 @@ throws JsonProcessingException, IOException { ObjectNode chartJson = JsonNodeFactory.instance.objectNode(); - chartJson.put("possibleLearners", service.getCountLessonLearnersByContentId(toolContentId)); - chartJson.put("startedLearners", service.getCountUsersByContentId(toolContentId)); - chartJson.put("completedLearners", service.getCountLearnersWithFinishedCurrentAttempt(toolContentId)); + chartJson.set("possibleLearners", service.getLessonLearnersByContentIdJson(toolContentId)); + chartJson.set("startedLearners", service.getLearnersByContentIdJson(toolContentId)); + chartJson.set("completedLearners", service.getLearnersWithFinishedCurrentAttemptJson(toolContentId)); chartJson.put("sessionCount", service.getSessionsByContentId(toolContentId).size()); - Map> answeredQuestionsByUsers = service.getAnsweredQuestionsByUsers(toolContentId); + Map answeredQuestionsByUsers = service.getAnsweredQuestionsByUsersJson(toolContentId); if (!answeredQuestionsByUsers.isEmpty()) { chartJson.set("answeredQuestionsByUsers", JsonUtil.readObject(answeredQuestionsByUsers)); Map answeredQuestionsByUsersCount = answeredQuestionsByUsers.entrySet().stream() @@ -320,14 +282,14 @@ UserSummary userSummary = service.getUserSummary(contentId, userId, sessionId); request.setAttribute(AssessmentConstants.ATTR_USER_SUMMARY, userSummary); - Map learnerInteractions = learnerInteractionService - .getFirstLearnerInteractions(contentId, userId.intValue()); + Map learnerInteractions = learnerInteractionService.getFirstLearnerInteractions( + contentId, userId.intValue()); request.setAttribute("learnerInteractions", learnerInteractions); Assessment assessment = service.getAssessmentByContentId(contentId); - boolean questionEtherpadEnabled = assessment.isUseSelectLeaderToolOuput() - && assessment.isQuestionEtherpadEnabled() - && StringUtils.isNotBlank(Configuration.get(ConfigurationKeys.ETHERPAD_API_KEY)); + boolean questionEtherpadEnabled = + assessment.isUseSelectLeaderToolOuput() && assessment.isQuestionEtherpadEnabled() + && StringUtils.isNotBlank(Configuration.get(ConfigurationKeys.ETHERPAD_API_KEY)); request.setAttribute(AssessmentConstants.ATTR_IS_QUESTION_ETHERPAD_ENABLED, questionEtherpadEnabled); request.setAttribute(AssessmentConstants.ATTR_TOOL_SESSION_ID, sessionId); @@ -349,8 +311,8 @@ @ResponseBody public String saveUserGrade(HttpServletRequest request, HttpServletResponse response) { String responseText = null; - if ((request.getParameter(AssessmentConstants.PARAM_NOT_A_NUMBER) == null) - && !StringUtils.isEmpty(request.getParameter(AssessmentConstants.PARAM_QUESTION_RESULT_UID))) { + if ((request.getParameter(AssessmentConstants.PARAM_NOT_A_NUMBER) == null) && !StringUtils.isEmpty( + request.getParameter(AssessmentConstants.PARAM_QUESTION_RESULT_UID))) { Long questionResultUid = WebUtil.readLongParam(request, AssessmentConstants.PARAM_QUESTION_RESULT_UID); HttpSession ss = SessionManager.getSession(); UserDTO teacher = (UserDTO) ss.getAttribute(AttributeNames.USER); @@ -607,10 +569,11 @@ + questionResult.getJustificationEscaped(); } userData.add(response); - + // show confidence levels if this feature is turned ON if (assessment.isEnableConfidenceLevels()) { - userData.add(questionResult.getQbQuestion().getType().equals(QbQuestion.TYPE_MARK_HEDGING) ? -1 + userData.add(questionResult.getQbQuestion().getType().equals(QbQuestion.TYPE_MARK_HEDGING) + ? -1 : questionResult.getConfidenceLevel()); } @@ -623,15 +586,17 @@ if (!ratings.isEmpty()) { int numberOfVotes = ratings.size(); double ratingSum = ratings.stream().mapToDouble(Rating::getRating).sum(); - String averageRating = NumberUtil - .formatLocalisedNumberForceDecimalPlaces(ratingSum / numberOfVotes, null, 2); + String averageRating = NumberUtil.formatLocalisedNumberForceDecimalPlaces( + ratingSum / numberOfVotes, null, 2); starString = "
"; - starString += "
"; + starString += + "
"; starString += "
"; - starString += "
"; + starString += + "
"; String msg = service.getMessage("label.average.rating", new Object[] { averageRating, numberOfVotes }); starString += msg; @@ -641,16 +606,13 @@ userData.add(starString); } - //LDEV_NTU-11 Swapping Mark and Response columns in Assessment Monitor userData.add(questionResult.getQbQuestion().getType().equals(QbQuestion.TYPE_ESSAY) && questionResult.getMarkedBy() == null ? "-" : questionResult.getMark().toString()); - userData.add( - questionResult.getMarkedBy() == null - ? (questionResult.getQbQuestion().getType().equals(QbQuestion.TYPE_ESSAY) - ? service.getMessage("label.monitoring.user.summary.grade.required") - : "") - : questionResult.getMarkedBy().getFullName()); + userData.add(questionResult.getMarkedBy() == null + ? (questionResult.getQbQuestion().getType().equals(QbQuestion.TYPE_ESSAY) ? service.getMessage( + "label.monitoring.user.summary.grade.required") : "") + : questionResult.getMarkedBy().getFullName()); userData.add(questionResult.getMarkerComment()); } else { userData.add(""); @@ -771,7 +733,8 @@ results = service.getMarksArrayForLeaders(contentId); } else { Long sessionId = WebUtil.readLongParam(request, AssessmentConstants.ATTR_TOOL_SESSION_ID, true); - results = sessionId == null ? service.getMarksArrayByContentId(contentId) + results = sessionId == null + ? service.getMarksArrayByContentId(contentId) : service.getMarksArray(sessionId); } } @@ -940,7 +903,8 @@ Assessment assessment = service.getAssessmentByContentId(toolContentId); assessment.setRelativeTimeLimit(relativeTimeLimit); // set time limit as seconds from start of epoch, using current server time zone - assessment.setAbsoluteTimeLimit(absoluteTimeLimit == null ? null + assessment.setAbsoluteTimeLimit(absoluteTimeLimit == null + ? null : LocalDateTime.ofEpochSecond(absoluteTimeLimit, 0, OffsetDateTime.now().getOffset())); service.saveOrUpdateAssessment(assessment); } @@ -961,8 +925,8 @@ if (grouping != null) { Set groups = grouping.getGroups(); for (Group group : groups) { - if (!group.getUsers().isEmpty() - && group.getGroupName().toLowerCase().contains(searchString.toLowerCase())) { + if (!group.getUsers().isEmpty() && group.getGroupName().toLowerCase() + .contains(searchString.toLowerCase())) { ObjectNode groupJSON = JsonNodeFactory.instance.objectNode(); groupJSON.put("label", groupLabel + group.getGroupName() + "\""); groupJSON.put("value", "group-" + group.getGroupId()); Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/TblMonitoringController.java =================================================================== diff -u -ra6bd52bd717d1e9e860036461c7c71c45cf8113a -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/TblMonitoringController.java (.../TblMonitoringController.java) (revision a6bd52bd717d1e9e860036461c7c71c45cf8113a) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/TblMonitoringController.java (.../TblMonitoringController.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -105,7 +105,7 @@ request.setAttribute("maxOptionsInQuestion", maxOptionsInQuestion); request.setAttribute("vsaPresent", vsaPresent); - int totalNumberOfUsers = assessmentService.getCountUsersByContentId(toolContentId); + int totalNumberOfUsers = assessmentService.getCountLearnersByContentId(toolContentId); if (totalNumberOfUsers > 0) { for (QuestionDTO questionDto : questionDtos) { @@ -143,7 +143,7 @@ TblAssessmentDTO assessmentDto = new TblAssessmentDTO(); - int attemptedLearnersNumber = assessmentService.getCountUsersByContentId(toolContentId); + int attemptedLearnersNumber = assessmentService.getCountLearnersByContentId(toolContentId); assessmentDto.setAttemptedLearnersNumber(attemptedLearnersNumber); assessmentDto.setActivityTitle(activityTitle); @@ -189,7 +189,7 @@ model.addAttribute(AttributeNames.PARAM_TOOL_CONTENT_ID, toolContentId); model.addAttribute("allowDiscloseAnswers", assessment.isAllowDiscloseAnswers()); - int attemptedLearnersNumber = assessmentService.getCountUsersByContentId(toolContentId); + int attemptedLearnersNumber = assessmentService.getCountLearnersByContentId(toolContentId); model.addAttribute("attemptedLearnersNumber", attemptedLearnersNumber); return "pages/tblmonitoring/assessment"; @@ -355,4 +355,4 @@ return "pages/tblmonitoring/teams"; } -} +} \ No newline at end of file Index: lams_tool_assessment/web/includes/javascript/chart.js =================================================================== diff -u -r8a1b8b9cba5668f9c9b47a716093214aeec87f30 -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 --- lams_tool_assessment/web/includes/javascript/chart.js (.../chart.js) (revision 8a1b8b9cba5668f9c9b47a716093214aeec87f30) +++ lams_tool_assessment/web/includes/javascript/chart.js (.../chart.js) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) @@ -25,17 +25,20 @@ } function drawActivityCompletionChart(data, animate){ - var newData = [ data.possibleLearners - data.startedLearners, - data.startedLearners - data.completedLearners, - data.completedLearners + var newData = [ data.possibleLearners.length - data.startedLearners.length, + data.startedLearners.length - data.completedLearners.length, + data.completedLearners.length ]; if (activityCompletionChart != null) { // chart already exists, just update data activityCompletionChart.data.datasets[0].data = newData; activityCompletionChart.update(); return; } - + + // store current data for custom tooltip + $('#activity-completion-chart').data('tooltip-input', data); + let ctx = document.getElementById('activity-completion-chart').getContext('2d'); activityCompletionChart = new Chart(ctx, { @@ -206,14 +209,14 @@ var counter = 0, users = $('#answered-questions-chart').data('tooltip-input')[tooltipModel.dataPoints[0].label]; $(users).each(function(){ - var portraitDiv = $(definePortrait(this[1], this[0], STYLE_SMALL, true, LAMS_URL)).css({ + var portraitDiv = $(definePortrait(this.portraitUuid, this.id, STYLE_SMALL, true, LAMS_URL)).css({ 'vertical-align' : 'middle' }), userDiv = $('
').append(portraitDiv).appendTo(tooltipEl).css({ 'padding-bottom' : '5px' }); - $('').text(this[3] ? this[3] + ' (' + this[2] + ')' : this[2]).appendTo(userDiv).css({ + $('').text(this.group ? this.group + ' (' + this.name + ')' : this.name).appendTo(userDiv).css({ 'padding-left' : '10px' });