Index: lams_build/3rdParty.userlibraries =================================================================== diff -u -r0b8e098f13d7ba82677614bcd878fde6f077c9e7 -r153c59d8375056a983c1bb0bb34044057c7c410a --- lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision 0b8e098f13d7ba82677614bcd878fde6f077c9e7) +++ lams_build/3rdParty.userlibraries (.../3rdParty.userlibraries) (revision 153c59d8375056a983c1bb0bb34044057c7c410a) @@ -1,45 +1,45 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: lams_common/src/java/org/lamsfoundation/lams/commonContext.xml =================================================================== diff -u -rb6d0779192887979b5d3d244dec3506d12a7e401 -r153c59d8375056a983c1bb0bb34044057c7c410a --- lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision b6d0779192887979b5d3d244dec3506d12a7e401) +++ lams_common/src/java/org/lamsfoundation/lams/commonContext.xml (.../commonContext.xml) (revision 153c59d8375056a983c1bb0bb34044057c7c410a) @@ -564,6 +564,7 @@ + Index: lams_tool_doku/src/java/org/lamsfoundation/lams/tool/dokumaran/web/controller/MonitoringController.java =================================================================== diff -u -rb6d0779192887979b5d3d244dec3506d12a7e401 -r153c59d8375056a983c1bb0bb34044057c7c410a --- lams_tool_doku/src/java/org/lamsfoundation/lams/tool/dokumaran/web/controller/MonitoringController.java (.../MonitoringController.java) (revision b6d0779192887979b5d3d244dec3506d12a7e401) +++ lams_tool_doku/src/java/org/lamsfoundation/lams/tool/dokumaran/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 153c59d8375056a983c1bb0bb34044057c7c410a) @@ -25,7 +25,11 @@ import java.io.IOException; import java.io.PrintWriter; +import java.util.Collections; +import java.util.Comparator; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.http.Cookie; @@ -36,12 +40,18 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.lamsfoundation.lams.etherpad.EtherpadException; +import org.lamsfoundation.lams.gradebook.GradebookUserActivity; +import org.lamsfoundation.lams.gradebook.service.IGradebookService; +import org.lamsfoundation.lams.security.ISecurityService; +import org.lamsfoundation.lams.tool.ToolSession; import org.lamsfoundation.lams.tool.dokumaran.DokumaranConstants; import org.lamsfoundation.lams.tool.dokumaran.dto.ReflectDTO; import org.lamsfoundation.lams.tool.dokumaran.dto.SessionDTO; import org.lamsfoundation.lams.tool.dokumaran.model.Dokumaran; import org.lamsfoundation.lams.tool.dokumaran.model.DokumaranSession; +import org.lamsfoundation.lams.tool.dokumaran.model.DokumaranUser; import org.lamsfoundation.lams.tool.dokumaran.service.IDokumaranService; +import org.lamsfoundation.lams.tool.service.ILamsCoreToolService; import org.lamsfoundation.lams.usermanagement.dto.UserDTO; import org.lamsfoundation.lams.util.Configuration; import org.lamsfoundation.lams.util.ConfigurationKeys; @@ -50,18 +60,41 @@ import org.lamsfoundation.lams.web.util.AttributeNames; import org.lamsfoundation.lams.web.util.SessionMap; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; 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 com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + @Controller @RequestMapping("/monitoring") public class MonitoringController { public static Logger log = Logger.getLogger(MonitoringController.class); + public static final int LEARNER_MARKS_SORTING_FIRST_NAME_ASC = 0; + public static final int LEARNER_MARKS_SORTING_FIRST_NAME_DESC = 1; + public static final int LEARNER_MARKS_SORTING_LAST_NAME_ASC = 2; + public static final int LEARNER_MARKS_SORTING_LAST_NAME_DESC = 3; + @Autowired private IDokumaranService dokumaranService; + @Autowired + private IGradebookService gradebookService; + + @Autowired + @Qualifier("lamsCoreToolService") + private ILamsCoreToolService toolService; + + @Autowired + private ISecurityService securityService; + @RequestMapping("/summary") private String summary(HttpServletRequest request, HttpServletResponse response) throws EtherpadException { // initial Session Map @@ -122,6 +155,80 @@ return "pages/monitoring/monitoring"; } + @RequestMapping("/getLearnerMarks") + @ResponseBody + private String getLearnerMarks(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + + Long toolSessionId = WebUtil.readLongParam(request, "toolSessionId"); + + // paging parameters of tablesorter + int size = WebUtil.readIntParam(request, "size"); + int page = WebUtil.readIntParam(request, "page"); + Integer isSortFirstName = WebUtil.readIntParam(request, "column[0]", true); + Integer isSortLastName = WebUtil.readIntParam(request, "column[1]", true); + + // identify sorting type + int sorting = LEARNER_MARKS_SORTING_FIRST_NAME_ASC; + if (isSortFirstName != null) { + sorting = isSortFirstName.equals(1) ? LEARNER_MARKS_SORTING_FIRST_NAME_DESC + : LEARNER_MARKS_SORTING_FIRST_NAME_ASC; + } else if (isSortLastName != null) { + sorting = isSortLastName.equals(1) ? LEARNER_MARKS_SORTING_LAST_NAME_DESC + : LEARNER_MARKS_SORTING_LAST_NAME_ASC; + } + + // get all session users and sort them according to the parameter from tablesorter + List users = dokumaranService.getUsersBySession(toolSessionId).stream() + .sorted(Comparator.comparing(sorting <= 1 ? DokumaranUser::getFirstName : DokumaranUser::getLastName)) + .collect(Collectors.toList()); + // reverse if sorting is descending + if (sorting == LEARNER_MARKS_SORTING_FIRST_NAME_DESC || sorting == LEARNER_MARKS_SORTING_LAST_NAME_DESC) { + Collections.reverse(users); + } + + // paging + int endIndex = (page + 1) * size; + users = users.subList(page * size, users.size() > endIndex ? endIndex : users.size()); + + ArrayNode rows = JsonNodeFactory.instance.arrayNode(); + ObjectNode responsedata = JsonNodeFactory.instance.objectNode(); + responsedata.put("total_rows", users.size()); + + ToolSession toolSession = toolService.getToolSessionById(toolSessionId); + Map gradebookUserActivities = gradebookService + .getGradebookUserActivities(toolSession.getToolActivity().getActivityId()).stream() + .filter(g -> g.getMark() != null) + .collect(Collectors.toMap(g -> g.getLearner().getUserId(), GradebookUserActivity::getMark)); + + for (DokumaranUser user : users) { + ObjectNode responseRow = JsonNodeFactory.instance.objectNode(); + + responseRow.put("userId", user.getUserId()); + responseRow.put("firstName", user.getFirstName()); + responseRow.put("lastName", user.getLastName()); + Double mark = gradebookUserActivities.get(user.getUserId().intValue()); + responseRow.put("mark", mark == null ? "" : String.valueOf(mark)); + + rows.add(responseRow); + } + + responsedata.set("rows", rows); + response.setContentType("application/json;charset=utf-8"); + return responsedata.toString(); + } + + @RequestMapping(path = "/updateLearnerMark", method = RequestMethod.POST) + private void updateLearnerMark(@RequestParam long toolSessionId, @RequestParam int userId, + @RequestParam Double mark) { + ToolSession toolSession = toolService.getToolSessionById(toolSessionId); + long lessonId = toolSession.getLesson().getLessonId(); + securityService.isLessonMonitor(lessonId, getUserId(), "update Doku learner mark", true); + + gradebookService.updateGradebookUserActivityMark(mark, null, userId, toolSessionId, true); + + } + @RequestMapping("/fixFaultySession") private void fixFaultySession(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { @@ -172,4 +279,9 @@ } + private Integer getUserId() { + HttpSession ss = SessionManager.getSession(); + UserDTO user = (UserDTO) ss.getAttribute(AttributeNames.USER); + return user != null ? user.getUserID() : null; + } } Index: lams_tool_doku/web/pages/monitoring/summary.jsp =================================================================== diff -u -r024f71c24280149ce16489cd0bca12fa127b17c9 -r153c59d8375056a983c1bb0bb34044057c7c410a --- lams_tool_doku/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 024f71c24280149ce16489cd0bca12fa127b17c9) +++ lams_tool_doku/web/pages/monitoring/summary.jsp (.../summary.jsp) (revision 153c59d8375056a983c1bb0bb34044057c7c410a) @@ -4,8 +4,12 @@ <%@ page import="org.lamsfoundation.lams.tool.dokumaran.DokumaranConstants"%> + + + + + + + + @@ -177,7 +189,125 @@ return false; }); + + + // marks table for each group + var tablesorters = $(".tablesorter"); + // intialise tablesorter tables + tablesorters.tablesorter({ + theme: 'bootstrap', + headerTemplate : '{content} {icon}', + sortInitialOrder: 'asc', + sortList: [[0]], + widgets: [ "uitheme", "resizable", "editable" ], + headers: { 0: { sorter: true}, 1: { sorter: true}, 2: { sorter: false} }, + sortList : [[0,1]], + showProcessing: false, + widgetOptions: { + resizable: true, + + // only marks is editable + editable_columns : [2], + editable_enterToAccept : true, // press enter to accept content, or click outside if false + editable_autoAccept : false, // accepts any changes made to the table cell automatically + editable_autoResort : false, // auto resort after the content has changed. + editable_validate : function (text, original, columnIndex) { + // removing all text produces " ", so get rid of it + text = text ? text.replace(/ /g, '').trim() : null; + // acceptable values are empty text or a number + return !text || !isNaN(text) ? text : original; + }, + editable_selectAll : function(txt, columnIndex, $element) { + // note $element is the div inside of the table cell, so use $element.closest('td') to get the cell + // only select everthing within the element when the content starts with the letter "B" + return true; + }, + editable_wrapContent : '
', // wrap all editable cell content... makes this widget work in IE, and with autocomplete + editable_trimContent : true, // trim content ( removes outer tabs & carriage returns ) + editable_editComplete : 'editComplete' // event fired after the table content has been edited + } + }); + + // update mark on edit + tablesorters.each(function(){ + // config event variable new in v2.17.6 + $(this).children('tbody').on('editComplete', 'td', function(event, config) { + var $this = $(this), + mark = $this.text() ? +$this.text() : null, + toolSessionId = +$this.closest('.tablesorter').attr('toolSessionId'), + userId = +$this.closest('tr').attr('userId'); + $.ajax({ + async: true, + url: '', + data : { + 'toolSessionId' : toolSessionId, + 'userId' : userId, + 'mark' : mark, + '' : '' + }, + type: 'post', + error: function (request, status, error) { + alert(''); + } + }); + + }); + }); + + // pager processing + tablesorters.each(function() { + var toolSessionId = $(this).attr('toolSessionId'); + + $(this).tablesorterPager({ + processAjaxOnInit: true, + initialRows: { + total: 10 + }, + savePages: false, + container: $(this).find(".ts-pager"), + output: '{startRow} to {endRow} ({totalRows})', + cssPageDisplay: '.pagedisplay', + cssPageSize: '.pagesize', + cssDisabled: 'disabled', + ajaxUrl : "" + toolSessionId, + ajaxProcessing: function (data, table) { + if (data && data.hasOwnProperty('rows')) { + var rows = [], + json = {}; + + for (i = 0; i < data.rows.length; i++){ + var userData = data.rows[i]; + + rows += ''; + + rows += ''; + rows += userData['firstName']; + rows += ''; + + rows += ''; + rows += userData['lastName']; + rows += ''; + + rows += ''; + rows += userData['mark']; + rows += ''; + + rows += ''; + } + + json.total = data.total_rows; + json.rows = $(rows); + return json; + } + } + }) + .bind('pagerInitialized pagerComplete', function(event, options){ + if ( options.totalRows == 0 ) { + $.tablesorter.showError($(this), ''); + } + }); + }); }); function displayCountdown() { @@ -298,6 +428,21 @@ + + +
+

+ +

+ + + +   + + + +
+