Format Server/Network Overview values in the frontend

This commit is contained in:
Aurora Lahtela 2024-01-20 11:34:20 +02:00
parent f9d2b0767f
commit 8e94d26ff3
12 changed files with 126 additions and 68 deletions

View File

@ -16,7 +16,6 @@
*/
package com.djrapitops.plan.delivery.rendering.json;
import com.djrapitops.plan.delivery.domain.DateHolder;
import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.delivery.domain.mutators.TPSMutator;
import com.djrapitops.plan.delivery.formatting.Formatter;
@ -55,17 +54,14 @@ import java.util.concurrent.TimeUnit;
@Singleton
public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<String, Object>> {
private final Formatter<Long> day;
private final PlanConfig config;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final ServerSensor<?> serverSensor;
private final Formatter<Long> timeAmount;
private final Formatter<Double> decimals;
private final Formatter<Double> percentage;
private final ServerUptimeCalculator serverUptimeCalculator;
private final Formatter<DateHolder> year;
@Inject
public ServerOverviewJSONCreator(
@ -82,9 +78,6 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<Strin
this.serverSensor = serverSensor;
this.serverUptimeCalculator = serverUptimeCalculator;
year = formatters.year();
day = formatters.dayLong();
timeAmount = formatters.timeAmount();
decimals = formatters.decimals();
percentage = formatters.percentage();
}
@ -118,7 +111,7 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<Strin
double averageTPS = tpsMutator.averageTPS();
sevenDays.put("average_tps", averageTPS != -1 ? decimals.apply(averageTPS) : GenericLang.UNAVAILABLE.getKey());
sevenDays.put("low_tps_spikes", tpsMutator.lowTpsSpikeCount(config.get(DisplaySettings.GRAPH_TPS_THRESHOLD_MED)));
sevenDays.put("downtime", timeAmount.apply(tpsMutator.serverDownTime()));
sevenDays.put("downtime", tpsMutator.serverDownTime());
return sevenDays;
}
@ -137,18 +130,19 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<Strin
numbers.put("online_players", getOnlinePlayers(serverUUID, db));
Optional<DateObj<Integer>> lastPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo));
Optional<DateObj<Integer>> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID));
numbers.put("last_peak_date", lastPeak.map(year).orElse("-"));
numbers.put("last_peak_date", lastPeak.map(DateObj::getDate).map(Object.class::cast).orElse("-"));
numbers.put("last_peak_players", lastPeak.map(dateObj -> dateObj.getValue().toString()).orElse("-"));
numbers.put("best_peak_date", allTimePeak.map(year).orElse("-"));
numbers.put("best_peak_date", allTimePeak.map(DateObj::getDate).map(Object.class::cast).orElse("-"));
numbers.put("best_peak_players", allTimePeak.map(dateObj -> dateObj.getValue().toString()).orElse("-"));
Long totalPlaytime = db.query(SessionQueries.playtime(0L, now, serverUUID));
numbers.put("playtime", timeAmount.apply(totalPlaytime));
numbers.put("player_playtime", userCount != 0 ? timeAmount.apply(totalPlaytime / userCount) : "-");
numbers.put("playtime", totalPlaytime);
numbers.put("player_playtime", userCount != 0 ? totalPlaytime / userCount : "-");
numbers.put("sessions", db.query(SessionQueries.sessionCount(0L, now, serverUUID)));
numbers.put("player_kills", db.query(KillQueries.playerKillCount(0L, now, serverUUID)));
numbers.put("mob_kills", db.query(KillQueries.mobKillCount(0L, now, serverUUID)));
numbers.put("deaths", db.query(KillQueries.deathCount(0L, now, serverUUID)));
numbers.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID).map(timeAmount)
numbers.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID)
.map(Object.class::cast)
.orElse(GenericLang.UNAVAILABLE.getKey()));
return numbers;
@ -171,9 +165,9 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<Strin
Map<String, Object> weeks = new HashMap<>();
weeks.put("start", day.apply(twoWeeksAgo));
weeks.put("midpoint", day.apply(oneWeekAgo));
weeks.put("end", day.apply(now));
weeks.put("start", twoWeeksAgo);
weeks.put("midpoint", oneWeekAgo);
weeks.put("end", now);
Integer uniqueBefore = db.query(PlayerCountQueries.uniquePlayerCount(twoWeeksAgo, oneWeekAgo, serverUUID));
Integer uniqueAfter = db.query(PlayerCountQueries.uniquePlayerCount(oneWeekAgo, now, serverUUID));
@ -199,9 +193,9 @@ public class ServerOverviewJSONCreator implements ServerTabJSONCreator<Map<Strin
Long playtimeAfter = db.query(SessionQueries.playtime(oneWeekAgo, now, serverUUID));
long avgPlaytimeBefore = uniqueBefore != 0 ? playtimeBefore / uniqueBefore : 0L;
long avgPlaytimeAfter = uniqueAfter != 0 ? playtimeAfter / uniqueAfter : 0L;
Trend avgPlaytimeTrend = new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false, timeAmount);
weeks.put("average_playtime_before", timeAmount.apply(avgPlaytimeBefore));
weeks.put("average_playtime_after", timeAmount.apply(avgPlaytimeAfter));
Trend avgPlaytimeTrend = new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false);
weeks.put("average_playtime_before", avgPlaytimeBefore);
weeks.put("average_playtime_after", avgPlaytimeAfter);
weeks.put("average_playtime_trend", avgPlaytimeTrend);
Long sessionsBefore = db.query(SessionQueries.sessionCount(twoWeeksAgo, oneWeekAgo, serverUUID));

