Display individual standings below team standings
This commit is contained in:
@@ -7,6 +7,7 @@ import org.jeudego.pairgoth.model.MacMahon
|
||||
import org.jeudego.pairgoth.model.Pairable
|
||||
import org.jeudego.pairgoth.model.PairingType
|
||||
import org.jeudego.pairgoth.model.Player
|
||||
import org.jeudego.pairgoth.model.TeamTournament
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.model.getID
|
||||
import org.jeudego.pairgoth.model.historyBefore
|
||||
@@ -50,9 +51,9 @@ fun Tournament<*>.getSortedPairables(round: Int, includePreliminary: Boolean = f
|
||||
val mmBase = pairable.mmBase()
|
||||
val score = roundScore(mmBase +
|
||||
(nbW(pairable) ?: 0.0) +
|
||||
(1..round).map { round ->
|
||||
(1..round).sumOf { round ->
|
||||
if (playersPerRound.getOrNull(round - 1)?.contains(pairable.id) == true) 0.0 else 1.0
|
||||
}.sum() * pairing.pairingParams.main.mmsValueAbsent)
|
||||
} * pairing.pairingParams.main.mmsValueAbsent)
|
||||
Pair(
|
||||
if (pairing.pairingParams.main.sosValueAbsentUseBase) mmBase
|
||||
else roundScore(mmBase + round/2),
|
||||
@@ -100,14 +101,14 @@ fun Tournament<*>.getSortedPairables(round: Int, includePreliminary: Boolean = f
|
||||
Criterion.DC -> StandingsHandler.nullMap
|
||||
}
|
||||
}
|
||||
val pairables = pairables.values.filter { includePreliminary || it.final }.map { it.toDetailedJson() }
|
||||
pairables.forEach { player ->
|
||||
val jsonPairables = pairables.values.filter { includePreliminary || it.final }.map { it.toDetailedJson() }
|
||||
jsonPairables.forEach { player ->
|
||||
for (crit in criteria) {
|
||||
player[crit.first] = crit.second[player.getID()] ?: 0.0
|
||||
}
|
||||
player["results"] = Json.MutableArray(List(round) { "0=" })
|
||||
}
|
||||
val sortedPairables = pairables.sortedWith { left, right ->
|
||||
val sortedPairables = jsonPairables.sortedWith { left, right ->
|
||||
for (crit in criteria) {
|
||||
val lval = left.getDouble(crit.first) ?: 0.0
|
||||
val rval = right.getDouble(crit.first) ?: 0.0
|
||||
@@ -129,15 +130,16 @@ fun Tournament<*>.getSortedPairables(round: Int, includePreliminary: Boolean = f
|
||||
return sortedPairables
|
||||
}
|
||||
|
||||
fun Tournament<*>.populateStandings(sortedPairables: List<Json.Object>, round: Int = rounds) {
|
||||
val sortedMap = sortedPairables.associateBy {
|
||||
fun Tournament<*>.populateStandings(sortedEntries: List<Json.Object>, round: Int = rounds, individualStandings: Boolean) {
|
||||
val sortedMap = sortedEntries.associateBy {
|
||||
it.getID()!!
|
||||
}
|
||||
|
||||
// refresh name, firstname, club and level
|
||||
val refMap = if (individualStandings) players else pairables
|
||||
sortedMap.forEach { (id, pairable) ->
|
||||
val mutable = pairable as Json.MutableObject
|
||||
pairables[id]?.let {
|
||||
refMap[id]?.let {
|
||||
mutable["name"] = it.name
|
||||
if (it is Player) {
|
||||
mutable["firstname"] = it.firstname
|
||||
@@ -150,7 +152,8 @@ fun Tournament<*>.populateStandings(sortedPairables: List<Json.Object>, round: I
|
||||
|
||||
// fill result
|
||||
for (r in 1..round) {
|
||||
games(r).values.forEach { game ->
|
||||
val roundGames = if (individualStandings) individualGames(r) else games(r)
|
||||
roundGames.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
|
||||
@@ -186,3 +189,56 @@ fun Tournament<*>.populateStandings(sortedPairables: List<Json.Object>, round: I
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TeamTournament.getSortedTeamMembers(round: Int, includePreliminary: Boolean = false): List<Json.Object> {
|
||||
|
||||
val teamGames = historyBefore(round + 1)
|
||||
val individualHistory = teamGames.map { roundTeamGames ->
|
||||
roundTeamGames.flatMap { game -> individualGames[game.id]?.toList() ?: listOf() }
|
||||
}
|
||||
val historyHelper = HistoryHelper(individualHistory) {
|
||||
pairables.mapValues {
|
||||
Pair(0.0, wins[it.key] ?: 0.0)
|
||||
}
|
||||
}
|
||||
val neededCriteria = mutableListOf(Criterion.NBW, Criterion.RATING)
|
||||
val criteria = neededCriteria.map { crit ->
|
||||
crit.name to when (crit) {
|
||||
Criterion.NBW -> historyHelper.wins
|
||||
Criterion.RANK -> pairables.mapValues { it.value.rank }
|
||||
Criterion.RATING -> pairables.mapValues { it.value.rating }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
val jsonPlayers = players.values.filter { includePreliminary || it.final }.map { it.toDetailedJson() }
|
||||
jsonPlayers.forEach { player ->
|
||||
for (crit in criteria) {
|
||||
player[crit.first] = crit.second?.get(player.getID()) ?: 0.0
|
||||
}
|
||||
player["results"] = Json.MutableArray(List(round) { "0=" })
|
||||
}
|
||||
val sortedPlayers = jsonPlayers.sortedWith { left, right ->
|
||||
for (crit in criteria) {
|
||||
val lval = left.getDouble(crit.first) ?: 0.0
|
||||
val rval = right.getDouble(crit.first) ?: 0.0
|
||||
val cmp = lval.compareTo(rval)
|
||||
if (cmp != 0) return@sortedWith -cmp
|
||||
}
|
||||
return@sortedWith 0
|
||||
}.mapIndexed() { i, obj ->
|
||||
obj.set("num", i+1)
|
||||
}
|
||||
var place = 1
|
||||
sortedPlayers.groupBy { p ->
|
||||
Triple(
|
||||
criteria.getOrNull(0)?.first?.let { crit -> p.getDouble(crit) ?: 0.0 } ?: 0.0,
|
||||
criteria.getOrNull(1)?.first?.let { crit -> p.getDouble(crit) ?: 0.0 } ?: 0.0,
|
||||
criteria.getOrNull(2)?.first?.let { crit -> p.getDouble(crit) ?: 0.0 } ?: 0.0
|
||||
)
|
||||
}.forEach {
|
||||
it.value.forEach { p -> p["place"] = place }
|
||||
place += it.value.size
|
||||
}
|
||||
|
||||
return sortedPlayers
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ import org.jeudego.pairgoth.model.Criterion
|
||||
import org.jeudego.pairgoth.model.Criterion.*
|
||||
import org.jeudego.pairgoth.model.ID
|
||||
import org.jeudego.pairgoth.model.PairingType
|
||||
import org.jeudego.pairgoth.model.TeamTournament
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.model.adjustedTime
|
||||
import org.jeudego.pairgoth.model.displayRank
|
||||
@@ -27,10 +28,18 @@ object StandingsHandler: PairgothApiHandler {
|
||||
override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? {
|
||||
val tournament = getTournament(request)
|
||||
val round = getSubSelector(request)?.toIntOrNull() ?: tournament.rounds
|
||||
val includePreliminary = request.getParameter("include_preliminary")?.let { it.toBoolean() } ?: false
|
||||
val includePreliminary = request.getParameter("include_preliminary")?.toBoolean() ?: false
|
||||
|
||||
val sortedPairables = tournament.getSortedPairables(round, includePreliminary)
|
||||
tournament.populateStandings(sortedPairables, round)
|
||||
val individualStandings = tournament is TeamTournament &&
|
||||
tournament.type.individual &&
|
||||
request.getParameter("individual_standings")?.toBoolean() == true
|
||||
|
||||
val sortedEntries = if (individualStandings) {
|
||||
tournament.getSortedTeamMembers(round)
|
||||
} else {
|
||||
tournament.getSortedPairables(round, includePreliminary)
|
||||
}
|
||||
tournament.populateStandings(sortedEntries, round, individualStandings)
|
||||
|
||||
val acceptHeader = request.getHeader("Accept") as String?
|
||||
val accept = acceptHeader?.substringBefore(";")
|
||||
@@ -44,7 +53,7 @@ object StandingsHandler: PairgothApiHandler {
|
||||
PrintWriter(OutputStreamWriter(response.outputStream, encoding))
|
||||
}
|
||||
return when (accept) {
|
||||
"application/json" -> sortedPairables.toJsonArray()
|
||||
"application/json" -> sortedEntries.toJsonArray()
|
||||
"application/egf" -> {
|
||||
response.contentType = "text/plain;charset=${encoding}"
|
||||
val neededCriteria = ArrayList(tournament.pairing.placementParams.criteria)
|
||||
@@ -52,19 +61,19 @@ object StandingsHandler: PairgothApiHandler {
|
||||
if (neededCriteria.first() == SCOREX) {
|
||||
neededCriteria.add(1, MMS)
|
||||
}
|
||||
exportToEGFFormat(tournament, sortedPairables, neededCriteria, writer)
|
||||
exportToEGFFormat(tournament, sortedEntries, neededCriteria, writer)
|
||||
writer.flush()
|
||||
return null
|
||||
}
|
||||
"application/ffg" -> {
|
||||
response.contentType = "text/plain;charset=${encoding}"
|
||||
exportToFFGFormat(tournament, sortedPairables, writer)
|
||||
exportToFFGFormat(tournament, sortedEntries, writer)
|
||||
writer.flush()
|
||||
return null
|
||||
}
|
||||
"text/csv" -> {
|
||||
response.contentType = "text/csv;charset=${encoding}"
|
||||
exportToCSVFormat(tournament, sortedPairables, writer)
|
||||
exportToCSVFormat(tournament, sortedEntries, writer)
|
||||
writer.flush()
|
||||
return null
|
||||
}
|
||||
|
@@ -263,7 +263,7 @@ class TeamTournament(
|
||||
override fun individualGames(round: Int): Map<ID, Game> {
|
||||
val teamGames = games(round)
|
||||
return if (type.individual) {
|
||||
return teamGames.values.flatMap { game ->
|
||||
teamGames.values.flatMap { game ->
|
||||
if (game.white == 0 || game.black == 0 ) listOf()
|
||||
else individualGames[game.id]?.toList() ?: listOf()
|
||||
}.associateBy { it.id }
|
||||
|
@@ -56,6 +56,10 @@
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* header, center, footer */
|
||||
|
||||
#header {
|
||||
|
@@ -184,7 +184,7 @@ unpairable, non disponibles,
|
||||
supports the implémente le système d’appariement
|
||||
white blanc
|
||||
White Blanc
|
||||
white vs. black blanc vs. Noir
|
||||
white vs. black Blanc vs. Noir
|
||||
confirmed. confirmé(s).
|
||||
Note that login to this instance is reserved to French federation actors plus several external people at our discretion. Send us La connexion à cette instance est réservée aux acteurs de la FFG et à quelques personnes extérieures, à notre discrétion. Envoyez-nous
|
||||
yyyymmdd-city aaaammjj-ville
|
||||
|
@@ -30,6 +30,9 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
#if($tour.type.startsWith('TEAM'))
|
||||
<div class="strong">Team Standings</div>
|
||||
#end
|
||||
<div id="standings-container" class="roundbox">
|
||||
#set($standings = $api.get("tour/${params.id}/standings/$round"))
|
||||
#if($standings.isObject() && ($standings.error || $standings.message))
|
||||
@@ -105,6 +108,78 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
#if($tour.type.startsWith('TEAM'))
|
||||
<div class="strong">Individual Standings</div>
|
||||
<div id="individual-standings-container" class="roundbox">
|
||||
#set($indvstandings = $api.get("tour/${params.id}/standings/$round?individual_standings=true"))
|
||||
#if($indvstandings.isObject() && ($indvstandings.error || $indvstandings.message))
|
||||
#if($indvstandings.error)
|
||||
#set($error = $indvstandings.error)
|
||||
#else
|
||||
#set($error = $indvstandings.message)
|
||||
#end
|
||||
<script type="text/javascript">
|
||||
onLoad(() => {
|
||||
showError("$error")
|
||||
});
|
||||
</script>
|
||||
#set($indvstandings = [])
|
||||
#end
|
||||
#set($indvsmap = {})
|
||||
#foreach($part in $indvstandings)
|
||||
#set($indvsmap[$part.num] = $part)
|
||||
#end
|
||||
<table id="individual-standings-table" class="ui striped table">
|
||||
<thead>
|
||||
<th>Num</th>
|
||||
<th>Plc</th>
|
||||
<th>Name</th>
|
||||
<th>Rank</th>
|
||||
<th>Ctr</th>
|
||||
<th>Nbw</th>
|
||||
#foreach($r in [1..$round])
|
||||
<th>R$r</th>
|
||||
#end
|
||||
#set($indvcriteres = ['NBW'])
|
||||
#foreach($crit in $indvcriteres)
|
||||
<th>$crit</th>
|
||||
#end
|
||||
</thead>
|
||||
<tbody>
|
||||
#foreach($part in $indvstandings)
|
||||
<tr data-id="$part.id">
|
||||
<td>$part.num</td>
|
||||
<td>$part.place</td>
|
||||
<td>$esc.html($part.name)#if($part.firstname) $esc.html($part.firstname)#end</td>
|
||||
<td data-sort="$part.rank">#rank($part.rank)</td>
|
||||
<td>#if($part.country)$part.country#end</td>
|
||||
<td>$number.format('0.#', $part.NBW)</td>
|
||||
#set($mx = $round - 1)
|
||||
#foreach($r in [0..$mx])
|
||||
#set($rst = $part.results[$r])
|
||||
#set($opp_num = $math.toLong($rst))
|
||||
#if($opp_num)
|
||||
#set($opponent = $!indvsmap[$opp_num])
|
||||
#else
|
||||
#set($opponent = false)
|
||||
#end
|
||||
#if($rst.contains('+'))
|
||||
#set($rst = "<b>$rst</b>")
|
||||
#elseif($rst.contains('-'))
|
||||
#set($rst = "<i>$rst</i>")
|
||||
#end
|
||||
<td class="nobreak game-result" #if($opponent)title="$esc.html($opponent.name)#if($opponent.firstname) $esc.html($opponent.firstname)#end #rank($opponent.rank)#if($opponent.country) $opponent.country#end"#end>$rst</td>
|
||||
#end
|
||||
#foreach($crit in $indvcriteres)
|
||||
#set($value = "$number.format('0.#', $part[$crit])")
|
||||
<td data-sort="$value">$value.replace('.5', '½')</td>
|
||||
#end
|
||||
</tr>
|
||||
#end
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
#end
|
||||
<div class="right form-actions">
|
||||
#if(!$tour.frozen && $round == $tour.rounds)
|
||||
<button id="freeze" class="ui orange floating right labeled icon button">
|
||||
|
Reference in New Issue
Block a user