Skip to content

Commit 0427d2a

Browse files
committed
Add SESSION_LENGTH_AVERAGE datapoint
1 parent 6a41298 commit 0427d2a

9 files changed

Lines changed: 226 additions & 2 deletions

File tree

Plan/common/src/main/java/com/djrapitops/plan/delivery/domain/auth/WebPermission.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ public enum WebPermission implements Supplier<String>, Lang {
151151
DATA_PLAYER_SESSION_COUNT("See Session count datapoint of players"),
152152
DATA_SERVER_SESSION_COUNT("See Session count datapoint of servers"),
153153
DATA_NETWORK_SESSION_COUNT("See Session count datapoint of network"),
154+
DATA_PLAYER_SESSION_LENGTH_AVERAGE("See Average session length datapoint of players"),
155+
DATA_SERVER_SESSION_LENGTH_AVERAGE("See Average session length datapoint of servers"),
156+
DATA_NETWORK_SESSION_LENGTH_AVERAGE("See Average session length datapoint of network"),
154157
DATA_SERVER_PLAYERS_ONLINE_PEAK("See Player online peak -datapoint of servers"),
155158
DATA_NETWORK_PLAYERS_ONLINE_PEAK("See Player online peak -datapoint of network"),
156159

Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/datapoint/DatapointType.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public enum DatapointType {
4141
NEW_PLAYERS(NewPlayers.class, DatapointCacheKey.SESSION),
4242
REGULAR_PLAYERS(RegularPlayers.class, DatapointCacheKey.SESSION),
4343
SESSION_COUNT(SessionCount.class, DatapointCacheKey.SESSION),
44+
SESSION_LENGTH_AVERAGE(SessionLengthAverage.class, DatapointCacheKey.SESSION),
4445
PLAYERS_ONLINE_PEAK(PlayersOnlinePeak.class, DatapointCacheKey.TPS);
4546

4647
private final Class<? extends Datapoint<?>> datapointClass;

Plan/common/src/main/java/com/djrapitops/plan/delivery/rendering/json/datapoint/types/DatapointModule.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ public interface DatapointModule {
7878

7979
@Binds
8080
@IntoSet
81-
Datapoint<?> bindPlayerOnlinePeak(PlayersOnlinePeak playersOnlinePeak);
81+
Datapoint<?> bindSessionLengthAverage(SessionLengthAverage sessionLengthAverage);
8282

83+
@Binds
84+
@IntoSet
85+
Datapoint<?> bindPlayerOnlinePeak(PlayersOnlinePeak playersOnlinePeak);
8386
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* This file is part of Player Analytics (Plan).
3+
*
4+
* Plan is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License v3 as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* Plan is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
package com.djrapitops.plan.delivery.rendering.json.datapoint.types;
18+
19+
import com.djrapitops.plan.delivery.domain.auth.WebPermission;
20+
import com.djrapitops.plan.delivery.domain.datatransfer.GenericFilter;
21+
import com.djrapitops.plan.delivery.rendering.json.datapoint.Datapoint;
22+
import com.djrapitops.plan.delivery.rendering.json.datapoint.DatapointType;
23+
import com.djrapitops.plan.gathering.cache.SessionCache;
24+
import com.djrapitops.plan.gathering.domain.ActiveSession;
25+
import com.djrapitops.plan.identification.ServerInfo;
26+
import com.djrapitops.plan.storage.database.DBSystem;
27+
import com.djrapitops.plan.storage.database.Database;
28+
import com.djrapitops.plan.storage.database.queries.objects.PlaytimeAndCount;
29+
import com.djrapitops.plan.storage.database.queries.objects.SessionQueries;
30+
31+
import javax.inject.Inject;
32+
import javax.inject.Singleton;
33+
import java.util.Optional;
34+
import java.util.UUID;
35+
import java.util.stream.Stream;
36+
37+
/**
38+
* Datapoint for average session length.
39+
*
40+
* @author AuroraLS3
41+
*/
42+
@Singleton
43+
public class SessionLengthAverage implements Datapoint<Long> {
44+
45+
private final DBSystem dbSystem;
46+
private final ServerInfo serverInfo;
47+
48+
@Inject
49+
public SessionLengthAverage(DBSystem dbSystem, ServerInfo serverInfo) {
50+
this.dbSystem = dbSystem;
51+
this.serverInfo = serverInfo;
52+
}
53+
54+
@Override
55+
public DatapointType getType() {
56+
return DatapointType.SESSION_LENGTH_AVERAGE;
57+
}
58+
59+
@Override
60+
public FormatType getFormatType() {
61+
return FormatType.TIME_AMOUNT;
62+
}
63+
64+
@Override
65+
public WebPermission getPermission(GenericFilter filter) {
66+
if (filter.getPlayerUUID().isPresent()) {
67+
return WebPermission.DATA_PLAYER_SESSION_LENGTH_AVERAGE;
68+
} else if (!filter.getServerUUIDs().isEmpty()) {
69+
return WebPermission.DATA_SERVER_SESSION_LENGTH_AVERAGE;
70+
} else {
71+
return WebPermission.DATA_NETWORK_SESSION_LENGTH_AVERAGE;
72+
}
73+
}
74+
75+
@Override
76+
public Optional<Long> getValue(GenericFilter filter) {
77+
Database db = dbSystem.getDatabase();
78+
Optional<UUID> playerUUID = filter.getPlayerUUID();
79+
80+
long playtime;
81+
long sessionCount;
82+
83+
long now = System.currentTimeMillis();
84+
if (playerUUID.isPresent()) {
85+
PlaytimeAndCount dbResults = db.query(SessionQueries.playtimeAndCount(filter.getAfter(), filter.getBefore(), playerUUID.get(), filter.getServerUUIDs()));
86+
playtime = dbResults.getPlaytime();
87+
sessionCount = dbResults.getCount();
88+
89+
if (filter.contains(serverInfo.getServerUUID())) {
90+
Optional<ActiveSession> cachedSession = SessionCache.getCachedSession(playerUUID.get())
91+
.filter(session -> session.isWithin(filter.getAfter(), filter.getBefore()));
92+
if (cachedSession.isPresent()) {
93+
playtime += now - cachedSession.get().getStart();
94+
sessionCount += 1;
95+
}
96+
}
97+
} else {
98+
PlaytimeAndCount dbResults = db.query(SessionQueries.playtimeAndCount(filter.getAfter(), filter.getBefore(), filter.getServerUUIDs()));
99+
playtime = dbResults.getPlaytime();
100+
sessionCount = dbResults.getCount();
101+
102+
if (filter.contains(serverInfo.getServerUUID())) {
103+
Stream<ActiveSession> activeSessions = SessionCache.getActiveSessions().stream()
104+
.filter(session -> session.isWithin(filter.getAfter(), filter.getBefore()));
105+
106+
for (ActiveSession session : (Iterable<ActiveSession>) activeSessions::iterator) {
107+
playtime += now - session.getStart();
108+
sessionCount += 1;
109+
}
110+
}
111+
}
112+
113+
if (sessionCount == 0) {
114+
return Optional.of(0L);
115+
}
116+
117+
return Optional.of(playtime / sessionCount);
118+
}
119+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* This file is part of Player Analytics (Plan).
3+
*
4+
* Plan is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU Lesser General Public License v3 as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* Plan is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public License
15+
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
package com.djrapitops.plan.storage.database.queries.objects;
18+
19+
/**
20+
* Object for holding total playtime and session count.
21+
*
22+
* @author AuroraLS3
23+
*/
24+
public class PlaytimeAndCount {
25+
26+
private final long playtime;
27+
private final long count;
28+
29+
public PlaytimeAndCount(long playtime, long count) {
30+
this.playtime = playtime;
31+
this.count = count;
32+
}
33+
34+
public long getPlaytime() {
35+
return playtime;
36+
}
37+
38+
public long getCount() {
39+
return count;
40+
}
41+
}

Plan/common/src/main/java/com/djrapitops/plan/storage/database/queries/objects/SessionQueries.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,56 @@ public Long processResults(ResultSet set) throws SQLException {
537537
};
538538
}
539539

540+
public static Query<PlaytimeAndCount> playtimeAndCount(long after, long before, List<ServerUUID> serverUUIDs) {
541+
return db -> {
542+
String sql = SELECT +
543+
playtimeColumn(db.getSql(), after, before) + "," +
544+
"COUNT(1) as count" +
545+
FROM + SessionsTable.TABLE_NAME +
546+
WHERE + (!serverUUIDs.isEmpty() ? SessionsTable.SERVER_ID + " IN " + ServerTable.selectServerIds(serverUUIDs) +
547+
AND : "") + SessionsTable.SESSION_END + ">=?" +
548+
AND + SessionsTable.SESSION_START + "<=?";
549+
return db.query(new QueryStatement<>(sql) {
550+
@Override
551+
public void prepare(PreparedStatement statement) throws SQLException {
552+
statement.setLong(1, after);
553+
statement.setLong(2, before);
554+
}
555+
556+
@Override
557+
public PlaytimeAndCount processResults(ResultSet set) throws SQLException {
558+
return set.next() ? new PlaytimeAndCount(set.getLong("playtime"), set.getLong("count")) : new PlaytimeAndCount(0L, 0L);
559+
}
560+
});
561+
};
562+
}
563+
564+
public static Query<PlaytimeAndCount> playtimeAndCount(long after, long before, UUID playerUUID, List<ServerUUID> serverUUIDs) {
565+
return db -> {
566+
String sql = SELECT +
567+
playtimeColumn(db.getSql(), after, before) + "," +
568+
"COUNT(1) as count" +
569+
FROM + SessionsTable.TABLE_NAME +
570+
WHERE + (!serverUUIDs.isEmpty() ? SessionsTable.SERVER_ID + " IN " + ServerTable.selectServerIds(serverUUIDs) +
571+
AND : "") + SessionsTable.SESSION_END + ">=?" +
572+
AND + SessionsTable.SESSION_START + "<=?" +
573+
AND + SessionsTable.USER_ID + "=" + UsersTable.SELECT_USER_ID;
574+
return db.query(new QueryStatement<>(sql) {
575+
@Override
576+
public void prepare(PreparedStatement statement) throws SQLException {
577+
statement.setLong(1, after);
578+
statement.setLong(2, before);
579+
statement.setString(3, playerUUID.toString());
580+
}
581+
582+
@Override
583+
public PlaytimeAndCount processResults(ResultSet set) throws SQLException {
584+
return set.next() ? new PlaytimeAndCount(set.getLong("playtime"), set.getLong("count")) : new PlaytimeAndCount(0L, 0L);
585+
}
586+
});
587+
};
588+
}
589+
540590
private static @NonNull String playtimeColumn(Sql sql, long after, long before) {
541591
return "SUM(" + sql.least(SessionsTable.SESSION_END + "," + before) + "-" + sql.greatest(SessionsTable.SESSION_START + "," + after) + ") as playtime";
542592
}

Plan/common/src/test/java/com/djrapitops/plan/delivery/webserver/AccessControlTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ static Stream<Arguments> testCases() {
210210
Arguments.of("/v1/datapoint?type=SESSION_COUNT", WebPermission.DATA_NETWORK_SESSION_COUNT, 200, 403),
211211
Arguments.of("/v1/datapoint?type=SESSION_COUNT&server=" + TestConstants.SERVER_UUID_STRING, WebPermission.DATA_SERVER_SESSION_COUNT, 200, 403),
212212
Arguments.of("/v1/datapoint?type=SESSION_COUNT&player=" + TestConstants.PLAYER_ONE_UUID_STRING, WebPermission.DATA_PLAYER_SESSION_COUNT, 200, 403),
213+
Arguments.of("/v1/datapoint?type=SESSION_LENGTH_AVERAGE", WebPermission.DATA_NETWORK_SESSION_LENGTH_AVERAGE, 200, 403),
214+
Arguments.of("/v1/datapoint?type=SESSION_LENGTH_AVERAGE&server=" + TestConstants.SERVER_UUID_STRING, WebPermission.DATA_SERVER_SESSION_LENGTH_AVERAGE, 200, 403),
215+
Arguments.of("/v1/datapoint?type=SESSION_LENGTH_AVERAGE&player=" + TestConstants.PLAYER_ONE_UUID_STRING, WebPermission.DATA_PLAYER_SESSION_LENGTH_AVERAGE, 200, 403),
213216
Arguments.of("/v1/datapoint?type=PLAYERS_ONLINE_PEAK", WebPermission.DATA_NETWORK_PLAYERS_ONLINE_PEAK, 200, 403),
214217
Arguments.of("/v1/datapoint?type=PLAYERS_ONLINE_PEAK&server=" + TestConstants.SERVER_UUID_STRING, WebPermission.DATA_SERVER_PLAYERS_ONLINE_PEAK, 200, 403),
215218
Arguments.of("/v1/datapoint?type=PLAYERS_ONLINE_PEAK&player=" + TestConstants.PLAYER_ONE_UUID_STRING, WebPermission.DATA_PLAYER, 400, 403),

Plan/react/dashboard/src/components/datapoint/QueryDatapoint.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type Props<K extends DatapointType> = {
2727
permission?: string;
2828
} & Omit<DatapointProps, 'value'>;
2929

30+
type ValueProps<K extends DatapointType> = Omit<Props<K>, 'color' | 'name' | 'icon'>;
31+
3032
type FormatProps<K extends DatapointType> = Readonly<{
3133
value?: DatapointTypeMap[K],
3234
formatType?: FormatType
@@ -116,7 +118,7 @@ export function QueryDatapoint<K extends DatapointType>({permission, dataType, f
116118
/>
117119
}
118120

119-
export function QueryDatapointValue<K extends DatapointType>({permission, dataType, filter}: Props<K>) {
121+
export function QueryDatapointValue<K extends DatapointType>({permission, dataType, filter}: ValueProps<K>) {
120122
const {hasPermission} = useAuth();
121123
const allowed = hasPermission(calculatePermission(dataType, permission, filter));
122124
const {data, error} = useDatapointQuery(allowed, dataType, filter);

Plan/react/dashboard/src/dataHooks/model/datapoint/Datapoint.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export enum DatapointType {
2222
REGULAR_PLAYERS = "REGULAR_PLAYERS",
2323
PLAYERS_ONLINE_PEAK = "PLAYERS_ONLINE_PEAK",
2424
SESSION_COUNT = "SESSION_COUNT",
25+
SESSION_LENGTH_AVERAGE = "SESSION_LENGTH_AVERAGE",
2526
}
2627

2728
export type DatapointTypeMap = {
@@ -33,6 +34,7 @@ export type DatapointTypeMap = {
3334
NEW_PLAYERS: number;
3435
REGULAR_PLAYERS: number;
3536
SESSION_COUNT: number;
37+
SESSION_LENGTH_AVERAGE: number;
3638
SERVER_OCCUPIED: OutOf;
3739
MOST_ACTIVE_WORLD: OutOfCategory;
3840
MOST_ACTIVE_GAME_MODE: OutOfCategory;

0 commit comments

Comments
 (0)