package org.karmaexchange.dao; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import javax.xml.bind.annotation.XmlRootElement; import org.karmaexchange.task.LeaderboardReducer.LeaderboardScore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import com.google.common.collect.Lists; import com.googlecode.objectify.Key; import com.googlecode.objectify.annotation.Cache; import com.googlecode.objectify.annotation.Entity; @XmlRootElement @Entity @Cache // Caching is valuable for the leaderboard since it is fetched by key. @Data @NoArgsConstructor @EqualsAndHashCode(callSuper=true) @ToString(callSuper=true) public class Leaderboard extends NameBaseDao<Leaderboard> { public enum LeaderboardType { ALL_TIME, THIRTY_DAY } private List<LeaderboardScoreAndUserInfo> scores = Lists.newArrayList(); public Leaderboard(Key<Organization> orgKey, LeaderboardType type, List<LeaderboardScore> scores, Map<Key<User>, User> usersMap) { // Using the same parent causes entity group contention. Which is not needed. name = createName(orgKey, type); for (LeaderboardScore score : scores) { User user = usersMap.get(score.getUserKey()); if (user != null) { this.scores.add(new LeaderboardScoreAndUserInfo(user, score.getLeaderboardKarmaPoints())); } } Collections.sort(this.scores, LeaderboardScoreAndUserInfo.KarmaPointsAndUserInfoComparator.INSTANCE); } private static String createName(Key<Organization> orgKey, LeaderboardType type) { return Organization.getOrgId(orgKey) + type.toString(); } public static Key<Leaderboard> createKey(Key<Organization> orgKey, LeaderboardType type) { return Key.<Leaderboard>create(Leaderboard.class, createName(orgKey, type)); } @Override protected Permission evalPermission() { // Admin tasks create the leaderboard. Everyone else only has read access. return Permission.READ; } @Data @NoArgsConstructor private static class LeaderboardScoreAndUserInfo { private UserInfoKeyWrapper user; private long leaderboardKarmaPoints; public LeaderboardScoreAndUserInfo(User user, long leaderboardKarmaPoints) { this.user = new UserInfoKeyWrapper(user); this.leaderboardKarmaPoints = leaderboardKarmaPoints; } public static class KarmaPointsAndUserInfoComparator implements Comparator<LeaderboardScoreAndUserInfo> { public static final KarmaPointsAndUserInfoComparator INSTANCE = new KarmaPointsAndUserInfoComparator(); @Override public int compare(LeaderboardScoreAndUserInfo score1, LeaderboardScoreAndUserInfo score2) { // Higher scored items come first. int result = Long.compare(score2.leaderboardKarmaPoints, score1.leaderboardKarmaPoints); if (result != 0) { return result; } result = nullSafeIgnoreCaseStringComparator(score1.getUser().getFirstName(), score2.getUser().getFirstName()); if (result != 0) { return result; } return nullSafeIgnoreCaseStringComparator(score1.getUser().getLastName(), score2.getUser().getLastName()); } private int nullSafeIgnoreCaseStringComparator(String s1, String s2) { if ((s1 == null) || (s2 == null)) { return (s1 == null) ? -1 : 1; } return s1.compareToIgnoreCase(s2); } } } }