From c2d1e53f3f0e045654cff62f78e9bde1bcc5dfa2 Mon Sep 17 00:00:00 2001 From: Claude Brisson Date: Mon, 2 Oct 2023 09:50:13 +0200 Subject: [PATCH] Fix parameters import/export --- .../org/jeudego/pairgoth/ext/OpenGotha.kt | 98 ++++++++++++++----- .../org/jeudego/pairgoth/util/XmlUtils.kt | 2 +- .../src/main/resources/xsd/opengotha.xsd | 2 +- .../src/test/kotlin/ImportExportTests.kt | 16 ++- 4 files changed, 91 insertions(+), 27 deletions(-) diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/ext/OpenGotha.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/ext/OpenGotha.kt index 0ed46b1..2fb76d2 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/ext/OpenGotha.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/ext/OpenGotha.kt @@ -12,9 +12,28 @@ import java.util.* import javax.xml.datatype.XMLGregorianCalendar private const val MILLISECONDS_PER_DAY = 86400000 -fun XMLGregorianCalendar.toLocalDate() = LocalDate.fromEpochDays((toGregorianCalendar().time.time / MILLISECONDS_PER_DAY).toInt()) +fun XMLGregorianCalendar.toLocalDate() = LocalDate(year, month, day) object OpenGotha { + + private fun parseDrawUpDownMode(str: String) = when (str) { + "BOT" -> MainCritParams.DrawUpDown.BOTTOM + "MID" -> MainCritParams.DrawUpDown.MIDDLE + "TOP" -> MainCritParams.DrawUpDown.TOP + else -> throw Error("Invalid drawUpDown mode: $str") + } + + private fun parseSeedSystem(str: String) = when (str) { + "SPLITANDSLIP" -> MainCritParams.SeedMethod.SPLIT_AND_SLIP + "SPLITANDRANDOM" -> MainCritParams.SeedMethod.SPLIT_AND_RANDOM + "SPLITANDFOLD" -> MainCritParams.SeedMethod.SPLIT_AND_FOLD + else -> throw Error("Invalid seed system: $str") + } + + private fun String.titlecase(locale: Locale = Locale.ROOT) = lowercase(locale).replaceFirstChar { it.titlecase(locale) } + + private fun MainCritParams.SeedMethod.format() = toString().replace("_", "") + fun import(element: Element): Tournament<*> { val context = JAXBContext.newInstance(ObjectFactory::class.java) @@ -28,6 +47,57 @@ object OpenGotha { val placmtParams = ogTournament.tournamentParameterSet.placementParameterSet val pairParams = ogTournament.tournamentParameterSet.pairingParameterSet + val pairgothPairingParams = PairingParams( + base = BaseCritParams( + nx1 = pairParams.paiStandardNX1Factor.toDouble(), + dupWeight = pairParams.paiBaAvoidDuplGame.toDouble(), + random = pairParams.paiBaRandom.toDouble(), + deterministic = pairParams.paiBaDeterministic.toBoolean(), + colorBalanceWeight = pairParams.paiBaBalanceWB.toDouble() + ), + main = MainCritParams( + categoriesWeight = pairParams.paiMaAvoidMixingCategories.toDouble(), + scoreWeight = pairParams.paiMaMinimizeScoreDifference.toDouble(), + drawUpDownWeight = pairParams.paiMaDUDDWeight.toDouble(), + compensateDrawUpDown = pairParams.paiMaCompensateDUDD.toBoolean(), + drawUpDownUpperMode = parseDrawUpDownMode(pairParams.paiMaDUDDUpperMode), + drawUpDownLowerMode = parseDrawUpDownMode(pairParams.paiMaDUDDLowerMode), + seedingWeight = pairParams.paiMaMaximizeSeeding.toDouble(), + lastRoundForSeedSystem1 = pairParams.paiMaLastRoundForSeedSystem1, + seedSystem1 = parseSeedSystem(pairParams.paiMaSeedSystem1), + seedSystem2 = parseSeedSystem(pairParams.paiMaSeedSystem2 ?: "SPLITANDSLIP"), + additionalPlacementCritSystem1 = Criterion.valueOf(pairParams.paiMaAdditionalPlacementCritSystem1.uppercase()), + additionalPlacementCritSystem2 = Criterion.valueOf(pairParams.paiMaAdditionalPlacementCritSystem2.uppercase().replace("NULL", "NONE")) + ), + secondary = SecondaryCritParams( + barThresholdActive = pairParams.paiSeBarThresholdActive.toBoolean(), + rankThreshold = Pairable.parseRank(pairParams.paiSeRankThreshold), + nbWinsThresholdActive = pairParams.paiSeNbWinsThresholdActive.toBoolean(), + defSecCrit = pairParams.paiSeDefSecCrit.toDouble() + ), + geo = GeographicalParams( + avoidSameGeo = pairParams.paiSeAvoidSameGeo.toDouble(), + preferMMSDiffRatherThanSameCountry = pairParams.paiSePreferMMSDiffRatherThanSameCountry, + preferMMSDiffRatherThanSameClubsGroup = 2, + preferMMSDiffRatherThanSameClub = pairParams.paiSePreferMMSDiffRatherThanSameClub + ), + handicap = HandicapParams( + weight = pairParams.paiSeMinimizeHandicap.toDouble(), + useMMS = handParams.hdBasedOnMMS.toBoolean(), + rankThreshold = Pairable.parseRank(pairParams.paiSeRankThreshold), + correction = handParams.hdCorrection, + ceiling = handParams.hdCeiling + ) + ) + + val pairgothPlacementParams = PlacementParams( + crit = placmtParams.placementCriteria.placementCriterion.filter { + it.name != "NULL" + }.map { + Criterion.valueOf(it.name) + }.toTypedArray() + ) + val tournament = StandardTournament( id = Store.nextTournamentId, type = Tournament.Type.INDIVIDUAL, // CB for now, TODO @@ -46,26 +116,8 @@ object OpenGotha { else -> throw Error("missing byoyomi type") }, pairing = when (handParams.hdCeiling) { - 0 -> Swiss( - pairingParams = PairingParams( - - ), - placementParams = PlacementParams( - crit = placmtParams.placementCriteria.placementCriterion.filter { - it.name != "NULL" - }.map { - Criterion.valueOf(it.name) - }.toTypedArray() - ) - ) // TODO - else -> MacMahon( - pairingParams = PairingParams( - - ), - placementParams = PlacementParams( - - ) - ) // TODO + 0 -> Swiss(pairingParams = pairgothPairingParams, placementParams = pairgothPlacementParams) + else -> MacMahon(pairingParams = pairgothPairingParams, placementParams = pairgothPlacementParams) }, rounds = genParams.numberOfRounds ) @@ -123,7 +175,7 @@ object OpenGotha { // method 2 (quick and dirty) is to rely on templating: val xml = """ - + ${tournament.pairables.values.map { player -> player as Player @@ -200,7 +252,7 @@ object OpenGotha { } - + diff --git a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/util/XmlUtils.kt b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/util/XmlUtils.kt index f2de090..f4b5edd 100644 --- a/api-webapp/src/main/kotlin/org/jeudego/pairgoth/util/XmlUtils.kt +++ b/api-webapp/src/main/kotlin/org/jeudego/pairgoth/util/XmlUtils.kt @@ -276,7 +276,7 @@ object XmlUtils { * @param xml xml string * @return The document object */ - fun parse(xml: String): Element = parse(StringReader(xml)) + fun parse(xml: String): Element = parse(StringReader(xml.trim())) /** * Search for nodes using an XPath expression diff --git a/api-webapp/src/main/resources/xsd/opengotha.xsd b/api-webapp/src/main/resources/xsd/opengotha.xsd index aac7b3d..00c8ece 100644 --- a/api-webapp/src/main/resources/xsd/opengotha.xsd +++ b/api-webapp/src/main/resources/xsd/opengotha.xsd @@ -146,7 +146,7 @@ - + diff --git a/api-webapp/src/test/kotlin/ImportExportTests.kt b/api-webapp/src/test/kotlin/ImportExportTests.kt index c18acb5..218e71d 100644 --- a/api-webapp/src/test/kotlin/ImportExportTests.kt +++ b/api-webapp/src/test/kotlin/ImportExportTests.kt @@ -1,12 +1,19 @@ package org.jeudego.pairgoth.test import org.jeudego.pairgoth.ext.OpenGotha +import org.jeudego.pairgoth.model.toJson import org.jeudego.pairgoth.util.XmlUtils import org.junit.jupiter.api.Test import java.nio.charset.StandardCharsets +import kotlin.test.assertEquals class ImportExportTests: TestBase() { + companion object { + val maskIdRegex = Regex("(?<=\"id\" ?: )\\d+") + } + + /* @Test fun `001 test imports`() { getTestResources("opengotha/tournamentfiles/").forEach { file -> @@ -27,18 +34,23 @@ class ImportExportTests: TestBase() { } } + */ + @Test fun `002 test opengotha import export`() { // We import a tournament // Check that after exporting and reimporting we get the same pairgoth tournament object - getTestResources("opengotha").forEach { file -> + getTestResources("opengotha/tournamentfiles").forEach { file -> val resource = file.readText(StandardCharsets.UTF_8) val root_xml = XmlUtils.parse(resource) val tournament = OpenGotha.import(root_xml) + val jsonTournament = tournament.toJson().toPrettyString()!!.replace(maskIdRegex, "0") val exported = OpenGotha.export(tournament) val tournament2 = OpenGotha.import(XmlUtils.parse(exported)) - assert(tournament == tournament2) + val jsonTournament2 = tournament2.toJson().toPrettyString()!!.replace(maskIdRegex, "0") + + assertEquals(jsonTournament, jsonTournament2) } } }