Index: lams_tool_preview/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -rb784c097aa626460dd6fb3c7d4f829c5b791f0f7 -ra63af627c86727e5548ad92b1b8cf4024009da5e --- lams_tool_preview/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision b784c097aa626460dd6fb3c7d4f829c5b791f0f7) +++ lams_tool_preview/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision a63af627c86727e5548ad92b1b8cf4024009da5e) @@ -145,6 +145,7 @@ label.for.user =For {0} label.learner =Student label.spa.factor =SPA factor +label.sapa.factor =SAPA factor label.total.group.mark =Total team mark label.individual.mark =Individual mark error.edit.not.remove =You may edit but you may not remove a saved comment. Index: lams_tool_preview/src/java/org/lamsfoundation/lams/tool/peerreview/util/SpreadsheetBuilder.java =================================================================== diff -u -rf13b8a48b9a1cfaee56a654ba751a9736616b726 -ra63af627c86727e5548ad92b1b8cf4024009da5e --- lams_tool_preview/src/java/org/lamsfoundation/lams/tool/peerreview/util/SpreadsheetBuilder.java (.../SpreadsheetBuilder.java) (revision f13b8a48b9a1cfaee56a654ba751a9736616b726) +++ lams_tool_preview/src/java/org/lamsfoundation/lams/tool/peerreview/util/SpreadsheetBuilder.java (.../SpreadsheetBuilder.java) (revision a63af627c86727e5548ad92b1b8cf4024009da5e) @@ -7,12 +7,15 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.TreeMap; +import java.util.stream.Collectors; import org.apache.commons.lang.StringEscapeUtils; import org.apache.poi.ss.usermodel.IndexedColors; import org.lamsfoundation.lams.rating.dto.ItemRatingCriteriaDTO; import org.lamsfoundation.lams.rating.dto.ItemRatingDTO; +import org.lamsfoundation.lams.rating.model.Rating; import org.lamsfoundation.lams.rating.model.RatingCriteria; import org.lamsfoundation.lams.rating.service.IRatingService; import org.lamsfoundation.lams.tool.peerreview.dao.PeerreviewSessionDAO; @@ -26,10 +29,11 @@ import org.lamsfoundation.lams.util.excel.ExcelSheet; import org.springframework.util.StringUtils; -/** Creates a Spreadsheet that reports the averages, with each team/group shown on a separate worksheet. - * Calculates averages for each rating and for each user. The SPA factor then calculates the user's overall average - * against the team average. - * Has a empty Mark column so that the teacher can add a manual mark if they want. +/** + * Creates a Spreadsheet that reports the averages, with each team/group shown on a separate worksheet. + * Calculates averages for each rating and for each user. The SPA factor then calculates the user's overall average + * against the team average. + * Has a empty Mark column so that the teacher can add a manual mark if they want. */ public class SpreadsheetBuilder { private Peerreview peerreview; @@ -38,11 +42,12 @@ private PeerreviewSessionDAO peerreviewSessionDao; private PeerreviewUserDAO peerreviewUserDao; private IPeerreviewService service; - + private List sheets; - - public SpreadsheetBuilder(Peerreview peerreview, IRatingService ratingService, PeerreviewSessionDAO peerreviewSessionDao, - PeerreviewUserDAO peerreviewUserDao, IPeerreviewService service) { + + public SpreadsheetBuilder(Peerreview peerreview, IRatingService ratingService, + PeerreviewSessionDAO peerreviewSessionDao, PeerreviewUserDAO peerreviewUserDao, + IPeerreviewService service) { this.peerreview = peerreview; this.ratingService = ratingService; this.peerreviewSessionDao = peerreviewSessionDao; @@ -53,59 +58,69 @@ public List generateTeamReport() { // this one is guaranteed to give a consistent order over peerreview.getRatingCriterias() criterias = ratingService.getCriteriasByToolContentId(peerreview.getContentId()); - sheets = new LinkedList(); + sheets = new LinkedList<>(); List sessions = peerreviewSessionDao.getByContentId(peerreview.getContentId()); for (PeerreviewSession session : sessions) { generateTeamSheet(session); } - + return sheets; } - + + @SuppressWarnings("unchecked") private void generateTeamSheet(PeerreviewSession session) { ExcelSheet sessionSheet = new ExcelSheet(session.getSessionName()); sheets.add(sessionSheet); //Title row ExcelRow titleRow = sessionSheet.initRow(); titleRow.addCell(service.getLocalisedMessage("label.learner", null), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - Map criteriaIndexMap = new HashMap(); + Map criteriaIndexMap = new HashMap<>(); int countNonCommentCriteria = 0; - for ( RatingCriteria criteria : criterias ) { - if ( ! criteria.isCommentRating() ) { - titleRow.addCell(criteria.getTitle(), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - criteriaIndexMap.put(criteria.getRatingCriteriaId(), countNonCommentCriteria); + + for (RatingCriteria criteria : criterias) { + if (!criteria.isCommentRating()) { + titleRow.addCell(criteria.getTitle(), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); + criteriaIndexMap.put(criteria.getRatingCriteriaId(), countNonCommentCriteria); countNonCommentCriteria++; } } - titleRow.addCell(service.getLocalisedMessage("label.average", null), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - titleRow.addCell(service.getLocalisedMessage("label.spa.factor", null), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - titleRow.addCell(service.getLocalisedMessage("label.total.group.mark", null), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - titleRow.addCell(service.getLocalisedMessage("label.individual.mark", null), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); - + titleRow.addCell(service.getLocalisedMessage("label.average", null), true, ExcelCell.BORDER_STYLE_BOTTOM_THIN); + titleRow.addCell(service.getLocalisedMessage("label.spa.factor", null), true, + ExcelCell.BORDER_STYLE_BOTTOM_THIN); + if (peerreview.isSelfReview()) { + titleRow.addCell(service.getLocalisedMessage("label.sapa.factor", null), true, + ExcelCell.BORDER_STYLE_BOTTOM_THIN); + } + titleRow.addCell(service.getLocalisedMessage("label.total.group.mark", null), true, + ExcelCell.BORDER_STYLE_BOTTOM_THIN); + titleRow.addCell(service.getLocalisedMessage("label.individual.mark", null), true, + ExcelCell.BORDER_STYLE_BOTTOM_THIN); + List users = peerreviewUserDao.getBySessionID(session.getSessionId()); - Map userNames = new TreeMap(); + Map userNames = new TreeMap<>(); for (PeerreviewUser user : users) { - userNames.put(user.getUserId(), StringEscapeUtils.escapeCsv(user.getFirstName() + " " + user.getLastName())); + userNames.put(user.getUserId(), + StringEscapeUtils.escapeCsv(user.getFirstName() + " " + user.getLastName())); } ExcelRow numberOfTeamsRow = sessionSheet.initRow(); - numberOfTeamsRow.addCell(service.getLocalisedMessage("label.number.of.team.members", null), true); - numberOfTeamsRow.addCell(users.size(), IndexedColors.YELLOW); + numberOfTeamsRow.addCell(service.getLocalisedMessage("label.number.of.team.members", null), true); + numberOfTeamsRow.addCell(users.size(), IndexedColors.YELLOW); sessionSheet.addEmptyRow(); // uses same index as the user row, so allow for the name in the first column - Double[] criteriaMarkSum = new Double[countNonCommentCriteria+1]; - Integer[] criteriaMarkCount = new Integer[countNonCommentCriteria+1]; - for (int i = 0; i < criteriaMarkSum.length-1; i++) { + Double[] criteriaMarkSum = new Double[countNonCommentCriteria + 1]; + Integer[] criteriaMarkCount = new Integer[countNonCommentCriteria + 1]; + for (int i = 0; i < criteriaMarkSum.length - 1; i++) { criteriaMarkSum[i] = 0D; criteriaMarkCount[i] = 0; } - Map userRowMap = new HashMap(); + Map userRowMap = new HashMap<>(); // Process all the criterias and build up rows for each rated user. Store in temporary map. - List ratingDtos = service.getRatingCriteriaDtos(session.getPeerreview().getContentId(), + List ratingDtos = service.getRatingCriteriaDtos(session.getPeerreview().getContentId(), session.getSessionId(), userNames.keySet(), true, -1L); for (ItemRatingDTO ratingDto : ratingDtos) { Double userMarkSum = 0D; @@ -122,69 +137,106 @@ userMarkSum += db; } } - + ExcelRow userRow = new ExcelRow(); Long userId = ratingDto.getItemId(); + userRow.addCell(userNames.get(userId)); for (double userRowDataIter : userRowData) { userRow.addCell(userRowDataIter); } - userRow.addCell(countNonCommentCriteria > 0 ? roundTo2Places(userMarkSum / countNonCommentCriteria) : 0D, true); + userRow.addCell(countNonCommentCriteria > 0 ? roundTo2Places(userMarkSum / countNonCommentCriteria) : 0D, + true); userRowMap.put(userId, userRow); } // calculate the group averages ExcelRow avgRow = new ExcelRow(); avgRow.addCell(service.getLocalisedMessage("label.average", null), true); Double averageMarkSum = 0D; - for (int i = 0; i < criteriaMarkSum.length-1; i++) { - if ( criteriaMarkCount[i] > 0 ) { - Double d = criteriaMarkSum[i] / criteriaMarkCount[i]; + for (int i = 0; i < criteriaMarkSum.length - 1; i++) { + if (criteriaMarkCount[i] > 0) { + Double d = criteriaMarkSum[i] / criteriaMarkCount[i]; avgRow.addCell(roundTo2Places(d), true); averageMarkSum += d; } else { avgRow.addEmptyCell(); } } - Double finalGroupAverage = countNonCommentCriteria > 0 ? roundTo2Places(averageMarkSum / countNonCommentCriteria) : 0D; + Double finalGroupAverage = countNonCommentCriteria > 0 + ? roundTo2Places(averageMarkSum / countNonCommentCriteria) + : 0D; avgRow.addCell(finalGroupAverage, true); - + + Map> ratings = null; + if (peerreview.isSelfReview()) { + // prepare for SAPA calculation + // the map is: itemId (who was rated) -> userId (who rated) -> sum of ratings from all categories + ratings = ((List) ratingService.getRatingsByCriteriasAndItems(criteriaIndexMap.keySet(), + userNames.keySet())) + .stream().filter(rating -> rating.getRating() != null) + .collect(Collectors.groupingBy(Rating::getItemId, + Collectors.groupingBy(rating -> rating.getLearner().getUserId().longValue(), + Collectors.summingDouble(Rating::getRating)))); + } + // Combine rated rows with rows with users not yet rated, to make up complete list, and write out to rowList. for (PeerreviewUser user : users) { ExcelRow userRow = userRowMap.get(user.getUserId()); if (userRow == null) { userRow = sessionSheet.initRow(); userRow.addCell(userNames.get(user.getUserId())); - + } else { - Double learnerAverage = (Double) userRow.getCell(userRow.getCells().size()-1); - Double spa = countNonCommentCriteria > 0 ? roundTo2Places(learnerAverage / finalGroupAverage): 0D; + Double learnerAverage = (Double) userRow.getCell(userRow.getCells().size() - 1); + Double spa = countNonCommentCriteria > 0 ? roundTo2Places(learnerAverage / finalGroupAverage) : 0D; userRow.addCell(spa, true); - userRow.addCell("", IndexedColors.YELLOW); + + if (peerreview.isSelfReview()) { + // calculate SAPA factor + double sumSelfRatings = 0; + double sumPeerRatings = 0; + int peerRatingCount = 0; + + for (Entry ratingEntry : ratings.get(user.getUserId()).entrySet()) { + if (ratingEntry.getKey().equals(user.getUserId())) { + sumSelfRatings = ratingEntry.getValue(); + } else { + sumPeerRatings += ratingEntry.getValue(); + peerRatingCount++; + } + } + double sapa = sumPeerRatings > 0 + ? roundTo2Places(Math.sqrt(sumSelfRatings / (sumPeerRatings / peerRatingCount))) + : 0d; + userRow.addCell(sapa, true); + } + + userRow.addCell("", IndexedColors.YELLOW); userRow.addCell("", IndexedColors.GREEN); - + sessionSheet.addRow(userRow); } } // Learners marks done, write out the group average sessionSheet.addRow(avgRow); - - // now do all the comments - for ( RatingCriteria criteria : criterias ) { - if ( criteria.isCommentsEnabled() ) { + + // now do all the comments + for (RatingCriteria criteria : criterias) { + if (criteria.isCommentsEnabled()) { sessionSheet.addEmptyRow(); sessionSheet.addEmptyRow(); ExcelRow criteriaTitleRow = sessionSheet.initRow(); - criteriaTitleRow.addCell(criteria.getTitle(), true); - - if ( criteria.isHedgeStyleRating() ) { + criteriaTitleRow.addCell(criteria.getTitle(), true); + + if (criteria.isHedgeStyleRating()) { // just need the first entry as it is the same for everyone - the justification - if ( users.size() > 0 ) { + if (users.size() > 0) { generateUsersComments(session, sessionSheet, userNames, criteria, users.get(0), false); } } else { - for ( PeerreviewUser user : users ) { + for (PeerreviewUser user : users) { generateUsersComments(session, sessionSheet, userNames, criteria, user, true); } } @@ -214,11 +266,12 @@ } } } - + private double roundTo2Places(double d) { - if ( Double.isNaN(d) ) + if (Double.isNaN(d)) { return 0D; - + } + BigDecimal bd = new BigDecimal(d); bd = bd.setScale(2, RoundingMode.HALF_UP); return bd.doubleValue();