Example tournaments
This commit is contained in:
@@ -235,8 +235,18 @@ fun Tournament.Companion.fromJson(json: Json.Object, default: Tournament<*>? = n
|
||||
rounds = json.getInt("rounds") ?: default?.rounds ?: badRequest("missing rounds"),
|
||||
pairing = json.getObject("pairing")?.let { Pairing.fromJson(it, default?.pairing) } ?: default?.pairing ?: badRequest("missing pairing")
|
||||
)
|
||||
json["pairables"]?.let { pairables ->
|
||||
|
||||
(json["players"] as Json.Array?)?.forEach { obj ->
|
||||
val pairable = obj as Json.Object
|
||||
tournament.players[pairable.getID("id")!!] = Player.fromJson(pairable)
|
||||
}
|
||||
(json["games"] as Json.Array?)?.forEachIndexed { i, arr ->
|
||||
val round = i + 1
|
||||
val tournamentGames = tournament.games(round)
|
||||
val games = arr as Json.Array
|
||||
games.forEach { obj ->
|
||||
val game = obj as Json.Object
|
||||
tournamentGames[game.getID("id")!!] = Game.fromJson(game)
|
||||
}
|
||||
}
|
||||
return tournament
|
||||
}
|
||||
|
@@ -20,7 +20,8 @@ object Upload {
|
||||
// Check that we have a file upload request
|
||||
val isMultipart: Boolean = ServletFileUpload.isMultipartContent(request)
|
||||
if (!isMultipart) {
|
||||
throw IOException("multipart content expected")
|
||||
logger.warn("multipart content expected")
|
||||
return listOf()
|
||||
}
|
||||
val files = mutableListOf<Pair<String, ByteArray>>()
|
||||
|
||||
|
@@ -1,6 +1,12 @@
|
||||
package org.jeudego.pairgoth.view
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.ExperimentalPathApi
|
||||
import kotlin.io.path.walk
|
||||
|
||||
|
||||
/**
|
||||
* Generic utilities
|
||||
@@ -71,4 +77,21 @@ class PairgothTool {
|
||||
games.filter {
|
||||
it.getInt("b")!! != 0 && it.getInt("w")!! != 0
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPathApi::class)
|
||||
fun getExampleTournaments(): List<String> {
|
||||
val classLoader: ClassLoader = PairgothTool::class.java.classLoader
|
||||
val examplesPath = Paths.get(classLoader.getResource(EXAMPLES_DIRECTORY).toURI())
|
||||
return examplesPath.walk().filter(Files::isRegularFile).map { it.fileName.toString().removeSuffix(".tour") }.sorted().toList()
|
||||
}
|
||||
|
||||
fun getExampleTournament(name: String): Json.Object {
|
||||
val classLoader: ClassLoader = PairgothTool::class.java.classLoader
|
||||
return Json.parse(classLoader.getResource("$EXAMPLES_DIRECTORY/$name.tour").readText())?.asObject()
|
||||
?: throw Error("wrong resource file")
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXAMPLES_DIRECTORY = "examples"
|
||||
}
|
||||
}
|
@@ -1,7 +1,9 @@
|
||||
package org.jeudego.pairgoth.web
|
||||
|
||||
import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.util.Upload
|
||||
import org.jeudego.pairgoth.view.ApiTool
|
||||
import org.jeudego.pairgoth.view.PairgothTool.Companion.EXAMPLES_DIRECTORY
|
||||
import java.nio.charset.StandardCharsets
|
||||
import javax.servlet.http.HttpServlet
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
@@ -11,11 +13,28 @@ class ImportServlet: HttpServlet() {
|
||||
|
||||
override fun doPost(req: HttpServletRequest, resp: HttpServletResponse) {
|
||||
val api = ApiTool().apply { setRequest(req) }
|
||||
val example = req.getParameter("example") as String?
|
||||
if (example != null) uploadExample(req, resp)
|
||||
else {
|
||||
val uploads = Upload.handleFileUpload(req)
|
||||
if (uploads.size != 1) resp.sendError(HttpServletResponse.SC_BAD_REQUEST)
|
||||
else {
|
||||
val xml = uploads.first().second.toString(StandardCharsets.UTF_8)
|
||||
val apiResp = api.post("tour", xml)
|
||||
val name = uploads.first().first
|
||||
val bytes = uploads.first().second
|
||||
var apiResp: Json? = null
|
||||
if (name.endsWith(".tour")) {
|
||||
val json = Json.parse(bytes.toString(StandardCharsets.UTF_8))
|
||||
if (json == null || !json.isObject) {
|
||||
resp.sendError(HttpServletResponse.SC_BAD_REQUEST)
|
||||
} else {
|
||||
apiResp = api.post("tour", json.asObject())
|
||||
}
|
||||
}
|
||||
else { // xml ?
|
||||
val xml = bytes.toString(StandardCharsets.UTF_8)
|
||||
apiResp = api.post("tour", xml)
|
||||
}
|
||||
if (apiResp != null) {
|
||||
if (apiResp.isObject && apiResp.asObject().getBoolean("success") == true) {
|
||||
resp.contentType = "application/json; charset=UTF-8"
|
||||
resp.writer.println(apiResp.toString())
|
||||
@@ -25,3 +44,25 @@ class ImportServlet: HttpServlet() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun uploadExample(request: HttpServletRequest, response: HttpServletResponse) {
|
||||
val name = request.getParameter("example")
|
||||
val classLoader = ImportServlet::class.java.classLoader
|
||||
val example = classLoader.getResource("$EXAMPLES_DIRECTORY/$name.tour")?.readText()?.let {
|
||||
Json.parse(it)
|
||||
}
|
||||
if (example == null || !example.isObject) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST)
|
||||
} else {
|
||||
val api = ApiTool().apply { setRequest(request) }
|
||||
val apiResp = api.post("tour", example)
|
||||
if (apiResp.isObject && apiResp.asObject().getBoolean("success") == true) {
|
||||
response.contentType = "application/json; charset=UTF-8"
|
||||
response.writer.println(apiResp.toString())
|
||||
} else {
|
||||
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
1494
view-webapp/src/main/resources/examples/20180907-marseille.tour
Normal file
1494
view-webapp/src/main/resources/examples/20180907-marseille.tour
Normal file
File diff suppressed because it is too large
Load Diff
5790
view-webapp/src/main/resources/examples/20210403-paris.tour
Normal file
5790
view-webapp/src/main/resources/examples/20210403-paris.tour
Normal file
File diff suppressed because it is too large
Load Diff
4844
view-webapp/src/main/resources/examples/20230127-grenoble.tour
Normal file
4844
view-webapp/src/main/resources/examples/20230127-grenoble.tour
Normal file
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,8 @@ Cancel Annuler
|
||||
Change Mettre à jour
|
||||
Chinese rules Règles chinoises
|
||||
Choose format Choix du format
|
||||
Clone Dupliquer
|
||||
Clone example tournament Dupliquer un tournoi d'exemple
|
||||
Close Fermer
|
||||
Club Club
|
||||
Compile from the sources Compiler depuis les sources
|
||||
@@ -34,6 +36,7 @@ Download the standalone web interface module which suits your need, then follow
|
||||
Edit Éditer
|
||||
Encoding Encodage
|
||||
Enter the magic word Entrer le mot magique
|
||||
Example tournament Tournoi
|
||||
Export Exporter
|
||||
Export tournament Exporter le tournoi
|
||||
Family name Nom de famille
|
||||
|
@@ -7,6 +7,10 @@
|
||||
<i class="fa fa-upload"></i>
|
||||
Import tournament
|
||||
</a>
|
||||
<a id="example-tournament" class="ui green icon floating button">
|
||||
<i class="fa fa-copy"></i>
|
||||
Clone example tournament
|
||||
</a>
|
||||
</div>
|
||||
<div class="tournaments section">
|
||||
#set($files = $api.get('tour'))
|
||||
@@ -52,6 +56,33 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div id="clone-popup" class="popup">
|
||||
<div class="popup-body">
|
||||
<form id="clone-form" class="ui form">
|
||||
<div class="popup-content">
|
||||
<div class="field">
|
||||
<label>Example tournament</label>
|
||||
<select id="exampleTournamentName">
|
||||
<option value=""></option>
|
||||
#foreach($tour in $utils.exampleTournaments)
|
||||
<option value="$tour">$tour</option>
|
||||
#end
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="popup-footer">
|
||||
<button id="cancel-clone" type="button" class="ui gray right labeled icon floating close button">
|
||||
<i class="times icon"></i>
|
||||
Cancel
|
||||
</button>
|
||||
<button id="clone" type="button" class="ui green right labeled icon floating button">
|
||||
<i class="plus icon"></i>
|
||||
Clone
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
// #[[
|
||||
function doImport() {
|
||||
@@ -75,6 +106,26 @@
|
||||
});
|
||||
}
|
||||
|
||||
function doClone(name) {
|
||||
fetch(`/api/import?example=${name}`, {
|
||||
method: 'POST',
|
||||
body: {}
|
||||
}).then(resp => {
|
||||
if (resp.ok) return resp.json();
|
||||
else throw resp;
|
||||
}).then(json => {
|
||||
if (json.success) {
|
||||
console.log(`/tour?id=${json.id}`)
|
||||
document.location.href = `/tour?id=${json.id}`
|
||||
} else {
|
||||
showError(json.error || 'unknown error')
|
||||
}
|
||||
}).catch(err => {
|
||||
error(err);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
onLoad(()=>{
|
||||
$('#import-tournament').on('click', e => {
|
||||
modal('import-popup');
|
||||
@@ -88,6 +139,15 @@
|
||||
} else showError('no file choosen');
|
||||
close_modal();
|
||||
});
|
||||
$('#example-tournament').on('click', e => {
|
||||
modal('clone-popup');
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
$('#clone').on('click', e => {
|
||||
let example = $('#exampleTournamentName')[0].value;
|
||||
doClone(example);
|
||||
});
|
||||
});
|
||||
// ]]#
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user