Use ratings date in cache filename
This commit is contained in:
@@ -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()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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 =
|
||||||
|
@@ -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
|
||||||
|
@@ -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À-ÿ]"
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user