mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2025-01-21 23:51:29 +01:00
Add Network Calendar to Network Overview
Does the same as Server Calendar but for the whole network. Affects issues: - Close #3111
This commit is contained in:
parent
9886040de7
commit
621e0b6f6e
@ -35,6 +35,7 @@ public enum WebPermission implements Supplier<String>, Lang {
|
||||
PAGE_NETWORK_OVERVIEW_GRAPHS_ONLINE("See Players Online graph"),
|
||||
PAGE_NETWORK_OVERVIEW_GRAPHS_DAY_BY_DAY("See Day by Day graph"),
|
||||
PAGE_NETWORK_OVERVIEW_GRAPHS_HOUR_BY_HOUR("See Hour by Hour graph"),
|
||||
PAGE_NETWORK_OVERVIEW_GRAPHS_CALENDAR("See Network calendar"),
|
||||
PAGE_NETWORK_SERVER_LIST("See list of servers"),
|
||||
PAGE_NETWORK_PLAYERBASE("See Playerbase Overview -tab"),
|
||||
PAGE_NETWORK_PLAYERBASE_OVERVIEW("See Playerbase Overview numbers"),
|
||||
|
@ -126,6 +126,7 @@ public class NetworkPageExporter extends FileExporter {
|
||||
"graph?type=activity",
|
||||
"graph?type=geolocation",
|
||||
"graph?type=uniqueAndNew",
|
||||
"graph?type=serverCalendar",
|
||||
"network/pingTable",
|
||||
"sessions",
|
||||
"extensionData?server=" + serverUUID,
|
||||
|
@ -310,6 +310,33 @@ public class GraphJSONCreator {
|
||||
",\"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<Long, Integer> uniquePerDay = db.query(
|
||||
PlayerCountQueries.uniquePlayerCounts(twoYearsAgo, now, timeZoneOffset)
|
||||
);
|
||||
NavigableMap<Long, Integer> newPerDay = db.query(
|
||||
PlayerCountQueries.newPlayerCounts(twoYearsAgo, now, timeZoneOffset)
|
||||
);
|
||||
NavigableMap<Long, Long> playtimePerDay = db.query(
|
||||
SessionQueries.playtimePerDay(twoYearsAgo, now, timeZoneOffset)
|
||||
);
|
||||
NavigableMap<Long, Integer> sessionsPerDay = db.query(
|
||||
SessionQueries.sessionCountPerDay(twoYearsAgo, now, timeZoneOffset)
|
||||
);
|
||||
return "{\"data\":" +
|
||||
graphs.calendar().serverCalendar(
|
||||
uniquePerDay,
|
||||
newPerDay,
|
||||
playtimePerDay,
|
||||
sessionsPerDay
|
||||
).toCalendarSeries() +
|
||||
",\"firstDay\":" + 1 + '}';
|
||||
}
|
||||
|
||||
public Map<String, Object> serverWorldPieJSONAsMap(ServerUUID serverUUID) {
|
||||
Database db = dbSystem.getDatabase();
|
||||
WorldTimes worldTimes = db.query(WorldTimesQueries.fetchServerTotalWorldTimes(serverUUID));
|
||||
|
@ -250,6 +250,8 @@ public class GraphsJSONResolver extends JSONResolver {
|
||||
return List.of(WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_DAY_BY_DAY);
|
||||
case GRAPH_HOURLY_UNIQUE_NEW:
|
||||
return List.of(WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_HOUR_BY_HOUR);
|
||||
case GRAPH_CALENDAR:
|
||||
return List.of(WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_CALENDAR);
|
||||
case GRAPH_SERVER_PIE:
|
||||
return List.of(WebPermission.PAGE_NETWORK_SESSIONS_SERVER_PIE);
|
||||
case GRAPH_WORLD_MAP:
|
||||
@ -313,6 +315,8 @@ public class GraphsJSONResolver extends JSONResolver {
|
||||
return graphJSON.uniqueAndNewGraphJSON();
|
||||
case GRAPH_HOURLY_UNIQUE_NEW:
|
||||
return graphJSON.hourlyUniqueAndNewGraphJSON();
|
||||
case GRAPH_CALENDAR:
|
||||
return graphJSON.networkCalendarJSON();
|
||||
case GRAPH_SERVER_PIE:
|
||||
return graphJSON.serverPreferencePieJSONAsMap();
|
||||
case GRAPH_HOSTNAME_PIE:
|
||||
|
@ -215,6 +215,7 @@ public enum HtmlLang implements Lang {
|
||||
// React
|
||||
LABEL_TITLE_SESSION_CALENDAR("html.label.sessionCalendar", "Session Calendar"),
|
||||
LABEL_TITLE_SERVER_CALENDAR("html.label.serverCalendar", "Server Calendar"),
|
||||
LABEL_TITLE_NETWORK_CALENDAR("html.label.networkCalendar", "Network Calendar"),
|
||||
LABEL_LABEL_JOIN_ADDRESS("html.label.joinAddress", "Join Address"),
|
||||
LABEL_LABEL_SESSION_MEDIAN("html.label.medianSessionLength", "Median Session Length"),
|
||||
LABEL_LABEL_KDR("html.label.kdr", "KDR"),
|
||||
|
@ -452,6 +452,46 @@ public class SessionQueries {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Query session count for each day within range across the whole network.
|
||||
*
|
||||
* @param after After epoch ms
|
||||
* @param before Before epoch ms
|
||||
* @param timeZoneOffset Offset in ms to determine start of day.
|
||||
* @return Map - Epoch ms (Start of day at 0 AM, no offset) : Session count of that day
|
||||
*/
|
||||
public static Query<NavigableMap<Long, Integer>> sessionCountPerDay(long after, long before, long timeZoneOffset) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
String selectSessionsPerDay = SELECT +
|
||||
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) +
|
||||
"*1000 as date," +
|
||||
"COUNT(1) as session_count" +
|
||||
FROM + SessionsTable.TABLE_NAME +
|
||||
WHERE + SessionsTable.SESSION_END + "<=?" +
|
||||
AND + SessionsTable.SESSION_START + ">=?" +
|
||||
GROUP_BY + "date";
|
||||
|
||||
return database.query(new QueryStatement<NavigableMap<Long, Integer>>(selectSessionsPerDay, 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("session_count"));
|
||||
}
|
||||
return uniquePerDay;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Long> playtime(long after, long before, ServerUUID serverUUID) {
|
||||
String sql = SELECT + "SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" +
|
||||
FROM + SessionsTable.TABLE_NAME +
|
||||
@ -562,6 +602,46 @@ public class SessionQueries {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Query playtime for each day within range across the whole network.
|
||||
*
|
||||
* @param after After epoch ms
|
||||
* @param before Before epoch ms
|
||||
* @param timeZoneOffset Offset in ms to determine start of day.
|
||||
* @return Map - Epoch ms (Start of day at 0 AM, no offset) : Playtime of that day
|
||||
*/
|
||||
public static Query<NavigableMap<Long, Long>> playtimePerDay(long after, long before, long timeZoneOffset) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
String selectPlaytimePerDay = SELECT +
|
||||
sql.dateToEpochSecond(sql.dateToDayStamp(sql.epochSecondToDate('(' + SessionsTable.SESSION_START + "+?)/1000"))) +
|
||||
"*1000 as date," +
|
||||
"SUM(" + SessionsTable.SESSION_END + '-' + SessionsTable.SESSION_START + ") as playtime" +
|
||||
FROM + SessionsTable.TABLE_NAME +
|
||||
WHERE + SessionsTable.SESSION_END + "<=?" +
|
||||
AND + SessionsTable.SESSION_START + ">=?" +
|
||||
GROUP_BY + "date";
|
||||
|
||||
return database.query(new QueryStatement<NavigableMap<Long, Long>>(selectPlaytimePerDay, 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, Long> processResults(ResultSet set) throws SQLException {
|
||||
NavigableMap<Long, Long> uniquePerDay = new TreeMap<>();
|
||||
while (set.next()) {
|
||||
uniquePerDay.put(set.getLong("date"), set.getLong("playtime"));
|
||||
}
|
||||
return uniquePerDay;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Long> averagePlaytimePerDay(long after, long before, long timeZoneOffset, ServerUUID serverUUID) {
|
||||
return database -> {
|
||||
Sql sql = database.getSql();
|
||||
|
@ -115,6 +115,7 @@ class AccessControlTest {
|
||||
Arguments.of("/v1/graph?type=playersOnline&server=" + TestConstants.SERVER_UUID_STRING + "", WebPermission.PAGE_SERVER_OVERVIEW_PLAYERS_ONLINE_GRAPH, 200, 403),
|
||||
Arguments.of("/v1/graph?type=uniqueAndNew", WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_DAY_BY_DAY, 200, 403),
|
||||
Arguments.of("/v1/graph?type=hourlyUniqueAndNew", WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_HOUR_BY_HOUR, 200, 403),
|
||||
Arguments.of("/v1/graph?type=serverCalendar", WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_CALENDAR, 200, 403),
|
||||
Arguments.of("/v1/graph?type=serverPie", WebPermission.PAGE_NETWORK_SESSIONS_SERVER_PIE, 200, 403),
|
||||
Arguments.of("/v1/graph?type=joinAddressPie", WebPermission.PAGE_NETWORK_JOIN_ADDRESSES_GRAPHS_PIE, 200, 403),
|
||||
Arguments.of("/v1/graph?type=activity", WebPermission.PAGE_NETWORK_PLAYERBASE_GRAPHS, 200, 403),
|
||||
|
@ -155,6 +155,7 @@ class AccessControlVisibilityTest {
|
||||
Arguments.arguments(WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_ONLINE, "online-activity-nav", "overview"),
|
||||
Arguments.arguments(WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_DAY_BY_DAY, "day-by-day-nav", "overview"),
|
||||
Arguments.arguments(WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_HOUR_BY_HOUR, "hour-by-hour-nav", "overview"),
|
||||
Arguments.arguments(WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_CALENDAR, "network-calendar-nav", "overview"),
|
||||
Arguments.arguments(WebPermission.PAGE_NETWORK_OVERVIEW, "recent-players", "overview"),
|
||||
Arguments.arguments(WebPermission.PAGE_NETWORK_OVERVIEW, "network-as-numbers", "overview"),
|
||||
Arguments.arguments(WebPermission.PAGE_NETWORK_OVERVIEW, "week-comparison", "overview"),
|
||||
|
@ -21,8 +21,8 @@
|
||||
<property name="headerFile" value="${config_loc}/java.header"/>
|
||||
</module>
|
||||
<module name="FileLength">
|
||||
<!-- This value is alright. Notable: SessionQueries 808 -->
|
||||
<property name="max" value="1000"/>
|
||||
<!-- This value is alright. Notable: SessionQueries 1007 -->
|
||||
<property name="max" value="1250"/>
|
||||
<property name="fileExtensions" value=".java"/>
|
||||
</module>
|
||||
<module name="LineLength">
|
||||
|
@ -4,7 +4,12 @@ import {Card} from "react-bootstrap";
|
||||
import CardTabs from "../../../CardTabs";
|
||||
import {faChartArea} from "@fortawesome/free-solid-svg-icons";
|
||||
import {useDataRequest} from "../../../../hooks/dataFetchHook";
|
||||
import {fetchDayByDayGraph, fetchHourByHourGraph, fetchPlayersOnlineGraph} from "../../../../service/serverService";
|
||||
import {
|
||||
fetchDayByDayGraph,
|
||||
fetchHourByHourGraph,
|
||||
fetchNetworkCalendarGraph,
|
||||
fetchPlayersOnlineGraph
|
||||
} from "../../../../service/serverService";
|
||||
import {ErrorViewBody} from "../../../../views/ErrorView";
|
||||
import {ChartLoader} from "../../../navigation/Loader";
|
||||
import TimeByTimeGraph from "../../../graphs/TimeByTimeGraph";
|
||||
@ -12,6 +17,8 @@ import PlayersOnlineGraph from "../../../graphs/PlayersOnlineGraph";
|
||||
import {useMetadata} from "../../../../hooks/metadataHook";
|
||||
import StackedPlayersOnlineGraph from "../../../graphs/StackedPlayersOnlineGraph";
|
||||
import {useAuth} from "../../../../hooks/authenticationHook";
|
||||
import {faCalendar} from "@fortawesome/free-regular-svg-icons";
|
||||
import ServerCalendar from "../../../calendar/ServerCalendar";
|
||||
|
||||
const SingleProxyPlayersOnlineGraph = ({serverUUID}) => {
|
||||
const {data, loadingError} = useDataRequest(fetchPlayersOnlineGraph, [serverUUID]);
|
||||
@ -61,6 +68,16 @@ const HourByHourTab = () => {
|
||||
return <TimeByTimeGraph id={"hour-by-hour-graph"} data={data}/>
|
||||
}
|
||||
|
||||
const NetworkCalendarTab = () => {
|
||||
|
||||
const {data, loadingError} = useDataRequest(fetchNetworkCalendarGraph, [])
|
||||
|
||||
if (loadingError) return <ErrorViewBody error={loadingError}/>
|
||||
if (!data) return <ChartLoader/>;
|
||||
|
||||
return <ServerCalendar series={data.data} firstDay={data.firstDay}/>
|
||||
}
|
||||
|
||||
const NetworkOnlineActivityGraphsCard = () => {
|
||||
const {hasPermission} = useAuth();
|
||||
const {t} = useTranslation();
|
||||
@ -77,6 +94,10 @@ const NetworkOnlineActivityGraphsCard = () => {
|
||||
name: t('html.label.hourByHour'), icon: faChartArea, color: 'blue', href: 'hour-by-hour',
|
||||
element: <HourByHourTab/>,
|
||||
permission: 'page.network.overview.graphs.hour.by.hour'
|
||||
}, {
|
||||
name: t('html.label.networkCalendar'), icon: faCalendar, color: 'teal', href: 'network-calendar',
|
||||
element: <NetworkCalendarTab/>,
|
||||
permission: 'page.network.overview.graphs.calendar'
|
||||
}
|
||||
].filter(tab => hasPermission(tab.permission));
|
||||
return <Card>
|
||||
|
@ -204,6 +204,12 @@ export const fetchServerCalendarGraph = async (timestamp, identifier) => {
|
||||
return doGetRequest(url, timestamp);
|
||||
}
|
||||
|
||||
export const fetchNetworkCalendarGraph = async (timestamp) => {
|
||||
let url = `/v1/graph?type=serverCalendar`;
|
||||
if (staticSite) url = `/data/graph-serverCalendar.json`;
|
||||
return doGetRequest(url, timestamp);
|
||||
}
|
||||
|
||||
export const fetchPunchCardGraph = async (timestamp, identifier) => {
|
||||
let url = `/v1/graph?type=punchCard&server=${identifier}`;
|
||||
if (staticSite) url = `/data/graph-punchCard_${identifier}.json`;
|
||||
|
Loading…
Reference in New Issue
Block a user