Merge from webview2
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
package org.jeudego.pairgoth.api
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.web.ApiException
|
||||
import org.jeudego.pairgoth.server.ApiException
|
||||
import org.slf4j.LoggerFactory
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
@@ -3,7 +3,7 @@ package org.jeudego.pairgoth.api
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import org.jeudego.pairgoth.web.Event
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
|
||||
interface PairgothApiHandler: ApiHandler {
|
||||
|
@@ -6,7 +6,8 @@ import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.getID
|
||||
import org.jeudego.pairgoth.model.toID
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.web.Event.*
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import org.jeudego.pairgoth.server.Event.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
@@ -15,10 +16,18 @@ object PairingHandler: PairgothApiHandler {
|
||||
override fun get(request: HttpServletRequest, response: HttpServletResponse): Json? {
|
||||
val tournament = getTournament(request)
|
||||
val round = getSubSelector(request)?.toIntOrNull() ?: badRequest("invalid round number")
|
||||
if (round > tournament.lastRound() + 1) badRequest("invalid round: previous round has not been played")
|
||||
val playing = tournament.games(round).values.flatMap {
|
||||
listOf(it.black, it.white)
|
||||
}.toSet()
|
||||
return tournament.pairables.values.filter { !it.skip.contains(round) && !playing.contains(it.id) }.map { it.id }.toJsonArray()
|
||||
val unpairables = tournament.pairables.values.filter { it.skip.contains(round) }.sortedByDescending { it.rating }.map { it.id }.toJsonArray()
|
||||
val pairables = tournament.pairables.values.filter { !it.skip.contains(round) && !playing.contains(it.id) }.sortedByDescending { it.rating }.map { it.id }.toJsonArray()
|
||||
val games = tournament.games(round).values
|
||||
return Json.Object(
|
||||
"games" to games.map { it.toJson() }.toCollection(Json.MutableArray()),
|
||||
"pairables" to pairables,
|
||||
"unpairables" to unpairables
|
||||
)
|
||||
}
|
||||
|
||||
override fun post(request: HttpServletRequest): Json {
|
||||
@@ -54,9 +63,19 @@ object PairingHandler: PairgothApiHandler {
|
||||
// only allow last round (if players have not been paired in the last round, it *may* be possible to be more laxist...)
|
||||
if (round != tournament.lastRound()) badRequest("cannot edit pairings in other rounds but the last")
|
||||
val payload = getObjectPayload(request)
|
||||
val game = tournament.games(round)[payload.getInt("id")] ?: badRequest("invalid game id")
|
||||
val gameId = payload.getInt("id") ?: badRequest("invalid game id")
|
||||
val game = tournament.games(round)[gameId] ?: badRequest("invalid game id")
|
||||
val playing = (tournament.games(round).values).filter { it.id != gameId }.flatMap {
|
||||
listOf(it.black, it.white)
|
||||
}.toSet()
|
||||
game.black = payload.getID("b") ?: badRequest("missing black player id")
|
||||
game.white = payload.getID("w") ?: badRequest("missing white player id")
|
||||
val black = tournament.pairables[game.black] ?: badRequest("invalid black player id")
|
||||
val white = tournament.pairables[game.black] ?: badRequest("invalid white player id")
|
||||
if (black.skip.contains(round)) badRequest("black is not playing this round")
|
||||
if (white.skip.contains(round)) badRequest("white is not playing this round")
|
||||
if (playing.contains(black.id)) badRequest("black is already in another game")
|
||||
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")
|
||||
tournament.dispatchEvent(gameUpdated, Json.Object("round" to round, "game" to game.toJson()))
|
||||
return Json.Object("success" to true)
|
||||
|
@@ -5,8 +5,8 @@ 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.web.Event
|
||||
import org.jeudego.pairgoth.web.Event.*
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import org.jeudego.pairgoth.server.Event.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
|
@@ -5,7 +5,7 @@ import com.republicate.kson.toJsonArray
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.web.Event
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
|
@@ -4,8 +4,8 @@ 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.web.Event
|
||||
import org.jeudego.pairgoth.web.Event.*
|
||||
import org.jeudego.pairgoth.server.Event
|
||||
import org.jeudego.pairgoth.server.Event.*
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
|
@@ -10,9 +10,9 @@ import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.model.fromJson
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import org.jeudego.pairgoth.web.ApiServlet
|
||||
import org.jeudego.pairgoth.web.Event
|
||||
import org.jeudego.pairgoth.web.Event.*
|
||||
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
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
@@ -64,8 +64,7 @@ object TournamentHandler: PairgothApiHandler {
|
||||
clear()
|
||||
putAll(tournament.games(round))
|
||||
}
|
||||
Store.replaceTournament(updated)
|
||||
tournament.dispatchEvent(tournamentUpdated, tournament.toJson())
|
||||
updated.dispatchEvent(tournamentUpdated, updated.toJson())
|
||||
return Json.Object("success" to true)
|
||||
}
|
||||
|
||||
|
@@ -151,6 +151,7 @@ object OpenGotha {
|
||||
it.value.map { game ->
|
||||
Game(
|
||||
id = Store.nextGameId,
|
||||
table = game.tableNumber,
|
||||
black = canonicMap[game.blackPlayer] ?: throw Error("player not found: ${game.blackPlayer}"),
|
||||
white = canonicMap[game.whitePlayer] ?: throw Error("player not found: ${game.whitePlayer}"),
|
||||
handicap = game.handicap,
|
||||
|
@@ -6,6 +6,7 @@ import java.util.*
|
||||
|
||||
data class Game(
|
||||
val id: ID,
|
||||
var table: Int,
|
||||
var white: ID,
|
||||
var black: ID,
|
||||
var handicap: Int = 0,
|
||||
@@ -33,6 +34,7 @@ data class Game(
|
||||
|
||||
fun Game.toJson() = Json.Object(
|
||||
"id" to id,
|
||||
"t" to table,
|
||||
"w" to white,
|
||||
"b" to black,
|
||||
"h" to handicap,
|
||||
@@ -42,6 +44,7 @@ fun Game.toJson() = Json.Object(
|
||||
|
||||
fun Game.Companion.fromJson(json: Json.Object) = Game(
|
||||
id = json.getID("id") ?: throw Error("missing game id"),
|
||||
table = json.getInt("t") ?: throw Error("missing game table"),
|
||||
white = json.getID("w") ?: throw Error("missing white player"),
|
||||
black = json.getID("b") ?: throw Error("missing black player"),
|
||||
handicap = json.getInt("h") ?: 0,
|
||||
|
@@ -3,6 +3,7 @@ package org.jeudego.pairgoth.model
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.api.ApiHandler.Companion.badRequest
|
||||
import org.jeudego.pairgoth.store.Store
|
||||
import java.util.*
|
||||
|
||||
// Pairable
|
||||
|
||||
@@ -51,6 +52,13 @@ fun Pairable.Companion.parseRank(rankStr: String): Int {
|
||||
|
||||
// Player
|
||||
|
||||
enum class DatabaseId {
|
||||
AGA,
|
||||
EGF,
|
||||
FFG;
|
||||
val key get() = this.name.lowercase(Locale.ROOT)
|
||||
}
|
||||
|
||||
class Player(
|
||||
id: ID,
|
||||
name: String,
|
||||
@@ -62,7 +70,7 @@ class Player(
|
||||
): Pairable(id, name, rating, rank) {
|
||||
companion object
|
||||
// used to store external IDs ("FFG" => FFG ID, "EGF" => EGF PIN, "AGA" => AGA ID ...)
|
||||
val externalIds = mutableMapOf<String, String>()
|
||||
val externalIds = mutableMapOf<DatabaseId, String>()
|
||||
override fun toJson(): Json.Object = Json.MutableObject(
|
||||
"id" to id,
|
||||
"name" to name,
|
||||
@@ -71,8 +79,11 @@ class Player(
|
||||
"rank" to rank,
|
||||
"country" to country,
|
||||
"club" to club
|
||||
).also {
|
||||
if (skip.isNotEmpty()) it["skip"] = Json.Array(skip)
|
||||
).also { json ->
|
||||
if (skip.isNotEmpty()) json["skip"] = Json.Array(skip)
|
||||
externalIds.forEach { (dbid, id) ->
|
||||
json[dbid.key] = id
|
||||
}
|
||||
}
|
||||
override fun nameSeed(separator: String): String {
|
||||
return name + separator + firstname
|
||||
@@ -92,4 +103,9 @@ fun Player.Companion.fromJson(json: Json.Object, default: Player? = null) = Play
|
||||
json.getArray("skip")?.let {
|
||||
if (it.isNotEmpty()) player.skip.addAll(it.map { id -> (id as Number).toInt() })
|
||||
}
|
||||
DatabaseId.values().forEach { dbid ->
|
||||
json.getString(dbid.key)?.let { id ->
|
||||
player.externalIds[dbid] = id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -49,7 +49,7 @@ data class MainCritParams(
|
||||
val additionalPlacementCritSystem1: Criterion = Criterion.RATING,
|
||||
val additionalPlacementCritSystem2: Criterion = Criterion.NONE,
|
||||
) {
|
||||
enum class DrawUpDown {TOP, MIDDLE, BOTTOM}
|
||||
enum class DrawUpDown { TOP, MIDDLE, BOTTOM }
|
||||
enum class SeedMethod { SPLIT_AND_FOLD, SPLIT_AND_RANDOM, SPLIT_AND_SLIP }
|
||||
companion object {
|
||||
const val MAX_CATEGORIES_WEIGHT = 20000000000000.0 // 2e13
|
||||
@@ -185,7 +185,7 @@ class MacMahon(
|
||||
handicap = HandicapParams(
|
||||
weight = MainCritParams.MAX_SCORE_WEIGHT, // TODO - contradictory with the comment above (not used anyway ?!)
|
||||
useMMS = true,
|
||||
rankThreshold = 0,
|
||||
rankThreshold = -20,
|
||||
ceiling = 9
|
||||
)
|
||||
),
|
||||
|
@@ -3,7 +3,7 @@ package org.jeudego.pairgoth.oauth
|
||||
// In progress
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.web.WebappManager
|
||||
import org.jeudego.pairgoth.server.WebappManager
|
||||
//import com.republicate.modality.util.AESCryptograph
|
||||
//import com.republicate.modality.util.Cryptograph
|
||||
import org.apache.commons.codec.binary.Base64
|
||||
@@ -21,7 +21,7 @@ abstract class OAuthHelper {
|
||||
protected get() = WebappManager.getMandatoryProperty("oauth." + name + ".secret")
|
||||
protected val redirectURI: String?
|
||||
protected get() = try {
|
||||
val uri: String = WebappManager.Companion.getProperty("webapp.url") + "/oauth.html"
|
||||
val uri: String = WebappManager.getProperty("webapp.external.url") + "/oauth.html"
|
||||
URLEncoder.encode(uri, "UTF-8")
|
||||
} catch (uee: UnsupportedEncodingException) {
|
||||
logger.error("could not encode redirect URI", uee)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package org.jeudego.pairgoth.pairing
|
||||
|
||||
import org.jeudego.pairgoth.model.*
|
||||
import java.util.*
|
||||
|
||||
abstract class BasePairingHelper(
|
||||
history: List<List<Game>>, // History of all games played for each round
|
||||
@@ -131,4 +132,11 @@ abstract class BasePairingHelper(
|
||||
open fun nameSort(p: Pairable, q: Pairable): Int {
|
||||
return if (p.name > q.name) 1 else -1
|
||||
}
|
||||
|
||||
val tables = history.mapTo(mutableListOf()) { games ->
|
||||
games.map { it.table }.fold(BitSet()) { acc, table ->
|
||||
acc.set(table)
|
||||
acc
|
||||
}
|
||||
}
|
||||
}
|
@@ -28,7 +28,7 @@ sealed class BaseSolver(
|
||||
|
||||
companion object {
|
||||
val rand = Random(/* seed from properties - TODO */)
|
||||
val DEBUG_EXPORT_WEIGHT = true
|
||||
val DEBUG_EXPORT_WEIGHT = false
|
||||
var byePlayers: MutableList<Pairable> = mutableListOf()
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ sealed class BaseSolver(
|
||||
|
||||
var result = sorted.flatMap { games(white = it[0], black = it[1]) }
|
||||
// add game for ByePlayer
|
||||
if (chosenByePlayer != ByePlayer) result += Game(id = Store.nextGameId, white = ByePlayer.id, black = chosenByePlayer.id, result = Game.Result.fromSymbol('b'))
|
||||
if (chosenByePlayer != ByePlayer) result += Game(id = Store.nextGameId, table = 0, white = ByePlayer.id, black = chosenByePlayer.id, result = Game.Result.fromSymbol('b'))
|
||||
|
||||
if (DEBUG_EXPORT_WEIGHT) {
|
||||
//println("DUDD debug")
|
||||
@@ -535,7 +535,9 @@ sealed class BaseSolver(
|
||||
|
||||
open fun games(black: Pairable, white: Pairable): List<Game> {
|
||||
// CB TODO team of individuals pairing
|
||||
|
||||
return listOf(Game(id = Store.nextGameId, black = black.id, white = white.id, handicap = pairing.handicap.handicap(black, white), drawnUpDown = white.group-black.group))
|
||||
val usedTables = tables.getOrNull(round - 1) ?: BitSet().also { tables.add(it) }
|
||||
val table = if (black.id == 0 || white.id == 0) 0 else usedTables.nextClearBit(1)
|
||||
usedTables.set(table)
|
||||
return listOf(Game(id = Store.nextGameId, table = table, black = black.id, white = white.id, handicap = pairing.handicap.handicap(white, black), drawnUpDown = white.group-black.group))
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
package org.jeudego.pairgoth.server
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import java.io.IOException
|
@@ -1,4 +1,4 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
package org.jeudego.pairgoth.server
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.api.ApiHandler
|
||||
@@ -23,7 +23,7 @@ import javax.servlet.http.HttpServlet
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
class ApiServlet : HttpServlet() {
|
||||
class ApiServlet: HttpServlet() {
|
||||
|
||||
public override fun doGet(request: HttpServletRequest, response: HttpServletResponse) {
|
||||
doRequest(request, response)
|
||||
@@ -62,7 +62,7 @@ class ApiServlet : HttpServlet() {
|
||||
|
||||
// validate request
|
||||
|
||||
if ("dev" == WebappManager.getProperty("webapp.env")) {
|
||||
if ("dev" == WebappManager.getProperty("env")) {
|
||||
response.addHeader("Access-Control-Allow-Origin", "*")
|
||||
}
|
||||
validateAccept(request);
|
||||
@@ -90,7 +90,7 @@ class ApiServlet : HttpServlet() {
|
||||
"team" -> TeamHandler
|
||||
else -> ApiHandler.badRequest("unknown sub-entity: $subEntity")
|
||||
}
|
||||
"player" -> PlayerHandler
|
||||
// "player" -> PlayerHandler
|
||||
else -> ApiHandler.badRequest("unknown entity: $entity")
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ class ApiServlet : HttpServlet() {
|
||||
cause: Throwable? = null
|
||||
) {
|
||||
try {
|
||||
if (code == 500) {
|
||||
if (code == 500 || response.isCommitted) {
|
||||
logger.error(
|
||||
"Request {} {} gave error {} {}",
|
||||
request.method,
|
||||
@@ -245,7 +245,14 @@ class ApiServlet : HttpServlet() {
|
||||
cause
|
||||
)
|
||||
}
|
||||
response.sendError(code, message)
|
||||
response.status = code
|
||||
if (response.isCommitted) return
|
||||
val errorPayload = Json.Object(
|
||||
"success" to false,
|
||||
"error" to (message ?: "unknown error")
|
||||
)
|
||||
setContentType(response)
|
||||
errorPayload.toString(response.writer)
|
||||
} catch (ioe: IOException) {
|
||||
logger.error("Could not send back error", ioe)
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
package org.jeudego.pairgoth.server
|
||||
|
||||
import info.macias.sse.events.MessageEvent
|
||||
import java.util.concurrent.atomic.AtomicLong
|
@@ -1,4 +1,4 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
package org.jeudego.pairgoth.server
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.util.Colorizer.blue
|
||||
@@ -15,9 +15,11 @@ fun Logger.logRequest(req: HttpServletRequest, logHeaders: Boolean = false) {
|
||||
.append(req.localName)
|
||||
val port = req.localPort
|
||||
if (port != 80) builder.append(':').append(port)
|
||||
/*
|
||||
if (!req.contextPath.isEmpty()) {
|
||||
builder.append(req.contextPath)
|
||||
}
|
||||
*/
|
||||
builder.append(req.requestURI)
|
||||
if (req.method == "GET") {
|
||||
val qs = req.queryString
|
@@ -1,4 +1,4 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
package org.jeudego.pairgoth.server
|
||||
|
||||
import info.macias.sse.EventBroadcast
|
||||
import info.macias.sse.events.MessageEvent
|
@@ -1,4 +1,4 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
package org.jeudego.pairgoth.server
|
||||
|
||||
import com.republicate.mailer.SmtpLoop
|
||||
import org.apache.commons.lang3.tuple.Pair
|
||||
@@ -51,8 +51,7 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
||||
/* ServletContextListener interface */
|
||||
override fun contextInitialized(sce: ServletContextEvent) {
|
||||
context = sce.servletContext
|
||||
logger.info("---------- Starting Pairgoth Server ----------")
|
||||
context.setAttribute("manager", this)
|
||||
logger.info("---------- Starting $WEBAPP_NAME ----------")
|
||||
webappRoot = context.getRealPath("/")
|
||||
try {
|
||||
// load default properties
|
||||
@@ -63,7 +62,8 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
||||
properties[(key as String).removePrefix(PAIRGOTH_PROPERTIES_PREFIX)] = value
|
||||
}
|
||||
|
||||
logger.info("Using profile {}", properties.getProperty("webapp.env"))
|
||||
val env = properties.getProperty("env")
|
||||
logger.info("Using profile $env", )
|
||||
|
||||
// set system user agent string to empty string
|
||||
System.setProperty("http.agent", "")
|
||||
@@ -84,11 +84,15 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
||||
}
|
||||
|
||||
override fun contextDestroyed(sce: ServletContextEvent) {
|
||||
logger.info("---------- Stopping Web Application ----------")
|
||||
logger.info("---------- Stopping $WEBAPP_NAME ----------")
|
||||
|
||||
stopService("smtp");
|
||||
|
||||
val context = sce.servletContext
|
||||
for (service in webServices.keys) stopService(service, true)
|
||||
// ??? DriverManager.deregisterDriver(com.mysql.cj.jdbc.Driver ...);
|
||||
|
||||
logger.info("---------- Stopped $WEBAPP_NAME ----------")
|
||||
}
|
||||
|
||||
/* ServletContextAttributeListener interface */
|
||||
@@ -101,6 +105,7 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
||||
override fun sessionDestroyed(se: HttpSessionEvent) {}
|
||||
|
||||
companion object {
|
||||
const val WEBAPP_NAME = "Pairgoth API Server"
|
||||
const val PAIRGOTH_PROPERTIES_PREFIX = "pairgoth."
|
||||
lateinit var webappRoot: String
|
||||
lateinit var context: ServletContext
|
||||
@@ -111,10 +116,10 @@ class WebappManager : ServletContextListener, ServletContextAttributeListener, H
|
||||
return properties.getProperty(prop)
|
||||
}
|
||||
fun getMandatoryProperty(prop: String): String {
|
||||
return properties.getProperty(prop) ?: throw Error("missing property: ${prop}")
|
||||
return getProperty(prop) ?: throw Error("missing property: ${prop}")
|
||||
}
|
||||
|
||||
val webappURL by lazy { getProperty("webapp.url") }
|
||||
val webappURL by lazy { getProperty("webapp.external.url") }
|
||||
|
||||
private val services = mutableMapOf<String, Pair<Runnable, Thread>>()
|
||||
|
@@ -10,7 +10,7 @@ import org.jeudego.pairgoth.model.fromJson
|
||||
import org.jeudego.pairgoth.model.getID
|
||||
import org.jeudego.pairgoth.model.toFullJson
|
||||
import org.jeudego.pairgoth.model.toID
|
||||
import org.jeudego.pairgoth.model.toJson
|
||||
import java.lang.Integer.max
|
||||
import java.nio.file.Path
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
@@ -64,11 +64,15 @@ class FileStore(pathStr: String): StoreImplementation {
|
||||
}
|
||||
val json = Json.parse(path.resolve(file).readText())?.asObject() ?: throw Error("could not read tournament")
|
||||
val tournament = Tournament.fromJson(json)
|
||||
var maxPlayerId = 0
|
||||
var maxGameId = 0
|
||||
val players = json["players"] as Json.Array? ?: Json.Array()
|
||||
tournament.players.putAll(
|
||||
players.associate {
|
||||
(it as Json.Object).let { player ->
|
||||
Pair(player.getID("id") ?: throw Error("invalid tournament file"), Player.fromJson(player))
|
||||
Pair(player.getID("id") ?: throw Error("invalid tournament file"), Player.fromJson(player)).also {
|
||||
maxPlayerId = max(maxPlayerId, it.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -77,29 +81,41 @@ class FileStore(pathStr: String): StoreImplementation {
|
||||
tournament.teams.putAll(
|
||||
teams.associate {
|
||||
(it as Json.Object).let { team ->
|
||||
Pair(team.getID("id") ?: throw Error("invalid tournament file"), tournament.teamFromJson(team))
|
||||
Pair(team.getID("id") ?: throw Error("invalid tournament file"), tournament.teamFromJson(team)).also {
|
||||
maxPlayerId = max(maxPlayerId, it.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
val games = json["games"] as Json.Array? ?: Json.Array()
|
||||
(1..games.size).forEach { round ->
|
||||
var nextDefaultTable = 1;
|
||||
val roundGames = games[round - 1] as Json.Array
|
||||
tournament.games(round).putAll(
|
||||
games.associate {
|
||||
roundGames.associate {
|
||||
(it as Json.Object).let { game ->
|
||||
Pair(game.getID("id") ?: throw Error("invalid tournament file"), Game.fromJson(game))
|
||||
val fixedGame =
|
||||
if (game.containsKey("t")) game
|
||||
else Json.MutableObject(game).set("t", nextDefaultTable++)
|
||||
Pair(game.getID("id") ?: throw Error("invalid tournament file"), Game.fromJson(fixedGame)).also {
|
||||
maxGameId = max(maxGameId, it.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
_nextPlayerId.set(maxPlayerId + 1)
|
||||
_nextGameId.set(maxGameId + 1)
|
||||
return tournament
|
||||
}
|
||||
|
||||
override fun replaceTournament(tournament: Tournament<*>) {
|
||||
val filename = tournament.filename()
|
||||
val file = path.resolve(filename).toFile()
|
||||
if (!file.exists()) throw Error("File $filename does not exist")
|
||||
file.renameTo(path.resolve(filename + "-${timestamp}").toFile())
|
||||
if (file.exists()) {
|
||||
file.renameTo(path.resolve(filename + "-${timestamp}").toFile())
|
||||
}
|
||||
addTournament(tournament)
|
||||
}
|
||||
|
||||
|
@@ -2,14 +2,14 @@ package org.jeudego.pairgoth.store
|
||||
|
||||
import org.jeudego.pairgoth.model.ID
|
||||
import org.jeudego.pairgoth.model.Tournament
|
||||
import org.jeudego.pairgoth.web.WebappManager
|
||||
import org.jeudego.pairgoth.server.WebappManager
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
private fun createStoreImplementation(): StoreImplementation {
|
||||
return when (val storeProperty = WebappManager.getProperty("store") ?: "memory") {
|
||||
"memory" -> MemoryStore()
|
||||
"file" -> {
|
||||
val filePath = WebappManager.getMandatoryProperty("store.file.path") ?: "."
|
||||
val filePath = WebappManager.getProperty("store.file.path") ?: "."
|
||||
FileStore(filePath)
|
||||
}
|
||||
else -> throw Error("unknown store: $storeProperty")
|
||||
|
@@ -1,6 +1,19 @@
|
||||
# webapp
|
||||
webapp.env = dev
|
||||
webapp.url = http://localhost:8080
|
||||
# environment
|
||||
env = dev
|
||||
|
||||
# webapp connector
|
||||
webapp.protocol = http
|
||||
webapp.interface = localhost
|
||||
webapp.port = 8080
|
||||
webapp.context = /
|
||||
webapp.external.url = http://localhost:8080
|
||||
|
||||
# api connector
|
||||
api.protocol = http
|
||||
api.interface = localhost
|
||||
api.port = 8085
|
||||
api.context = /api
|
||||
api.external.url = http://localhost:8085/api
|
||||
|
||||
# store
|
||||
store = file
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<listener-class>com.republicate.slf4j.impl.ServletContextLoggerListener</listener-class>
|
||||
</listener>
|
||||
<listener>
|
||||
<listener-class>org.jeudego.pairgoth.web.WebappManager</listener-class>
|
||||
<listener-class>org.jeudego.pairgoth.server.WebappManager</listener-class>
|
||||
</listener>
|
||||
|
||||
<!-- filters -->
|
||||
@@ -29,11 +29,11 @@
|
||||
<!-- servlets -->
|
||||
<servlet>
|
||||
<servlet-name>api</servlet-name>
|
||||
<servlet-class>org.jeudego.pairgoth.web.ApiServlet</servlet-class>
|
||||
<servlet-class>org.jeudego.pairgoth.server.ApiServlet</servlet-class>
|
||||
</servlet>
|
||||
<servlet>
|
||||
<servlet-name>sse</servlet-name>
|
||||
<servlet-class>org.jeudego.pairgoth.web.SSEServlet</servlet-class>
|
||||
<servlet-class>org.jeudego.pairgoth.server.SSEServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
<async-supported>true</async-supported>
|
||||
</servlet>
|
||||
|
@@ -180,7 +180,7 @@ class BasicTests: TestBase() {
|
||||
"""[{"id":$aTournamentGameID,"w":$anotherPlayerID,"b":$aPlayerID,"h":0,"r":"?","dd":0}]"""
|
||||
)
|
||||
assertTrue(possibleResults.contains(games.toString()), "pairing differs")
|
||||
games = TestAPI.get("/api/tour/$aTournamentID/res/1").asArray()
|
||||
games = TestAPI.get("/api/tour/$aTournamentID/res/1").asObject().getArray("games")!!
|
||||
assertTrue(possibleResults.contains(games.toString()), "results differs")
|
||||
val empty = TestAPI.get("/api/tour/$aTournamentID/pair/1").asArray()
|
||||
assertEquals("[]", empty.toString(), "no more pairables for round 1")
|
||||
|
@@ -2,9 +2,9 @@ package org.jeudego.pairgoth.test
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.api.ApiHandler
|
||||
import org.jeudego.pairgoth.web.ApiServlet
|
||||
import org.jeudego.pairgoth.web.SSEServlet
|
||||
import org.jeudego.pairgoth.web.WebappManager
|
||||
import org.jeudego.pairgoth.server.ApiServlet
|
||||
import org.jeudego.pairgoth.server.SSEServlet
|
||||
import org.jeudego.pairgoth.server.WebappManager
|
||||
import org.mockito.kotlin.*
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
|
Reference in New Issue
Block a user