From 419d41be4c6030b6e91961a46f754711cc19b817 Mon Sep 17 00:00:00 2001 From: Claude Brisson Date: Fri, 15 Mar 2024 15:13:24 +0100 Subject: [PATCH] Add rating date tootip, and avoid registering twice a player --- .../kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt | 3 +++ .../main/kotlin/org/jeudego/pairgoth/model/Tournament.kt | 1 + .../org/jeudego/pairgoth/ratings/EGFRatingsHandler.kt | 4 ++++ .../org/jeudego/pairgoth/ratings/RatingsHandler.kt | 4 +++- .../org/jeudego/pairgoth/ratings/RatingsManager.kt | 8 +++++++- .../kotlin/org/jeudego/pairgoth/view/PairgothTool.kt | 3 +++ view-webapp/src/main/webapp/WEB-INF/translations/fr | 8 +++++++- view-webapp/src/main/webapp/js/tour-registration.inc.js | 9 ++++++++- view-webapp/src/main/webapp/tour-registration.inc.html | 7 +++++-- 9 files changed, 41 insertions(+), 6 deletions(-) diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt index 99d99fd..7454eae 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PlayerHandler.kt @@ -23,6 +23,9 @@ object PlayerHandler: PairgothApiHandler { val tournament = getTournament(request) val payload = getObjectPayload(request) val player = Player.fromJson(payload) + player.externalIds.forEach { dbId, pId -> + if (tournament.hasPlayer(dbId, pId)) badRequest("player already registered") + } tournament.players[player.id] = player tournament.dispatchEvent(PlayerAdded, request, player.toJson()) return Json.Object("success" to true, "id" to player.id) diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt index 7013859..2320d24 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/model/Tournament.kt @@ -119,6 +119,7 @@ sealed class Tournament ( } fun pairedPlayers() = games.flatMap { it.values }.flatMap { listOf(it.black, it.white) }.toSet() + fun hasPlayer(dbId: DatabaseId, pId: String) = players.values.filter { player -> pId == player.externalIds[dbId] }.isNotEmpty() } // standard tournament of individuals diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/EGFRatingsHandler.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/EGFRatingsHandler.kt index 8c5141c..e1dcd8a 100644 --- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/EGFRatingsHandler.kt +++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/EGFRatingsHandler.kt @@ -33,6 +33,10 @@ object EGFRatingsHandler: RatingsHandler(RatingsManager.Ratings.EGF) { if ("UK" == player.getString("country")) { player["country"] = "GB" } + // fix for missing firstnames + if (player.getString("firstname") == null) { + player["firstname"] = "" + } } } } diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/RatingsHandler.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/RatingsHandler.kt index bd39cb3..3819bef 100644 --- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/RatingsHandler.kt +++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/RatingsHandler.kt @@ -32,7 +32,9 @@ abstract class RatingsHandler(val origin: RatingsManager.Ratings) { WebappManager.properties.getProperty("ratings.${origin.name.lowercase(Locale.ROOT)}")?.let { URL(it) } ?: defaultURL } - public fun getRatingsFiles() = RatingsManager.path.useDirectoryEntries("${origin.name}-*.json") { entries -> + fun activeDate() = ymd.parse(activeRatingsFile.name.substringBefore(".json").substringAfter("${origin.name}-")) + + fun getRatingsFiles() = RatingsManager.path.useDirectoryEntries("${origin.name}-*.json") { entries -> entries.sortedBy { it.fileName.name }.map { it.toFile() }.toList() diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/RatingsManager.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/RatingsManager.kt index 7f8a20d..07075e6 100644 --- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/RatingsManager.kt +++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/ratings/RatingsManager.kt @@ -8,6 +8,7 @@ import java.io.BufferedReader import java.lang.Exception import java.nio.file.Path import java.nio.file.Paths +import java.time.format.DateTimeFormatter import java.util.* import java.util.concurrent.locks.ReadWriteLock import java.util.concurrent.locks.ReentrantReadWriteLock @@ -131,10 +132,15 @@ object RatingsManager: Runnable { val left = a as Json.Object val right = b as Json.Object val cmp = left.getString("name")!!.compareTo(right.getString("name")!!) - if (cmp == 0) left.getString("firstname")!!.compareTo(right.getString("firstname")!!) + if (cmp == 0) (left.getString("firstname") ?: "").compareTo(right.getString("firstname") ?: "") else cmp }.toCollection(Json.MutableArray()) } val index = PlayerIndex() + + public fun getRatingsDates() = ratingsHandlers.filter{ it.value.active }.map { + Pair(it.key.name.lowercase(), + DateTimeFormatter.ISO_LOCAL_DATE.format(it.value.activeDate())) + }.toMap() } diff --git a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/view/PairgothTool.kt b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/view/PairgothTool.kt index 1009671..9daf75c 100644 --- a/view-webapp/src/main/kotlin/org/jeudego/pairgoth/view/PairgothTool.kt +++ b/view-webapp/src/main/kotlin/org/jeudego/pairgoth/view/PairgothTool.kt @@ -1,6 +1,7 @@ package org.jeudego.pairgoth.view import com.republicate.kson.Json +import org.jeudego.pairgoth.ratings.RatingsManager import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths @@ -94,4 +95,6 @@ class PairgothTool { companion object { const val EXAMPLES_DIRECTORY = "examples" } + + fun getRatingsDates() = RatingsManager.getRatingsDates() } \ No newline at end of file diff --git a/view-webapp/src/main/webapp/WEB-INF/translations/fr b/view-webapp/src/main/webapp/WEB-INF/translations/fr index 7bd2165..4af49a0 100644 --- a/view-webapp/src/main/webapp/WEB-INF/translations/fr +++ b/view-webapp/src/main/webapp/WEB-INF/translations/fr @@ -51,7 +51,7 @@ French rules Règles françaises Given name Prénom Goban Goban Hd correction Correction hd -Hd threshold Seuil hd +No hd threshold Seuil sans hd How to use Comment utiliser Import Importer Import tournament Importer un tournoi @@ -122,6 +122,8 @@ Team of 4 individual players Équipe de 4 joueurs individuels Team of 5 individual players Équipe de 5 joueurs individuels That's the best option if you feel more comfortable when running locally or whenever you want to be able to do the pairing without internet. Pairgoth will launch a local web server to which you can connect using a browser. C’est la meilleure option si vous préférez le faire tourner localement ou quand vous voulez être capable de faire l’appariement sans internet. Pairgoth lancera un serveur web local auquel vous pourrez vous connecter depuis un navigateur. Time system Temps +Tournament director Directeur de tournoi +Tournament name Nom du tournoi Tournament type Type de tournoi Unpair Désapparier Unregister Désinscrire @@ -137,6 +139,7 @@ Black Noir club club country pays d d +end date date de fin first name prénom from du games ) parties ) @@ -163,9 +166,11 @@ sources sources sources on FFG's gitlab sur le serveur gitab de la FFG standalone, web interface indépendant, interface web standalone, web interface, via docker indépendant, interface web, via docker +start date date de début table table the configuration guide le guide de configuration to au +tournament location lieu du tournoi unpairable players joueurs non disponibles unpairable, non disponibles, version 0.1 supports the version 0.1 supporte le système d’appariement @@ -174,6 +179,7 @@ White Blanc white vs. black blanc vs. Noir confirmed. confirmé(s). Note that login to this instance is reserved to French federation actors plus several external people at our discretion. Send us La connexion à cette instance est réservée aux acteurs de la FFG et à quelques personnes extérieures, à notre discrétion. Envoyez-nous +yyyymmdd-city aaaammjj-ville an email un email to request an access. pour demander un accès. (not yet available) (pas encore disponible) diff --git a/view-webapp/src/main/webapp/js/tour-registration.inc.js b/view-webapp/src/main/webapp/js/tour-registration.inc.js index b4b387b..5d33193 100644 --- a/view-webapp/src/main/webapp/js/tour-registration.inc.js +++ b/view-webapp/src/main/webapp/js/tour-registration.inc.js @@ -74,12 +74,13 @@ function fillPlayer(player) { let form = $('#player-form')[0]; form.val('name', player.name); form.val('firstname', player.firstname); - console.log(country); form.val('country', country); form.val('club', player.club); form.val('rank', parseRank(player.rank)); form.val('rating', player.rating); form.val('final', false); + form.val('ffg_id', player.ffg); + form.val('egf_id', player.egf); $('#needle')[0].value = ''; initSearch(); $('#register').focus(); @@ -195,6 +196,12 @@ onLoad(() => { skip: form.find('input.participation').map((input,i) => [i+1, input.checked]).filter(arr => !arr[1]).map(arr => arr[0]), final: form.val('final') } + for (let origin of ['egf', 'ffg']) { + let value = form.val(`${origin}_id`); + if (value) { + player[origin] = value; + } + } if (form.hasClass('add')) { api.postJson(`tour/${tour_id}/part`, player) .then(player => { diff --git a/view-webapp/src/main/webapp/tour-registration.inc.html b/view-webapp/src/main/webapp/tour-registration.inc.html index 1c35216..61584d7 100644 --- a/view-webapp/src/main/webapp/tour-registration.inc.html +++ b/view-webapp/src/main/webapp/tour-registration.inc.html @@ -78,6 +78,9 @@