Table numbers exclusion: handle saving

This commit is contained in:
Claude Brisson
2024-07-27 15:53:41 +02:00
parent 02e1fb8319
commit 6e97c91b2f
5 changed files with 77 additions and 26 deletions

View File

@@ -67,24 +67,40 @@ object TournamentHandler: PairgothApiHandler {
val payload = getObjectPayload(request) val payload = getObjectPayload(request)
// disallow changing type // disallow changing type
if (payload.getString("type")?.let { it != tournament.type.name } == true) badRequest("tournament type cannot be changed") if (payload.getString("type")?.let { it != tournament.type.name } == true) badRequest("tournament type cannot be changed")
val updated = Tournament.fromJson(payload, tournament) // specific handling for 'excludeTables'
// copy players, games, criteria (this copy should be provided by the Tournament class - CB TODO) if (payload.containsKey("excludeTables")) {
updated.players.putAll(tournament.players) val tablesExclusion = payload.getString("excludeTables") ?: badRequest("missing 'excludeTables'")
if (tournament is TeamTournament && updated is TeamTournament) { validateTablesExclusion(tablesExclusion)
updated.teams.putAll(tournament.teams) val round = payload.getInt("round") ?: badRequest("missing 'round'")
while (tournament.tablesExclusion.size < round) tournament.tablesExclusion.add("")
tournament.tablesExclusion[round - 1] = tablesExclusion
tournament.dispatchEvent(TournamentUpdated, request, tournament.toJson())
} else {
val updated = Tournament.fromJson(payload, tournament)
// copy players, games, criteria (this copy should be provided by the Tournament class - CB TODO)
updated.players.putAll(tournament.players)
if (tournament is TeamTournament && updated is TeamTournament) {
updated.teams.putAll(tournament.teams)
}
for (round in 1..tournament.lastRound()) updated.games(round).apply {
clear()
putAll(tournament.games(round))
}
updated.dispatchEvent(TournamentUpdated, request, updated.toJson())
} }
for (round in 1..tournament.lastRound()) updated.games(round).apply {
clear()
putAll(tournament.games(round))
}
updated.dispatchEvent(TournamentUpdated, request, updated.toJson())
return Json.Object("success" to true) return Json.Object("success" to true)
} }
private fun validateTablesExclusion(exclusion: String) {
if (!tablesExclusionValidator.matches(exclusion)) badRequest("invalid tables exclusion pattern")
}
override fun delete(request: HttpServletRequest, response: HttpServletResponse): Json { override fun delete(request: HttpServletRequest, response: HttpServletResponse): Json {
val tournament = getTournament(request) val tournament = getTournament(request)
getStore(request).deleteTournament(tournament) getStore(request).deleteTournament(tournament)
tournament.dispatchEvent(TournamentDeleted, request, Json.Object("id" to tournament.id)) tournament.dispatchEvent(TournamentDeleted, request, Json.Object("id" to tournament.id))
return Json.Object("success" to true) return Json.Object("success" to true)
} }
private val tablesExclusionValidator = Regex("^(?:(?:\\s+|,)*\\d+(?:-\\d+)?)*$")
} }

View File

