Playtime & Sessions per day queries

This commit is contained in:
Rsl1122 2019-08-14 08:35:22 +03:00
parent 7a2636f5b2
commit 63c2db3cb2
11 changed files with 195 additions and 62 deletions

View File

@ -141,7 +141,7 @@ public class NetworkContainer extends DynamicDataContainer {
config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD),
config.get(TimeSettings.ACTIVE_LOGIN_THRESHOLD),
formatters.timeAmount(), formatters.decimals(), formatters.percentage(),
config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT")
config.getTimeZone()
));
putCachingSupplier(NetworkKeys.HEALTH_INDEX, () -> getUnsafe(healthInformation).getServerHealth());
putCachingSupplier(NetworkKeys.HEALTH_NOTES, () -> getUnsafe(healthInformation).toHtml());

View File

@ -19,6 +19,7 @@ package com.djrapitops.plan.db;
import com.djrapitops.plan.api.exceptions.database.DBInitException;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.transactions.Transaction;
import com.djrapitops.plan.db.sql.parsing.Sql;
import java.util.concurrent.Future;
@ -67,6 +68,10 @@ public interface Database {
*/
DBType getType();
default Sql getSql() {
return getType().getSql();
}
State getState();
enum State {

View File

@ -79,7 +79,7 @@ public class PlayerCountQueries {
*/
public static Query<NavigableMap<Long, Integer>> uniquePlayerCounts(long after, long before, long timeZoneOffset, UUID serverUUID) {
return database -> {
Sql sql = database.getType().getSql();
Sql sql = database.getSql();
String selectUniquePlayersPerDay = SELECT +
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) +
"*1000 as date," +
@ -113,7 +113,7 @@ public class PlayerCountQueries {
public static Query<Integer> averageUniquePlayerCount(long after, long before, long timeZoneOffset, UUID serverUUID) {
return database -> {
Sql sql = database.getType().getSql();
Sql sql = database.getSql();
String selectUniquePlayersPerDay = SELECT +
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) +
"*1000 as date," +
@ -163,7 +163,7 @@ public class PlayerCountQueries {
*/
public static Query<NavigableMap<Long, Integer>> newPlayerCounts(long after, long before, long timeZoneOffset, UUID serverUUID) {
return database -> {
Sql sql = database.getType().getSql();
Sql sql = database.getSql();
String selectNewPlayersQuery = SELECT +
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + UserInfoTable.REGISTERED + "+?)/1000"))) +
"*1000 as date," +
@ -197,7 +197,7 @@ public class PlayerCountQueries {
public static Query<Integer> averageNewPlayerCount(long after, long before, long timeZoneOffset, UUID serverUUID) {
return database -> {
Sql sql = database.getType().getSql();
Sql sql = database.getSql();
String selectNewPlayersQuery = SELECT +
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + UserInfoTable.REGISTERED + "+?)/1000"))) +
"*1000 as date," +

View File

@ -26,6 +26,7 @@ import com.djrapitops.plan.data.time.WorldTimes;
import com.djrapitops.plan.db.access.Query;
import com.djrapitops.plan.db.access.QueryAllStatement;
import com.djrapitops.plan.db.access.QueryStatement;
import com.djrapitops.plan.db.sql.parsing.Sql;
import com.djrapitops.plan.db.sql.tables.*;
import com.djrapitops.plan.utilities.comparators.DateHolderRecentComparator;
@ -370,6 +371,40 @@ public class SessionQueries {
};
}
public static Query<NavigableMap<Long, Integer>> sessionCountPerDay(long after, long before, long timeZoneOffset, UUID serverUUID) {
return database -> {
Sql sql = database.getSql();
String selectSessionsPerDay = SELECT +
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) +
"*1000 as date," +
"COUNT(1) as session_count" +
FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.SESSION_END + "<=?" +
AND + SessionsTable.SESSION_START + ">=?" +
AND + SessionsTable.SERVER_UUID + "=?" +
GROUP_BY + "date";
return database.query(new QueryStatement<NavigableMap<Long, Integer>>(selectSessionsPerDay, 100) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, timeZoneOffset);
statement.setLong(2, before);
statement.setLong(3, after);
statement.setString(4, serverUUID.toString());
}
@Override
public NavigableMap<Long, Integer> processResults(ResultSet set) throws SQLException {
NavigableMap<Long, Integer> uniquePerDay = new TreeMap<>();
while (set.next()) {
uniquePerDay.put(set.getLong("date"), set.getInt("session_count"));
}
return uniquePerDay;
}
});
};
}
public static Query<Long> playtime(long after, long before, UUID serverUUID) {
String sql = SELECT + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" +
FROM + SessionsTable.TABLE_NAME +
@ -391,6 +426,71 @@ public class SessionQueries {
};
}
public static Query<NavigableMap<Long, Long>> playtimePerDay(long after, long before, long timeZoneOffset, UUID serverUUID) {
return database -> {
Sql sql = database.getSql();
String selectPlaytimePerDay = SELECT +
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) +
"*1000 as date," +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" +
FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.SESSION_END + "<=?" +
AND + SessionsTable.SESSION_START + ">=?" +
AND + SessionsTable.SERVER_UUID + "=?" +
GROUP_BY + "date";
return database.query(new QueryStatement<NavigableMap<Long, Long>>(selectPlaytimePerDay, 100) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, timeZoneOffset);
statement.setLong(2, before);
statement.setLong(3, after);
statement.setString(4, serverUUID.toString());
}
@Override
public NavigableMap<Long, Long> processResults(ResultSet set) throws SQLException {
NavigableMap<Long, Long> uniquePerDay = new TreeMap<>();
while (set.next()) {
uniquePerDay.put(set.getLong("date"), set.getLong("playtime"));
}
return uniquePerDay;
}
});
};
}
public static Query<Long> averagePlaytimePerDay(long after, long before, long timeZoneOffset, UUID serverUUID) {
return database -> {
Sql sql = database.getSql();
String selectPlaytimePerDay = SELECT +
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) +
"*1000 as date," +
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" +
FROM + SessionsTable.TABLE_NAME +
WHERE + SessionsTable.SESSION_END + "<=?" +
AND + SessionsTable.SESSION_START + ">=?" +
AND + SessionsTable.SERVER_UUID + "=?" +
GROUP_BY + "date";
String selectAverage = SELECT + "AVG(playtime) as average" + FROM + '(' + selectPlaytimePerDay + ") q1";
return database.query(new QueryStatement<Long>(selectAverage, 100) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setLong(1, timeZoneOffset);
statement.setLong(2, before);
statement.setLong(3, after);
statement.setString(4, serverUUID.toString());
}
@Override
public Long processResults(ResultSet set) throws SQLException {
return set.next() ? set.getLong("average") : 0;
}
});
};
}
public static Query<Long> afkTime(long after, long before, UUID serverUUID) {
String sql = SELECT + "SUM(" + SessionsTable.AFK_TIME + ") as afk_time" +
FROM + SessionsTable.TABLE_NAME +

View File

@ -67,7 +67,7 @@ public class GraphJSONParser {
this.config = config;
this.dbSystem = dbSystem;
this.graphs = graphs;
this.timeZone = config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT");
this.timeZone = config.getTimeZone();
}
public String performanceGraphJSON(UUID serverUUID) {
@ -121,10 +121,18 @@ public class GraphJSONParser {
NavigableMap<Long, Integer> newPerDay = db.query(
PlayerCountQueries.newPlayerCounts(twoYearsAgo, now, timeZone.getOffset(now), serverUUID)
);
NavigableMap<Long, Long> playtimePerDay = db.query(
SessionQueries.playtimePerDay(twoYearsAgo, now, timeZone.getOffset(now), serverUUID)
);
NavigableMap<Long, Integer> sessionsPerDay = db.query(
SessionQueries.sessionCountPerDay(twoYearsAgo, now, timeZone.getOffset(now), serverUUID)
);
return "{\"data\":" +
graphs.calendar().serverCalendar(
uniquePerDay,
newPerDay
newPerDay,
playtimePerDay,
sessionsPerDay
).toCalendarSeries() +
",\"firstDay\":" + 1 + '}';
}

View File

@ -29,7 +29,6 @@ import com.djrapitops.plan.db.access.queries.objects.UserInfoQueries;
import com.djrapitops.plan.system.database.DBSystem;
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.Formatter;
import com.djrapitops.plan.utilities.formatting.Formatters;
@ -66,7 +65,7 @@ public class OnlineActivityOverviewJSONParser implements TabJSONParser<Map<Strin
timeAmountFormatter = formatters.timeAmount();
decimalFormatter = formatters.decimals();
percentageFormatter = formatters.percentage();
this.timeZone = config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT");
this.timeZone = config.getTimeZone();
}
public Map<String, Object> createJSONAsMap(UUID serverUUID) {
@ -146,16 +145,15 @@ public class OnlineActivityOverviewJSONParser implements TabJSONParser<Map<Strin
numbers.put("playtime_7d", timeAmountFormatter.apply(playtimeWeek));
numbers.put("playtime_24h", timeAmountFormatter.apply(playtimeDay));
// TODO
numbers.put("playtime_30d_avg", timeAmountFormatter.apply(-1L));
numbers.put("playtime_30d_avg", timeAmountFormatter.apply(db.query(SessionQueries.averagePlaytimePerDay(monthAgo, now, timeZoneOffset, serverUUID))));
numbers.put("playtime_30d_avg_trend", new Trend(
-1,
-1,
db.query(SessionQueries.averagePlaytimePerDay(monthAgo, halfMonthAgo, timeZoneOffset, serverUUID)),
db.query(SessionQueries.averagePlaytimePerDay(halfMonthAgo, now, timeZoneOffset, serverUUID)),
false,
timeAmountFormatter
));
numbers.put("playtime_7d_avg", timeAmountFormatter.apply(-1L));
numbers.put("playtime_24h_avg", timeAmountFormatter.apply(-1L));
numbers.put("playtime_7d_avg", timeAmountFormatter.apply(db.query(SessionQueries.averagePlaytimePerDay(weekAgo, now, timeZoneOffset, serverUUID))));
numbers.put("playtime_24h_avg", timeAmountFormatter.apply(db.query(SessionQueries.playtime(dayAgo, now, serverUUID))));
Long sessionsMonth = db.query(SessionQueries.sessionCount(monthAgo, now, serverUUID));
Long sessionsWeek = db.query(SessionQueries.sessionCount(weekAgo, now, serverUUID));

View File

@ -75,7 +75,7 @@ public class ServerOverviewJSONParser implements TabJSONParser<Map<String, Objec
timeAmount = formatters.timeAmount();
decimals = formatters.decimals();
percentage = formatters.percentage();
this.timeZone = config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT");
this.timeZone = config.getTimeZone();
}
public Map<String, Object> createJSONAsMap(UUID serverUUID) {

View File

@ -121,6 +121,10 @@ public class PlanConfig extends Config {
set(setting.getPath(), value);
}
public TimeZone getTimeZone() {
return get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT");
}
public PluginsConfigSection getPluginsConfigSection() {
return pluginsConfigSection;
}

View File

@ -18,14 +18,13 @@ package com.djrapitops.plan.utilities.html.graphs.calendar;
import com.djrapitops.plan.data.store.containers.PlayerContainer;
import com.djrapitops.plan.system.settings.config.PlanConfig;
import com.djrapitops.plan.system.settings.paths.TimeSettings;
import com.djrapitops.plan.system.settings.theme.Theme;
import com.djrapitops.plan.utilities.formatting.Formatters;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.NavigableMap;
import java.util.SortedMap;
import java.util.TimeZone;
/**
* Factory class for different objects representing HTML calendars.
@ -53,18 +52,20 @@ public class CalendarFactory {
return new PlayerCalendar(
player,
formatters.timeAmount(), formatters.yearLong(), formatters.iso8601NoClockLong(), theme,
config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT")
config.getTimeZone()
);
}
public ServerCalendar serverCalendar(
SortedMap<Long, Integer> uniquePerDay,
SortedMap<Long, Integer> newPerDay
SortedMap<Long, Integer> newPerDay,
SortedMap<Long, Long> playtimePerDay,
NavigableMap<Long, Integer> sessionsPerDay
) {
return new ServerCalendar(
uniquePerDay, newPerDay,
uniquePerDay, newPerDay, playtimePerDay, sessionsPerDay,
formatters.iso8601NoClockLong(), formatters.timeAmount(), theme,
config.get(TimeSettings.USE_SERVER_TIME) ? TimeZone.getDefault() : TimeZone.getTimeZone("GMT")
config.getTimeZone()
);
}
}

View File

@ -20,7 +20,10 @@ import com.djrapitops.plan.system.settings.theme.Theme;
import com.djrapitops.plan.system.settings.theme.ThemeVal;
import com.djrapitops.plan.utilities.formatting.Formatter;
import java.util.*;
import java.util.Map;
import java.util.NavigableMap;
import java.util.SortedMap;
import java.util.TimeZone;
/**
* Utility for creating FullCalendar calendar event array on Player page.
@ -40,7 +43,10 @@ public class ServerCalendar {
private final TimeZone timeZone;
ServerCalendar(
SortedMap<Long, Integer> uniquePerDay, SortedMap<Long, Integer> newPerDay,
SortedMap<Long, Integer> uniquePerDay,
SortedMap<Long, Integer> newPerDay,
SortedMap<Long, Long> playtimePerDay,
NavigableMap<Long, Integer> sessionsPerDay,
Formatter<Long> iso8601Formatter,
Formatter<Long> timeAmountFormatter,
Theme theme,
@ -50,8 +56,8 @@ public class ServerCalendar {
this.newPerDay = newPerDay;
this.iso8601Formatter = iso8601Formatter;
this.timeAmountFormatter = timeAmountFormatter;
sessionsPerDay = new TreeMap<>(); // TODO
playtimePerDay = new TreeMap<>(); // TODO
this.sessionsPerDay = sessionsPerDay;
this.playtimePerDay = playtimePerDay;
this.theme = theme;
this.timeZone = timeZone;
}
@ -60,57 +66,45 @@ public class ServerCalendar {
StringBuilder series = new StringBuilder("[");
series.append("{\"title\": \"badcode\",\"start\":0}");
appendSessionRelatedData(series);
appendRegistered(series);
appendTimeZoneOffsetData(series);
return series.append("]").toString();
}
private void appendRegistered(StringBuilder series) {
Map<String, Integer> registeredByDay = getRegisteredByDay();
private void appendTimeZoneOffsetData(StringBuilder series) {
// Has a timezone offset
appendUniquePlayers(series);
appendNewPlayers(series);
appendSessionCounts(series);
appendPlaytime(series);
}
for (Map.Entry<String, Integer> entry : registeredByDay.entrySet()) {
Integer newPlayers = entry.getValue();
private void appendNewPlayers(StringBuilder series) {
for (Map.Entry<Long, Integer> entry : newPerDay.entrySet()) {
int newPlayers = entry.getValue();
if (newPlayers <= 0) {
continue;
}
String day = entry.getKey();
Long key = entry.getKey();
String day = iso8601Formatter.apply(key - timeZone.getOffset(key));// Remove the timezone offset since Calendar uses UTC
series.append(",{\"title\": \"New: ").append(newPlayers)
.append("\",\"start\":\"").append(day)
.append("\",\"color\": \"").append(theme.getValue(ThemeVal.LIGHT_GREEN)).append('"')
.append("}");
}
}
private void appendSessionRelatedData(StringBuilder series) {
// Has a timezone offset
private void appendUniquePlayers(StringBuilder series) {
for (Map.Entry<Long, Integer> entry : uniquePerDay.entrySet()) {
if (entry.getValue() <= 0) {
long uniquePlayers = entry.getValue();
if (uniquePlayers <= 0) {
continue;
}
Long key = entry.getKey();
String day = iso8601Formatter.apply(key - timeZone.getOffset(entry.getKey()));// Remove the timezone offset since Calendar uses UTC
Integer sessionCount = sessionsPerDay.getOrDefault(key, 0);
Long playtime = playtimePerDay.getOrDefault(key, 0L);
long uniquePlayers = entry.getValue();
if (playtime > 0) {
series.append(",{\"title\": \"Playtime: ").append(timeAmountFormatter.apply(playtime))
.append("\",\"start\":\"").append(day)
.append("\",\"color\": \"").append(theme.getValue(ThemeVal.GREEN)).append('"')
.append("}");
}
if (sessionCount > 0) {
series.append(",{\"title\": \"Sessions: ").append(sessionCount)
.append("\",\"start\":\"").append(day)
.append("\",\"color\": \"").append(theme.getValue(ThemeVal.TEAL)).append('"')
.append("}");
}
String day = iso8601Formatter.apply(key - timeZone.getOffset(key));// Remove the timezone offset since Calendar uses UTC
series.append(",{\"title\": \"Unique: ").append(uniquePlayers)
.append("\",\"start\":\"").append(day)
@ -119,12 +113,35 @@ public class ServerCalendar {
}
}
private Map<String, Integer> getRegisteredByDay() {
Map<String, Integer> registeredByDay = new HashMap<>();
for (Map.Entry<Long, Integer> entry : newPerDay.entrySet()) {
String day = iso8601Formatter.apply(entry.getKey());
registeredByDay.put(day, entry.getValue());
private void appendPlaytime(StringBuilder series) {
for (Map.Entry<Long, Long> entry : playtimePerDay.entrySet()) {
long playtime = entry.getValue();
if (playtime <= 0) {
continue;
}
Long key = entry.getKey();
String day = iso8601Formatter.apply(key - timeZone.getOffset(key));// Remove the timezone offset since Calendar uses UTC
series.append(",{\"title\": \"Playtime: ").append(timeAmountFormatter.apply(playtime))
.append("\",\"start\":\"").append(day)
.append("\",\"color\": \"").append(theme.getValue(ThemeVal.GREEN)).append('"')
.append("}");
}
}
private void appendSessionCounts(StringBuilder series) {
for (Map.Entry<Long, Integer> entry : sessionsPerDay.entrySet()) {
int sessionCount = entry.getValue();
if (sessionCount <= 0) {
continue;
}
Long key = entry.getKey();
String day = iso8601Formatter.apply(key - timeZone.getOffset(key));// Remove the timezone offset since Calendar uses UTC
series.append(",{\"title\": \"Sessions: ").append(sessionCount)
.append("\",\"start\":\"").append(day)
.append("\",\"color\": \"").append(theme.getValue(ThemeVal.TEAL)).append('"')
.append("}");
}
return registeredByDay;
}
}

View File

@ -454,7 +454,7 @@
</tr>
<tr>
<td><i class="col-green far fa-fw fa-clock"></i> Average Playtime /
Player
Day
</td>
<td id="data_playtime_30d_avg"></td>
<td id="data_playtime_7d_avg"></td>