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 java.net.URL
import java.time.LocalDate
object AGARatingsHandler: RatingsHandler(RatingsManager.Ratings.AGA) {
override val defaultURL: URL by lazy {
throw Error("No URL for AGA...")
throw Error("No functional URL for AGA...")
}
override val active = false
override fun parsePayload(payload: String): Json.Array {
return Json.Array()
override fun parsePayload(payload: String): Pair<LocalDate, 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 java.net.URL
import java.time.LocalDate
import java.time.format.DateTimeFormatter
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 fun parsePayload(payload: String): Json.Array {
return payload.lines().filter {
it.matches(Regex("\\s+\\d+(?!.*\\(undefined\\)|Anonymous).*"))
}.mapNotNullTo(Json.MutableArray()) {
val match = linePattern.matchEntire(it)
if (match == null) {
logger.error("could not parse line: $it")
null
} else {
val pairs = groups.map {
Pair(it, match.groups[it]?.value)
}.toTypedArray()
Json.MutableObject(*pairs).also { player ->
player["origin"] = "EGF"
// override rank with rating equivalent
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"
override fun parsePayload(payload: String): Pair<LocalDate, Json.Array> {
val ratingsDateString = payload.lines().filter { it.startsWith("(") }.first().trim().removeSurrounding("(", ")")
val ratingsDate = LocalDate.parse(ratingsDateString, ratingsDateFormatter)
return Pair(
ratingsDate,
payload.lines().filter {
it.matches(Regex("\\s+\\d+(?!.*\\(undefined\\)|Anonymous).*"))
}.mapNotNullTo(Json.MutableArray()) {
val match = linePattern.matchEntire(it)
if (match == null) {
logger.error("could not parse line: $it")
null
} else {
val pairs = groups.map {
Pair(it, match.groups[it]?.value)
}.toTypedArray()
Json.MutableObject(*pairs).also { player ->
player["origin"] = "EGF"
// override rank with rating equivalent
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
var linePattern =

View File

@@ -1,36 +1,40 @@
package org.jeudego.pairgoth.ratings
import com.republicate.kson.Json
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request
import java.net.URL
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.time.LocalDate
import java.time.format.DateTimeFormatter
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 fun parsePayload(payload: String): Json.Array {
return payload.lines().mapNotNullTo(Json.MutableArray()) { line ->
val match = linePattern.matchEntire(line)
if (match == null) {
logger.error("could not parse line: $line")
null
} else {
val pairs = groups.map {
Pair(it, match.groups[it]?.value)
}.toTypedArray()
Json.MutableObject(*pairs).also {
it["origin"] = "FFG"
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 parsePayload(payload: String): Pair<LocalDate, Json.Array> {
val ratingsDateString = payload.lineSequence().first().substringAfter("#Echelle au ").substringBefore(" ")
val ratingsDate = LocalDate.parse(ratingsDateString, ratingsDateFormatter)
return Pair(
ratingsDate,
payload.lines().mapNotNullTo(Json.MutableArray()) { line ->
val match = linePattern.matchEntire(line)
if (match == null) {
logger.error("could not parse line: $line")
null
} else {
val pairs = groups.map {
Pair(it, match.groups[it]?.value)
}.toTypedArray()
Json.MutableObject(*pairs).also {
it["origin"] = "FFG"
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

View File

@@ -6,18 +6,27 @@ import okhttp3.OkHttpClient
import okhttp3.Request
import org.jeudego.pairgoth.web.WebappManager
import org.slf4j.LoggerFactory
import java.io.File
import java.net.URL
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.concurrent.TimeUnit
import kotlin.io.path.name
import kotlin.io.path.useDirectoryEntries
abstract class RatingsHandler(val origin: RatingsManager.Ratings) {
private val delay = TimeUnit.HOURS.toMillis(1L)
companion object {
private val delay = TimeUnit.HOURS.toMillis(1L)
private val ymd = DateTimeFormatter.ofPattern("yyyyMMdd")
}
private val client = OkHttpClient()
abstract val defaultURL: URL
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
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
}
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 {
return if (Date().time - cacheFile.lastModified() > delay) {
RatingsManager.logger.info("Updating $origin cache from $url")
val payload = fetchPayload()
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 latestRatingsFile = getLatestRatingsFile()
if (latestRatingsFile != null && Date().time - latestRatingsFile.lastModified() < delay) {
return initIfNeeded(latestRatingsFile)
}
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 {
@@ -62,7 +86,7 @@ abstract class RatingsHandler(val origin: RatingsManager.Ratings) {
}
open fun defaultCharset() = StandardCharsets.UTF_8
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 atom = "[-._`'a-zA-ZÀ-ÿ]"
}