Added session aggregates to query results

This commit is contained in:
Risto Lahtela 2021-01-19 11:43:30 +02:00
parent 8ee28e07dd
commit c0e253d56d
4 changed files with 115 additions and 12 deletions

View File

@ -17,6 +17,7 @@
package com.djrapitops.plan.delivery.webserver.resolver.json;
import com.djrapitops.plan.delivery.domain.DateMap;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.json.PlayersTableJSONCreator;
import com.djrapitops.plan.delivery.rendering.json.graphs.GraphJSONCreator;
@ -37,6 +38,7 @@ 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.GeoInfoQueries;
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
import com.djrapitops.plan.storage.database.queries.objects.playertable.QueryTablePlayersQuery;
import com.djrapitops.plan.storage.json.JSONStorage;
import com.djrapitops.plan.utilities.java.Maps;
@ -146,15 +148,27 @@ public class QueryJSONResolver implements Resolver {
long after = dateFormat.parse(viewJSON.afterDate + " " + viewJSON.afterTime).getTime();
long before = dateFormat.parse(viewJSON.beforeDate + " " + viewJSON.beforeTime).getTime();
Database database = dbSystem.getDatabase();
return Maps.builder(String.class, Object.class)
.put("players", getPlayersTableData(playerUUIDs, after, before))
.put("activity", getActivityGraphData(playerUUIDs, after, before))
.put("geolocation", getGeolocationData(playerUUIDs))
.put("sessions", getSessionSummaryData(playerUUIDs, after, before))
.build();
}
private Map<String, String> getSessionSummaryData(Set<UUID> playerUUIDs, long after, long before) {
Database database = dbSystem.getDatabase();
Map<String, Long> summary = database.query(SessionQueries.summaryOfPlayers(playerUUIDs, after, before));
Map<String, String> formattedSummary = new HashMap<>();
Formatter<Long> timeAmount = formatters.timeAmount();
for (Map.Entry<String, Long> entry : summary.entrySet()) {
formattedSummary.put(entry.getKey(), timeAmount.apply(entry.getValue()));
}
formattedSummary.put("total_sessions", Long.toString(summary.get("total_sessions")));
formattedSummary.put("average_sessions", Long.toString(summary.get("average_sessions")));
return formattedSummary;
}
private Map<String, Object> getGeolocationData(Set<UUID> playerUUIDs) {
Database database = dbSystem.getDatabase();
return graphJSONCreator.createGeolocationJSON(

View File

@ -31,6 +31,7 @@ import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.*;
import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator;
import com.djrapitops.plan.utilities.java.Maps;
import org.apache.commons.text.TextStringBuilder;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@ -836,4 +837,55 @@ public class SessionQueries {
}
};
}
public static Query<Map<String, Long>> summaryOfPlayers(Set<UUID> playerUUIDs, long after, long before) {
String selectAggregates = SELECT +
SessionsTable.USER_UUID + ',' +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime," +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + '-' + SessionsTable.AFK_TIME + ") as active_playtime," +
"COUNT(1) as session_count" +
FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.SESSION_START + ">?" +
AND + SessionsTable.SESSION_END + "<?" +
AND + SessionsTable.USER_UUID + " IN ('" +
new TextStringBuilder().appendWithSeparators(playerUUIDs, "','").build() + "')" +
GROUP_BY + SessionsTable.USER_UUID;
String sql = SELECT +
"SUM(playtime) as total_playtime," +
"AVG(playtime) as average_playtime," +
"SUM(active_playtime) as total_active_playtime," +
"AVG(active_playtime) as average_active_playtime," +
"SUM(playtime-active_playtime) as total_afk_playtime," +
"AVG(playtime-active_playtime) as average_afk_playtime," +
"AVG(playtime) as average_playtime," +
"SUM(session_count) as total_sessions," +
"AVG(session_count) as average_sessions" +
FROM + "(" + selectAggregates + ") s";
return new QueryStatement<Map<String, Long>>(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, after);
statement.setLong(2, before);
}
@Override
public Map<String, Long> processResults(ResultSet set) throws SQLException {
long sessionCount = set.getLong("total_sessions");
long playtime = set.getLong("total_playtime");
return Maps.builder(String.class, Long.class)
.put("total_playtime", playtime)
.put("average_playtime", set.getLong("average_playtime"))
.put("total_afk_playtime", set.getLong("total_afk_playtime"))
.put("average_afk_playtime", set.getLong("average_afk_playtime"))
.put("total_active_playtime", set.getLong("total_active_playtime"))
.put("average_active_playtime", set.getLong("average_active_playtime"))
.put("total_sessions", sessionCount)
.put("average_sessions", set.getLong("average_sessions"))
.put("average_session_length", sessionCount != 0 ? playtime / sessionCount : -1L)
.build();
}
};
}
}

