diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java index 8426a2ef3..409a8edbe 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java @@ -16,8 +16,10 @@ */ package com.djrapitops.plan.delivery.webserver.resolver.json; +import com.djrapitops.plan.delivery.domain.DateMap; import com.djrapitops.plan.delivery.formatting.Formatters; import com.djrapitops.plan.delivery.rendering.json.PlayersTableJSONCreator; +import com.djrapitops.plan.delivery.rendering.json.graphs.GraphJSONCreator; import com.djrapitops.plan.delivery.web.resolver.MimeType; import com.djrapitops.plan.delivery.web.resolver.Resolver; import com.djrapitops.plan.delivery.web.resolver.Response; @@ -30,12 +32,14 @@ import com.djrapitops.plan.settings.config.paths.TimeSettings; import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.storage.database.DBSystem; import com.djrapitops.plan.storage.database.Database; +import com.djrapitops.plan.storage.database.queries.analysis.NetworkActivityIndexQueries; import com.djrapitops.plan.storage.database.queries.filter.Filter; import com.djrapitops.plan.storage.database.queries.filter.FilterQuery; import com.djrapitops.plan.storage.database.queries.filter.QueryFilters; import com.djrapitops.plan.storage.database.queries.objects.playertable.QueryTablePlayersQuery; import com.djrapitops.plan.storage.json.JSONStorage; import com.djrapitops.plan.utilities.java.Maps; +import com.djrapitops.plugin.api.TimeAmount; import com.google.gson.Gson; import javax.inject.Inject; @@ -54,6 +58,7 @@ public class QueryJSONResolver implements Resolver { private final PlanConfig config; private final DBSystem dbSystem; private final JSONStorage jsonStorage; + private final GraphJSONCreator graphJSONCreator; private final Locale locale; private final Formatters formatters; @@ -63,6 +68,7 @@ public class QueryJSONResolver implements Resolver { PlanConfig config, DBSystem dbSystem, JSONStorage jsonStorage, + GraphJSONCreator graphJSONCreator, Locale locale, Formatters formatters ) { @@ -70,6 +76,7 @@ public class QueryJSONResolver implements Resolver { this.config = config; this.dbSystem = dbSystem; this.jsonStorage = jsonStorage; + this.graphJSONCreator = graphJSONCreator; this.locale = locale; this.formatters = formatters; } @@ -140,12 +147,34 @@ public class QueryJSONResolver implements Resolver { Database database = dbSystem.getDatabase(); return Maps.builder(String.class, Object.class) - .put("players", new PlayersTableJSONCreator( - database.query(new QueryTablePlayersQuery(playerUUIDs, after, before, config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD))), - Collections.emptyMap(), - config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB), - formatters, locale - ).toJSONMap()) + .put("players", getPlayersTableData(playerUUIDs, after, before)) + .put("activity", getActivityGraphData(playerUUIDs, after, before)) .build(); } + + private Map getActivityGraphData(Set playerUUIDs, long after, long before) { + Database database = dbSystem.getDatabase(); + Long threshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD); + + long twoMonthsBeforeLastDate = before - TimeAmount.MONTH.toMillis(2L); + long stopDate = Math.max(twoMonthsBeforeLastDate, after); + + DateMap> activityData = new DateMap<>(); + for (long time = before; time >= stopDate; time -= TimeAmount.WEEK.toMillis(1L)) { + activityData.put(time, database.query(NetworkActivityIndexQueries.fetchActivityIndexGroupingsOn(time, threshold, playerUUIDs))); + } + + Map activityGraphJSON = graphJSONCreator.createActivityGraphJSON(activityData); + return activityGraphJSON; + } + + private Map getPlayersTableData(Set playerUUIDs, long after, long before) { + Database database = dbSystem.getDatabase(); + return new PlayersTableJSONCreator( + database.query(new QueryTablePlayersQuery(playerUUIDs, after, before, config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD))), + Collections.emptyMap(), + config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB), + formatters, locale + ).toJSONMap(); + } } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/NetworkActivityIndexQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/NetworkActivityIndexQueries.java index 8e39565b3..416bd4cb7 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/NetworkActivityIndexQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/analysis/NetworkActivityIndexQueries.java @@ -21,6 +21,7 @@ import com.djrapitops.plan.storage.database.queries.Query; import com.djrapitops.plan.storage.database.queries.QueryStatement; import com.djrapitops.plan.storage.database.sql.tables.SessionsTable; import com.djrapitops.plan.storage.database.sql.tables.UsersTable; +import org.apache.commons.text.TextStringBuilder; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -162,6 +163,36 @@ public class NetworkActivityIndexQueries { }; } + public static Query> fetchActivityIndexGroupingsOn(long date, long threshold, Collection playerUUIDs) { + String selectActivityIndex = selectActivityIndexSQL(); + + String selectIndexes = SELECT + "activity_index" + + FROM + UsersTable.TABLE_NAME + " u" + + LEFT_JOIN + '(' + selectActivityIndex + ") s on s." + SessionsTable.USER_UUID + "=u." + UsersTable.USER_UUID + + WHERE + "u." + UsersTable.REGISTERED + "<=?" + + AND + "u." + UsersTable.USER_UUID + " IN ('" + + new TextStringBuilder().appendWithSeparators(playerUUIDs, "','").build() + "')"; + + return new QueryStatement>(selectIndexes) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + setSelectActivityIndexSQLParameters(statement, 1, threshold, date); + statement.setLong(9, date); + } + + @Override + public Map processResults(ResultSet set) throws SQLException { + Map groups = new HashMap<>(); + while (set.next()) { + double activityIndex = set.getDouble("activity_index"); + String group = ActivityIndex.getGroup(activityIndex); + groups.put(group, groups.getOrDefault(group, 0) + 1); + } + return groups; + } + }; + } + public static Query countNewPlayersTurnedRegular(long after, long before, Long threshold) { String selectActivityIndex = selectActivityIndexSQL(); diff --git a/Plan/common/src/main/resources/assets/plan/web/js/query.js b/Plan/common/src/main/resources/assets/plan/web/js/query.js index f013ca031..ffc2f97c5 100644 --- a/Plan/common/src/main/resources/assets/plan/web/js/query.js +++ b/Plan/common/src/main/resources/assets/plan/web/js/query.js @@ -303,19 +303,29 @@ function runQuery() { renderDataResultScreen(json.data.players.data.length, json.view ? json.view : {}); + // Set URL so that the query result can be shared window.history.replaceState({}, '', `${location.pathname}?timestamp=${json.timestamp}`); + // Player table $('.player-table').DataTable({ responsive: true, columns: json.data.players.columns, data: json.data.players.data, order: [[5, "desc"]] - }) + }); + + // Activity graphs + const activity_data = json.data.activity; + activityPie('activityPie', { + name: 'Players', colorByPoint: true, data: activity_data.activity_pie_series + }); + stackChart('activityStackGraph', activity_data.activity_labels, activity_data.activity_series, 'Players'); const activityIndexHeader = document.querySelector("#DataTables_Table_0 thead th:nth-of-type(2)"); const lastSeenHeader = document.querySelector("#DataTables_Table_0 thead th:nth-of-type(6)"); - activityIndexHeader.innerHTML += ` (${filterView.beforeDate})` + document.querySelector("#activity-date").innerHTML = json.view.beforeDate; + activityIndexHeader.innerHTML += ` (${json.view.beforeDate})` lastSeenHeader.innerHTML += ` (view)` }); } @@ -351,5 +361,28 @@ function renderDataResultScreen(resultCount, view) { +
+
+
+
+
+ Activity of matched players
+
+
+
+
+ +
+
+
+
+ Activity on
+
+
+
+
+
`; } \ No newline at end of file diff --git a/Plan/common/src/main/resources/assets/plan/web/query.html b/Plan/common/src/main/resources/assets/plan/web/query.html index c8f75528e..70640355d 100644 --- a/Plan/common/src/main/resources/assets/plan/web/query.html +++ b/Plan/common/src/main/resources/assets/plan/web/query.html @@ -316,8 +316,12 @@ + + + +