View File

@ -16,9 +16,7 @@
*/
package com.djrapitops.plan.delivery.rendering.json.network;
import com.djrapitops.plan.delivery.domain.DateHolder;
import com.djrapitops.plan.delivery.domain.DateObj;
import com.djrapitops.plan.delivery.formatting.Formatter;
import com.djrapitops.plan.delivery.formatting.Formatters;
import com.djrapitops.plan.delivery.rendering.json.Trend;
import com.djrapitops.plan.gathering.ServerSensor;
@ -50,14 +48,11 @@ import java.util.concurrent.TimeUnit;
@Singleton
public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<String, Object>> {
private final Formatter<Long> day;
private final PlanConfig config;
private final DBSystem dbSystem;
private final ServerInfo serverInfo;
private final ServerSensor<?> serverSensor;
private final Formatter<Long> timeAmount;
private final ServerUptimeCalculator serverUptimeCalculator;
private final Formatter<DateHolder> year;
@Inject
public NetworkOverviewJSONCreator(
@ -73,10 +68,6 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
this.serverInfo = serverInfo;
this.serverSensor = serverSensor;
this.serverUptimeCalculator = serverUptimeCalculator;
year = formatters.year();
day = formatters.dayLong();
timeAmount = formatters.timeAmount();
}
public Map<String, Object> createJSONAsMap() {
@ -122,17 +113,18 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
ServerUUID serverUUID = serverInfo.getServerUUID();
Optional<DateObj<Integer>> lastPeak = db.query(TPSQueries.fetchPeakPlayerCount(serverUUID, twoDaysAgo));
Optional<DateObj<Integer>> allTimePeak = db.query(TPSQueries.fetchAllTimePeakPlayerCount(serverUUID));
numbers.put("last_peak_date", lastPeak.map(year).orElse("-"));
numbers.put("last_peak_date", lastPeak.map(DateObj::getDate).map(Object.class::cast).orElse("-"));
numbers.put("last_peak_players", lastPeak.map(dateObj -> dateObj.getValue().toString()).orElse("-"));
numbers.put("best_peak_date", allTimePeak.map(year).orElse("-"));
numbers.put("best_peak_date", allTimePeak.map(DateObj::getDate).map(Object.class::cast).orElse("-"));
numbers.put("best_peak_players", allTimePeak.map(dateObj -> dateObj.getValue().toString()).orElse("-"));
Long totalPlaytime = db.query(SessionQueries.playtime(0L, now));
numbers.put("playtime", timeAmount.apply(totalPlaytime));
numbers.put("player_playtime", userCount != 0 ? timeAmount.apply(totalPlaytime / userCount) : "-");
numbers.put("playtime", totalPlaytime);
numbers.put("player_playtime", userCount != 0 ? totalPlaytime / userCount : "-");
Long sessionCount = db.query(SessionQueries.sessionCount(0L, now));
numbers.put("sessions", sessionCount);
numbers.put("session_length_avg", sessionCount != 0 ? timeAmount.apply(totalPlaytime / sessionCount) : "-");
numbers.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID).map(timeAmount)
numbers.put("session_length_avg", sessionCount != 0 ? totalPlaytime / sessionCount : "-");
numbers.put("current_uptime", serverUptimeCalculator.getServerUptimeMillis(serverUUID)
.map(Object.class::cast)
.orElse(GenericLang.UNAVAILABLE.getKey()));
return numbers;
@ -147,9 +139,9 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
Map<String, Object> weeks = new HashMap<>();
weeks.put("start", day.apply(twoWeeksAgo));
weeks.put("midpoint", day.apply(oneWeekAgo));
weeks.put("end", day.apply(now));
weeks.put("start", twoWeeksAgo);
weeks.put("midpoint", oneWeekAgo);
weeks.put("end", now);
Integer uniqueBefore = db.query(PlayerCountQueries.uniquePlayerCount(twoWeeksAgo, oneWeekAgo));
Integer uniqueAfter = db.query(PlayerCountQueries.uniquePlayerCount(oneWeekAgo, now));
@ -175,9 +167,9 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
Long playtimeAfter = db.query(SessionQueries.playtime(oneWeekAgo, now));
long avgPlaytimeBefore = uniqueBefore != 0 ? playtimeBefore / uniqueBefore : 0L;
long avgPlaytimeAfter = uniqueAfter != 0 ? playtimeAfter / uniqueAfter : 0L;
Trend avgPlaytimeTrend = new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false, timeAmount);
weeks.put("average_playtime_before", timeAmount.apply(avgPlaytimeBefore));
weeks.put("average_playtime_after", timeAmount.apply(avgPlaytimeAfter));
Trend avgPlaytimeTrend = new Trend(avgPlaytimeBefore, avgPlaytimeAfter, false);
weeks.put("average_playtime_before", avgPlaytimeBefore);
weeks.put("average_playtime_after", avgPlaytimeAfter);
weeks.put("average_playtime_trend", avgPlaytimeTrend);
Long sessionsBefore = db.query(SessionQueries.sessionCount(twoWeeksAgo, oneWeekAgo));
@ -189,9 +181,9 @@ public class NetworkOverviewJSONCreator implements NetworkTabJSONCreator<Map<Str
long avgSessionLengthBefore = sessionsBefore != 0 ? playtimeBefore / sessionsBefore : 0;
long avgSessionLengthAfter = sessionsAfter != 0 ? playtimeAfter / sessionsAfter : 0;
Trend avgSessionLengthTrend = new Trend(avgSessionLengthBefore, avgSessionLengthAfter, false, timeAmount);
weeks.put("session_length_average_before", timeAmount.apply(avgSessionLengthBefore));
weeks.put("session_length_average_after", timeAmount.apply(avgSessionLengthAfter));
Trend avgSessionLengthTrend = new Trend(avgSessionLengthBefore, avgSessionLengthAfter, false);
weeks.put("session_length_average_before", avgSessionLengthBefore);
weeks.put("session_length_average_after", avgSessionLengthAfter);
weeks.put("session_length_average_trend", avgSessionLengthTrend);
return weeks;

