use serde::{Deserialize, Serialize};
use crate::models::localization::LocalizedText;
use crate::models::names::{ChallengeKeyword, ChallengeName, TargetName};
use crate::models::paths::BundleRelativePath;
use super::datasets::{DatasetsSpec, PublicDatasetsSpec};
use super::execution::{ChallengeExecutionSpec, PublicChallengeExecutionSpec};
use super::metrics::MetricSchemaSpec;
use super::targets::ChallengeTargetSpec;
pub const MIN_CHALLENGE_KEYWORDS: usize = 1;
pub const MAX_CHALLENGE_KEYWORDS: usize = 6;
#[derive(Debug, Clone, Serialize, Deserialize, garde::Validate, schemars::JsonSchema)]
#[garde(allow_unvalidated)]
#[serde(deny_unknown_fields)]
pub struct ChallengeBundleSpec {
pub schema_version: i32,
pub challenge_name: ChallengeName,
pub challenge_title: String,
pub summary: LocalizedText,
#[garde(length(min = MIN_CHALLENGE_KEYWORDS, max = MAX_CHALLENGE_KEYWORDS))]
#[schemars(length(min = 1, max = 6))]
pub keywords: Vec<ChallengeKeyword>,
pub solution: SolutionSpec,
pub targets: Vec<ChallengeTargetSpec>,
pub starts_at: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub closes_at: Option<String>,
pub eligibility: ChallengeEligibilitySpec,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub validation_submission_limit: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub official_submission_limit: Option<i64>,
pub visibility: ChallengeVisibilitySpec,
pub solution_publication: ChallengeSolutionPublicationPolicy,
pub execution: ChallengeExecutionSpec,
pub datasets: DatasetsSpec,
#[serde(default)]
#[schemars(required)]
pub metric_schema: MetricSchemaSpec,
}
impl ChallengeBundleSpec {
pub fn target(&self, target: &TargetName) -> Option<&ChallengeTargetSpec> {
self.targets
.iter()
.find(|candidate| &candidate.name == target)
}
pub fn sole_target(&self) -> Option<&TargetName> {
match self.targets.as_slice() {
[target] => Some(&target.name),
_ => None,
}
}
pub fn official_evaluation_may_expose_private_material(&self) -> bool {
if self.datasets.private_benchmark_enabled || self.execution.has_official_evaluation_setup()
{
return true;
}
match &self.execution {
ChallengeExecutionSpec::SeparatedEvaluator(spec) => {
spec.official_runs.as_ref().is_none_or(|path| {
!path
.as_path()
.starts_with(self.datasets.public_dir.as_path())
})
}
ChallengeExecutionSpec::PipedStdio(spec) => {
spec.official_session.as_ref().is_none_or(|path| {
!path
.as_path()
.starts_with(self.datasets.public_dir.as_path())
})
}
ChallengeExecutionSpec::CoexecutedBenchmark(_) => false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PublicChallengeBundleSpec {
pub schema_version: i32,
pub challenge_name: ChallengeName,
pub challenge_title: String,
pub summary: LocalizedText,
#[schemars(length(min = 1, max = 6))]
pub keywords: Vec<ChallengeKeyword>,
pub solution: SolutionSpec,
pub targets: Vec<ChallengeTargetSpec>,
pub starts_at: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub closes_at: Option<String>,
pub eligibility: ChallengeEligibilitySpec,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub validation_submission_limit: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub official_submission_limit: Option<i64>,
pub visibility: ChallengeVisibilitySpec,
pub solution_publication: ChallengeSolutionPublicationPolicy,
pub execution: PublicChallengeExecutionSpec,
pub datasets: PublicDatasetsSpec,
#[serde(default)]
#[schemars(required)]
pub metric_schema: MetricSchemaSpec,
}
impl PublicChallengeBundleSpec {
pub fn target(&self, target: &TargetName) -> Option<&ChallengeTargetSpec> {
self.targets
.iter()
.find(|candidate| &candidate.name == target)
}
pub fn sole_target(&self) -> Option<&TargetName> {
match self.targets.as_slice() {
[target] => Some(&target.name),
_ => None,
}
}
}
impl From<ChallengeBundleSpec> for PublicChallengeBundleSpec {
fn from(spec: ChallengeBundleSpec) -> Self {
Self {
schema_version: spec.schema_version,
challenge_name: spec.challenge_name,
challenge_title: spec.challenge_title,
summary: spec.summary,
keywords: spec.keywords,
solution: spec.solution,
targets: spec.targets,
starts_at: spec.starts_at,
closes_at: spec.closes_at,
eligibility: spec.eligibility,
validation_submission_limit: spec.validation_submission_limit,
official_submission_limit: spec.official_submission_limit,
visibility: spec.visibility,
solution_publication: spec.solution_publication,
execution: spec.execution.into(),
datasets: PublicDatasetsSpec {
public_dir: spec.datasets.public_dir,
public_policy: spec.datasets.public_policy,
private_benchmark_policy: spec.datasets.private_benchmark_policy,
private_benchmark_enabled: spec.datasets.private_benchmark_enabled,
},
metric_schema: spec.metric_schema,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ChallengeEligibilitySpec {
#[serde(rename = "type")]
pub eligibility_type: ChallengeEligibilityType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ChallengeEligibilityType {
Open,
PrivateShortlist,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ChallengeVisibilitySpec {
pub leaderboard: ChallengeVisibility,
pub score_distribution: ChallengeVisibility,
pub result_detail: ChallengeResultDetailVisibility,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ChallengeVisibility {
PublicLive,
PublicAfterClose,
Hidden,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ChallengeResultDetailVisibility {
SubmitterLivePublicLive,
SubmitterLivePublicAfterClose,
SubmitterOnly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ChallengeSolutionPublicationPolicy {
Private,
Public,
PublicAfterClose,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SolutionSpec {
pub protocol: String,
pub manifest_file: BundleRelativePath,
}