From 9c7d3007f84bcde826eb2f5f98ad3f722d8db3b6 Mon Sep 17 00:00:00 2001 From: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:50:40 +0300 Subject: [PATCH] Use real data --- .../delivery/domain/datatransfer/ViewDto.java | 9 ++ .../resolver/json/QueryJSONResolver.java | 34 +++++-- .../queries/objects/SessionQueries.java | 27 ++++++ .../cards/common/FirstMomentsCard.js | 89 ++++++++++++------- .../cards/query/QueryOptionsCard.js | 3 +- .../graphs/NetworkOnlineActivityGraphsCard.js | 3 +- .../server/graphs/OnlineActivityGraphsCard.js | 3 +- .../dashboard/src/service/serverService.js | 24 ++++- .../src/views/server/ServerPlayerRetention.js | 6 ++ 9 files changed, 150 insertions(+), 48 deletions(-) diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/ViewDto.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/ViewDto.java index 67b85bdeb..3716f46df 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/ViewDto.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/datatransfer/ViewDto.java @@ -24,8 +24,10 @@ import org.apache.commons.lang3.StringUtils; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -41,6 +43,7 @@ public class ViewDto { private final String beforeDate; private final String beforeTime; private final List servers; + private final Set wantedData; public ViewDto(Formatters formatters, List servers) { this.servers = servers; @@ -55,6 +58,8 @@ public class ViewDto { this.afterTime = after[1]; this.beforeDate = before[0]; this.beforeTime = before[1]; + + this.wantedData = new HashSet<>(); } public long getAfterEpochMs() throws ParseException { @@ -72,6 +77,10 @@ public class ViewDto { .collect(Collectors.toList()); } + public boolean isWanted(String key) { + return wantedData == null || wantedData.isEmpty() || wantedData.contains(key); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java index 086656a84..0b0eb19be 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/resolver/json/QueryJSONResolver.java @@ -22,10 +22,12 @@ import com.djrapitops.plan.delivery.domain.datatransfer.InputFilterDto; import com.djrapitops.plan.delivery.domain.datatransfer.InputQueryDto; import com.djrapitops.plan.delivery.domain.datatransfer.PlayerListDto; import com.djrapitops.plan.delivery.domain.datatransfer.ViewDto; +import com.djrapitops.plan.delivery.domain.mutators.SessionsMutator; import com.djrapitops.plan.delivery.formatting.Formatter; import com.djrapitops.plan.delivery.formatting.Formatters; import com.djrapitops.plan.delivery.rendering.json.PlayersTableJSONCreator; import com.djrapitops.plan.delivery.rendering.json.graphs.GraphJSONCreator; +import com.djrapitops.plan.delivery.rendering.json.graphs.Graphs; import com.djrapitops.plan.delivery.web.resolver.MimeType; import com.djrapitops.plan.delivery.web.resolver.Resolver; import com.djrapitops.plan.delivery.web.resolver.Response; @@ -35,6 +37,7 @@ import com.djrapitops.plan.delivery.web.resolver.request.WebUser; import com.djrapitops.plan.delivery.webserver.RequestBodyConverter; import com.djrapitops.plan.delivery.webserver.cache.JSONStorage; import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionQueryResultTableDataQuery; +import com.djrapitops.plan.gathering.domain.FinishedSession; import com.djrapitops.plan.identification.ServerInfo; import com.djrapitops.plan.identification.ServerUUID; import com.djrapitops.plan.settings.config.PlanConfig; @@ -81,6 +84,7 @@ public class QueryJSONResolver implements Resolver { private final DBSystem dbSystem; private final ServerInfo serverInfo; private final JSONStorage jsonStorage; + private final Graphs graphs; private final GraphJSONCreator graphJSONCreator; private final Locale locale; private final Formatters formatters; @@ -91,7 +95,9 @@ public class QueryJSONResolver implements Resolver { QueryFilters filters, PlanConfig config, DBSystem dbSystem, - ServerInfo serverInfo, JSONStorage jsonStorage, + ServerInfo serverInfo, + JSONStorage jsonStorage, + Graphs graphs, GraphJSONCreator graphJSONCreator, Locale locale, Formatters formatters, @@ -102,6 +108,7 @@ public class QueryJSONResolver implements Resolver { this.dbSystem = dbSystem; this.serverInfo = serverInfo; this.jsonStorage = jsonStorage; + this.graphs = graphs; this.graphJSONCreator = graphJSONCreator; this.locale = locale; this.formatters = formatters; @@ -111,7 +118,9 @@ public class QueryJSONResolver implements Resolver { @Override public boolean canAccess(Request request) { WebUser user = request.getUser().orElse(new WebUser("")); - return user.hasPermission(WebPermission.ACCESS_QUERY); + return user.hasPermission(WebPermission.ACCESS_QUERY) + || user.hasPermission(WebPermission.PAGE_NETWORK_OVERVIEW_GRAPHS_CALENDAR) + || user.hasPermission(WebPermission.PAGE_SERVER_ONLINE_ACTIVITY_GRAPHS_CALENDAR); } @GET @@ -213,12 +222,21 @@ public class QueryJSONResolver implements Resolver { long before = view.getBeforeEpochMs(); List serverUUIDs = view.getServerUUIDs(); - return Maps.builder(String.class, Object.class) - .put("players", getPlayersTableData(userIds, serverUUIDs, after, before)) - .put("activity", getActivityGraphData(userIds, serverUUIDs, after, before)) - .put("geolocation", getGeolocationData(userIds)) - .put("sessions", getSessionSummaryData(userIds, serverUUIDs, after, before)) - .build(); + Maps.Builder data = Maps.builder(String.class, Object.class); + + if (view.isWanted("players")) data.put("players", getPlayersTableData(userIds, serverUUIDs, after, before)); + if (view.isWanted("activity")) data.put("activity", getActivityGraphData(userIds, serverUUIDs, after, before)); + if (view.isWanted("geolocation")) data.put("geolocation", getGeolocationData(userIds)); + if (view.isWanted("sessions")) data.put("sessions", getSessionSummaryData(userIds, serverUUIDs, after, before)); + if (view.isWanted("sessionList")) data.put("sessionList", getSessionList(userIds, serverUUIDs, after, before)); + + return data.build(); + } + + private List> getSessionList(Set userIds, List serverUUIDs, long after, long before) { + Database database = dbSystem.getDatabase(); + List sessions = database.query(SessionQueries.fetchQuerySessions(userIds, serverUUIDs, after, before)); + return new SessionsMutator(sessions).toPlayerNameJSONMaps(graphs, config.getWorldAliasSettings(), formatters); } private Map getSessionSummaryData(Set userIds, List serverUUIDs, long after, long before) { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java index fcc57b395..d5b7e60fb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java @@ -946,6 +946,33 @@ public class SessionQueries { }; } + public static Query> fetchQuerySessions(Set userIds, List serverUUIDs, long after, long before) { + String uuidsInSet = " IN (" + new TextStringBuilder().appendWithSeparators(userIds, ",") + ")"; + String selectServerIds = SELECT + ServerTable.ID + + FROM + ServerTable.TABLE_NAME + + WHERE + ServerTable.SERVER_UUID + " IN ('" + new TextStringBuilder().appendWithSeparators(serverUUIDs, "','") + "')"; + + String sql = SELECT_SESSIONS_STATEMENT + + WHERE + SessionsTable.SESSION_START + ">?" + + AND + SessionsTable.SESSION_END + "(sql, 2000) { + @Override + public void prepare(PreparedStatement statement) throws SQLException { + statement.setLong(1, after); + statement.setLong(2, before); + } + + @Override + public List processResults(ResultSet set) throws SQLException { + return extractDataFromSessionSelectStatement(set); + } + }; + } + public static Query> summaryOfPlayers(Set userIds, List serverUUIDs, long after, long before) { String uuidsInSet = " IN (" + new TextStringBuilder().appendWithSeparators(userIds, ",") + ")"; String selectServerIds = SELECT + ServerTable.ID + diff --git a/Plan/react/dashboard/src/components/cards/common/FirstMomentsCard.js b/Plan/react/dashboard/src/components/cards/common/FirstMomentsCard.js index 36bdbd835..c73b6ec9f 100644 --- a/Plan/react/dashboard/src/components/cards/common/FirstMomentsCard.js +++ b/Plan/react/dashboard/src/components/cards/common/FirstMomentsCard.js @@ -2,7 +2,7 @@ import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {Card} from "react-bootstrap"; import CardHeader from "../CardHeader"; import {faChevronLeft, faChevronRight, faHandHoldingHeart} from "@fortawesome/free-solid-svg-icons"; -import {fetchFirstMoments} from "../../../service/serverService"; +import {fetchFirstMoments, fetchPlayersOnlineGraph} from "../../../service/serverService"; import {CardLoader} from "../../navigation/Loader"; import XRangeGraph from "../../graphs/XRangeGraph"; import {Link} from "react-router-dom"; @@ -10,50 +10,65 @@ import {tooltip} from "../../../util/graphs"; import {useTranslation} from "react-i18next"; import Graph from "../../graphs/Graph"; import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {useDataRequest} from "../../../hooks/dataFetchHook"; +import {useMetadata} from "../../../hooks/metadataHook"; +import {ErrorViewBody} from "../../../views/ErrorView"; +import FormattedTime from "../../text/FormattedTime"; + +const dayMs = 24 * 60 * 60 * 1000; const FirstMomentsCard = ({identifier}) => { const {t} = useTranslation(); + const {timeZoneOffsetMinutes, networkMetadata} = useMetadata(); - const [data, setData] = useState(undefined); + const [selectedDay, setSelectedDay] = useState(1648760400000); //Date.now()) + + const {data: playersOnline, loadingError} = useDataRequest(fetchPlayersOnlineGraph, [identifier]); const [sessionPlots, setSessionPlots] = useState([]); const loadData = useCallback(async () => { - const loaded = await fetchFirstMoments(0, 0, identifier); - setData(loaded); + const startOfDay = selectedDay - (selectedDay + timeZoneOffsetMinutes * 60 * 1000) % dayMs; + const endOfDay = startOfDay + dayMs; + const { + data: loaded, + error + } = await fetchFirstMoments(startOfDay, endOfDay, networkMetadata?.servers.find(s => s.serverUUID === identifier)); + console.log(loaded); const sessionsByPlayer = {}; - for (const session of loaded.sessions) { - const player = session.player_name; - if (!sessionsByPlayer[player]) sessionsByPlayer[player] = []; - sessionsByPlayer[player].push(session); + if (loaded?.data) { + for (const session of loaded.data.sessionList) { + const player = session.player_name; + if (!sessionsByPlayer[player]) sessionsByPlayer[player] = []; + sessionsByPlayer[player].push(session); + } } const sessionPlots = []; let i = 1; for (const entry of Object.entries(sessionsByPlayer)) { sessionPlots.push({ - name: "Player " + i, + name: entry[1][0].player_name, uuid: entry[1][0].player_uuid, points: entry[1].map(session => { - const dayMs = 24 * 60 * 60 * 1000; - const addStart = Math.floor(Math.random() * dayMs); - const start = Date.now() - (Date.now() % dayMs) + addStart; - const end = start + Math.floor(Math.random() * (dayMs - addStart)); + const start = session.startMillis; + const end = session.endMillis; return {x: start, x2: end, color: session.first_session ? '#4caf50' : '#4ab4de'}; - }).sort((a, b) => a.x - b.x > 0 ? 1 : -1) + }).sort((a, b) => a.x - b.x > 0 ? 1 : -1), + playtime: entry[1].reduce((partialSum, session) => partialSum + session.endMillis - session.startMillis, 0) }) i++; } setSessionPlots(sessionPlots.sort((a, b) => a.points[0].x - b.points[0].x > 0 ? 1 : -1)); - }, [setData, setSessionPlots, identifier]); + }, [selectedDay, setSessionPlots, identifier]); useEffect(() => { loadData() }, [loadData]); const playersOnlineOptions = useMemo(() => { - if (!data || !sessionPlots) return {}; + if (!playersOnline || !sessionPlots?.length) return {}; - const startOfDay = sessionPlots ? (sessionPlots[0].points[0].x - sessionPlots[0].points[0].x % (24 * 60 * 60 * 1000)) : 0; - const endOfDay = startOfDay + (24 * 60 * 60 * 1000); + const startOfDay = selectedDay + timeZoneOffsetMinutes; + const endOfDay = startOfDay + dayMs; return { yAxis: { title: { @@ -80,22 +95,25 @@ const FirstMomentsCard = ({identifier}) => { name: t('html.label.playersOnline'), type: 'spline', tooltip: tooltip.zeroDecimals, - data: data ? data.graphs[0].points : [], + data: playersOnline ? playersOnline.playersOnline : [], color: "#90b7f3", yAxis: 0 }] } - }, [data, t, sessionPlots]); + }, [playersOnline, t, sessionPlots]); - if (!data) return + if (!sessionPlots) return ; return (
- on 2023-04-10 - - + on {new Date(selectedDay).toISOString().split("T")[0]} + +
{/**/} @@ -105,25 +123,28 @@ const FirstMomentsCard = ({identifier}) => { + + + + + - - - - - {sessionPlots.map((plot, i) => - - + )}
Players Online + {loadingError && } + {!loadingError && + } + -
Player Sessions Playtime
Players Online - - -
{plot.name} + 0s
diff --git a/Plan/react/dashboard/src/components/cards/query/QueryOptionsCard.js b/Plan/react/dashboard/src/components/cards/query/QueryOptionsCard.js index 70a61e54e..627df3286 100644 --- a/Plan/react/dashboard/src/components/cards/query/QueryOptionsCard.js +++ b/Plan/react/dashboard/src/components/cards/query/QueryOptionsCard.js @@ -121,7 +121,8 @@ const QueryOptionsCard = () => { afterTime: fromTime ? fromTime : options.view.afterTime, beforeDate: toDate ? toDate : options.view.beforeDate, beforeTime: toTime ? toTime : options.view.beforeTime, - servers: selectedServers.map(index => options.view.servers[index]) + servers: selectedServers.map(index => options.view.servers[index]), + wantedData: ["players", "activity", "geolocations", "sessions"] }, filters } diff --git a/Plan/react/dashboard/src/components/cards/server/graphs/NetworkOnlineActivityGraphsCard.js b/Plan/react/dashboard/src/components/cards/server/graphs/NetworkOnlineActivityGraphsCard.js index 57e96c7ff..605487eb1 100644 --- a/Plan/react/dashboard/src/components/cards/server/graphs/NetworkOnlineActivityGraphsCard.js +++ b/Plan/react/dashboard/src/components/cards/server/graphs/NetworkOnlineActivityGraphsCard.js @@ -94,7 +94,8 @@ const NetworkCalendarTab = () => { view: { afterDate: start, afterTime: "00:00", beforeDate: end, beforeTime: "00:00", - servers: [] + servers: [], + wantedData: ["players"] } } setQueryData(undefined); diff --git a/Plan/react/dashboard/src/components/cards/server/graphs/OnlineActivityGraphsCard.js b/Plan/react/dashboard/src/components/cards/server/graphs/OnlineActivityGraphsCard.js index 50efe54e9..0693123d8 100644 --- a/Plan/react/dashboard/src/components/cards/server/graphs/OnlineActivityGraphsCard.js +++ b/Plan/react/dashboard/src/components/cards/server/graphs/OnlineActivityGraphsCard.js @@ -71,7 +71,8 @@ const ServerCalendarTab = () => { view: { afterDate: start, afterTime: "00:00", beforeDate: end, beforeTime: "00:00", - servers: networkMetadata?.servers.filter(server => server.serverUUID === identifier) || [] + servers: networkMetadata?.servers.filter(server => server.serverUUID === identifier) || [], + wantedData: ["players"] } } setQueryData(undefined); diff --git a/Plan/react/dashboard/src/service/serverService.js b/Plan/react/dashboard/src/service/serverService.js index c24a1a90d..03311d39f 100644 --- a/Plan/react/dashboard/src/service/serverService.js +++ b/Plan/react/dashboard/src/service/serverService.js @@ -1,5 +1,6 @@ import {doGetRequest, staticSite} from "./backendConfiguration"; -import {firstMoments} from "./mockData"; +import Highcharts from "highcharts/highstock"; +import {postQuery} from "./queryService"; export const fetchServerIdentity = async (timestamp, identifier) => { let url = `/v1/serverIdentity?server=${identifier}`; @@ -341,6 +342,23 @@ export const fetchPluginHistory = async (timestamp, identifier) => { return doGetRequest(url, timestamp); } -export const fetchFirstMoments = async (timestamp, after, before, identifier) => { - return firstMoments; +export const fetchFirstMoments = async (after, before, server) => { + const start = Highcharts.dateFormat('%d/%m/%Y', after); + const end = Highcharts.dateFormat('%d/%m/%Y', before); + const query = { + filters: [{ + kind: "registeredBetween", + parameters: { + afterDate: start, afterTime: "00:00", + beforeDate: end, beforeTime: "00:00" + } + }], + view: { + afterDate: start, afterTime: "00:00", + beforeDate: end, beforeTime: "00:00", + servers: server ? [server] : [], + wantedData: ["sessionList"] + } + } + return await postQuery(query); } \ No newline at end of file diff --git a/Plan/react/dashboard/src/views/server/ServerPlayerRetention.js b/Plan/react/dashboard/src/views/server/ServerPlayerRetention.js index 9ed35ceb6..104082c89 100644 --- a/Plan/react/dashboard/src/views/server/ServerPlayerRetention.js +++ b/Plan/react/dashboard/src/views/server/ServerPlayerRetention.js @@ -5,6 +5,7 @@ import LoadIn from "../../components/animation/LoadIn"; import PlayerRetentionGraphCard from "../../components/cards/common/PlayerRetentionGraphCard"; import {useParams} from "react-router-dom"; import {useAuth} from "../../hooks/authenticationHook"; +import FirstMomentsCard from "../../components/cards/common/FirstMomentsCard"; const ServerPlayerRetention = () => { const {hasPermission} = useAuth(); @@ -19,6 +20,11 @@ const ServerPlayerRetention = () => { } + + + + + )