diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ApiHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ApiHandler.kt index 5bd1047..8b8cc82 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ApiHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ApiHandler.kt @@ -25,7 +25,7 @@ interface ApiHandler { notImplemented() } - fun put(request: HttpServletRequest, response: HttpServletResponse): Json { + fun put(request: HttpServletRequest, response: HttpServletResponse): Json? { notImplemented() } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ApiTools.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ApiTools.kt index 8017344..6190f66 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ApiTools.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ApiTools.kt @@ -3,6 +3,7 @@ package org.jeudego.pairgoth.api import com.republicate.kson.Json import org.jeudego.pairgoth.model.Criterion import org.jeudego.pairgoth.model.DatabaseId +import org.jeudego.pairgoth.model.Game import org.jeudego.pairgoth.model.MacMahon import org.jeudego.pairgoth.model.Pairable import org.jeudego.pairgoth.model.Pairable.Companion.MIN_RANK @@ -34,6 +35,10 @@ fun Tournament<*>.getSortedPairables(round: Int, includePreliminary: Boolean = f else ceil(score - epsilon) } + if (frozen != null) { + return ArrayList(frozen!!.map { it -> it as Json.Object }) + } + // CB TODO - factorize history helper creation between here and solver classes val historyHelper = HistoryHelper(historyBefore(round + 1)) { if (pairing.type == PairingType.SWISS) wins.mapValues { Pair(0.0, it.value) } @@ -116,5 +121,50 @@ fun Tournament<*>.getSortedPairables(round: Int, includePreliminary: Boolean = f it.value.forEach { p -> p["place"] = place } place += it.value.size } + return sortedPairables } + +fun Tournament<*>.populateResultsArray(sortedPairables: List, round: Int = rounds) { + // fill result + val sortedMap = sortedPairables.associateBy { + it.getID()!! + } + + for (r in 1..round) { + games(r).values.forEach { game -> + val white = if (game.white != 0) sortedMap[game.white] else null + val black = if (game.black != 0) sortedMap[game.black] else null + val whiteNum = white?.getInt("num") ?: 0 + 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 "${game.handicap}" + assert(white != null || black != null) + if (white != null) { + val mark = when (game.result) { + Game.Result.UNKNOWN -> "?" + Game.Result.BLACK, Game.Result.BOTHLOOSE -> "-" + Game.Result.WHITE, Game.Result.BOTHWIN -> "+" + Game.Result.JIGO, Game.Result.CANCELLED -> "=" + } + val results = white.getArray("results") as Json.MutableArray + results[r - 1] = + if (blackNum == 0) "0$mark" + else "$blackNum$mark/$whiteColor$handicap" + } + if (black != null) { + val mark = when (game.result) { + Game.Result.UNKNOWN -> "?" + Game.Result.BLACK, Game.Result.BOTHWIN -> "+" + Game.Result.WHITE, Game.Result.BOTHLOOSE -> "-" + Game.Result.JIGO, Game.Result.CANCELLED -> "=" + } + val results = black.getArray("results") as Json.MutableArray + results[r - 1] = + if (whiteNum == 0) "0$mark" + else "$whiteNum$mark/$blackColor$handicap" + } + } + } +} diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt index 8140eae..1fa0ae4 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairingHandler.kt @@ -3,7 +3,6 @@ package org.jeudego.pairgoth.api import com.republicate.kson.Json import com.republicate.kson.toJsonArray import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest -import org.jeudego.pairgoth.api.TournamentHandler.dispatchEvent import org.jeudego.pairgoth.model.Game import org.jeudego.pairgoth.model.Tournament import org.jeudego.pairgoth.model.getID @@ -66,7 +65,7 @@ object PairingHandler: PairgothApiHandler { return ret } - override fun put(request: HttpServletRequest, response: HttpServletResponse): Json { + override fun put(request: HttpServletRequest, response: HttpServletResponse): Json? { val tournament = getTournament(request) val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number") // only allow last round (if players have not been paired in the last round, it *may* be possible to be more laxist...) 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 d7c7b64..a0e63c8 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 @@ -31,7 +31,7 @@ object PlayerHandler: PairgothApiHandler { return Json.Object("success" to true, "id" to player.id) } - override fun put(request: HttpServletRequest, response: HttpServletResponse): Json { + override fun put(request: HttpServletRequest, response: HttpServletResponse): Json? { val tournament = getTournament(request) val id = getSubSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid player selector") val player = tournament.players[id] ?: badRequest("invalid player id") diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt index daef968..d9c4b63 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/ResultsHandler.kt @@ -18,7 +18,7 @@ object ResultsHandler: PairgothApiHandler { return games.map { it.toJson() }.toJsonArray() } - override fun put(request: HttpServletRequest, response: HttpServletResponse): Json { + override fun put(request: HttpServletRequest, response: HttpServletResponse): Json? { val tournament = getTournament(request) val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number") val payload = getObjectPayload(request) diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/StandingsHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/StandingsHandler.kt index e103ac1..41bdbd8 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/StandingsHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/StandingsHandler.kt @@ -2,27 +2,23 @@ package org.jeudego.pairgoth.api import com.republicate.kson.Json import com.republicate.kson.toJsonArray +import org.jeudego.pairgoth.api.PairingHandler.dispatchEvent 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.TimeSystem.TimeSystemType.* +import org.jeudego.pairgoth.model.toJson +import org.jeudego.pairgoth.server.Event import org.jeudego.pairgoth.server.WebappManager import java.io.OutputStreamWriter import java.nio.charset.StandardCharsets @@ -35,46 +31,8 @@ object StandingsHandler: PairgothApiHandler { val includePreliminary = request.getParameter("include_preliminary")?.let { it.toBoolean() } ?: false val sortedPairables = tournament.getSortedPairables(round, includePreliminary) - val sortedMap = sortedPairables.associateBy { - it.getID()!! - } + tournament.populateResultsArray(sortedPairables, round) - for (r in 1..round) { - tournament.games(r).values.forEach { game -> - val white = if (game.white != 0) sortedMap[game.white] else null - val black = if (game.black != 0) sortedMap[game.black] else null - val whiteNum = white?.getInt("num") ?: 0 - 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 "${game.handicap}" - assert(white != null || black != null) - if (white != null) { - val mark = when (game.result) { - UNKNOWN -> "?" - BLACK, BOTHLOOSE -> "-" - WHITE, BOTHWIN -> "+" - JIGO, CANCELLED -> "=" - } - val results = white.getArray("results") as Json.MutableArray - results[r - 1] = - if (blackNum == 0) "0$mark" - else "$blackNum$mark/$whiteColor$handicap" - } - if (black != null) { - val mark = when (game.result) { - UNKNOWN -> "?" - BLACK, BOTHWIN -> "+" - WHITE, BOTHLOOSE -> "-" - JIGO, CANCELLED -> "=" - } - val results = black.getArray("results") as Json.MutableArray - results[r - 1] = - if (whiteNum == 0) "0$mark" - else "$whiteNum$mark/$blackColor$handicap" - } - } - } val acceptHeader = request.getHeader("Accept") as String? val accept = acceptHeader?.substringBefore(";") val acceptEncoding = acceptHeader?.substringAfter(";charset=", "utf-8") ?: "utf-8" @@ -268,6 +226,14 @@ ${ } } + override fun put(request: HttpServletRequest, response: HttpServletResponse): Json? { + val tournament = getTournament(request) + val sortedPairables = tournament.getSortedPairables(tournament.rounds) + tournament.frozen = sortedPairables.toJsonArray() + tournament.dispatchEvent(Event.TournamentUpdated, request, tournament.toJson()) + return Json.Object("status" to "ok") + } + private val numFormat = DecimalFormat("###0.#") private val frDate: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy") } diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TeamHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TeamHandler.kt index 5389ddc..a638382 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TeamHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TeamHandler.kt @@ -29,7 +29,7 @@ object TeamHandler: PairgothApiHandler { return Json.Object("success" to true, "id" to team.id) } - override fun put(request: HttpServletRequest, response: HttpServletResponse): Json { + override fun put(request: HttpServletRequest, response: HttpServletResponse): Json? { val tournament = getTournament(request) if (tournament !is TeamTournament) badRequest("tournament is not a team tournament") val id = getSubSelector(request)?.toIntOrNull() ?: badRequest("missing or invalid player selector") diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt index 25b0e55..258bf16 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/TournamentHandler.kt @@ -34,6 +34,7 @@ object TournamentHandler: PairgothApiHandler { // additional attributes for the webapp json["stats"] = tour.stats() json["teamSize"] = tour.type.playersNumber + json["frozen"] = tour.frozen != null } } } ?: badRequest("no tournament with id #${id}") @@ -61,7 +62,7 @@ object TournamentHandler: PairgothApiHandler { return Json.Object("success" to true, "id" to tournament.id) } - override fun put(request: HttpServletRequest, response: HttpServletResponse): Json { + override fun put(request: HttpServletRequest, response: HttpServletResponse): Json? { // CB TODO - some checks are needed here (cannot lower rounds number if games have been played in removed rounds, for instance) val tournament = getTournament(request) val payload = getObjectPayload(request) 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 4358531..ede0652 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 @@ -52,6 +52,9 @@ sealed class Tournament ( protected val _pairables = mutableMapOf() val pairables: Map get() = _pairables + // frozen standings + var frozen: Json.Array? = null + // pairing fun pair(round: Int, pairables: List): List { // Minimal check on round number. @@ -335,7 +338,7 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n tournament.teams[team.getID("id")!!] = tournament.teamFromJson(team) } } - (json["games"] as Json.Array?)?.forEachIndexed { i, arr -> + json.getArray("games")?.forEachIndexed { i, arr -> val round = i + 1 val tournamentGames = tournament.games(round) val games = arr as Json.Array @@ -344,6 +347,9 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n tournamentGames[game.getID("id")!!] = Game.fromJson(game) } } + json.getArray("frozen")?.also { + tournament.frozen = it + } return tournament } @@ -374,5 +380,8 @@ fun Tournament<*>.toFullJson(): Json.Object { json["teams"] = Json.Array(teams.values.map { it.toJson() }) } json["games"] = Json.Array((1..lastRound()).mapTo(Json.MutableArray()) { round -> games(round).values.mapTo(Json.MutableArray()) { it.toJson() } }); + if (frozen != null) { + json["frozen"] = frozen + } return json } diff --git a/view-webapp/src/main/webapp/js/tour-standings.inc.js b/view-webapp/src/main/webapp/js/tour-standings.inc.js index ec477df..b051334 100644 --- a/view-webapp/src/main/webapp/js/tour-standings.inc.js +++ b/view-webapp/src/main/webapp/js/tour-standings.inc.js @@ -24,6 +24,16 @@ function publishHtml() { close_modal(); } +function freeze() { + api.put(`tour/${tour_id}/standings/${activeRound}`, {} + ).then(resp => { + if (resp.ok) { + document.location.reload(); + } + else throw "freeze error" + }).catch(err => showError(err)); +} + onLoad(() => { new Tablesort($('#standings-table')[0]); $('.criterium').on('click', e => { @@ -86,4 +96,7 @@ onLoad(() => { $('.publish-html').on('click', e => { publishHtml(); }); + $('#freeze').on('click', e => { + freeze() + }); }); diff --git a/view-webapp/src/main/webapp/tour-standings.inc.html b/view-webapp/src/main/webapp/tour-standings.inc.html index f8ec9a0..467c865 100644 --- a/view-webapp/src/main/webapp/tour-standings.inc.html +++ b/view-webapp/src/main/webapp/tour-standings.inc.html @@ -105,6 +105,12 @@
+#if(!$tour.frozen && $round == $tour.rounds) + +#end