Fix country import; fix layout; bump to 0.3

This commit is contained in:
Claude Brisson
2024-02-21 23:19:52 +01:00
parent 999221de9d
commit 816ef24136
19 changed files with 44 additions and 30 deletions

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.jeudego.pairgoth</groupId> <groupId>org.jeudego.pairgoth</groupId>
<artifactId>engine-parent</artifactId> <artifactId>engine-parent</artifactId>
<version>0.2</version> <version>0.3</version>
</parent> </parent>
<artifactId>api-webapp</artifactId> <artifactId>api-webapp</artifactId>

View File

@@ -124,7 +124,7 @@ object OpenGotha {
shortName = genParams.shortName, shortName = genParams.shortName,
startDate = genParams.beginDate.toLocalDate(), startDate = genParams.beginDate.toLocalDate(),
endDate = genParams.endDate.toLocalDate(), endDate = genParams.endDate.toLocalDate(),
country = "FR", // no country in opengotha format country = "fr", // no country in opengotha format
location = genParams.location, location = genParams.location,
online = genParams.isBInternet ?: false, online = genParams.isBInternet ?: false,
timeSystem = when (genParams.complementaryTimeSystem) { timeSystem = when (genParams.complementaryTimeSystem) {

View File

@@ -21,9 +21,10 @@ import kotlin.io.path.useDirectoryEntries
private const val LEFT_PAD = 6 // left padding of IDs with '0' in filename private const val LEFT_PAD = 6 // left padding of IDs with '0' in filename
private fun Tournament<*>.filename() = "${id.toString().padStart(LEFT_PAD, '0')}-${shortName}.tour" private fun Tournament<*>.filename() = "${id.toString().padStart(LEFT_PAD, '0')}-${shortName}.tour"
class FileStore(pathStr: String): StoreImplementation { class FileStore(pathStr: String): IStore {
companion object { companion object {
private val filenameRegex = Regex("^(\\d+)-(.*)\\.tour$") private val filenameRegex = Regex("^(\\d+)-(.*)\\.tour$")
private val displayFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
private val timestampFormat: DateFormat = SimpleDateFormat("yyyyMMddHHmmss") private val timestampFormat: DateFormat = SimpleDateFormat("yyyyMMddHHmmss")
private val timestamp: String get() = timestampFormat.format(Date()) private val timestamp: String get() = timestampFormat.format(Date())
} }
@@ -37,11 +38,17 @@ class FileStore(pathStr: String): StoreImplementation {
_nextTournamentId.set(getTournaments().keys.maxOrNull() ?: 0.toID()) _nextTournamentId.set(getTournaments().keys.maxOrNull() ?: 0.toID())
} }
override fun getTournaments(): Map<ID, String> {
private fun lastModified(path: Path) = displayFormat.format(Date(path.toFile().lastModified()))
override fun getTournaments(): Map<ID, Map<String, String>> {
return path.useDirectoryEntries("*.tour") { entries -> return path.useDirectoryEntries("*.tour") { entries ->
entries.mapNotNull { entry -> entries.mapNotNull { entry ->
val match = filenameRegex.matchEntire(entry.fileName.toString()) val match = filenameRegex.matchEntire(entry.fileName.toString())
match?.let { Pair(it.groupValues[1].toID(), it.groupValues[2]) } match?.let { Pair(it.groupValues[1].toID(), mapOf(
"name" to it.groupValues[2],
"lastModified" to lastModified(entry))
) }
}.sortedBy { it.first }.toMap() }.sortedBy { it.first }.toMap()
} }
} }

View File

@@ -8,13 +8,13 @@ internal val _nextTournamentId = AtomicInteger()
internal val _nextPlayerId = AtomicInteger() internal val _nextPlayerId = AtomicInteger()
internal val _nextGameId = AtomicInteger() internal val _nextGameId = AtomicInteger()
interface StoreImplementation { interface IStore {
val nextTournamentId get() = _nextTournamentId.incrementAndGet() val nextTournamentId get() = _nextTournamentId.incrementAndGet()
val nextPlayerId get() = _nextPlayerId.incrementAndGet() val nextPlayerId get() = _nextPlayerId.incrementAndGet()
val nextGameId get() = _nextGameId.incrementAndGet() val nextGameId get() = _nextGameId.incrementAndGet()
fun getTournaments(): Map<ID, String> fun getTournaments(): Map<ID, Map<String, String>>
fun addTournament(tournament: Tournament<*>) fun addTournament(tournament: Tournament<*>)
fun getTournament(id: ID): Tournament<*>? fun getTournament(id: ID): Tournament<*>?
fun replaceTournament(tournament: Tournament<*>) fun replaceTournament(tournament: Tournament<*>)

View File

@@ -3,10 +3,12 @@ package org.jeudego.pairgoth.store
import org.jeudego.pairgoth.model.ID import org.jeudego.pairgoth.model.ID
import org.jeudego.pairgoth.model.Tournament import org.jeudego.pairgoth.model.Tournament
class MemoryStore: StoreImplementation { class MemoryStore: IStore {
private val tournaments = mutableMapOf<ID, Tournament<*>>() private val tournaments = mutableMapOf<ID, Tournament<*>>()
override fun getTournaments(): Map<ID, String> = tournaments.mapValues { it.value.shortName } override fun getTournaments(): Map<ID, Map<String, String>> = tournaments.mapValues {
mapOf("name" to it.value.shortName)
}
override fun addTournament(tournament: Tournament<*>) { override fun addTournament(tournament: Tournament<*>) {
if (tournaments.containsKey(tournament.id)) throw Error("tournament id #${tournament.id} already exists") if (tournaments.containsKey(tournament.id)) throw Error("tournament id #${tournament.id} already exists")

View File

@@ -1,11 +1,8 @@
package org.jeudego.pairgoth.store package org.jeudego.pairgoth.store
import org.jeudego.pairgoth.model.ID
import org.jeudego.pairgoth.model.Tournament
import org.jeudego.pairgoth.server.WebappManager import org.jeudego.pairgoth.server.WebappManager
import java.util.concurrent.atomic.AtomicInteger
private fun createStoreImplementation(): StoreImplementation { private fun createStoreImplementation(): IStore {
return when (val storeProperty = WebappManager.getProperty("store") ?: "memory") { return when (val storeProperty = WebappManager.getProperty("store") ?: "memory") {
"memory" -> MemoryStore() "memory" -> MemoryStore()
"file" -> { "file" -> {
@@ -16,4 +13,4 @@ private fun createStoreImplementation(): StoreImplementation {
} }
} }
object Store: StoreImplementation by createStoreImplementation() object Store: IStore by createStoreImplementation()

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>org.jeudego.pairgoth</groupId> <groupId>org.jeudego.pairgoth</groupId>
<artifactId>engine-parent</artifactId> <artifactId>engine-parent</artifactId>
<version>0.2</version> <version>0.3</version>
</parent> </parent>
<artifactId>application</artifactId> <artifactId>application</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>

View File

@@ -5,7 +5,7 @@
<groupId>org.jeudego.pairgoth</groupId> <groupId>org.jeudego.pairgoth</groupId>
<artifactId>engine-parent</artifactId> <artifactId>engine-parent</artifactId>
<version>0.2</version> <version>0.3</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<!-- CB: Temporary add my repository, while waiting for SSE Java server module author to incorporate my PR or for me to fork it --> <!-- CB: Temporary add my repository, while waiting for SSE Java server module author to incorporate my PR or for me to fork it -->

View File

@@ -7,7 +7,7 @@
<parent> <parent>
<groupId>org.jeudego.pairgoth</groupId> <groupId>org.jeudego.pairgoth</groupId>
<artifactId>engine-parent</artifactId> <artifactId>engine-parent</artifactId>
<version>0.2</version> <version>0.3</version>
</parent> </parent>
<artifactId>view-webapp</artifactId> <artifactId>view-webapp</artifactId>

View File

@@ -48,7 +48,7 @@ abstract class OAuthHelper {
protected abstract fun getUserInfosURL(accessToken: String): Pair<String, List<NameValuePair>> protected abstract fun getUserInfosURL(accessToken: String): Pair<String, List<NameValuePair>>
@Throws(IOException::class) @Throws(IOException::class)
fun getUserInfos(accessToken: String): Json { fun getUserInfos(accessToken: String): Json.Object {
val (url, params) = getUserInfosURL(accessToken) val (url, params) = getUserInfosURL(accessToken)
return JsonApiClient.get(url, *params.toTypedArray()).asObject() return JsonApiClient.get(url, *params.toTypedArray()).asObject()
} }

View File

@@ -21,7 +21,7 @@ object EGFRatingsHandler: RatingsHandler(RatingsManager.Ratings.EGF) {
player["origin"] = "EGF" player["origin"] = "EGF"
// override rank with rating equivalent // override rank with rating equivalent
player["rating"]?.toString()?.toIntOrNull()?.let { rating -> player["rating"]?.toString()?.toIntOrNull()?.let { rating ->
player["rank"] = ((rating - 2050)/100).let { if (rating < 0) "${-it+1}k" else "${it+1}d" } player["rank"] = ((rating - 2050)/100).let { if (it < 0) "${-it+1}k" else "${it+1}d" }
} }
} }
} }

View File

@@ -10,7 +10,7 @@ object CredentialsChecker {
private const val CREDENTIALS_DB = "pairgoth.db" private const val CREDENTIALS_DB = "pairgoth.db"
private val hasher = MessageDigest.getInstance("SHA-256") private val hasher = MessageDigest.getInstance("SHA-256")
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
fun check(email: String, password: String): String? { fun check(email: String, password: String): Json.Object? {
initDatabase() initDatabase()
val sha256 = hasher.digest(password.toByteArray(StandardCharsets.UTF_8)).toHexString() val sha256 = hasher.digest(password.toByteArray(StandardCharsets.UTF_8)).toHexString()
DriverManager.getConnection("jdbc:sqlite:$CREDENTIALS_DB").use { conn -> DriverManager.getConnection("jdbc:sqlite:$CREDENTIALS_DB").use { conn ->
@@ -19,7 +19,7 @@ object CredentialsChecker {
setString(1, email) setString(1, email)
setString(2, password) setString(2, password)
}.executeQuery() }.executeQuery()
return if (rs.next()) email else null return if (rs.next()) Json.Object("email" to email) else null
} }
} }

View File

@@ -32,12 +32,12 @@ class LoginServlet: HttpServlet() {
} }
} }
fun checkSesame(payload: Json.Object): Boolean? { fun checkSesame(payload: Json.Object): Json.Object? {
val expected = WebappManager.getProperty("auth.sesame") ?: throw Error("sesame wrongly configured") val expected = WebappManager.getProperty("auth.sesame") ?: throw Error("sesame wrongly configured")
return if (payload.getString("sesame")?.equals(expected) == true) true else null return if (payload.getString("sesame")?.equals(expected) == true) Json.Object("logged" to true) else null
} }
fun checkLoginPass(payload: Json.Object): String? { fun checkLoginPass(payload: Json.Object): Json.Object? {
return CredentialsChecker.check( return CredentialsChecker.check(
payload.getString("email") ?: throw Error("Missing login field"), payload.getString("email") ?: throw Error("Missing login field"),
payload.getString("password") ?: throw Error("missing password field")) payload.getString("password") ?: throw Error("missing password field"))

View File

@@ -77,6 +77,10 @@
/* align-items: center; */ /* align-items: center; */
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
> .section {
margin-left: auto;
margin-right: auto;
}
} }
} }

View File

@@ -3,7 +3,7 @@
, allowing you to tweak it in any possible way. Be sure to contribute back your enhancements! , vous permettant de complètement le modifier. Noubliez pas de partager vos améliorations ! , allowing you to tweak it in any possible way. Be sure to contribute back your enhancements! , vous permettant de complètement le modifier. Noubliez pas de partager vos améliorations !
, the well known pairing system software developed by , le logiciel dappariement très connu développé par , the well known pairing system software developed by , le logiciel dappariement très connu développé par
, your Go Pairing Engine! , votre logiciel dappariement de Go ! , your Go Pairing Engine! , votre logiciel dappariement de Go !
.ui.form input[type=checkbox][name=online] { vertical-align: initial; } , last modified , dernière modification :
1st round seeding méthode 1è ronde 1st round seeding méthode 1è ronde
: If you prefer convenience, you can simply use the : Si vous préférez la commodité, vous pouvez simplement utiliser linstance : If you prefer convenience, you can simply use the : Si vous préférez la commodité, vous pouvez simplement utiliser linstance
: This mode allows you to run : Ce mode vous permet de lancer : This mode allows you to run : Ce mode vous permet de lancer
@@ -37,6 +37,7 @@ Export Exporter
Export tournament Exporter le tournoi Export tournament Exporter le tournoi
Family name Nom de famille Family name Nom de famille
Filter Filtrer Filter Filtrer
Filter... Filtrer…
First name Prénom First name Prénom
Fischer timing Cadence Fischer Fischer timing Cadence Fischer
French rules Règles françaises French rules Règles françaises

View File

@@ -23,9 +23,9 @@
</script> </script>
#else #else
#foreach($tour in $files.entrySet()) #foreach($tour in $files.entrySet())
<a href="tour?id=${tour.key}" class="ui open basic secondary white icon floating button"> <a href="tour?id=${tour.key}" class="ui open basic secondary white icon floating button" title="id $tour.key, last modified $tour.value.lastModified">
<i class="fa fa-folder-open-o"></i> <i class="fa fa-folder-open-o"></i>
$tour.value $tour.value.name
</a> </a>
#end #end
#end #end

View File

@@ -40,6 +40,7 @@ function search(needle) {
store('searchFormState', searchFormState); store('searchFormState', searchFormState);
api.postJson('search', search) api.postJson('search', search)
.then(result => { .then(result => {
console.log(result)
if (Array.isArray(result)) { if (Array.isArray(result)) {
searchResult = result searchResult = result
let html = resultTemplate.render(result); let html = resultTemplate.render(result);

View File

@@ -4,7 +4,7 @@
<div id="reg-view"> <div id="reg-view">
<div id="list-header"> <div id="list-header">
<div id="filter-box" class="ui icon input"> <div id="filter-box" class="ui icon input">
<input type="text" id="filter" placeholder="Search..."/> <input type="text" id="filter" placeholder="Filter..."/>
<i class="circular times link icon"></i> <i class="circular times link icon"></i>
</div> </div>
<div> <div>
@@ -73,6 +73,7 @@
<input type="hidden" name="id"/> <input type="hidden" name="id"/>
<div class="popup-content"> <div class="popup-content">
<div id="search-form" class="five stackable fields"> <div id="search-form" class="five stackable fields">
#if($tour.country)
<div class="two wide field"> <div class="two wide field">
<div class="toggle"> <div class="toggle">
<input id="countryFilter" name="countryFilter" type="checkbox" value="$tour.country"/> <input id="countryFilter" name="countryFilter" type="checkbox" value="$tour.country"/>
@@ -82,6 +83,7 @@
<label>$tour.country.toUpperCase()</label> <label>$tour.country.toUpperCase()</label>
</div> </div>
</div> </div>
#end
<div class="ten wide field"> <div class="ten wide field">
<div class="ui icon input"> <div class="ui icon input">
<input id="needle" name="needle" type="text" placeholder="Search..."> <input id="needle" name="needle" type="text" placeholder="Search...">

View File

@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>org.jeudego.pairgoth</groupId> <groupId>org.jeudego.pairgoth</groupId>
<artifactId>engine-parent</artifactId> <artifactId>engine-parent</artifactId>
<version>0.2</version> <version>0.3</version>
</parent> </parent>
<artifactId>webserver</artifactId> <artifactId>webserver</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>