JSON parser for player table data

This commit is contained in:
Rsl1122 2019-04-30 18:36:50 +03:00
parent 7b5739995e
commit 71250c3d65
4 changed files with 256 additions and 3 deletions

View File

@ -64,8 +64,9 @@ public class Icon {
return color;
}
public void setColor(Color color) {
public Icon setColor(Color color) {
this.color = color;
return this;
}
@Override

View File

@ -39,6 +39,7 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
private final List<ExtensionTableData> tableData;
private List<String> order;
private List<ExtensionDescriptive> descriptives;
// Table and Graph data will be added later.
@ -52,6 +53,7 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
stringData = new HashMap<>();
tableData = new ArrayList<>();
descriptives = new ArrayList<>();
}
public TabInformation getTabInformation() {
@ -86,6 +88,17 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
return tableData;
}
/**
* Get all Descriptives for this tabs data.
* <p>
* Only available after the Tab has been built.
*
* @return List of {@link ExtensionDescriptive}s.
*/
public List<ExtensionDescriptive> getDescriptives() {
return descriptives;
}
@Override
public int compareTo(ExtensionTabData other) {
return Integer.compare(this.tabInformation.getTabPriority(), other.tabInformation.getTabPriority()); // Lower is first
@ -118,7 +131,6 @@ public class ExtensionTabData implements Comparable<ExtensionTabData> {
}
private void createOrderingList() {
List<ExtensionDescriptive> descriptives = new ArrayList<>();
booleanData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
doubleData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);
percentageData.values().stream().map(ExtensionData::getDescriptive).forEach(descriptives::add);

View File

@ -14,7 +14,6 @@ public class JSONResponse<T> extends Response {
public JSONResponse(T object) {
super(ResponseType.JSON);
super.setHeader("HTTP/1.1 200 OK");
try {
@ -27,4 +26,10 @@ public class JSONResponse<T> extends Response {
super.setContent("{\"error\":\"Gson for json responses not available on this server: " + e.toString() + "\"}");
}
}
public JSONResponse(String jsonString) {
super(ResponseType.JSON);
super.setHeader("HTTP/1.1 200 OK");
super.setContent(jsonString);
}
}

View File

@ -0,0 +1,235 @@
package com.djrapitops.plan.utilities.html.tables;
import com.djrapitops.plan.api.PlanAPI;
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.GeoInfoMutator;
import com.djrapitops.plan.data.store.mutators.SessionsMutator;
import com.djrapitops.plan.extension.FormatType;
import com.djrapitops.plan.extension.icon.Color;
import com.djrapitops.plan.extension.implementation.results.*;
import com.djrapitops.plan.utilities.comparators.PlayerContainerLastPlayedComparator;
import com.djrapitops.plan.utilities.formatting.Formatter;
import com.djrapitops.plan.utilities.formatting.Formatters;
import com.djrapitops.plan.utilities.html.Html;
import com.djrapitops.plan.utilities.html.icon.Family;
import com.djrapitops.plan.utilities.html.icon.Icon;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
/**
* Parsing utility for creating jQuery Datatables JSON for a Players Table.
* <p>
* See https://www.datatables.net/manual/data/orthogonal-data#HTML-5 for sort kinds
*
* @author Rsl1122
*/
public class PlayersTableJSONParser {
private final List<PlayerContainer> players;
private final List<ExtensionDescriptive> extensionDescriptives;
private final Map<UUID, ExtensionTabData> extensionData;
private final int maxPlayers;
private final long activeMsThreshold;
private final int activeLoginThreshold;
private final boolean openPlayerPageInNewTab;
private Map<FormatType, Formatter<Long>> numberFormatters;
private Formatter<Double> decimalFormatter;
private Formatter<Double> percentageFormatter;
public PlayersTableJSONParser(
// Data
List<PlayerContainer> players,
Map<UUID, ExtensionTabData> extensionData,
// Settings
int maxPlayers, long activeMsThreshold, int activeLoginThreshold, boolean openPlayerPageInNewTab,
// Formatters
Formatters formatters
) {
// Data
this.players = players;
this.extensionData = extensionData;
extensionDescriptives = extensionData.values().stream()
.map(ExtensionTabData::getDescriptives)
.flatMap(Collection::stream)
.distinct().sorted()
.collect(Collectors.toList());
// Settings
this.maxPlayers = maxPlayers;
this.activeMsThreshold = activeMsThreshold;
this.activeLoginThreshold = activeLoginThreshold;
this.openPlayerPageInNewTab = openPlayerPageInNewTab;
// Formatters
numberFormatters = new EnumMap<>(FormatType.class);
numberFormatters.put(FormatType.DATE_SECOND, formatters.secondLong());
numberFormatters.put(FormatType.DATE_YEAR, formatters.yearLong());
numberFormatters.put(FormatType.TIME_MILLISECONDS, formatters.timeAmount());
numberFormatters.put(FormatType.NONE, Object::toString);
this.decimalFormatter = formatters.decimals();
this.percentageFormatter = formatters.percentage();
}
public String toJSONString() {
String data = parseData();
String columnHeaders = parseColumnHeaders();
return "{columns:" + columnHeaders + ",data:" + data + '}';
}
private String parseData() {
StringBuilder dataJSON = new StringBuilder("[");
PlanAPI planAPI = PlanAPI.getInstance();
long now = System.currentTimeMillis();
players.sort(new PlayerContainerLastPlayedComparator());
int currentPlayerNumber = 0;
for (PlayerContainer player : players) {
if (currentPlayerNumber >= maxPlayers) {
break;
}
UUID playerUUID = player.getValue(PlayerKeys.UUID).orElse(null);
if (playerUUID == null) {
continue;
}
appendPlayerData(dataJSON, planAPI, now, player);
appendExtensionData(dataJSON, extensionData.get(playerUUID));
currentPlayerNumber++;
}
return dataJSON.append(']').toString();
}
private void appendPlayerData(StringBuilder dataJSON, PlanAPI planAPI, long now, PlayerContainer player) {
String name = player.getValue(PlayerKeys.NAME).orElse("Unknown");
String url = planAPI.getPlayerInspectPageLink(name);
SessionsMutator sessionsMutator = SessionsMutator.forContainer(player);
int loginTimes = sessionsMutator.count();
long playtime = sessionsMutator.toPlaytime();
long registered = player.getValue(PlayerKeys.REGISTERED).orElse(0L);
long lastSeen = sessionsMutator.toLastSeen();
ActivityIndex activityIndex = player.getActivityIndex(now, activeMsThreshold, activeLoginThreshold);
boolean isBanned = player.getValue(PlayerKeys.BANNED).orElse(false);
String activityString = activityIndex.getFormattedValue(decimalFormatter)
+ (isBanned ? " (<b>Banned</b>)" : " (" + activityIndex.getGroup() + ")");
String geolocation = GeoInfoMutator.forContainer(player).mostRecent().map(GeoInfo::getGeolocation).orElse("-");
Html link = openPlayerPageInNewTab ? Html.LINK_EXTERNAL : Html.LINK;
appendData(dataJSON,
'"' + link.parse(url, name) + '"',
"{display:\"" + activityString + "\",sort: " + activityIndex + '}',
"{display:\"" + numberFormatters.get(FormatType.TIME_MILLISECONDS).apply(playtime) + "\",sort: " + playtime + '}',
loginTimes,
"{display:\"" + numberFormatters.get(FormatType.DATE_YEAR).apply(registered) + "\",sort: " + registered + '}',
"{display:\"" + numberFormatters.get(FormatType.DATE_YEAR).apply(lastSeen) + "\",sort: " + lastSeen + '}',
geolocation
);
}
private void appendExtensionData(StringBuilder dataJSON, ExtensionTabData tabData) {
for (ExtensionDescriptive descriptive : extensionDescriptives) {
dataJSON.append(',');
String key = descriptive.getName();
// If it's a double, append a double
Optional<ExtensionDoubleData> doubleValue = tabData.getDouble(key);
if (doubleValue.isPresent()) {
dataJSON.append(doubleValue.get().getFormattedValue(decimalFormatter));
continue;
}
// If it's a percentage, append a percentage
Optional<ExtensionDoubleData> percentageValue = tabData.getPercentage(key);
if (percentageValue.isPresent()) {
dataJSON.append(percentageValue.get().getFormattedValue(percentageFormatter));
continue;
}
Optional<ExtensionNumberData> numberValue = tabData.getNumber(key);
if (numberValue.isPresent()) {
ExtensionNumberData numberData = numberValue.get();
FormatType formatType = numberData.getFormatType();
if (formatType == FormatType.NONE) {
// If it's a number, append a number
dataJSON.append(numberData.getFormattedValue(numberFormatters.get(formatType)));
} else {
// If it's a formatted number, append a formatted number and sort by the number value
dataJSON.append("{display: \"").append(numberData.getFormattedValue(numberFormatters.get(formatType)))
.append("\",sort: ").append(numberData.getRawValue()).append('}');
}
continue;
}
// If it's a String append a String, otherwise the player has no value for this extension provider.
String stringValue = tabData.getString(key).map(ExtensionStringData::getFormattedValue).orElse("-");
dataJSON.append('"').append(stringValue).append('"');
}
}
private String getValue(ExtensionTabData tabData, String key) {
tabData.getPercentage(key).map(data -> data.getFormattedValue(percentageFormatter));
tabData.getNumber(key).map(data -> data.getFormattedValue(numberFormatters.get(data.getFormatType())));
tabData.getString(key).map(ExtensionStringData::getFormattedValue);
return "-";
}
private void appendData(StringBuilder dataJSON, Serializable... dataRows) {
int max = dataRows.length;
for (int i = 0; i < max; i++) {
dataJSON.append("{").append(dataRows[i]).append("}");
if (i < max - 1) {
dataJSON.append(',');
}
}
}
private String parseColumnHeaders() {
StringBuilder columnHeaders = new StringBuilder("[");
appendDataHeaders(columnHeaders,
Icon.called("user") + " Name",
Icon.called("check") + " Activity Index",
Icon.called("clock").of(Family.REGULAR) + " Playtime",
Icon.called("calendar-plus").of(Family.REGULAR) + " Sessions",
Icon.called("user-plus") + " Registered",
Icon.called("calendar-check").of(Family.REGULAR) + " Last Seen",
Icon.called("globe") + " Geolocation"
);
appendExtensionHeaders(columnHeaders);
return columnHeaders.append(']').toString();
}
private void appendDataHeaders(StringBuilder columnHeaders, Serializable... headers) {
int max = headers.length;
for (int i = 0; i < max; i++) {
columnHeaders.append("{title: \"").append(headers[i]).append("\"}");
if (i < max - 1) {
columnHeaders.append(',');
}
}
}
private void appendExtensionHeaders(StringBuilder columnHeaders) {
for (ExtensionDescriptive provider : extensionDescriptives) {
columnHeaders.append(',');
columnHeaders.append(Icon.fromExtensionIcon(provider.getIcon().setColor(Color.NONE)).toHtml()).append(' ').append(provider.getText());
}
}
}