[O] Better trend graph

pull/17/head
Azalea 2024-03-03 14:53:18 -05:00
parent 9ae23e4395
commit 48819c10a9
2 changed files with 23 additions and 5 deletions

View File

@ -13,6 +13,8 @@ import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader import org.springframework.web.bind.annotation.RequestHeader
import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RequestParam
import java.time.LocalDate
import java.time.format.DateTimeFormatter
typealias RP = RequestParam typealias RP = RequestParam
typealias RB = RequestBody typealias RB = RequestBody
@ -40,8 +42,6 @@ operator fun Int.minus(message: String): Nothing {
val emailRegex = "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9-]+(\\.[\\p{L}0-9-]+)*(\\.[\\p{L}]{2,})$".toRegex() val emailRegex = "^(?=.{1,64}@)[\\p{L}0-9_-]+(\\.[\\p{L}0-9_-]+)*@[^-][\\p{L}0-9-]+(\\.[\\p{L}0-9-]+)*(\\.[\\p{L}]{2,})$".toRegex()
fun Str.isValidEmail(): Bool = emailRegex.matches(this) fun Str.isValidEmail(): Bool = emailRegex.matches(this)
fun millis() = System.currentTimeMillis()
val HTTP = HttpClient(CIO) { val HTTP = HttpClient(CIO) {
install(ContentNegotiation) { install(ContentNegotiation) {
json(Json { json(Json {
@ -51,6 +51,11 @@ val HTTP = HttpClient(CIO) {
} }
} }
// Date and time
fun millis() = System.currentTimeMillis()
val DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd")
fun LocalDate.isoDate() = format(DATE_FORMAT)
fun Long.toHex(len: Int = 16): Str = "0x${this.toString(len).padStart(len, '0').uppercase()}" fun Long.toHex(len: Int = 16): Str = "0x${this.toString(len).padStart(len, '0').uppercase()}"
fun Map<String, Any>.toUrl() = entries.joinToString("&") { (k, v) -> "$k=$v" } fun Map<String, Any>.toUrl() = entries.joinToString("&") { (k, v) -> "$k=$v" }
operator fun <K, V> Map<K, V>.plus(map: Map<K, V>) = operator fun <K, V> Map<K, V>.plus(map: Map<K, V>) =

View File

@ -1,5 +1,6 @@
package icu.samnyan.aqua.net.utils package icu.samnyan.aqua.net.utils
import ext.isoDate
import ext.millis import ext.millis
import ext.minus import ext.minus
import icu.samnyan.aqua.net.games.GenericGameSummary import icu.samnyan.aqua.net.games.GenericGameSummary
@ -9,6 +10,7 @@ import icu.samnyan.aqua.net.games.TrendOut
import icu.samnyan.aqua.sega.general.model.Card import icu.samnyan.aqua.sega.general.model.Card
import org.springframework.data.jpa.repository.JpaRepository import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.repository.NoRepositoryBean import org.springframework.data.repository.NoRepositoryBean
import java.time.LocalDate
data class TrendLog(val date: String, val rating: Int) data class TrendLog(val date: String, val rating: Int)
@ -16,8 +18,13 @@ data class TrendLog(val date: String, val rating: Int)
* Find the trend of a user's rating * Find the trend of a user's rating
*/ */
fun findTrend(log: List<TrendLog>): List<TrendOut> { fun findTrend(log: List<TrendLog>): List<TrendOut> {
// Limit to 60 days by filtering out the dates that are too old
val minDate = LocalDate.now().minusDays(60).isoDate()
val now = LocalDate.now().isoDate()
// O(n log n) // O(n log n)
val d = log.sortedBy { it.date }.toList() val d = log.filter { it.date >= minDate }.sortedBy { it.date }.toList()
// Precompute the play counts for each date in O(n) // Precompute the play counts for each date in O(n)
val playCounts = d.groupingBy { it.date }.eachCount() val playCounts = d.groupingBy { it.date }.eachCount()
@ -26,10 +33,16 @@ fun findTrend(log: List<TrendLog>): List<TrendOut> {
val maxRating = d.groupingBy { it.date }.fold(0) { acc, e -> maxOf(acc, e.rating) } val maxRating = d.groupingBy { it.date }.fold(0) { acc, e -> maxOf(acc, e.rating) }
// Use the precomputed play counts // Use the precomputed play counts
return d.distinctBy { it.date } val trend = d.distinctBy { it.date }
.map { TrendOut(it.date, maxRating[it.date] ?: 0, .map { TrendOut(it.date, maxRating[it.date] ?: 0,
playCounts[it.date] ?: 0) } playCounts[it.date] ?: 0) }
.sortedBy { it.date } .sortedBy { it.date }.toMutableList()
// Fill in the missing dates (min date and current date)
trend[0].let { if (it.date != minDate) trend.add(0, TrendOut(minDate, 0, 0)) }
trend.last().let { if (it.date != now) trend.add(TrendOut(now, it.rating, 0)) }
return trend
} }
// Here are some interfaces to generalize across multiple games // Here are some interfaces to generalize across multiple games