From 986a753918bf7e92be4faba5940770ffbf09e853 Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Thu, 15 Aug 2019 14:24:30 +0300 Subject: [PATCH] Playerbase overview numbers to network page --- .../analysis/NetworkActivityIndexQueries.java | 415 ++++++++++++++++++ .../queries/analysis/PlayerCountQueries.java | 31 ++ .../queries/objects/SessionQueries.java | 66 +++ .../OnlineActivityOverviewJSONParser.java | 2 +- .../system/json/PerformanceJSONParser.java | 2 +- .../json/PlayerBaseOverviewJSONParser.java | 2 +- .../plan/system/json/PvPPvEJSONParser.java | 2 +- .../system/json/ServerOverviewJSONParser.java | 2 +- ...ONParser.java => ServerTabJSONParser.java} | 2 +- .../json/SessionsOverviewJSONParser.java | 2 +- .../NetworkPlayerBaseOverviewJSONParser.java | 151 +++++++ .../json/network/NetworkTabJSONParser.java | 34 ++ .../pages/json/NetworkTabJSONHandler.java | 53 +++ .../pages/json/NeworkJSONHandler.java | 57 +++ .../webserver/pages/json/RootJSONHandler.java | 7 +- .../pages/json/ServerTabJSONHandler.java | 4 +- .../utilities/html/pages/NetworkPage.java | 72 +-- .../utilities/html/pages/PageFactory.java | 5 +- .../plan/utilities/html/pages/ServerPage.java | 36 +- .../assets/plan/web/js/network-values.js | 6 +- .../resources/assets/plan/web/network.html | 126 +++--- .../resources/assets/plan/web/server.html | 45 +- 22 files changed, 978 insertions(+), 144 deletions(-) create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/NetworkActivityIndexQueries.java rename Plan/common/src/main/java/com/djrapitops/plan/system/json/{TabJSONParser.java => ServerTabJSONParser.java} (93%) create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/json/network/NetworkPlayerBaseOverviewJSONParser.java create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/json/network/NetworkTabJSONParser.java create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/NetworkTabJSONHandler.java create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/NeworkJSONHandler.java diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/NetworkActivityIndexQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/NetworkActivityIndexQueries.java new file mode 100644 index 000000000..dd7d58417 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/NetworkActivityIndexQueries.java @@ -0,0 +1,415 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.db.access.queries.analysis; + +import com.djrapitops.plan.data.store.mutators.ActivityIndex; +import com.djrapitops.plan.db.access.Query; +import com.djrapitops.plan.db.access.QueryStatement; +import com.djrapitops.plan.db.sql.tables.SessionsTable; +import com.djrapitops.plan.db.sql.tables.UsersTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; + +/** + * Queries for Activity Index that attempts to gain insight into player activity levels. + *

+ * Old formula for activity index was not linear and difficult to turn into a query due to conditional multipliers. + * Thus a new formula was written. + *

+ * {@code T} - Time played after someone is considered active on a particular week + * {@code t1, t2, t3} - Time played that week + *

+ * Activity index takes into account last 3 weeks. + *

+ * Activity for a single week is calculated using {@code A(t) = (1 / (pi/2 * (t/T) + 1))}. + * A(t) is based on function f(x) = 1 / (x + 1), which has property f(0) = 1, decreasing from there, but not in a straight line. + * You can see the function plotted here https://www.wolframalpha.com/input/?i=1+%2F+(x%2B1)+from+-1+to+2 + *

+ * To fine tune the curve pi/2 is used since it felt like a good curve. + *

+ * Activity index A is calculated by using the formula: + * {@code A = 5 - 5 * [A(t1) + A(t2) + A(t3)] / 3} + *

+ * Plot for A and limits + * https://www.wolframalpha.com/input/?i=plot+y+%3D+5+-+5+*+(1+%2F+(pi%2F2+*+x%2B1))+and+y+%3D1+and+y+%3D+2+and+y+%3D+3+and+y+%3D+3.75+from+-0.5+to+3 + *

+ * New Limits for A would thus be + * {@code < 1: Inactive} + * {@code > 1: Irregular} + * {@code > 2: Regular} + * {@code > 3: Active} + * {@code > 3.75: Very Active} + * + * @author Rsl1122 + */ +public class NetworkActivityIndexQueries { + + private NetworkActivityIndexQueries() { + // Static method class + } + + public static Query fetchRegularPlayerCount(long date, long playtimeThreshold) { + return fetchActivityGroupCount(date, playtimeThreshold, ActivityIndex.REGULAR, 5.1); + } + + private static String selectActivityIndexSQL() { + String selectActivePlaytimeSQL = SELECT + + SessionsTable.USER_UUID + + ",SUM(" + + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + '-' + SessionsTable.AFK_TIME + + ") as active_playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?" + + GROUP_BY + SessionsTable.USER_UUID; + + String selectThreeWeeks = selectActivePlaytimeSQL + UNION + selectActivePlaytimeSQL + UNION + selectActivePlaytimeSQL; + + return SELECT + + "5.0 - 5.0 * AVG(1 / (?/2 * (q1.active_playtime/?) +1)) as activity_index," + + "q1." + SessionsTable.USER_UUID + + FROM + '(' + selectThreeWeeks + ") q1" + + GROUP_BY + "q1." + SessionsTable.USER_UUID; + } + + private static void setSelectActivityIndexSQLParameters(PreparedStatement statement, int index, long playtimeThreshold, long date) throws SQLException { + statement.setDouble(index, Math.PI); + statement.setLong(index + 1, playtimeThreshold); + + statement.setLong(index + 2, date - TimeUnit.DAYS.toMillis(7L)); + statement.setLong(index + 3, date); + statement.setLong(index + 4, date - TimeUnit.DAYS.toMillis(14L)); + statement.setLong(index + 5, date - TimeUnit.DAYS.toMillis(7L)); + statement.setLong(index + 6, date - TimeUnit.DAYS.toMillis(21L)); + statement.setLong(index + 7, date - TimeUnit.DAYS.toMillis(14L)); + } + + public static Query fetchActivityGroupCount(long date, long playtimeThreshold, double above, double below) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectIndexes = SELECT + "COALESCE(activity_index, 0) as activity_index" + + FROM + UsersTable.TABLE_NAME + " u" + + LEFT_JOIN + '(' + selectActivityIndex + ") q2 on q2." + SessionsTable.USER_UUID + "=u." + UsersTable.USER_UUID + + WHERE + "u." + UsersTable.REGISTERED + "<=?"; + + String selectActivePlayerCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectIndexes + ") i" + + WHERE + "i.activity_index>=?" + + AND + "i.activity_index(selectActivePlayerCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, playtimeThreshold, date); + statement.setLong(9, date); + statement.setDouble(10, above); + statement.setDouble(11, below); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + public static Query> fetchActivityIndexGroupingsOn(long date, long threshold) { + return db -> { + Map groups = new HashMap<>(); + groups.put("Very Active", db.query(fetchActivityGroupCount(date, threshold, ActivityIndex.VERY_ACTIVE, 5.1))); + groups.put("Active", db.query(fetchActivityGroupCount(date, threshold, ActivityIndex.ACTIVE, ActivityIndex.VERY_ACTIVE))); + groups.put("Regular", db.query(fetchActivityGroupCount(date, threshold, ActivityIndex.REGULAR, ActivityIndex.ACTIVE))); + groups.put("Irregular", db.query(fetchActivityGroupCount(date, threshold, ActivityIndex.IRREGULAR, ActivityIndex.REGULAR))); + groups.put("Inactive", db.query(fetchActivityGroupCount(date, threshold, -0.1, ActivityIndex.IRREGULAR))); + return groups; + }; + } + + public static Query countNewPlayersTurnedRegular(long after, long before, Long threshold) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectActivePlayerCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectActivityIndex + ") q2" + + INNER_JOIN + UsersTable.TABLE_NAME + " u on u." + UsersTable.USER_UUID + "=q2." + SessionsTable.USER_UUID + + WHERE + "u." + UsersTable.REGISTERED + ">=?" + + AND + "u." + UsersTable.REGISTERED + "<=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectActivePlayerCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, before); + statement.setLong(9, after); + statement.setLong(10, before); + statement.setDouble(11, ActivityIndex.REGULAR); + statement.setDouble(12, 5.1); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + /** + * @param start Start of the tracking, those regular will be counted here. + * @param end End of the tracking, those inactive will be count here. + * @param threshold Playtime threshold + * @return Query how many players went from regular to inactive in a span of time. + */ + public static Query countRegularPlayersTurnedInactive(long start, long end, Long threshold) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectActivePlayerCount = SELECT + "COUNT(1) as count" + + FROM + '(' + selectActivityIndex + ") q2" + + // Join two select activity index queries together to query Regular and Inactive players + INNER_JOIN + '(' + selectActivityIndex.replace("q1", "q3") + ") q4" + + " on q2." + SessionsTable.USER_UUID + "=q4." + SessionsTable.USER_UUID + + WHERE + "q2.activity_index>=?" + + AND + "q2.activity_index=?" + + AND + "q4.activity_index(selectActivePlayerCount) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, end); + setSelectActivityIndexSQLParameters(statement, 9, threshold, start); + statement.setDouble(17, ActivityIndex.REGULAR); + statement.setDouble(18, 5.1); + statement.setDouble(19, -0.1); + statement.setDouble(20, ActivityIndex.IRREGULAR); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("count") : 0; + } + }; + } + + public static Query averagePlaytimePerRegularPlayer(long after, long before, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectPlaytimePerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "SUM(p." + SessionsTable.SESSION_END + "-p." + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, before); + statement.setLong(9, before); + statement.setLong(10, after); + statement.setDouble(11, ActivityIndex.REGULAR); + statement.setDouble(12, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query averageSessionLengthPerRegularPlayer(long after, long before, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectSessionLengthPerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "p." + SessionsTable.SESSION_END + "-p." + SessionsTable.SESSION_START + " as length" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, before); + statement.setLong(9, before); + statement.setLong(10, after); + statement.setDouble(11, ActivityIndex.REGULAR); + statement.setDouble(12, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query averageAFKPerRegularPlayer(long after, long before, Long threshold) { + return database -> { + // INNER JOIN limits the users to only those that are regular + String selectPlaytimePerPlayer = SELECT + + "p." + SessionsTable.USER_UUID + "," + + "SUM(p." + SessionsTable.AFK_TIME + ") as afk" + + FROM + SessionsTable.TABLE_NAME + " p" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") q2 on q2." + SessionsTable.USER_UUID + "=p." + SessionsTable.USER_UUID + + WHERE + "p." + SessionsTable.SESSION_END + "<=?" + + AND + "p." + SessionsTable.SESSION_START + ">=?" + + AND + "q2.activity_index>=?" + + AND + "q2.activity_index(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, before); + statement.setLong(9, before); + statement.setLong(10, after); + statement.setDouble(11, ActivityIndex.REGULAR); + statement.setDouble(12, 5.1); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + + public static Query> activityIndexForNewPlayers(long after, long before, Long threshold) { + String selectNewUUIDs = SELECT + UsersTable.USER_UUID + + FROM + UsersTable.TABLE_NAME + + WHERE + UsersTable.REGISTERED + "<=?" + + AND + UsersTable.REGISTERED + ">=?"; + + String sql = SELECT + "activity_index" + + FROM + '(' + selectNewUUIDs + ") n" + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID; + + return new QueryStatement>(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + setSelectActivityIndexSQLParameters(statement, 3, threshold, before); + } + + @Override + public Collection processResults(ResultSet set) throws SQLException { + Collection indexes = new ArrayList<>(); + while (set.next()) { + indexes.add(new ActivityIndex(set.getDouble("activity_index"), before)); + } + return indexes; + } + }; + } + + public static Query averageActivityIndexForRetainedPlayers(long after, long before, Long threshold) { + String selectNewUUIDs = SELECT + UsersTable.USER_UUID + + FROM + UsersTable.TABLE_NAME + + WHERE + UsersTable.REGISTERED + "<=?" + + AND + UsersTable.REGISTERED + ">=?"; + + String selectUniqueUUIDs = SELECT + "DISTINCT " + SessionsTable.USER_UUID + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?"; + + String sql = SELECT + "AVG(activity_index) as average" + + FROM + '(' + selectNewUUIDs + ") n" + + INNER_JOIN + '(' + selectUniqueUUIDs + ") u on n." + SessionsTable.USER_UUID + "=u." + SessionsTable.USER_UUID + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + + // Have played in the last half of the time frame + long half = before - (after - before) / 2; + statement.setLong(3, half); + statement.setLong(4, before); + setSelectActivityIndexSQLParameters(statement, 5, threshold, before); + } + + @Override + public ActivityIndex processResults(ResultSet set) throws SQLException { + return set.next() ? new ActivityIndex(set.getDouble("average"), before) : new ActivityIndex(0.0, before); + } + }; + } + + public static Query averageActivityIndexForNonRetainedPlayers(long after, long before, Long threshold) { + String selectNewUUIDs = SELECT + UsersTable.USER_UUID + + FROM + UsersTable.TABLE_NAME + + WHERE + UsersTable.REGISTERED + "<=?" + + AND + UsersTable.REGISTERED + ">=?"; + + String selectUniqueUUIDs = SELECT + "DISTINCT " + SessionsTable.USER_UUID + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SESSION_END + "<=?"; + + String sql = SELECT + "AVG(activity_index) as average" + + FROM + '(' + selectNewUUIDs + ") n" + + LEFT_JOIN + '(' + selectUniqueUUIDs + ") u on n." + SessionsTable.USER_UUID + "=u." + SessionsTable.USER_UUID + + INNER_JOIN + '(' + selectActivityIndexSQL() + ") a on n." + SessionsTable.USER_UUID + "=a." + SessionsTable.USER_UUID + + WHERE + "n." + SessionsTable.USER_UUID + IS_NULL; + + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + + // Have played in the last half of the time frame + long half = before - (after - before) / 2; + statement.setLong(3, half); + statement.setLong(4, before); + setSelectActivityIndexSQLParameters(statement, 5, threshold, before); + } + + @Override + public ActivityIndex processResults(ResultSet set) throws SQLException { + return set.next() ? new ActivityIndex(set.getDouble("average"), before) : new ActivityIndex(0.0, before); + } + }; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/PlayerCountQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/PlayerCountQueries.java index f45daac7c..7e736abdb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/PlayerCountQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/PlayerCountQueries.java @@ -58,6 +58,21 @@ public class PlayerCountQueries { }; } + private static QueryStatement queryPlayerCount(String sql, long after, long before) { + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + } + + @Override + public Integer processResults(ResultSet set) throws SQLException { + return set.next() ? set.getInt("player_count") : 0; + } + }; + } + public static Query uniquePlayerCount(long after, long before, UUID serverUUID) { String sql = SELECT + "COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" + FROM + SessionsTable.TABLE_NAME + @@ -68,6 +83,22 @@ public class PlayerCountQueries { return queryPlayerCount(sql, after, before, serverUUID); } + /** + * Fetch uniquePlayer count for ALL servers. + * + * @param after After epoch ms + * @param before Before epoch ms + * @return Unique player count (players who played within time frame) + */ + public static Query uniquePlayerCount(long after, long before) { + String sql = SELECT + "COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?"; + + return queryPlayerCount(sql, after, before); + } + /** * Fetch a EpochMs - Count map of unique players on a server. * diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/SessionQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/SessionQueries.java index 9d712730e..73cd1990e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/SessionQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/SessionQueries.java @@ -523,6 +523,39 @@ public class SessionQueries { }; } + /** + * Fetch average playtime per ALL players. + * + * @param after After epoch ms + * @param before Before epoch ms + * @return Average ms played / player, calculated with grouped sums from sessions table. + */ + public static Query averagePlaytimePerPlayer(long after, long before) { + return database -> { + String selectPlaytimePerPlayer = SELECT + + SessionsTable.USER_UUID + "," + + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + GROUP_BY + SessionsTable.USER_UUID; + String selectAverage = SELECT + "AVG(playtime) as average" + FROM + '(' + selectPlaytimePerPlayer + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + public static Query averageAfkPerPlayer(long after, long before, UUID serverUUID) { return database -> { String selectAfkPerPlayer = SELECT + @@ -551,6 +584,39 @@ public class SessionQueries { }; } + /** + * Fetch average Afk per ALL players. + * + * @param after After epoch ms + * @param before Before epoch ms + * @return Average ms afk / player, calculated with grouped sums from sessions table. + */ + public static Query averageAfkPerPlayer(long after, long before) { + return database -> { + String selectAfkPerPlayer = SELECT + + SessionsTable.USER_UUID + "," + + "SUM(" + SessionsTable.AFK_TIME + ") as afk" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + GROUP_BY + SessionsTable.USER_UUID; + String selectAverage = SELECT + "AVG(afk) as average" + FROM + '(' + selectAfkPerPlayer + ") q1"; + + return database.query(new QueryStatement(selectAverage, 100) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("average") : 0; + } + }); + }; + } + public static Query afkTime(long after, long before, UUID serverUUID) { String sql = SELECT + "SUM(" + SessionsTable.AFK_TIME + ") as afk_time" + FROM + SessionsTable.TABLE_NAME + diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/OnlineActivityOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/OnlineActivityOverviewJSONParser.java index 566d1dba7..4e5dfef05 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/OnlineActivityOverviewJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/OnlineActivityOverviewJSONParser.java @@ -46,7 +46,7 @@ import java.util.concurrent.TimeUnit; * @author Rsl1122 */ @Singleton -public class OnlineActivityOverviewJSONParser implements TabJSONParser> { +public class OnlineActivityOverviewJSONParser implements ServerTabJSONParser> { private PlanConfig config; private DBSystem dbSystem; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PerformanceJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PerformanceJSONParser.java index 59ec0b705..d2252cf9b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PerformanceJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PerformanceJSONParser.java @@ -40,7 +40,7 @@ import java.util.concurrent.TimeUnit; * @author Rsl1122 */ @Singleton -public class PerformanceJSONParser implements TabJSONParser> { +public class PerformanceJSONParser implements ServerTabJSONParser> { private final PlanConfig config; private final DBSystem dbSystem; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayerBaseOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayerBaseOverviewJSONParser.java index 2bbcbf660..6982fb741 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayerBaseOverviewJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayerBaseOverviewJSONParser.java @@ -39,7 +39,7 @@ import java.util.concurrent.TimeUnit; * @author Rsl1122 */ @Singleton -public class PlayerBaseOverviewJSONParser implements TabJSONParser> { +public class PlayerBaseOverviewJSONParser implements ServerTabJSONParser> { private PlanConfig config; private DBSystem dbSystem; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PvPPvEJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PvPPvEJSONParser.java index 984695e70..677d5fdaa 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/PvPPvEJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PvPPvEJSONParser.java @@ -33,7 +33,7 @@ import java.util.concurrent.TimeUnit; * @author Rsl1122 */ @Singleton -public class PvPPvEJSONParser implements TabJSONParser> { +public class PvPPvEJSONParser implements ServerTabJSONParser> { private DBSystem dbSystem; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerOverviewJSONParser.java index 1e6fd2fc0..64ca4a369 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerOverviewJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerOverviewJSONParser.java @@ -46,7 +46,7 @@ import java.util.concurrent.TimeUnit; * @author Rsl1122 */ @Singleton -public class ServerOverviewJSONParser implements TabJSONParser> { +public class ServerOverviewJSONParser implements ServerTabJSONParser> { private final Formatter day; private PlanConfig config; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/TabJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerTabJSONParser.java similarity index 93% rename from Plan/common/src/main/java/com/djrapitops/plan/system/json/TabJSONParser.java rename to Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerTabJSONParser.java index 27b778673..4dda64a2b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/TabJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerTabJSONParser.java @@ -24,7 +24,7 @@ import java.util.function.Function; * * @author Rsl1122 */ -public interface TabJSONParser extends Function { +public interface ServerTabJSONParser extends Function { T createJSONAsMap(UUID serverUUID); diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/SessionsOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/SessionsOverviewJSONParser.java index 4e9c6e8e2..883d9ba96 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/json/SessionsOverviewJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/SessionsOverviewJSONParser.java @@ -39,7 +39,7 @@ import java.util.concurrent.TimeUnit; * @author Rsl1122 */ @Singleton -public class SessionsOverviewJSONParser implements TabJSONParser> { +public class SessionsOverviewJSONParser implements ServerTabJSONParser> { private DBSystem dbSystem; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/network/NetworkPlayerBaseOverviewJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/network/NetworkPlayerBaseOverviewJSONParser.java new file mode 100644 index 000000000..8fe1dcdcd --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/network/NetworkPlayerBaseOverviewJSONParser.java @@ -0,0 +1,151 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.system.json.network; + +import com.djrapitops.plan.db.Database; +import com.djrapitops.plan.db.access.queries.analysis.NetworkActivityIndexQueries; +import com.djrapitops.plan.db.access.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.db.access.queries.objects.SessionQueries; +import com.djrapitops.plan.system.database.DBSystem; +import com.djrapitops.plan.system.json.Trend; +import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.utilities.formatting.Formatter; +import com.djrapitops.plan.utilities.formatting.Formatters; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /server-page Playerbase Overview tab. + * + * @author Rsl1122 + */ +@Singleton +public class NetworkPlayerBaseOverviewJSONParser implements NetworkTabJSONParser> { + + private PlanConfig config; + private DBSystem dbSystem; + + private Formatter timeAmount; + private Formatter percentage; + + @Inject + public NetworkPlayerBaseOverviewJSONParser( + PlanConfig config, + DBSystem dbSystem, + Formatters formatters + ) { + this.config = config; + this.dbSystem = dbSystem; + + timeAmount = formatters.timeAmount(); + percentage = formatters.percentage(); + } + + public Map createJSONAsMap() { + Map serverOverview = new HashMap<>(); + serverOverview.put("trends", createTrendsMap()); + serverOverview.put("insights", createInsightsMap()); + return serverOverview; + } + + private Map createTrendsMap() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + long twoMonthsAgo = now - TimeUnit.DAYS.toMillis(60L); + Long playThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map trends = new HashMap<>(); + + Integer playersBefore = db.query(PlayerCountQueries.uniquePlayerCount(0L, monthAgo)); + Integer playersAfter = db.query(PlayerCountQueries.uniquePlayerCount(0L, now)); + trends.put("total_players_then", playersBefore); + trends.put("total_players_now", playersAfter); + trends.put("total_players_trend", new Trend(playersBefore, playersAfter, false)); + + Integer regularBefore = db.query(NetworkActivityIndexQueries.fetchRegularPlayerCount(monthAgo, playThreshold)); + Integer regularAfter = db.query(NetworkActivityIndexQueries.fetchRegularPlayerCount(now, playThreshold)); + trends.put("regular_players_then", regularBefore); + trends.put("regular_players_now", regularAfter); + trends.put("regular_players_trend", new Trend(regularBefore, regularAfter, false)); + + Long avgPlaytimeBefore = db.query(SessionQueries.averagePlaytimePerPlayer(twoMonthsAgo, monthAgo)); + Long avgPlaytimeAfter = db.query(SessionQueries.averagePlaytimePerPlayer(monthAgo, now)); + trends.put("playtime_avg_then", timeAmount.apply(avgPlaytimeBefore)); + trends.put("playtime_avg_now", timeAmount.apply(avgPlaytimeAfter)); + trends.put("playtime_avg_trend", new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false, timeAmount)); + + Long avgAfkBefore = db.query(SessionQueries.averageAfkPerPlayer(twoMonthsAgo, monthAgo)); + Long avgAfkAfter = db.query(SessionQueries.averageAfkPerPlayer(monthAgo, now)); + double afkPercBefore = avgPlaytimeBefore != 0 ? (double) avgAfkBefore / avgPlaytimeBefore : 0; + double afkPercAfter = avgPlaytimeAfter != 0 ? (double) avgAfkAfter / avgPlaytimeAfter : 0; + trends.put("afk_then", percentage.apply(afkPercBefore)); + trends.put("afk_now", percentage.apply(afkPercAfter)); + trends.put("afk_trend", new Trend(afkPercBefore, afkPercAfter, Trend.REVERSED, percentage)); + + Long avgRegularPlaytimeBefore = db.query(NetworkActivityIndexQueries.averagePlaytimePerRegularPlayer(twoMonthsAgo, monthAgo, playThreshold)); + Long avgRegularPlaytimeAfter = db.query(NetworkActivityIndexQueries.averagePlaytimePerRegularPlayer(monthAgo, now, playThreshold)); + trends.put("regular_playtime_avg_then", timeAmount.apply(avgRegularPlaytimeBefore)); + trends.put("regular_playtime_avg_now", timeAmount.apply(avgRegularPlaytimeAfter)); + trends.put("regular_playtime_avg_trend", new Trend(avgRegularPlaytimeBefore, avgRegularPlaytimeAfter, false, timeAmount)); + + Long avgRegularSessionLengthBefore = db.query(NetworkActivityIndexQueries.averageSessionLengthPerRegularPlayer(twoMonthsAgo, monthAgo, playThreshold)); + Long avgRegularSessionLengthAfter = db.query(NetworkActivityIndexQueries.averageSessionLengthPerRegularPlayer(monthAgo, now, playThreshold)); + trends.put("regular_session_avg_then", timeAmount.apply(avgRegularSessionLengthBefore)); + trends.put("regular_session_avg_now", timeAmount.apply(avgRegularSessionLengthAfter)); + trends.put("regular_session_avg_trend", new Trend(avgRegularSessionLengthBefore, avgRegularSessionLengthAfter, false, timeAmount)); + + Long avgRegularAfkBefore = db.query(NetworkActivityIndexQueries.averageAFKPerRegularPlayer(twoMonthsAgo, monthAgo, playThreshold)); + Long avgRegularAfkAfter = db.query(NetworkActivityIndexQueries.averageAFKPerRegularPlayer(monthAgo, now, playThreshold)); + double afkRegularPercBefore = avgRegularPlaytimeBefore != 0 ? (double) avgRegularAfkBefore / avgRegularPlaytimeBefore : 0; + double afkRegularPercAfter = avgRegularPlaytimeAfter != 0 ? (double) avgRegularAfkAfter / avgRegularPlaytimeAfter : 0; + trends.put("regular_afk_avg_then", percentage.apply(afkRegularPercBefore)); + trends.put("regular_afk_avg_now", percentage.apply(afkRegularPercAfter)); + trends.put("regular_afk_avg_trend", new Trend(afkRegularPercBefore, afkRegularPercAfter, Trend.REVERSED, percentage)); + + return trends; + } + + private Map createInsightsMap() { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long halfMonthAgo = now - TimeUnit.DAYS.toMillis(30L); + long monthAgo = now - TimeUnit.DAYS.toMillis(30L); + Long playThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + Map insights = new HashMap<>(); + + int newToRegular = db.query(NetworkActivityIndexQueries.countNewPlayersTurnedRegular(monthAgo, now, playThreshold)); + Integer newToRegularBefore = db.query(NetworkActivityIndexQueries.countNewPlayersTurnedRegular(monthAgo, halfMonthAgo, playThreshold)); + Integer newToRegularAfter = db.query(NetworkActivityIndexQueries.countNewPlayersTurnedRegular(halfMonthAgo, now, playThreshold)); + insights.put("new_to_regular", newToRegular); + insights.put("new_to_regular_trend", new Trend(newToRegularBefore, newToRegularAfter, false)); + + Integer regularToInactive = db.query(NetworkActivityIndexQueries.countRegularPlayersTurnedInactive(monthAgo, now, playThreshold)); + Integer regularToInactiveBefore = db.query(NetworkActivityIndexQueries.countRegularPlayersTurnedInactive(monthAgo, halfMonthAgo, playThreshold)); + Integer regularToInactiveAfter = db.query(NetworkActivityIndexQueries.countRegularPlayersTurnedInactive(halfMonthAgo, now, playThreshold)); + insights.put("regular_to_inactive", regularToInactive); + insights.put("regular_to_inactive_trend", new Trend(regularToInactiveBefore, regularToInactiveAfter, Trend.REVERSED)); + + return insights; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/network/NetworkTabJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/network/NetworkTabJSONParser.java new file mode 100644 index 000000000..5542bfca5 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/network/NetworkTabJSONParser.java @@ -0,0 +1,34 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.system.json.network; + +import java.util.function.Supplier; + +/** + * Interface for different tab JSON parsers. + * + * @author Rsl1122 + */ +public interface NetworkTabJSONParser extends Supplier { + + T createJSONAsMap(); + + @Override + default T get() { + return createJSONAsMap(); + } +} diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/NetworkTabJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/NetworkTabJSONHandler.java new file mode 100644 index 000000000..c940b2dfa --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/NetworkTabJSONHandler.java @@ -0,0 +1,53 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.system.webserver.pages.json; + +import com.djrapitops.plan.api.exceptions.WebUserAuthException; +import com.djrapitops.plan.api.exceptions.connection.WebException; +import com.djrapitops.plan.system.json.network.NetworkTabJSONParser; +import com.djrapitops.plan.system.webserver.Request; +import com.djrapitops.plan.system.webserver.RequestTarget; +import com.djrapitops.plan.system.webserver.auth.Authentication; +import com.djrapitops.plan.system.webserver.pages.PageHandler; +import com.djrapitops.plan.system.webserver.response.Response; +import com.djrapitops.plan.system.webserver.response.data.JSONResponse; + +import java.util.function.Supplier; + +/** + * Generic Tab JSON handler for any tab's data. + * + * @author Rsl1122 + */ +public class NetworkTabJSONHandler implements PageHandler { + + private final Supplier jsonParser; + + public NetworkTabJSONHandler(NetworkTabJSONParser jsonParser) { + this.jsonParser = jsonParser; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + return new JSONResponse(jsonParser.get()); + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return auth.getWebUser().getPermLevel() <= 0; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/NeworkJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/NeworkJSONHandler.java new file mode 100644 index 000000000..d0c6ac0fa --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/NeworkJSONHandler.java @@ -0,0 +1,57 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.system.webserver.pages.json; + +import com.djrapitops.plan.api.exceptions.WebUserAuthException; +import com.djrapitops.plan.system.json.network.NetworkPlayerBaseOverviewJSONParser; +import com.djrapitops.plan.system.json.network.NetworkTabJSONParser; +import com.djrapitops.plan.system.webserver.RequestTarget; +import com.djrapitops.plan.system.webserver.auth.Authentication; +import com.djrapitops.plan.system.webserver.pages.TreePageHandler; +import com.djrapitops.plan.system.webserver.response.ResponseFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; + +/** + * Root handler for different JSON end points. + * + * @author Rsl1122 + */ +@Singleton +public class NeworkJSONHandler extends TreePageHandler { + + @Inject + public NeworkJSONHandler( + ResponseFactory responseFactory, + NetworkPlayerBaseOverviewJSONParser playerBaseOverviewJSONParser + ) { + super(responseFactory); + + registerPage("playerbaseOverview", playerBaseOverviewJSONParser); + } + + private void registerPage(String identifier, NetworkTabJSONParser tabJSONParser) { + registerPage(identifier, new NetworkTabJSONHandler<>(tabJSONParser)); + + } + + @Override + public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException { + return true; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java index 3700da837..e5c87d008 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/RootJSONHandler.java @@ -41,7 +41,6 @@ public class RootJSONHandler extends TreePageHandler { public RootJSONHandler( ResponseFactory responseFactory, Identifiers identifiers, - JSONFactory jsonFactory, GraphsJSONHandler graphsJSONHandler, SessionsJSONHandler sessionsJSONHandler, PlayersTableJSONHandler playersTableJSONHandler, @@ -53,7 +52,8 @@ public class RootJSONHandler extends TreePageHandler { PvPPvEJSONParser pvPPvEJSONParser, PlayerBaseOverviewJSONParser playerBaseOverviewJSONParser, PerformanceJSONParser performanceJSONParser, - PlayerJSONHandler playerJSONHandler + PlayerJSONHandler playerJSONHandler, + NeworkJSONHandler neworkJSONHandler ) { super(responseFactory); @@ -73,9 +73,10 @@ public class RootJSONHandler extends TreePageHandler { registerPage("performanceOverview", performanceJSONParser); registerPage("player", playerJSONHandler); + registerPage("network", neworkJSONHandler); } - private void registerPage(String identifier, TabJSONParser tabJSONParser) { + private void registerPage(String identifier, ServerTabJSONParser tabJSONParser) { registerPage(identifier, new ServerTabJSONHandler<>(identifiers, tabJSONParser)); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/ServerTabJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/ServerTabJSONHandler.java index a73adeb6c..7488f588b 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/ServerTabJSONHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/ServerTabJSONHandler.java @@ -19,7 +19,7 @@ package com.djrapitops.plan.system.webserver.pages.json; import com.djrapitops.plan.api.exceptions.WebUserAuthException; import com.djrapitops.plan.api.exceptions.connection.WebException; import com.djrapitops.plan.system.Identifiers; -import com.djrapitops.plan.system.json.TabJSONParser; +import com.djrapitops.plan.system.json.ServerTabJSONParser; import com.djrapitops.plan.system.webserver.Request; import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; @@ -40,7 +40,7 @@ public class ServerTabJSONHandler implements PageHandler { private final Identifiers identifiers; private final Function jsonParser; - public ServerTabJSONHandler(Identifiers identifiers, TabJSONParser jsonParser) { + public ServerTabJSONHandler(Identifiers identifiers, ServerTabJSONParser jsonParser) { this.identifiers = identifiers; this.jsonParser = jsonParser; } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/NetworkPage.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/NetworkPage.java index 2d03839c0..f5e4e3077 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/NetworkPage.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/NetworkPage.java @@ -17,18 +17,20 @@ package com.djrapitops.plan.utilities.html.pages; import com.djrapitops.plan.api.exceptions.ParseException; -import com.djrapitops.plan.data.store.containers.NetworkContainer; -import com.djrapitops.plan.data.store.keys.NetworkKeys; -import com.djrapitops.plan.data.store.keys.ServerKeys; +import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerDataQuery; +import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.file.PlanFiles; -import com.djrapitops.plan.system.info.server.properties.ServerProperties; +import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.ProxySettings; +import com.djrapitops.plan.system.settings.theme.Theme; +import com.djrapitops.plan.system.settings.theme.ThemeVal; import com.djrapitops.plan.system.update.VersionCheckSystem; import com.djrapitops.plan.utilities.formatting.Formatters; import com.djrapitops.plan.utilities.formatting.PlaceholderReplacer; -import java.util.ArrayList; - -import static com.djrapitops.plan.data.store.keys.NetworkKeys.*; +import java.util.List; /** * Html String parser for /network page. @@ -37,57 +39,57 @@ import static com.djrapitops.plan.data.store.keys.NetworkKeys.*; */ public class NetworkPage implements Page { - private final NetworkContainer networkContainer; + private final DBSystem dbSystem; private final VersionCheckSystem versionCheckSystem; private final PlanFiles files; - private final ServerProperties serverProperties; + private final PlanConfig config; + private final Theme theme; + private final ServerInfo serverInfo; private final Formatters formatters; NetworkPage( - NetworkContainer networkContainer, + DBSystem dbSystem, VersionCheckSystem versionCheckSystem, PlanFiles files, - ServerProperties serverProperties, + PlanConfig config, + Theme theme, + ServerInfo serverInfo, Formatters formatters ) { - this.networkContainer = networkContainer; + this.dbSystem = dbSystem; this.versionCheckSystem = versionCheckSystem; this.files = files; - this.serverProperties = serverProperties; + this.config = config; + this.theme = theme; + this.serverInfo = serverInfo; this.formatters = formatters; } @Override public String toHtml() throws ParseException { try { - networkContainer.putSupplier(NetworkKeys.PLAYERS_ONLINE, serverProperties::getOnlinePlayers); + PlaceholderReplacer placeholders = new PlaceholderReplacer(); - PlaceholderReplacer placeholderReplacer = new PlaceholderReplacer(); - placeholderReplacer.addAllPlaceholdersFrom(networkContainer, - VERSION, NETWORK_NAME, TIME_ZONE, - PLAYERS_ONLINE, PLAYERS_ONLINE_SERIES, PLAYERS_TOTAL, PLAYERS_GRAPH_COLOR, - REFRESH_TIME_F, RECENT_PEAK_TIME_F, ALL_TIME_PEAK_TIME_F, - PLAYERS_ALL_TIME_PEAK, PLAYERS_RECENT_PEAK, - PLAYERS_DAY, PLAYERS_WEEK, PLAYERS_MONTH, - PLAYERS_NEW_DAY, PLAYERS_NEW_WEEK, PLAYERS_NEW_MONTH, - WORLD_MAP_SERIES, WORLD_MAP_HIGH_COLOR, WORLD_MAP_LOW_COLOR, - COUNTRY_CATEGORIES, COUNTRY_SERIES, - HEALTH_INDEX, HEALTH_NOTES, - ACTIVITY_PIE_SERIES, ACTIVITY_STACK_SERIES, ACTIVITY_STACK_CATEGORIES, - SERVERS_TAB - ); - placeholderReplacer.put("update", versionCheckSystem.getUpdateHtml().orElse("")); + placeholders.put("networkDisplayName", config.get(ProxySettings.NETWORK_NAME)); - ServerPluginTabs serverPluginTabs = new ServerPluginTabs(networkContainer.getBungeeContainer().getValue(ServerKeys.EXTENSION_DATA).orElse(new ArrayList<>()), formatters); + placeholders.put("gmPieColors", theme.getValue(ThemeVal.GRAPH_GM_PIE)); + placeholders.put("playersGraphColor", theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE)); + placeholders.put("timeZone", config.getTimeZoneOffsetHours()); - String nav = serverPluginTabs.getNav(); - String tabs = serverPluginTabs.getTabs(); + placeholders.put("update", versionCheckSystem.getUpdateHtml().orElse("")); - placeholderReplacer.put("navPluginsTabs", nav); - placeholderReplacer.put("tabsPlugins", tabs); + List extensionData = dbSystem.getDatabase() + .query(new ExtensionServerDataQuery(serverInfo.getServerUUID())); + ServerPluginTabs pluginTabs = new ServerPluginTabs(extensionData, formatters); - return placeholderReplacer.apply(files.getCustomizableResourceOrDefault("web/network.html").asString()); + String nav = pluginTabs.getNav(); + String tabs = pluginTabs.getTabs(); + + placeholders.put("navPluginsTabs", nav); + placeholders.put("tabsPlugins", tabs); + + return placeholders.apply(files.getCustomizableResourceOrDefault("web/network.html").asString()); } catch (Exception e) { throw new ParseException(e); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/PageFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/PageFactory.java index fb71b5535..e66154b3d 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/PageFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/PageFactory.java @@ -110,6 +110,7 @@ public class PageFactory { theme.get(), versionCheckSystem.get(), fileSystem.get(), + dbSystem.get(), formatters.get() )).orElseThrow(() -> new NotFoundException("Server not found in the database")); } @@ -161,7 +162,7 @@ public class PageFactory { public NetworkPage networkPage() { NetworkContainer networkContainer = dbSystem.get().getDatabase() .query(ContainerFetchQueries.fetchNetworkContainer()); // Not cached, big. - return new NetworkPage(networkContainer, - versionCheckSystem.get(), fileSystem.get(), serverInfo.get().getServerProperties(), formatters.get()); + return new NetworkPage(dbSystem.get(), + versionCheckSystem.get(), fileSystem.get(), config.get(), theme.get(), serverInfo.get(), formatters.get()); } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/ServerPage.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/ServerPage.java index 1d4c18ebb..624fe32a7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/ServerPage.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/pages/ServerPage.java @@ -20,6 +20,9 @@ import com.djrapitops.plan.api.exceptions.ParseException; import com.djrapitops.plan.data.store.containers.DataContainer; import com.djrapitops.plan.data.store.containers.RawDataContainer; import com.djrapitops.plan.data.store.keys.AnalysisKeys; +import com.djrapitops.plan.extension.implementation.results.server.ExtensionServerData; +import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionServerDataQuery; +import com.djrapitops.plan.system.database.DBSystem; import com.djrapitops.plan.system.file.PlanFiles; import com.djrapitops.plan.system.info.server.Server; import com.djrapitops.plan.system.settings.config.PlanConfig; @@ -31,6 +34,7 @@ import com.djrapitops.plan.utilities.formatting.Formatters; import com.djrapitops.plan.utilities.formatting.PlaceholderReplacer; import java.io.IOException; +import java.util.List; import static com.djrapitops.plan.data.store.keys.AnalysisKeys.*; @@ -46,6 +50,7 @@ public class ServerPage implements Page { private Theme theme; private final VersionCheckSystem versionCheckSystem; private final PlanFiles files; + private final DBSystem dbSystem; private Formatters formatters; ServerPage( @@ -54,6 +59,7 @@ public class ServerPage implements Page { Theme theme, VersionCheckSystem versionCheckSystem, PlanFiles files, + DBSystem dbSystem, Formatters formatters ) { this.server = server; @@ -61,20 +67,18 @@ public class ServerPage implements Page { this.theme = theme; this.versionCheckSystem = versionCheckSystem; this.files = files; + this.dbSystem = dbSystem; this.formatters = formatters; } @Override public String toHtml() throws ParseException { - PlaceholderReplacer placeholderReplacer = new PlaceholderReplacer(); + PlaceholderReplacer placeholders = new PlaceholderReplacer(); - placeholderReplacer.put("serverName", server.getIdentifiableName()); - placeholderReplacer.put("serverDisplayName", server.getName()); + placeholders.put("serverName", server.getIdentifiableName()); + placeholders.put("serverDisplayName", server.getName()); - long now = System.currentTimeMillis(); DataContainer constants = new RawDataContainer(); - constants.putRawData(AnalysisKeys.REFRESH_TIME_F, formatters.clockLong().apply(now)); - constants.putRawData(AnalysisKeys.REFRESH_TIME_FULL_F, formatters.secondLong().apply(now)); constants.putRawData(AnalysisKeys.VERSION, versionCheckSystem.getCurrentVersion()); constants.putRawData(AnalysisKeys.TIME_ZONE, config.getTimeZoneOffsetHours()); @@ -97,7 +101,7 @@ public class ServerPage implements Page { constants.putRawData(AnalysisKeys.MAX_PING_COLOR, theme.getValue(ThemeVal.GRAPH_MAX_PING)); constants.putRawData(AnalysisKeys.MIN_PING_COLOR, theme.getValue(ThemeVal.GRAPH_MIN_PING)); - placeholderReplacer.addAllPlaceholdersFrom(constants, + placeholders.addAllPlaceholdersFrom(constants, VERSION, TIME_ZONE, FIRST_DAY, TPS_MEDIUM, TPS_HIGH, DISK_MEDIUM, DISK_HIGH, @@ -110,14 +114,24 @@ public class ServerPage implements Page { ); if (server.isProxy()) { - placeholderReplacer.put("backButton", "

  • arrow_backcloud
  • "); + placeholders.put("backButton", "
  • arrow_backcloud
  • "); } else { - placeholderReplacer.put("backButton", ""); + placeholders.put("backButton", ""); } - placeholderReplacer.put("update", versionCheckSystem.getUpdateHtml().orElse("")); + placeholders.put("update", versionCheckSystem.getUpdateHtml().orElse("")); + + List extensionData = dbSystem.getDatabase() + .query(new ExtensionServerDataQuery(server.getUuid())); + ServerPluginTabs pluginTabs = new ServerPluginTabs(extensionData, formatters); + + String nav = pluginTabs.getNav(); + String tabs = pluginTabs.getTabs(); + + placeholders.put("navPluginsTabs", nav); + placeholders.put("tabsPlugins", tabs); try { - return placeholderReplacer.apply(files.getCustomizableResourceOrDefault("web/server.html").asString()); + return placeholders.apply(files.getCustomizableResourceOrDefault("web/server.html").asString()); } catch (IOException e) { throw new ParseException(e); } diff --git a/Plan/common/src/main/resources/assets/plan/web/js/network-values.js b/Plan/common/src/main/resources/assets/plan/web/js/network-values.js index 99f1c00ff..02b777310 100644 --- a/Plan/common/src/main/resources/assets/plan/web/js/network-values.js +++ b/Plan/common/src/main/resources/assets/plan/web/js/network-values.js @@ -269,9 +269,9 @@ function loadPlayerbaseOverviewValues(json, error) { $(element).find('#data_regular_session_avg_then').text(data.regular_session_avg_then); $(element).find('#data_regular_session_avg_now').text(data.regular_session_avg_now); $(element).find('#data_regular_session_avg_trend').replaceWith(trend(data.regular_session_avg_trend)); - $(element).find('#data_regular_afk_then').text(data.regular_afk_then); - $(element).find('#data_regular_afk_now').text(data.regular_afk_now); - $(element).find('#data_regular_afk_trend').replaceWith(trend(data.regular_afk_trend)); + $(element).find('#data_regular_afk_then').text(data.regular_afk_avg_then); + $(element).find('#data_regular_afk_now').text(data.regular_afk_avg_now); + $(element).find('#data_regular_afk_trend').replaceWith(trend(data.regular_afk_avg_trend)); // Insights data = json.insights; diff --git a/Plan/common/src/main/resources/assets/plan/web/network.html b/Plan/common/src/main/resources/assets/plan/web/network.html index 80e5fae5d..c0973af03 100644 --- a/Plan/common/src/main/resources/assets/plan/web/network.html +++ b/Plan/common/src/main/resources/assets/plan/web/network.html @@ -25,7 +25,9 @@ - +

    Please wait.. @@ -796,16 +798,16 @@

    -
    +
    -

    Network Name +

    ${networkDisplayName} · Playerbase Overview

    - +
    Playerbase development
    -
    - -
    +
    - +
    Current Playerbase
    -
    - -
    +
    - +
    @@ -845,71 +843,78 @@ class="fa fa-fw fa-exchange-alt col-amber"> Trends for 30 days
    - +
    - - - - + + + + + + - - - + + + - - - + + + - - - - - - - - - - + + + + + + + + + + + + + + + - - - + + + - - - + + +
    -
    +
    @@ -918,17 +923,17 @@ class="far fa-fw fa-life-ring col-red"> Insights for 30 Days
    -
    +

    New Regular32

    + class="float-right">

    Regular Inactive45

    + class="float-right">

    Comparing 30d ago to Now @@ -1265,10 +1270,23 @@ + + + + + + + + + + +