248 lines
15 KiB
Java
248 lines
15 KiB
Java
/*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
package com.djrapitops.plan.delivery.rendering.json;
|
|
|
|
import com.djrapitops.plan.delivery.domain.keys.SessionKeys;
|
|
import com.djrapitops.plan.delivery.domain.mutators.PlayersOnlineResolver;
|
|
import com.djrapitops.plan.delivery.domain.mutators.RetentionData;
|
|
import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator;
|
|
import com.djrapitops.plan.delivery.domain.mutators.TPSMutator;
|
|
import com.djrapitops.plan.delivery.formatting.Formatter;
|
|
import com.djrapitops.plan.delivery.formatting.Formatters;
|
|
import com.djrapitops.plan.gathering.domain.TPS;
|
|
import com.djrapitops.plan.settings.config.PlanConfig;
|
|
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
|
|
import com.djrapitops.plan.settings.config.paths.TimeSettings;
|
|
import com.djrapitops.plan.storage.database.DBSystem;
|
|
import com.djrapitops.plan.storage.database.Database;
|
|
import com.djrapitops.plan.storage.database.queries.analysis.ActivityIndexQueries;
|
|
import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries;
|
|
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
|
|
import com.djrapitops.plan.storage.database.queries.objects.TPSQueries;
|
|
import com.djrapitops.plan.storage.database.queries.objects.UserInfoQueries;
|
|
|
|
import javax.inject.Inject;
|
|
import javax.inject.Singleton;
|
|
import java.util.*;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
/**
|
|
* Parses JSON payload for /server-page Online Activity Overview tab.
|
|
*
|
|
* @author Rsl1122
|
|
*/
|
|
@Singleton
|
|
public class OnlineActivityOverviewJSONParser implements ServerTabJSONParser<Map<String, Object>> {
|
|
|
|
private final PlanConfig config;
|
|
private final DBSystem dbSystem;
|
|
|
|
private final Formatter<Long> timeAmountFormatter;
|
|
private final Formatter<Double> decimalFormatter;
|
|
private final Formatter<Double> percentageFormatter;
|
|
private final TimeZone timeZone;
|
|
|
|
@Inject
|
|
public OnlineActivityOverviewJSONParser(
|
|
PlanConfig config,
|
|
DBSystem dbSystem,
|
|
Formatters formatters
|
|
) {
|
|
this.config = config;
|
|
this.dbSystem = dbSystem;
|
|
|
|
timeAmountFormatter = formatters.timeAmount();
|
|
decimalFormatter = formatters.decimals();
|
|
percentageFormatter = formatters.percentage();
|
|
this.timeZone = config.getTimeZone();
|
|
}
|
|
|
|
public Map<String, Object> createJSONAsMap(UUID serverUUID) {
|
|
Map<String, Object> serverOverview = new HashMap<>();
|
|
serverOverview.put("numbers", createNumbersMap(serverUUID));
|
|
serverOverview.put("insights", createInsightsMap(serverUUID));
|
|
return serverOverview;
|
|
}
|
|
|
|
private Map<String, Object> createNumbersMap(UUID serverUUID) {
|
|
Database db = dbSystem.getDatabase();
|
|
long now = System.currentTimeMillis();
|
|
long dayAgo = now - TimeUnit.DAYS.toMillis(1L);
|
|
long weekAgo = now - TimeUnit.DAYS.toMillis(7L);
|
|
long halfMonthAgo = now - TimeUnit.DAYS.toMillis(15L);
|
|
long monthAgo = now - TimeUnit.DAYS.toMillis(30L);
|
|
int timeZoneOffset = timeZone.getOffset(now);
|
|
Long playThreshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
|
|
|
|
Map<String, Object> numbers = new HashMap<>();
|
|
|
|
numbers.put("unique_players_30d", db.query(PlayerCountQueries.uniquePlayerCount(monthAgo, now, serverUUID)));
|
|
numbers.put("unique_players_30d_trend", new Trend(
|
|
db.query(PlayerCountQueries.uniquePlayerCount(monthAgo, halfMonthAgo, serverUUID)),
|
|
db.query(PlayerCountQueries.uniquePlayerCount(halfMonthAgo, now, serverUUID)),
|
|
false
|
|
));
|
|
numbers.put("unique_players_7d", db.query(PlayerCountQueries.uniquePlayerCount(weekAgo, now, serverUUID)));
|
|
numbers.put("unique_players_24h", db.query(PlayerCountQueries.uniquePlayerCount(dayAgo, now, serverUUID)));
|
|
|
|
numbers.put("unique_players_30d_avg", db.query(PlayerCountQueries.averageUniquePlayerCount(monthAgo, now, timeZoneOffset, serverUUID)));
|
|
numbers.put("unique_players_30d_avg_trend", new Trend(
|
|
db.query(PlayerCountQueries.averageUniquePlayerCount(monthAgo, halfMonthAgo, timeZoneOffset, serverUUID)),
|
|
db.query(PlayerCountQueries.averageUniquePlayerCount(halfMonthAgo, now, timeZoneOffset, serverUUID)),
|
|
false
|
|
));
|
|
numbers.put("unique_players_7d_avg", db.query(PlayerCountQueries.averageUniquePlayerCount(weekAgo, now, timeZoneOffset, serverUUID)));
|
|
numbers.put("unique_players_24h_avg", db.query(PlayerCountQueries.averageUniquePlayerCount(dayAgo, now, timeZoneOffset, serverUUID)));
|
|
|
|
Integer new30d = db.query(PlayerCountQueries.newPlayerCount(monthAgo, now, serverUUID));
|
|
Integer new7d = db.query(PlayerCountQueries.newPlayerCount(weekAgo, now, serverUUID));
|
|
Integer new1d = db.query(PlayerCountQueries.newPlayerCount(dayAgo, now, serverUUID));
|
|
numbers.put("new_players_30d", new30d);
|
|
numbers.put("new_players_30d_trend", new Trend(
|
|
db.query(PlayerCountQueries.newPlayerCount(monthAgo, halfMonthAgo, serverUUID)),
|
|
db.query(PlayerCountQueries.newPlayerCount(halfMonthAgo, now, serverUUID)),
|
|
false
|
|
));
|
|
numbers.put("new_players_7d", new7d);
|
|
numbers.put("new_players_24h", new1d);
|
|
|
|
numbers.put("new_players_30d_avg", db.query(PlayerCountQueries.averageNewPlayerCount(monthAgo, now, timeZoneOffset, serverUUID)));
|
|
numbers.put("new_players_30d_avg_trend", new Trend(
|
|
db.query(PlayerCountQueries.averageNewPlayerCount(monthAgo, halfMonthAgo, timeZoneOffset, serverUUID)),
|
|
db.query(PlayerCountQueries.averageNewPlayerCount(halfMonthAgo, now, timeZoneOffset, serverUUID)),
|
|
false
|
|
));
|
|
numbers.put("new_players_7d_avg", db.query(PlayerCountQueries.averageNewPlayerCount(weekAgo, now, timeZoneOffset, serverUUID)));
|
|
numbers.put("new_players_24h_avg", db.query(PlayerCountQueries.averageNewPlayerCount(dayAgo, now, timeZoneOffset, serverUUID)));
|
|
|
|
int retained30d = db.query(PlayerCountQueries.retainedPlayerCount(monthAgo, now, serverUUID));
|
|
int retained7d = db.query(PlayerCountQueries.retainedPlayerCount(weekAgo, now, serverUUID));
|
|
double retentionPerc30d = new30d != 0 ? (double) retained30d / new30d : -1;
|
|
double retentionPerc7d = new7d != 0 ? (double) retained7d / new7d : -1;
|
|
numbers.put("new_players_retention_30d", retained30d);
|
|
numbers.put("new_players_retention_30d_perc", percentageFormatter.apply(retentionPerc30d));
|
|
numbers.put("new_players_retention_7d", retained7d);
|
|
numbers.put("new_players_retention_7d_perc", percentageFormatter.apply(retentionPerc7d));
|
|
|
|
int prediction1d = RetentionData.countRetentionPrediction(
|
|
db.query(ActivityIndexQueries.activityIndexForNewPlayers(dayAgo, now, serverUUID, playThreshold)),
|
|
db.query(ActivityIndexQueries.averageActivityIndexForRetainedPlayers(monthAgo, now, serverUUID, playThreshold)),
|
|
db.query(ActivityIndexQueries.averageActivityIndexForNonRetainedPlayers(monthAgo, now, serverUUID, playThreshold))
|
|
);
|
|
double retentionPerc1d = new1d != 0 ? (double) prediction1d / new1d : -1;
|
|
numbers.put("new_players_retention_24h", prediction1d);
|
|
numbers.put("new_players_retention_24h_perc", percentageFormatter.apply(retentionPerc1d));
|
|
|
|
Long playtimeMonth = db.query(SessionQueries.playtime(monthAgo, now, serverUUID));
|
|
Long playtimeWeek = db.query(SessionQueries.playtime(weekAgo, now, serverUUID));
|
|
Long playtimeDay = db.query(SessionQueries.playtime(dayAgo, now, serverUUID));
|
|
Long playtimeBefore = db.query(SessionQueries.playtime(monthAgo, halfMonthAgo, serverUUID));
|
|
Long playtimeAfter = db.query(SessionQueries.playtime(halfMonthAgo, now, serverUUID));
|
|
numbers.put("playtime_30d", timeAmountFormatter.apply(playtimeMonth));
|
|
numbers.put("playtime_30d_trend", new Trend(playtimeBefore, playtimeAfter, false, timeAmountFormatter));
|
|
numbers.put("playtime_7d", timeAmountFormatter.apply(playtimeWeek));
|
|
numbers.put("playtime_24h", timeAmountFormatter.apply(playtimeDay));
|
|
|
|
numbers.put("playtime_30d_avg", timeAmountFormatter.apply(db.query(SessionQueries.averagePlaytimePerDay(monthAgo, now, timeZoneOffset, serverUUID))));
|
|
numbers.put("playtime_30d_avg_trend", new Trend(
|
|
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(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));
|
|
Long sessionsDay = db.query(SessionQueries.sessionCount(dayAgo, now, serverUUID));
|
|
Long sessionsBefore = db.query(SessionQueries.sessionCount(monthAgo, halfMonthAgo, serverUUID));
|
|
Long sessionsAfter = db.query(SessionQueries.sessionCount(halfMonthAgo, now, serverUUID));
|
|
numbers.put("sessions_30d", sessionsMonth);
|
|
numbers.put("sessions_30d_trend", new Trend(sessionsBefore, sessionsAfter, false));
|
|
numbers.put("sessions_7d", sessionsWeek);
|
|
numbers.put("sessions_24h", sessionsDay);
|
|
|
|
Long sessionLengthAvgMonth = sessionsMonth != 0 ? playtimeMonth / sessionsMonth : 0;
|
|
Long sessionLengthAvgWeek = sessionsWeek != 0 ? playtimeWeek / sessionsWeek : 0;
|
|
Long sessionLengthAvgDay = sessionsDay != 0 ? playtimeDay / sessionsDay : 0;
|
|
numbers.put("session_length_30d_avg", timeAmountFormatter.apply(sessionLengthAvgMonth));
|
|
numbers.put("session_length_30d_trend", new Trend(
|
|
sessionsBefore != 0 ? playtimeBefore / sessionsBefore : 0,
|
|
sessionsAfter != 0 ? playtimeAfter / sessionsAfter : 0,
|
|
false,
|
|
timeAmountFormatter
|
|
));
|
|
numbers.put("session_length_7d_avg", timeAmountFormatter.apply(sessionLengthAvgWeek));
|
|
numbers.put("session_length_24h_avg", timeAmountFormatter.apply(sessionLengthAvgDay));
|
|
|
|
TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(monthAgo, now, serverUUID)));
|
|
numbers.put("average_tps", decimalFormatter.apply(tpsMutator.averageTPS()));
|
|
numbers.put("low_tps_spikes", tpsMutator.lowTpsSpikeCount(config.getNumber(DisplaySettings.GRAPH_TPS_THRESHOLD_MED)));
|
|
numbers.put("downtime", timeAmountFormatter.apply(tpsMutator.serverDownTime()));
|
|
|
|
return numbers;
|
|
}
|
|
|
|
private Map<String, Object> createInsightsMap(UUID serverUUID) {
|
|
Database db = dbSystem.getDatabase();
|
|
long now = System.currentTimeMillis();
|
|
long halfMonthAgo = now - TimeUnit.DAYS.toMillis(15L);
|
|
long monthAgo = now - TimeUnit.DAYS.toMillis(30L);
|
|
|
|
Map<String, Object> insights = new HashMap<>();
|
|
|
|
SessionsMutator sessions = new SessionsMutator(db.query(SessionQueries.fetchServerSessionsWithoutKillOrWorldData(monthAgo, now, serverUUID)));
|
|
List<TPS> tpsData = db.query(TPSQueries.fetchTPSDataOfServer(monthAgo, now, serverUUID));
|
|
Map<UUID, Long> registerDates = db.query(UserInfoQueries.fetchRegisterDates(monthAgo, now, serverUUID));
|
|
|
|
PlayersOnlineResolver playersOnlineResolver = new PlayersOnlineResolver(new TPSMutator(tpsData));
|
|
SessionsMutator firstSessions = sessions.filterBy(session -> {
|
|
long registered = registerDates.getOrDefault(session.getValue(SessionKeys.UUID).orElse(null), -501L);
|
|
long start = session.getDate();
|
|
return Math.abs(registered - start) < 500L;
|
|
});
|
|
SessionsMutator firstSessionsBefore = firstSessions.filterSessionsBetween(monthAgo, halfMonthAgo);
|
|
SessionsMutator firstSessionsAfter = firstSessions.filterSessionsBetween(halfMonthAgo, now);
|
|
|
|
long avgSessionLength = firstSessions.toAverageSessionLength();
|
|
long avgSessionLengthBefore = firstSessionsBefore.toAverageSessionLength();
|
|
long avgSessionLengthAfter = firstSessionsAfter.toAverageSessionLength();
|
|
insights.put("first_session_length_avg", timeAmountFormatter.apply(avgSessionLength));
|
|
insights.put("first_session_length_trend", new Trend(avgSessionLengthBefore, avgSessionLengthAfter, false, timeAmountFormatter));
|
|
|
|
int lonelyJoins = playersOnlineResolver.findLonelyJoins(sessions.toSessionStarts());
|
|
int loneJoinsBefore = playersOnlineResolver.findLonelyJoins(sessions.filterSessionsBetween(monthAgo, halfMonthAgo).toSessionStarts());
|
|
int loneJoinsAfter = playersOnlineResolver.findLonelyJoins(sessions.filterSessionsBetween(halfMonthAgo, now).toSessionStarts());
|
|
insights.put("lone_joins", lonelyJoins);
|
|
insights.put("lone_joins_trend", new Trend(loneJoinsBefore, loneJoinsAfter, true));
|
|
|
|
int newLonelyJoins = playersOnlineResolver.findLonelyJoins(firstSessions.toSessionStarts());
|
|
int newLoneJoinsBefore = playersOnlineResolver.findLonelyJoins(firstSessionsBefore.toSessionStarts());
|
|
int newLoneJoinsAfter = playersOnlineResolver.findLonelyJoins(firstSessionsAfter.toSessionStarts());
|
|
insights.put("lone_new_joins", newLonelyJoins);
|
|
insights.put("lone_new_joins_trend", new Trend(newLoneJoinsBefore, newLoneJoinsAfter, true));
|
|
|
|
double playersOnlineOnRegister = firstSessions.toAveragePlayersOnline(playersOnlineResolver);
|
|
double playersOnlineOnRegisterBefore = firstSessionsBefore.toAveragePlayersOnline(playersOnlineResolver);
|
|
double playersOnlineOnRegisterAfter = firstSessionsAfter.toAveragePlayersOnline(playersOnlineResolver);
|
|
insights.put("players_first_join_avg", decimalFormatter.apply(playersOnlineOnRegister));
|
|
insights.put("players_first_join_trend", new Trend(playersOnlineOnRegisterBefore, playersOnlineOnRegisterAfter, false, decimalFormatter));
|
|
|
|
return insights;
|
|
}
|
|
} |