diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt index 58a813a..f28139c 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/api/PairgothApiHandler.kt @@ -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) + if (event != Event.TournamentAdded && event != Event.TournamentDeleted) Store.replaceTournament(this) } 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 c41e81f..acb1650 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 @@ -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 @@ -59,7 +58,7 @@ object PairingHandler: PairgothApiHandler { } 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 } @@ -78,6 +77,7 @@ object PairingHandler: PairgothApiHandler { game.black = payload.getID("b") ?: badRequest("missing black player id") game.white = payload.getID("w") ?: badRequest("missing white player id") tournament.recomputeDUDD(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") @@ -90,10 +90,15 @@ object PairingHandler: PairgothApiHandler { 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") if (payload.containsKey("t")) { - // TODO CB - update *all* tables numbers accordingly game.table = payload.getString("t")?.toIntOrNull() ?: badRequest("invalid table number") } - tournament.dispatchEvent(gameUpdated, Json.Object("round" to round, "game" to game.toJson())) + 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) } @@ -111,7 +116,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 ") @@ -120,7 +125,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) } } 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 5c0795b..1780b4d 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 @@ -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) } } 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 372fdc6..a87b06d 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 @@ -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) } } 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 0e9af1b..ad5a5ca 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 @@ -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) } } 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 acd3d03..3f8d8c6 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 @@ -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) } } 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 f3adff8..c2e955e 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 @@ -90,6 +90,23 @@ sealed class Tournament ( val blackplayer = solver.pairables.find { p-> p.id == game.black }!! game.drawnUpDown = solver.dudd(blackplayer, whiteplayer) } + + 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 diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Event.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Event.kt index 9557757..57147ee 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Event.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/server/Event.kt @@ -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 { 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 18ebe20..3fece49 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 @@ -45,4 +45,17 @@ class PairgothTool { "SDC" to "Simplified direct confrontation", // Simplified direct confrontation "DC" to "Direct confrontation", // Direct confrontation ) + + fun getResultsStats(games: Collection): Json.Object { + var total = 0 + var known = 0 + games + .filter{ it.getInt("b")!! != 0 && it.getInt("w")!! != 0 } + .map { it -> it.getString("r") } + .forEach { + ++total + if ("?" != it) ++known + } + return Json.Object("total" to total, "known" to known) + } } \ No newline at end of file diff --git a/view-webapp/src/main/sass/main.scss b/view-webapp/src/main/sass/main.scss index 53b3511..0477ee6 100644 --- a/view-webapp/src/main/sass/main.scss +++ b/view-webapp/src/main/sass/main.scss @@ -425,6 +425,9 @@ font-size: 1rem !important; line-height: 1.1rem !important; min-width: 60vw; + &::before { + top: 0; + } } } diff --git a/view-webapp/src/main/webapp/WEB-INF/translations/fr b/view-webapp/src/main/webapp/WEB-INF/translations/fr index 5802fc6..3444910 100644 --- a/view-webapp/src/main/webapp/WEB-INF/translations/fr +++ b/view-webapp/src/main/webapp/WEB-INF/translations/fr @@ -145,5 +145,6 @@ the configuration guide le guide de configuration to à unpairable players joueurs non disponibles version 0.1 supports the version 0.1 supporte le système d’appariement +white blanc white vs. black blanc vs. Noir confirmed. confirmé(s). \ No newline at end of file diff --git a/view-webapp/src/main/webapp/js/tour-results.inc.js b/view-webapp/src/main/webapp/js/tour-results.inc.js index 9a76861..648a2e7 100644 --- a/view-webapp/src/main/webapp/js/tour-results.inc.js +++ b/view-webapp/src/main/webapp/js/tour-results.inc.js @@ -1,4 +1,4 @@ -function setResult(id, result) { +function setResult(id, result, previous) { api.putJson(`tour/${tour_id}/res/${activeRound}`, { id: id, result: result }) .then(res => { if (res !== 'error') { @@ -19,6 +19,16 @@ function setResult(id, result) { let resultCell = row.find('td.result'); resultCell.text(dispResult).data('result', result); standingsUpToDate = false; + + if (previous === '?') { + let indicator = $('#known')[0]; + let known = parseInt(indicator.innerText); + indicator.innerText = ++known; + } else if (result === '?') { + let indicator = $('#known')[0]; + let known = parseInt(indicator.innerText); + indicator.innerText = --known; + } } }) } @@ -36,9 +46,9 @@ onLoad(()=>{ $('#results-table .result').on('click', e => { let cell = e.target.closest('.result'); let gameId = e.target.closest('tr').data('id'); - let result = cell.data('result'); - let index = results.indexOf(result); - result = results[(index + 1)%results.length]; - setResult(gameId, result); + let oldResult = cell.data('result'); + let index = results.indexOf(oldResult); + let newResult = results[(index + 1)%results.length]; + setResult(gameId, newResult, oldResult); }); }); diff --git a/view-webapp/src/main/webapp/tour-results.inc.html b/view-webapp/src/main/webapp/tour-results.inc.html index bbbf404..7386aea 100644 --- a/view-webapp/src/main/webapp/tour-results.inc.html +++ b/view-webapp/src/main/webapp/tour-results.inc.html @@ -4,6 +4,8 @@ $round +#set($stats = $utils.getResultsStats($games)) + $stats.known / $stats.total
@@ -23,7 +25,7 @@ - + #end #end
#$game.t #if($white)$white.name $white.firstname #rank($white.rank)#{else}BIP#end #if($black)$black.name $black.firstname #rank($black.rank)#{else}BIP#end$dispRst[$game.r]$dispRst[$game.r]