@@ -6,8 +6,6 @@ import com.republicate.kson.toJsonArray
//import kotlinx.datetime.LocalDate //import kotlinx.datetime.LocalDate
import java.time.LocalDate import java.time.LocalDate
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest 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.nextPlayerId import org.jeudego.pairgoth.store.nextPlayerId
import org.jeudego.pairgoth.store.nextTournamentId import org.jeudego.pairgoth.store.nextTournamentId
import kotlin.math.max import kotlin.math.max
@@ -30,7 +28,8 @@ sealed class Tournament <P: Pairable>(
val pairing: Pairing, val pairing: Pairing,
val rules: Rules = Rules.FRENCH, val rules: Rules = Rules.FRENCH,
val gobanSize: Int = 19, val gobanSize: Int = 19,
val komi: Double = 7.5 val komi: Double = 7.5,
val tablesExclusion: MutableList<String> = mutableListOf()
) { ) {
companion object {} companion object {}
enum class Type(val playersNumber: Int, val individual: Boolean = true) { enum class Type(val playersNumber: Int, val individual: Boolean = true) {
@@ -170,8 +169,9 @@ class StandardTournament(
pairing: Pairing, pairing: Pairing,
rules: Rules = Rules.FRENCH, rules: Rules = Rules.FRENCH,
gobanSize: Int = 19, gobanSize: Int = 19,
komi: Double = 7.5 komi: Double = 7.5,
): Tournament<Player>(id, type, name, shortName, startDate, endDate, director, country, location, online, timeSystem, rounds, pairing, rules, gobanSize, komi) { tablesExclusion: MutableList<String> = mutableListOf()
): Tournament<Player>(id, type, name, shortName, startDate, endDate, director, country, location, online, timeSystem, rounds, pairing, rules, gobanSize, komi, tablesExclusion) {
override val players get() = _pairables override val players get() = _pairables
} }
@@ -192,8 +192,9 @@ class TeamTournament(
pairing: Pairing, pairing: Pairing,
rules: Rules = Rules.FRENCH, rules: Rules = Rules.FRENCH,
gobanSize: Int = 19, gobanSize: Int = 19,
komi: Double = 7.5 komi: Double = 7.5,
): Tournament<TeamTournament.Team>(id, type, name, shortName, startDate, endDate, director, country, location, online, timeSystem, rounds, pairing, rules, gobanSize, komi) { tablesExclusion: MutableList<String> = mutableListOf()
): Tournament<TeamTournament.Team>(id, type, name, shortName, startDate, endDate, director, country, location, online, timeSystem, rounds, pairing, rules, gobanSize, komi, tablesExclusion) {
companion object { companion object {
private val epsilon = 0.0001 private val epsilon = 0.0001
} }
@@ -267,7 +268,8 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n
gobanSize = json.getInt("gobanSize") ?: default?.gobanSize ?: 19, gobanSize = json.getInt("gobanSize") ?: default?.gobanSize ?: 19,
timeSystem = json.getObject("timeSystem")?.let { TimeSystem.fromJson(it) } ?: default?.timeSystem ?: badRequest("missing timeSystem"), timeSystem = json.getObject("timeSystem")?.let { TimeSystem.fromJson(it) } ?: default?.timeSystem ?: badRequest("missing timeSystem"),
rounds = json.getInt("rounds") ?: default?.rounds ?: badRequest("missing rounds"), rounds = json.getInt("rounds") ?: default?.rounds ?: badRequest("missing rounds"),
pairing = json.getObject("pairing")?.let { Pairing.fromJson(it, default?.pairing) } ?: default?.pairing ?: badRequest("missing pairing") pairing = json.getObject("pairing")?.let { Pairing.fromJson(it, default?.pairing) } ?: default?.pairing ?: badRequest("missing pairing"),
tablesExclusion = json.getArray("tablesExclusion")?.map { item -> item as String }?.toMutableList() ?: default?.tablesExclusion ?: mutableListOf()
) )
else else
TeamTournament( TeamTournament(
@@ -286,7 +288,8 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n
gobanSize = json.getInt("gobanSize") ?: default?.gobanSize ?: 19, gobanSize = json.getInt("gobanSize") ?: default?.gobanSize ?: 19,
timeSystem = json.getObject("timeSystem")?.let { TimeSystem.fromJson(it) } ?: default?.timeSystem ?: badRequest("missing timeSystem"), timeSystem = json.getObject("timeSystem")?.let { TimeSystem.fromJson(it) } ?: default?.timeSystem ?: badRequest("missing timeSystem"),
rounds = json.getInt("rounds") ?: default?.rounds ?: badRequest("missing rounds"), rounds = json.getInt("rounds") ?: default?.rounds ?: badRequest("missing rounds"),
pairing = json.getObject("pairing")?.let { Pairing.fromJson(it, default?.pairing) } ?: default?.pairing ?: badRequest("missing pairing") pairing = json.getObject("pairing")?.let { Pairing.fromJson(it, default?.pairing) } ?: default?.pairing ?: badRequest("missing pairing"),
tablesExclusion = json.getArray("tablesExclusion")?.map { item -> item as String }?.toMutableList() ?: default?.tablesExclusion ?: mutableListOf()
) )
json.getArray("players")?.forEach { obj -> json.getArray("players")?.forEach { obj ->
val pairable = obj as Json.Object val pairable = obj as Json.Object
@@ -326,7 +329,8 @@ fun Tournament<*>.toJson() = Json.MutableObject(
"gobanSize" to gobanSize, "gobanSize" to gobanSize,
"timeSystem" to timeSystem.toJson(), "timeSystem" to timeSystem.toJson(),
"rounds" to rounds, "rounds" to rounds,
"pairing" to pairing.toJson() "pairing" to pairing.toJson(),
"tablesExclusion" to tablesExclusion.toJsonArray()
) )
fun Tournament<*>.toFullJson(): Json.Object { fun Tournament<*>.toFullJson(): Json.Object {

View File

@@ -405,6 +405,10 @@
margin-top: 0.2em; margin-top: 0.2em;
} }
.tables-exclusion {
margin-top: 0.2em;
}
/* results section */ /* results section */
#results-filter { #results-filter {

View File

@@ -1,12 +1,31 @@
let focused = undefined; let focused = undefined;
function pair(parts) { function pair(parts) {
api.postJson(`tour/${tour_id}/pair/${activeRound}`, parts)
.then(rst => { let doWork = () => {
if (rst !== 'error') { api.postJson(`tour/${tour_id}/pair/${activeRound}`, parts)
document.location.reload(); .then(rst => {
} if (rst !== 'error') {
}); document.location.reload();
}
});
}
let tablesExclusionControl = $('#exclude-tables');
let value = tablesExclusionControl[0].value;
let origValue = tablesExclusionControl.data('orig');
if (value === origValue) {
// tables exclusion value did not change
doWork();
} else {
// tables exclusion value has change, we must save it first
api.putJson(`tour/${tour_id}`, { round: activeRound, excludeTables: value })
.then(rst => {
if (rst !== 'error') {
doWork();
}
});
}
} }
function unpair(games) { function unpair(games) {

View File

@@ -22,6 +22,14 @@
<button class="ui floating choose-round next-round button">&raquo;</button> <button class="ui floating choose-round next-round button">&raquo;</button>
</div> </div>
<div class="pairing-stats nobreak">( $pairables.size() pairable, $games.size() games )</div> <div class="pairing-stats nobreak">( $pairables.size() pairable, $games.size() games )</div>
<div class="tables-exclusion">
#if($tour.tablesExclusion && $round <= $tour.tablesExclusion.size())
#set($tablesExclusion = $!tour.tablesExclusion[$round - 1])
#else
#set($tablesExclusion = '')
#end
Exclude table numbers: <input type="text" id="exclude-tables" name="exclude-tables" placeholder="ex: 1-34, 38, 45-77" data-orig="$tablesExclusion" value="$tablesExclusion"/>
</div>
<div id="pairing-lists"> <div id="pairing-lists">
<div id="pairing-left"> <div id="pairing-left">
<div id="pairables" class="multi-select" title="pairable players"> <div id="pairables" class="multi-select" title="pairable players">