/*
* 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 .
*/
package com.djrapitops.plan.delivery.rendering.json.graphs;
import com.djrapitops.plan.delivery.domain.DateMap;
import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.delivery.domain.JoinAddressCount;
import com.djrapitops.plan.delivery.domain.JoinAddressCounts;
import com.djrapitops.plan.delivery.domain.datatransfer.ServerDto;
import com.djrapitops.plan.delivery.domain.datatransfer.graphs.GraphCollection;
import com.djrapitops.plan.delivery.domain.datatransfer.graphs.ServerSpecificLineGraph;
import com.djrapitops.plan.delivery.domain.mutators.MutatorFunctions;
import com.djrapitops.plan.delivery.domain.mutators.PingMutator;
import com.djrapitops.plan.delivery.domain.mutators.TPSMutator;
import com.djrapitops.plan.delivery.rendering.json.graphs.bar.BarGraph;
import com.djrapitops.plan.delivery.rendering.json.graphs.line.LineGraph;
import com.djrapitops.plan.delivery.rendering.json.graphs.line.LineGraphFactory;
import com.djrapitops.plan.delivery.rendering.json.graphs.line.PingGraph;
import com.djrapitops.plan.delivery.rendering.json.graphs.line.Point;
import com.djrapitops.plan.delivery.rendering.json.graphs.pie.Pie;
import com.djrapitops.plan.delivery.rendering.json.graphs.pie.PieSlice;
import com.djrapitops.plan.delivery.rendering.json.graphs.pie.WorldPie;
import com.djrapitops.plan.delivery.rendering.json.graphs.special.WorldMap;
import com.djrapitops.plan.delivery.rendering.json.graphs.stack.StackGraph;
import com.djrapitops.plan.gathering.domain.FinishedSession;
import com.djrapitops.plan.gathering.domain.Ping;
import com.djrapitops.plan.gathering.domain.WorldTimes;
import com.djrapitops.plan.identification.Server;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.settings.config.PlanConfig;
import com.djrapitops.plan.settings.config.paths.DataGatheringSettings;
import com.djrapitops.plan.settings.config.paths.DisplaySettings;
import com.djrapitops.plan.settings.config.paths.TimeSettings;
import com.djrapitops.plan.settings.locale.Locale;
import com.djrapitops.plan.settings.locale.lang.GenericLang;
import com.djrapitops.plan.settings.theme.Theme;
import com.djrapitops.plan.settings.theme.ThemeVal;
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.NetworkActivityIndexQueries;
import com.djrapitops.plan.storage.database.queries.analysis.PlayerCountQueries;
import com.djrapitops.plan.storage.database.queries.objects.*;
import com.djrapitops.plan.storage.database.sql.tables.JoinAddressTable;
import com.djrapitops.plan.utilities.comparators.DateHolderOldestComparator;
import com.djrapitops.plan.utilities.comparators.PieSliceComparator;
import com.djrapitops.plan.utilities.java.Lists;
import com.djrapitops.plan.utilities.java.Maps;
import net.playeranalytics.plugin.scheduling.TimeAmount;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Creates Graph related Data JSON.
*
* @author AuroraLS3
*/
@Singleton
public class GraphJSONCreator {
private final PlanConfig config;
private final Locale locale;
private final Theme theme;
private final DBSystem dbSystem;
private final Graphs graphs;
@Inject
public GraphJSONCreator(
PlanConfig config,
Locale locale,
Theme theme,
DBSystem dbSystem,
Graphs graphs
) {
this.config = config;
this.locale = locale;
this.theme = theme;
this.dbSystem = dbSystem;
this.graphs = graphs;
}
public String performanceGraphJSON(ServerUUID serverUUID) {
long now = System.currentTimeMillis();
Database db = dbSystem.getDatabase();
LineGraphFactory lineGraphs = graphs.line();
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180);
TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(halfYearAgo, now, serverUUID)));
return '{' +
"\"playersOnline\":" + lineGraphs.playersOnlineGraph(tpsMutator).toHighChartsSeries() +
",\"tps\":" + lineGraphs.tpsGraph(tpsMutator).toHighChartsSeries() +
",\"cpu\":" + lineGraphs.cpuGraph(tpsMutator).toHighChartsSeries() +
",\"ram\":" + lineGraphs.ramGraph(tpsMutator).toHighChartsSeries() +
",\"entities\":" + lineGraphs.entityGraph(tpsMutator).toHighChartsSeries() +
",\"chunks\":" + lineGraphs.chunkGraph(tpsMutator).toHighChartsSeries() +
",\"disk\":" + lineGraphs.diskGraph(tpsMutator).toHighChartsSeries() +
",\"colors\":{" +
"\"playersOnline\":\"" + theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE) + "\"," +
"\"cpu\":\"" + theme.getValue(ThemeVal.GRAPH_CPU) + "\"," +
"\"ram\":\"" + theme.getValue(ThemeVal.GRAPH_RAM) + "\"," +
"\"entities\":\"" + theme.getValue(ThemeVal.GRAPH_ENTITIES) + "\"," +
"\"chunks\":\"" + theme.getValue(ThemeVal.GRAPH_CHUNKS) + "\"," +
"\"low\":\"" + theme.getValue(ThemeVal.GRAPH_TPS_LOW) + "\"," +
"\"med\":\"" + theme.getValue(ThemeVal.GRAPH_TPS_MED) + "\"," +
"\"high\":\"" + theme.getValue(ThemeVal.GRAPH_TPS_HIGH) + "\"}" +
",\"zones\":{" +
"\"tpsThresholdMed\":" + config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED) + ',' +
"\"tpsThresholdHigh\":" + config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_HIGH) + ',' +
"\"diskThresholdMed\":" + config.get(DisplaySettings.GRAPH_DISK_THRESHOLD_MED) + ',' +
"\"diskThresholdHigh\":" + config.get(DisplaySettings.GRAPH_DISK_THRESHOLD_HIGH) +
"}}";
}
public Map optimizedPerformanceGraphJSON(ServerUUID serverUUID) {
long now = System.currentTimeMillis();
long twoMonthsAgo = now - TimeUnit.DAYS.toMillis(60);
long monthAgo = now - TimeUnit.DAYS.toMillis(30);
long lowestResolution = TimeUnit.MINUTES.toMillis(20);
long lowResolution = TimeUnit.MINUTES.toMillis(5);
Database db = dbSystem.getDatabase();
TPSMutator lowestResolutionData = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServerInResolution(0, twoMonthsAgo, lowestResolution, serverUUID)));
TPSMutator lowResolutionData = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServerInResolution(twoMonthsAgo, monthAgo, lowResolution, serverUUID)));
TPSMutator highResolutionData = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(monthAgo, now, serverUUID)));
String serverName = db.query(ServerQueries.fetchServerMatchingIdentifier(serverUUID))
.map(Server::getIdentifiableName)
.orElse(serverUUID.toString());
List values = lowestResolutionData.toArrays(new LineGraph.GapStrategy(
config.isTrue(DisplaySettings.GAPS_IN_GRAPH_DATA),
lowestResolution + TimeUnit.MINUTES.toMillis(1),
TimeUnit.MINUTES.toMillis(1),
TimeUnit.MINUTES.toMillis(30),
null
));
values.addAll(lowResolutionData.toArrays(new LineGraph.GapStrategy(
config.isTrue(DisplaySettings.GAPS_IN_GRAPH_DATA),
lowResolution + TimeUnit.MINUTES.toMillis(1),
TimeUnit.MINUTES.toMillis(1),
TimeUnit.MINUTES.toMillis(30),
null
)));
values.addAll(highResolutionData.toArrays(new LineGraph.GapStrategy(
config.isTrue(DisplaySettings.GAPS_IN_GRAPH_DATA),
TimeUnit.MINUTES.toMillis(3),
TimeUnit.MINUTES.toMillis(1),
TimeUnit.MINUTES.toMillis(30),
null
)));
return Maps.builder(String.class, Object.class)
.put("keys", new String[]{"date", "playersOnline", "tps", "cpu", "ram", "entities", "chunks", "disk"})
.put("values", values)
.put("colors", Maps.builder(String.class, Object.class)
.put("playersOnline", theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE))
.put("cpu", theme.getValue(ThemeVal.GRAPH_CPU))
.put("ram", theme.getValue(ThemeVal.GRAPH_RAM))
.put("entities", theme.getValue(ThemeVal.GRAPH_ENTITIES))
.put("chunks", theme.getValue(ThemeVal.GRAPH_CHUNKS))
.put("low", theme.getValue(ThemeVal.GRAPH_TPS_LOW))
.put("med", theme.getValue(ThemeVal.GRAPH_TPS_MED))
.put("high", theme.getValue(ThemeVal.GRAPH_TPS_HIGH))
.build())
.put("zones", Maps.builder(String.class, Object.class)
.put("tpsThresholdMed", config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED))
.put("tpsThresholdHigh", config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_HIGH))
.put("diskThresholdMed", config.get(DisplaySettings.GRAPH_DISK_THRESHOLD_MED))
.put("diskThresholdHigh", config.get(DisplaySettings.GRAPH_DISK_THRESHOLD_HIGH))
.build())
.put("serverName", serverName)
.put("serverUUID", serverUUID)
.build();
}
public String playersOnlineGraph(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
long now = System.currentTimeMillis();
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L);
List points = Lists.map(
db.query(TPSQueries.fetchPlayersOnlineOfServer(halfYearAgo, now, serverUUID)),
Point::fromDateObj
);
return "{\"playersOnline\":" + graphs.line().lineGraph(points).toHighChartsSeries() +
",\"color\":\"" + theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE) + "\"}";
}
public String uniqueAndNewGraphJSON(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
LineGraphFactory lineGraphs = graphs.line();
long now = System.currentTimeMillis();
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L);
int timeZoneOffset = config.getTimeZone().getOffset(now);
NavigableMap uniquePerDay = db.query(
PlayerCountQueries.uniquePlayerCounts(halfYearAgo, now, timeZoneOffset, serverUUID)
);
NavigableMap newPerDay = db.query(
PlayerCountQueries.newPlayerCounts(halfYearAgo, now, timeZoneOffset, serverUUID)
);
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay, TimeUnit.DAYS.toMillis(1L));
}
public String hourlyUniqueAndNewGraphJSON(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
LineGraphFactory lineGraphs = graphs.line();
long now = System.currentTimeMillis();
long weekAgo = now - TimeUnit.DAYS.toMillis(7L);
int timeZoneOffset = config.getTimeZone().getOffset(now);
NavigableMap uniquePerDay = db.query(
PlayerCountQueries.hourlyUniquePlayerCounts(weekAgo, now, timeZoneOffset, serverUUID)
);
NavigableMap newPerDay = db.query(
PlayerCountQueries.newPlayerCounts(weekAgo, now, timeZoneOffset, serverUUID)
);
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay, TimeUnit.HOURS.toMillis(1L));
}
public String createUniqueAndNewJSON(LineGraphFactory lineGraphs, NavigableMap uniquePerDay, NavigableMap newPerDay, long gapFillPeriod) {
LineGraph.GapStrategy gapStrategy = new LineGraph.GapStrategy(true, gapFillPeriod, 0, gapFillPeriod, 0.0);
return "{\"uniquePlayers\":" +
lineGraphs.lineGraph(MutatorFunctions.toPoints(
MutatorFunctions.addMissing(uniquePerDay, gapFillPeriod, 0)
), gapStrategy).toHighChartsSeries() +
",\"newPlayers\":" +
lineGraphs.lineGraph(MutatorFunctions.toPoints(
MutatorFunctions.addMissing(newPerDay, gapFillPeriod, 0)
), gapStrategy).toHighChartsSeries() +
",\"colors\":{" +
"\"playersOnline\":\"" + theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE) + "\"," +
"\"newPlayers\":\"" + theme.getValue(ThemeVal.LIGHT_GREEN) + "\"" +
"}}";
}
public String uniqueAndNewGraphJSON() {
Database db = dbSystem.getDatabase();
LineGraphFactory lineGraphs = graphs.line();
long now = System.currentTimeMillis();
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L);
int timeZoneOffset = config.getTimeZone().getOffset(now);
NavigableMap uniquePerDay = db.query(
PlayerCountQueries.uniquePlayerCounts(halfYearAgo, now, timeZoneOffset)
);
NavigableMap newPerDay = db.query(
PlayerCountQueries.newPlayerCounts(halfYearAgo, now, timeZoneOffset)
);
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay, TimeUnit.DAYS.toMillis(1L));
}
public String hourlyUniqueAndNewGraphJSON() {
Database db = dbSystem.getDatabase();
LineGraphFactory lineGraphs = graphs.line();
long now = System.currentTimeMillis();
long weekAgo = now - TimeUnit.DAYS.toMillis(7L);
int timeZoneOffset = config.getTimeZone().getOffset(now);
NavigableMap uniquePerDay = db.query(
PlayerCountQueries.hourlyUniquePlayerCounts(weekAgo, now, timeZoneOffset)
);
NavigableMap newPerDay = db.query(
PlayerCountQueries.hourlyNewPlayerCounts(weekAgo, now, timeZoneOffset)
);
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay, TimeUnit.HOURS.toMillis(1L));
}
public String serverCalendarJSON(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
long now = System.currentTimeMillis();
long twoYearsAgo = now - TimeUnit.DAYS.toMillis(730L);
int timeZoneOffset = config.getTimeZone().getOffset(now);
NavigableMap uniquePerDay = db.query(
PlayerCountQueries.uniquePlayerCounts(twoYearsAgo, now, timeZoneOffset, serverUUID)
);
NavigableMap newPerDay = db.query(
PlayerCountQueries.newPlayerCounts(twoYearsAgo, now, timeZoneOffset, serverUUID)
);
NavigableMap playtimePerDay = db.query(
SessionQueries.playtimePerDay(twoYearsAgo, now, timeZoneOffset, serverUUID)
);
NavigableMap sessionsPerDay = db.query(
SessionQueries.sessionCountPerDay(twoYearsAgo, now, timeZoneOffset, serverUUID)
);
return "{\"data\":" +
graphs.calendar().serverCalendar(
uniquePerDay,
newPerDay,
playtimePerDay,
sessionsPerDay
).toCalendarSeries() +
",\"firstDay\":" + 1 + '}';
}
public String networkCalendarJSON() {
Database db = dbSystem.getDatabase();
long now = System.currentTimeMillis();
long twoYearsAgo = now - TimeUnit.DAYS.toMillis(730L);
int timeZoneOffset = config.getTimeZone().getOffset(now);
NavigableMap uniquePerDay = db.query(
PlayerCountQueries.uniquePlayerCounts(twoYearsAgo, now, timeZoneOffset)
);
NavigableMap newPerDay = db.query(
PlayerCountQueries.newPlayerCounts(twoYearsAgo, now, timeZoneOffset)
);
NavigableMap playtimePerDay = db.query(
SessionQueries.playtimePerDay(twoYearsAgo, now, timeZoneOffset)
);
NavigableMap sessionsPerDay = db.query(
SessionQueries.sessionCountPerDay(twoYearsAgo, now, timeZoneOffset)
);
return "{\"data\":" +
graphs.calendar().serverCalendar(
uniquePerDay,
newPerDay,
playtimePerDay,
sessionsPerDay
).toCalendarSeries() +
",\"firstDay\":" + 1 + '}';
}
public Map serverWorldPieJSONAsMap(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
WorldTimes worldTimes = db.query(WorldTimesQueries.fetchServerTotalWorldTimes(serverUUID));
WorldPie worldPie = graphs.pie().worldPie(worldTimes);
return Maps.builder(String.class, Object.class)
.put("world_series", worldPie.getSlices())
.put("gm_series", worldPie.toHighChartsDrillDownMaps())
.build();
}
public Map activityGraphsJSONAsMap(ServerUUID serverUUID) {
Database db = dbSystem.getDatabase();
long date = System.currentTimeMillis();
Long threshold = config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD);
DateMap