Skip to content

Commit

Permalink
Support cms
Browse files Browse the repository at this point in the history
  • Loading branch information
kunyavskiy committed Aug 29, 2023
1 parent 19f22f3 commit 17dbcb6
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 0 deletions.
6 changes: 6 additions & 0 deletions config/_examples/_cms/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"type": "cms",
"activeContest": "ceoi22_5f2",
"otherContests": ["ceoi22_5f1"],
"url": "https://ceoi.hsin.hr/ranking/"
}
54 changes: 54 additions & 0 deletions schemas/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,60 @@
"url"
]
},
{
"type": "object",
"properties": {
"type": {
"const": "cms"
},
"url": {
"type": "string"
},
"activeContest": {
"type": "string"
},
"otherContests": {
"type": "array",
"items": {
"type": "string"
}
},
"network": {
"type": "object",
"properties": {
"allowUnsecureConnections": {
"type": "boolean"
}
},
"additionalProperties": false,
"required": [
]
},
"emulation": {
"type": "object",
"properties": {
"speed": {
"type": "number"
},
"startTime": {
"type": "string"
}
},
"additionalProperties": false,
"required": [
"speed",
"startTime"
]
}
},
"additionalProperties": false,
"required": [
"type",
"url",
"activeContest",
"otherContests"
]
},
{
"type": "object",
"properties": {
Expand Down
116 changes: 116 additions & 0 deletions src/cds/src/main/kotlin/org/icpclive/cds/cms/CmsDataSoruce.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package org.icpclive.cds.cms

import org.icpclive.api.*
import org.icpclive.cds.cms.model.*
import org.icpclive.cds.common.ContestParseResult
import org.icpclive.cds.common.FullReloadContestDataSource
import org.icpclive.cds.common.jsonLoader
import org.icpclive.cds.settings.CmsSettings
import org.icpclive.util.Enumerator
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

internal class CmsDataSource(val settings: CmsSettings) : FullReloadContestDataSource(5.seconds) {
private val contestsLoader = jsonLoader<Map<String, Contest>>(settings.network, null) { "${settings.url}/contests/" }
private val tasksLoader = jsonLoader<Map<String, Task>>(settings.network, null) { "${settings.url}/tasks/"}
private val teamsLoader = jsonLoader<Map<String, Team>>(settings.network, null) { "${settings.url}/teams/"}
private val usersLoader = jsonLoader<Map<String, User>>(settings.network, null) { "${settings.url}/users/"}
private val submissionsLoader = jsonLoader<Map<String, Submission>>(settings.network, null) { "${settings.url}/submissions/"}
private val subchangesLoader = jsonLoader<Map<String, Subchange>>(settings.network, null) { "${settings.url}/subchanges/"}
private val problemId = Enumerator<String>()
private val teamId = Enumerator<String>()
private val submissionId = Enumerator<String>()

override suspend fun loadOnce(): ContestParseResult {
val contests = contestsLoader.load()
val mainContest = contests[settings.activeContest] ?: error("No data for contest ${settings.activeContest}")
val finishedContestsProblems = mutableSetOf<String>()
val runningContestProblems = mutableSetOf<String>()
val problems = buildList {
val problems = tasksLoader.load().entries.groupBy { it.value.contest }.mapValues {
it.value.map { (k, v) ->
ProblemInfo(
letter = v.short_name,
name = v.name,
id = problemId[k],
contestSystemId = k,
ordinal = 0,
scoreMergeMode = when (v.score_mode) {
ScoreMode.max -> ScoreMergeMode.MAX_TOTAL
ScoreMode.max_subtask -> ScoreMergeMode.MAX_PER_GROUP
},
minScore = 0.0,
maxScore = v.max_score
)
}
}
for (other in settings.otherContests) {
for (p in problems[other] ?: emptyList()) {
add(p.copy(ordinal = size))
finishedContestsProblems.add(p.contestSystemId)
}
}
for (p in problems[settings.activeContest] ?: emptyList()) {
add(p.copy(ordinal = size))
runningContestProblems.add(p.contestSystemId)
}
}
val organizations = teamsLoader.load().map { (k, v) ->
OrganizationInfo(
cdsId = k,
displayName = v.name,
fullName = v.name
)
}
val teams = usersLoader.load().map {(k, v) ->
TeamInfo(
id = teamId[k],
fullName = "[${v.team}] ${v.f_name} ${v.l_name}",
displayName = "${v.f_name} ${v.l_name}",
contestSystemId = k,
groups = emptyList(),
hashTag = null,
medias = emptyMap(),
isHidden = false,
isOutOfContest = false,
organizationId = v.team,
customFields = mapOf(
"country" to v.team,
"first_name" to v.f_name,
"last_name" to v.l_name
)
)
}
val info = ContestInfo(
name = mainContest.name,
status = ContestStatus.byCurrentTime(mainContest.begin, mainContest.end - mainContest.begin),
resultType = ContestResultType.IOI,
startTime = mainContest.begin,
contestLength = mainContest.end - mainContest.begin,
freezeTime = mainContest.end - mainContest.begin,
problemList = problems,
teamList = teams,
groupList = emptyList(),
organizationList = organizations,
penaltyRoundingMode = PenaltyRoundingMode.ZERO,
)
val submissions = submissionsLoader.load().mapNotNull { (k, v) ->
if (v.task !in runningContestProblems && v.task !in finishedContestsProblems) {
return@mapNotNull null
}
RunInfo(
id = submissionId[k],
result = null,
percentage = 0.0,
problemId = problemId[v.task],
teamId = teamId[v.user],
time = if (v.task in runningContestProblems) v.time - mainContest.begin else Duration.ZERO
)
}.associateBy { it.id }.toMutableMap()
subchangesLoader.load().entries.sortedBy { it.value.time }.forEach {(_, it) ->
val r = submissions[submissionId[it.submission]] ?: return@forEach
submissions[r.id] = r.copy(result = IOIRunResult(it.extra.map { it.toDouble() }))
}
return ContestParseResult(info, submissions.values.sortedBy { it.id }, emptyList())
}
}
14 changes: 14 additions & 0 deletions src/cds/src/main/kotlin/org/icpclive/cds/cms/model/Contest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.icpclive.cds.cms.model

import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable
import org.icpclive.util.UnixSecondsSerializer

@Serializable
data class Contest(
val name: String,
@Serializable(with = UnixSecondsSerializer::class)
val begin: Instant,
@Serializable(with = UnixSecondsSerializer::class)
val end: Instant,
)
11 changes: 11 additions & 0 deletions src/cds/src/main/kotlin/org/icpclive/cds/cms/model/Subchange.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.icpclive.cds.cms.model

import kotlinx.serialization.Serializable

@Serializable
class Subchange(
val score: Double,
val submission: String,
val extra: List<String>,
val time: Int
)
13 changes: 13 additions & 0 deletions src/cds/src/main/kotlin/org/icpclive/cds/cms/model/Submission.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.icpclive.cds.cms.model

import kotlinx.datetime.Instant
import kotlinx.serialization.Serializable
import org.icpclive.util.UnixSecondsSerializer

@Serializable
class Submission(
val user: String,
val task: String,
@Serializable(with = UnixSecondsSerializer::class)
val time: Instant
)
20 changes: 20 additions & 0 deletions src/cds/src/main/kotlin/org/icpclive/cds/cms/model/Task.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.icpclive.cds.cms.model

import kotlinx.serialization.Serializable

enum class ScoreMode {
max,
max_subtask,
}

@Serializable
data class Task(
val name: String,
val short_name: String,
val contest: String,
val max_score: Double,
val score_precision: Int,
val extra_headers: List<String>,
val order:Int,
val score_mode: ScoreMode
)
6 changes: 6 additions & 0 deletions src/cds/src/main/kotlin/org/icpclive/cds/cms/model/Team.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.icpclive.cds.cms.model

import kotlinx.serialization.Serializable

@Serializable
data class Team(val name: String)
10 changes: 10 additions & 0 deletions src/cds/src/main/kotlin/org/icpclive/cds/cms/model/User.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.icpclive.cds.cms.model

import kotlinx.serialization.Serializable

@Serializable
class User(
val f_name: String,
val l_name: String,
val team: String
)
14 changes: 14 additions & 0 deletions src/cds/src/main/kotlin/org/icpclive/cds/settings/CDSSettings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.icpclive.cds.atcoder.AtcoderDataSource
import org.icpclive.cds.cats.CATSDataSource
import org.icpclive.cds.clics.ClicsDataSource
import org.icpclive.cds.clics.FeedVersion
import org.icpclive.cds.cms.CmsDataSource
import org.icpclive.cds.codedrills.CodeDrillsDataSource
import org.icpclive.cds.codeforces.CFDataSource
import org.icpclive.cds.common.ContestDataSource
Expand Down Expand Up @@ -239,6 +240,19 @@ class AtcoderSettings(
override fun toDataSource(creds: Map<String, String>) = AtcoderDataSource(this)
}

@SerialName("cms")
@Serializable
class CmsSettings(
val url: String,
val activeContest: String,
val otherContests: List<String>,
override val network: NetworkSettings? = null,
override val emulation: EmulationSettings? = null
) : CDSSettings() {
override fun toDataSource(creds: Map<String, String>) = CmsDataSource(this)
}


@OptIn(ExperimentalSerializationApi::class)
fun parseFileToCdsSettings(path: Path) : CDSSettings {
val file = path.toFile()
Expand Down

0 comments on commit 17dbcb6

Please sign in to comment.