mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-29 20:48:01 +01:00
Implemented a Hour by Hour graph
- Unique players, New players per hour Affects issues: - #1232
This commit is contained in:
parent
93dc1bbb9e
commit
fb2bf97cc6
@ -134,17 +134,33 @@ public class GraphJSONCreator {
|
||||
PlayerCountQueries.newPlayerCounts(halfYearAgo, now, timeZoneOffset, serverUUID)
|
||||
);
|
||||
|
||||
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay);
|
||||
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay, TimeUnit.DAYS.toMillis(1L));
|
||||
}
|
||||
|
||||
public String createUniqueAndNewJSON(LineGraphFactory lineGraphs, NavigableMap<Long, Integer> uniquePerDay, NavigableMap<Long, Integer> newPerDay) {
|
||||
public String hourlyUniqueAndNewGraphJSON(UUID 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<Long, Integer> uniquePerDay = db.query(
|
||||
PlayerCountQueries.hourlyUniquePlayerCounts(weekAgo, now, timeZoneOffset, serverUUID)
|
||||
);
|
||||
NavigableMap<Long, Integer> newPerDay = db.query(
|
||||
PlayerCountQueries.newPlayerCounts(weekAgo, now, timeZoneOffset, serverUUID)
|
||||
);
|
||||
|
||||
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay, TimeUnit.HOURS.toMillis(1L));
|
||||
}
|
||||
|
||||
public String createUniqueAndNewJSON(LineGraphFactory lineGraphs, NavigableMap<Long, Integer> uniquePerDay, NavigableMap<Long, Integer> newPerDay, long gapFillPeriod) {
|
||||
return "{\"uniquePlayers\":" +
|
||||
lineGraphs.lineGraph(MutatorFunctions.toPoints(
|
||||
MutatorFunctions.addMissing(uniquePerDay, TimeUnit.DAYS.toMillis(1L), 0)
|
||||
MutatorFunctions.addMissing(uniquePerDay, gapFillPeriod, 0)
|
||||
)).toHighChartsSeries() +
|
||||
",\"newPlayers\":" +
|
||||
lineGraphs.lineGraph(MutatorFunctions.toPoints(
|
||||
MutatorFunctions.addMissing(newPerDay, TimeUnit.DAYS.toMillis(1L), 0)
|
||||
MutatorFunctions.addMissing(newPerDay, gapFillPeriod, 0)
|
||||
)).toHighChartsSeries() +
|
||||
",\"colors\":{" +
|
||||
"\"playersOnline\":\"" + theme.getValue(ThemeVal.GRAPH_PLAYERS_ONLINE) + "\"," +
|
||||
@ -165,7 +181,23 @@ public class GraphJSONCreator {
|
||||
PlayerCountQueries.newPlayerCounts(halfYearAgo, now, timeZoneOffset)
|
||||
);
|
||||
|
||||
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay);
|
||||
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<Long, Integer> uniquePerDay = db.query(
|
||||
PlayerCountQueries.hourlyUniquePlayerCounts(weekAgo, now, timeZoneOffset)
|
||||
);
|
||||
NavigableMap<Long, Integer> newPerDay = db.query(
|
||||
PlayerCountQueries.hourlyNewPlayerCounts(weekAgo, now, timeZoneOffset)
|
||||
);
|
||||
|
||||
return createUniqueAndNewJSON(lineGraphs, uniquePerDay, newPerDay, TimeUnit.HOURS.toMillis(1L));
|
||||
}
|
||||
|
||||
public String serverCalendarJSON(UUID serverUUID) {
|
||||
|
@ -32,6 +32,7 @@ public enum DataID {
|
||||
GRAPH_PERFORMANCE,
|
||||
GRAPH_ONLINE,
|
||||
GRAPH_UNIQUE_NEW,
|
||||
GRAPH_HOURLY_UNIQUE_NEW,
|
||||
GRAPH_CALENDAR,
|
||||
GRAPH_WORLD_PIE,
|
||||
GRAPH_WORLD_MAP,
|
||||
|
@ -92,6 +92,8 @@ public class GraphsJSONResolver implements Resolver {
|
||||
return DataID.GRAPH_ONLINE;
|
||||
case "uniqueAndNew":
|
||||
return DataID.GRAPH_UNIQUE_NEW;
|
||||
case "hourlyUniqueAndNew":
|
||||
return DataID.GRAPH_HOURLY_UNIQUE_NEW;
|
||||
case "serverCalendar":
|
||||
return DataID.GRAPH_CALENDAR;
|
||||
case "worldPie":
|
||||
@ -119,6 +121,8 @@ public class GraphsJSONResolver implements Resolver {
|
||||
return graphJSON.playersOnlineGraph(serverUUID);
|
||||
case GRAPH_UNIQUE_NEW:
|
||||
return graphJSON.uniqueAndNewGraphJSON(serverUUID);
|
||||
case GRAPH_HOURLY_UNIQUE_NEW:
|
||||
return graphJSON.hourlyUniqueAndNewGraphJSON(serverUUID);
|
||||
case GRAPH_CALENDAR:
|
||||
return graphJSON.serverCalendarJSON(serverUUID);
|
||||
case GRAPH_WORLD_PIE:
|
||||
@ -142,6 +146,8 @@ public class GraphsJSONResolver implements Resolver {
|
||||
return graphJSON.activityGraphsJSONAsMap();
|
||||
case GRAPH_UNIQUE_NEW:
|
||||
return graphJSON.uniqueAndNewGraphJSON();
|
||||
case GRAPH_HOURLY_UNIQUE_NEW:
|
||||
return graphJSON.hourlyUniqueAndNewGraphJSON();
|
||||
case GRAPH_SERVER_PIE:
|
||||
return graphJSON.serverPreferencePieJSONAsMap();
|
||||
case GRAPH_WORLD_MAP:
|
||||
|
@ -166,6 +166,49 @@ public class PlayerCountQueries {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a EpochMs - Count map of unique players on a server.
|
||||
*
|
||||
* @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.
|
||||
* @param serverUUID UUID of the Plan server
|
||||
* @return Map: Epoch ms (Start of day at 0 AM, no offset) - How many unique players played that day
|
||||
*/
|
||||
public static Query<NavigableMap<Long, Integer>> hourlyUniquePlayerCounts(long after, long before, long timeZoneOffset, UUID serverUUID) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
String selectUniquePlayersPerDay = SELECT +
|
||||
sql.dateToEpochSecond(sql.dateToHourStamp(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 + ">=?" +
|
||||
AND + SessionsTable.SERVER_UUID + "=?" +
|
||||
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);
|
||||
statement.setString(4, serverUUID.toString());
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a EpochMs - Count map of unique players on ALL servers.
|
||||
*
|
||||
@ -206,6 +249,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 (Start of day at 0 AM, no offset) - How many unique players played that day
|
||||
*/
|
||||
public static Query<NavigableMap<Long, Integer>> hourlyUniquePlayerCounts(long after, long before, long timeZoneOffset) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
String selectUniquePlayersPerDay = SELECT +
|
||||
sql.dateToEpochSecond(sql.dateToHourStamp(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();
|
||||
@ -324,6 +407,49 @@ public class PlayerCountQueries {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a EpochMs - Count map of new players on a server.
|
||||
*
|
||||
* @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.
|
||||
* @param serverUUID UUID of the Plan server
|
||||
* @return Map: Epoch ms (Start of day at 0 AM, no offset) - How many new players joined that day
|
||||
*/
|
||||
public static Query<NavigableMap<Long, Integer>> hourlyNewPlayerCounts(long after, long before, long timeZoneOffset, UUID serverUUID) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
String selectNewPlayersQuery = SELECT +
|
||||
sql.dateToEpochSecond(sql.dateToHourStamp(sql.epochSecondToDate('(' + UserInfoTable.REGISTERED + "+?)/1000"))) +
|
||||
"*1000 as date," +
|
||||
"COUNT(1) as player_count" +
|
||||
FROM + UserInfoTable.TABLE_NAME +
|
||||
WHERE + UserInfoTable.REGISTERED + "<=?" +
|
||||
AND + UserInfoTable.REGISTERED + ">=?" +
|
||||
AND + UserInfoTable.SERVER_UUID + "=?" +
|
||||
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);
|
||||
statement.setString(4, serverUUID.toString());
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a EpochMs - Count map of new players on a server.
|
||||
*
|
||||
@ -364,6 +490,46 @@ public class PlayerCountQueries {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a EpochMs - Count map of new players on a server.
|
||||
*
|
||||
* @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 (Start of day at 0 AM, no offset) - How many new players joined that day
|
||||
*/
|
||||
public static Query<NavigableMap<Long, Integer>> hourlyNewPlayerCounts(long after, long before, long timeZoneOffset) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
String selectNewPlayersQuery = SELECT +
|
||||
sql.dateToEpochSecond(sql.dateToHourStamp(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();
|
||||
|
@ -79,6 +79,8 @@ public abstract class Sql {
|
||||
|
||||
public abstract String dateToDayStamp(String sql);
|
||||
|
||||
public abstract String dateToHourStamp(String sql);
|
||||
|
||||
public abstract String dateToDayOfWeek(String sql);
|
||||
|
||||
public abstract String dateToHour(String sql);
|
||||
@ -101,6 +103,11 @@ public abstract class Sql {
|
||||
return "DATE(" + sql + ')';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dateToHourStamp(String sql) {
|
||||
return "DATE_FORMAT(" + sql + ",'%Y-%m-%d %H:00:00')";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dateToDayOfWeek(String sql) {
|
||||
return "DAYOFWEEK(" + sql + ')';
|
||||
@ -130,6 +137,11 @@ public abstract class Sql {
|
||||
return "DAY_OF_WEEK(" + sql + ')';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dateToHourStamp(String sql) {
|
||||
return "DATE_FORMAT(" + sql + ",'yyyy-MM-dd HH:00:00')";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dateToHour(String sql) {
|
||||
return "HOUR(" + sql + ')';
|
||||
@ -154,6 +166,11 @@ public abstract class Sql {
|
||||
return "strftime('%Y-%m-%d'," + sql + ')';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dateToHourStamp(String sql) {
|
||||
return "strftime('%Y-%m-%d %H:00:00'," + sql + ')';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String dateToDayOfWeek(String sql) {
|
||||
return "strftime('%w'," + sql + ")+1";
|
||||
|
@ -162,6 +162,12 @@
|
||||
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>
|
||||
<li class="nav-item">
|
||||
<a aria-controls="home" aria-selected="true" class="nav-link col-black"
|
||||
data-toggle="tab" href="#hourly-unique-tab" id="online-hourly-unique-tab"
|
||||
role="tab"><i
|
||||
class="fa fa-fw fa-chart-area col-blue"></i> Hour by Hour</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="onlineActivityTabContent">
|
||||
<div aria-labelledby="online-online-tab" class="tab-pane show active"
|
||||
@ -175,6 +181,11 @@
|
||||
role="tabpanel">
|
||||
<div class="chart-area" id="uniqueChart"><span class="loader"></span></div>
|
||||
</div>
|
||||
<div aria-labelledby="online-unique-tab" class="tab-pane"
|
||||
id="hourly-unique-tab" role="tabpanel">
|
||||
<div class="chart-area" id="hourlyUniqueChart"><span class="loader"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -887,6 +898,22 @@
|
||||
}
|
||||
});
|
||||
|
||||
jsonRequest("./v1/graph?type=hourlyUniqueAndNew", function (json, error) {
|
||||
if (json) {
|
||||
var uniquePlayers = {
|
||||
name: s.name.uniquePlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals,
|
||||
data: json.uniquePlayers, color: v.colors.playersOnline
|
||||
};
|
||||
var newPlayers = {
|
||||
name: s.name.newPlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals,
|
||||
data: json.newPlayers, color: v.colors.newPlayers
|
||||
};
|
||||
dayByDay('hourlyUniqueChart', [uniquePlayers, newPlayers]);
|
||||
} else if (error) {
|
||||
$('#uniqueChart').text("Failed to load graph data: " + error)
|
||||
}
|
||||
});
|
||||
|
||||
jsonRequest("./v1/graph?type=serverPie", function (json, error) {
|
||||
if (json) {
|
||||
serverPieSeries = {
|
||||
|
@ -333,6 +333,12 @@
|
||||
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>
|
||||
<li class="nav-item">
|
||||
<a aria-controls="home" aria-selected="true" class="nav-link col-black"
|
||||
data-toggle="tab" href="#hourly-unique-tab" id="online-hourly-unique-tab"
|
||||
role="tab"><i
|
||||
class="fa fa-fw fa-chart-area col-blue"></i> Hour by Hour</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a aria-controls="profile" aria-selected="false" class="nav-link col-black"
|
||||
data-toggle="tab"
|
||||
@ -348,10 +354,14 @@
|
||||
</ul>
|
||||
<div class="tab-content" id="onlineActivityTabContent">
|
||||
<div aria-labelledby="online-unique-tab" class="tab-pane show active"
|
||||
id="unique-tab"
|
||||
role="tabpanel">
|
||||
id="unique-tab" role="tabpanel">
|
||||
<div class="chart-area" id="uniqueChart"><span class="loader"></span></div>
|
||||
</div>
|
||||
<div aria-labelledby="online-unique-tab" class="tab-pane"
|
||||
id="hourly-unique-tab" role="tabpanel">
|
||||
<div class="chart-area" id="hourlyUniqueChart"><span class="loader"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div aria-labelledby="online-calendar-tab" class="tab-pane" id="calendar-tab"
|
||||
role="tabpanel">
|
||||
<div class="row">
|
||||
@ -1509,6 +1519,22 @@
|
||||
}
|
||||
});
|
||||
|
||||
jsonRequest("../v1/graph?type=hourlyUniqueAndNew&server=${serverUUID}", function (json, error) {
|
||||
if (json) {
|
||||
var uniquePlayers = {
|
||||
name: s.name.uniquePlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals,
|
||||
data: json.uniquePlayers, color: json.colors.playersOnline
|
||||
};
|
||||
var newPlayers = {
|
||||
name: s.name.newPlayers, type: s.type.spline, tooltip: s.tooltip.zeroDecimals,
|
||||
data: json.newPlayers, color: json.colors.newPlayers
|
||||
};
|
||||
dayByDay('hourlyUniqueChart', [uniquePlayers, newPlayers]);
|
||||
} else if (error) {
|
||||
$('#uniqueChart').text("Failed to load graph data: " + error)
|
||||
}
|
||||
});
|
||||
|
||||
jsonRequest("../v1/graph?type=serverCalendar&server=${serverUUID}", function (json, error) {
|
||||
if (json) {
|
||||
$('#calendar').text('');
|
||||
@ -1573,7 +1599,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
function setLoadingText(text) {
|
||||
$('.loader-text').text(text);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user