Skip to content

Commit 5038f37

Browse files
authored
Merge pull request #348 from franv314/master
Tool to export solution checks and reduced logging on the curses UI
2 parents edba141 + 4bdcce6 commit 5038f37

File tree

5 files changed

+142
-5
lines changed

5 files changed

+142
-5
lines changed

src/opt.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ impl UIOpt {
242242
/// Disable the Curses UI and fallback to PrintUI if verbose output is enabled.
243243
pub fn disable_if_needed(&mut self, logger: &LoggerOpt) {
244244
let mut show_warning = false;
245-
if logger.should_diable_curses() {
245+
if logger.should_disable_curses() {
246246
if let task_maker_format::ui::UIType::Curses = self.ui {
247247
// warning deferred to after the logger has been initialized
248248
show_warning = true;
@@ -280,9 +280,10 @@ impl LoggerOpt {
280280
std::env::set_var("RUST_BACKTRACE", "1");
281281
}
282282
match self.verbose {
283-
0 => std::env::set_var("RUST_LOG", "warn,tabox=warn"),
284-
1 => std::env::set_var("RUST_LOG", "info,tabox=info"),
285-
2 => std::env::set_var("RUST_LOG", "debug,tabox=debug"),
283+
0 => std::env::set_var("RUST_LOG", "error,tabox=error"),
284+
1 => std::env::set_var("RUST_LOG", "warn,tabox=warn"),
285+
2 => std::env::set_var("RUST_LOG", "info,tabox=info"),
286+
3 => std::env::set_var("RUST_LOG", "debug,tabox=debug"),
286287
_ => std::env::set_var("RUST_LOG", "trace,tabox=trace"),
287288
}
288289

@@ -292,7 +293,7 @@ impl LoggerOpt {
292293
better_panic::install();
293294
}
294295

295-
pub fn should_diable_curses(&self) -> bool {
296+
pub fn should_disable_curses(&self) -> bool {
296297
self.verbose > 0
297298
}
298299
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
use std::collections::HashMap;
2+
use std::path::PathBuf;
3+
4+
use anyhow::{bail, Context, Error};
5+
use clap::Parser;
6+
7+
use serde::{Deserialize, Serialize};
8+
use task_maker_format::ioi::IOITask;
9+
use task_maker_format::{
10+
EvaluationConfig, EvaluationData, Solution, SolutionCheckResult, TaskFormat,
11+
};
12+
13+
use crate::{FilterOpt, FindTaskOpt};
14+
15+
#[derive(Parser, Debug, Clone)]
16+
pub struct ExportSolutionChecksOpt {
17+
#[clap(flatten, next_help_heading = Some("TASK SEARCH"))]
18+
pub find_task: FindTaskOpt,
19+
20+
#[clap(flatten, next_help_heading = Some("FILTER"))]
21+
pub filter: FilterOpt,
22+
}
23+
24+
#[derive(Serialize, Deserialize)]
25+
struct SolutionWithChecks {
26+
path: PathBuf,
27+
checks: Vec<Option<SolutionCheckResult>>,
28+
min_score: f64,
29+
max_score: f64,
30+
}
31+
32+
pub fn main_export_solution_checks(opt: ExportSolutionChecksOpt) -> Result<(), Error> {
33+
let eval_config = EvaluationConfig {
34+
solution_filter: opt.filter.filter,
35+
booklet_solutions: false,
36+
no_statement: true,
37+
solution_paths: opt.filter.solution,
38+
disabled_sanity_checks: Default::default(),
39+
seed: Default::default(),
40+
dry_run: true,
41+
};
42+
let task = opt
43+
.find_task
44+
.find_task(&eval_config)
45+
.context("Failed to locate the task")?;
46+
47+
let TaskFormat::IOI(task) = task else {
48+
bail!("Exporting solution checks is only supported for IOI tasks")
49+
};
50+
51+
let (mut eval, _) = EvaluationData::new(task.path());
52+
let solutions = eval_config.find_solutions(
53+
task.path(),
54+
vec!["sol/*"],
55+
Some(task.grader_map.clone()),
56+
&mut eval,
57+
);
58+
59+
let subtasks = task
60+
.subtasks
61+
.iter()
62+
.filter_map(|(_, info)| info.name.clone().map(|name| (name, info.id)))
63+
.collect::<HashMap<_, _>>();
64+
65+
let checks = solutions
66+
.iter()
67+
.map(|solution| extract_solution_checks(&task, &subtasks, solution))
68+
.collect::<Result<Vec<_>, _>>()?;
69+
70+
println!("{}", serde_json::to_string_pretty(&checks)?);
71+
72+
Ok(())
73+
}
74+
75+
fn extract_solution_checks(
76+
task: &IOITask,
77+
subtasks: &HashMap<String, u32>,
78+
solution: &Solution,
79+
) -> anyhow::Result<SolutionWithChecks> {
80+
let mut checks = vec![None; task.subtasks.len()];
81+
for check in &solution.checks {
82+
if let Some(&idx) = subtasks.get(&check.subtask_name_pattern) {
83+
let idx: usize = idx.try_into()?;
84+
if checks[idx].is_some() {
85+
bail!(
86+
"Found multiple checks for subtask {} in solution {}",
87+
check.subtask_name_pattern,
88+
solution.source_file.path.display()
89+
);
90+
}
91+
checks[idx] = Some(check.result);
92+
} else if check.subtask_name_pattern == "*" {
93+
for subtask_check in checks.iter_mut() {
94+
if subtask_check.is_some() {
95+
bail!(
96+
"Found multiple checks for subtask {} in solution {}",
97+
check.subtask_name_pattern,
98+
solution.source_file.path.display()
99+
);
100+
}
101+
*subtask_check = Some(check.result);
102+
}
103+
} else {
104+
bail!(
105+
"Found invalid subtask check {} in solution {}",
106+
check.subtask_name_pattern,
107+
solution.source_file.path.display()
108+
);
109+
}
110+
}
111+
112+
let mut min_score = 0.;
113+
let mut max_score = 0.;
114+
115+
for (i, check) in checks.iter().enumerate() {
116+
if *check == Some(SolutionCheckResult::Accepted) {
117+
min_score += task.subtasks[&i.try_into()?].max_score;
118+
max_score += task.subtasks[&i.try_into()?].max_score;
119+
} else if *check == Some(SolutionCheckResult::PartialScore) || check.is_none() {
120+
max_score += task.subtasks[&i.try_into()?].max_score;
121+
}
122+
}
123+
124+
Ok(SolutionWithChecks {
125+
path: solution.source_file.path.clone(),
126+
checks,
127+
min_score,
128+
max_score,
129+
})
130+
}

src/tools/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use task_maker_rust::tools::add_solution_checks::main_add_solution_checks;
55
use task_maker_rust::tools::booklet::main_booklet;
66
use task_maker_rust::tools::clear::main_clear;
77
use task_maker_rust::tools::copy_competition_files::copy_competition_files_main;
8+
use task_maker_rust::tools::export_solution_checks::main_export_solution_checks;
89
use task_maker_rust::tools::find_bad_case::main_find_bad_case;
910
use task_maker_rust::tools::fuzz_checker::main_fuzz_checker;
1011
use task_maker_rust::tools::gen_autocompletion::main_get_autocompletion;
@@ -34,6 +35,7 @@ fn main() {
3435
Tool::FuzzChecker(opt) => main_fuzz_checker(opt),
3536
Tool::FindBadCase(opt) => main_find_bad_case(opt),
3637
Tool::AddSolutionChecks(opt) => main_add_solution_checks(opt, base_opt.logger),
38+
Tool::ExportSolutionChecks(opt) => main_export_solution_checks(opt),
3739
Tool::InternalSandbox => return task_maker_rust::main_sandbox(),
3840
}
3941
.nice_unwrap()

src/tools/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod add_solution_checks;
22
pub mod booklet;
33
pub mod clear;
44
pub mod copy_competition_files;
5+
pub mod export_solution_checks;
56
pub mod find_bad_case;
67
pub mod fuzz_checker;
78
pub mod gen_autocompletion;

src/tools/opt.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::tools::add_solution_checks::AddSolutionChecksOpt;
44
use crate::tools::booklet::BookletOpt;
55
use crate::tools::clear::ClearOpt;
66
use crate::tools::copy_competition_files::CopyCompetitionFilesOpt;
7+
use crate::tools::export_solution_checks::ExportSolutionChecksOpt;
78
use crate::tools::find_bad_case::FindBadCaseOpt;
89
use crate::tools::fuzz_checker::FuzzCheckerOpt;
910
use crate::tools::gen_autocompletion::GenAutocompletionOpt;
@@ -56,6 +57,8 @@ pub enum Tool {
5657
FindBadCase(FindBadCaseOpt),
5758
/// Add the @check comments to the solutions.
5859
AddSolutionChecks(AddSolutionChecksOpt),
60+
/// Exports solution checks to json.
61+
ExportSolutionChecks(ExportSolutionChecksOpt),
5962
/// Run the sandbox instead of the normal task-maker.
6063
///
6164
/// This option is left as undocumented as it's not part of the public API.

0 commit comments

Comments
 (0)