Use ratings date in cache filename

This commit is contained in:
Claude Brisson
2024-03-11 10:49:37 +01:00
parent dcaf2a26ff
commit 34aca7fdff
4 changed files with 105 additions and 65 deletions

View File

@@ -2,13 +2,17 @@ package org.jeudego.pairgoth.ratings
import com.republicate.kson.Json import com.republicate.kson.Json
import java.net.URL import java.net.URL
import java.time.LocalDate
object AGARatingsHandler: RatingsHandler(RatingsManager.Ratings.AGA) { object AGARatingsHandler: RatingsHandler(RatingsManager.Ratings.AGA) {
override val defaultURL: URL by lazy { override val defaultURL: URL by lazy {
throw Error("No URL for AGA...") throw Error("No functional URL for AGA...")
} }
override val active = false override val active = false
override fun parsePayload(payload: String): Json.Array { override fun parsePayload(payload: String): Pair<LocalDate, Json.Array> {
return Json.Array() return Pair(
LocalDate.MIN,
Json.Array()
)
} }
} }

View File

@@ -2,33 +2,41 @@ package org.jeudego.pairgoth.ratings
import com.republicate.kson.Json import com.republicate.kson.Json
import java.net.URL import java.net.URL
import java.time.LocalDate
import java.time.format.DateTimeFormatter
object EGFRatingsHandler: RatingsHandler(RatingsManager.Ratings.EGF) { object EGFRatingsHandler: RatingsHandler(RatingsManager.Ratings.EGF) {
val ratingsDateFormatter = DateTimeFormatter.ofPattern("dd MMM yyyy")
override val defaultURL = URL("https://www.europeangodatabase.eu/EGD/EGD_2_0/downloads/allworld_lp.html") override val defaultURL = URL("https://www.europeangodatabase.eu/EGD/EGD_2_0/downloads/allworld_lp.html")
override fun parsePayload(payload: String): Json.Array { override fun parsePayload(payload: String): Pair<LocalDate, Json.Array> {
return payload.lines().filter { val ratingsDateString = payload.lines().filter { it.startsWith("(") }.first().trim().removeSurrounding("(", ")")
it.matches(Regex("\\s+\\d+(?!.*\\(undefined\\)|Anonymous).*")) val ratingsDate = LocalDate.parse(ratingsDateString, ratingsDateFormatter)
}.mapNotNullTo(Json.MutableArray()) { return Pair(
val match = linePattern.matchEntire(it) ratingsDate,
if (match == null) { payload.lines().filter {
logger.error("could not parse line: $it") it.matches(Regex("\\s+\\d+(?!.*\\(undefined\\)|Anonymous).*"))
null }.mapNotNullTo(Json.MutableArray()) {
} else { val match = linePattern.matchEntire(it)
val pairs = groups.map { if (match == null) {
Pair(it, match.groups[it]?.value) logger.error("could not parse line: $it")
}.toTypedArray() null
Json.MutableObject(*pairs).also { player -> } else {
player["origin"] = "EGF" val pairs = groups.map {
// override rank with rating equivalent Pair(it, match.groups[it]?.value)
player["rating"]?.toString()?.toIntOrNull()?.let { rating -> }.toTypedArray()
player["rank"] = ((rating - 2050)/100).let { if (it < 0) "${-it+1}k" else "${it+1}d" } Json.MutableObject(*pairs).also { player ->
} player["origin"] = "EGF"
if ("UK" == player.getString("country")) { // override rank with rating equivalent
player["country"] = "GB" player["rating"]?.toString()?.toIntOrNull()?.let { rating ->
player["rank"] = ((rating - 2050)/100).let { if (it < 0) "${-it+1}k" else "${it+1}d" }
}
if ("UK" == player.getString("country")) {
player["country"] = "GB"
}
} }
} }
} }
} )
} }
// 19574643 Abad Jahin FR 38GJ 20k -- 15 2 T200202B // 19574643 Abad Jahin FR 38GJ 20k -- 15 2 T200202B
var linePattern = var linePattern =

View File

@@ -1,36 +1,40 @@
package org.jeudego.pairgoth.ratings package org.jeudego.pairgoth.ratings
import com.republicate.kson.Json import com.republicate.kson.Json
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import java.net.URL import java.net.URL
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.time.LocalDate
import java.time.format.DateTimeFormatter
object FFGRatingsHandler: RatingsHandler(RatingsManager.Ratings.FFG) { object FFGRatingsHandler: RatingsHandler(RatingsManager.Ratings.FFG) {
val ratingsDateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy")
override val defaultURL = URL("https://ffg.jeudego.org/echelle/echtxt/ech_ffg_V3.txt") override val defaultURL = URL("https://ffg.jeudego.org/echelle/echtxt/ech_ffg_V3.txt")
override fun parsePayload(payload: String): Json.Array { override fun parsePayload(payload: String): Pair<LocalDate, Json.Array> {
return payload.lines().mapNotNullTo(Json.MutableArray()) { line -> val ratingsDateString = payload.lineSequence().first().substringAfter("#Echelle au ").substringBefore(" ")
val match = linePattern.matchEntire(line) val ratingsDate = LocalDate.parse(ratingsDateString, ratingsDateFormatter)
if (match == null) { return Pair(
logger.error("could not parse line: $line") ratingsDate,
null payload.lines().mapNotNullTo(Json.MutableArray()) { line ->
} else { val match = linePattern.matchEntire(line)
val pairs = groups.map { if (match == null) {
Pair(it, match.groups[it]?.value) logger.error("could not parse line: $line")
}.toTypedArray() null
Json.MutableObject(*pairs).also { } else {
it["origin"] = "FFG" val pairs = groups.map {
val rating = it["rating"]?.toString()?.toIntOrNull() Pair(it, match.groups[it]?.value)
if (rating != null) { }.toTypedArray()
it["rank"] = (rating/100).let { if (rating < 0) "${-it+1}k" else "${it+1}d" } Json.MutableObject(*pairs).also {
// then adjust to match EGF ratings it["origin"] = "FFG"
it["rating"] = rating + 2050 val rating = it["rating"]?.toString()?.toIntOrNull()
if (rating != null) {
it["rank"] = (rating/100).let { if (rating < 0) "${-it+1}k" else "${it+1}d" }
// then adjust to match EGF ratings
it["rating"] = rating + 2050
}
} }
} }
} }
} )
} }
override fun defaultCharset() = StandardCharsets.ISO_8859_1 override fun defaultCharset() = StandardCharsets.ISO_8859_1

View File

@@ -6,18 +6,27 @@ import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import org.jeudego.pairgoth.web.WebappManager import org.jeudego.pairgoth.web.WebappManager
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File
import java.net.URL import java.net.URL
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.io.path.name
import kotlin.io.path.useDirectoryEntries
abstract class RatingsHandler(val origin: RatingsManager.Ratings) { abstract class RatingsHandler(val origin: RatingsManager.Ratings) {
companion object {
private val delay = TimeUnit.HOURS.toMillis(1L) private val delay = TimeUnit.HOURS.toMillis(1L)
private val ymd = DateTimeFormatter.ofPattern("yyyyMMdd")
}
private val client = OkHttpClient() private val client = OkHttpClient()
abstract val defaultURL: URL abstract val defaultURL: URL
open val active = true open val active = true
val cacheFile = RatingsManager.path.resolve("${origin.name}.json").toFile() // val cacheFile = RatingsManager.path.resolve("${origin.name}.json").toFile()
lateinit var players: Json.Array lateinit var players: Json.Array
private var updated = false private var updated = false
@@ -25,23 +34,38 @@ abstract class RatingsHandler(val origin: RatingsManager.Ratings) {
WebappManager.properties.getProperty("ratings.${origin.name.lowercase(Locale.ROOT)}")?.let { URL(it) } ?: defaultURL WebappManager.properties.getProperty("ratings.${origin.name.lowercase(Locale.ROOT)}")?.let { URL(it) } ?: defaultURL
} }
private fun getRatingsFiles() = RatingsManager.path.useDirectoryEntries("${origin.name}-*.json") { entries ->
entries.sortedBy { it.fileName.name }.map {
it.toFile()
}.toList()
}
private fun getLatestRatingsFile() = getRatingsFiles().lastOrNull()
private fun initIfNeeded(ratingsFile: File): Boolean {
return if (!this::players.isInitialized) {
players = Json.parse(ratingsFile.readText())?.asArray() ?: Json.Array()
true
} else false
}
fun updateIfNeeded(): Boolean { fun updateIfNeeded(): Boolean {
return if (Date().time - cacheFile.lastModified() > delay) { val latestRatingsFile = getLatestRatingsFile()
RatingsManager.logger.info("Updating $origin cache from $url") if (latestRatingsFile != null && Date().time - latestRatingsFile.lastModified() < delay) {
val payload = fetchPayload() return initIfNeeded(latestRatingsFile)
players = parsePayload(payload).also {
val cachePayload = it.toString()
cacheFile.printWriter().use { out ->
out.println(cachePayload)
}
}
true
} else if (!this::players.isInitialized) {
players = Json.parse(cacheFile.readText())?.asArray() ?: Json.Array()
true
} else {
false
} }
val payload = fetchPayload()
val (lastUpdated, lastPlayers) = parsePayload(payload)
val targetRatingsFilename = "${origin.name}-${ymd.format(lastUpdated)}.json"
if (latestRatingsFile != null && latestRatingsFile.name == targetRatingsFilename) {
return initIfNeeded(latestRatingsFile)
}
RatingsManager.logger.info("Updating $origin cache from $url")
RatingsManager.path.resolve(targetRatingsFilename).toFile().printWriter().use { out ->
out.println(lastPlayers.toString())
}
players = lastPlayers
return true
} }
fun fetchPlayers(): Json.Array { fun fetchPlayers(): Json.Array {
@@ -62,7 +86,7 @@ abstract class RatingsHandler(val origin: RatingsManager.Ratings) {
} }
open fun defaultCharset() = StandardCharsets.UTF_8 open fun defaultCharset() = StandardCharsets.UTF_8
fun updated() = updated fun updated() = updated
abstract fun parsePayload(payload: String): Json.Array abstract fun parsePayload(payload: String): Pair<LocalDate, Json.Array>
val logger = LoggerFactory.getLogger(origin.name) val logger = LoggerFactory.getLogger(origin.name)
val atom = "[-._`'a-zA-ZÀ-ÿ]" val atom = "[-._`'a-zA-ZÀ-ÿ]"
} }