mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-02-08 16:31:21 +01:00
Players online & Day by Day graph to network page
This commit is contained in:
parent
20e8a13ef9
commit
3d1e32fe88
@ -51,6 +51,11 @@ public class MutatorFunctions {
|
||||
filled.put(point.getKey(), point.getValue());
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if (lastX != null && now - lastX > accuracy) {
|
||||
addMissing(lastX, now, filled, accuracy, replacement);
|
||||
}
|
||||
|
||||
return filled;
|
||||
}
|
||||
|
||||
@ -62,6 +67,32 @@ public class MutatorFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Point> addMissing(List<Point> points, long accuracy, Integer replacement) {
|
||||
if (Verify.isEmpty(points)) return points;
|
||||
|
||||
List<Point> filled = new ArrayList<>();
|
||||
Long lastX = null;
|
||||
for (Point point : points) {
|
||||
long date = (long) point.getX();
|
||||
|
||||
if (lastX != null && date - lastX > accuracy) {
|
||||
addMissing(lastX, date, filled, accuracy, replacement);
|
||||
}
|
||||
lastX = date;
|
||||
filled.add(point);
|
||||
}
|
||||
|
||||
return filled;
|
||||
}
|
||||
|
||||
private static void addMissing(long from, long to, List<Point> points, long accuracy, Integer replacement) {
|
||||
long iterate = from;
|
||||
while (iterate < to) {
|
||||
points.add(new Point(iterate, replacement));
|
||||
iterate += accuracy;
|
||||
}
|
||||
}
|
||||
|
||||
public static int average(Map<Long, Integer> map) {
|
||||
return (int) map.values().stream()
|
||||
.mapToInt(i -> i)
|
||||
|
@ -143,6 +143,46 @@ public class PlayerCountQueries {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a EpochMs - Count map of unique players on ALL servers.
|
||||
*
|
||||
* @param after After epoch ms
|
||||
* @param before Before epoch ms
|
||||
* @param timeZoneOffset Offset from {@link java.util.TimeZone#getOffset(long)}, applied to the dates before grouping.
|
||||
* @return Map: Epoch ms (Accuracy of a day) - How many unique players played that day
|
||||
*/
|
||||
public static Query<NavigableMap<Long, Integer>> uniquePlayerCounts(long after, long before, long timeZoneOffset) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
String selectUniquePlayersPerDay = SELECT +
|
||||
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) +
|
||||
"*1000 as date," +
|
||||
"COUNT(DISTINCT " + SessionsTable.USER_UUID + ") as player_count" +
|
||||
FROM + SessionsTable.TABLE_NAME +
|
||||
WHERE + SessionsTable.SESSION_END + "<=?" +
|
||||
AND + SessionsTable.SESSION_START + ">=?" +
|
||||
GROUP_BY + "date";
|
||||
|
||||
return database.query(new QueryStatement<NavigableMap<Long, Integer>>(selectUniquePlayersPerDay, 100) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setLong(1, timeZoneOffset);
|
||||
statement.setLong(2, before);
|
||||
statement.setLong(3, after);
|
||||
}
|
||||
|
||||
@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("player_count"));
|
||||
}
|
||||
return uniquePerDay;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Integer> averageUniquePlayerCount(long after, long before, long timeZoneOffset, UUID serverUUID) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
@ -236,6 +276,38 @@ public class PlayerCountQueries {
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<NavigableMap<Long, Integer>> newPlayerCounts(long after, long before, long timeZoneOffset) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
String selectNewPlayersQuery = SELECT +
|
||||
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + UserInfoTable.REGISTERED + "+?)/1000"))) +
|
||||
"*1000 as date," +
|
||||
"COUNT(1) as player_count" +
|
||||
FROM + UsersTable.TABLE_NAME +
|
||||
WHERE + UsersTable.REGISTERED + "<=?" +
|
||||
AND + UsersTable.REGISTERED + ">=?" +
|
||||
GROUP_BY + "date";
|
||||
|
||||
return database.query(new QueryStatement<NavigableMap<Long, Integer>>(selectNewPlayersQuery, 100) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setLong(1, timeZoneOffset);
|
||||
statement.setLong(2, before);
|
||||
statement.setLong(3, after);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableMap<Long, Integer> processResults(ResultSet set) throws SQLException {
|
||||
NavigableMap<Long, Integer> newPerDay = new TreeMap<>();
|
||||
while (set.next()) {
|
||||
newPerDay.put(set.getLong("date"), set.getInt("player_count"));
|
||||
}
|
||||
return newPerDay;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Integer> averageNewPlayerCount(long after, long before, long timeZoneOffset, UUID serverUUID) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
|
@ -118,6 +118,67 @@ public class TPSQueries {
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<List<DateObj<Integer>>> fetchPlayersOnlineOfServer(long after, long before, UUID serverUUID) {
|
||||
String sql = SELECT + ServerTable.SERVER_UUID + ',' + DATE + ',' + PLAYERS_ONLINE +
|
||||
FROM + TABLE_NAME +
|
||||
INNER_JOIN + ServerTable.TABLE_NAME + " on " + ServerTable.TABLE_NAME + '.' + ServerTable.SERVER_ID + '=' + SERVER_ID +
|
||||
WHERE + ServerTable.SERVER_UUID + "=?" +
|
||||
AND + DATE + "<?" +
|
||||
AND + DATE + ">?";
|
||||
return new QueryStatement<List<DateObj<Integer>>>(sql, 1000) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setString(1, serverUUID.toString());
|
||||
statement.setLong(2, before);
|
||||
statement.setLong(3, after);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DateObj<Integer>> processResults(ResultSet set) throws SQLException {
|
||||
List<DateObj<Integer>> ofServer = new ArrayList<>();
|
||||
|
||||
while (set.next()) {
|
||||
ofServer.add(new DateObj<>(set.getLong(DATE), set.getInt(PLAYERS_ONLINE)));
|
||||
}
|
||||
|
||||
return ofServer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Map<UUID, List<DateObj<Integer>>>> fetchPlayersOnlineOfAllServersBut(long after, long before, UUID leaveOut) {
|
||||
String sql = SELECT + ServerTable.SERVER_UUID + ',' + DATE + ',' + PLAYERS_ONLINE +
|
||||
FROM + TABLE_NAME +
|
||||
INNER_JOIN + ServerTable.TABLE_NAME + " on " + ServerTable.TABLE_NAME + '.' + ServerTable.SERVER_ID + '=' + SERVER_ID +
|
||||
WHERE + ServerTable.SERVER_UUID + "!=?" +
|
||||
AND + ServerTable.INSTALLED + "=?" +
|
||||
AND + DATE + "<?" +
|
||||
AND + DATE + ">?";
|
||||
return new QueryStatement<Map<UUID, List<DateObj<Integer>>>>(sql, 1000) {
|
||||
@Override
|
||||
public void prepare(PreparedStatement statement) throws SQLException {
|
||||
statement.setString(1, leaveOut.toString());
|
||||
statement.setBoolean(2, true);
|
||||
statement.setLong(3, before);
|
||||
statement.setLong(4, after);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<UUID, List<DateObj<Integer>>> processResults(ResultSet set) throws SQLException {
|
||||
Map<UUID, List<DateObj<Integer>>> byServer = new HashMap<>();
|
||||
|
||||
while (set.next()) {
|
||||
UUID serverUUID = UUID.fromString(set.getString(ServerTable.SERVER_UUID));
|
||||
List<DateObj<Integer>> ofServer = byServer.getOrDefault(serverUUID, new ArrayList<>());
|
||||
ofServer.add(new DateObj<>(set.getLong(DATE), set.getInt(PLAYERS_ONLINE)));
|
||||
byServer.put(serverUUID, ofServer);
|
||||
}
|
||||
|
||||
return byServer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Map<Integer, List<TPS>>> fetchPlayerOnlineDataOfServers(Collection<Server> servers) {
|
||||
if (servers.isEmpty()) {
|
||||
return db -> new HashMap<>();
|
||||
|
@ -30,11 +30,14 @@ import com.djrapitops.plan.db.access.queries.analysis.PlayerCountQueries;
|
||||
import com.djrapitops.plan.db.access.queries.objects.*;
|
||||
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.html.graphs.Graphs;
|
||||
import com.djrapitops.plan.utilities.html.graphs.bar.BarGraph;
|
||||
import com.djrapitops.plan.utilities.html.graphs.line.LineGraph;
|
||||
import com.djrapitops.plan.utilities.html.graphs.line.LineGraphFactory;
|
||||
import com.djrapitops.plan.utilities.html.graphs.line.PingGraph;
|
||||
import com.djrapitops.plan.utilities.html.graphs.line.Point;
|
||||
import com.djrapitops.plan.utilities.html.graphs.pie.Pie;
|
||||
import com.djrapitops.plan.utilities.html.graphs.pie.WorldPie;
|
||||
import com.djrapitops.plan.utilities.html.graphs.special.WorldMap;
|
||||
@ -45,6 +48,7 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Perses Graph related Data JSON.
|
||||
@ -74,8 +78,10 @@ public class GraphJSONParser {
|
||||
public String performanceGraphJSON(UUID serverUUID) {
|
||||
Database db = dbSystem.getDatabase();
|
||||
LineGraphFactory lineGraphs = graphs.line();
|
||||
long now = System.currentTimeMillis();
|
||||
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L);
|
||||
TPSMutator tpsMutator = new TPSMutator(db.query(TPSQueries.fetchTPSDataOfServer(serverUUID)))
|
||||
.filterDataBetween(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(180L), System.currentTimeMillis());
|
||||
.filterDataBetween(halfYearAgo, now);
|
||||
return '{' +
|
||||
"\"playersOnline\":" + lineGraphs.playersOnlineGraph(tpsMutator).toHighChartsSeries() +
|
||||
",\"tps\":" + lineGraphs.tpsGraph(tpsMutator).toHighChartsSeries() +
|
||||
@ -87,6 +93,18 @@ public class GraphJSONParser {
|
||||
'}';
|
||||
}
|
||||
|
||||
public String playersOnlineGraph(UUID serverUUID) {
|
||||
Database db = dbSystem.getDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L);
|
||||
Boolean displayGaps = config.get(DisplaySettings.GAPS_IN_GRAPH_DATA);
|
||||
|
||||
List<Point> points = db.query(TPSQueries.fetchPlayersOnlineOfServer(halfYearAgo, now, serverUUID)).stream()
|
||||
.map(point -> new Point(point.getDate(), point.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
return "{\"playersOnline\":" + new LineGraph(points, displayGaps).toHighChartsSeries() + '}';
|
||||
}
|
||||
|
||||
public String uniqueAndNewGraphJSON(UUID serverUUID) {
|
||||
Database db = dbSystem.getDatabase();
|
||||
LineGraphFactory lineGraphs = graphs.line();
|
||||
@ -112,6 +130,31 @@ public class GraphJSONParser {
|
||||
'}';
|
||||
}
|
||||
|
||||
public String uniqueAndNewGraphJSON() {
|
||||
Database db = dbSystem.getDatabase();
|
||||
LineGraphFactory lineGraphs = graphs.line();
|
||||
long now = System.currentTimeMillis();
|
||||
long halfYearAgo = now - TimeUnit.DAYS.toMillis(180L);
|
||||
NavigableMap<Long, Integer> uniquePerDay = db.query(
|
||||
PlayerCountQueries.uniquePlayerCounts(halfYearAgo, now, timeZone.getOffset(now))
|
||||
);
|
||||
NavigableMap<Long, Integer> newPerDay = db.query(
|
||||
PlayerCountQueries.newPlayerCounts(halfYearAgo, now, timeZone.getOffset(now))
|
||||
);
|
||||
|
||||
return "{\"uniquePlayers\":" +
|
||||
lineGraphs.lineGraph(MutatorFunctions.toPointsWithRemovedOffset(
|
||||
MutatorFunctions.addMissing(uniquePerDay, TimeUnit.DAYS.toMillis(1L), 0),
|
||||
timeZone
|
||||
)).toHighChartsSeries() +
|
||||
",\"newPlayers\":" +
|
||||
lineGraphs.lineGraph(MutatorFunctions.toPointsWithRemovedOffset(
|
||||
MutatorFunctions.addMissing(newPerDay, TimeUnit.DAYS.toMillis(1L), 0),
|
||||
timeZone
|
||||
)).toHighChartsSeries() +
|
||||
'}';
|
||||
}
|
||||
|
||||
public String serverCalendarJSON(UUID serverUUID) {
|
||||
Database db = dbSystem.getDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
|
@ -69,6 +69,8 @@ public class GraphsJSONHandler implements PageHandler {
|
||||
switch (type) {
|
||||
case "performance":
|
||||
return new JSONResponse(graphJSON.performanceGraphJSON(serverUUID));
|
||||
case "playersOnline":
|
||||
return new JSONResponse(graphJSON.playersOnlineGraph(serverUUID));
|
||||
case "uniqueAndNew":
|
||||
return new JSONResponse(graphJSON.uniqueAndNewGraphJSON(serverUUID));
|
||||
case "serverCalendar":
|
||||
@ -92,6 +94,8 @@ public class GraphsJSONHandler implements PageHandler {
|
||||
switch (type) {
|
||||
case "activity":
|
||||
return new JSONResponse(graphJSON.activityGraphsJSONAsMap());
|
||||
case "uniqueAndNew":
|
||||
return new JSONResponse(graphJSON.uniqueAndNewGraphJSON());
|
||||
default:
|
||||
throw new BadRequestException("unknown 'type' parameter: " + type);
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.djrapitops.plan.utilities.html.graphs.line;
|
||||
|
||||
import com.djrapitops.plan.data.store.mutators.MutatorFunctions;
|
||||
import com.djrapitops.plan.utilities.html.graphs.HighChart;
|
||||
|
||||
import java.util.List;
|
||||
@ -62,6 +63,13 @@ public class LineGraph implements HighChart {
|
||||
return arrayBuilder.toString();
|
||||
}
|
||||
|
||||
public List<Point> getPoints() {
|
||||
if (displayGaps) {
|
||||
return MutatorFunctions.addMissing(points, TimeUnit.MINUTES.toMillis(1L), null);
|
||||
}
|
||||
return points;
|
||||
}
|
||||
|
||||
private void addMissingPoints(StringBuilder arrayBuilder, Long lastX, long date) {
|
||||
long iterate = lastX + TimeUnit.MINUTES.toMillis(1L);
|
||||
while (iterate < date) {
|
||||
|
@ -31,6 +31,7 @@ import com.djrapitops.plan.utilities.formatting.Formatters;
|
||||
import com.djrapitops.plan.utilities.formatting.PlaceholderReplacer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Html String parser for /network page.
|
||||
@ -71,16 +72,23 @@ public class NetworkPage implements Page {
|
||||
try {
|
||||
PlaceholderReplacer placeholders = new PlaceholderReplacer();
|
||||
|
||||
UUID serverUUID = serverInfo.getServerUUID();
|
||||
placeholders.put("networkDisplayName", config.get(ProxySettings.NETWORK_NAME));
|
||||
placeholders.put("serverUUID", serverUUID.toString());
|
||||
|
||||
placeholders.put("gmPieColors", theme.getValue(ThemeVal.GRAPH_GM_PIE));
|
||||
placeholders.put("playersGraphColor", theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE));
|
||||
placeholders.put("worldMapColLow", theme.getValue(ThemeVal.WORLD_MAP_LOW));
|
||||
placeholders.put("worldMapColHigh", theme.getValue(ThemeVal.WORLD_MAP_HIGH));
|
||||
placeholders.put("maxPingColor", theme.getValue(ThemeVal.GRAPH_MAX_PING));
|
||||
placeholders.put("minPingColor", theme.getValue(ThemeVal.GRAPH_MIN_PING));
|
||||
placeholders.put("avgPingColor", theme.getValue(ThemeVal.GRAPH_AVG_PING));
|
||||
placeholders.put("timeZone", config.getTimeZoneOffsetHours());
|
||||
|
||||
placeholders.put("update", versionCheckSystem.getUpdateHtml().orElse(""));
|
||||
|
||||
List<ExtensionServerData> extensionData = dbSystem.getDatabase()
|
||||
.query(new ExtensionServerDataQuery(serverInfo.getServerUUID()));
|
||||
.query(new ExtensionServerDataQuery(serverUUID));
|
||||
ServerPluginTabs pluginTabs = new ServerPluginTabs(extensionData, formatters);
|
||||
|
||||
String nav = pluginTabs.getNav();
|
||||
|
@ -154,18 +154,37 @@
|
||||
<!-- Online Activity Chart -->
|
||||
<div class="col-xl-9 col-lg-9 col-sm-12">
|
||||
<div class="card shadow mb-4">
|
||||
<div
|
||||
class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
||||
<h6 class="m-0 font-weight-bold col-black"><i
|
||||
class="fas fa-fw fa-chart-area col-blue"></i>
|
||||
Network Online Activity</h6>
|
||||
</div>
|
||||
<div class="chart-area">
|
||||
<canvas id="myAreaChart"></canvas>
|
||||
<ul class="nav nav-tabs" id="onlineActivityTab" role="tablist">
|
||||
<li class="nav-item">
|
||||
<a aria-controls="profile" aria-selected="false"
|
||||
class="nav-link active col-black"
|
||||
data-toggle="tab"
|
||||
href="#online-tab" id="online-online-tab" role="tab">
|
||||
<i class="fas fa-fw fa-chart-area col-blue"></i> Network Online Activity</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a aria-controls="home" aria-selected="true" class="nav-link col-black"
|
||||
data-toggle="tab" href="#unique-tab" id="online-unique-tab" role="tab"><i
|
||||
class="fa fa-fw fa-chart-area col-blue"></i> Day by Day</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="onlineActivityTabContent">
|
||||
<div aria-labelledby="online-online-tab" class="tab-pane fade show active"
|
||||
id="online-tab"
|
||||
role="tabpanel">
|
||||
<div class="chart-area" id="playersOnlineChart"><span class="loader"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div aria-labelledby="online-unique-tab" class="tab-pane fade"
|
||||
id="unique-tab"
|
||||
role="tabpanel">
|
||||
<div class="chart-area" id="uniqueChart"><span class="loader"></span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Players -->
|
||||
<div class="col-xl-3 col-lg-3 col-sm-12">
|
||||
<div class="card shadow mb-4">
|
||||
@ -1289,6 +1308,72 @@
|
||||
jsonRequest("../v1/network/playerbaseOverview", loadPlayerbaseOverviewValues);
|
||||
setLoadingText('Rendering graphs..');
|
||||
|
||||
var v = {
|
||||
colors: {
|
||||
playersOnline: '${playersGraphColor}',
|
||||
newPlayers: '#8BC34A',
|
||||
geolocationsLow: '${worldMapColLow}',
|
||||
geolocationsHigh: '${worldMapColHigh}',
|
||||
maxPing: '${maxPingColor}',
|
||||
minPing: '${minPingColor}',
|
||||
avgPing: '${avgPingColor}'
|
||||
}
|
||||
};
|
||||
|
||||
// HighCharts Series
|
||||
var s = {
|
||||
name: {
|
||||
playersOnline: 'Players Online',
|
||||
uniquePlayers: 'Unique Players',
|
||||
newPlayers: 'New Players',
|
||||
maxPing: 'Worst Ping',
|
||||
minPing: 'Best Ping',
|
||||
avgPing: 'Average Ping'
|
||||
},
|
||||
tooltip: {
|
||||
twoDecimals: {
|
||||
valueDecimals: 2
|
||||
},
|
||||
zeroDecimals: {
|
||||
valueDecimals: 0
|
||||
}
|
||||
},
|
||||
type: {
|
||||
areaSpline: 'areaspline',
|
||||
spline: 'spline'
|
||||
}
|
||||
};
|
||||
|
||||
jsonRequest("../v1/graph?type=playersOnline&server=${serverUUID}", function (data, error) {
|
||||
if (data) {
|
||||
var series = {
|
||||
playersOnline: {
|
||||
name: s.name.playersOnline, type: s.type.areaSpline, tooltip: s.tooltip.zeroDecimals,
|
||||
data: data.playersOnline, color: v.colors.playersOnline, yAxis: 0
|
||||
}
|
||||
};
|
||||
playersChart('playersOnlineChart', series.playersOnline, 2);
|
||||
} else if (error) {
|
||||
$('#playersOnlineChart').text("Failed to load graph data: " + error);
|
||||
}
|
||||
});
|
||||
|
||||
jsonRequest("../v1/graph?type=uniqueAndNew", function (data, error) {
|
||||
if (data) {
|
||||
var uniquePlayers = {
|
||||
name: s.name.uniquePlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals,
|
||||
data: data.uniquePlayers, color: v.colors.playersOnline
|
||||
};
|
||||
var newPlayers = {
|
||||
name: s.name.newPlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals,
|
||||
data: data.newPlayers, color: v.colors.newPlayers
|
||||
};
|
||||
lineChart('uniqueChart', [uniquePlayers, newPlayers]);
|
||||
} else if (error) {
|
||||
$('#uniqueChart').text("Failed to load graph data: " + error)
|
||||
}
|
||||
});
|
||||
|
||||
jsonRequest("../v1/graph?type=activity", function (json, error) {
|
||||
if (json) {
|
||||
activityPie('activityPie', {
|
||||
|
Loading…
Reference in New Issue
Block a user