@@ -7,154 +7,89 @@ import dev.slne.surf.cloud.api.common.util.toObjectList
77import it.unimi.dsi.fastutil.objects.Object2ObjectMap
88import it.unimi.dsi.fastutil.objects.ObjectList
99import it.unimi.dsi.fastutil.objects.ObjectSet
10+ import kotlinx.serialization.Contextual
11+ import kotlinx.serialization.Serializable
12+ import java.time.Instant
1013import java.time.ZonedDateTime
1114import kotlin.time.Duration
1215import kotlin.time.Duration.Companion.seconds
1316
1417class PlaytimeImpl (private val entries : ObjectList <PlaytimeEntry >) : Playtime {
15- override fun sumPlaytimes (since : ZonedDateTime ? ): Duration {
16- if (since == null ) {
17- return entries.sumOf { it.durationSeconds }.seconds
18- }
19-
20- return entries
21- .filter { it.createdAt.isAfter(since) }
22- .sumOf { it.durationSeconds }
23- .seconds
24- }
18+ override fun sumPlaytimes (since : ZonedDateTime ? ): Duration = entries
19+ .filter { since == null || it.createdAt.isAfter(since) }
20+ .sumOf { it.durationSeconds }
21+ .seconds
2522
2623 override fun sumByCategory (
2724 category : String ,
2825 since : ZonedDateTime ?
29- ): Duration {
30- if (since == null ) {
31- return entries
32- .filter { it.category.equals(category, true ) }
33- .sumOf { it.durationSeconds }
34- .seconds
26+ ): Duration = entries
27+ .filter {
28+ it.category.equals(category, ignoreCase = true )
29+ && (since == null || it.createdAt.isAfter(since))
3530 }
36-
37- return entries
38- .filter { it.category.equals(category, true ) && it.createdAt.isAfter(since) }
39- .sumOf { it.durationSeconds }
40- .seconds
41- }
31+ .sumOf { it.durationSeconds }
32+ .seconds
4233
4334 override fun sumByServer (
4435 server : String ,
4536 since : ZonedDateTime ?
46- ): Duration {
47- if (since == null ) {
48- return entries
49- .filter { it.server.equals(server, true ) }
50- .sumOf { it.durationSeconds }
51- .seconds
37+ ): Duration = entries
38+ .filter {
39+ it.server.equals(server, ignoreCase = true )
40+ && (since == null || it.createdAt.isAfter(since))
5241 }
42+ .sumOf { it.durationSeconds }
43+ .seconds
5344
54- return entries
55- .filter { it.server.equals(server, true ) && it.createdAt.isAfter(since) }
56- .sumOf { it.durationSeconds }
57- .seconds
58- }
59-
60- override fun getCategories (): ObjectSet <String > {
61- return entries.mapTo(mutableObjectSetOf()) { it.category }
62- }
45+ override fun getCategories (): ObjectSet <String > =
46+ entries.mapTo(mutableObjectSetOf()) { it.category }
6347
64- override fun getServers (): ObjectSet <String > {
65- return entries.mapTo(mutableObjectSetOf()) { it.server }
66- }
48+ override fun getServers (): ObjectSet <String > =
49+ entries.mapTo(mutableObjectSetOf()) { it.server }
6750
6851 override fun playtimeFor (
6952 server : String ,
7053 category : String? ,
7154 since : ZonedDateTime ?
72- ): Duration {
73- if (since == null ) {
74- return entries
75- .filter {
76- it.server.equals(server, true ) && (category == null || it.category.equals(
77- category,
78- true
79- ))
80- }
81- .sumOf { it.durationSeconds }
82- .seconds
83- }
84-
85- return entries
86- .filter {
87- it.server.equals(server, true ) && (category == null || it.category.equals(
88- category,
89- true
90- )) && it.createdAt.isAfter(since)
91- }
92- .sumOf { it.durationSeconds }
93- .seconds
94- }
95-
96- override fun playtimesPerServer (since : ZonedDateTime ? ): Object2ObjectMap <String , Duration > {
97- if (since == null ) {
98- return entries.groupBy { it.server }
99- .mapValuesTo(mutableObject2ObjectMapOf()) { (_, entry) -> entry.sumOf { it.durationSeconds }.seconds }
55+ ): Duration = entries
56+ .filter {
57+ it.server.equals(server, ignoreCase = true )
58+ && (category == null || it.category.equals(category, ignoreCase = true ))
59+ && (since == null || it.createdAt.isAfter(since))
10060 }
61+ .sumOf { it.durationSeconds }
62+ .seconds
10163
102- return entries
103- .filter { it.createdAt.isAfter(since) }
64+ override fun playtimesPerServer ( since : ZonedDateTime ? ): Object2ObjectMap < String , Duration > =
65+ entries.filter { since == null || it.createdAt.isAfter(since) }
10466 .groupBy { it.server }
105- .mapValuesTo(mutableObject2ObjectMapOf()) { (_, entry) -> entry.sumOf { it.durationSeconds }.seconds }
106- }
107-
108- override fun playtimesPerCategory (since : ZonedDateTime ? ): Object2ObjectMap <String , Duration > {
109- if (since == null ) {
110- return entries.groupBy { it.category }
111- .mapValuesTo(mutableObject2ObjectMapOf()) { (_, entry) -> entry.sumOf { it.durationSeconds }.seconds }
112- }
67+ .mapValuesTo(mutableObject2ObjectMapOf()) { (_, group) ->
68+ group.sumOf { it.durationSeconds }.seconds
69+ }
11370
114- return entries
115- .filter { it.createdAt.isAfter(since) }
71+ override fun playtimesPerCategory ( since : ZonedDateTime ? ): Object2ObjectMap < String , Duration > =
72+ entries.filter { since == null || it.createdAt.isAfter(since) }
11673 .groupBy { it.category }
117- .mapValuesTo(mutableObject2ObjectMapOf()) { (_, entry) -> entry.sumOf { it.durationSeconds }.seconds }
118- }
74+ .mapValuesTo(mutableObject2ObjectMapOf()) { (_, group) ->
75+ group.sumOf { it.durationSeconds }.seconds
76+ }
11977
12078 override fun averagePlaytimePerServer (
12179 category : String? ,
12280 since : ZonedDateTime ?
12381 ): Duration {
124- if (since == null ) {
125- val values = entries
126- .filter { category == null || it.category.equals(category, true ) }
127- .groupBy { it.server }
128- .mapValuesTo(mutableObject2ObjectMapOf()) { (_, entry) -> entry.sumOf { it.durationSeconds } }
129- .values
130-
131- if (values.isEmpty()) {
132- return Duration .ZERO
133- }
134-
135- return values
136- .average()
137- .seconds
138- }
139-
140- val values = entries
82+ val sumsPerServer = entries
14183 .filter {
142- (category == null || it.category.equals(
143- category,
144- true
145- )) && it.createdAt.isAfter(since)
84+ (category == null || it.category.equals(category, ignoreCase = true )) &&
85+ (since == null || it.createdAt.isAfter(since))
14686 }
14787 .groupBy { it.server }
148- .mapValuesTo(mutableObject2ObjectMapOf()) { (_, entry ) -> entry .sumOf { it.durationSeconds } }
88+ .mapValues { (_, group ) -> group .sumOf { it.durationSeconds } }
14989 .values
15090
151- if (values.isEmpty()) {
152- return Duration .ZERO
153- }
154-
155- return values
156- .average()
157- .seconds
91+ return if (sumsPerServer.isEmpty()) Duration .ZERO
92+ else sumsPerServer.average().seconds
15893 }
15994
16095 override fun timeline (
@@ -165,8 +100,8 @@ class PlaytimeImpl(private val entries: ObjectList<PlaytimeEntry>) : Playtime {
165100 val map = mutableObject2ObjectMapOf<ZonedDateTime , Duration >()
166101
167102 for (entry in entries) {
168- if (category != null && ! entry.category.equals(category, true )) continue
169- if (server != null && ! entry.server.equals(server, true )) continue
103+ if (category != null && ! entry.category.equals(category, ignoreCase = true )) continue
104+ if (server != null && ! entry.server.equals(server, ignoreCase = true )) continue
170105
171106 val bucket = floorToInterval(entry.createdAt, interval)
172107 map.merge(bucket, entry.durationSeconds.seconds) { a, b -> a + b }
@@ -178,63 +113,54 @@ class PlaytimeImpl(private val entries: ObjectList<PlaytimeEntry>) : Playtime {
178113 override fun topServers (
179114 limit : Int ,
180115 since : ZonedDateTime ?
181- ): ObjectList <Pair <String , Duration >> {
182- if (since == null ) {
183- return entries
184- .groupBy { it.server }
185- .mapValues { (_, entry) -> entry.sumOf { it.durationSeconds }.seconds }
186- .toList()
187- .sortedByDescending { it.second }
188- .take(limit)
189- .toObjectList()
190- }
191-
192- return entries
193- .filter { it.createdAt.isAfter(since) }
194- .groupBy { it.server }
195- .mapValues { (_, entry) -> entry.sumOf { it.durationSeconds }.seconds }
196- .toList()
197- .sortedByDescending { it.second }
198- .take(limit)
199- .toObjectList()
200- }
116+ ): ObjectList <Pair <String , Duration >> = entries
117+ .filter { since == null || it.createdAt.isAfter(since) }
118+ .groupBy { it.server }
119+ .mapValues { (_, group) -> group.sumOf { it.durationSeconds }.seconds }
120+ .toList()
121+ .sortedByDescending { it.second }
122+ .take(limit)
123+ .toObjectList()
201124
202125 override fun topCategories (
203126 limit : Int ,
204127 since : ZonedDateTime ?
205- ): ObjectList <Pair <String , Duration >> {
206- if (since == null ) {
207- return entries
208- .groupBy { it.category }
209- .mapValues { (_, entry) -> entry.sumOf { it.durationSeconds }.seconds }
210- .toList()
211- .sortedByDescending { it.second }
212- .take(limit)
213- .toObjectList()
214- }
215-
216- return entries
217- .filter { it.createdAt.isAfter(since) }
218- .groupBy { it.category }
219- .mapValues { (_, entry) -> entry.sumOf { it.durationSeconds }.seconds }
220- .toList()
221- .sortedByDescending { it.second }
222- .take(limit)
223- .toObjectList()
224- }
128+ ): ObjectList <Pair <String , Duration >> = entries
129+ .filter { since == null || it.createdAt.isAfter(since) }
130+ .groupBy { it.category }
131+ .mapValues { (_, group) -> group.sumOf { it.durationSeconds }.seconds }
132+ .toList()
133+ .sortedByDescending { it.second }
134+ .take(limit)
135+ .toObjectList()
225136}
226137
138+ /* *
139+ * Floors the given [ZonedDateTime] to the nearest interval defined by the [Duration].
140+ *
141+ * @param time The [ZonedDateTime] to be floored.
142+ * @param interval The [Duration] representing the interval to floor to.
143+ * @return A new [ZonedDateTime] floored to the nearest interval.
144+ */
227145private fun floorToInterval (time : ZonedDateTime , interval : Duration ): ZonedDateTime {
228146 val seconds = interval.inWholeSeconds
229147 val epochSeconds = time.toEpochSecond()
230148 val floored = (epochSeconds / seconds) * seconds
231- return ZonedDateTime .ofInstant(java.time. Instant .ofEpochSecond(floored), time.zone)
149+ return ZonedDateTime .ofInstant(Instant .ofEpochSecond(floored), time.zone)
232150}
233151
234-
152+ /* *
153+ * Represents a single entry in the playtime data.
154+ *
155+ * @property category The category of the playtime entry.
156+ * @property server The server associated with the playtime entry.
157+ * @property durationSeconds The duration of playtime in seconds.
158+ * @property createdAt The timestamp when the playtime entry was created.
159+ */
160+ @Serializable
235161data class PlaytimeEntry (
236162 val category : String ,
237163 val server : String ,
238164 val durationSeconds : Long ,
239- val createdAt : ZonedDateTime
165+ val createdAt : @Contextual ZonedDateTime ,
240166)
0 commit comments