mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-11-20 09:35:47 +01:00
Added player endpoint
This commit is contained in:
parent
13668170b6
commit
c41b8c201e
@ -30,6 +30,7 @@ import com.djrapitops.plan.utilities.html.graphs.Graphs;
|
||||
import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -248,16 +249,35 @@ public class SessionsMutator {
|
||||
.average().orElse(0.0);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> toPlayerJSONMaps(
|
||||
public List<Map<String, Object>> toPlayerNameJSONMaps(
|
||||
Graphs graphs,
|
||||
WorldAliasSettings worldAliasSettings,
|
||||
Formatters formatters
|
||||
) {
|
||||
return toJSONMaps(graphs, worldAliasSettings, formatters,
|
||||
sessionMap -> sessionMap.get("player_name"));
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> toServerNameJSONMaps(
|
||||
Graphs graphs,
|
||||
WorldAliasSettings worldAliasSettings,
|
||||
Formatters formatters
|
||||
) {
|
||||
return toJSONMaps(graphs, worldAliasSettings, formatters,
|
||||
sessionMap -> sessionMap.get("server_name"));
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> toJSONMaps(
|
||||
Graphs graphs,
|
||||
WorldAliasSettings worldAliasSettings,
|
||||
Formatters formatters,
|
||||
Function<Map<String, Object>, Object> nameFunction
|
||||
) {
|
||||
return sessions.stream().map(session -> {
|
||||
Map<String, Object> sessionMap = new HashMap<>();
|
||||
sessionMap.put("player_name", session.getValue(SessionKeys.NAME).orElse(session.getUnsafe(SessionKeys.UUID).toString()));
|
||||
sessionMap.put("name", sessionMap.get("player_name"));
|
||||
sessionMap.put("server_name", session.getValue(SessionKeys.SERVER_NAME).orElse(session.getUnsafe(SessionKeys.SERVER_UUID).toString()));
|
||||
sessionMap.put("name", nameFunction.apply(sessionMap));
|
||||
sessionMap.put("start", session.getValue(SessionKeys.START).map(formatters.yearLong()).orElse("-") +
|
||||
(session.supports(SessionKeys.END) ? "" : " (Online)"));
|
||||
sessionMap.put("end", session.getValue(SessionKeys.END).map(formatters.yearLong()).orElse("Online"));
|
||||
|
@ -18,6 +18,7 @@ package com.djrapitops.plan.system;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.connection.BadRequestException;
|
||||
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
|
||||
import com.djrapitops.plan.db.access.queries.objects.UserIdentifierQueries;
|
||||
import com.djrapitops.plan.system.database.DBSystem;
|
||||
import com.djrapitops.plan.system.info.server.Server;
|
||||
import com.djrapitops.plan.system.webserver.RequestTarget;
|
||||
@ -55,4 +56,18 @@ public class Identifiers {
|
||||
.map(Server::getUuid)
|
||||
.orElseThrow(() -> new BadRequestException("Given 'server' was not found in the database: '" + serverName + "'"));
|
||||
}
|
||||
|
||||
public UUID getPlayerUUID(RequestTarget target) throws BadRequestException {
|
||||
String playerIdentifier = target.getParameter("player")
|
||||
.orElseThrow(() -> new BadRequestException("'player' parameter was not defined."));
|
||||
|
||||
return UUIDUtility.parseFromString(playerIdentifier)
|
||||
.orElse(getPlayerUUIDFromName(playerIdentifier));
|
||||
}
|
||||
|
||||
private UUID getPlayerUUIDFromName(String playerName) throws BadRequestException {
|
||||
return dbSystem.getDatabase()
|
||||
.query(UserIdentifierQueries.fetchPlayerUUIDOf(playerName))
|
||||
.orElseThrow(() -> new BadRequestException("Given 'player' was not found in the database: '" + playerName + "'"));
|
||||
}
|
||||
}
|
@ -84,7 +84,7 @@ public class JSONFactory {
|
||||
List<Session> sessions = db.query(SessionQueries.fetchLatestSessionsOfServer(
|
||||
serverUUID, config.get(DisplaySettings.SESSIONS_PER_PAGE)
|
||||
));
|
||||
return new SessionsMutator(sessions).toPlayerJSONMaps(graphs, config.getWorldAliasSettings(), formatters);
|
||||
return new SessionsMutator(sessions).toPlayerNameJSONMaps(graphs, config.getWorldAliasSettings(), formatters);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> serverPlayerKillsAsJSONMap(UUID serverUUID) {
|
||||
|
@ -0,0 +1,187 @@
|
||||
package com.djrapitops.plan.system.json;
|
||||
|
||||
import com.djrapitops.plan.data.container.GeoInfo;
|
||||
import com.djrapitops.plan.data.store.containers.PlayerContainer;
|
||||
import com.djrapitops.plan.data.store.keys.PlayerKeys;
|
||||
import com.djrapitops.plan.data.store.mutators.ActivityIndex;
|
||||
import com.djrapitops.plan.data.store.mutators.PerServerMutator;
|
||||
import com.djrapitops.plan.data.store.mutators.PingMutator;
|
||||
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
|
||||
import com.djrapitops.plan.db.Database;
|
||||
import com.djrapitops.plan.db.access.queries.containers.PlayerContainerQuery;
|
||||
import com.djrapitops.plan.db.access.queries.objects.ServerQueries;
|
||||
import com.djrapitops.plan.system.cache.SessionCache;
|
||||
import com.djrapitops.plan.system.database.DBSystem;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.TimeSettings;
|
||||
import com.djrapitops.plan.utilities.formatting.Formatter;
|
||||
import com.djrapitops.plan.utilities.formatting.Formatters;
|
||||
import com.djrapitops.plan.utilities.html.graphs.Graphs;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Singleton
|
||||
public class PlayerJSONParser {
|
||||
|
||||
private final PlanConfig config;
|
||||
private final DBSystem dbSystem;
|
||||
private final Graphs graphs;
|
||||
private final Formatters formatters;
|
||||
|
||||
private final Formatter<Long> timeAmount;
|
||||
private final Formatter<Double> decimals;
|
||||
private final Formatter<Long> year;
|
||||
|
||||
@Inject
|
||||
public PlayerJSONParser(
|
||||
PlanConfig config,
|
||||
DBSystem dbSystem,
|
||||
Formatters formatters,
|
||||
Graphs graphs
|
||||
) {
|
||||
this.config = config;
|
||||
this.dbSystem = dbSystem;
|
||||
|
||||
this.formatters = formatters;
|
||||
timeAmount = formatters.timeAmount();
|
||||
decimals = formatters.decimals();
|
||||
year = formatters.yearLong();
|
||||
this.graphs = graphs;
|
||||
}
|
||||
|
||||
public Map<String, Object> createJSONAsMap(UUID playerUUID) {
|
||||
Database db = dbSystem.getDatabase();
|
||||
|
||||
Map<UUID, String> serverNames = db.query(ServerQueries.fetchServerNames());
|
||||
PlayerContainer player = db.query(new PlayerContainerQuery(playerUUID));
|
||||
SessionsMutator sessionsMutator = SessionsMutator.forContainer(player);
|
||||
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("info", createInfoJSONMap(player, serverNames));
|
||||
data.put("online_activity", createOnlineActivityJSONMap(sessionsMutator));
|
||||
|
||||
data.put("nicknames", player.getValue(PlayerKeys.NICKNAMES)
|
||||
.map(nicks -> Nickname.fromDataNicknames(nicks, serverNames, year))
|
||||
.orElse(Collections.emptyList()));
|
||||
data.put("connections", player.getValue(PlayerKeys.GEO_INFO)
|
||||
.map(geoInfo -> ConnectionInfo.fromGeoInfo(geoInfo, year))
|
||||
.orElse(Collections.emptyList()));
|
||||
data.put("player_kills", player.getValue(PlayerKeys.PLAYER_KILLS).orElse(Collections.emptyList()));
|
||||
data.put("player_deaths", player.getValue(PlayerKeys.PLAYER_DEATHS).orElse(Collections.emptyList()));
|
||||
data.put("sessions", sessionsMutator.toServerNameJSONMaps(graphs, config.getWorldAliasSettings(), formatters));
|
||||
return data;
|
||||
}
|
||||
|
||||
private Map<String, Object> createOnlineActivityJSONMap(SessionsMutator sessionsMutator) {
|
||||
long now = System.currentTimeMillis();
|
||||
long monthAgo = now - TimeUnit.DAYS.toMillis(30L);
|
||||
long weekAgo = now - TimeUnit.DAYS.toMillis(7L);
|
||||
SessionsMutator sessionsMonth = sessionsMutator.filterSessionsBetween(monthAgo, now);
|
||||
SessionsMutator sessionsWeek = sessionsMutator.filterSessionsBetween(weekAgo, now);
|
||||
|
||||
Map<String, Object> onlineActivity = new HashMap<>();
|
||||
|
||||
onlineActivity.put("playtime_30d", timeAmount.apply(sessionsMonth.toPlaytime()));
|
||||
onlineActivity.put("active_playtime_30d", timeAmount.apply(sessionsMonth.toActivePlaytime()));
|
||||
onlineActivity.put("afk_time_30d", timeAmount.apply(sessionsMonth.toAfkTime()));
|
||||
onlineActivity.put("average_session_length_30d", timeAmount.apply(sessionsMonth.toAverageSessionLength()));
|
||||
onlineActivity.put("session_count_30d", sessionsMonth.count());
|
||||
onlineActivity.put("player_kill_count_30d", sessionsMonth.toPlayerKillCount());
|
||||
onlineActivity.put("mob_kill_count_30d", sessionsMonth.toMobKillCount());
|
||||
onlineActivity.put("death_count_30d", sessionsMonth.toDeathCount());
|
||||
|
||||
onlineActivity.put("playtime_7d", timeAmount.apply(sessionsWeek.toPlaytime()));
|
||||
onlineActivity.put("active_playtime_7d", timeAmount.apply(sessionsWeek.toActivePlaytime()));
|
||||
onlineActivity.put("afk_time_7d", timeAmount.apply(sessionsWeek.toAfkTime()));
|
||||
onlineActivity.put("average_session_length_7d", timeAmount.apply(sessionsWeek.toAverageSessionLength()));
|
||||
onlineActivity.put("session_count_7d", sessionsWeek.count());
|
||||
onlineActivity.put("player_kill_count_7d", sessionsWeek.toPlayerKillCount());
|
||||
onlineActivity.put("mob_kill_count_7d", sessionsWeek.toMobKillCount());
|
||||
onlineActivity.put("death_count_7d", sessionsWeek.toDeathCount());
|
||||
|
||||
return onlineActivity;
|
||||
}
|
||||
|
||||
private Map<String, Object> createInfoJSONMap(PlayerContainer player, Map<UUID, String> serverNames) {
|
||||
SessionsMutator sessions = SessionsMutator.forContainer(player);
|
||||
ActivityIndex activityIndex = player.getActivityIndex(System.currentTimeMillis(), config.get(TimeSettings.ACTIVE_PLAY_THRESHOLD));
|
||||
PerServerMutator perServer = PerServerMutator.forContainer(player);
|
||||
PingMutator ping = PingMutator.forContainer(player);
|
||||
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
|
||||
info.put("online", SessionCache.getCachedSession(player.getUnsafe(PlayerKeys.UUID)).isPresent());
|
||||
info.put("operator", player.getValue(PlayerKeys.OPERATOR).orElse(false));
|
||||
info.put("banned", player.getValue(PlayerKeys.BANNED).orElse(false));
|
||||
info.put("kick_count", player.getValue(PlayerKeys.KICK_COUNT).orElse(0));
|
||||
info.put("player_kill_count", player.getValue(PlayerKeys.PLAYER_KILL_COUNT).orElse(0));
|
||||
info.put("mob_kill_count", player.getValue(PlayerKeys.MOB_KILL_COUNT).orElse(0));
|
||||
info.put("death_count", player.getValue(PlayerKeys.DEATH_COUNT).orElse(0));
|
||||
info.put("playtime", timeAmount.apply(sessions.toPlaytime()));
|
||||
info.put("active_playtime", timeAmount.apply(sessions.toActivePlaytime()));
|
||||
info.put("afk_time", timeAmount.apply(sessions.toAfkTime()));
|
||||
info.put("session_count", sessions.count());
|
||||
info.put("longest_session_length", timeAmount.apply(sessions.toLongestSessionLength()));
|
||||
info.put("session_median", timeAmount.apply(sessions.toMedianSessionLength()));
|
||||
info.put("activity_index", decimals.apply(activityIndex.getValue()));
|
||||
info.put("activity_index_group", activityIndex.getGroup());
|
||||
UUID favoriteServer = perServer.favoriteServer();
|
||||
info.put("favorite_server", serverNames.getOrDefault(favoriteServer, favoriteServer.toString()));
|
||||
info.put("average_ping", decimals.apply(ping.average()) + " ms");
|
||||
info.put("worst_ping", ping.max() + " ms");
|
||||
info.put("best_ping", ping.min() + " ms");
|
||||
info.put("registered", player.getValue(PlayerKeys.REGISTERED).map(year).orElse("-"));
|
||||
info.put("last_seen", player.getValue(PlayerKeys.LAST_SEEN).map(year).orElse("-"));
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public static class Nickname {
|
||||
private String nickname;
|
||||
private String server;
|
||||
private String date;
|
||||
|
||||
public Nickname(String nickname, String server, String date) {
|
||||
this.nickname = nickname;
|
||||
this.server = server;
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public static List<Nickname> fromDataNicknames(
|
||||
List<com.djrapitops.plan.data.store.objects.Nickname> nicknames,
|
||||
Map<UUID, String> serverNames,
|
||||
Formatter<Long> dateFormatter
|
||||
) {
|
||||
List<Nickname> mapped = new ArrayList<>();
|
||||
for (com.djrapitops.plan.data.store.objects.Nickname nickname : nicknames) {
|
||||
mapped.add(new Nickname(
|
||||
nickname.getName(),
|
||||
serverNames.getOrDefault(nickname.getServerUUID(), nickname.getServerUUID().toString()),
|
||||
dateFormatter.apply(nickname.getDate())
|
||||
));
|
||||
}
|
||||
return mapped;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ConnectionInfo {
|
||||
private String geolocation;
|
||||
private String date;
|
||||
|
||||
public ConnectionInfo(String geolocation, String date) {
|
||||
this.geolocation = geolocation;
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public static List<ConnectionInfo> fromGeoInfo(List<GeoInfo> geoInfo, Formatter<Long> dateFormatter) {
|
||||
return geoInfo.stream()
|
||||
.map(i -> new ConnectionInfo(i.getGeolocation(), dateFormatter.apply(i.getDate())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package com.djrapitops.plan.system.webserver.pages.json;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.WebUserAuthException;
|
||||
import com.djrapitops.plan.api.exceptions.connection.WebException;
|
||||
import com.djrapitops.plan.data.WebUser;
|
||||
import com.djrapitops.plan.system.Identifiers;
|
||||
import com.djrapitops.plan.system.json.PlayerJSONParser;
|
||||
import com.djrapitops.plan.system.webserver.Request;
|
||||
import com.djrapitops.plan.system.webserver.RequestTarget;
|
||||
import com.djrapitops.plan.system.webserver.auth.Authentication;
|
||||
import com.djrapitops.plan.system.webserver.pages.PageHandler;
|
||||
import com.djrapitops.plan.system.webserver.response.Response;
|
||||
import com.djrapitops.plan.system.webserver.response.data.JSONResponse;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.UUID;
|
||||
|
||||
@Singleton
|
||||
public class PlayerJSONHandler implements PageHandler {
|
||||
|
||||
private final Identifiers identifiers;
|
||||
private final PlayerJSONParser jsonParser;
|
||||
|
||||
@Inject
|
||||
public PlayerJSONHandler(Identifiers identifiers, PlayerJSONParser jsonParser) {
|
||||
this.identifiers = identifiers;
|
||||
this.jsonParser = jsonParser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response getResponse(Request request, RequestTarget target) throws WebException {
|
||||
UUID playerUUID = identifiers.getPlayerUUID(target); // Can throw BadRequestException
|
||||
return new JSONResponse<>(jsonParser.createJSONAsMap(playerUUID));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthorized(Authentication auth, RequestTarget target) throws WebUserAuthException {
|
||||
WebUser webUser = auth.getWebUser();
|
||||
return webUser.getPermLevel() <= 1 || webUser.getName().equalsIgnoreCase(target.get(target.size() - 1));
|
||||
|
||||
}
|
||||
}
|
@ -52,7 +52,8 @@ public class RootJSONHandler extends TreePageHandler {
|
||||
PlayerKillsJSONHandler playerKillsJSONHandler,
|
||||
PvPPvEJSONParser pvPPvEJSONParser,
|
||||
PlayerBaseOverviewJSONParser playerBaseOverviewJSONParser,
|
||||
PerformanceJSONParser performanceJSONParser
|
||||
PerformanceJSONParser performanceJSONParser,
|
||||
PlayerJSONHandler playerJSONHandler
|
||||
) {
|
||||
super(responseFactory);
|
||||
|
||||
@ -70,6 +71,8 @@ public class RootJSONHandler extends TreePageHandler {
|
||||
registerPage("playerVersus", pvPPvEJSONParser);
|
||||
registerPage("playerbaseOverview", playerBaseOverviewJSONParser);
|
||||
registerPage("performanceOverview", performanceJSONParser);
|
||||
|
||||
registerPage("player", playerJSONHandler);
|
||||
}
|
||||
|
||||
private <T> void registerPage(String identifier, TabJSONParser<T> tabJSONParser) {
|
||||
|
@ -25,7 +25,9 @@
|
||||
</head>
|
||||
|
||||
<body id="page-top">
|
||||
|
||||
<script>
|
||||
var gmPieColors = [${gmPieColors}];
|
||||
</script>
|
||||
<div class="page-loader">
|
||||
<span class="loader"></span>
|
||||
<span class="loader-text">Please wait..</span>
|
||||
@ -1294,66 +1296,29 @@
|
||||
<script src="js/sb-admin-2.js"></script>
|
||||
<script src="js/xmlhttprequests.js"></script>
|
||||
<script src="js/color-selector.js"></script>
|
||||
<script src="js/graphs.js"></script>
|
||||
|
||||
<!-- Page level plugins -->
|
||||
<script src="vendor/datatables/jquery.dataTables.min.js"></script>
|
||||
<script src="vendor/datatables/dataTables.bootstrap4.min.js"></script>
|
||||
<script src="vendor/highcharts/highstock.js"></script>
|
||||
<script src="vendor/highcharts/drilldown.js"></script>
|
||||
<script src="vendor/highcharts/highcharts-more.js"></script>
|
||||
<script src="vendor/highcharts/no-data-to-display.js"></script>
|
||||
<link href='vendor/fullcalendar/fullcalendar.min.css' rel='stylesheet'/>
|
||||
<script src='vendor/momentjs/moment.js'></script>
|
||||
<script src='vendor/fullcalendar/fullcalendar.min.js'></script>
|
||||
|
||||
<!-- Page level custom scripts -->
|
||||
<script src="js/sessionAccordion.js"></script>
|
||||
<script src="js/player-values.js"></script>
|
||||
|
||||
<script>
|
||||
setLoadingText('Loading player values..');
|
||||
loadPlayerOverviewValues(
|
||||
{
|
||||
info: {
|
||||
online: true,
|
||||
operator: true,
|
||||
banned: true,
|
||||
kick_count: 5,
|
||||
player_kill_count: 20,
|
||||
mob_kill_count: 1234,
|
||||
death_count: 8,
|
||||
playtime: '5m',
|
||||
active_playtime: '2m',
|
||||
afk_time: '3m',
|
||||
session_count: 3,
|
||||
longest_session_lenght: '4m',
|
||||
session_median: '30s',
|
||||
activity_index: 1.32,
|
||||
activity_index_group: 'Irregular',
|
||||
favorite_server: 'Server 1',
|
||||
average_ping: '6 ms',
|
||||
best_ping: '3 ms',
|
||||
worst_ping: '123 ms',
|
||||
registered: '10 January 2018',
|
||||
last_seen: '30 July 2019'
|
||||
},
|
||||
nicknames: [
|
||||
{nickname: 'Rsl1122', server: 'Server 1', date: '30 July 2019'}
|
||||
],
|
||||
connections: [
|
||||
{geolocation: 'Local Machine', date: '30 July 2019'}
|
||||
],
|
||||
online_activity: {
|
||||
playtime_30d: '4s',
|
||||
playtime_7d: '4s',
|
||||
active_playtime_30d: '2s',
|
||||
active_playtime_7d: '2s',
|
||||
afk_time_30d: '2s',
|
||||
afk_time_7d: '2s',
|
||||
average_session_length_30d: '2s',
|
||||
average_session_length_7d: '2s',
|
||||
session_count_30d: 3,
|
||||
session_count_7d: 3,
|
||||
player_kill_count_30d: 32,
|
||||
player_kill_count_7d: 32,
|
||||
mob_kill_count_30d: 32,
|
||||
mob_kill_count_7d: 32,
|
||||
death_count_30d: 32,
|
||||
death_count_7d: 32,
|
||||
}
|
||||
},
|
||||
null
|
||||
);
|
||||
jsonRequest("../v1/player?player=${playerName}", function (json, error) {
|
||||
loadPlayerOverviewValues(json, error);
|
||||
loadSessionAccordion(json, error);
|
||||
});
|
||||
setLoadingText('Rendering graphs..');
|
||||
setLoadingText('Sorting players table..');
|
||||
setLoadingText('Almost done..');
|
||||
|
@ -26,7 +26,6 @@
|
||||
|
||||
<body id="page-top">
|
||||
<script>
|
||||
var worldPieColors = [${worldPieColors}];
|
||||
var gmPieColors = [${gmPieColors}];
|
||||
</script>
|
||||
<div class="page-loader">
|
||||
|
@ -105,4 +105,14 @@ Type | Description
|
||||
`worldPie` | World Pie data of all sessions combined
|
||||
`activity` | Activity stack graph and pie graph data
|
||||
`geolocation` | World Map and bar graph data. World Map uses iso-a3 specification of Country codes and Bar graph uses country names.
|
||||
`ping` | Min, Avg, Max ping graph data.
|
||||
`ping` | Min, Avg, Max ping graph data.
|
||||
|
||||
### `GET /v1/player`
|
||||
|
||||
Fetch all data for a player page
|
||||
|
||||
Required parameters: `player`
|
||||
|
||||
Parameter|Expected value|Description
|
||||
--|--|--
|
||||
`player` | Name or UUID of a player | Used for identifying player the data should be about
|
||||
|
Loading…
Reference in New Issue
Block a user