From f493a7167c83ff5edc3894cf8a6fd9f3c276481e Mon Sep 17 00:00:00 2001 From: Rsl1122 Date: Wed, 3 Jul 2019 12:05:33 +0300 Subject: [PATCH] Implemented large part of server overview json --- .../queries/ServerAggregateQueries.java | 122 ++++++++++++ .../queries/analysis/PlayerCountQueries.java | 66 +++++++ .../ServerPlayersTableContainersQuery.java | 3 +- .../db/access/queries/objects/TPSQueries.java | 68 +++++++ .../plan/system/export/HtmlExport.java | 2 +- .../plan/system/export/JSONExport.java | 2 +- .../plan/system/export/SpecificExport.java | 2 +- .../plan/system/json/GraphJSONParser.java | 77 ++++++++ .../pages => }/json/JSONFactory.java | 3 +- .../json}/PlayersTableJSONParser.java | 2 +- .../system/json/ServerOverviewJSONParser.java | 185 ++++++++++++++++++ .../djrapitops/plan/system/json/Trend.java | 46 +++++ .../pages/json/GraphsJSONHandler.java | 50 +---- .../pages/json/PlayersTableJSONHandler.java | 4 +- .../webserver/pages/json/RootJSONHandler.java | 4 +- .../pages/json/ServerOverviewJSONHandler.java | 63 ++++++ .../resources/assets/plan/web/server.html | 85 +------- 17 files changed, 649 insertions(+), 135 deletions(-) create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/PlayerCountQueries.java create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/json/GraphJSONParser.java rename Plan/common/src/main/java/com/djrapitops/plan/system/{webserver/pages => }/json/JSONFactory.java (95%) rename Plan/common/src/main/java/com/djrapitops/plan/{utilities/html/tables => system/json}/PlayersTableJSONParser.java (99%) create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerOverviewJSONParser.java create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/json/Trend.java create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/ServerOverviewJSONHandler.java diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/ServerAggregateQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/ServerAggregateQueries.java index 0a88369ce..75bc15f0c 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/ServerAggregateQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/ServerAggregateQueries.java @@ -160,4 +160,126 @@ public class ServerAggregateQueries { } }; } + + public static Query totalPlaytime(UUID serverUUID) { + String sql = SELECT + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("playtime") : 0L; + } + }; + } + + public static Query sessionCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "COUNT(1) as count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + public static Query playerKillCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "COUNT(1) as count" + + FROM + KillsTable.TABLE_NAME + + WHERE + KillsTable.SERVER_UUID + "=?" + + AND + KillsTable.DATE + ">=?" + + AND + KillsTable.DATE + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + public static Query mobKillCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "SUM(" + SessionsTable.MOB_KILLS + ") as count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + public static Query deathCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "SUM(" + SessionsTable.DEATHS + ") as count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("count") : 0L; + } + }; + } + + public static Query playtime(long after, long before, UUID serverUUID) { + String sql = SELECT + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SERVER_UUID + "=?" + + AND + SessionsTable.SESSION_END + ">=?" + + AND + SessionsTable.SESSION_START + "<=?"; + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public Long processResults(ResultSet set) throws SQLException { + return set.next() ? set.getLong("playtime") : 0L; + } + }; + } } 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 new file mode 100644 index 000000000..ef9f41ec3 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/analysis/PlayerCountQueries.java @@ -0,0 +1,66 @@ +package com.djrapitops.plan.db.access.queries.analysis; + +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.UserInfoTable; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.UUID; + +import static com.djrapitops.plan.db.sql.parsing.Sql.*; + +/** + * Queries for server overview tab data. + * + * @author Rsl1122 + */ +public class PlayerCountQueries { + + private PlayerCountQueries() { + // Static method class + } + + private static QueryStatement queryPlayerCount(String sql, long after, long before, UUID serverUUID) { + return new QueryStatement(sql) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, before); + statement.setLong(2, after); + statement.setString(3, serverUUID.toString()); + } + + @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(" + SessionsTable.USER_UUID + ") as player_count" + + FROM + SessionsTable.TABLE_NAME + + WHERE + SessionsTable.SESSION_END + "<=?" + + AND + SessionsTable.SESSION_START + ">=?" + + AND + SessionsTable.SERVER_UUID + "=?" + + GROUP_BY + SessionsTable.USER_UUID; + + return queryPlayerCount(sql, after, before, serverUUID); + } + + public static Query uniquePlayerCountPerDay(long after, long before, UUID serverUUID) { + return db -> 0; // TODO + } + + public static Query newPlayerCount(long after, long before, UUID serverUUID) { + String sql = SELECT + "COUNT(" + UserInfoTable.USER_UUID + ") as player_count" + + FROM + UserInfoTable.TABLE_NAME + + WHERE + UserInfoTable.REGISTERED + "<=?" + + AND + UserInfoTable.REGISTERED + ">=?" + + AND + UserInfoTable.SERVER_UUID + "=?"; + + return queryPlayerCount(sql, after, before, serverUUID); + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java index c1594e184..68950c5e4 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/containers/ServerPlayersTableContainersQuery.java @@ -27,6 +27,7 @@ import com.djrapitops.plan.db.access.queries.objects.BaseUserQueries; import com.djrapitops.plan.db.access.queries.objects.GeoInfoQueries; import com.djrapitops.plan.db.access.queries.objects.SessionQueries; import com.djrapitops.plan.db.access.queries.objects.UserInfoQueries; +import com.djrapitops.plan.system.json.PlayersTableJSONParser; import java.util.*; @@ -34,7 +35,7 @@ import java.util.*; * Optimized version of {@link ServerPlayerContainersQuery} for /server page Players table. * * @author Rsl1122 - * @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For what needs to be included. + * @see PlayersTableJSONParser For what needs to be included. */ public class ServerPlayersTableContainersQuery implements Query> { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/TPSQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/TPSQueries.java index 528e3a863..abe479e45 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/TPSQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/db/access/queries/objects/TPSQueries.java @@ -81,6 +81,43 @@ public class TPSQueries { }; } + public static Query> fetchTPSDataOfServer(long after, long before, UUID serverUUID) { + String sql = Select.all(TABLE_NAME) + .where(SERVER_ID + "=" + ServerTable.STATEMENT_SELECT_SERVER_ID) + .and(DATE + ">=?").and(DATE + "<=?") + .toString(); + + return new QueryStatement>(sql, 50000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + statement.setLong(2, after); + statement.setLong(3, before); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + List data = new ArrayList<>(); + while (set.next()) { + + TPS tps = TPSBuilder.get() + .date(set.getLong(DATE)) + .tps(set.getDouble(TPS)) + .playersOnline(set.getInt(PLAYERS_ONLINE)) + .usedCPU(set.getDouble(CPU_USAGE)) + .usedMemory(set.getLong(RAM_USAGE)) + .entities(set.getInt(ENTITIES)) + .chunksLoaded(set.getInt(CHUNKS)) + .freeDiskSpace(set.getLong(FREE_DISK)) + .toTPS(); + + data.add(tps); + } + return data; + } + }; + } + public static Query>> fetchPlayerOnlineDataOfServers(Collection servers) { if (servers.isEmpty()) { return db -> new HashMap<>(); @@ -155,4 +192,35 @@ public class TPSQueries { public static Query>> fetchAllTimePeakPlayerCount(UUID serverUUID) { return fetchPeakPlayerCount(serverUUID, 0); } + + public static Query> fetchLatestTPSEntryForServer(UUID serverUUID) { + String sql = SELECT + "*" + + FROM + TABLE_NAME + + WHERE + SERVER_ID + '=' + ServerTable.STATEMENT_SELECT_SERVER_ID + + ORDER_BY + DATE + " DESC LIMIT 1"; + + return new QueryStatement>(sql, 50000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setString(1, serverUUID.toString()); + } + + @Override + public Optional processResults(ResultSet set) throws SQLException { + if (set.next()) { + return Optional.of(TPSBuilder.get() + .date(set.getLong(DATE)) + .tps(set.getDouble(TPS)) + .playersOnline(set.getInt(PLAYERS_ONLINE)) + .usedCPU(set.getDouble(CPU_USAGE)) + .usedMemory(set.getLong(RAM_USAGE)) + .entities(set.getInt(ENTITIES)) + .chunksLoaded(set.getInt(CHUNKS)) + .freeDiskSpace(set.getLong(FREE_DISK)) + .toTPS()); + } + return Optional.empty(); + } + }; + } } \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java index 844955ecb..99b8ecf28 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/HtmlExport.java @@ -27,13 +27,13 @@ import com.djrapitops.plan.system.file.PlanFiles; import com.djrapitops.plan.system.info.connection.ConnectionSystem; import com.djrapitops.plan.system.info.server.Server; import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.json.JSONFactory; import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.paths.ExportSettings; import com.djrapitops.plan.system.settings.theme.Theme; import com.djrapitops.plan.system.settings.theme.ThemeVal; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; import com.djrapitops.plan.system.webserver.response.pages.NetworkPageResponse; import com.djrapitops.plan.utilities.html.pages.InspectPage; import com.djrapitops.plan.utilities.html.pages.NetworkPage; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java index 04ddf947c..ae32747dc 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/JSONExport.java @@ -22,9 +22,9 @@ 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.info.server.ServerInfo; +import com.djrapitops.plan.system.json.JSONFactory; import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.paths.ExportSettings; -import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; import com.djrapitops.plan.system.webserver.response.ResponseFactory; import com.djrapitops.plugin.logging.L; import com.djrapitops.plugin.logging.error.ErrorHandler; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java b/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java index b10b48bd9..4ca538b8e 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/export/SpecificExport.java @@ -18,9 +18,9 @@ package com.djrapitops.plan.system.export; import com.djrapitops.plan.system.file.PlanFiles; import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.json.JSONFactory; import com.djrapitops.plan.system.webserver.cache.PageId; import com.djrapitops.plan.system.webserver.cache.ResponseCache; -import com.djrapitops.plan.system.webserver.pages.json.JSONFactory; import com.djrapitops.plan.system.webserver.response.Response; import java.io.File; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/GraphJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/GraphJSONParser.java new file mode 100644 index 000000000..f733af627 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/GraphJSONParser.java @@ -0,0 +1,77 @@ +package com.djrapitops.plan.system.json; + +import com.djrapitops.plan.data.store.mutators.MutatorFunctions; +import com.djrapitops.plan.data.store.mutators.PlayersMutator; +import com.djrapitops.plan.data.store.mutators.SessionsMutator; +import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.db.Database; +import com.djrapitops.plan.db.access.queries.containers.ServerPlayerContainersQuery; +import com.djrapitops.plan.db.access.queries.objects.SessionQueries; +import com.djrapitops.plan.db.access.queries.objects.TPSQueries; +import com.djrapitops.plan.system.database.DBSystem; +import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.utilities.html.graphs.Graphs; +import com.djrapitops.plan.utilities.html.graphs.line.LineGraphFactory; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.TimeZone; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Perses Graph related Data JSON. + * + * @author Rsl1122 + */ +@Singleton +public class GraphJSONParser { + + private final DBSystem dbSystem; + private final Graphs graphs; + private final TimeZone timeZone; + + @Inject + public GraphJSONParser( + PlanConfig config, + DBSystem dbSystem, + Graphs graphs + ) { + this.dbSystem = dbSystem; + this.graphs = graphs; + this.timeZone = config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT"); + } + + public String performanceGraphJSON(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + LineGraphFactory lineGraphs = graphs.line(); + TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(serverUUID))) + .filterDataBetween(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(180L), System.currentTimeMillis()); + return '{' + + "\"playersOnline\":" + lineGraphs.playersOnlineGraph(tpsMutator).toHighChartsSeries() + + ",\"tps\":" + lineGraphs.tpsGraph(tpsMutator).toHighChartsSeries() + + ",\"cpu\":" + lineGraphs.cpuGraph(tpsMutator).toHighChartsSeries() + + ",\"ram\":" + lineGraphs.ramGraph(tpsMutator).toHighChartsSeries() + + ",\"entities\":" + lineGraphs.entityGraph(tpsMutator).toHighChartsSeries() + + ",\"chunks\":" + lineGraphs.chunkGraph(tpsMutator).toHighChartsSeries() + + ",\"disk\":" + lineGraphs.diskGraph(tpsMutator).toHighChartsSeries() + + '}'; + } + + public String uniqueAndNewGraphJSON(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + LineGraphFactory lineGraphs = graphs.line(); + SessionsMutator sessionsMutator = new SessionsMutator(db.query(SessionQueries.fetchSessionsOfServerFlat(serverUUID))) + .filterSessionsBetween(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(180L), System.currentTimeMillis()); + PlayersMutator playersMutator = new PlayersMutator(db.query(new ServerPlayerContainersQuery(serverUUID))) + .filterRegisteredBetween(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(180L), System.currentTimeMillis()); + + return "{\"uniquePlayers\":" + + lineGraphs.lineGraph(MutatorFunctions.toPointsWithRemovedOffset(sessionsMutator.uniqueJoinsPerDay(timeZone), timeZone)).toHighChartsSeries() + + ",\"newPlayers\":" + + lineGraphs.lineGraph(MutatorFunctions.toPointsWithRemovedOffset(playersMutator.newPerDay(timeZone), timeZone)).toHighChartsSeries() + + '}'; + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/JSONFactory.java similarity index 95% rename from Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java rename to Plan/common/src/main/java/com/djrapitops/plan/system/json/JSONFactory.java index f42363709..746616951 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/JSONFactory.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/JSONFactory.java @@ -14,7 +14,7 @@ * 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; +package com.djrapitops.plan.system.json; import com.djrapitops.plan.db.Database; import com.djrapitops.plan.db.access.queries.containers.ServerPlayersTableContainersQuery; @@ -24,7 +24,6 @@ import com.djrapitops.plan.system.settings.config.PlanConfig; import com.djrapitops.plan.system.settings.paths.DisplaySettings; import com.djrapitops.plan.system.settings.paths.TimeSettings; import com.djrapitops.plan.utilities.formatting.Formatters; -import com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser; import javax.inject.Inject; import javax.inject.Singleton; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayersTableJSONParser.java similarity index 99% rename from Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java rename to Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayersTableJSONParser.java index 0a4dee239..ce32bfa53 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/utilities/html/tables/PlayersTableJSONParser.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/PlayersTableJSONParser.java @@ -14,7 +14,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with Plan. If not, see . */ -package com.djrapitops.plan.utilities.html.tables; +package com.djrapitops.plan.system.json; import com.djrapitops.plan.api.PlanAPI; import com.djrapitops.plan.data.container.GeoInfo; 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 new file mode 100644 index 000000000..573290154 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/ServerOverviewJSONParser.java @@ -0,0 +1,185 @@ +package com.djrapitops.plan.system.json; + +import com.djrapitops.plan.data.container.TPS; +import com.djrapitops.plan.data.store.mutators.TPSMutator; +import com.djrapitops.plan.data.store.objects.DateHolder; +import com.djrapitops.plan.data.store.objects.DateObj; +import com.djrapitops.plan.db.Database; +import com.djrapitops.plan.db.access.queries.ServerAggregateQueries; +import com.djrapitops.plan.db.access.queries.analysis.PlayerCountQueries; +import com.djrapitops.plan.db.access.queries.objects.TPSQueries; +import com.djrapitops.plan.system.database.DBSystem; +import com.djrapitops.plan.system.info.server.ServerInfo; +import com.djrapitops.plan.system.settings.config.PlanConfig; +import com.djrapitops.plan.system.settings.paths.DisplaySettings; +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.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +/** + * Parses JSON payload for /server-page Server Overview tab. + * + * @author Rsl1122 + */ +@Singleton +public class ServerOverviewJSONParser { + + private PlanConfig config; + private DBSystem dbSystem; + private ServerInfo serverInfo; + + private Formatter timeAmountFormatter; + private Formatter decimalFormatter; + private Formatter percentageFormatter; + private Formatter dateFormatter; + + @Inject + public ServerOverviewJSONParser( + PlanConfig config, + DBSystem dbSystem, + ServerInfo serverInfo, + Formatters formatters + ) { + this.config = config; + this.dbSystem = dbSystem; + this.serverInfo = serverInfo; + + dateFormatter = formatters.year(); + timeAmountFormatter = formatters.timeAmount(); + decimalFormatter = formatters.decimals(); + percentageFormatter = formatters.percentage(); + } + + public Map createServerOverviewJSONAsMap(UUID serverUUID) { + Map serverOverview = new HashMap<>(); + serverOverview.put("last_7_days", createLast7DaysMap(serverUUID)); + serverOverview.put("numbers", createNumbersMap(serverUUID)); + serverOverview.put("weeks", createWeeksMap(serverUUID)); + return serverOverview; + } + + private Map createLast7DaysMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long sevenDaysAgo = now - TimeUnit.DAYS.toMillis(7L); + + Map sevenDays = new HashMap<>(); + + sevenDays.put("unique_players", db.query(PlayerCountQueries.uniquePlayerCount(sevenDaysAgo, now, serverUUID))); + sevenDays.put("unique_players_day", db.query(PlayerCountQueries.uniquePlayerCountPerDay(sevenDaysAgo, now, serverUUID))); // TODO + sevenDays.put("new_players", db.query(PlayerCountQueries.newPlayerCount(sevenDaysAgo, now, serverUUID))); + sevenDays.put("new_players_retention", 0); // TODO + sevenDays.put("new_players_retention_perc", percentageFormatter.apply(-1.0)); // TODO + TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(sevenDaysAgo, now, serverUUID))); + sevenDays.put("average_tps", decimalFormatter.apply(tpsMutator.averageTPS())); + sevenDays.put("low_tps_spikes", tpsMutator.lowTpsSpikeCount(config.getNumber(DisplaySettings.GRAPH_TPS_THRESHOLD_MED))); + sevenDays.put("downtime", timeAmountFormatter.apply(tpsMutator.serverDownTime())); + + return sevenDays; + } + + private Map createNumbersMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long twoDaysAgo = now - TimeUnit.DAYS.toMillis(2L); + + Map numbers = new HashMap<>(); + + numbers.put("total_players", db.query(ServerAggregateQueries.serverUserCount(serverUUID))); + numbers.put("regular_players", 0); // TODO + numbers.put("online_players", getOnlinePlayers(serverUUID, db)); + Optional> lastPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo)); + Optional> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID)); + numbers.put("last_peak_date", lastPeak.map(dateFormatter).orElse("-")); + numbers.put("last_peak_players", lastPeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); + numbers.put("best_peak_date", allTimePeak.map(dateFormatter).orElse("-")); + numbers.put("best_peak_players", allTimePeak.map(dateObj -> dateObj.getValue().toString()).orElse("-")); + numbers.put("playtime", timeAmountFormatter.apply(db.query(ServerAggregateQueries.totalPlaytime(serverUUID)))); + numbers.put("player_playtime", "-"); // TODO + numbers.put("sessions", db.query(ServerAggregateQueries.sessionCount(0L, now, serverUUID))); + numbers.put("player_kills", db.query(ServerAggregateQueries.playerKillCount(0L, now, serverUUID))); + numbers.put("mob_kills", db.query(ServerAggregateQueries.mobKillCount(0L, now, serverUUID))); + numbers.put("deaths", db.query(ServerAggregateQueries.deathCount(0L, now, serverUUID))); + + return numbers; + } + + private Object getOnlinePlayers(UUID serverUUID, Database db) { + return serverUUID.equals(serverInfo.getServerUUID()) + ? serverInfo.getServerProperties().getOnlinePlayers() + : db.query(TPSQueries.fetchLatestTPSEntryForServer(serverUUID)) + .map(TPS::getPlayers).map(Object::toString) + .orElse("Unknown"); + } + + private Map createWeeksMap(UUID serverUUID) { + Database db = dbSystem.getDatabase(); + long now = System.currentTimeMillis(); + long oneWeekAgo = now - TimeUnit.DAYS.toMillis(7L); + long twoWeeksAgo = now - TimeUnit.DAYS.toMillis(14L); + + Map weeks = new HashMap<>(); + + Integer uniqueBefore = db.query(PlayerCountQueries.uniquePlayerCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Integer uniqueAfter = db.query(PlayerCountQueries.uniquePlayerCount(oneWeekAgo, now, serverUUID)); + Trend uniqueTrend = new Trend(uniqueBefore, uniqueAfter, false); + weeks.put("unique_before", uniqueBefore); + weeks.put("unique_after", uniqueAfter); + weeks.put("unique_trend", uniqueTrend); + + Integer newBefore = db.query(PlayerCountQueries.newPlayerCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Integer newAfter = db.query(PlayerCountQueries.newPlayerCount(oneWeekAgo, now, serverUUID)); + Trend newTrend = new Trend(newBefore, newAfter, false); + weeks.put("new_before", newBefore); + weeks.put("new_after", newAfter); + weeks.put("new_trend", newTrend); + + weeks.put("regular_before", "-"); // TODO + weeks.put("regular_after", "-"); + weeks.put("regular_trend", new Trend(0, 0, false)); + + Long playtimeBefore = db.query(ServerAggregateQueries.playtime(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long playtimeAfter = db.query(ServerAggregateQueries.playtime(oneWeekAgo, now, serverUUID)); + Trend playtimeTrend = new Trend(playtimeBefore, playtimeAfter, false, timeAmountFormatter); + weeks.put("playtime_before", timeAmountFormatter.apply(playtimeBefore)); + weeks.put("playtime_after", timeAmountFormatter.apply(playtimeAfter)); + weeks.put("playtime_trend", playtimeTrend); + + Long sessionsBefore = db.query(ServerAggregateQueries.sessionCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long sessionsAfter = db.query(ServerAggregateQueries.sessionCount(oneWeekAgo, now, serverUUID)); + Trend sessionsTrend = new Trend(sessionsBefore, sessionsAfter, false); + weeks.put("sessions_before", sessionsBefore); + weeks.put("sessions_after", sessionsAfter); + weeks.put("sessions_trend", sessionsTrend); + + Long pksBefore = db.query(ServerAggregateQueries.playerKillCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long pksAfter = db.query(ServerAggregateQueries.playerKillCount(oneWeekAgo, now, serverUUID)); + Trend pksTrend = new Trend(pksBefore, pksAfter, false); + weeks.put("player_kills_before", pksBefore); + weeks.put("player_kills_after", pksAfter); + weeks.put("player_kills_trend", pksTrend); + + Long mkBefore = db.query(ServerAggregateQueries.mobKillCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long mkAfter = db.query(ServerAggregateQueries.mobKillCount(oneWeekAgo, now, serverUUID)); + Trend mkTrend = new Trend(mkBefore, mkAfter, false); + weeks.put("mob_kills_before", mkBefore); + weeks.put("mob_kills_after", mkAfter); + weeks.put("mob_kills_trend", mkTrend); + + Long deathsBefore = db.query(ServerAggregateQueries.deathCount(twoWeeksAgo, oneWeekAgo, serverUUID)); + Long deathsAfter = db.query(ServerAggregateQueries.deathCount(oneWeekAgo, now, serverUUID)); + Trend deathTrend = new Trend(deathsBefore, deathsAfter, true); + weeks.put("deaths_before", deathsBefore); + weeks.put("deaths_after", deathsAfter); + weeks.put("deaths_trend", deathTrend); + + return weeks; + } +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/json/Trend.java b/Plan/common/src/main/java/com/djrapitops/plan/system/json/Trend.java new file mode 100644 index 000000000..33c17f1f0 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/json/Trend.java @@ -0,0 +1,46 @@ +package com.djrapitops.plan.system.json; + +import com.djrapitops.plan.utilities.formatting.Formatter; + +/** + * Represents a trend in the data - used for JSON format. + * + * @author Rsl1122 + */ +public class Trend { + + private String text; + private String direction; + private boolean reversed; + + public Trend(long before, long after, boolean reversed) { + long difference = Math.abs(before - after); + this.text = Long.toString(difference); + this.direction = getDirection(before, after); + this.reversed = reversed; + } + + public Trend(long before, long after, boolean reversed, Formatter formatter) { + long difference = Math.abs(before - after); + this.text = formatter.apply(difference); + this.direction = getDirection(before, after); + this.reversed = reversed; + } + + public Trend(double before, double after, boolean reversed, Formatter formatter) { + double difference = Math.abs(before - after); + this.text = formatter.apply(difference); + this.direction = getDirection(before, after); + this.reversed = reversed; + } + + private String getDirection(double before, double after) { + if (before < after) { + return "+"; + } else if (before > after) { + return "-"; + } + return null; + } + +} \ No newline at end of file diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/GraphsJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/GraphsJSONHandler.java index b901b9796..6153b7aed 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/GraphsJSONHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/GraphsJSONHandler.java @@ -19,30 +19,17 @@ package com.djrapitops.plan.system.webserver.pages.json; import com.djrapitops.plan.api.exceptions.WebUserAuthException; import com.djrapitops.plan.api.exceptions.connection.BadRequestException; import com.djrapitops.plan.api.exceptions.connection.WebException; -import com.djrapitops.plan.data.store.mutators.MutatorFunctions; -import com.djrapitops.plan.data.store.mutators.PlayersMutator; -import com.djrapitops.plan.data.store.mutators.SessionsMutator; -import com.djrapitops.plan.data.store.mutators.TPSMutator; -import com.djrapitops.plan.db.Database; -import com.djrapitops.plan.db.access.queries.containers.ServerPlayerContainersQuery; -import com.djrapitops.plan.db.access.queries.objects.SessionQueries; -import com.djrapitops.plan.db.access.queries.objects.TPSQueries; import com.djrapitops.plan.system.database.DBSystem; -import com.djrapitops.plan.system.settings.config.PlanConfig; -import com.djrapitops.plan.system.settings.paths.TimeSettings; +import com.djrapitops.plan.system.json.GraphJSONParser; 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.response.Response; import com.djrapitops.plan.system.webserver.response.data.JSONResponse; -import com.djrapitops.plan.utilities.html.graphs.Graphs; -import com.djrapitops.plan.utilities.html.graphs.line.LineGraphFactory; import javax.inject.Inject; import javax.inject.Singleton; -import java.util.TimeZone; import java.util.UUID; -import java.util.concurrent.TimeUnit; /** * JSON handler for different graph data JSON requests. @@ -52,19 +39,15 @@ import java.util.concurrent.TimeUnit; @Singleton public class GraphsJSONHandler extends ServerParameterJSONHandler { - private final Graphs graphs; - private final TimeZone timeZone; + private final GraphJSONParser graphJSON; @Inject public GraphsJSONHandler( - PlanConfig config, DBSystem dbSystem, - Graphs graphs + GraphJSONParser graphJSON ) { super(dbSystem); - this.graphs = graphs; - - this.timeZone = config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT"); + this.graphJSON = graphJSON; } @Override @@ -77,32 +60,11 @@ public class GraphsJSONHandler extends ServerParameterJSONHandler { } private String generateGraphDataJSONOfType(String type, UUID serverUUID) throws BadRequestException { - Database db = dbSystem.getDatabase(); - LineGraphFactory lineGraphs = graphs.line(); switch (type) { case "performance": - TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(serverUUID))) - .filterDataBetween(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(180L), System.currentTimeMillis()); - return '{' + - "\"playersOnline\":" + lineGraphs.playersOnlineGraph(tpsMutator).toHighChartsSeries() + - ",\"tps\":" + lineGraphs.tpsGraph(tpsMutator).toHighChartsSeries() + - ",\"cpu\":" + lineGraphs.cpuGraph(tpsMutator).toHighChartsSeries() + - ",\"ram\":" + lineGraphs.ramGraph(tpsMutator).toHighChartsSeries() + - ",\"entities\":" + lineGraphs.entityGraph(tpsMutator).toHighChartsSeries() + - ",\"chunks\":" + lineGraphs.chunkGraph(tpsMutator).toHighChartsSeries() + - ",\"disk\":" + lineGraphs.diskGraph(tpsMutator).toHighChartsSeries() + - '}'; + return graphJSON.performanceGraphJSON(serverUUID); case "uniqueAndNew": - SessionsMutator sessionsMutator = new SessionsMutator(db.query(SessionQueries.fetchSessionsOfServerFlat(serverUUID))) - .filterSessionsBetween(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(180L), System.currentTimeMillis()); - PlayersMutator playersMutator = new PlayersMutator(db.query(new ServerPlayerContainersQuery(serverUUID))) - .filterRegisteredBetween(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(180L), System.currentTimeMillis()); - - return "{\"uniquePlayers\":" + - lineGraphs.lineGraph(MutatorFunctions.toPointsWithRemovedOffset(sessionsMutator.uniqueJoinsPerDay(timeZone), timeZone)).toHighChartsSeries() + - ",\"newPlayers\":" + - lineGraphs.lineGraph(MutatorFunctions.toPointsWithRemovedOffset(playersMutator.newPerDay(timeZone), timeZone)).toHighChartsSeries() + - '}'; + return graphJSON.uniqueAndNewGraphJSON(serverUUID); default: throw new BadRequestException("unknown 'type' parameter: " + type); } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java index fae3356d8..ec00450d9 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/PlayersTableJSONHandler.java @@ -19,6 +19,8 @@ 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.database.DBSystem; +import com.djrapitops.plan.system.json.JSONFactory; +import com.djrapitops.plan.system.json.PlayersTableJSONParser; import com.djrapitops.plan.system.webserver.Request; import com.djrapitops.plan.system.webserver.RequestTarget; import com.djrapitops.plan.system.webserver.auth.Authentication; @@ -33,7 +35,7 @@ import java.util.UUID; * JSON handler for different Player table JSON requests. * * @author Rsl1122 - * @see com.djrapitops.plan.utilities.html.tables.PlayersTableJSONParser For JSON parsing of /server players table. + * @see PlayersTableJSONParser For JSON parsing of /server players table. */ @Singleton public class PlayersTableJSONHandler extends ServerParameterJSONHandler { 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 449759826..1f2a10235 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 @@ -37,12 +37,14 @@ public class RootJSONHandler extends TreePageHandler { public RootJSONHandler( ResponseFactory responseFactory, GraphsJSONHandler graphsJSONHandler, - PlayersTableJSONHandler playersTableJSONHandler + PlayersTableJSONHandler playersTableJSONHandler, + ServerOverviewJSONHandler serverOverviewJSONHandler ) { super(responseFactory); registerPage("players", playersTableJSONHandler); registerPage("graph", graphsJSONHandler); + registerPage("serverOverview", serverOverviewJSONHandler); } @Override diff --git a/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/ServerOverviewJSONHandler.java b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/ServerOverviewJSONHandler.java new file mode 100644 index 000000000..be8a88cd9 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/system/webserver/pages/json/ServerOverviewJSONHandler.java @@ -0,0 +1,63 @@ +/* + * 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.database.DBSystem; +import com.djrapitops.plan.system.json.ServerOverviewJSONParser; +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.response.Response; +import com.djrapitops.plan.system.webserver.response.data.JSONResponse; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.UUID; + +/** + * JSON handler for different Player table JSON requests. + * + * @author Rsl1122 + * @see com.djrapitops.plan.system.json.PlayersTableJSONParser For JSON parsing of /server players table. + */ +@Singleton +public class ServerOverviewJSONHandler extends ServerParameterJSONHandler { + + private final ServerOverviewJSONParser serverOverviewJSON; + + @Inject + public ServerOverviewJSONHandler( + DBSystem dbSystem, + ServerOverviewJSONParser serverOverviewJSON + ) { + super(dbSystem); + this.serverOverviewJSON = serverOverviewJSON; + } + + @Override + public Response getResponse(Request request, RequestTarget target) throws WebException { + UUID serverUUID = getServerUUID(target); // Can throw BadRequestException + return new JSONResponse<>(serverOverviewJSON.createServerOverviewJSONAsMap(serverUUID)); + } + + @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/resources/assets/plan/web/server.html b/Plan/common/src/main/resources/assets/plan/web/server.html index e72458a77..1438d5a95 100644 --- a/Plan/common/src/main/resources/assets/plan/web/server.html +++ b/Plan/common/src/main/resources/assets/plan/web/server.html @@ -194,11 +194,11 @@

New Players

New Player Retention -  


Average TPS 19,92

+ class="float-right">

Low TPS Spikes

Server downtime setLoadingText('Calculating values..'); - loadServerOverviewValues( - { - last_7_days: { - unique_players: 5, - unique_players_day: 2, - new_players: 0, - new_players_retention: 0, - new_players_retention_perc: '-', - average_tps: '19,92', - low_tps_spikes: 0, - downtime: '5d 3h 33m 46s' - }, - numbers: { - total_players: 4353, - regular_players: 432, - online_players: 43, - last_peak_date: 'Yesterday, 16:28', - last_peak_players: 43, - best_peak_date: 'Sep 5 2017, 12:43', - best_peak_players: 51, - playtime: '8h 5m 56s', - player_playtime: '43m 5s', - sessions: 98, - player_kills: 0, - mob_kills: 22, - deaths: 8 - }, - weeks: { - unique_before: 5, - unique_after: 5, - unique_trend: { - text: 0, - direction: null - }, - new_before: 45, - new_after: 44, - new_trend: { - text: 1, - direction: '-' - }, - regular_before: 427, - regular_after: 432, - regular_trend: { - text: 5, - direction: '+' - }, - playtime_before: '8h 5m 56s', - playtime_after: '8h 53m 24s', - playtime_trend: { - text: '47m 32s', - direction: '+' - }, - sessions_before: '32', - sessions_after: '64', - sessions_trend: { - text: 32, - direction: '+' - }, - player_kills_before: 32, - player_kills_after: 64, - player_kills_trend: { - text: 32, - direction: '+' - }, - mob_kills_before: 5432, - mob_kills_after: 654, - mob_kills_trend: { - text: 4778, - direction: '-' - }, - deaths_before: 8, - deaths_after: 13, - deaths_trend: { - text: 5, - direction: '+', - reversed: true - } - } - }, null // TODO Replace with result of a XMLHTTPRequest - ); + jsonRequest("../v1/serverOverview?serverName=${serverName}", loadServerOverviewValues); loadOnlineActivityOverviewValues( { numbers: {