View File

@ -286,15 +286,6 @@ function runQuery() {
<p class="loader-text">Loading..</p>
</div>`;
const navButton = document.querySelector('.navbar-nav .nav-item');
const element = document.createElement('li');
element.classList.add("nav-item", "nav-button")
element.innerHTML = `<a class="nav-link" href="./query">
<i class="fas fa-fw fa-undo"></i>
<span>Make another query</span>
</a>`
navButton.insertAdjacentElement('beforebegin', element);
jsonRequest(getQueryAddress(), function (json, error) {
if (!json.data) {
window.history.replaceState({}, '', `${location.pathname}?error=${error ? error : 'Query result expired'}`);
@ -348,7 +339,17 @@ function runQuery() {
worldMap('worldMap', geolocation_data.colors.low, geolocation_data.colors.high, geolocationSeries);
horizontalBarChart('countryBarChart', geolocationBarCategories, [geolocationBarSeries], 'Players');
const session_data = json.data.sessions;
document.querySelector("#data_total_playtime").innerHTML = session_data.total_playtime;
document.querySelector("#data_average_playtime").innerHTML = session_data.average_playtime;
document.querySelector("#data_total_afk_playtime").innerHTML = session_data.total_afk_playtime;
document.querySelector("#data_average_afk_playtime").innerHTML = session_data.average_afk_playtime;
document.querySelector("#data_total_active_playtime").innerHTML = session_data.total_active_playtime;
document.querySelector("#data_average_active_playtime").innerHTML = session_data.average_active_playtime;
document.querySelector("#data_total_sessions").innerHTML = session_data.total_sessions;
document.querySelector("#data_average_sessions").innerHTML = session_data.average_sessions;
document.querySelector("#data_average_session_length").innerHTML = session_data.average_session_length;
});
}
@ -408,7 +409,37 @@ function renderDataResultScreen(resultCount, view) {
</div>
<div class="row">
<div class="col-xl-12 col-lg-12 col-sm-12">
<div class="col-xl-3 col-lg-3 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="col-teal far fa-calendar"></i> Sessions within view</h6>
</div>
<div class="card-body" id="data_players">
<p><i class="col-teal far fa-fw fa-calendar-check"></i> Sessions<span
class="float-right"><b id="data_total_sessions"></b></span></p>
<p><i class="col-teal far fa-fw fa-calendar-check"></i> Average Sessions / Player<span
class="float-right"><b id="data_average_sessions"></b></span></p>
<p><i class="col-teal far fa-fw fa-clock"></i> Average Session Length<span
class="float-right" id="data_average_session_length"></span></p>
<hr>
<p><i class="col-green far fa-fw fa-clock"></i> Playtime<span
class="float-right" id="data_total_playtime"></span></p>
<p><i class="col-green far fa-fw fa-clock"></i> Active Playtime<span
class="float-right" id="data_total_active_playtime"></span></p>
<p><i class="col-grey far fa-fw fa-clock"></i> AFK Playtime<span
class="float-right" id="data_total_afk_playtime"></span></p>
<hr>
<p><i class="col-green far fa-fw fa-clock"></i> Average Playtime / Player<span
class="float-right" id="data_average_playtime"></span></p>
<p><i class="col-green far fa-fw fa-clock"></i> Average Active Playtime / Player<span
class="float-right" id="data_average_active_playtime"></span></p>
<p><i class="col-grey far fa-fw fa-clock"></i> Average AFK Playtime / Player<span
class="float-right" id="data_average_afk_playtime"></span></p>
</div>
</div>
</div>
<div class="col-xl-9 col-lg-9 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

View File

@ -39,6 +39,12 @@
<hr class="sidebar-divider my-0">
<li class="nav-item nav-button active">
<a class="nav-link" href="./query">
<i class="fas fa-fw fa-undo"></i>
<span>Make another query</span>
</a>
</li>
<li class="nav-item nav-button">
<a class="nav-link" href="/">
<i class="far fa-fw fa-hand-point-left"></i>
<span>to main page</span></a>