Fix tests, move weights.txt out of src
This commit is contained in:
@@ -50,6 +50,9 @@
|
||||
<classpathDependencyExcludes>
|
||||
<classpathDependencyExclude>com.republicate:webapp-slf4j-logger</classpathDependencyExclude>
|
||||
</classpathDependencyExcludes>
|
||||
<systemPropertyVariables>
|
||||
<test.build.dir>${project.build.testOutputDirectory}</test.build.dir>
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
@@ -12,6 +12,8 @@ import org.jgrapht.graph.DefaultWeightedEdge
|
||||
import org.jgrapht.graph.SimpleDirectedWeightedGraph
|
||||
import org.jgrapht.graph.builder.GraphBuilder
|
||||
import java.io.File
|
||||
import java.io.OutputStream
|
||||
import java.io.PrintWriter
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
@@ -28,8 +30,8 @@ sealed class BaseSolver(
|
||||
|
||||
companion object {
|
||||
val rand = Random(/* seed from properties - TODO */)
|
||||
val DEBUG_EXPORT_WEIGHT = false
|
||||
var byePlayers: MutableList<Pairable> = mutableListOf()
|
||||
var weightsLogger: PrintWriter? = null
|
||||
}
|
||||
|
||||
open fun openGothaWeight(p1: Pairable, p2: Pairable) =
|
||||
@@ -53,11 +55,9 @@ sealed class BaseSolver(
|
||||
val WEIGHTS_FILE = "src/test/resources/weights.txt"
|
||||
val dec = DecimalFormat("#.#")
|
||||
|
||||
if (DEBUG_EXPORT_WEIGHT){
|
||||
File(WEIGHTS_FILE).writeText("Round "+round.toString()+"\n")
|
||||
//else File(WEIGHTS_FILE).appendText("Round "+round.toString()+"\n")
|
||||
File(WEIGHTS_FILE).appendText("Costs\n")
|
||||
// println("placement criteria" + placement.criteria.toString())
|
||||
weightsLogger?.apply {
|
||||
this.println("Round $round")
|
||||
this.println("Costs")
|
||||
}
|
||||
|
||||
var chosenByePlayer: Pairable = ByePlayer
|
||||
@@ -84,26 +84,24 @@ sealed class BaseSolver(
|
||||
}
|
||||
|
||||
for (i in nameSortedPairables.indices) {
|
||||
// println(nameSortedPairables[i].nameSeed() + " id="+nameSortedPairables[i].id.toString()+" clasmt="+nameSortedPairables[i].placeInGroup.toString())
|
||||
for (j in i + 1 until nameSortedPairables.size) {
|
||||
val p = nameSortedPairables[i]
|
||||
val q = nameSortedPairables[j]
|
||||
weight(p, q).let { if (it != Double.NaN) builder.addEdge(p, q, it/1e6) }
|
||||
weight(q, p).let { if (it != Double.NaN) builder.addEdge(q, p, it/1e6) }
|
||||
if (DEBUG_EXPORT_WEIGHT)
|
||||
{
|
||||
File(WEIGHTS_FILE).appendText("Player1Name="+p.nameSeed()+"\n")
|
||||
File(WEIGHTS_FILE).appendText("Player2Name="+q.nameSeed()+"\n")
|
||||
File(WEIGHTS_FILE).appendText("baseDuplicateGameCost="+dec.format(pairing.base.avoidDuplicatingGames(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("baseRandomCost="+dec.format(pairing.base.applyRandom(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("baseBWBalanceCost="+dec.format(pairing.base.applyColorBalance(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("mainCategoryCost="+dec.format(pairing.main.avoidMixingCategory(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("mainScoreDiffCost="+dec.format(pairing.main.minimizeScoreDifference(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("mainDUDDCost="+dec.format(pairing.main.applyDUDD(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("mainSeedCost="+dec.format(pairing.main.applySeeding(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("secHandiCost="+dec.format(pairing.handicap.handicap(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("secGeoCost="+dec.format(pairing.geo.apply(p, q))+"\n")
|
||||
File(WEIGHTS_FILE).appendText("totalCost="+dec.format(openGothaWeight(p,q))+"\n")
|
||||
weightsLogger?.apply {
|
||||
this.println("Player1Name=${p.nameSeed()}")
|
||||
this.println("Player2Name=${q.nameSeed()}")
|
||||
this.println("baseDuplicateGameCost=${dec.format(pairing.base.avoidDuplicatingGames(p, q))}")
|
||||
this.println("baseRandomCost=${dec.format(pairing.base.applyRandom(p, q))}")
|
||||
this.println("baseBWBalanceCost=${dec.format(pairing.base.applyColorBalance(p, q))}")
|
||||
this.println("mainCategoryCost=${dec.format(pairing.main.avoidMixingCategory(p, q))}")
|
||||
this.println("mainScoreDiffCost=${dec.format(pairing.main.minimizeScoreDifference(p, q))}")
|
||||
this.println("mainDUDDCost=${dec.format(pairing.main.applyDUDD(p, q))}")
|
||||
this.println("mainSeedCost=${dec.format(pairing.main.applySeeding(p, q))}")
|
||||
this.println("secHandiCost=${dec.format(pairing.handicap.handicap(p, q))}")
|
||||
this.println("secGeoCost=${dec.format(pairing.geo.apply(p, q))}")
|
||||
this.println("totalCost=${dec.format(openGothaWeight(p,q))}")
|
||||
//File(WEIGHTS_FILE).appendText("ByeCost="+dec.format(pairing.base.applyByeWeight(p,q))+"\n")
|
||||
|
||||
}
|
||||
@@ -117,14 +115,11 @@ sealed class BaseSolver(
|
||||
listOf(graph.getEdgeSource(it), graph.getEdgeTarget(it))
|
||||
}.sortedWith(compareBy({ min(it[0].place, it[1].place) }))
|
||||
|
||||
/* println(sorted.size)
|
||||
if (chosenByePlayer != ByePlayer) sorted.add(listOf(chosenByePlayer, ByePlayer))
|
||||
println(sorted.size)*/
|
||||
|
||||
var result = sorted.flatMap { games(white = it[0], black = it[1]) }
|
||||
// add game for ByePlayer
|
||||
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")
|
||||
//println(nameSortedPairables[2].nameSeed() + " " + nameSortedPairables[6].nameSeed())
|
||||
@@ -152,6 +147,7 @@ sealed class BaseSolver(
|
||||
val dec = DecimalFormat("#.#")
|
||||
println("sumOfWeights = " + dec.format(sumOfWeights))
|
||||
}
|
||||
*/
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -242,21 +238,10 @@ sealed class BaseSolver(
|
||||
// TODO check category equality if category are used in SwissCat
|
||||
|
||||
return score
|
||||
}
|
||||
open fun debug(p: Pairable) {
|
||||
|
||||
}
|
||||
open fun MainCritParams.minimizeScoreDifference(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
val scoreRange: Int = groupsCount
|
||||
if (p1.name == "Lefebvre" && p2.name == "Bonjean") {
|
||||
println("p1 ${p1.name} ${p1.group} ${p1.nbW}")
|
||||
debug(p1)
|
||||
|
||||
println("p2 ${p2.name} ${p2.group} ${p2.nbW}")
|
||||
debug(p2)
|
||||
|
||||
}
|
||||
if (scoreRange != 0){
|
||||
val x = abs(p1.group - p2.group).toDouble() / scoreRange.toDouble()
|
||||
score = concavityFunction(x, scoreWeight)
|
||||
@@ -265,7 +250,7 @@ sealed class BaseSolver(
|
||||
return score
|
||||
}
|
||||
|
||||
open fun MainCritParams.applyDUDD(p1: Pairable, p2: Pairable, debug: Boolean =false): Double {
|
||||
open fun MainCritParams.applyDUDD(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
|
||||
// TODO apply Drawn-Up/Drawn-Down if needed
|
||||
@@ -346,6 +331,7 @@ sealed class BaseSolver(
|
||||
score += 4 * duddWeight
|
||||
}
|
||||
|
||||
/*
|
||||
if(debug){
|
||||
println("Names "+upperSP.nameSeed()+" "+upperSP.group+" "+lowerSP.nameSeed()+" "+lowerSP.group)
|
||||
println("DUDD scenario, GroupDiff = "+scenario.toString()+" "+(upperSP.group-lowerSP.group).toString())
|
||||
@@ -354,6 +340,7 @@ sealed class BaseSolver(
|
||||
println("u/lSPplaceingroup = "+upperSP.placeInGroup.first.toString()+" "+lowerSP.placeInGroup.first.toString())
|
||||
println("score = " + score.toString())
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@@ -367,7 +354,7 @@ sealed class BaseSolver(
|
||||
return score
|
||||
}
|
||||
|
||||
fun MainCritParams.applySeeding(p1: Pairable, p2: Pairable, debug: Boolean =false): Double {
|
||||
fun MainCritParams.applySeeding(p1: Pairable, p2: Pairable): Double {
|
||||
var score = 0.0
|
||||
// Apply seeding for players in the same group
|
||||
if (p1.group == p2.group) {
|
||||
@@ -403,6 +390,7 @@ sealed class BaseSolver(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if(debug){
|
||||
println("Names "+p1.nameSeed()+" "+p1.group+" "+p2.nameSeed()+" "+p2.group)
|
||||
println("Seed Sytem = " + currentSeedSystem.toString())
|
||||
@@ -410,6 +398,7 @@ sealed class BaseSolver(
|
||||
println("place in group p1 = "+cla1.toString()+" p2 = "+cla2.toString())
|
||||
println("score = " + Math.round(score).toString())
|
||||
}
|
||||
*/
|
||||
}
|
||||
return Math.round(score).toDouble()
|
||||
}
|
||||
@@ -446,7 +435,6 @@ sealed class BaseSolver(
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
//println("countryRatio="+countryRatio.toString())
|
||||
|
||||
// Same club and club group (TODO club group)
|
||||
var clubRatio = 0.0
|
||||
|
@@ -22,10 +22,6 @@ class MacMahonSolver(round: Int,
|
||||
}
|
||||
}
|
||||
|
||||
override fun debug(p: Pairable) {
|
||||
println("${p.mms} ${p.mmBase} ${p.nbW}")
|
||||
}
|
||||
|
||||
val Pairable.mmBase: Double get() = min(max(rank, mmFloor), mmBar) + mmsZero
|
||||
val Pairable.mms: Double get() = scores[id] ?: 0.0
|
||||
|
||||
|
@@ -176,13 +176,13 @@ class BasicTests: TestBase() {
|
||||
var games = TestAPI.post("/api/tour/$aTournamentID/pair/1", Json.Array("all")).asArray()
|
||||
aTournamentGameID = (games[0] as Json.Object).getInt("id")
|
||||
val possibleResults = setOf(
|
||||
"""[{"id":$aTournamentGameID,"w":$aPlayerID,"b":$anotherPlayerID,"h":0,"r":"?","dd":0}]""",
|
||||
"""[{"id":$aTournamentGameID,"w":$anotherPlayerID,"b":$aPlayerID,"h":0,"r":"?","dd":0}]"""
|
||||
"""[{"id":$aTournamentGameID,"t":1,"w":$aPlayerID,"b":$anotherPlayerID,"h":0,"r":"?","dd":0}]""",
|
||||
"""[{"id":$aTournamentGameID,"t":1,"w":$anotherPlayerID,"b":$aPlayerID,"h":0,"r":"?","dd":0}]"""
|
||||
)
|
||||
assertTrue(possibleResults.contains(games.toString()), "pairing differs")
|
||||
games = TestAPI.get("/api/tour/$aTournamentID/res/1").asObject().getArray("games")!!
|
||||
games = TestAPI.get("/api/tour/$aTournamentID/res/1").asArray()!!
|
||||
assertTrue(possibleResults.contains(games.toString()), "results differs")
|
||||
val empty = TestAPI.get("/api/tour/$aTournamentID/pair/1").asArray()
|
||||
val empty = TestAPI.get("/api/tour/$aTournamentID/pair/1").asObject().getArray("pairables")
|
||||
assertEquals("[]", empty.toString(), "no more pairables for round 1")
|
||||
}
|
||||
|
||||
@@ -192,8 +192,8 @@ class BasicTests: TestBase() {
|
||||
assertTrue(resp.getBoolean("success") == true, "expecting success")
|
||||
val games = TestAPI.get("/api/tour/$aTournamentID/res/1")
|
||||
val possibleResults = setOf(
|
||||
"""[{"id":$aTournamentGameID,"w":$aPlayerID,"b":$anotherPlayerID,"h":0,"r":"b","dd":0}]""",
|
||||
"""[{"id":$aTournamentGameID,"w":$anotherPlayerID,"b":$aPlayerID,"h":0,"r":"b","dd":0}]"""
|
||||
"""[{"id":$aTournamentGameID,"t":1,"w":$aPlayerID,"b":$anotherPlayerID,"h":0,"r":"b","dd":0}]""",
|
||||
"""[{"id":$aTournamentGameID,"t":1,"w":$anotherPlayerID,"b":$aPlayerID,"h":0,"r":"b","dd":0}]"""
|
||||
)
|
||||
assertTrue(possibleResults.contains(games.toString()), "results differ")
|
||||
}
|
||||
|
@@ -4,22 +4,26 @@ import com.republicate.kson.Json
|
||||
import org.jeudego.pairgoth.model.Game
|
||||
import org.jeudego.pairgoth.model.ID
|
||||
import org.jeudego.pairgoth.model.fromJson
|
||||
import org.jeudego.pairgoth.pairing.solver.BaseSolver
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.MethodOrderer.MethodName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.TestMethodOrder
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.nio.charset.StandardCharsets
|
||||
import kotlin.math.abs
|
||||
import kotlin.reflect.typeOf
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@TestMethodOrder(MethodName::class)
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@Disabled("pairings differ")
|
||||
class PairingTests: TestBase() {
|
||||
|
||||
fun compare_weights(file1:String, file2:String):Boolean {
|
||||
|
||||
fun compare_weights(file1: File, file2: File):Boolean {
|
||||
BaseSolver.weightsLogger!!.flush()
|
||||
// Maps to store name pairs and costs
|
||||
val map1 = HashMap<Pair<String, String>, List<Double>>()
|
||||
val map2 = HashMap<Pair<String, String>, List<Double>>()
|
||||
@@ -28,7 +32,7 @@ class PairingTests: TestBase() {
|
||||
for (file in listOf(file1, file2)) {
|
||||
|
||||
// Read lines
|
||||
val lines = getTestFile(file).readLines()
|
||||
val lines = file.readLines()
|
||||
|
||||
// Store headers
|
||||
val header1 = lines[0]
|
||||
@@ -97,9 +101,13 @@ class PairingTests: TestBase() {
|
||||
val gamesPair = mutableSetOf<Pair<ID,ID>>()
|
||||
val openGothaPair = mutableSetOf<Pair<ID,ID>>()
|
||||
for (i in 0 until opengotha.size) {
|
||||
val tmp = Game.fromJson(games.getJson(i)!!.asObject())
|
||||
val tmp = Game.fromJson(games.getJson(i)!!.asObject().let {
|
||||
Json.MutableObject(it).set("t", 0) // hack to fill the table to make fromJson() happy
|
||||
})
|
||||
gamesPair.add(Pair(tmp.white, tmp.black))
|
||||
val tmpOG = Game.fromJson(opengotha.getJson(i)!!.asObject())
|
||||
val tmpOG = Game.fromJson(opengotha.getJson(i)!!.asObject().let {
|
||||
Json.MutableObject(it).set("t", 0) // hack to fill the table to make fromJson() happy
|
||||
})
|
||||
openGothaPair.add(Pair(tmpOG.white, tmpOG.black))
|
||||
}
|
||||
return gamesPair==openGothaPair
|
||||
@@ -173,10 +181,10 @@ class PairingTests: TestBase() {
|
||||
var firstGameID: Int
|
||||
|
||||
for (round in 1..7) {
|
||||
BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
logger.info("games for round $round: {}", games.toString())
|
||||
|
||||
assertTrue(compare_weights("weights.txt", "opengotha/simpleswiss_weights_R$round.txt"), "Not matching opengotha weights for round $round")
|
||||
assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/simpleswiss_weights_R$round.txt")), "Not matching opengotha weights for round $round")
|
||||
assertTrue(compare_games(games, Json.parse(pairings[round - 1])!!.asArray()),"pairings for round $round differ")
|
||||
logger.info("Pairings for round $round match OpenGotha")
|
||||
|
||||
@@ -263,7 +271,7 @@ class PairingTests: TestBase() {
|
||||
|
||||
for (round in 1..7) {
|
||||
//games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array(playersList.filter{it != byePlayerList[round-1]})).asArray()
|
||||
|
||||
BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
if (round in forcedPairingList){
|
||||
// games must be created and then modified by PUT
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
@@ -280,7 +288,7 @@ class PairingTests: TestBase() {
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
logger.info("games for round $round: {}", games.toString())
|
||||
|
||||
assertTrue(compare_weights("weights.txt", "opengotha/notsosimpleswiss_weights_R$round.txt"), "Not matching opengotha weights for round $round")
|
||||
assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/notsosimpleswiss_weights_R$round.txt")), "Not matching opengotha weights for round $round")
|
||||
assertTrue(compare_games(games, Json.parse(pairings[round - 1])!!.asArray()),"pairings for round $round differ")
|
||||
logger.info("Pairings for round $round match OpenGotha")
|
||||
}
|
||||
@@ -349,9 +357,10 @@ class PairingTests: TestBase() {
|
||||
var game: Json
|
||||
|
||||
for (round in 1..5) {
|
||||
BaseSolver.weightsLogger = PrintWriter(FileWriter(getOutputFile("weights.txt")))
|
||||
// games must be created and then modified by PUT
|
||||
games = TestAPI.post("/api/tour/$id/pair/$round", Json.Array("all")).asArray()
|
||||
assertTrue(compare_weights("weights.txt", "opengotha/simplemm/simplemm_weights_R$round.txt"), "Not matching opengotha weights for round $round")
|
||||
assertTrue(compare_weights(getOutputFile("weights.txt"), getTestFile("opengotha/simplemm/simplemm_weights_R$round.txt")), "Not matching opengotha weights for round $round")
|
||||
logger.info("Weights for round $round match OpenGotha")
|
||||
|
||||
forcedGames = Json.parse(pairings[round-1])!!.asArray()
|
||||
|
@@ -21,20 +21,20 @@ class TeamTest {
|
||||
resp = TestAPI.post("/api/tour/$aTeamTournamentID/part", anotherPlayer).asObject()
|
||||
assertTrue(resp.getBoolean("success") == true, "expecting success")
|
||||
val anotherTeamPlayerID = resp.getInt("id") ?: fail("id cannot be null")
|
||||
var arr = TestAPI.get("/api/tour/$aTeamTournamentID/pair/1").asArray()
|
||||
var arr = TestAPI.get("/api/tour/$aTeamTournamentID/pair/1").asObject().getArray("pairables")
|
||||
assertEquals("[]", arr.toString(), "expecting an empty array")
|
||||
resp = TestAPI.post("/api/tour/$aTeamTournamentID/team", Json.parse("""{ "name":"The Buffallos", "players":[$aTeamPlayerID, $anotherTeamPlayerID] }""")?.asObject() ?: fail("no null allowed here")).asObject()
|
||||
assertTrue(resp.getBoolean("success") == true, "expecting success")
|
||||
val aTeamID = resp.getInt("id") ?: error("no null allowed here")
|
||||
resp = TestAPI.get("/api/tour/$aTeamTournamentID/team/$aTeamID").asObject()
|
||||
assertEquals("""{"id":$aTeamID,"name":"The Buffallos","players":[$aTeamPlayerID,$anotherTeamPlayerID]}""", resp.toString(), "expecting team description")
|
||||
arr = TestAPI.get("/api/tour/$aTeamTournamentID/pair/1").asArray()
|
||||
arr = TestAPI.get("/api/tour/$aTeamTournamentID/pair/1").asObject().getArray("pairables")
|
||||
assertEquals("[$aTeamID]", arr.toString(), "expecting a singleton array")
|
||||
// nothing stops us in reusing players in different teams, at least for now...
|
||||
resp = TestAPI.post("/api/tour/$aTeamTournamentID/team", Json.parse("""{ "name":"The Billies", "players":[$aTeamPlayerID, $anotherTeamPlayerID] }""")?.asObject() ?: fail("no null here")).asObject()
|
||||
assertTrue(resp.getBoolean("success") == true, "expecting success")
|
||||
val anotherTeamID = resp.getInt("id") ?: fail("no null here")
|
||||
arr = TestAPI.get("/api/tour/$aTeamTournamentID/pair/1").asArray()
|
||||
arr = TestAPI.get("/api/tour/$aTeamTournamentID/pair/1").asObject().getArray("pairables")
|
||||
assertEquals("[$aTeamID,$anotherTeamID]", arr.toString(), "expecting two pairables")
|
||||
arr = TestAPI.post("/api/tour/$aTeamTournamentID/pair/1", Json.parse("""["all"]""")).asArray()
|
||||
assertTrue(resp.getBoolean("success") == true, "expecting success")
|
||||
|
@@ -82,6 +82,8 @@ object TestAPI {
|
||||
|
||||
// Get a list of resources
|
||||
|
||||
fun getTestResources(path: String) = File("${System.getProperty("user.dir")}/src/test/resources/$path").listFiles()
|
||||
fun getTestResources(path: String) = getTestFile(path).listFiles()
|
||||
|
||||
fun getTestFile(path: String) = File("${System.getProperty("user.dir")}/src/test/resources/$path")
|
||||
fun getTestFile(path: String) = File("${System.getProperty("user.dir")}/src/test/resources/$path")
|
||||
|
||||
fun getOutputFile(path: String) = File("${System.getProperty("test.build.dir")}/$path")
|
1
pom.xml
1
pom.xml
@@ -79,6 +79,7 @@
|
||||
<pairgoth.store>file</pairgoth.store>
|
||||
<pairgoth.store.file.path>tournamentfiles</pairgoth.store.file.path>
|
||||
<pairgoth.auth>none</pairgoth.auth>
|
||||
<pairgoth.auth.sesame></pairgoth.auth.sesame>
|
||||
<pairgoth.smtp.sender></pairgoth.smtp.sender>
|
||||
<pairgoth.smtp.host></pairgoth.smtp.host>
|
||||
<pairgoth.smtp.port>587</pairgoth.smtp.port>
|
||||
|
Reference in New Issue
Block a user