Index: lams_build/lib/lams/lams.jar =================================================================== RCS file: /usr/local/cvsroot/lams_build/lib/lams/lams.jar,v diff -u -r1.420.2.95 -r1.420.2.96 Binary files differ Index: lams_central/src/java/org/lamsfoundation/lams/web/RatingServlet.java =================================================================== RCS file: /usr/local/cvsroot/lams_central/src/java/org/lamsfoundation/lams/web/RatingServlet.java,v diff -u -r1.1.2.7 -r1.1.2.8 --- lams_central/src/java/org/lamsfoundation/lams/web/RatingServlet.java 11 May 2016 07:07:58 -0000 1.1.2.7 +++ lams_central/src/java/org/lamsfoundation/lams/web/RatingServlet.java 10 Oct 2016 00:58:25 -0000 1.1.2.8 @@ -81,6 +81,7 @@ // get rating value as either float or comment String try { boolean doSave = true; + boolean ratingLimitsByCriteria = WebUtil.readBooleanParam(request, "ratingLimitsByCriteria", false); Long maxRatingsForItem = WebUtil.readLongParam(request, "maxRatingsForItem", true); log.debug("RatingServlet: Check Max rates for an item reached. Item " + itemId + " criteria id " @@ -93,8 +94,10 @@ ToolActivityRatingCriteria toolCriteria = (ToolActivityRatingCriteria) criteria; List itemIds = new LinkedList(); itemIds.add(itemId); - Map itemIdToRatedUsersCountMap = ratingService - .countUsersRatedEachItem(toolCriteria.getToolContentId(), itemIds, userId.intValue()); + Map itemIdToRatedUsersCountMap = ratingLimitsByCriteria ? + ratingService.countUsersRatedEachItemByCriteria(ratingCriteriaId, itemIds, userId) : + ratingService.countUsersRatedEachItem(toolCriteria.getToolContentId(), itemIds, userId); + Long currentRatings = itemIdToRatedUsersCountMap.get(itemId); if (currentRatings != null && maxRatingsForItem.compareTo(currentRatings) <= 0) { JSONObject.put("error", true); @@ -109,11 +112,16 @@ if (doSave) { if (criteria.isCommentsEnabled()) { - String comment = WebUtil.readStrParam(request, "comment"); - ratingService.commentItem(criteria, userId, itemId, comment); - JSONObject.put("comment", StringEscapeUtils.escapeCsv(comment)); - - } else { + // can have but do not have to have comment + String comment = WebUtil.readStrParam(request, "comment", true); + if ( comment != null ) { + ratingService.commentItem(criteria, userId, itemId, comment); + JSONObject.put("comment", StringEscapeUtils.escapeCsv(comment)); + } + } + + String floatString = request.getParameter("rate"); + if ( floatString != null && floatString.length() > 0 ) { float rating = Float.parseFloat(request.getParameter("rate")); ItemRatingCriteriaDTO averageRatingDTO = ratingService.rateItem(criteria, userId, itemId, rating); @@ -128,12 +136,15 @@ boolean hasRatingLimits = WebUtil.readBooleanParam(request, "hasRatingLimits", false); // refresh countRatedItems in case there is rating limit set + // Preview tool counts rated items on a criteria basis, other tools on a set of criteria basis! if (hasRatingLimits) { // as long as this can be requested only for LEARNER_ITEM_CRITERIA_TYPE type, cast Criteria LearnerItemRatingCriteria learnerItemRatingCriteria = (LearnerItemRatingCriteria) criteria; Long toolContentId = learnerItemRatingCriteria.getToolContentId(); - int countRatedItems = ratingService.getCountItemsRatedByUser(toolContentId, userId); + int countRatedItems = ratingLimitsByCriteria ? + ratingService.getCountItemsRatedByUserByCriteria(ratingCriteriaId, userId) : + ratingService.getCountItemsRatedByUser(toolContentId, userId); JSONObject.put("countRatedItems", countRatedItems); } } Index: lams_central/web/css/defaultHTML_learner.css =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/css/defaultHTML_learner.css,v diff -u -r1.32.2.32 -r1.32.2.33 --- lams_central/web/css/defaultHTML_learner.css 2 Oct 2016 02:10:45 -0000 1.32.2.32 +++ lams_central/web/css/defaultHTML_learner.css 10 Oct 2016 00:58:17 -0000 1.32.2.33 @@ -25,6 +25,23 @@ padding-right: 5px; padding-left: 5px; } + .rating-criteria-tag { + width: 100%; + } + .rating-criteria-tag input[type="text"] { + width: 100%; + } + .rating-criteria-tag .ui-widget input{ + font-family:"Helvetica Neue",Helvetica,Arial,sans-serif; + font-size:11px; + } + #criterias-table { + margin-left: 0px; + border-bottom: inherit; + } + #criterias-table td { + vertical-align:middle; + } .navbar-login { margin-bottom: 5px; } @@ -152,10 +169,6 @@ .panel-admin-title { color:#337ab7 !important; } -.panel-title { - font-size: inherit; - font-weight: bolder; -} body.stripes { background: url('/lams/images/css/light-fabric.jpg'); } @@ -347,8 +360,16 @@ .rating-criteria-tag input[type="text"] { width: 100%; } +.rating-criteria-tag .ui-widget input{ + font-family:"Helvetica Neue",Helvetica,Arial,sans-serif; + font-size:12px; + line-height:1.42857143; + padding: 2px; +} + #criterias-table { margin-left: 0px; + border-bottom: inherit; } #criterias-table td { vertical-align:middle; @@ -493,14 +514,4 @@ } section{ padding-left: 15px; -} - -/* Login page */ - -.footer { - width: 100%; - /* Set the fixed height of the footer here */ - background-color: #f5f5f5; - font-size: 75%; - border-top: 1px solid#e7e7e7; -} +} \ No newline at end of file Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_central/web/includes/javascript/interact.min.js'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_central/web/includes/javascript/jquery.ui.touch-punch.js =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/includes/javascript/jquery.ui.touch-punch.js,v diff -u -r1.1 -r1.1.2.1 --- lams_central/web/includes/javascript/jquery.ui.touch-punch.js 18 Nov 2013 17:57:14 -0000 1.1 +++ lams_central/web/includes/javascript/jquery.ui.touch-punch.js 10 Oct 2016 00:58:17 -0000 1.1.2.1 @@ -1,11 +1,183 @@ -/* - * jQuery UI Touch Punch 0.2.2 +/*! + * jQuery UI Touch Punch 0.2.3 * - * Copyright 2011, Dave Furfero + * Copyright 2011–2014, Dave Furfero * Dual licensed under the MIT or GPL Version 2 licenses. * * Depends: * jquery.ui.widget.js * jquery.ui.mouse.js */ -(function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery); \ No newline at end of file +(function ($) { + + debugger; + // Detect touch support + $.support.touch = 'ontouchend' in document; + + // Ignore browsers without touch support + if (!$.support.touch) { + return; + } + + var mouseProto = $.ui.mouse.prototype, + _mouseInit = mouseProto._mouseInit, + _mouseDestroy = mouseProto._mouseDestroy, + touchHandled; + + /** + * Simulate a mouse event based on a corresponding touch event + * @param {Object} event A touch event + * @param {String} simulatedType The corresponding mouse event + */ + function simulateMouseEvent (event, simulatedType) { + + // Ignore multi-touch events + if (event.originalEvent.touches.length > 1) { + return; + } + + event.preventDefault(); + + var touch = event.originalEvent.changedTouches[0], + simulatedEvent = document.createEvent('MouseEvents'); + + // Initialize the simulated mouse event using the touch event's coordinates + simulatedEvent.initMouseEvent( + simulatedType, // type + true, // bubbles + true, // cancelable + window, // view + 1, // detail + touch.screenX, // screenX + touch.screenY, // screenY + touch.clientX, // clientX + touch.clientY, // clientY + false, // ctrlKey + false, // altKey + false, // shiftKey + false, // metaKey + 0, // button + null // relatedTarget + ); + + // Dispatch the simulated event to the target element + event.target.dispatchEvent(simulatedEvent); + } + + /** + * Handle the jQuery UI widget's touchstart events + * @param {Object} event The widget element's touchstart event + */ + mouseProto._touchStart = function (event) { + + debugger; + + var self = this; + + // Ignore the event if another widget is already being handled + if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) { + return; + } + + // Set the flag to prevent other widgets from inheriting the touch event + touchHandled = true; + + // Track movement to determine if interaction was a click + self._touchMoved = false; + + // Simulate the mouseover event + simulateMouseEvent(event, 'mouseover'); + + // Simulate the mousemove event + simulateMouseEvent(event, 'mousemove'); + + // Simulate the mousedown event + simulateMouseEvent(event, 'mousedown'); + }; + + /** + * Handle the jQuery UI widget's touchmove events + * @param {Object} event The document's touchmove event + */ + mouseProto._touchMove = function (event) { + + // Ignore event if not handled + if (!touchHandled) { + return; + } + + // Interaction was not a click + this._touchMoved = true; + + // Simulate the mousemove event + simulateMouseEvent(event, 'mousemove'); + }; + + /** + * Handle the jQuery UI widget's touchend events + * @param {Object} event The document's touchend event + */ + mouseProto._touchEnd = function (event) { + + // Ignore event if not handled + if (!touchHandled) { + return; + } + + // Simulate the mouseup event + simulateMouseEvent(event, 'mouseup'); + + // Simulate the mouseout event + simulateMouseEvent(event, 'mouseout'); + + // If the touch interaction did not move, it should trigger a click + if (!this._touchMoved) { + + // Simulate the click event + simulateMouseEvent(event, 'click'); + } + + // Unset the flag to allow other widgets to inherit the touch event + touchHandled = false; + }; + + /** + * A duck punch of the $.ui.mouse _mouseInit method to support touch events. + * This method extends the widget with bound touch event handlers that + * translate touch events to mouse events and pass them to the widget's + * original mouse event handling methods. + */ + mouseProto._mouseInit = function () { + + var self = this; + + // Delegate the touch handlers to the widget's element + self.element.bind({ + touchstart: $.proxy(self, '_touchStart'), + touchmove: $.proxy(self, '_touchMove'), + touchend: $.proxy(self, '_touchEnd') + }); + + // Call the original $.ui.mouse init method + _mouseInit.call(self); + }; + + /** + * Remove the touch event handlers + */ + mouseProto._mouseDestroy = function () { + + var self = this; + + // Delegate the touch handlers to the widget's element + self.element.unbind({ + touchstart: $.proxy(self, '_touchStart'), + touchmove: $.proxy(self, '_touchMove'), + touchend: $.proxy(self, '_touchEnd') + }); + + // Call the original $.ui.mouse destroy method + _mouseDestroy.call(self); + }; + +})(jQuery); \ No newline at end of file Index: lams_central/web/includes/javascript/rating.js =================================================================== RCS file: /usr/local/cvsroot/lams_central/web/includes/javascript/rating.js,v diff -u -r1.1.2.5 -r1.1.2.6 --- lams_central/web/includes/javascript/rating.js 21 Oct 2015 01:42:24 -0000 1.1.2.5 +++ lams_central/web/includes/javascript/rating.js 10 Oct 2016 00:58:17 -0000 1.1.2.6 @@ -30,17 +30,26 @@ maxRatingsForItem = ""; else maxRatingsForItem = MAX_RATINGS_FOR_ITEM; - + + var ratingLimitsByCriteria; + if ( typeof LIMIT_BY_CRITERIA === "undefined" || LIMIT_BY_CRITERIA === undefined ) + ratingLimitsByCriteria = false; + else + ratingLimitsByCriteria = LIMIT_BY_CRITERIA; + $(".rating-stars-new").filter($(".rating-stars")).jRating({ - phpPath : LAMS_URL + "servlet/rateItem?hasRatingLimits=" + HAS_RATING_LIMITS+"&maxRatingsForItem="+maxRatingsForItem, + phpPath : LAMS_URL + "servlet/rateItem?hasRatingLimits=" + HAS_RATING_LIMITS + "&ratingLimitsByCriteria=" + ratingLimitsByCriteria + "&maxRatingsForItem=" + maxRatingsForItem, rateMax : 5, decimalLength : 1, onSuccess : function(data, itemId){ $("#user-rating-" + itemId).html(data.userRating); $("#average-rating-" + itemId).html(data.averageRating); $("#number-of-votes-" + itemId).html(data.numberOfVotes); $("#rating-stars-caption-" + itemId).css("visibility", "visible"); + var parts = itemId.split('-'); + $("#comment-tick-" + parts[1]).css("visibility", "visible"); + //handle rating limits if available handleRatingLimits(data.countRatedItems, itemId); }, @@ -93,6 +102,7 @@ idBox: commentsCriteriaId + '-' + itemId, comment: comment, hasRatingLimits: HAS_RATING_LIMITS, + ratingLimitsByCriteria: ratingLimitsByCriteria, maxRatingsForItem: maxRatingsForItem }, success: function(data, textStatus) { @@ -137,6 +147,8 @@ if (HAS_RATING_LIMITS) { + debugger; + //update info box $("#count-rated-items").html(countRatedItems); Index: lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/rating/Rating.hbm.xml =================================================================== RCS file: /usr/local/cvsroot/lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/rating/Rating.hbm.xml,v diff -u -r1.1.2.2 -r1.1.2.3 --- lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/rating/Rating.hbm.xml 13 Apr 2015 18:17:52 -0000 1.1.2.2 +++ lams_common/conf/hibernate/mappings/org/lamsfoundation/lams/rating/Rating.hbm.xml 10 Oct 2016 00:53:43 -0000 1.1.2.3 @@ -58,7 +58,7 @@ @hibernate.property column="rating_criteria_type_id" length="11" + + + + + + + + + + + + Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch2040071.sql'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_common/src/java/org/lamsfoundation/lams/rating/dao/IRatingCommentDAO.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/dao/IRatingCommentDAO.java,v diff -u -r1.3.2.4 -r1.3.2.5 --- lams_common/src/java/org/lamsfoundation/lams/rating/dao/IRatingCommentDAO.java 11 May 2016 07:07:28 -0000 1.3.2.4 +++ lams_common/src/java/org/lamsfoundation/lams/rating/dao/IRatingCommentDAO.java 10 Oct 2016 00:53:43 -0000 1.3.2.5 @@ -20,7 +20,6 @@ * **************************************************************** */ - package org.lamsfoundation.lams.rating.dao; import java.util.Collection; @@ -63,6 +62,11 @@ List getCommentsByCriteriaAndItemsAndUser(Long ratingCriteriaId, Collection itemIds, Integer userId); + /** + * Get the comment relating to a ranking/hedging/star criteria, rather than the comment for a comment criteria. + */ + List getRelatedCommentByCriteriaAndUser(Long ratingCriteriaId, Integer userId); + RatingComment getComment(Long ratingCriteriaId, Integer userId, Long itemId); } Index: lams_common/src/java/org/lamsfoundation/lams/rating/dao/IRatingDAO.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/dao/IRatingDAO.java,v diff -u -r1.1.2.6 -r1.1.2.7 --- lams_common/src/java/org/lamsfoundation/lams/rating/dao/IRatingDAO.java 11 May 2016 07:07:27 -0000 1.1.2.6 +++ lams_common/src/java/org/lamsfoundation/lams/rating/dao/IRatingDAO.java 10 Oct 2016 00:53:43 -0000 1.1.2.7 @@ -30,18 +30,23 @@ import java.util.Map; import org.lamsfoundation.lams.rating.dto.ItemRatingCriteriaDTO; +import org.lamsfoundation.lams.rating.dto.StyledRatingDTO; import org.lamsfoundation.lams.rating.model.Rating; public interface IRatingDAO { void saveOrUpdate(Object object); + + void delete(Object object); Rating getRating(Long ratingCriteriaId, Integer userId, Long itemId); List getRatingsByItem(Long contentId, Long itemId); List getRatingsByUser(Long contentId, Integer userId); + List getRatingsByUserCriteria(Long criteriaId, Integer userId); + /** * Returns rating statistics by particular item * @@ -83,6 +88,16 @@ int getCountItemsRatedByUser(final Long toolContentId, final Integer userId); /** + * Returns number of items rated by specified user in a current activity, for a particular criteria. It counts comments as ratings + * iff it is a comment rating. This method is applicable only for RatingCriterias of LEARNER_ITEM_CRITERIA_TYPE type. + * + * @param toolContentId + * @param userId + * @return + */ + int getCountItemsRatedByUserByCriteria(final Long criteriaId, final Integer userId); + + /** * Count how many users rated and commented each item. * * @param contentId @@ -93,4 +108,21 @@ Map countUsersRatedEachItem(final Long contentId, final Collection itemIds, Integer excludeUserId); + /** + * Count how many users rated and commented each item for a particular criteria. + * + * @param contentId + * @param itemIds + * @param excludeUserId + * @return + */ + Map countUsersRatedEachItemByCriteria(final Long criteriaId, final Collection itemIds, + Integer excludeUserId); + + /** + * Used by tools to get the ratings and comments relating to their items. To be used within SQL and supply the toolContentId as :toolContentId. + * See Peer Review for example usage. + */ + String getRatingSelectJoinSQL(Integer ratingStyle, boolean getByUser); + } Index: lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingCommentDAO.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingCommentDAO.java,v diff -u -r1.4.2.6 -r1.4.2.7 --- lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingCommentDAO.java 11 May 2016 07:07:29 -0000 1.4.2.6 +++ lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingCommentDAO.java 10 Oct 2016 00:53:43 -0000 1.4.2.7 @@ -52,6 +52,9 @@ private static final String FIND_COMMENTS_BY_CRITERIA = "SELECT r.itemId, r.learner.userId, r.comment " + "FROM " + RatingComment.class.getName() + " AS r where r.ratingCriteria.ratingCriteriaId=?"; + private static final String FIND_RELATED_COMMENT_BY_CRITERIA_AND_USER = "SELECT r.itemId, r.learner.userId, r.comment FROM " + RatingComment.class.getName() + + " AS r where r.ratingCriteria.ratingCriteriaId=:ratingCriteriaId AND r.learner.userId=:userId"; + // private static final String COUNT_COMMENTS_BY_ITEM_AND_USER = "SELECT COUNT(r) FROM " // + RatingComment.class.getName() // + " AS r " @@ -93,6 +96,14 @@ return convertIntoCommentDtos(results); } + @Override + public List getRelatedCommentByCriteriaAndUser(Long ratingCriteriaId, Integer userId) { + List results = getSession().createQuery(FIND_RELATED_COMMENT_BY_CRITERIA_AND_USER) + .setLong("ratingCriteriaId", ratingCriteriaId) + .setInteger("userId", userId).list(); + + return convertIntoCommentDtos(results); + } /* * Converts DB results presentation into list of RatingCommentDTO. * Index: lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingCriteriaDAO.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingCriteriaDAO.java,v diff -u -r1.1.2.6 -r1.1.2.7 --- lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingCriteriaDAO.java 11 May 2016 07:07:29 -0000 1.1.2.6 +++ lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingCriteriaDAO.java 10 Oct 2016 00:53:43 -0000 1.1.2.7 @@ -40,10 +40,10 @@ + " AS r WHERE r.toolContentId=? order by r.orderId asc"; private static final String IS_COMMENTS_ENABLED_FOR_TOOL_CONTENT_ID = "SELECT COUNT(*) FROM " - + RatingCriteria.class.getName() + " AS r WHERE r.toolContentId=? AND r.commentsEnabled=1"; + + RatingCriteria.class.getName() + " AS r WHERE r.toolContentId=? AND r.ratingStyle = 0"; private static final String GET_COMMENTS_MIN_WORDS_LIMIT_FOR_TOOL_CONTENT_ID = "SELECT r.commentsMinWordsLimit FROM " - + RatingCriteria.class.getName() + " AS r WHERE r.toolContentId=? AND r.commentsEnabled=1"; + + RatingCriteria.class.getName() + " AS r WHERE r.toolContentId=? AND r.ratingStyle = 0"; @Override public void saveOrUpdate(RatingCriteria criteria) { Index: lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingDAO.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingDAO.java,v diff -u -r1.1.2.6 -r1.1.2.7 --- lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingDAO.java 11 May 2016 07:07:29 -0000 1.1.2.6 +++ lams_common/src/java/org/lamsfoundation/lams/rating/dao/hibernate/RatingDAO.java 10 Oct 2016 00:53:43 -0000 1.1.2.7 @@ -39,6 +39,7 @@ import org.lamsfoundation.lams.rating.dto.ItemRatingCriteriaDTO; import org.lamsfoundation.lams.rating.model.Rating; import org.lamsfoundation.lams.rating.model.RatingComment; +import org.lamsfoundation.lams.rating.model.RatingCriteria; public class RatingDAO extends LAMSBaseDAO implements IRatingDAO { @@ -57,6 +58,9 @@ private static final String FIND_RATINGS_BY_USER = "FROM " + Rating.class.getName() + " AS r where r.ratingCriteria.toolContentId=? AND r.learner.userId=?"; + private static final String FIND_RATINGS_BY_USER_CRITERIA = "FROM " + Rating.class.getName() + + " AS r where r.ratingCriteria.ratingCriteriaId=? AND r.learner.userId=?"; + private static final String FIND_RATING_AVERAGE_BY_ITEM = "SELECT AVG(r.rating), COUNT(*) FROM " + Rating.class.getName() + " AS r where r.ratingCriteria.ratingCriteriaId=? AND r.itemId=?"; @@ -72,6 +76,38 @@ + Rating.class.getName() + " AS r where r.ratingCriteria.toolContentId=? GROUP BY r.itemId, r.ratingCriteria.ratingCriteriaId"; + // Used by tools to get the ratings and comments relating to their items, as submitted by a particular user. + // To be used within SQL and supply the userId as :userId and criteriaId as :ratingCriteriaId + // See Peer Review for example usage. Special version for comment style as there is no entry in the lams_rating table for a comment style rating. + private static final String TOOL_SELECT_LEFT_JOIN_BY_USER_STANDARD = "SELECT r.item_id, rc.comment, r2.rating, AVG(r.rating) average_rating, COUNT(r.rating) count_vote " + + " FROM lams_rating r " + + " LEFT JOIN lams_rating r2 ON r2.rating_criteria_id = r.rating_criteria_id AND r.item_id = r2.item_id AND r2.user_id = :userId " + + " LEFT JOIN lams_rating_comment rc ON rc.rating_criteria_id = r.rating_criteria_id AND rc.item_id = r2.item_id AND rc.user_id = :userId " + + " WHERE r.rating_criteria_id = :ratingCriteriaId " + + " GROUP BY r.item_id"; + + // Used by tools to get the ratings and comments relating to their items. To be used within SQL and supply criteriaId as :ratingCriteriaId + // See Peer Review for example usage. + private static final String TOOL_SELECT_LEFT_JOIN_BY_USER_COMMENT = "SELECT r.item_id, r.comment " + + " FROM lams_rating_comment r WHERE r.rating_criteria_id = :ratingCriteriaId "; + + // Same as TOOL_SELECT_LEFT_JOIN_BY_USER_STANDARD except that it returns the average results for a single user (:userId), as left by other users + private static final String TOOL_SELECT_LEFT_JOIN_FOR_USER_STANDARD = "SELECT DISTINCT r.item_id, rc.comment, NULL rating, calc.average_rating, calc.count_vote " + + " FROM lams_rating r" + + " JOIN lams_rating_criteria c ON r.item_id = :userId AND c.tool_content_id = :toolContentId " + + " AND c.rating_criteria_id = :ratingCriteriaId AND c.rating_criteria_id = r.rating_criteria_id " + + " LEFT JOIN lams_rating_comment rc ON rc.rating_criteria_id = r.rating_criteria_id AND rc.item_id = r.item_id and rc.user_id = r.user_id " + + " LEFT JOIN ( " + + " SELECT r2.item_id, AVG(r2.rating) average_rating, COUNT(*) count_vote " + + " FROM lams_rating r2 WHERE r2.rating_criteria_id = :ratingCriteriaId AND r2.item_id = :userId " + + " GROUP BY r2.item_id " + + " ) calc ON calc.item_id = r.item_id "; + + // Same as TOOL_SELECT_LEFT_JOIN_BY_USER_COMMENT except that it returns all the comments results for a single user (:userId), as left by other users + private static final String TOOL_SELECT_LEFT_JOIN_FOR_USER_COMMENT = "SELECT r.item_id, r.comment" + + " FROM lams_rating_comment r " + + " WHERE r.item_id = :userId AND r.rating_criteria_id = :ratingCriteriaId"; + // private static final String COUNT_ITEMS_RATED_BY_ACTIVITY_AND_USER = "SELECT COUNT(DISTINCT r.itemId)+(SELECT COUNT(comment) FROM " // + RatingComment.class.getName() // + " AS comment " @@ -86,6 +122,11 @@ getSession().flush(); } + public void delete(Object object) { + getSession().delete(object); + getSession().flush(); + } + @Override public Rating getRating(Long ratingCriteriaId, Integer userId, Long itemId) { List list = (List) doFind(FIND_RATING_BY_CRITERIA_AND_USER_AND_ITEM, @@ -119,6 +160,11 @@ } @Override + public List getRatingsByUserCriteria(Long criteriaId, Integer userId) { + return (List) doFind(FIND_RATINGS_BY_USER_CRITERIA, new Object[] { criteriaId, userId }); + } + + @Override public ItemRatingCriteriaDTO getRatingAverageDTOByItem(Long ratingCriteriaId, Long itemId) { List list = (List) doFind(FIND_RATING_AVERAGE_BY_ITEM, new Object[] { ratingCriteriaId, itemId }); @@ -183,6 +229,29 @@ } @Override + public int getCountItemsRatedByUserByCriteria(final Long criteriaId, final Integer userId) { + + // unions don't work in HQL so doing 2 separate DB queries (http://stackoverflow.com/a/3940445) + String FIND_ITEM_IDS_RATED_BY_USER = "SELECT DISTINCT r.itemId FROM " + Rating.class.getName() + " AS r " + + " WHERE r.ratingCriteria.ratingCriteriaId = :criteriaId AND r.learner.userId =:userId"; + + String FIND_ITEM_IDS_COMMENTED_BY_USER = "SELECT DISTINCT comment.itemId FROM " + RatingComment.class.getName() + + " AS comment " + + " WHERE comment.ratingCriteria.ratingCriteriaId = :criteriaId AND comment.learner.userId =:userId AND comment.ratingCriteria.commentsEnabled IS TRUE"; + + List ratedItemIds = this.getSession().createQuery(FIND_ITEM_IDS_RATED_BY_USER) + .setLong("criteriaId", criteriaId).setInteger("userId", userId).list(); + + List commentedItemIds = this.getSession().createQuery(FIND_ITEM_IDS_COMMENTED_BY_USER) + .setLong("criteriaId", criteriaId).setInteger("userId", userId).list(); + + Set unionItemIds = new HashSet(ratedItemIds); + unionItemIds.addAll(commentedItemIds); + + return unionItemIds.size(); + } + + @Override public Map countUsersRatedEachItem(final Long contentId, final Collection itemIds, Integer excludeUserId) { @@ -207,6 +276,43 @@ .createQuery(FIND_ITEMID_USERID_COMMENT_PAIRS_BY_CONTENT_AND_ITEMS).setLong("contentId", contentId) .setParameterList("itemIds", itemIds).list(); + return createUsersRatedEachItem(itemIds, excludeUserId, itemIdToRatedUsersCountMap, ratedItemObjs, + commentedItemObjs); + } + + @Override + public Map countUsersRatedEachItemByCriteria(final Long criteriaId, final Collection itemIds, + Integer excludeUserId) { + + HashMap itemIdToRatedUsersCountMap = new HashMap(); + if (itemIds.isEmpty()) { + return itemIdToRatedUsersCountMap; + } + + // unions don't work in HQL so doing 2 separate DB queries (http://stackoverflow.com/a/3940445) + String FIND_ITEMID_USERID_PAIRS_BY_CONTENT_AND_ITEMS = "SELECT r.itemId, r.learner.userId FROM " + + Rating.class.getName() + + " AS r where r.ratingCriteria.ratingCriteriaId=:criteriaId AND r.itemId IN (:itemIds)"; + + String FIND_ITEMID_USERID_COMMENT_PAIRS_BY_CONTENT_AND_ITEMS = "SELECT comment.itemId, comment.learner.userId FROM " + + RatingComment.class.getName() + + " AS r where comment.ratingCriteria.ratingStyle=0 AND comment.ratingCriteria.ratingCriteriaId=:criteriaId " + + " AND comment.itemId IN (:itemIds) AND comment.ratingCriteria.commentsEnabled IS TRUE"; + + List ratedItemObjs = getSession().createQuery(FIND_ITEMID_USERID_PAIRS_BY_CONTENT_AND_ITEMS) + .setLong("criteriaId", criteriaId).setParameterList("itemIds", itemIds).list(); + + List commentedItemObjs = getSession() + .createQuery(FIND_ITEMID_USERID_COMMENT_PAIRS_BY_CONTENT_AND_ITEMS).setLong("criteriaId", criteriaId) + .setParameterList("itemIds", itemIds).list(); + + return createUsersRatedEachItem(itemIds, excludeUserId, itemIdToRatedUsersCountMap, ratedItemObjs, + commentedItemObjs); + } + + private Map createUsersRatedEachItem(final Collection itemIds, Integer excludeUserId, + HashMap itemIdToRatedUsersCountMap, List ratedItemObjs, + List commentedItemObjs) { for (Long itemId : itemIds) { HashSet userIds = new HashSet(); @@ -239,5 +345,16 @@ return itemIdToRatedUsersCountMap; } - + /** + * Used by tools to get the ratings and comments relating to their items. To be used within SQL and supply the toolContentId as :toolContentId. + * If getAllValues == true then returns data for all users (monitoring), otherwise just the data for a single user. + * See Peer Review for example usage. + */ + public String getRatingSelectJoinSQL(Integer ratingStyle, boolean getByUser){ + if ( ratingStyle == RatingCriteria.RATING_STYLE_COMMENT ) + return getByUser ? TOOL_SELECT_LEFT_JOIN_BY_USER_COMMENT : TOOL_SELECT_LEFT_JOIN_FOR_USER_COMMENT; + else + return getByUser ? TOOL_SELECT_LEFT_JOIN_BY_USER_STANDARD : TOOL_SELECT_LEFT_JOIN_FOR_USER_STANDARD; + } + } Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/rating/dto/StyledCriteriaRatingDTO.java'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 1.1 refers to a dead (removed) revision in file `lams_common/src/java/org/lamsfoundation/lams/rating/dto/StyledRatingDTO.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_common/src/java/org/lamsfoundation/lams/rating/model/Rating.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/model/Rating.java,v diff -u -r1.1.2.5 -r1.1.2.6 --- lams_common/src/java/org/lamsfoundation/lams/rating/model/Rating.java 11 May 2016 07:07:28 -0000 1.1.2.5 +++ lams_common/src/java/org/lamsfoundation/lams/rating/model/Rating.java 10 Oct 2016 00:53:43 -0000 1.1.2.6 @@ -41,7 +41,7 @@ private User learner; - private float rating; + private Float rating; public Rating() { } @@ -95,11 +95,11 @@ /** */ - public void setRating(float rating) { + public void setRating(Float rating) { this.rating = rating; } - public float getRating() { + public Float getRating() { return this.rating; } } Index: lams_common/src/java/org/lamsfoundation/lams/rating/model/RatingCriteria.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/model/RatingCriteria.java,v diff -u -r1.1.2.5 -r1.1.2.6 --- lams_common/src/java/org/lamsfoundation/lams/rating/model/RatingCriteria.java 11 May 2016 07:07:28 -0000 1.1.2.5 +++ lams_common/src/java/org/lamsfoundation/lams/rating/model/RatingCriteria.java 10 Oct 2016 00:53:43 -0000 1.1.2.6 @@ -35,6 +35,24 @@ /** * Base class for all RatingCriterias. If you add another subclass, you must update * RatingCriteriaDAO.getRatingCriteriaByRatingCriteriaId() and add a ACTIVITY_TYPE constant. + * + * Criteria Styles: + * + * Comment: This supports the old style Peer Review comments, with every reviewer leaving one comment for each item/user + * being reviewed for a set of (star) criteria. + * Not support in the in Peer Review once it supports Ranking, Hedging, etc but kept for compatibility with existing data. + * Could be included in new style Peer Review with user interface changes. + * + * Star: The original style of comment, with up to 5 stars being used. For every star criteria, each reviewer leaves + * one star rating (in lams_rating) for each item/user and possibly one comment (in lams_rating_comment) for each item/user. + * + * Ranking: Ordering of the top 1, top 2, top 3, top 4, top 5 or all items/users. For every ranking criteria, each reviewer leaves + * one ranking (in lams_rating) for [ 1/2/3/4/5/all ] items/users and no comments. + * + * Hedging: Allocating out marks across items/users. For every hedging criteria, each reviewer leaves + * one ranking (in lams_rating) for 1 or more items/users and 0 or 1 justification comment overall (in lams_rating_comment). + * That is, for Hedging a reviewer makes one justification comment, whereas in Star the reviewer makes one comment for each user/item being reviewed. + * */ public abstract class RatingCriteria implements Serializable, Nullable, Comparable, Cloneable { @@ -65,6 +83,21 @@ public static final String I18N_DESCRIPTION = "rating.criteria.description"; public static final String I18N_HELP_TEXT = "rating.criteria.helptext"; + /** What style to use for doing the rating? Stored in Int member + * If Style = RATING_STYLE_COMMENT then the entry is for a comment criteria, with the data in RatingComment. + * If Style = RATING_STYLE_STAR then maxRating is 5, which lines up with the number of stars and comments are allowed. + * If Style = RATING_STYLE_RANKING then maxRating will be 1 through 5 or -1 for rank all. + * If Style = RATING_STYLE_HEDGING then maxRating contains the sum total mark to which the hedged ratings should add up. + * The comments table is also used to store the rating justification for hedging. */ + public static final int RATING_STYLE_COMMENT = 0; + public static final int RATING_STYLE_STAR = 1; + public static final int RATING_STYLE_RANKING = 2; + public static final int RATING_STYLE_HEDGING = 3; + + public static final int RATING_STYLE_STAR_DEFAULT_MAX = 5; + public static final int RATING_STYLE_RANKING_DEFAULT_MAX = 5; + public static final int RATING_STYLE_RANKING_RANK_ALL = 0; + // --------------------------------------------------------------------- // Instance variables // --------------------------------------------------------------------- @@ -83,10 +116,19 @@ /** The type of ratingCriteria */ private Integer ratingCriteriaTypeId; - private boolean commentsEnabled; + private boolean commentsEnabled; // comments for RATING_STYLE_COMMENT, RATING_STYLE_STAR justification for RATING_STYLE_HEDGING private int commentsMinWordsLimit; + private Integer ratingStyle; // see comments above for RATING_STYLE + + private Integer maxRating; // see comments above for RATING_STYLE + + private Integer minimumRates; // Minimum number of people for whom one user may rate this criteria. Used for RATING_STYLE_STAR. + + private Integer maximumRates; // Minimum number of people for whom one user may rate this criteria. Used for RATING_STYLE_STAR. + + // --------------------------------------------------------------------- // Object constructors // --------------------------------------------------------------------- @@ -97,22 +139,34 @@ /** full constructor */ public RatingCriteria(Long ratingCriteriaId, String title, Integer orderId, Date createDateTime, - Integer ratingCriteriaTypeId) { + Integer ratingCriteriaTypeId, Integer ratingStyle, Integer maxRating, Integer minimumRates, Integer maximumRates) { this.ratingCriteriaId = ratingCriteriaId; this.title = title; this.orderId = orderId; this.ratingCriteriaTypeId = ratingCriteriaTypeId; + this.ratingStyle = ratingStyle; + this.maxRating = maxRating; + this.minimumRates = minimumRates; + this.maximumRates = maximumRates; } /** default constructor */ public RatingCriteria() { + this.ratingStyle = RATING_STYLE_STAR; + this.maxRating = RATING_STYLE_STAR_DEFAULT_MAX; + this.minimumRates = 0; + this.maximumRates = 0; } /** minimal constructor */ public RatingCriteria(Long ratingCriteriaId, Date createDateTime, RatingCriteria parentRatingCriteria, Integer ratingCriteriaTypeId) { this.ratingCriteriaId = ratingCriteriaId; this.ratingCriteriaTypeId = ratingCriteriaTypeId; + this.ratingStyle = RATING_STYLE_STAR; + this.maxRating = RATING_STYLE_STAR_DEFAULT_MAX; + this.minimumRates = 0; + this.maximumRates = 0; } public static RatingCriteria getRatingCriteriaInstance(int ratingCriteriaType) { @@ -246,9 +300,72 @@ } // --------------------------------------------------------------------- + // RatingStyle checking methods + // --------------------------------------------------------------------- + /** + * Is it a Comment? + */ + public boolean isCommentRating() { + return getRatingStyle().intValue() == RatingCriteria.RATING_STYLE_COMMENT; + } + + /** + * Is it a Star Rating? + */ + public boolean isStarStyleRating() { + return getRatingStyle().intValue() == RatingCriteria.RATING_STYLE_STAR; + } + + /** + * Is it a Ranking? + */ + public boolean isRankingStyleRating() { + return getRatingStyle().intValue() == RatingCriteria.RATING_STYLE_RANKING; + } + + /** + * Is it a Hedging Mark? + */ + public boolean isHedgeStyleRating() { + return getRatingStyle().intValue() == RatingCriteria.RATING_STYLE_HEDGING; + } + + // --------------------------------------------------------------------- // Data Transfer object creation methods // --------------------------------------------------------------------- + public Integer getRatingStyle() { + return ratingStyle; + } + + public void setRatingStyle(Integer ratingStyle) { + this.ratingStyle = ratingStyle; + } + + public Integer getMaxRating() { + return maxRating; + } + + public void setMaxRating(Integer maxRating) { + this.maxRating = maxRating; + } + + public Integer getMinimumRates() { + return minimumRates; + } + + public void setMinimumRates(Integer minimumRates) { + this.minimumRates = minimumRates; + } + + public Integer getMaximumRates() { + return maximumRates; + } + + public void setMaximumRates(Integer maximumRates) { + this.maximumRates = maximumRates; + } + @Override public Object clone() { RatingCriteria criteria = null; Index: lams_common/src/java/org/lamsfoundation/lams/rating/service/IRatingService.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/service/IRatingService.java,v diff -u -r1.1.2.6 -r1.1.2.7 --- lams_common/src/java/org/lamsfoundation/lams/rating/service/IRatingService.java 11 May 2016 07:07:29 -0000 1.1.2.6 +++ lams_common/src/java/org/lamsfoundation/lams/rating/service/IRatingService.java 10 Oct 2016 00:53:43 -0000 1.1.2.7 @@ -31,14 +31,24 @@ import javax.servlet.http.HttpServletRequest; +import org.apache.tomcat.util.json.JSONArray; +import org.apache.tomcat.util.json.JSONException; import org.lamsfoundation.lams.rating.dto.ItemRatingCriteriaDTO; import org.lamsfoundation.lams.rating.dto.ItemRatingDTO; +import org.lamsfoundation.lams.rating.dto.StyledCriteriaRatingDTO; import org.lamsfoundation.lams.rating.model.Rating; import org.lamsfoundation.lams.rating.model.RatingCriteria; public interface IRatingService { void saveOrUpdateRating(Rating rating); + + /** + * Save a group of ratings as the new ratings for this criteria, marking any existing ratings NULL. + * Returns the number of "real" ratings, which should be newRatings.size. + * @return + */ + public int rateItems(RatingCriteria ratingCriteria, Integer userId, Map newRatings); /** * Read modified rating criterias from request, then update existing ones/add new ones/delete removed ones. Used on @@ -117,7 +127,30 @@ */ ItemRatingDTO getRatingCriteriaDtoWithActualRatings(Long contentId, Long itemId); + /** + * Used by tools to get the ratings and comments relating to their items. To be used within SQL and supply the toolContentId as :toolContentId, + * criteria id as :ratingCriteriaId and current user id as :userId + * If getByUser == true then returns data for all users, as left by the current user, otherwise gives the data for the current user as left by other users + * See Peer Review for example usage. + */ + String getRatingSelectJoinSQL(Integer ratingStyle, boolean getByUser); + + /** + * Convert the raw data from the database to StyledCriteriaRatingDTO and StyleRatingDTO. The rating service expects its own fields + * to be first in each Object array, and the last item in the array to be an item description (eg formatted user's name) + * Will go back to the database for the justification comment that would apply to hedging. + */ + StyledCriteriaRatingDTO convertToStyledDTO(RatingCriteria ratingCriteria, Long currentUser, boolean includeCurrentUser, List rawDataRows); + + /** + * Convert the raw data from the database to JSON rows similar to StyleRatingDTO. The rating service expects its own fields + * to be first in each Object array, and the last item in the array to be an item description (eg formatted user's name) + * Will go back to the database for the justification comment that would apply to hedging. + */ + JSONArray convertToStyledJSON(RatingCriteria ratingCriteria, Long currentUser, boolean includeCurrentUser, List rawDataRows, boolean needRatesPerUser) throws JSONException; + + /** * Returns number of images rated by specified user in a current activity. It counts comments as ratings. This * method is applicable only for RatingCriterias of LEARNER_ITEM_CRITERIA_TYPE type. * @@ -128,6 +161,16 @@ int getCountItemsRatedByUser(final Long toolContentId, final Integer userId); /** + * Returns number of items rated by specified user in a current activity, for a particular criteria. It counts comments as ratings + * iff it is a comment rating. This method is applicable only for RatingCriterias of LEARNER_ITEM_CRITERIA_TYPE type. + * + * @param toolContentId + * @param userId + * @return + */ + int getCountItemsRatedByUserByCriteria(final Long criteriaId, final Integer userId); + + /** * Count how many users rated and commented each item. * * @param contentId @@ -137,5 +180,8 @@ */ Map countUsersRatedEachItem(final Long contentId, final Collection itemIds, Integer excludeUserId); + + Map countUsersRatedEachItemByCriteria(final Long criteriaId, final Collection itemIds, + Integer excludeUserId); } Index: lams_common/src/java/org/lamsfoundation/lams/rating/service/RatingService.java =================================================================== RCS file: /usr/local/cvsroot/lams_common/src/java/org/lamsfoundation/lams/rating/service/RatingService.java,v diff -u -r1.1.2.6 -r1.1.2.7 --- lams_common/src/java/org/lamsfoundation/lams/rating/service/RatingService.java 11 May 2016 07:07:29 -0000 1.1.2.6 +++ lams_common/src/java/org/lamsfoundation/lams/rating/service/RatingService.java 10 Oct 2016 00:53:43 -0000 1.1.2.7 @@ -25,6 +25,7 @@ package org.lamsfoundation.lams.rating.service; +import java.math.BigInteger; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collection; @@ -39,17 +40,23 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; +import org.apache.tomcat.util.json.JSONArray; +import org.apache.tomcat.util.json.JSONException; +import org.apache.tomcat.util.json.JSONObject; import org.lamsfoundation.lams.rating.dao.IRatingCommentDAO; import org.lamsfoundation.lams.rating.dao.IRatingCriteriaDAO; import org.lamsfoundation.lams.rating.dao.IRatingDAO; import org.lamsfoundation.lams.rating.dto.ItemRatingCriteriaDTO; import org.lamsfoundation.lams.rating.dto.ItemRatingDTO; import org.lamsfoundation.lams.rating.dto.RatingCommentDTO; import org.lamsfoundation.lams.rating.dto.RatingDTO; +import org.lamsfoundation.lams.rating.dto.StyledCriteriaRatingDTO; +import org.lamsfoundation.lams.rating.dto.StyledRatingDTO; import org.lamsfoundation.lams.rating.model.LearnerItemRatingCriteria; import org.lamsfoundation.lams.rating.model.Rating; import org.lamsfoundation.lams.rating.model.RatingComment; import org.lamsfoundation.lams.rating.model.RatingCriteria; +import org.lamsfoundation.lams.rating.model.ToolActivityRatingCriteria; import org.lamsfoundation.lams.usermanagement.User; import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; import org.lamsfoundation.lams.util.MessageService; @@ -85,19 +92,31 @@ } @Override + public int getCountItemsRatedByUserByCriteria(final Long criteriaId, final Integer userId) { + return ratingDAO.getCountItemsRatedByUserByCriteria(criteriaId, userId); + } + + @Override public Map countUsersRatedEachItem(final Long contentId, final Collection itemIds, Integer excludeUserId) { return ratingDAO.countUsersRatedEachItem(contentId, itemIds, excludeUserId); } @Override + public Map countUsersRatedEachItemByCriteria(final Long criteriaId, final Collection itemIds, + Integer excludeUserId) { + return ratingDAO.countUsersRatedEachItemByCriteria(criteriaId, itemIds, excludeUserId); + } + + @Override public void saveOrUpdateRating(Rating rating) { ratingDAO.saveOrUpdate(rating); } @Override public ItemRatingCriteriaDTO rateItem(RatingCriteria ratingCriteria, Integer userId, Long itemId, float ratingFloat) { + Long ratingCriteriaId = ratingCriteria.getRatingCriteriaId(); Rating rating = ratingDAO.getRating(ratingCriteriaId, userId, itemId); @@ -120,6 +139,38 @@ } @Override + public int rateItems(RatingCriteria ratingCriteria, Integer userId, Map newRatings) { + + User learner = (User) userManagementService.findById(User.class, userId); + int numRatings = 0; + + List dbRatings = ratingDAO.getRatingsByUserCriteria(ratingCriteria.getRatingCriteriaId(), userId); + for ( Rating rating: dbRatings ) { + Long itemId = rating.getItemId(); + Float newRating = newRatings.get(itemId); + if ( newRating != null ) { + rating.setRating(newRating); + newRatings.remove(itemId); + numRatings++; + ratingDAO.saveOrUpdate(rating); + } else { + rating.setRating((Float)null); + } + } + for ( Map.Entry entry : newRatings.entrySet() ) { + Rating rating = new Rating(); + rating.setItemId(entry.getKey()); + rating.setLearner(learner); + rating.setRatingCriteria(ratingCriteria); + rating.setRating(entry.getValue()); + ratingDAO.saveOrUpdate(rating); + numRatings++; + } + + return numRatings; + } + + @Override public void commentItem(RatingCriteria ratingCriteria, Integer userId, Long itemId, String comment) { RatingComment ratingComment = ratingCommentDAO.getComment(ratingCriteria.getRatingCriteriaId(), userId, itemId); @@ -174,7 +225,7 @@ Long criteriaId = criteria.getRatingCriteriaId(); //comments' criteria are handled earlier, at the beginning of this function - if (criteria.isCommentsEnabled()) { + if (criteria.getRatingStyle() == 0) { continue; } @@ -226,17 +277,11 @@ boolean isSingleItem = itemIds.size() == 1; Long singleItemId = isSingleItem ? itemIds.iterator().next() : null; - // initialize itemDtos - List itemDtos = new LinkedList(); - for (Long itemId : itemIds) { - ItemRatingDTO itemDto = new ItemRatingDTO(); - itemDto.setItemId(itemId); - itemDtos.add(itemDto); - } + List itemDtos = initializeItemDtos(itemIds); //handle comments criteria for (RatingCriteria criteria : criterias) { - if (criteria.isCommentsEnabled()) { + if (criteria.isCommentRating()) { Long commentCriteriaId = criteria.getRatingCriteriaId(); List commentDtos; @@ -281,6 +326,17 @@ return itemDtos; } + private List initializeItemDtos(Collection itemIds) { + // initialize itemDtos + List itemDtos = new LinkedList(); + for (Long itemId : itemIds) { + ItemRatingDTO itemDto = new ItemRatingDTO(); + itemDto.setItemId(itemId); + itemDtos.add(itemDto); + } + return itemDtos; + } + @Override public ItemRatingDTO getRatingCriteriaDtoWithActualRatings(Long contentId, Long itemId) { @@ -305,7 +361,7 @@ Long criteriaId = criteria.getRatingCriteriaId(); // comments' criteria are handled earlier, at the beginning of this function - if (criteria.isCommentsEnabled()) { + if (criteria.isCommentRating()) { continue; } @@ -332,6 +388,7 @@ return itemDto; } + @Override public List getCriteriasByToolContentId(Long toolContentId) { List criterias = ratingCriteriaDAO.getByToolContentId(toolContentId); @@ -357,12 +414,44 @@ mapOrderIdToRatingCriteria.put(ratingCriteriaIter.getOrderId(), ratingCriteriaIter); } + + for ( Map.Entry entry : request.getParameterMap().entrySet()) { + log.debug("entry: "+entry.getKey()+" "+entry.getValue()); + } + int criteriaMaxOrderId = WebUtil.readIntParam(request, "criteriaMaxOrderId"); // i is equal to an old orderId for (int i = 1; i <= criteriaMaxOrderId; i++) { String criteriaTitle = WebUtil.readStrParam(request, "criteriaTitle" + i, true); + Integer ratingStyle = WebUtil.readIntParam(request, "ratingStyle" + i, true); + if ( ratingStyle == null ) + ratingStyle = RatingCriteria.RATING_STYLE_STAR; + + Integer maxRating = WebUtil.readIntParam(request, "maxRating" + i, true); + if (maxRating == null) { + switch (ratingStyle) { + case RatingCriteria.RATING_STYLE_STAR: + maxRating = RatingCriteria.RATING_STYLE_STAR_DEFAULT_MAX; + break; + case RatingCriteria.RATING_STYLE_RANKING: + maxRating = RatingCriteria.RATING_STYLE_RANKING_DEFAULT_MAX; + break; + case RatingCriteria.RATING_STYLE_HEDGING: + maxRating = 0; + break; + } + } + + Integer minRatings = 0; + Integer maxRatings = 0; + if ( ratingStyle == RatingCriteria.RATING_STYLE_STAR ) { + minRatings = WebUtil.readIntParam(request, "minimumRates" + i, true); + maxRatings = WebUtil.readIntParam(request, "maximumRates" + i, true); + } + boolean commentsEnabled = ( ratingStyle != RatingCriteria.RATING_STYLE_COMMENT ? WebUtil.readBooleanParam(request, "enableComments" + i, false) : false ); + RatingCriteria ratingCriteria = mapOrderIdToRatingCriteria.get(i); if (StringUtils.isNotBlank(criteriaTitle)) { int newCriteriaOrderId = WebUtil.readIntParam(request, "criteriaOrderId" + i); @@ -376,6 +465,19 @@ ratingCriteria.setOrderId(newCriteriaOrderId); ratingCriteria.setTitle(criteriaTitle); + ratingCriteria.setRatingStyle(ratingStyle); + ratingCriteria.setMaxRating(maxRating); + ratingCriteria.setCommentsEnabled(commentsEnabled); + if ( commentsEnabled ) { + Integer commentsMinWordsLimit = WebUtil.readIntParam(request, "commentsMinWordsLimit" + i, true); + ratingCriteria.setCommentsMinWordsLimit(commentsMinWordsLimit != null ? commentsMinWordsLimit : 0); + } else { + ratingCriteria.setCommentsMinWordsLimit(0); + } + + ratingCriteria.setMinimumRates( minRatings ); + ratingCriteria.setMaximumRates( maxRatings ); + ratingCriteriaDAO.saveOrUpdate(ratingCriteria); // !!updatedCriterias.add(ratingCriteria); @@ -392,7 +494,7 @@ // find comments' responsible RatingCriteria RatingCriteria commentsResponsibleCriteria = null; for (RatingCriteria ratingCriteriaIter : oldCriterias) { - if (ratingCriteriaIter.isCommentsEnabled()) { + if (ratingCriteriaIter.isCommentRating()) { commentsResponsibleCriteria = ratingCriteriaIter; break; } @@ -405,6 +507,7 @@ ((LearnerItemRatingCriteria) commentsResponsibleCriteria).setToolContentId(toolContentId); commentsResponsibleCriteria.setOrderId(0); commentsResponsibleCriteria.setCommentsEnabled(true); + commentsResponsibleCriteria.setRatingStyle(RatingCriteria.RATING_STYLE_COMMENT); } int commentsMinWordsLimit = WebUtil.readIntParam(request, "commentsMinWordsLimit"); @@ -420,7 +523,163 @@ } } + /** + * Convert the raw data from the database to StyledCriteriaRatingDTO and StyleRatingDTO. The rating service expects + * the potential itemId followed by rating.* (its own fields) and the last item in the array to be an item + * description (eg formatted user's name) Will go back to the database for the justification comment that would + * apply to hedging. + * + * If includeCurrentUser == true will include the current users' records (used for SelfReview and Monitoring) + * otherwise skips the current user, so they do not rate themselves! + * + * Entries in Object array for comment style: + * tool item id (usually user id), rating.item_id, rating_comment.comment, (tool fields)+ + * Entries in Object array for other styles: + * tool item id (usually user id), rating.item_id, rating_comment.comment, + * rating.rating, calculated.average_rating, calculated.count_vote, (tool fields)+ + */ @Override + public StyledCriteriaRatingDTO convertToStyledDTO(RatingCriteria ratingCriteria, Long currentUserId, boolean includeCurrentUser, + List rawDataRows) { + + StyledCriteriaRatingDTO criteriaDto = new StyledCriteriaRatingDTO(); + criteriaDto.setRatingCriteria(ratingCriteria); + + if (ratingCriteria.isHedgeStyleRating() && ratingCriteria.isCommentsEnabled() ) { + RatingComment justification = ratingCommentDAO.getComment(ratingCriteria.getRatingCriteriaId(), + currentUserId.intValue(), ratingCriteria.getRatingCriteriaId()); + if (justification != null) + criteriaDto.setJustificationComment(justification.getComment()); + } else if ( ratingCriteria.isStarStyleRating() ) { + int ratedCount = getCountItemsRatedByUserByCriteria(ratingCriteria.getRatingCriteriaId(), currentUserId.intValue()); + criteriaDto.setCountRatedItems(ratedCount); + } + + boolean isComment = ratingCriteria.isCommentRating(); + if ( rawDataRows != null ) { + + List ratingDtos = new ArrayList(); + criteriaDto.setRatingDtos(ratingDtos); + + + NumberFormat numberFormat = NumberFormat.getInstance(Locale.US); + numberFormat.setMaximumFractionDigits(1); + + for (Object[] row : rawDataRows) { + int numColumns = row.length; + if ( ( isComment && numColumns < 4 ) || ( !isComment && numColumns < 7) ) { + log.error("convertToStyledDTO: ratingCriteria" + ratingCriteria.getRatingCriteriaId() + " UserId: " + + currentUserId + " Skipping data row as there are not enough columns. Only " + numColumns + + " columns. Data:" + row); + break; + } + + long itemId = ((BigInteger) row[0]).longValue(); + if ( includeCurrentUser || itemId != currentUserId) { + + if (row[1] != null && row[0] != row[1]) { + log.error("convertToStyledDTO: ratingCriteria" + ratingCriteria.getRatingCriteriaId() + " UserId: " + + currentUserId + " Potential issue: expected item id " + row[0] + " does match real item id" + + row[1] + ". Data: " + row); + } + + StyledRatingDTO dto = new StyledRatingDTO(((BigInteger) row[0]).longValue()); + dto.setComment((String) row[2]); + dto.setItemDescription((String) row[numColumns - 1]); + if ( ! isComment ) { + dto.setUserRating(row[3] == null ? "" : numberFormat.format((Float) row[3])); + dto.setAverageRating(row[4] == null ? "" : numberFormat.format((Double) row[4])); + dto.setNumberOfVotes(row[5] == null ? "" : numberFormat.format((BigInteger) row[5])); + } + ratingDtos.add(dto); + } + } + } + + return criteriaDto; + } + + /** + * Convert the raw data from the database to JSON, similar on StyledCriteriaRatingDTO and StyleRatingDTO. + * The rating service expects the potential itemId followed by rating.* (its own fields) and the last item + * in the array to be an item description (eg formatted user's name) Will go back to the database for the + * justification comment that would apply to hedging. + * + * If includeCurrentUser == true will include the current users' records (used for SelfReview and Monitoring) + * otherwise skips the current user, so they do not rate themselves! + * + * In JSON for use with tablesorter. + * Entries in Object array for comment style: + * tool item id (usually user id), rating.item_id, rating_comment.comment, (tool fields)+ + * Entries in Object array for other styles: + * tool item id (usually user id), rating.item_id, rating_comment.comment, + * rating.rating, calculated.average_rating, calculated.count_vote, (tool fields)+ + * @throws JSONException + */ + @Override + public JSONArray convertToStyledJSON(RatingCriteria ratingCriteria, Long currentUserId, boolean includeCurrentUser, + List rawDataRows, boolean needRatesPerUser) throws JSONException { + + JSONArray rows = new JSONArray(); + boolean isComment = ratingCriteria.isCommentRating(); + + if ( rawDataRows != null ) { + + List itemIds = needRatesPerUser ? new ArrayList(rawDataRows.size()) : null; + + NumberFormat numberFormat = NumberFormat.getInstance(Locale.US); + numberFormat.setMaximumFractionDigits(1); + + for (Object[] row : rawDataRows) { + int numColumns = row.length; + if ( ( isComment && numColumns < 4 ) || ( !isComment && numColumns < 7) ) { + log.error("convertToStyledDTO: ratingCriteria" + ratingCriteria.getRatingCriteriaId() + " UserId: " + + currentUserId + " Skipping data row as there are not enough columns. Only " + numColumns + + " columns. Data:" + row); + break; + } + + Long itemId = ((BigInteger) row[0]).longValue(); + if ( includeCurrentUser || itemId != currentUserId) { + + if (row[1] != null && row[0] != row[1]) { + log.error("convertToStyledDTO: ratingCriteria" + ratingCriteria.getRatingCriteriaId() + " UserId: " + + currentUserId + " Potential issue: expected item id " + row[0] + " does match real item id" + + row[1] + ". Data: " + row); + } + + JSONObject userRow = new JSONObject(); + userRow.put("itemId", itemId); + userRow.put("comment", row[2] == null ? "" : (String) row[2]); + userRow.put("itemDescription", row[numColumns - 1] == null ? "" : (String) row[numColumns - 1]); + if ( ! isComment ) { + userRow.put("userRating", row[3] == null ? "" : numberFormat.format((Float) row[3])); + userRow.put("averageRating", row[4] == null ? "" : numberFormat.format((Double) row[4])); + userRow.put("numberOfVotes", row[5] == null ? "" : numberFormat.format((BigInteger) row[5])); + } + + rows.put(userRow); + + if ( needRatesPerUser ) + itemIds.add(itemId); + } + } + + if ( needRatesPerUser ) { + Map countUsersRatedEachItemMap = ratingDAO.countUsersRatedEachItemByCriteria(ratingCriteria.getRatingCriteriaId(), itemIds, -1); + for ( int i=0; i < rows.length(); i++ ) { + JSONObject row = rows.getJSONObject(i); + Long count = countUsersRatedEachItemMap.get(row.get("itemId")); + row.put("ratesPerUser", count != null ? count : 0); + } + } + } + + return rows; + + } + + @Override public boolean isCommentsEnabled(Long toolContentId) { return ratingCriteriaDAO.isCommentsEnabledForToolContent(toolContentId); } @@ -430,6 +689,10 @@ return ratingCriteriaDAO.getCommentsMinWordsLimitForToolContent(toolContentId); } + @Override + public String getRatingSelectJoinSQL(Integer ratingStyle, boolean getByUser) { + return ratingDAO.getRatingSelectJoinSQL(ratingStyle, getByUser); + } /* ********** Used by Spring to "inject" the linked objects ************* */ public void setRatingDAO(IRatingDAO ratingDAO) {