EGF and FFG exports
This commit is contained in:
@@ -3,21 +3,27 @@ package org.jeudego.pairgoth.api
|
|||||||
import com.republicate.kson.Json
|
import com.republicate.kson.Json
|
||||||
import com.republicate.kson.toJsonArray
|
import com.republicate.kson.toJsonArray
|
||||||
import org.jeudego.pairgoth.model.Criterion
|
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.MacMahon
|
||||||
import org.jeudego.pairgoth.model.Pairable
|
import org.jeudego.pairgoth.model.Pairable
|
||||||
import org.jeudego.pairgoth.model.PairingType
|
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.model.historyBefore
|
||||||
import org.jeudego.pairgoth.pairing.HistoryHelper
|
import org.jeudego.pairgoth.pairing.HistoryHelper
|
||||||
import org.jeudego.pairgoth.pairing.solver.MacMahonSolver
|
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.HttpServletRequest
|
||||||
import javax.servlet.http.HttpServletResponse
|
import javax.servlet.http.HttpServletResponse
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import org.jeudego.pairgoth.model.TimeSystem.TimeSystemType.*
|
||||||
import org.jeudego.pairgoth.model.Criterion.*
|
import java.text.DecimalFormat
|
||||||
import org.jeudego.pairgoth.model.Game.Result.*
|
|
||||||
import org.jeudego.pairgoth.model.ID
|
|
||||||
import org.jeudego.pairgoth.model.getID
|
|
||||||
|
|
||||||
object StandingsHandler: PairgothApiHandler {
|
object StandingsHandler: PairgothApiHandler {
|
||||||
override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? {
|
override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? {
|
||||||
@@ -83,7 +89,7 @@ object StandingsHandler: PairgothApiHandler {
|
|||||||
for (crit in criteria) {
|
for (crit in criteria) {
|
||||||
player[crit.first] = crit.second[player.getID()] ?: 0.0
|
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 ->
|
val sortedPairables = pairables.sortedWith { left, right ->
|
||||||
for (crit in criteria) {
|
for (crit in criteria) {
|
||||||
@@ -114,38 +120,156 @@ object StandingsHandler: PairgothApiHandler {
|
|||||||
val blackNum = black?.getInt("num") ?: 0
|
val blackNum = black?.getInt("num") ?: 0
|
||||||
val whiteColor = if (black == null) "" else "w"
|
val whiteColor = if (black == null) "" else "w"
|
||||||
val blackColor = if (white == null) "" else "b"
|
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)
|
assert(white != null || black != null)
|
||||||
if (white != null) {
|
if (white != null) {
|
||||||
val mark = when (game.result) {
|
val mark = when (game.result) {
|
||||||
UNKNOWN -> "?"
|
UNKNOWN -> "?"
|
||||||
BLACK -> "-"
|
BLACK, BOTHLOOSE -> "-"
|
||||||
WHITE -> "+"
|
WHITE, BOTHWIN -> "+"
|
||||||
JIGO -> "="
|
JIGO, CANCELLED -> "="
|
||||||
CANCELLED -> "X"
|
|
||||||
BOTHWIN -> "++"
|
|
||||||
BOTHLOOSE -> "--"
|
|
||||||
}
|
}
|
||||||
val results = white.getArray("results") as Json.MutableArray
|
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) {
|
if (black != null) {
|
||||||
val mark = when (game.result) {
|
val mark = when (game.result) {
|
||||||
UNKNOWN -> "?"
|
UNKNOWN -> "?"
|
||||||
BLACK -> "+"
|
BLACK, BOTHWIN -> "+"
|
||||||
WHITE -> "-"
|
WHITE, BOTHLOOSE -> "-"
|
||||||
JIGO -> "="
|
JIGO, CANCELLED -> "="
|
||||||
CANCELLED -> "X"
|
|
||||||
BOTHWIN -> "++"
|
|
||||||
BOTHLOOSE -> "--"
|
|
||||||
}
|
}
|
||||||
val results = black.getArray("results") as Json.MutableArray
|
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>()
|
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")
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@ package org.jeudego.pairgoth.ext
|
|||||||
|
|
||||||
import jakarta.xml.bind.JAXBContext
|
import jakarta.xml.bind.JAXBContext
|
||||||
import jakarta.xml.bind.JAXBElement
|
import jakarta.xml.bind.JAXBElement
|
||||||
import kotlinx.datetime.LocalDate
|
import java.time.LocalDate
|
||||||
import org.jeudego.pairgoth.model.*
|
import org.jeudego.pairgoth.model.*
|
||||||
import org.jeudego.pairgoth.opengotha.TournamentType
|
import org.jeudego.pairgoth.opengotha.TournamentType
|
||||||
import org.jeudego.pairgoth.opengotha.ObjectFactory
|
import org.jeudego.pairgoth.opengotha.ObjectFactory
|
||||||
@@ -13,7 +13,7 @@ import javax.xml.datatype.XMLGregorianCalendar
|
|||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
private const val MILLISECONDS_PER_DAY = 86400000
|
private const val MILLISECONDS_PER_DAY = 86400000
|
||||||
fun XMLGregorianCalendar.toLocalDate() = LocalDate(year, month, day)
|
fun XMLGregorianCalendar.toLocalDate() = LocalDate.of(year, month, day)
|
||||||
|
|
||||||
object OpenGotha {
|
object OpenGotha {
|
||||||
|
|
||||||
@@ -114,10 +114,10 @@ object OpenGotha {
|
|||||||
location = genParams.location,
|
location = genParams.location,
|
||||||
online = genParams.isBInternet ?: false,
|
online = genParams.isBInternet ?: false,
|
||||||
timeSystem = when (genParams.complementaryTimeSystem) {
|
timeSystem = when (genParams.complementaryTimeSystem) {
|
||||||
"SUDDENDEATH" -> SuddenDeath(genParams.basicTime)
|
"SUDDENDEATH" -> SuddenDeath(genParams.basicTime * 60)
|
||||||
"STDBYOYOMI" -> StandardByoyomi(genParams.basicTime, genParams.stdByoYomiTime, 1) // no periods?
|
"STDBYOYOMI" -> StandardByoyomi(genParams.basicTime * 60, genParams.stdByoYomiTime, 1) // no periods?
|
||||||
"CANBYOYOMI" -> CanadianByoyomi(genParams.basicTime, genParams.canByoYomiTime, genParams.nbMovesCanTime)
|
"CANBYOYOMI" -> CanadianByoyomi(genParams.basicTime * 60, genParams.canByoYomiTime, genParams.nbMovesCanTime)
|
||||||
"FISCHER" -> FischerTime(genParams.basicTime, genParams.fischerTime)
|
"FISCHER" -> FischerTime(genParams.basicTime * 60, genParams.fischerTime)
|
||||||
else -> throw Error("missing byoyomi type")
|
else -> throw Error("missing byoyomi type")
|
||||||
},
|
},
|
||||||
pairing = when (handParams.hdCeiling) {
|
pairing = when (handParams.hdCeiling) {
|
||||||
@@ -269,9 +269,9 @@ object OpenGotha {
|
|||||||
}
|
}
|
||||||
</ByePlayer>
|
</ByePlayer>
|
||||||
<TournamentParameterSet>
|
<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.SUDDEN_DEATH -> "SUDDENDEATH"
|
||||||
TimeSystem.TimeSystemType.STANDARD -> "STDBYOYOMI"
|
TimeSystem.TimeSystemType.JAPANESE -> "STDBYOYOMI"
|
||||||
TimeSystem.TimeSystemType.CANADIAN -> "CANBYOYOMI"
|
TimeSystem.TimeSystemType.CANADIAN -> "CANBYOYOMI"
|
||||||
TimeSystem.TimeSystemType.FISCHER -> "FISCHER"
|
TimeSystem.TimeSystemType.FISCHER -> "FISCHER"
|
||||||
} }" director="" endDate="${tournament.endDate}" fischerTime="${tournament.timeSystem.increment}" genCountNotPlayedGamesAsHalfPoint="false" genMMBar="${
|
} }" director="" endDate="${tournament.endDate}" fischerTime="${tournament.timeSystem.increment}" genCountNotPlayedGamesAsHalfPoint="false" genMMBar="${
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package org.jeudego.pairgoth.model
|
package org.jeudego.pairgoth.model
|
||||||
|
|
||||||
import com.republicate.kson.Json
|
import com.republicate.kson.Json
|
||||||
import org.jeudego.pairgoth.api.ApiHandler
|
|
||||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||||
import org.jeudego.pairgoth.model.TimeSystem.TimeSystemType.*
|
import org.jeudego.pairgoth.model.TimeSystem.TimeSystemType.*
|
||||||
|
|
||||||
@@ -15,7 +14,7 @@ data class TimeSystem(
|
|||||||
val stones: Int
|
val stones: Int
|
||||||
) {
|
) {
|
||||||
companion object {}
|
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) =
|
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) =
|
fun StandardByoyomi(mainTime: Int, byoyomi: Int, periods: Int) =
|
||||||
TimeSystem(
|
TimeSystem(
|
||||||
type = STANDARD,
|
type = JAPANESE,
|
||||||
mainTime = mainTime,
|
mainTime = mainTime,
|
||||||
increment = 0,
|
increment = 0,
|
||||||
byoyomi = byoyomi,
|
byoyomi = byoyomi,
|
||||||
@@ -86,9 +85,16 @@ fun TimeSystem.Companion.fromJson(json: Json.Object) =
|
|||||||
|
|
||||||
fun TimeSystem.toJson() = when (type) {
|
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.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 ->
|
TimeSystem.TimeSystemType.FISCHER ->
|
||||||
if (maxTime == Int.MAX_VALUE) Json.Object("type" to type.name, "mainTime" to mainTime, "increment" to increment)
|
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)
|
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)
|
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,7 @@ package org.jeudego.pairgoth.model
|
|||||||
|
|
||||||
import com.republicate.kson.Json
|
import com.republicate.kson.Json
|
||||||
import com.republicate.kson.toJsonArray
|
import com.republicate.kson.toJsonArray
|
||||||
import kotlinx.datetime.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.MacMahonSolver
|
||||||
import org.jeudego.pairgoth.pairing.solver.SwissSolver
|
import org.jeudego.pairgoth.pairing.solver.SwissSolver
|
||||||
@@ -169,8 +169,8 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n
|
|||||||
type = type,
|
type = type,
|
||||||
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
|
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
|
||||||
shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"),
|
shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"),
|
||||||
startDate = json.getLocalDate("startDate") ?: default?.startDate ?: badRequest("missing startDate"),
|
startDate = json.getString("startDate")?.let { LocalDate.parse(it) } ?: default?.startDate ?: badRequest("missing startDate"),
|
||||||
endDate = json.getLocalDate("endDate") ?: default?.endDate ?: badRequest("missing endDate"),
|
endDate = json.getString("endDate")?.let { LocalDate.parse(it) } ?: default?.endDate ?: badRequest("missing endDate"),
|
||||||
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
||||||
location = json.getString("location") ?: default?.location ?: badRequest("missing location"),
|
location = json.getString("location") ?: default?.location ?: badRequest("missing location"),
|
||||||
online = json.getBoolean("online") ?: default?.online ?: false,
|
online = json.getBoolean("online") ?: default?.online ?: false,
|
||||||
@@ -187,8 +187,8 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n
|
|||||||
type = type,
|
type = type,
|
||||||
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
|
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
|
||||||
shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"),
|
shortName = json.getString("shortName") ?: default?.shortName ?: badRequest("missing shortName"),
|
||||||
startDate = json.getLocalDate("startDate") ?: default?.startDate ?: badRequest("missing startDate"),
|
startDate = json.getString("startDate")?.let { LocalDate.parse(it) } ?: default?.startDate ?: badRequest("missing startDate"),
|
||||||
endDate = json.getLocalDate("endDate") ?: default?.endDate ?: badRequest("missing endDate"),
|
endDate = json.getString("endDate")?.let { LocalDate.parse(it) } ?: default?.endDate ?: badRequest("missing endDate"),
|
||||||
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
||||||
location = json.getString("location") ?: default?.location ?: badRequest("missing location"),
|
location = json.getString("location") ?: default?.location ?: badRequest("missing location"),
|
||||||
online = json.getBoolean("online") ?: default?.online ?: false,
|
online = json.getBoolean("online") ?: default?.online ?: false,
|
||||||
|
@@ -218,8 +218,12 @@ class ApiServlet: HttpServlet() {
|
|||||||
"Missing 'Accept' header"
|
"Missing 'Accept' header"
|
||||||
)
|
)
|
||||||
// CB TODO 1) a reference to a specific API call at this point is a code smell.
|
// 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
|
// 2) there will be other content types: .tou, .h9, .html
|
||||||
if (!isJson(accept) && (!isXml(accept) || !request.requestURI.matches(Regex("/api/tour/\\d+")))) throw ApiException(
|
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,
|
HttpServletResponse.SC_BAD_REQUEST,
|
||||||
"Invalid 'Accept' header"
|
"Invalid 'Accept' header"
|
||||||
)
|
)
|
||||||
|
@@ -153,11 +153,6 @@
|
|||||||
<artifactId>kotlin-reflect</artifactId>
|
<artifactId>kotlin-reflect</artifactId>
|
||||||
<version>${kotlin.version}</version>
|
<version>${kotlin.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.jetbrains.kotlinx</groupId>
|
|
||||||
<artifactId>kotlinx-datetime-jvm</artifactId>
|
|
||||||
<version>0.4.0</version>
|
|
||||||
</dependency>
|
|
||||||
<!-- servlets and mail APIs -->
|
<!-- servlets and mail APIs -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>jakarta.servlet</groupId>
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
@@ -54,6 +54,11 @@
|
|||||||
|
|
||||||
/* registration section */
|
/* registration section */
|
||||||
|
|
||||||
|
#players-list {
|
||||||
|
max-width: 95vw;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
#player {
|
#player {
|
||||||
&.create {
|
&.create {
|
||||||
.edition {
|
.edition {
|
||||||
@@ -280,4 +285,8 @@
|
|||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#standings-container {
|
||||||
|
max-width: 95vw;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,13 +9,16 @@ const apiVersion = '1.0';
|
|||||||
// .catch(err => { ... });
|
// .catch(err => { ... });
|
||||||
|
|
||||||
const base = '/api/';
|
const base = '/api/';
|
||||||
let headers = function() {
|
let headers = function(withJson) {
|
||||||
let ret = {
|
let ret = {
|
||||||
"Content-Type": "application/json; charset=utf-8",
|
'Accept-Version': apiVersion,
|
||||||
"Accept-Version": apiVersion,
|
'Accept': 'application/json',
|
||||||
"Accept": "application/json",
|
'X-Browser-Key': store('browserKey')
|
||||||
"X-Browser-Key": store('browserKey')
|
|
||||||
};
|
};
|
||||||
|
if (typeof(withJson) === 'undefined') withJson = true;
|
||||||
|
if (withJson) {
|
||||||
|
ret['Content-Type'] = 'application/json';
|
||||||
|
}
|
||||||
let accessToken = store('accessToken');
|
let accessToken = store('accessToken');
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
ret['Authorization'] = `Bearer ${accessToken}`;
|
ret['Authorization'] = `Bearer ${accessToken}`;
|
||||||
|
@@ -1,3 +1,20 @@
|
|||||||
|
function publish(format, extension) {
|
||||||
|
let form = $('#tournament-infos')[0];
|
||||||
|
let shortName = form.val('shortName');
|
||||||
|
let hdrs = headers();
|
||||||
|
hdrs['Accept'] = `application/${format}`
|
||||||
|
fetch(`api/tour/${tour_id}/standings/${activeRound}`, {
|
||||||
|
headers: hdrs
|
||||||
|
}).then(resp => {
|
||||||
|
if (resp.ok) return resp.text()
|
||||||
|
else throw "publish error"
|
||||||
|
}).then(txt => {
|
||||||
|
let blob = new Blob(['\uFEFF', txt.trim()], {type: 'plain/text;charset=utf-8'});
|
||||||
|
downloadFile(blob, `${shortName}.${extension}`);
|
||||||
|
close_modal();
|
||||||
|
}).catch(err => showError(err));
|
||||||
|
}
|
||||||
|
|
||||||
onLoad(() => {
|
onLoad(() => {
|
||||||
$('.criterium').on('click', e => {
|
$('.criterium').on('click', e => {
|
||||||
let alreadyOpen = e.target.closest('select');
|
let alreadyOpen = e.target.closest('select');
|
||||||
@@ -45,4 +62,10 @@ onLoad(() => {
|
|||||||
$('#publish-modal').on('click', e => {
|
$('#publish-modal').on('click', e => {
|
||||||
close_modal();
|
close_modal();
|
||||||
});
|
});
|
||||||
|
$('.publish-ffg').on('click', e => {
|
||||||
|
publish('ffg', 'tou');
|
||||||
|
});
|
||||||
|
$('.publish-egf').on('click', e => {
|
||||||
|
publish('egf', 'h9');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -101,9 +101,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="popup-footer">
|
<div class="popup-footer">
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button class="ui gray floating cancel button">Cancel</button>
|
<button type="button" class="ui gray floating cancel button">Cancel</button>
|
||||||
<button class="ui blue floating button">EGF</button>
|
<button type="button" class="ui blue floating publish-egf button">EGF</button>
|
||||||
<button class="ui blue floating button">FFG</button>
|
<button type="button" class="ui blue floating publish-ffg button">FFG</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Reference in New Issue
Block a user