Index: lams_tool_assessment/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r1fd4080f02aa04c5938c33bc4d962749e1a3882b -ra92e6d07a437f70d76d3927293986933ccce3fbe --- lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 1fd4080f02aa04c5938c33bc4d962749e1a3882b) +++ lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision a92e6d07a437f70d76d3927293986933ccce3fbe) @@ -385,6 +385,9 @@ label.average.rating =Average rating {0}/{1} votes label.your.rating =Your rating {0}, average rating {1}/{2} votes label.comment.textarea.tip =Type your comment here then click on the green tick +label.rating = Rating +label.comment =Comment +label.comment.date = Date error.resource.image.comment.blank =Comment can not be blank. #======= End labels: Exported 372 labels for en AU ===== Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java =================================================================== diff -u -r1307599981af4c3f63c2da571c07e150d4713e8d -ra92e6d07a437f70d76d3927293986933ccce3fbe --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java (.../LearningController.java) (revision 1307599981af4c3f63c2da571c07e150d4713e8d) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/LearningController.java (.../LearningController.java) (revision a92e6d07a437f70d76d3927293986933ccce3fbe) @@ -1142,7 +1142,7 @@ // It is rating other groups' answers on results page. // Criterion gets automatically created and there must be only one. List criteria = ratingService.getCriteriasByToolContentId(assessment.getContentId()); - if (criteria.size() > 2) { + if (criteria.size() >= 2) { throw new IllegalArgumentException("There can be only one criterion for an Assessment activity. " + "If other criteria are introduced, the criterion for rating other groups' answers needs to become uniquely identifiable."); } Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java =================================================================== diff -u -r36e9121497b2c963250d22ec0f660fd66934182e -ra92e6d07a437f70d76d3927293986933ccce3fbe --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 36e9121497b2c963250d22ec0f660fd66934182e) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision a92e6d07a437f70d76d3927293986933ccce3fbe) @@ -29,10 +29,14 @@ import java.util.Date; import java.util.LinkedList; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TimeZone; import java.util.TreeSet; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; @@ -46,6 +50,7 @@ import org.lamsfoundation.lams.qb.dto.QbStatsActivityDTO; import org.lamsfoundation.lams.qb.model.QbQuestion; import org.lamsfoundation.lams.qb.service.IQbService; +import org.lamsfoundation.lams.rating.dto.RatingCommentDTO; import org.lamsfoundation.lams.rating.model.Rating; import org.lamsfoundation.lams.rating.model.RatingCriteria; import org.lamsfoundation.lams.rating.service.IRatingService; @@ -68,6 +73,7 @@ import org.lamsfoundation.lams.tool.assessment.model.QuestionReference; 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.util.CommonConstants; import org.lamsfoundation.lams.util.Configuration; @@ -496,7 +502,7 @@ // It is rating other groups' answers on results page. // Criterion gets automatically created in learner and there must be only one. List criteria = ratingService.getCriteriasByToolContentId(assessment.getContentId()); - if (criteria.size() > 2) { + if (criteria.size() >= 2) { throw new IllegalArgumentException("There can be only one criterion for an Assessment activity. " + "If other criteria are introduced, the criterion for rating other groups' answers needs to become uniquely identifiable."); } @@ -555,12 +561,6 @@ } userData.add(AssessmentEscapeUtils.printResponsesForJqgrid(questionResult)); - if (userDto.getPortraitId() == null) { - userData.add(""); - } else { - userData.add(userDto.getPortraitId()); - } - } else { userData.add(""); userData.add(""); @@ -569,9 +569,19 @@ if (assessment.isEnableConfidenceLevels()) { userData.add(-1); } + if (question.isGroupsAnswersDisclosed()) { + userData.add("-"); + } userData.add("-"); } + userData.add(userDto.getUserId()); + if (userDto.getPortraitId() == null) { + userData.add(""); + } else { + userData.add(userDto.getPortraitId()); + } + ObjectNode userRow = JsonNodeFactory.instance.objectNode(); userRow.put("id", i++); userRow.set("cell", userData); @@ -590,6 +600,61 @@ return null; } + @SuppressWarnings("unchecked") + @RequestMapping("/getAnswerRatings") + @ResponseBody + public String getAnswerRatings(@RequestParam long questionResultUid, Locale locale, HttpServletResponse response) + throws IOException { + AssessmentQuestionResult questionResult = service.getAssessmentQuestionResultByUid(questionResultUid); + long toolContentId = questionResult.getAssessmentResult().getAssessment().getContentId(); + List criteria = ratingService.getCriteriasByToolContentId(toolContentId); + if (criteria.size() >= 2) { + throw new IllegalArgumentException("There can be only one criterion for an Assessment activity. " + + "If other criteria are introduced, the criterion for rating other groups' answers needs to become uniquely identifiable."); + } + + Long ratingCriteriaId = criteria.get(0).getRatingCriteriaId(); + List ratings = ratingService.getRatingsByCriteriasAndItems(Arrays.asList(ratingCriteriaId), + Arrays.asList(questionResultUid)); + List comments = ratingService.getCommentsByCriteriaAndItem(ratingCriteriaId, null, + questionResultUid); + Map commentsByUserId = comments.stream() + .collect(Collectors.toMap(RatingCommentDTO::getUserId, Function.identity())); + + ArrayNode rows = JsonNodeFactory.instance.arrayNode(); + int i = 0; + + for (Rating rating : ratings) { + ArrayNode ratingJSON = JsonNodeFactory.instance.arrayNode(); + User learner = rating.getLearner(); + long userId = learner.getUserId(); + RatingCommentDTO comment = commentsByUserId.get(userId); + + ratingJSON.add(rating.getUid()); + ratingJSON.add(comment.getUserFullName()); + ratingJSON.add(DateUtil.convertToStringForJSON(comment.getPostedDate(), locale)); + ratingJSON.add(NumberUtil.formatLocalisedNumberForceDecimalPlaces(rating.getRating(), null, 2)); + ratingJSON.add(HtmlUtils.htmlEscape(comment.getComment())); + ratingJSON.add(userId); + ratingJSON.add(learner.getPortraitUuid() == null ? "" : learner.getPortraitUuid().toString()); + + ObjectNode ratingRow = JsonNodeFactory.instance.objectNode(); + ratingRow.put("id", i++); + ratingRow.set("cell", ratingJSON); + + rows.add(ratingRow); + } + + ObjectNode responseJSON = JsonNodeFactory.instance.objectNode(); + responseJSON.put("total", 1); + responseJSON.put("page", 1); + responseJSON.put("records", rows.size()); + responseJSON.set("rows", rows); + + response.setContentType("application/json;charset=utf-8"); + return responseJSON.toString(); + } + /** * Get the mark summary with data arranged in bands. Can be displayed graphically or in a table. */ Index: lams_tool_assessment/web/pages/monitoring/parts/questionsummary.jsp =================================================================== diff -u -r36e9121497b2c963250d22ec0f660fd66934182e -ra92e6d07a437f70d76d3927293986933ccce3fbe --- lams_tool_assessment/web/pages/monitoring/parts/questionsummary.jsp (.../questionsummary.jsp) (revision 36e9121497b2c963250d22ec0f660fd66934182e) +++ lams_tool_assessment/web/pages/monitoring/parts/questionsummary.jsp (.../questionsummary.jsp) (revision a92e6d07a437f70d76d3927293986933ccce3fbe) @@ -90,13 +90,16 @@ "", "", + 'userId', 'portraitId' ], colModel:[ {name:'questionResultUid', index:'questionResultUid', width:0, hidden: true}, {name:'maxMark', index:'maxMark', width:0, hidden: true}, - {name:'userName',index:'userName', width:120, searchoptions: { clearSearch: false }, formatter:userNameFormatter}, + {name:'userName',index:'userName', width:120, searchoptions: { clearSearch: false }, formatter : function(cellvalue, options, rowObject) { + return definePortraitPopover(rowObject[rowObject.length - 1], rowObject[rowObject.length - 2], rowObject[2]); + }}, {name:'grade', index:'grade', width:80, sorttype:"float", search:false, editable:true, editoptions: {size:4, maxlength: 4}, align:"right", classes: 'vertical-align' }, {name:'confidence', index:'confidence', width: 80, search:false, classes: 'vertical-align', formatter: gradientNumberFormatter}, @@ -105,6 +108,7 @@ {name:'rating', index:'rating', width:120, align:"center", search:false}, {name:'response', index:'response', width:400, sortable:false, search:false}, + {name:'userId', index:'userId', width:0, hidden: true}, {name:'portraitId', index:'portraitId', width:0, hidden: true} ], multiselect: false, @@ -144,7 +148,61 @@ loadComplete: function () { initializeJRating(); initializePortraitPopover(''); - } + }, + subGrid: true, + subGridOptions: { + reloadOnExpand : false, + hasSubgrid: function (options) { + // if there are no ratings for the given answer, there will be no subgrid + return options.data.rating != '-'; + } + }, + subGridRowExpanded: function(subgrid_id, row_id) { + var subgridTableId = subgrid_id+"_t"; + var questionResultUid = jQuery("#session${sessionDto.sessionId}") + .getRowData(row_id)["questionResultUid"]; + + jQuery("#"+subgrid_id).html("
"); + + jQuery("#"+subgridTableId).jqGrid({ + datatype: "json", + loadonce:true, + rowNum: 100, + url: "?questionResultUid=" + questionResultUid, + height: "100%", + autowidth:true, + autoencode:false, + guiStyle: "bootstrap", + iconSet: 'fontAwesome', + colNames:[ + 'ratingId', + '', + '', + '', + '', + 'userId', + 'portraitId' + ], + colModel:[ + {name:'ratingId', index:'ratingId', width:0, hidden:true}, + {name:'userName',index:'userName', width:45, formatter : function(cellvalue, options, rowObject) { + // get portrait UUID, user ID and user name + return definePortraitPopover(rowObject[rowObject.length - 1], rowObject[rowObject.length - 2], rowObject[1]); + }}, + {name:'date', index:'date', width:45, align:"center", title:false}, + {name:'rating', index:'rating', width:31, align:"center", title:false}, + {name:'comment', index:'comment', title:false}, + {name:'userId', index:'userId', width:0, hidden: true}, + {name:'portraitId', index:'portraitId', width:0, hidden: true} + ], + loadComplete: function () { + initializePortraitPopover(''); + }, + loadError: function(xhr,st,err) { + jQuery("#"+subgridTableId).clearGridData(); + } + }); + } }) .jqGrid('filterToolbar', { @@ -214,12 +272,6 @@ self.parent.tb_remove(); } } - - function userNameFormatter (cellvalue, options, rowObject) { - var portraitId = rowObject[rowObject.length - 1]; - return definePortraitPopover(portraitId, rowObject[0], rowObject[2]); - } -