View File

@ -8,6 +8,8 @@ import {faCalendarCheck, faClock} from "@fortawesome/free-regular-svg-icons";
import React from "react";
import {TableRow} from "../../../table/TableRow";
import {CardLoader} from "../../../navigation/Loader";
import FormattedDay from "../../../text/FormattedDay.jsx";
import FormattedTime, {formatTimeFunction} from "../../../text/FormattedTime.jsx";
const ServerWeekComparisonCard = ({data}) => {
const {t} = useTranslation();
@ -20,11 +22,18 @@ const ServerWeekComparisonCard = ({data}) => {
</h6>
</Card.Header>
<ComparisonTable comparisonHeader={t('html.label.comparing7days')}
headers={[data.start + ' - ' + data.midpoint, data.midpoint + ' - ' + data.end, t('html.label.trend')]}>
<TableRow icon={faUsers} color="blue" text={t('html.label.uniquePlayers')}
values={[data.unique_before, data.unique_after,
headers={[
<><FormattedDay date={data.start}/> - <FormattedDay date={data.midpoint}/></>,
<><FormattedDay date={data.midpoint}/> - <FormattedDay date={data.end}/></>,
t('html.label.trend')]}>
<TableRow icon={faUsers} color="blue"
text={t('html.label.uniquePlayers')}
values={[
data.unique_before,
data.unique_after,
<BigTrend key={JSON.stringify(data.unique_trend)}
trend={data.unique_trend}/>]}/>
trend={data.unique_trend}/>
]}/>
<TableRow icon={faUsers} color="light-green" text={t('html.label.newPlayers')}
values={[data.new_before, data.new_after,
<BigTrend key={JSON.stringify(data.new_trend)}
@ -35,14 +44,20 @@ const ServerWeekComparisonCard = ({data}) => {
trend={data.regular_trend}/>]}/>
<TableRow icon={faClock} color="green"
text={t('html.label.averagePlaytime') + ' ' + t('html.label.perPlayer')}
values={[data.average_playtime_before, data.average_playtime_after,
values={[
<FormattedTime key={"before-ms"} timeMs={data.average_playtime_before}/>,
<FormattedTime key={"after-ms"} timeMs={data.average_playtime_after}/>,
<BigTrend key={JSON.stringify(data.average_playtime_trend)}
trend={data.average_playtime_trend}/>]}/>
<TableRow icon={faClock} color="teal"
text={t('html.label.averageSessionLength')}
values={[data.session_length_average_before, data.session_length_average_after,
<BigTrend key={JSON.stringify(data.session_length_average_trend)}
trend={data.session_length_average_trend}/>]}/>
trend={data.average_playtime_trend}
format={formatTimeFunction}/>]}/>
{data.session_length_average_before !== undefined && <TableRow
icon={faClock} color="teal"
text={t('html.label.averageSessionLength')}
values={[<FormattedTime key={"before-ms"} timeMs={data.session_length_average_before}/>,
<FormattedTime key={"after-ms"} timeMs={data.session_length_average_after}/>,
<BigTrend key={JSON.stringify(data.session_length_average_trend)}
trend={data.session_length_average_trend}
format={formatTimeFunction}/>]}/>}
<TableRow icon={faCalendarCheck} color="teal" text={t('html.label.sessions')}
values={[data.sessions_before, data.sessions_after,
<BigTrend key={JSON.stringify(data.sessions_trend)}

View File

@ -9,6 +9,8 @@ import {CardLoader} from "../../../navigation/Loader";
import ExtendableCardBody from "../../../layout/extension/ExtendableCardBody";
import {useMetadata} from "../../../../hooks/metadataHook";
import CurrentUptime from "../../../datapoint/CurrentUptime";
import FormattedTime from "../../../text/FormattedTime.jsx";
import FormattedDate from "../../../text/FormattedDate.jsx";
const ServerAsNumbersCard = ({data}) => {
const {t} = useTranslation();
@ -40,23 +42,23 @@ const ServerAsNumbersCard = ({data}) => {
value={data.online_players} bold/>
{showPeaks && <>
<hr/>
<Datapoint name={t('html.label.lastPeak') + ' (' + data.last_peak_date + ')'}
<Datapoint name={<>{t('html.label.lastPeak')} (<FormattedDate date={data.last_peak_date}/>)</>}
color={'blue'} icon={faChartLine}
value={data.last_peak_players} valueLabel={t('html.unit.players')} bold/>
<Datapoint name={t('html.label.bestPeak') + ' (' + data.best_peak_date + ')'}
<Datapoint name={<>{t('html.label.bestPeak')} (<FormattedDate date={data.best_peak_date}/>)</>}
color={'light-green'} icon={faChartLine}
value={data.best_peak_players} valueLabel={t('html.unit.players')} bold/>
</>}
<hr/>
<Datapoint name={t('html.label.totalPlaytime')}
color={'green'} icon={faClock}
value={data.playtime}/>
value={<FormattedTime timeMs={data.playtime}/>}/>
<Datapoint name={t('html.label.averagePlaytime') + ' ' + t('html.label.perPlayer')}
color={'green'} icon={faClock}
value={data.player_playtime}/>
value={<FormattedTime timeMs={data.player_playtime}/>}/>
<Datapoint name={t('html.label.averageSessionLength')}
color={'teal'} icon={faClock}
value={data.session_length_avg}/>
value={<FormattedTime timeMs={data.session_length_avg}/>}/>
<Datapoint name={t('html.label.sessions')}
color={'teal'} icon={faCalendarCheck}
value={data.sessions} bold/>

View File

@ -4,6 +4,8 @@ import {faPowerOff} from "@fortawesome/free-solid-svg-icons";
import {faQuestionCircle} from "@fortawesome/free-regular-svg-icons";
import Datapoint from "../Datapoint";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import FormattedTime from "../text/FormattedTime.jsx";
import {isNumber} from "../../util/isNumber.js";
const CurrentUptime = ({uptime}) => {
const {t} = useTranslation();
@ -15,7 +17,7 @@ const CurrentUptime = ({uptime}) => {
return (
<Datapoint icon={faPowerOff} color={'light-green'}
name={t('html.label.currentUptime')}
value={uptime} valueLabel={infoBubble}/>
value={isNumber(uptime) && <FormattedTime timeMs={uptime}/> || uptime} valueLabel={infoBubble}/>
)
};

View File

@ -3,13 +3,16 @@ import {usePreferences} from "../../hooks/preferencesHook";
import {SimpleDateFormat} from "../../util/format/SimpleDateFormat";
import {useMetadata} from "../../hooks/metadataHook";
import {useTranslation} from "react-i18next";
import {isNumber} from "../../util/isNumber.js";
const FormattedDate = ({date}) => {
console.log(date);
const {t} = useTranslation();
const {timeZoneOffsetHours} = useMetadata();
const {preferencesLoaded, dateFormatNoSeconds, recentDaysInDateFormat} = usePreferences();
if (!preferencesLoaded || date === undefined || date === null) return <></>
if (!preferencesLoaded || date === undefined || date === null) return <></>;
if (!isNumber(date)) return date;
const pattern = dateFormatNoSeconds;
const recentDays = recentDaysInDateFormat;

View File

@ -0,0 +1,24 @@
import React from 'react';
import {SimpleDateFormat} from "../../util/format/SimpleDateFormat";
import {useMetadata} from "../../hooks/metadataHook";
import {isNumber} from "../../util/isNumber.js";
const FormattedDay = ({date}) => {
const {timeZoneOffsetHours} = useMetadata();
if (date === undefined || date === null) return <></>;
if (!isNumber(date)) return date;
const pattern = "MMMMM d";
const offset = timeZoneOffsetHours * 60 * 60 * 1000;
const timestamp = date - offset;
const formatted = date !== 0 ? new SimpleDateFormat(pattern).format(timestamp) : '-';
return (
<>{formatted}</>
)
};
export default FormattedDay

View File

@ -1,11 +1,13 @@
import React from 'react';
import {usePreferences} from "../../hooks/preferencesHook";
import {formatTimeAmount} from "../../util/format/TimeAmountFormat";
import {isNumber} from "../../util/isNumber.js";
const FormattedTime = ({timeMs}) => {
const {preferencesLoaded, timeFormat} = usePreferences();
if (!preferencesLoaded) return <></>
if (!preferencesLoaded) return <></>;
if (!isNumber(timeMs)) return timeMs;
const options = {
YEAR: timeFormat.year,
@ -26,4 +28,10 @@ const FormattedTime = ({timeMs}) => {
)
};
export const formatTimeFunction = time => {
return (
<FormattedTime timeMs={time}/>
);
}
export default FormattedTime

View File

@ -9,17 +9,20 @@ const TrendDownGood = ({value}) => <span className="badge bg-success"><Fa icon={
const TrendSame = ({value}) => <span className="badge bg-warning"><Fa icon={faCaretRight}/>{value}</span>;
const BigTrend = ({trend}) => {
const BigTrend = ({trend, format}) => {
if (!trend) {
return <TrendSame value={'?'}/>;
}
const value = format ? format(trend.text) : trend.text;
switch (trend.direction) {
case '+':
return (trend.reversed ? <TrendUpBad value={trend.text}/> : <TrendUpGood value={trend.text}/>);
return (trend.reversed ? <TrendUpBad value={trend.text}/> : <TrendUpGood value={value}/>);
case '-':
return (trend.reversed ? <TrendDownGood value={trend.text}/> : <TrendDownBad value={trend.text}/>);
return (trend.reversed ? <TrendDownGood value={trend.text}/> : <TrendDownBad value={value}/>);
default:
return <TrendSame value={trend.text}/>;
return <TrendSame value={value}/>;
}
}

View File

@ -1505,4 +1505,14 @@ ul.filters {
.nav-item.disabled {
opacity: 30%;
}
}
.link {
color: var(--bs-link-color);
opacity: var(--bs-link-opacity);
text-decoration: underline;
}
.link:hover {
color: var(--bs-link-hover-color)
}

View File

@ -0,0 +1,4 @@
// https://stackoverflow.com/a/1421988/20825073
export function isNumber(n) {
return !isNaN(parseFloat(n)) && !isNaN(n - 0)
}

View File

@ -23,6 +23,7 @@ import {CardLoader} from "../../components/navigation/Loader";
import ExtendableRow from "../../components/layout/extension/ExtendableRow";
import {useAuth} from "../../hooks/authenticationHook";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import FormattedTime from "../../components/text/FormattedTime.jsx";
const Last7DaysCard = ({data}) => {
const {t} = useTranslation();
@ -64,7 +65,7 @@ const Last7DaysCard = ({data}) => {
value={data.low_tps_spikes} bold/>
<Datapoint name={t('html.label.downtime')}
color={'red'} icon={faPowerOff}
value={data.downtime}/>
value={<FormattedTime timeMs={data.downtime}/>}/>
</Card.Body>
</Card>
)