Teams handing in progress

This commit is contained in:
Claude Brisson
2024-04-15 16:33:17 +02:00
parent 2395c4e071
commit 179a502bbc
20 changed files with 249 additions and 68 deletions

View File

@@ -88,7 +88,7 @@ fun Tournament<*>.getSortedPairables(round: Int): List<Json.Object> {
Criterion.DC -> StandingsHandler.nullMap
}
}
val pairables = pairables.values.filter { it.final }.map { it.toMutableJson() }
val pairables = pairables.values.filter { it.final }.map { it.toDetailedJson() }
pairables.forEach { player ->
for (crit in criteria) {
player[crit.first] = (crit.second[player.getID()] ?: 0.0).toInt()

View File

@@ -14,8 +14,8 @@ object PlayerHandler: PairgothApiHandler {
override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? {
val tournament = getTournament(request)
return when (val pid = getSubSelector(request)?.toIntOrNull()) {
null -> tournament.pairables.values.map { it.toJson() }.toJsonArray()
else -> tournament.pairables[pid]?.toJson() ?: badRequest("no player with id #${pid}")
null -> tournament.players.values.map { it.toJson() }.toJsonArray()
else -> tournament.players[pid]?.toJson() ?: badRequest("no player with id #${pid}")
}
}

View File

@@ -14,8 +14,8 @@ object TeamHandler: PairgothApiHandler {
val tournament = getTournament(request)
if (tournament !is TeamTournament) badRequest("tournament is not a team tournament")
return when (val pid = getSubSelector(request)?.toIntOrNull()) {
null -> tournament.teams.values.map { it.toJson() }.toJsonArray()
else -> tournament.teams[pid]?.toJson() ?: badRequest("no team with id #${pid}")
null -> tournament.teams.values.map { it.toDetailedJson() }.toJsonArray()
else -> tournament.teams[pid]?.toDetailedJson() ?: badRequest("no team with id #${pid}")
}
}

View File

@@ -26,12 +26,14 @@ object TournamentHandler: PairgothApiHandler {
else ->
when {
ApiServlet.isJson(accept) -> {
getStore(request).getTournament(id)?.let {
getStore(request).getTournament(id)?.let { tour ->
if (accept == "application/pairgoth") {
it.toFullJson()
tour.toFullJson()
} else {
it.toJson().also { json ->
(json as Json.MutableObject)["stats"] = it.stats()
tour.toJson().also { json ->
// additional attributes for the webapp
json["stats"] = tour.stats()
json["teamSize"] = tour.type.playersNumber
}
}
} ?: badRequest("no tournament with id #${id}")

View File

@@ -8,13 +8,14 @@ import java.util.*
// Pairable
sealed class Pairable(val id: ID, val name: String, open val rating: Int, open val rank: Int, val final: Boolean, val mmsCorrection: Int = 0) {
sealed class Pairable(val id: ID, val name: String, val rating: Int, val rank: Int, val final: Boolean, val mmsCorrection: Int = 0) {
companion object {
const val MIN_RANK: Int = -30 // 30k
const val MAX_RANK: Int = 8 // 9D
}
abstract fun toJson(): Json.Object
fun toJson(): Json.Object = toMutableJson()
abstract fun toMutableJson(): Json.MutableObject
open fun toDetailedJson() = toMutableJson()
abstract val club: String?
abstract val country: String?
open fun fullName(separator: String = " "): String {
@@ -28,9 +29,6 @@ sealed class Pairable(val id: ID, val name: String, open val rating: Int, open v
}
object ByePlayer: Pairable(0, "bye", 0, Int.MIN_VALUE, true) {
override fun toJson(): Json.Object {
throw Error("bye player should never be serialized")
}
override fun toMutableJson(): Json.MutableObject {
throw Error("bye player should never be serialized")
}
@@ -95,7 +93,6 @@ class Player(
}
}
override fun toJson(): Json.Object = toMutableJson()
override fun fullName(separator: String): String {
return name + separator + firstname
}

View File

@@ -8,7 +8,6 @@ import java.time.LocalDate
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
import org.jeudego.pairgoth.pairing.solver.MacMahonSolver
import org.jeudego.pairgoth.pairing.solver.SwissSolver
import org.jeudego.pairgoth.store.Store
import org.jeudego.pairgoth.store.nextPlayerId
import org.jeudego.pairgoth.store.nextTournamentId
import kotlin.math.max
@@ -172,39 +171,53 @@ class TeamTournament(
gobanSize: Int = 19,
komi: Double = 7.5
): Tournament<TeamTournament.Team>(id, type, name, shortName, startDate, endDate, director, country, location, online, timeSystem, rounds, pairing, rules, gobanSize, komi) {
companion object {}
companion object {
private val epsilon = 0.0001
}
override val players = mutableMapOf<ID, Player>()
val teams: MutableMap<ID, Team> = _pairables
inner class Team(id: ID, name: String, final: Boolean): Pairable(id, name, 0, 0, final) {
private fun List<Int>.average(provider: (Player)->Int) = (sumOf {id -> provider(players[id]!!)} - epsilon / players.size).roundToInt()
inner class Team(id: ID, name: String, rating: Int, rank: Int, final: Boolean, mmsCorrection: Int = 0): Pairable(id, name, rating, rank, final, mmsCorrection) {
val playerIds = mutableSetOf<ID>()
val teamPlayers: Set<Player> get() = playerIds.mapNotNull { players[id] }.toSet()
override val rating: Int get() = if (teamPlayers.isEmpty()) super.rating else (teamPlayers.sumOf { player -> player.rating.toDouble() } / players.size).roundToInt()
override val rank: Int get() = if (teamPlayers.isEmpty()) super.rank else (teamPlayers.sumOf { player -> player.rank.toDouble() } / players.size).roundToInt()
override val club: String? get() = teamPlayers.map { club }.distinct().let { if (it.size == 1) it[0] else null }
override val country: String? get() = teamPlayers.map { country }.distinct().let { if (it.size == 1) it[0] else null }
val teamPlayers: Set<Player> get() = playerIds.mapNotNull { players[it] }.toSet()
override val club: String? get() = teamPlayers.map { it.club }.distinct().let { if (it.size == 1) it[0] else null }
override val country: String? get() = teamPlayers.map { it.country }.distinct().let { if (it.size == 1) it[0] else null }
override fun toMutableJson() = Json.MutableObject(
"id" to id,
"name" to name,
"players" to playerIds.toList().toJsonArray()
)
override fun toJson(): Json.Object = toMutableJson()
override fun toDetailedJson() = toMutableJson().also { json ->
json["rank"] = rank
country?.also { json["country"] = it }
club?.also { json["club"] = it }
}
val teamOfIndividuals: Boolean get() = type.individual
}
fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null) = Team(
id = json.getInt("id") ?: default?.id ?: nextPlayerId,
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
final = json.getBoolean("final") ?: default?.final ?: badRequest("missing final")
).apply {
json.getArray("players")?.let { arr ->
arr.mapTo(playerIds) {
if (it != null && it is Number) it.toInt().also { id -> players.containsKey(id) }
fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null): Team {
val teamPlayersIds = json.getArray("players")?.let { arr ->
arr.map {
if (it != null && it is Number) it.toInt().also { id ->
if (!players.containsKey(id)) badRequest("invalid player id: ${id}")
}
else badRequest("invalid players array")
}
} ?: badRequest("missing players")
return Team(
id = json.getInt("id") ?: default?.id ?: nextPlayerId,
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
rating = json.getInt("rating") ?: default?.rating ?: teamPlayersIds.average(Player::rating),
rank = json.getInt("rank") ?: default?.rank ?: teamPlayersIds.average(Player::rank),
final = teamPlayersIds.all { players[it]!!.final },
mmsCorrection = json.getInt("mmsCorrection") ?: default?.mmsCorrection ?: 0
).also {
it.playerIds.addAll(teamPlayersIds)
}
}
}
// Serialization
@@ -250,10 +263,16 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n
rounds = json.getInt("rounds") ?: default?.rounds ?: badRequest("missing rounds"),
pairing = json.getObject("pairing")?.let { Pairing.fromJson(it, default?.pairing) } ?: default?.pairing ?: badRequest("missing pairing")
)
(json["players"] as Json.Array?)?.forEach { obj ->
json.getArray("players")?.forEach { obj ->
val pairable = obj as Json.Object
tournament.players[pairable.getID("id")!!] = Player.fromJson(pairable)
}
if (tournament is TeamTournament) {
json.getArray("teams")?.forEach { obj ->
val team = obj as Json.Object
tournament.teams[team.getID("id")!!] = tournament.teamFromJson(team)
}
}
(json["games"] as Json.Array?)?.forEachIndexed { i, arr ->
val round = i + 1
val tournamentGames = tournament.games(round)
@@ -286,7 +305,7 @@ fun Tournament<*>.toJson() = Json.MutableObject(
)
fun Tournament<*>.toFullJson(): Json.Object {
val json = Json.MutableObject(toJson())
val json = toJson()
json["players"] = Json.Array(players.values.map { it.toJson() })
if (this is TeamTournament) {
json["teams"] = Json.Array(teams.values.map { it.toJson() })

View File

@@ -2,11 +2,13 @@ package org.jeudego.pairgoth.test
import com.republicate.kson.Json
import com.republicate.kson.toJsonObject
import com.republicate.kson.toMutableJsonObject
import org.jeudego.pairgoth.model.ID
import org.junit.jupiter.api.MethodOrderer.MethodName
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.TestMethodOrder
import java.io.Serializable
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
@@ -143,7 +145,13 @@ class BasicTests: TestBase() {
// filter out "id", and also "komi", "rules" and "gobanSize" which were provided by default
// also filter out "pairing", which is filled by all default values
val cmp = Json.Object(*resp.entries.filter { it.key !in listOf("id", "komi", "rules", "gobanSize", "pairing") }.map { Pair(it.key, it.value) }.toTypedArray())
val expected = aTournament.entries.filter { it.key != "pairing" }.map { Pair(it.key, it.value) }.toMap().toJsonObject()
val expected = aTournament.entries.filter { it.key != "pairing" }.map { Pair(it.key, it.value) }.toMap().toMutableJsonObject().also { map ->
map["stats"] = Json.Array(
Json.Object("participants" to 0, "paired" to 0, "games" to 0, "ready" to 0),
Json.Object("participants" to 0, "paired" to 0, "games" to 0, "ready" to 0)
)
map["teamSize"] = 1
}
assertEquals(expected.toString(), cmp.toString(), "tournament differs")
}