Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java =================================================================== diff -u -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 -r6d67af7eb9c49d59b6b0ff579b472dcf7022d437 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentResultDAOHibernate.java (.../AssessmentResultDAOHibernate.java) (revision 6d67af7eb9c49d59b6b0ff579b472dcf7022d437) @@ -125,7 +125,7 @@ + " (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" + + " s.session_name 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" @@ -152,7 +152,7 @@ + " (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" + + " s.session_name 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" Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java =================================================================== diff -u -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 -r6d67af7eb9c49d59b6b0ff579b472dcf7022d437 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java (.../AssessmentUserDAOHibernate.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/dao/hibernate/AssessmentUserDAOHibernate.java (.../AssessmentUserDAOHibernate.java) (revision 6d67af7eb9c49d59b6b0ff579b472dcf7022d437) @@ -62,7 +62,7 @@ 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" + + " s.session_name 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" Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java =================================================================== diff -u -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 -r6d67af7eb9c49d59b6b0ff579b472dcf7022d437 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/service/AssessmentServiceImpl.java (.../AssessmentServiceImpl.java) (revision 6d67af7eb9c49d59b6b0ff579b472dcf7022d437) @@ -4096,7 +4096,6 @@ 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); } } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java =================================================================== diff -u -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 -r6d67af7eb9c49d59b6b0ff579b472dcf7022d437 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 6d67af7eb9c49d59b6b0ff579b472dcf7022d437) @@ -23,7 +23,6 @@ package org.lamsfoundation.lams.tool.assessment.web.controller; -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; @@ -223,7 +222,7 @@ @RequestMapping(path = "/getCompletionChartsData") @ResponseBody public String getCompletionChartsData(@RequestParam long toolContentId, HttpServletResponse response) - throws JsonProcessingException, IOException { + throws IOException { ObjectNode chartJson = JsonNodeFactory.instance.objectNode(); chartJson.set("possibleLearners", service.getLessonLearnersByContentIdJson(toolContentId)); @@ -239,6 +238,10 @@ chartJson.set("answeredQuestionsByUsersCount", JsonUtil.readObject(answeredQuestionsByUsersCount)); } + Assessment assessment = service.getAssessmentByContentId(toolContentId); + chartJson.put("useLeader", assessment.isUseSelectLeaderToolOuput()); + chartJson.put("isGrouped", service.isGroupedActivity(toolContentId)); + response.setContentType("application/json;charset=utf-8"); return chartJson.toString(); } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/TblMonitoringController.java =================================================================== diff -u -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 -r6d67af7eb9c49d59b6b0ff579b472dcf7022d437 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/TblMonitoringController.java (.../TblMonitoringController.java) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/TblMonitoringController.java (.../TblMonitoringController.java) (revision 6d67af7eb9c49d59b6b0ff579b472dcf7022d437) @@ -1,33 +1,11 @@ package org.lamsfoundation.lams.tool.assessment.web.controller; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import javax.servlet.http.HttpServletRequest; - import org.apache.commons.lang.StringUtils; import org.lamsfoundation.lams.learning.service.ILearnerService; import org.lamsfoundation.lams.qb.model.QbQuestion; import org.lamsfoundation.lams.tool.assessment.AssessmentConstants; -import org.lamsfoundation.lams.tool.assessment.dto.AssessmentResultDTO; -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.TblAssessmentDTO; -import org.lamsfoundation.lams.tool.assessment.dto.TblAssessmentQuestionDTO; -import org.lamsfoundation.lams.tool.assessment.dto.TblAssessmentQuestionResultDTO; -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.AssessmentQuestionResult; -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.AssessmentServiceImpl; import org.lamsfoundation.lams.tool.assessment.service.IAssessmentService; import org.lamsfoundation.lams.tool.assessment.util.AssessmentEscapeUtils; @@ -41,6 +19,9 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import javax.servlet.http.HttpServletRequest; +import java.util.*; + @Controller @RequestMapping("/tblmonitoring") public class TblMonitoringController { @@ -122,7 +103,6 @@ request.setAttribute("questions", questionDtos); request.setAttribute(AttributeNames.PARAM_TOOL_CONTENT_ID, toolContentId); - request.setAttribute("groupsInAnsweredQuestionsChart", assessment.isUseSelectLeaderToolOuput()); request.setAttribute("assessment", assessment); request.setAttribute("isTbl", true); @@ -272,7 +252,6 @@ request.setAttribute("sessions", sessions); request.setAttribute("questionDtos", tblQuestionDtos); request.setAttribute(AttributeNames.PARAM_TOOL_CONTENT_ID, toolContentId); - request.setAttribute("groupsInAnsweredQuestionsChart", assessment.isUseSelectLeaderToolOuput()); request.setAttribute("assessment", assessment); request.setAttribute("isTbl", true); Index: lams_tool_assessment/web/includes/javascript/chart.js =================================================================== diff -u -r98b45e9d9615cd05b9dd569e237a2bbcf2969265 -r6d67af7eb9c49d59b6b0ff579b472dcf7022d437 --- lams_tool_assessment/web/includes/javascript/chart.js (.../chart.js) (revision 98b45e9d9615cd05b9dd569e237a2bbcf2969265) +++ lams_tool_assessment/web/includes/javascript/chart.js (.../chart.js) (revision 6d67af7eb9c49d59b6b0ff579b472dcf7022d437) @@ -1,4 +1,4 @@ -function drawCompletionCharts(toolContentId, useGroups,animate) { +function drawCompletionCharts(toolContentId, animate) { $.ajax({ 'url' : WEB_APP_URL + 'monitoring/getCompletionChartsData.do', 'data': { @@ -8,39 +8,69 @@ 'success' : function(data) { // draw charts for the first time drawActivityCompletionChart(data, animate); - drawAnsweredQuestionsChart(data, useGroups, animate); + drawAnsweredQuestionsChart(data, animate); } }); - + if (activityCompletionChart == null && answeredQuestionsChart == null && COMPLETION_CHART_UPDATE_INTERVAL > 0) { if (typeof completionChartInterval != 'undefined' && completionChartInterval) { window.clearInterval(completionChartInterval); } - + // set up update interval for the charts completionChartInterval = window.setInterval(function(){ - drawCompletionCharts(toolContentId, useGroups,animate); + drawCompletionCharts(toolContentId, animate); }, COMPLETION_CHART_UPDATE_INTERVAL); } } function drawActivityCompletionChart(data, animate){ - var newData = [ data.possibleLearners.length - data.startedLearners.length, - data.startedLearners.length - data.completedLearners.length, - data.completedLearners.length - ]; + // prepare data for the chart + let notStartedLearners = data.possibleLearners.filter(function (learner) { + let found = false; + $.each(data.startedLearners, function (index, startedLearner) { + if (learner.id == startedLearner.id) { + found = true; + return false; + } + }); + return !found; + }), + startedNotCompletedLearners = data.startedLearners.filter(function (learner) { + let found = false; + $.each(data.completedLearners, function (index, completedLearner) { + if (learner.id == completedLearner.id) { + found = true; + return false; + } + }); + return !found; + }), + newData = [notStartedLearners.length, + startedNotCompletedLearners.length, + data.completedLearners.length + ]; + + let chartPlaceholder = $('#activity-completion-chart'); + if (chartPlaceholder.length === 0) { + // no sessions yet, so no chart placeholder + return; + } + + // store current data for custom tooltip + chartPlaceholder.data('tooltip-input', [notStartedLearners, startedNotCompletedLearners, data.completedLearners]); + chartPlaceholder.data('useGroupsAsNames', false); + chartPlaceholder.data('isGrouped', data.isGrouped); + 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 = chartPlaceholder[0].getContext('2d'); - let ctx = document.getElementById('activity-completion-chart').getContext('2d'); - activityCompletionChart = new Chart(ctx, { type : 'doughnut', borderWidth : 0, @@ -54,14 +84,14 @@ datasets : [ { data : newData, backgroundColor : [ 'rgba(5, 204, 214, 1)', - 'rgba(255, 195, 55, 1)', - 'rgba(253, 60, 165, 1)', - ], + 'rgba(255, 195, 55, 1)', + 'rgba(253, 60, 165, 1)', + ], borderWidth : 0, } ], labels : [ LABELS.ACTIVITY_COMPLETION_CHART_POSSIBLE_LEARNERS, - LABELS.ACTIVITY_COMPLETION_CHART_STARTED_LEARNERS, - LABELS.ACTIVITY_COMPLETION_CHART_COMPLETED_LEARNERS ] + LABELS.ACTIVITY_COMPLETION_CHART_STARTED_LEARNERS, + LABELS.ACTIVITY_COMPLETION_CHART_COMPLETED_LEARNERS ] }, options : { layout : { @@ -106,34 +136,51 @@ animateScale : true, animateRotate : true, duration : animate ? 1000 : 0 + }, + tooltips : { + enabled : false, + custom : function(tooltipModel) { + listCompletionChartLearners.call(this, chartPlaceholder, tooltipModel) + } } } }); } -function drawAnsweredQuestionsChart(data, useGroups, animate){ + +function drawAnsweredQuestionsChart(data, animate){ if (!data.answeredQuestionsByUsers) { return; } - + + let chartPlaceholder = $('#answered-questions-chart'), + useGroupsAsNames = data.useLeader && data.isGrouped; + + if (chartPlaceholder.length === 0) { + // no sessions yet, so no chart placeholder + return; + } // store current data for custom tooltip - $('#answered-questions-chart').data('tooltip-input', data.answeredQuestionsByUsers); - + chartPlaceholder.data('tooltip-input', data.answeredQuestionsByUsers); + chartPlaceholder.data('useGroupsAsNames', useGroupsAsNames); + chartPlaceholder.data('isGrouped', data.isGrouped); + if (answeredQuestionsChart != null) { // chart already exists, just update data answeredQuestionsChart.data.datasets[0].data = Object.values(data.answeredQuestionsByUsersCount); answeredQuestionsChart.update(); return; } - - let ctx = document.getElementById('answered-questions-chart').getContext('2d'); + + let ctx = chartPlaceholder[0].getContext('2d'); + answeredQuestionsChart = new Chart(ctx, { type : 'bar', data : { datasets : [ { data : Object.values(data.answeredQuestionsByUsersCount), backgroundColor : 'rgba(255, 195, 55, 1)' - + } ], labels : Object.keys(data.answeredQuestionsByUsersCount), }, @@ -150,102 +197,114 @@ display: true, fontSize : '15', lineHeight: 3, - text : useGroups ? LABELS.ANSWERED_QUESTIONS_CHART_TITLE_GROUPS : LABELS.ANSWERED_QUESTIONS_CHART_TITLE + text : useGroupsAsNames ? LABELS.ANSWERED_QUESTIONS_CHART_TITLE_GROUPS : LABELS.ANSWERED_QUESTIONS_CHART_TITLE }, animation : { duration : animate ? 1000 : 0 }, scales : { xAxes : [{ - scaleLabel : { - display : true, - labelString : LABELS.ANSWERED_QUESTIONS_CHART_X_AXIS - } + scaleLabel : { + display : true, + labelString : LABELS.ANSWERED_QUESTIONS_CHART_X_AXIS } + } ], yAxes : [ { - ticks : { + ticks : { beginAtZero : true, stepSize : 1, maxTicksLimit : 5, // prevent scale to change on each update // set suggested max number of students to 3/4 // of all possible learners - suggestedMax : Math.max(2, Math.floor(3 * (useGroups ? data.sessionCount : data.possibleLearners) / 4)) + suggestedMax : Math.max(2, Math.floor(3 * (useGroupsAsNames ? data.sessionCount : data.possibleLearners.length) / 4)) }, scaleLabel : { display : true, - labelString : useGroups ? LABELS.ANSWERED_QUESTIONS_CHART_Y_AXIS_GROUPS : LABELS.ANSWERED_QUESTIONS_CHART_Y_AXIS_STUDENTS, + labelString : useGroupsAsNames ? LABELS.ANSWERED_QUESTIONS_CHART_Y_AXIS_GROUPS : LABELS.ANSWERED_QUESTIONS_CHART_Y_AXIS_STUDENTS, fontSize : 14 } } ] }, tooltips : { - enabled : false, - custom : function(tooltipModel) { - // always remove the tooltip at the beginning - $('.answered-questions-chart-tooltip').remove(); - if (tooltipModel.opacity === 0) { - // if it should be hidden, there is nothing to do - return; - } - - // create tooltip - var tooltipEl = $('
').addClass('answered-questions-chart-tooltip') - .appendTo(document.body) - .css({ - 'opacity' : 1, - 'position' :'absolute', - 'pointerEvents' : 'none', - 'background-color' : 'white', - 'padding' : '15px', - 'border' : 'thin solid #ddd', - 'border-radius' : '25px' - }); - - // iterate over learners, get their names and portraits - var counter = 0, - users = $('#answered-questions-chart').data('tooltip-input')[tooltipModel.dataPoints[0].label]; - $(users).each(function(){ - 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' - }); + enabled : false, + custom : function(tooltipModel) { + listCompletionChartLearners.call(this, chartPlaceholder, tooltipModel) + } + } + } + }); +} - $('').text(this.group ? this.group + ' (' + this.name + ')' : this.name).appendTo(userDiv).css({ - 'padding-left' : '10px' - }); - - if (counter === 15) { - // do not display more than 15 learners - if (users.length > 16) { - $('').text('...').appendTo(tooltipEl).css({ - 'font-weight' : 'bold', - 'font-size' : '20px', - 'text-align' : 'center' - }); - } - return false; - } - counter++; - }); - - // do not add padding for the last element as the tooltip does not look symmetric - tooltipEl.children(':last-child').css({ - 'padding-bottom' : '0' - }); - - var position = this._chart.canvas.getBoundingClientRect(); - tooltipEl.css({ - 'left' : position.left + window.pageXOffset + tooltipModel.caretX - tooltipEl.width() - 60 + 'px', - 'top' : Math.max(10, position.top + window.pageYOffset + tooltipModel.caretY - tooltipEl.height()/2) + 'px', - }); - } + +function listCompletionChartLearners(chartPlaceholder, tooltipModel) { + + let tooltipClassName = chartPlaceholder.attr('id') + '-tooltip'; + // always remove the tooltip at the beginning + $('.' + tooltipClassName).remove(); + if (tooltipModel.opacity === 0) { + // if it should be hidden, there is nothing to do + return; + } + + // create tooltip + var tooltipEl = $('').addClass(tooltipClassName) + .appendTo(document.body) + .css({ + 'opacity' : 1, + 'position' :'absolute', + 'pointerEvents' : 'none', + 'background-color' : 'white', + 'padding' : '15px', + 'border' : 'thin solid #ddd', + 'border-radius' : '25px' + }); + + // iterate over learners, get their names and portraits + var counter = 0, + data = chartPlaceholder.data('tooltip-input'), + useGroupsAsNames = chartPlaceholder.data('useGroupsAsNames'), + isGrouped = chartPlaceholder.data('isGrouped'), + users = data[tooltipModel.dataPoints[0].index]; + $(users).each(function(){ + 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(useGroupsAsNames ? this.group + ' (' + this.name + ')' + : (this.name + (isGrouped && this.group ? ' (' + this.group + ') ' : ''))) + .appendTo(userDiv).css({ + 'padding-left' : '10px' + }); + + if (counter === 15) { + // do not display more than 15 learners + if (users.length > 16) { + $('').text('...').appendTo(tooltipEl).css({ + 'font-weight' : 'bold', + 'font-size' : '20px', + 'text-align' : 'center' + }); } + return false; } + counter++; }); + + // do not add padding for the last element as the tooltip does not look symmetric + tooltipEl.children(':last-child').css({ + 'padding-bottom' : '0' + }); + + var position = this._chart.canvas.getBoundingClientRect(); + tooltipEl.css({ + 'left' : position.left + window.pageXOffset + tooltipModel.caretX - tooltipEl.width() - 60 + 'px', + 'top' : Math.max(10, position.top + window.pageYOffset + tooltipModel.caretY - tooltipEl.height()/2) + 'px', + }); } \ No newline at end of file Index: lams_tool_assessment/web/pages/monitoring/summary.jsp =================================================================== diff -u -r1dbecfa7a6a96832fa3c1578a4ac977b424a2b9d -r6d67af7eb9c49d59b6b0ff579b472dcf7022d437 --- lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 1dbecfa7a6a96832fa3c1578a4ac977b424a2b9d) +++ lams_tool_assessment/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 6d67af7eb9c49d59b6b0ff579b472dcf7022d437) @@ -18,194 +18,194 @@