mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-27 19:47:49 +01:00
Added activity graphs to query results
This commit is contained in:
parent
c1d53c27d3
commit
a3c16b4b21
@ -16,8 +16,10 @@
|
|||||||
*/
|
*/
|
||||||
package com.djrapitops.plan.delivery.webserver.resolver.json;
|
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.formatting.Formatters;
|
||||||
import com.djrapitops.plan.delivery.rendering.json.PlayersTableJSONCreator;
|
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.MimeType;
|
||||||
import com.djrapitops.plan.delivery.web.resolver.Resolver;
|
import com.djrapitops.plan.delivery.web.resolver.Resolver;
|
||||||
import com.djrapitops.plan.delivery.web.resolver.Response;
|
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.settings.locale.Locale;
|
||||||
import com.djrapitops.plan.storage.database.DBSystem;
|
import com.djrapitops.plan.storage.database.DBSystem;
|
||||||
import com.djrapitops.plan.storage.database.Database;
|
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.Filter;
|
||||||
import com.djrapitops.plan.storage.database.queries.filter.FilterQuery;
|
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.filter.QueryFilters;
|
||||||
import com.djrapitops.plan.storage.database.queries.objects.playertable.QueryTablePlayersQuery;
|
import com.djrapitops.plan.storage.database.queries.objects.playertable.QueryTablePlayersQuery;
|
||||||
import com.djrapitops.plan.storage.json.JSONStorage;
|
import com.djrapitops.plan.storage.json.JSONStorage;
|
||||||
import com.djrapitops.plan.utilities.java.Maps;
|
import com.djrapitops.plan.utilities.java.Maps;
|
||||||
|
import com.djrapitops.plugin.api.TimeAmount;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@ -54,6 +58,7 @@ public class QueryJSONResolver implements Resolver {
|
|||||||
private final PlanConfig config;
|
private final PlanConfig config;
|
||||||
private final DBSystem dbSystem;
|
private final DBSystem dbSystem;
|
||||||
private final JSONStorage jsonStorage;
|
private final JSONStorage jsonStorage;
|
||||||
|
private final GraphJSONCreator graphJSONCreator;
|
||||||
private final Locale locale;
|
private final Locale locale;
|
||||||
private final Formatters formatters;
|
private final Formatters formatters;
|
||||||
|
|
||||||
@ -63,6 +68,7 @@ public class QueryJSONResolver implements Resolver {
|
|||||||
PlanConfig config,
|
PlanConfig config,
|
||||||
DBSystem dbSystem,
|
DBSystem dbSystem,
|
||||||
JSONStorage jsonStorage,
|
JSONStorage jsonStorage,
|
||||||
|
GraphJSONCreator graphJSONCreator,
|
||||||
Locale locale,
|
Locale locale,
|
||||||
Formatters formatters
|
Formatters formatters
|
||||||
) {
|
) {
|
||||||
@ -70,6 +76,7 @@ public class QueryJSONResolver implements Resolver {
|
|||||||
this.config = config;
|
this.config = config;
|
||||||
this.dbSystem = dbSystem;
|
this.dbSystem = dbSystem;
|
||||||
this.jsonStorage = jsonStorage;
|
this.jsonStorage = jsonStorage;
|
||||||
|
this.graphJSONCreator = graphJSONCreator;
|
||||||
this.locale = locale;
|
this.locale = locale;
|
||||||
this.formatters = formatters;
|
this.formatters = formatters;
|
||||||
}
|
}
|
||||||
@ -140,12 +147,34 @@ public class QueryJSONResolver implements Resolver {
|
|||||||
|
|
||||||
Database database = dbSystem.getDatabase();
|
Database database = dbSystem.getDatabase();
|
||||||
return Maps.builder(String.class, Object.class)
|
return Maps.builder(String.class, Object.class)
|
||||||
.put("players", new PlayersTableJSONCreator(
|
.put("players", getPlayersTableData(playerUUIDs, after, before))
|
||||||
database.query(new QueryTablePlayersQuery(playerUUIDs, after, before, config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD))),
|
.put("activity", getActivityGraphData(playerUUIDs, after, before))
|
||||||
Collections.emptyMap(),
|
|
||||||
config.get(DisplaySettings.OPEN_PLAYER_LINKS_IN_NEW_TAB),
|
|
||||||
formatters, locale
|
|
||||||
).toJSONMap())
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> getActivityGraphData(Set<UUID> 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<Map<String, Integer>> 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<String, Object> activityGraphJSON = graphJSONCreator.createActivityGraphJSON(activityData);
|
||||||
|
return activityGraphJSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> getPlayersTableData(Set<UUID> 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.queries.QueryStatement;
|
||||||
import com.djrapitops.plan.storage.database.sql.tables.SessionsTable;
|
import com.djrapitops.plan.storage.database.sql.tables.SessionsTable;
|
||||||
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
|
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
|
||||||
|
import org.apache.commons.text.TextStringBuilder;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
@ -162,6 +163,36 @@ public class NetworkActivityIndexQueries {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Query<Map<String, Integer>> fetchActivityIndexGroupingsOn(long date, long threshold, Collection<UUID> 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<Map<String, Integer>>(selectIndexes) {
|
||||||
|
@Override
|
||||||
|
public void prepare(PreparedStatement statement) throws SQLException {
|
||||||
|
setSelectActivityIndexSQLParameters(statement, 1, threshold, date);
|
||||||
|
statement.setLong(9, date);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Integer> processResults(ResultSet set) throws SQLException {
|
||||||
|
Map<String, Integer> 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<Integer> countNewPlayersTurnedRegular(long after, long before, Long threshold) {
|
public static Query<Integer> countNewPlayersTurnedRegular(long after, long before, Long threshold) {
|
||||||
String selectActivityIndex = selectActivityIndexSQL();
|
String selectActivityIndex = selectActivityIndexSQL();
|
||||||
|
|
||||||
|
@ -303,19 +303,29 @@ function runQuery() {
|
|||||||
|
|
||||||
renderDataResultScreen(json.data.players.data.length, json.view ? json.view : {});
|
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}`);
|
window.history.replaceState({}, '', `${location.pathname}?timestamp=${json.timestamp}`);
|
||||||
|
|
||||||
|
// Player table
|
||||||
$('.player-table').DataTable({
|
$('.player-table').DataTable({
|
||||||
responsive: true,
|
responsive: true,
|
||||||
columns: json.data.players.columns,
|
columns: json.data.players.columns,
|
||||||
data: json.data.players.data,
|
data: json.data.players.data,
|
||||||
order: [[5, "desc"]]
|
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 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)");
|
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)`
|
lastSeenHeader.innerHTML += ` (view)`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -351,5 +361,28 @@ function renderDataResultScreen(resultCount, view) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-8 col-lg-8 col-sm-12">
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
||||||
|
<h6 class="m-0 font-weight-bold col-black"><i
|
||||||
|
class="fas fa-fw fa-chart-line col-amber"></i>
|
||||||
|
Activity of matched players</h6>
|
||||||
|
</div>
|
||||||
|
<div class="chart-area" id="activityStackGraph"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-xl-4 col-lg-4 col-sm-12">
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
||||||
|
<h6 class="m-0 font-weight-bold col-black"><i
|
||||||
|
class="fa fa-fw fa-users col-amber"></i>
|
||||||
|
Activity on <span id="activity-date"></span></h6>
|
||||||
|
</div>
|
||||||
|
<div class="chart-area" id="activityPie"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
@ -316,8 +316,12 @@
|
|||||||
<!-- Page level plugins -->
|
<!-- Page level plugins -->
|
||||||
<script src="vendor/datatables/jquery.dataTables.min.js"></script>
|
<script src="vendor/datatables/jquery.dataTables.min.js"></script>
|
||||||
<script src="vendor/datatables/dataTables.bootstrap4.min.js"></script>
|
<script src="vendor/datatables/dataTables.bootstrap4.min.js"></script>
|
||||||
|
<script src="./vendor/highcharts/highstock.js"></script>
|
||||||
|
<script src="./vendor/highcharts/map.js"></script>
|
||||||
|
<script src="./vendor/highcharts/world.js"></script>
|
||||||
|
|
||||||
<!-- Page level custom scripts -->
|
<!-- Page level custom scripts -->
|
||||||
|
<script src="./js/graphs.js"></script>
|
||||||
<script src='./js/query.js'></script>
|
<script src='./js/query.js'></script>
|
||||||
|
|
||||||
<script id="mainScript">
|
<script id="mainScript">
|
||||||
|
Loading…
Reference in New Issue
Block a user