Merge from master
This commit is contained in:
@@ -13,7 +13,7 @@ interface ApiHandler {
|
||||
when (request.method) {
|
||||
"GET" -> get(request, response)
|
||||
"POST" -> post(request)
|
||||
"PUT" -> put(request)
|
||||
"PUT" -> put(request)
|
||||
"DELETE" -> delete(request)
|
||||
else -> notImplemented()
|
||||
}
|
||||
|
@@ -16,7 +16,7 @@ interface PairgothApiHandler: ApiHandler {
|
||||
fun Tournament<*>.dispatchEvent(event: Event, data: Json? = null) {
|
||||
Event.dispatch(event, Json.Object("tournament" to id, "data" to data))
|
||||
// when storage is not in memory, the tournament has to be persisted
|
||||
if (event != Event.tournamentAdded && event != Event.tournamentDeleted && event != Event.gameUpdated)
|
||||
if (event != Event.TournamentAdded && event != Event.TournamentDeleted)
|
||||
Store.replaceTournament(this)
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@ import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.model.getID
|
||||
import org.jeudego.pairgoth.model.toID
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import org.jeudego.pairgoth.server.Event.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
@@ -21,9 +20,11 @@ object PairingHandler: PairgothApiHandler {
|
||||
val playing = tournament.games(round).values.flatMap {
|
||||
listOf(it.black, it.white)
|
||||
}.toSet()
|
||||
val unpairables = tournament.pairables.values.filter { it.skip.contains(round) }.sortedByDescending { it.rating }.map { it.id }.toJsonArray()
|
||||
val pairables = tournament.pairables.values.filter { !it.skip.contains(round) && !playing.contains(it.id) }.sortedByDescending { it.rating }.map { it.id }.toJsonArray()
|
||||
val games = tournament.games(round).values
|
||||
val unpairables = tournament.pairables.values.filter { !it.final || it.skip.contains(round) }.sortedByDescending { it.rating }.map { it.id }.toJsonArray()
|
||||
val pairables = tournament.pairables.values.filter { it.final && !it.skip.contains(round) && !playing.contains(it.id) }.sortedByDescending { it.rating }.map { it.id }.toJsonArray()
|
||||
val games = tournament.games(round).values.sortedBy {
|
||||
if (it.table == 0) Int.MAX_VALUE else it.table
|
||||
}
|
||||
return Json.Object(
|
||||
"games" to games.map { it.toJson() }.toCollection(Json.MutableArray()),
|
||||
"pairables" to pairables,
|
||||
@@ -44,19 +45,20 @@ object PairingHandler: PairgothApiHandler {
|
||||
}.toSet()
|
||||
val pairables =
|
||||
if (allPlayers)
|
||||
tournament.pairables.values.filter { !it.skip.contains(round) && !playing.contains(it.id) }
|
||||
tournament.pairables.values.filter { it.final && !it.skip.contains(round) && !playing.contains(it.id) }
|
||||
else payload.map {
|
||||
// CB - because of the '["all"]' map, conversion to int lands here... Better API syntax for 'all players'?
|
||||
if (it is Number) it.toID() else badRequest("invalid pairable id: #$it")
|
||||
}.map { id ->
|
||||
tournament.pairables[id]?.also {
|
||||
if (!it.final) badRequest("pairable #$id registration status is not final")
|
||||
if (it.skip.contains(round)) badRequest("pairable #$id does not play round $round")
|
||||
if (playing.contains(it.id)) badRequest("pairable #$id already plays round $round")
|
||||
} ?: badRequest("invalid pairable id: #$id")
|
||||
}
|
||||
val games = tournament.pair(round, pairables)
|
||||
val ret = games.map { it.toJson() }.toJsonArray()
|
||||
tournament.dispatchEvent(gamesAdded, Json.Object("round" to round, "games" to ret))
|
||||
tournament.dispatchEvent(GamesAdded, Json.Object("round" to round, "games" to ret))
|
||||
return ret
|
||||
}
|
||||
|
||||
@@ -71,20 +73,37 @@ object PairingHandler: PairgothApiHandler {
|
||||
val playing = (tournament.games(round).values).filter { it.id != gameId }.flatMap {
|
||||
listOf(it.black, it.white)
|
||||
}.toSet()
|
||||
if (game.result != Game.Result.UNKNOWN && (
|
||||
game.black != payload.getInt("b") ||
|
||||
game.white != payload.getInt("w") ||
|
||||
game.handicap != payload.getInt("h")
|
||||
)) badRequest("Game already has a result")
|
||||
game.black = payload.getID("b") ?: badRequest("missing black player id")
|
||||
game.white = payload.getID("w") ?: badRequest("missing white player id")
|
||||
|
||||
tournament.recomputeHdAndDUDD(round, game.id)
|
||||
val previousTable = game.table;
|
||||
// temporary
|
||||
//payload.getInt("dudd")?.let { game.drawnUpDown = it }
|
||||
val black = tournament.pairables[game.black] ?: badRequest("invalid black player id")
|
||||
val white = tournament.pairables[game.black] ?: badRequest("invalid white player id")
|
||||
if (!black.final) badRequest("black registration status is not final")
|
||||
if (!white.final) badRequest("white registration status is not final")
|
||||
if (black.skip.contains(round)) badRequest("black is not playing this round")
|
||||
if (white.skip.contains(round)) badRequest("white is not playing this round")
|
||||
if (playing.contains(black.id)) badRequest("black is already in another game")
|
||||
if (playing.contains(white.id)) badRequest("white is already in another game")
|
||||
if (payload.containsKey("h")) game.handicap = payload.getString("h")?.toIntOrNull() ?: badRequest("invalid handicap")
|
||||
tournament.dispatchEvent(gameUpdated, Json.Object("round" to round, "game" to game.toJson()))
|
||||
if (payload.containsKey("t")) {
|
||||
game.table = payload.getString("t")?.toIntOrNull() ?: badRequest("invalid table number")
|
||||
}
|
||||
tournament.dispatchEvent(GameUpdated, Json.Object("round" to round, "game" to game.toJson()))
|
||||
if (game.table != previousTable && tournament.renumberTables(round, game)) {
|
||||
val games = tournament.games(round).values.sortedBy {
|
||||
if (it.table == 0) Int.MAX_VALUE else it.table
|
||||
}
|
||||
tournament.dispatchEvent(TablesRenumbered, Json.Object("round" to round, "games" to games.map { it.toJson() }.toCollection(Json.MutableArray())))
|
||||
}
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
@@ -102,7 +121,7 @@ object PairingHandler: PairgothApiHandler {
|
||||
payload.forEach {
|
||||
val id = (it as Number).toInt()
|
||||
val game = tournament.games(round)[id] ?: throw Error("invalid game id")
|
||||
if (game.result != Game.Result.UNKNOWN) {
|
||||
if (game.result != Game.Result.UNKNOWN && game.black != 0 && game.white != 0) {
|
||||
ApiHandler.logger.error("cannot unpair game id ${game.id}: it has a result")
|
||||
// we'll only skip it
|
||||
// throw Error("cannot unpair ")
|
||||
@@ -111,7 +130,7 @@ object PairingHandler: PairgothApiHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
tournament.dispatchEvent(gamesDeleted, Json.Object("round" to round, "games" to payload))
|
||||
tournament.dispatchEvent(GamesDeleted, Json.Object("round" to round, "games" to payload))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ import com.republicate.kson.toJsonArray
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.Player
|
||||
import org.jeudego.pairgoth.model.fromJson
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import org.jeudego.pairgoth.server.Event.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
@@ -25,7 +24,7 @@ object PlayerHandler: PairgothApiHandler {
|
||||
val payload = getObjectPayload(request)
|
||||
val player = Player.fromJson(payload)
|
||||
tournament.players[player.id] = player
|
||||
tournament.dispatchEvent(playerAdded, player.toJson())
|
||||
tournament.dispatchEvent(PlayerAdded, player.toJson())
|
||||
return Json.Object("success" to true, "id" to player.id)
|
||||
}
|
||||
|
||||
@@ -36,7 +35,7 @@ object PlayerHandler: PairgothApiHandler {
|
||||
val payload = getObjectPayload(request)
|
||||
val updated = Player.fromJson(payload, player)
|
||||
tournament.players[updated.id] = updated
|
||||
tournament.dispatchEvent(playerUpdated, player.toJson())
|
||||
tournament.dispatchEvent(PlayerUpdated, player.toJson())
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ object PlayerHandler: PairgothApiHandler {
|
||||
val tournament = getTournament(request)
|
||||
val id = getSubSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid player selector")
|
||||
tournament.players.remove(id) ?: badRequest("invalid player id")
|
||||
tournament.dispatchEvent(playerDeleted, Json.Object("id" to id))
|
||||
tournament.dispatchEvent(PlayerDeleted, Json.Object("id" to id))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
@@ -24,7 +24,7 @@ object ResultsHandler: PairgothApiHandler {
|
||||
val payload = getObjectPayload(request)
|
||||
val game = tournament.games(round)[payload.getInt("id")] ?: badRequest("invalid game id")
|
||||
game.result = Game.Result.fromSymbol(payload.getChar("result") ?: badRequest("missing result"))
|
||||
tournament.dispatchEvent(Event.resultUpdated, Json.Object("round" to round, "data" to game))
|
||||
tournament.dispatchEvent(Event.ResultUpdated, Json.Object("round" to round, "data" to game))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
@@ -3,21 +3,27 @@ package org.jeudego.pairgoth.api
|
||||
import com.republicate.kson.Json
|
||||
import com.republicate.kson.toJsonArray
|
||||
import org.jeudego.pairgoth.model.Criterion
|
||||
import org.jeudego.pairgoth.model.Criterion.*
|
||||
import org.jeudego.pairgoth.model.Game.Result.*
|
||||
import org.jeudego.pairgoth.model.ID
|
||||
import org.jeudego.pairgoth.model.MacMahon
|
||||
import org.jeudego.pairgoth.model.Pairable
|
||||
import org.jeudego.pairgoth.model.PairingType
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.model.adjustedTime
|
||||
import org.jeudego.pairgoth.model.displayRank
|
||||
import org.jeudego.pairgoth.model.getID
|
||||
import org.jeudego.pairgoth.model.historyBefore
|
||||
import org.jeudego.pairgoth.pairing.HistoryHelper
|
||||
import org.jeudego.pairgoth.pairing.solver.MacMahonSolver
|
||||
import java.io.PrintWriter
|
||||
import java.time.format.DateTimeFormatter
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
import org.jeudego.pairgoth.model.Criterion.*
|
||||
import org.jeudego.pairgoth.model.Game.Result.*
|
||||
import org.jeudego.pairgoth.model.ID
|
||||
import org.jeudego.pairgoth.model.getID
|
||||
import org.jeudego.pairgoth.model.TimeSystem.TimeSystemType.*
|
||||
import java.text.DecimalFormat
|
||||
|
||||
object StandingsHandler: PairgothApiHandler {
|
||||
override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? {
|
||||
@@ -78,12 +84,12 @@ object StandingsHandler: PairgothApiHandler {
|
||||
DC -> nullMap
|
||||
}
|
||||
}
|
||||
val pairables = tournament.pairables.values.map { it.toMutableJson() }
|
||||
val pairables = tournament.pairables.values.filter { it.final }.map { it.toMutableJson() }
|
||||
pairables.forEach { player ->
|
||||
for (crit in criteria) {
|
||||
player[crit.first] = crit.second[player.getID()] ?: 0.0
|
||||
}
|
||||
player["results"] = Json.MutableArray(List(round) { "=0" })
|
||||
player["results"] = Json.MutableArray(List(round) { "0=" })
|
||||
}
|
||||
val sortedPairables = pairables.sortedWith { left, right ->
|
||||
for (crit in criteria) {
|
||||
@@ -114,38 +120,156 @@ object StandingsHandler: PairgothApiHandler {
|
||||
val blackNum = black?.getInt("num") ?: 0
|
||||
val whiteColor = if (black == null) "" else "w"
|
||||
val blackColor = if (white == null) "" else "b"
|
||||
val handicap = if (game.handicap == 0) "" else "/h${game.handicap}"
|
||||
val handicap = if (game.handicap == 0) "" else "${game.handicap}"
|
||||
assert(white != null || black != null)
|
||||
if (white != null) {
|
||||
val mark = when (game.result) {
|
||||
UNKNOWN -> "?"
|
||||
BLACK -> "-"
|
||||
WHITE -> "+"
|
||||
JIGO -> "="
|
||||
CANCELLED -> "X"
|
||||
BOTHWIN -> "++"
|
||||
BOTHLOOSE -> "--"
|
||||
BLACK, BOTHLOOSE -> "-"
|
||||
WHITE, BOTHWIN -> "+"
|
||||
JIGO, CANCELLED -> "="
|
||||
}
|
||||
val results = white.getArray("results") as Json.MutableArray
|
||||
results[r - 1] = "$whiteColor$mark$blackNum$handicap"
|
||||
results[r - 1] =
|
||||
if (blackNum == 0) "0$mark"
|
||||
else "$blackNum$mark/$whiteColor$handicap"
|
||||
}
|
||||
if (black != null) {
|
||||
val mark = when (game.result) {
|
||||
UNKNOWN -> "?"
|
||||
BLACK -> "+"
|
||||
WHITE -> "-"
|
||||
JIGO -> "="
|
||||
CANCELLED -> "X"
|
||||
BOTHWIN -> "++"
|
||||
BOTHLOOSE -> "--"
|
||||
BLACK, BOTHWIN -> "+"
|
||||
WHITE, BOTHLOOSE -> "-"
|
||||
JIGO, CANCELLED -> "="
|
||||
}
|
||||
val results = black.getArray("results") as Json.MutableArray
|
||||
results[r - 1] = "$blackColor$mark$whiteNum$handicap"
|
||||
results[r - 1] =
|
||||
if (whiteNum == 0) "0$mark"
|
||||
else "$whiteNum$mark/$blackColor$handicap"
|
||||
}
|
||||
}
|
||||
}
|
||||
return sortedPairables.toJsonArray()
|
||||
val accept = request.getHeader("Accept")?.substringBefore(";")
|
||||
return when(accept) {
|
||||
"application/json" -> sortedPairables.toJsonArray()
|
||||
"application/egf" -> {
|
||||
exportToEGFFormat(tournament, sortedPairables, neededCriteria, response.writer)
|
||||
return null
|
||||
}
|
||||
"application/ffg" -> {
|
||||
exportToFFGFormat(tournament, sortedPairables, response.writer)
|
||||
return null
|
||||
}
|
||||
else -> ApiHandler.badRequest("invalid Accept header: $accept")
|
||||
}
|
||||
}
|
||||
|
||||
val nullMap = mapOf<ID, Double>()
|
||||
|
||||
private fun exportToEGFFormat(tournament: Tournament<*>, lines: List<Json.Object>, criteria: List<Criterion>, writer: PrintWriter) {
|
||||
val mainTime = tournament.timeSystem.mainTime
|
||||
val adjustedTime = tournament.timeSystem.adjustedTime()
|
||||
val egfClass =
|
||||
if (tournament.online) {
|
||||
when (tournament.timeSystem.type) {
|
||||
FISCHER ->
|
||||
if (mainTime >= 1800 && adjustedTime >= 3000) "D"
|
||||
else "X"
|
||||
else ->
|
||||
if (mainTime >= 2400 && adjustedTime >= 3000) "D"
|
||||
else "X"
|
||||
}
|
||||
} else {
|
||||
when (tournament.timeSystem.type) {
|
||||
FISCHER ->
|
||||
if (mainTime >= 2700 && adjustedTime >= 4500) "A"
|
||||
else if (mainTime >= 1800 && adjustedTime >= 3000) "B"
|
||||
else if (mainTime >= 1200 && adjustedTime >= 1800) "C"
|
||||
else "X"
|
||||
else ->
|
||||
if (mainTime >= 3600 && adjustedTime >= 4500) "A"
|
||||
else if (mainTime >= 2400 && adjustedTime >= 3000) "B"
|
||||
else if (mainTime >= 1500 && adjustedTime >= 1800) "C"
|
||||
else "X"
|
||||
}
|
||||
}
|
||||
val ret =
|
||||
"""
|
||||
; CL[${egfClass}]
|
||||
; EV[${tournament.name}]
|
||||
; PC[${tournament.country.lowercase()},${tournament.location}]
|
||||
; DT[${tournament.startDate},${tournament.endDate}]
|
||||
; HA[${
|
||||
if (tournament.pairing.type == PairingType.MAC_MAHON) "h${tournament.pairing.pairingParams.handicap.correction}"
|
||||
else "h9"
|
||||
|
||||
}]
|
||||
; KM[${tournament.komi}]
|
||||
; TM[${tournament.timeSystem.adjustedTime() / 60}]
|
||||
; CM[Generated by Pairgoth v0.1]
|
||||
;
|
||||
; Pl Name Rk Co Club ${ criteria.map { it.name.replace(Regex("(S?)O?(SOS|DOS)[MW]?"), "$1$2").padStart(7, ' ') }.joinToString(" ") }
|
||||
${
|
||||
lines.joinToString("\n") { player ->
|
||||
"${
|
||||
player.getString("num")!!.padStart(4, ' ')
|
||||
} ${
|
||||
"${player.getString("name")} ${player.getString("firstname")}".padEnd(30, ' ').take(30)
|
||||
} ${
|
||||
displayRank(player.getInt("rank")!!).uppercase().padStart(3, ' ')
|
||||
} ${
|
||||
player.getString("country")!!.uppercase()
|
||||
} ${
|
||||
(player.getString("club") ?: "").padStart(4).take(4)
|
||||
} ${
|
||||
criteria.joinToString(" ") { numFormat.format(player.getDouble(it.name)!!).let { if (it.contains('.')) it else "$it " }.padStart(7, ' ') }
|
||||
} ${
|
||||
player.getArray("results")!!.map {
|
||||
(it as String).padStart(8, ' ')
|
||||
}.joinToString(" ")
|
||||
}"
|
||||
}
|
||||
}
|
||||
"""
|
||||
writer.println(ret)
|
||||
}
|
||||
|
||||
private fun exportToFFGFormat(tournament: Tournament<*>, lines: List<Json.Object>, writer: PrintWriter) {
|
||||
// let's try in UTF-8
|
||||
val ret =
|
||||
""";name=${tournament.shortName}
|
||||
;date=${frDate.format(tournament.startDate)}
|
||||
;vill=${tournament.location}${if (tournament.online) "(online)" else ""}
|
||||
;comm=${tournament.name}
|
||||
;prog=Pairgoth v0.1
|
||||
;time=${tournament.timeSystem.mainTime / 60}
|
||||
;ta=${tournament.timeSystem.adjustedTime() / 60}
|
||||
;size=${tournament.gobanSize}
|
||||
;komi=${tournament.komi}
|
||||
;
|
||||
;Num Nom Prenom Niv Licence Club
|
||||
${
|
||||
lines.joinToString("\n") { player ->
|
||||
"${
|
||||
player.getString("num")!!.padStart(4, ' ')
|
||||
} ${
|
||||
"${player.getString("name")} ${player.getString("firstname")}".padEnd(24, ' ').take(24)
|
||||
} ${
|
||||
displayRank(player.getInt("rank")!!).uppercase().padStart(3, ' ')
|
||||
} ${
|
||||
player.getString("ffg") ?: " "
|
||||
} ${
|
||||
(player.getString("club") ?: "").padStart(6).take(6)
|
||||
} ${
|
||||
player.getArray("results")!!.joinToString(" ") {
|
||||
(it as String).replace("/", "").replace(Regex("(?<=[bw])$"), "0").padStart(7, ' ')
|
||||
}
|
||||
}"
|
||||
}
|
||||
}
|
||||
"""
|
||||
writer.println(ret)
|
||||
}
|
||||
|
||||
private val numFormat = DecimalFormat("###0.#")
|
||||
private val frDate: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy")
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ import com.republicate.kson.Json
|
||||
import com.republicate.kson.toJsonArray
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.TeamTournament
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import org.jeudego.pairgoth.server.Event.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
@@ -26,7 +25,7 @@ object TeamHandler: PairgothApiHandler {
|
||||
val payload = getObjectPayload(request)
|
||||
val team = tournament.teamFromJson(payload)
|
||||
tournament.teams[team.id] = team
|
||||
tournament.dispatchEvent(teamAdded, team.toJson())
|
||||
tournament.dispatchEvent(TeamAdded, team.toJson())
|
||||
return Json.Object("success" to true, "id" to team.id)
|
||||
}
|
||||
|
||||
@@ -38,7 +37,7 @@ object TeamHandler: PairgothApiHandler {
|
||||
val payload = getObjectPayload(request)
|
||||
val updated = tournament.teamFromJson(payload, team)
|
||||
tournament.teams[updated.id] = updated
|
||||
tournament.dispatchEvent(teamUpdated, team.toJson())
|
||||
tournament.dispatchEvent(TeamUpdated, team.toJson())
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
@@ -47,7 +46,7 @@ object TeamHandler: PairgothApiHandler {
|
||||
if (tournament !is TeamTournament) badRequest("tournament is not a team tournament")
|
||||
val id = getSubSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid team selector")
|
||||
tournament.teams.remove(id) ?: badRequest("invalid team id")
|
||||
tournament.dispatchEvent(teamDeleted, Json.Object("id" to id))
|
||||
tournament.dispatchEvent(TeamDeleted, Json.Object("id" to id))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ import org.jeudego.pairgoth.model.fromJson
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import org.jeudego.pairgoth.server.ApiServlet
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import org.jeudego.pairgoth.server.Event.*
|
||||
import org.w3c.dom.Element
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
@@ -44,7 +43,7 @@ object TournamentHandler: PairgothApiHandler {
|
||||
else -> badRequest("missing or invalid payload")
|
||||
}
|
||||
Store.addTournament(tournament)
|
||||
tournament.dispatchEvent(tournamentAdded, tournament.toJson())
|
||||
tournament.dispatchEvent(TournamentAdded, tournament.toJson())
|
||||
return Json.Object("success" to true, "id" to tournament.id)
|
||||
}
|
||||
|
||||
@@ -64,14 +63,14 @@ object TournamentHandler: PairgothApiHandler {
|
||||
clear()
|
||||
putAll(tournament.games(round))
|
||||
}
|
||||
updated.dispatchEvent(tournamentUpdated, updated.toJson())
|
||||
updated.dispatchEvent(TournamentUpdated, updated.toJson())
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
override fun delete(request: HttpServletRequest): Json {
|
||||
val tournament = getTournament(request)
|
||||
Store.deleteTournament(tournament)
|
||||
tournament.dispatchEvent(tournamentDeleted, Json.Object("id" to tournament.id))
|
||||
tournament.dispatchEvent(TournamentDeleted, Json.Object("id" to tournament.id))
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@ package org.jeudego.pairgoth.ext
|
||||
|
||||
import jakarta.xml.bind.JAXBContext
|
||||
import jakarta.xml.bind.JAXBElement
|
||||
import kotlinx.datetime.LocalDate
|
||||
import java.time.LocalDate
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import org.jeudego.pairgoth.opengotha.TournamentType
|
||||
import org.jeudego.pairgoth.opengotha.ObjectFactory
|
||||
@@ -13,7 +13,7 @@ import javax.xml.datatype.XMLGregorianCalendar
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private const val MILLISECONDS_PER_DAY = 86400000
|
||||
fun XMLGregorianCalendar.toLocalDate() = LocalDate(year, month, day)
|
||||
fun XMLGregorianCalendar.toLocalDate() = LocalDate.of(year, month, day)
|
||||
|
||||
object OpenGotha {
|
||||
|
||||
@@ -114,10 +114,10 @@ object OpenGotha {
|
||||
location = genParams.location,
|
||||
online = genParams.isBInternet ?: false,
|
||||
timeSystem = when (genParams.complementaryTimeSystem) {
|
||||
"SUDDENDEATH" -> SuddenDeath(genParams.basicTime)
|
||||
"STDBYOYOMI" -> StandardByoyomi(genParams.basicTime, genParams.stdByoYomiTime, 1) // no periods?
|
||||
"CANBYOYOMI" -> CanadianByoyomi(genParams.basicTime, genParams.canByoYomiTime, genParams.nbMovesCanTime)
|
||||
"FISCHER" -> FischerTime(genParams.basicTime, genParams.fischerTime)
|
||||
"SUDDENDEATH" -> SuddenDeath(genParams.basicTime * 60)
|
||||
"STDBYOYOMI" -> StandardByoyomi(genParams.basicTime * 60, genParams.stdByoYomiTime, 1) // no periods?
|
||||
"CANBYOYOMI" -> CanadianByoyomi(genParams.basicTime * 60, genParams.canByoYomiTime, genParams.nbMovesCanTime)
|
||||
"FISCHER" -> FischerTime(genParams.basicTime * 60, genParams.fischerTime)
|
||||
else -> throw Error("missing byoyomi type")
|
||||
},
|
||||
pairing = when (handParams.hdCeiling) {
|
||||
@@ -145,7 +145,8 @@ object OpenGotha {
|
||||
rating = player.rating,
|
||||
rank = Pairable.parseRank(player.rank),
|
||||
country = player.country,
|
||||
club = player.club
|
||||
club = player.club,
|
||||
final = "FIN" == player.registeringStatus
|
||||
).also {
|
||||
player.participating.toString().forEachIndexed { i,c ->
|
||||
if (c == '0') it.skip.add(i + 1)
|
||||
@@ -215,7 +216,9 @@ object OpenGotha {
|
||||
player.displayRank()
|
||||
}" rating="${
|
||||
player.rating
|
||||
}" ratingOrigin="" registeringStatus="FIN" smmsCorrection="0"/>"""
|
||||
}" ratingOrigin="" registeringStatus="${
|
||||
if (player.final) "FIN" else "PRE"
|
||||
}" smmsCorrection="0"/>"""
|
||||
}
|
||||
}
|
||||
</Players>
|
||||
@@ -269,9 +272,9 @@ object OpenGotha {
|
||||
}
|
||||
</ByePlayer>
|
||||
<TournamentParameterSet>
|
||||
<GeneralParameterSet bInternet="${tournament.online}" basicTime="${tournament.timeSystem.mainTime}" beginDate="${tournament.startDate}" canByoYomiTime="${tournament.timeSystem.byoyomi}" complementaryTimeSystem="${when(tournament.timeSystem.type) {
|
||||
<GeneralParameterSet bInternet="${tournament.online}" basicTime="${tournament.timeSystem.mainTime / 60}" beginDate="${tournament.startDate}" canByoYomiTime="${tournament.timeSystem.byoyomi}" complementaryTimeSystem="${when(tournament.timeSystem.type) {
|
||||
TimeSystem.TimeSystemType.SUDDEN_DEATH -> "SUDDENDEATH"
|
||||
TimeSystem.TimeSystemType.STANDARD -> "STDBYOYOMI"
|
||||
TimeSystem.TimeSystemType.JAPANESE -> "STDBYOYOMI"
|
||||
TimeSystem.TimeSystemType.CANADIAN -> "CANBYOYOMI"
|
||||
TimeSystem.TimeSystemType.FISCHER -> "FISCHER"
|
||||
} }" director="" endDate="${tournament.endDate}" fischerTime="${tournament.timeSystem.increment}" genCountNotPlayedGamesAsHalfPoint="false" genMMBar="${
|
||||
|
@@ -7,10 +7,10 @@ import java.util.*
|
||||
|
||||
// Pairable
|
||||
|
||||
sealed class Pairable(val id: ID, val name: String, open val rating: Int, open val rank: Int) {
|
||||
sealed class Pairable(val id: ID, val name: String, open val rating: Int, open val rank: Int, val final: Boolean, val mmsCorrection: Int = 0) {
|
||||
companion object {
|
||||
val MIN_RANK: Int = -30 // 30k
|
||||
val MAX_RANK: Int = 8 // 9D
|
||||
const val MIN_RANK: Int = -30 // 30k
|
||||
const val MAX_RANK: Int = 8 // 9D
|
||||
}
|
||||
abstract fun toJson(): Json.Object
|
||||
abstract fun toMutableJson(): Json.MutableObject
|
||||
@@ -26,7 +26,7 @@ sealed class Pairable(val id: ID, val name: String, open val rating: Int, open v
|
||||
}
|
||||
}
|
||||
|
||||
object ByePlayer: Pairable(0, "bye", 0, Int.MIN_VALUE) {
|
||||
object ByePlayer: Pairable(0, "bye", 0, Int.MIN_VALUE, true) {
|
||||
override fun toJson(): Json.Object {
|
||||
throw Error("bye player should never be serialized")
|
||||
}
|
||||
@@ -70,8 +70,10 @@ class Player(
|
||||
rating: Int,
|
||||
rank: Int,
|
||||
override var country: String,
|
||||
override var club: String
|
||||
): Pairable(id, name, rating, rank) {
|
||||
override var club: String,
|
||||
final: Boolean,
|
||||
mmsCorrection: Int = 0
|
||||
): Pairable(id, name, rating, rank, final, mmsCorrection) {
|
||||
companion object
|
||||
// used to store external IDs ("FFG" => FFG ID, "EGF" => EGF PIN, "AGA" => AGA ID ...)
|
||||
val externalIds = mutableMapOf<DatabaseId, String>()
|
||||
@@ -82,9 +84,11 @@ class Player(
|
||||
"rating" to rating,
|
||||
"rank" to rank,
|
||||
"country" to country,
|
||||
"club" to club
|
||||
"club" to club,
|
||||
"final" to final
|
||||
).also { json ->
|
||||
if (skip.isNotEmpty()) json["skip"] = Json.Array(skip)
|
||||
if (mmsCorrection != 0) json["mmsCorrection"] = mmsCorrection
|
||||
externalIds.forEach { (dbid, id) ->
|
||||
json[dbid.key] = id
|
||||
}
|
||||
@@ -103,7 +107,9 @@ fun Player.Companion.fromJson(json: Json.Object, default: Player? = null) = Play
|
||||
rating = json.getInt("rating") ?: default?.rating ?: badRequest("missing rating"),
|
||||
rank = json.getInt("rank") ?: default?.rank ?: badRequest("missing rank"),
|
||||
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
||||
club = json.getString("club") ?: default?.club ?: badRequest("missing club")
|
||||
club = json.getString("club") ?: default?.club ?: badRequest("missing club"),
|
||||
final = json.getBoolean("final") ?: default?.final ?: true,
|
||||
mmsCorrection = json.getInt("mmsCorrection") ?: default?.mmsCorrection ?: 0
|
||||
).also { player ->
|
||||
player.skip.clear()
|
||||
json.getArray("skip")?.let {
|
||||
|
@@ -6,6 +6,7 @@ import org.jeudego.pairgoth.model.MainCritParams.SeedMethod.SPLIT_AND_SLIP
|
||||
import org.jeudego.pairgoth.model.PairingType.*
|
||||
import org.jeudego.pairgoth.pairing.solver.MacMahonSolver
|
||||
import org.jeudego.pairgoth.pairing.solver.SwissSolver
|
||||
import java.util.*
|
||||
|
||||
// base pairing parameters
|
||||
data class BaseCritParams(
|
||||
@@ -172,7 +173,7 @@ class Swiss(
|
||||
): Pairing(SWISS, pairingParams, placementParams) {
|
||||
companion object {}
|
||||
override fun pair(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): List<Game> {
|
||||
return SwissSolver(round, tournament.historyBefore(round), pairables, pairingParams, placementParams).pair()
|
||||
return SwissSolver(round, tournament.historyBefore(round), pairables, pairingParams, placementParams, tournament.usedTables(round)).pair()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +202,7 @@ class MacMahon(
|
||||
): Pairing(MAC_MAHON, pairingParams, placementParams) {
|
||||
companion object {}
|
||||
override fun pair(tournament: Tournament<*>, round: Int, pairables: List<Pairable>): List<Game> {
|
||||
return MacMahonSolver(round, tournament.historyBefore(round), pairables, pairingParams, placementParams, mmFloor, mmBar).pair()
|
||||
return MacMahonSolver(round, tournament.historyBefore(round), pairables, pairingParams, placementParams, tournament.usedTables(round), mmFloor, mmBar).pair()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package org.jeudego.pairgoth.model
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.api.ApiHandler
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.TimeSystem.TimeSystemType.*
|
||||
|
||||
@@ -15,7 +14,7 @@ data class TimeSystem(
|
||||
val stones: Int
|
||||
) {
|
||||
companion object {}
|
||||
enum class TimeSystemType { CANADIAN, STANDARD, FISCHER, SUDDEN_DEATH }
|
||||
enum class TimeSystemType { CANADIAN, JAPANESE, FISCHER, SUDDEN_DEATH }
|
||||
}
|
||||
|
||||
fun CanadianByoyomi(mainTime: Int, byoyomi: Int, stones: Int) =
|
||||
@@ -30,7 +29,7 @@ fun CanadianByoyomi(mainTime: Int, byoyomi: Int, stones: Int) =
|
||||
|
||||
fun StandardByoyomi(mainTime: Int, byoyomi: Int, periods: Int) =
|
||||
TimeSystem(
|
||||
type = STANDARD,
|
||||
type = JAPANESE,
|
||||
mainTime = mainTime,
|
||||
increment = 0,
|
||||
byoyomi = byoyomi,
|
||||
@@ -86,9 +85,16 @@ fun TimeSystem.Companion.fromJson(json: Json.Object) =
|
||||
|
||||
fun TimeSystem.toJson() = when (type) {
|
||||
TimeSystem.TimeSystemType.CANADIAN -> Json.Object("type" to type.name, "mainTime" to mainTime, "byoyomi" to byoyomi, "stones" to stones)
|
||||
TimeSystem.TimeSystemType.STANDARD -> Json.Object("type" to type.name, "mainTime" to mainTime, "byoyomi" to byoyomi, "periods" to periods)
|
||||
TimeSystem.TimeSystemType.JAPANESE -> Json.Object("type" to type.name, "mainTime" to mainTime, "byoyomi" to byoyomi, "periods" to periods)
|
||||
TimeSystem.TimeSystemType.FISCHER ->
|
||||
if (maxTime == Int.MAX_VALUE) Json.Object("type" to type.name, "mainTime" to mainTime, "increment" to increment)
|
||||
else Json.Object("type" to type.name, "mainTime" to mainTime, "increment" to increment, "maxTime" to maxTime)
|
||||
TimeSystem.TimeSystemType.SUDDEN_DEATH -> Json.Object("type" to type.name, "mainTime" to mainTime)
|
||||
}
|
||||
|
||||
fun TimeSystem.adjustedTime() = when (type) {
|
||||
TimeSystem.TimeSystemType.CANADIAN -> mainTime + 60 * byoyomi / stones
|
||||
TimeSystem.TimeSystemType.JAPANESE -> mainTime + 45 * byoyomi
|
||||
TimeSystem.TimeSystemType.FISCHER -> mainTime + 120 * increment
|
||||
TimeSystem.TimeSystemType.SUDDEN_DEATH -> mainTime
|
||||
}
|
||||
|
@@ -2,7 +2,9 @@ package org.jeudego.pairgoth.model
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import com.republicate.kson.toJsonArray
|
||||
import kotlinx.datetime.LocalDate
|
||||
// CB TODO - review
|
||||
//import kotlinx.datetime.LocalDate
|
||||
import java.time.LocalDate
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.pairing.HistoryHelper
|
||||
import org.jeudego.pairgoth.pairing.solver.MacMahonSolver
|
||||
@@ -10,6 +12,7 @@ import org.jeudego.pairgoth.pairing.solver.SwissSolver
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import java.util.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
sealed class Tournament <P: Pairable>(
|
||||
@@ -76,9 +79,9 @@ sealed class Tournament <P: Pairable>(
|
||||
// TODO cleaner solver instantiation
|
||||
val history = historyBefore(round)
|
||||
val solver = if (pairing is Swiss) {
|
||||
SwissSolver(round, history, pairables.values.toList(), pairing.pairingParams, pairing.placementParams)
|
||||
SwissSolver(round, history, pairables.values.toList(), pairing.pairingParams, pairing.placementParams, usedTables(round))
|
||||
} else if (pairing is MacMahon) {
|
||||
MacMahonSolver(round, history, pairables.values.toList(), pairing.pairingParams, pairing.placementParams, pairing.mmFloor, pairing.mmBar)
|
||||
MacMahonSolver(round, history, pairables.values.toList(), pairing.pairingParams, pairing.placementParams, usedTables(round), pairing.mmFloor, pairing.mmBar)
|
||||
} else throw Exception("Invalid tournament type")
|
||||
|
||||
// Recomputes DUDD and hd
|
||||
@@ -88,6 +91,29 @@ sealed class Tournament <P: Pairable>(
|
||||
game.drawnUpDown = solver.dudd(black, white)
|
||||
game.handicap = solver.hd(black, white)
|
||||
}
|
||||
|
||||
fun usedTables(round: Int): BitSet =
|
||||
games(round).values.map { it.table }.fold(BitSet()) { acc, table ->
|
||||
acc.set(table)
|
||||
acc
|
||||
}
|
||||
|
||||
fun renumberTables(round: Int, pivot: Game? = null): Boolean {
|
||||
var changed = false
|
||||
var nextTable = 1
|
||||
games(round).values.filter{ game -> pivot?.let { pivot.id != game.id } ?: true }.sortedBy { game ->
|
||||
val whiteRank = pairables[game.white]?.rating ?: Int.MIN_VALUE
|
||||
val blackRank = pairables[game.black]?.rating ?: Int.MIN_VALUE
|
||||
-(2 * whiteRank + 2 * blackRank) / 2
|
||||
}.forEach { game ->
|
||||
if (pivot != null && nextTable == pivot.table) {
|
||||
++nextTable
|
||||
}
|
||||
changed = changed || game.table != nextTable
|
||||
game.table = nextTable++
|
||||
}
|
||||
return changed
|
||||
}
|
||||
}
|
||||
|
||||
// standard tournament of individuals
|
||||
@@ -133,7 +159,7 @@ class TeamTournament(
|
||||
override val players = mutableMapOf<ID, Player>()
|
||||
val teams: MutableMap<ID, Team> = _pairables
|
||||
|
||||
inner class Team(id: ID, name: String): Pairable(id, name, 0, 0) {
|
||||
inner class Team(id: ID, name: String, final: Boolean): Pairable(id, name, 0, 0, final) {
|
||||
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()
|
||||
@@ -151,7 +177,8 @@ class TeamTournament(
|
||||
|
||||
fun teamFromJson(json: Json.Object, default: TeamTournament.Team? = null) = Team(
|
||||
id = json.getInt("id") ?: default?.id ?: Store.nextPlayerId,
|
||||
name = json.getString("name") ?: default?.name ?: badRequest("missing name")
|
||||
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) {
|
||||
@@ -174,8 +201,8 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n
|
||||
type = type,
|
||||
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
|
||||
shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"),
|
||||
startDate = json.getLocalDate("startDate") ?: default?.startDate ?: badRequest("missing startDate"),
|
||||
endDate = json.getLocalDate("endDate") ?: default?.endDate ?: badRequest("missing endDate"),
|
||||
startDate = json.getString("startDate")?.let { LocalDate.parse(it) } ?: default?.startDate ?: badRequest("missing startDate"),
|
||||
endDate = json.getString("endDate")?.let { LocalDate.parse(it) } ?: default?.endDate ?: badRequest("missing endDate"),
|
||||
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
||||
location = json.getString("location") ?: default?.location ?: badRequest("missing location"),
|
||||
online = json.getBoolean("online") ?: default?.online ?: false,
|
||||
@@ -192,8 +219,8 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n
|
||||
type = type,
|
||||
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
|
||||
shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"),
|
||||
startDate = json.getLocalDate("startDate") ?: default?.startDate ?: badRequest("missing startDate"),
|
||||
endDate = json.getLocalDate("endDate") ?: default?.endDate ?: badRequest("missing endDate"),
|
||||
startDate = json.getString("startDate")?.let { LocalDate.parse(it) } ?: default?.startDate ?: badRequest("missing startDate"),
|
||||
endDate = json.getString("endDate")?.let { LocalDate.parse(it) } ?: default?.endDate ?: badRequest("missing endDate"),
|
||||
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
||||
location = json.getString("location") ?: default?.location ?: badRequest("missing location"),
|
||||
online = json.getBoolean("online") ?: default?.online ?: false,
|
||||
|
@@ -136,11 +136,4 @@ abstract class BasePairingHelper(
|
||||
open fun nameSort(p: Pairable, q: Pairable): Int {
|
||||
return if (p.name > q.name) 1 else -1
|
||||
}
|
||||
|
||||
val tables = history.mapTo(mutableListOf()) { games ->
|
||||
games.map { it.table }.fold(BitSet()) { acc, table ->
|
||||
acc.set(table)
|
||||
acc
|
||||
}
|
||||
}
|
||||
}
|
@@ -24,6 +24,7 @@ sealed class BaseSolver(
|
||||
pairables: List<Pairable>, // All pairables for this round, it may include the bye player
|
||||
pairing: PairingParams,
|
||||
placement: PlacementParams,
|
||||
val usedTables: BitSet
|
||||
) : BasePairingHelper(history, pairables, pairing, placement) {
|
||||
|
||||
companion object {
|
||||
@@ -540,7 +541,6 @@ sealed class BaseSolver(
|
||||
}
|
||||
open fun games(black: Pairable, white: Pairable): List<Game> {
|
||||
// CB TODO team of individuals pairing
|
||||
val usedTables = tables.getOrNull(round - 1) ?: BitSet().also { tables.add(it) }
|
||||
val table = if (black.id == 0 || white.id == 0) 0 else usedTables.nextClearBit(1)
|
||||
usedTables.set(table)
|
||||
return listOf(Game(id = Store.nextGameId, table = table, black = black.id, white = white.id, handicap = hd(white, black), drawnUpDown = dudd(black, white)))
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package org.jeudego.pairgoth.pairing.solver
|
||||
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import java.util.*
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@@ -9,9 +10,9 @@ class MacMahonSolver(round: Int,
|
||||
pairables: List<Pairable>,
|
||||
pairingParams: PairingParams,
|
||||
placementParams: PlacementParams,
|
||||
usedTables: BitSet,
|
||||
private val mmFloor: Int, private val mmBar: Int):
|
||||
BaseSolver(round, history, pairables, pairingParams, placementParams) {
|
||||
|
||||
BaseSolver(round, history, pairables, pairingParams, placementParams, usedTables) {
|
||||
|
||||
override val scores: Map<ID, Double> by lazy {
|
||||
require (mmBar > mmFloor) { "MMFloor is higher than MMBar" }
|
||||
@@ -32,7 +33,7 @@ class MacMahonSolver(round: Int,
|
||||
}
|
||||
}
|
||||
|
||||
val Pairable.mmBase: Double get() = min(max(rank, mmFloor), mmBar) + mmsZero
|
||||
val Pairable.mmBase: Double get() = min(max(rank, mmFloor), mmBar) + mmsZero + mmsCorrection
|
||||
val Pairable.mms: Double get() = scores[id] ?: 0.0
|
||||
|
||||
// CB TODO - configurable criteria
|
||||
|
@@ -1,13 +1,16 @@
|
||||
package org.jeudego.pairgoth.pairing.solver
|
||||
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import java.util.*
|
||||
|
||||
class SwissSolver(round: Int,
|
||||
history: List<List<Game>>,
|
||||
pairables: List<Pairable>,
|
||||
pairingParams: PairingParams,
|
||||
placementParams: PlacementParams):
|
||||
BaseSolver(round, history, pairables, pairingParams, placementParams) {
|
||||
placementParams: PlacementParams,
|
||||
usedTables: BitSet
|
||||
):
|
||||
BaseSolver(round, history, pairables, pairingParams, placementParams, usedTables) {
|
||||
|
||||
// In a Swiss tournament the main criterion is the number of wins and already computed
|
||||
|
||||
|
@@ -218,8 +218,12 @@ class ApiServlet: HttpServlet() {
|
||||
"Missing 'Accept' header"
|
||||
)
|
||||
// CB TODO 1) a reference to a specific API call at this point is a code smell.
|
||||
// 2) there will e other content types: .tou, .h9, .html
|
||||
if (!isJson(accept) && (!isXml(accept) || !request.requestURI.matches(Regex("/api/tour/\\d+")))) throw ApiException(
|
||||
// 2) there will be other content types: .tou, .h9, .html
|
||||
if (!isJson(accept) &&
|
||||
(!isXml(accept) || !request.requestURI.matches(Regex("/api/tour/\\d+"))) &&
|
||||
(accept != "application/ffg" && accept != "application/egf" || !request.requestURI.matches(Regex("/api/tour/\\d+/standings/\\d+")))
|
||||
|
||||
) throw ApiException(
|
||||
HttpServletResponse.SC_BAD_REQUEST,
|
||||
"Invalid 'Accept' header"
|
||||
)
|
||||
|
@@ -4,19 +4,20 @@ import info.macias.sse.events.MessageEvent
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
enum class Event {
|
||||
tournamentAdded,
|
||||
tournamentUpdated,
|
||||
tournamentDeleted,
|
||||
playerAdded,
|
||||
playerUpdated,
|
||||
playerDeleted,
|
||||
teamAdded,
|
||||
teamUpdated,
|
||||
teamDeleted,
|
||||
gamesAdded,
|
||||
gamesDeleted,
|
||||
gameUpdated,
|
||||
resultUpdated,
|
||||
TournamentAdded,
|
||||
TournamentUpdated,
|
||||
TournamentDeleted,
|
||||
PlayerAdded,
|
||||
PlayerUpdated,
|
||||
PlayerDeleted,
|
||||
TeamAdded,
|
||||
TeamUpdated,
|
||||
TeamDeleted,
|
||||
GamesAdded,
|
||||
GamesDeleted,
|
||||
GameUpdated,
|
||||
ResultUpdated,
|
||||
TablesRenumbered
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
Reference in New Issue
Block a user