From 14905a489a0cfdba95fb14d78a81abf5b2869eb8 Mon Sep 17 00:00:00 2001 From: Francois G Date: Sat, 8 Apr 2023 17:33:19 +0200 Subject: [PATCH] Add route for groups leaderboard --- .../jeffcheasey88/peeratcode/model/Group.java | 109 +++++++++++++++++- .../peeratcode/model/Player.java | 25 ++-- .../peeratcode/repository/DatabaseQuery.java | 3 + .../repository/DatabaseRepository.java | 57 +++++++-- .../peeratcode/routes/Leaderboard.java | 35 +++++- 5 files changed, 206 insertions(+), 23 deletions(-) diff --git a/src/be/jeffcheasey88/peeratcode/model/Group.java b/src/be/jeffcheasey88/peeratcode/model/Group.java index e41f30b..a73a4bc 100644 --- a/src/be/jeffcheasey88/peeratcode/model/Group.java +++ b/src/be/jeffcheasey88/peeratcode/model/Group.java @@ -1,11 +1,17 @@ package be.jeffcheasey88.peeratcode.model; +import java.sql.Timestamp; +import java.util.*; + +import org.json.simple.JSONArray; import org.json.simple.JSONObject; -public class Group { +public class Group implements Comparable { private String name; private int linkToChapter; private int linkToPuzzle; + private List players; + private Timestamp endDate; public Group(JSONObject json){ this.name = (String)json.get("name"); @@ -14,11 +20,55 @@ public class Group { } public Group(String name, int initChap, int initPuzz) { + this(name, initChap, initPuzz, null); + } + public Group(String name, int initChap, int initPuzz, Timestamp initEnd) { this.name = name; this.linkToChapter = initChap; this.linkToPuzzle = initPuzz; + this.endDate = initEnd; } - + + public void addPlayer(Player newPlayer) { + if (newPlayer != null) { + if (players == null) + players = new ArrayList(); + + int pPosition = players.indexOf(newPlayer); + if (pPosition < 0) { + players.add(newPlayer); + } + else { + players.get(pPosition).addScore(newPlayer.getTotalScore(), newPlayer.getTotalTries()); + } + } + } + public SortedSet getPlayers() { + return new TreeSet(players); + } + + public int getScore() { + int score = 0; + + if (players != null) { + for (Player p: players) { + score = score + p.getTotalScore(); + } + } + return score; + } + + public int getTries() { + int tries = 0; + + if (players != null) { + for (Player p: players) { + tries = tries + p.getTotalTries(); + } + } + return tries; + } + public String getName() { return name; } @@ -44,10 +94,61 @@ public class Group { } public JSONObject toJson() { + return this.toJson(null); + } + public JSONObject toJson(Integer rank) { JSONObject groupJSON = new JSONObject(); groupJSON.put("name", name); - if (linkToChapter > 0) groupJSON.put("chapter", linkToChapter); - if (linkToPuzzle > 0) groupJSON.put("puzzle", linkToPuzzle); + if (rank != null) groupJSON.put("rank", rank); + else if (linkToChapter > 0) groupJSON.put("chapter", linkToChapter); + else if (linkToPuzzle > 0) groupJSON.put("puzzle", linkToPuzzle); + if (endDate != null) groupJSON.put("end_date", endDate.toString()); + if (players != null) { + JSONArray groupsPlayerJSON = new JSONArray(); + for (Player p:players) { + JSONObject playerJSON = new JSONObject(); + playerJSON.put("pseudo", p.getPseudo()); + playerJSON.put("score", p.getTotalScore()); + playerJSON.put("completion", p.getTotalCompletion()); + playerJSON.put("tries", p.getTotalTries()); + groupsPlayerJSON.add(playerJSON); + } + groupJSON.put("players", groupsPlayerJSON); + } return groupJSON; } + + @Override + public int compareTo(Group arg0) { + int comparo = arg0.getScore() - getScore(); + if (comparo == 0) { + comparo = players.size() - arg0.players.size(); + if (comparo == 0) { + comparo = getTries() - arg0.getTries(); + if (comparo == 0) { + comparo = name.compareTo(arg0.name); + } + } + } + return comparo; + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Group other = (Group) obj; + return Objects.equals(name, other.name); + } + + } diff --git a/src/be/jeffcheasey88/peeratcode/model/Player.java b/src/be/jeffcheasey88/peeratcode/model/Player.java index 59677f6..1d701a9 100644 --- a/src/be/jeffcheasey88/peeratcode/model/Player.java +++ b/src/be/jeffcheasey88/peeratcode/model/Player.java @@ -1,16 +1,9 @@ package be.jeffcheasey88.peeratcode.model; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Base64; import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.SortedSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -43,6 +36,18 @@ public class Player implements Comparable { totalTries = 0; } + public Player(String pseudo, int score, int tries) { + // For groups leaderboard + this.pseudo = pseudo; + totalScore = score; + totalTries = tries; + if (totalTries > 0) + totalCompletion = totalTries; + else + totalCompletion = 0; + email = ""; // TO make compareTo and equals works as usual + } + public String getPseudo() { return this.pseudo; } @@ -115,6 +120,12 @@ public class Player implements Comparable { this.totalScore = totalScore; } + public void addScore(int addScore, int tries) { + totalScore = totalScore + addScore; + totalTries = totalTries + tries; + totalCompletion++; + } + public int getTotalCompletion() { return totalCompletion; } diff --git a/src/be/jeffcheasey88/peeratcode/repository/DatabaseQuery.java b/src/be/jeffcheasey88/peeratcode/repository/DatabaseQuery.java index 9689439..9e74f10 100644 --- a/src/be/jeffcheasey88/peeratcode/repository/DatabaseQuery.java +++ b/src/be/jeffcheasey88/peeratcode/repository/DatabaseQuery.java @@ -11,7 +11,10 @@ public enum DatabaseQuery { PUZZLES_IN_CHAPTER_QUERY("SELECT p.*, GROUP_CONCAT(t.name) AS tags FROM puzzles p LEFT JOIN containsTags ct ON ct.fk_puzzle = p.id_puzzle LEFT JOIN tags t ON t.id_tag = ct.fk_tag WHERE fk_chapter = ? GROUP BY p.id_puzzle"), ALL_CHAPTERS_QUERY("SELECT * FROM chapters WHERE id_chapter > 0"), ALL_GROUPS("SELCT * FROM groups"), + ALL_PLAYERS_FOR_LEADERBOARD("select p.*, scores.*, g.* from players p ,(SELECT fk_player, SUM(c.score) AS score, COUNT(c.id_completion) AS completions, SUM(c.tries) AS tries, rank() over(ORDER BY score DESC) AS rank FROM completions c GROUP BY c.fk_player) AS scores LEFT JOIN containsGroups cg ON scores.fk_player = cg.fk_player LEFT JOIN groups g ON cg.fk_group = g.id_group WHERE p.id_player = scores.fk_player ORDER BY g.fk_chapter, g.fk_puzzle"), + ALL_GROUP_FOR_CHAPTER_LEADERBOARD("SELECT g.*, pl.pseudo, co.score, co.tries, ch.end_date FROM groups g LEFT JOIN containsGroups cg ON g.id_group = cg.fk_group LEFT JOIN players pl ON cg.fk_player = pl.id_player LEFT JOIN completions co ON pl.id_player = co.fk_player LEFT JOIN chapters ch ON g.fk_chapter = ch.id_chapter WHERE fk_chapter = ? AND (co.fk_puzzle IN (SELECT id_puzzle FROM puzzles puz WHERE puz.fk_chapter = g.fk_chapter) OR co.score IS NULL);"), + CHECK_PSEUDO_AVAILABLE_QUERY("SELECT * FROM players WHERE pseudo = ?"), CHECK_EMAIL_AVAILABLE_QUERY("SELECT * FROM players WHERE email = ?"), REGISTER_QUERY("INSERT INTO players (pseudo, email, passwd, firstname, lastname, description, avatar) VALUES (?, ?, ?, ?, ?, ?, ?)"), diff --git a/src/be/jeffcheasey88/peeratcode/repository/DatabaseRepository.java b/src/be/jeffcheasey88/peeratcode/repository/DatabaseRepository.java index a26fad3..0b1c085 100644 --- a/src/be/jeffcheasey88/peeratcode/repository/DatabaseRepository.java +++ b/src/be/jeffcheasey88/peeratcode/repository/DatabaseRepository.java @@ -80,7 +80,13 @@ public class DatabaseRepository { } private Group makeGroup(ResultSet result) throws SQLException { - return new Group(result.getString("name"), result.getInt("fk_chapter"), result.getInt("fk_puzzle")); + if (hasColumn(result, "end_date")) + return new Group(result.getString("name"), result.getInt("fk_chapter"), result.getInt("fk_puzzle"), result.getTimestamp("end_date")); + else + return new Group(result.getString("name"), result.getInt("fk_chapter"), result.getInt("fk_puzzle")); + } + private Player makeGroupPlayer(ResultSet result) throws SQLException { + return new Player(result.getString("pseudo"), result.getInt("score"), result.getInt("tries")); } private Badge makeBadge(ResultSet rs) throws SQLException { @@ -91,8 +97,9 @@ public class DatabaseRepository { // Found on StackOverflow ResultSetMetaData rsmd = rs.getMetaData(); int columns = rsmd.getColumnCount(); - for(int x = 1; x <= columns; x++){ - if(columnName.equals(rsmd.getColumnName(x))) return true; + for (int x = 1; x <= columns; x++) { + if (columnName.equals(rsmd.getColumnName(x))) + return true; } return false; } @@ -238,6 +245,33 @@ public class DatabaseRepository { return null; } + public SortedSet getAllGroupForChapterLeaderboard(int chapterId) { + try { + ensureConnection(); + PreparedStatement groupsStmt = DatabaseQuery.ALL_GROUP_FOR_CHAPTER_LEADERBOARD.prepare(this.con); + groupsStmt.setInt(1, chapterId); + ResultSet result = groupsStmt.executeQuery(); + List groups = new ArrayList(); + Group tmpGroup; + while (result.next()) { + tmpGroup = makeGroup(result); + if (tmpGroup != null) { + int gPosition = groups.indexOf(tmpGroup); + if (gPosition < 0) { + tmpGroup.addPlayer(makeGroupPlayer(result)); + groups.add(tmpGroup); + } else { + groups.get(gPosition).addPlayer(makeGroupPlayer(result)); + } + } + } + return new TreeSet(groups); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + public Badge getBadge(int badgeId) { try { ensureConnection(); @@ -380,7 +414,8 @@ public class DatabaseRepository { ResultSet inserted = playerStatement.getGeneratedKeys(); if (inserted.next()) { int newPlayerId = inserted.getInt(1); - try (PreparedStatement containsGroupsStatement = con.prepareStatement(DatabaseQuery.REGISTER_PLAYER_IN_EXISTING_GROUP.toString())) { + try (PreparedStatement containsGroupsStatement = con + .prepareStatement(DatabaseQuery.REGISTER_PLAYER_IN_EXISTING_GROUP.toString())) { containsGroupsStatement.setInt(1, newPlayerId); containsGroupsStatement.setString(2, sgroup); containsGroupsStatement.executeUpdate(); @@ -390,10 +425,9 @@ public class DatabaseRepository { } } } - } - catch (SQLException e) { - con.rollback(); - con.setAutoCommit(true); + } catch (SQLException e) { + con.rollback(); + con.setAutoCommit(true); } } catch (SQLException e) { e.printStackTrace(); @@ -453,8 +487,8 @@ public class DatabaseRepository { statement.setInt(6, newCompletion.getScore()); statement.executeUpdate(); } - - public boolean insertGroup(Group group){ + + public boolean insertGroup(Group group) { try { ensureConnection(); PreparedStatement statement = DatabaseQuery.INSERT_GROUP.prepare(this.con); @@ -462,7 +496,8 @@ public class DatabaseRepository { statement.setInt(2, group.getLinkToChapter()); statement.setInt(3, group.getLinkToPuzzle()); return statement.executeUpdate() >= 0; - }catch(Exception e){} + } catch (Exception e) { + } return false; } diff --git a/src/be/jeffcheasey88/peeratcode/routes/Leaderboard.java b/src/be/jeffcheasey88/peeratcode/routes/Leaderboard.java index 0230787..4ec7307 100644 --- a/src/be/jeffcheasey88/peeratcode/routes/Leaderboard.java +++ b/src/be/jeffcheasey88/peeratcode/routes/Leaderboard.java @@ -1,5 +1,6 @@ package be.jeffcheasey88.peeratcode.routes; +import java.io.IOException; import java.util.Base64; import java.util.SortedSet; import java.util.regex.Matcher; @@ -7,6 +8,7 @@ import java.util.regex.Matcher; import org.json.simple.JSONArray; import org.json.simple.JSONObject; +import be.jeffcheasey88.peeratcode.model.Group; import be.jeffcheasey88.peeratcode.model.Player; import be.jeffcheasey88.peeratcode.repository.DatabaseRepository; import be.jeffcheasey88.peeratcode.webserver.HttpReader; @@ -24,10 +26,41 @@ public class Leaderboard implements Response { this.databaseRepo = databaseRepo; } - @Route(path = "^\\/leaderboard$") + @Route(path = "^\\/leaderboard\\/?(\\d+)?$") @Override public void exec(Matcher matcher, User user, HttpReader reader, HttpWriter writer) throws Exception { HttpUtil.responseHeaders(writer, 200, "Access-Control-Allow-Origin: *"); + if (matcher.group(1) != null){ + groupsLeaderboard(Integer.parseInt(matcher.group(1)), writer); + } else { + playersLeaderboard(writer); + } + } + + private void groupsLeaderboard(int chapterId, HttpWriter writer) throws IOException { + SortedSet allGroupsForChapter = databaseRepo.getAllGroupForChapterLeaderboard(chapterId); + JSONArray groupsJSON = new JSONArray(); + if (allGroupsForChapter != null) { + int rank = 1; + int sameRankCount = 1; + Group previousGroup = null; + for (Group g: allGroupsForChapter) { + if (previousGroup != null) { + if (g.compareTo(previousGroup) == 0) { + sameRankCount++; + } else { + rank = rank + sameRankCount; + sameRankCount = 1; + } + } + groupsJSON.add(g.toJson(rank)); + previousGroup = g; + } + } + writer.write(groupsJSON.toJSONString().replace("\\", "")); + } + + private void playersLeaderboard(HttpWriter writer) throws IOException { SortedSet allPlayers = databaseRepo.getAllPlayerForLeaderboard(); JSONArray playersJSON = new JSONArray(); if (allPlayers != null) {