From 3d594c9710569d1692b49cfad3a460834c583c90 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sat, 21 Jun 2025 12:05:47 -0400 Subject: [PATCH 1/3] feat: diff.output.xxx -> diff.xxx --- joj3_config_generator/models/task.py | 22 +++---------- joj3_config_generator/transformers/task.py | 38 ++++++++++++---------- tests/convert/basic/task.toml | 10 +++--- tests/convert/diff/task.toml | 18 +++++----- 4 files changed, 39 insertions(+), 49 deletions(-) diff --git a/joj3_config_generator/models/task.py b/joj3_config_generator/models/task.py index 5944a6e..1d9794a 100644 --- a/joj3_config_generator/models/task.py +++ b/joj3_config_generator/models/task.py @@ -79,8 +79,8 @@ class ParserKeyword(BaseModel): weight: List[int] = [] -class ParserDiffOutputs(BaseModel): - score: int = 0 +class ParserDiff(BaseModel): + score: int = DEFAULT_CASE_SCORE ignore_spaces: bool = Field( True, validation_alias=AliasChoices("ignore-spaces", "ignore_spaces") ) @@ -97,17 +97,6 @@ class ParserDiffOutputs(BaseModel): ) -class ParserDiff(BaseModel): - output: ParserDiffOutputs = ParserDiffOutputs() - - -class ParserDiffFull(ParserDiff): - default_score: int = Field( - DEFAULT_CASE_SCORE, - validation_alias=AliasChoices("default-score", "default_score"), - ) - - class StageFiles(BaseModel): import_: List[str] = Field([], validation_alias="import") export: List[str] = [] @@ -162,15 +151,11 @@ class CaseBase(BaseModel): limit: Limit = Limit() -class StageCase(CaseBase): - diff: ParserDiffFull = ParserDiffFull() - - class DictCase(CaseBase): diff: ParserDiff = ParserDiff() -class Stage(StageCase): +class Stage(CaseBase): name: str = "" # stage name skip: List[str] = [] @@ -190,6 +175,7 @@ class Stage(StageCase): validation_alias=AliasChoices("result-detail", "result_detail"), ) file: ParserFile = ParserFile() + diff: ParserDiff = ParserDiff() cases: Dict[str, DictCase] = {} diff --git a/joj3_config_generator/transformers/task.py b/joj3_config_generator/transformers/task.py index 5e6c70f..c0ea1c6 100644 --- a/joj3_config_generator/transformers/task.py +++ b/joj3_config_generator/transformers/task.py @@ -182,7 +182,7 @@ def fix_file(file_parser_config: task.ParserFile, file_parser: result.Parser) -> def fix_diff( - _: task.ParserDiffFull, + _: task.ParserDiff, diff_parser: result.Parser, task_stage: task.Stage, executor: result.Executor, @@ -228,22 +228,24 @@ def fix_diff( if cmd.proc_limit == executor.with_.default.proc_limit: cmd.proc_limit = None stage_cases.append(cmd) + + def get_diff_attribute(attribute_name: str) -> Any: + if case.diff and attribute_name in case.diff.model_fields_set: + return getattr(case.diff, attribute_name) + return getattr(task_stage.diff, attribute_name) + parser_case = result.DiffCasesConfig( outputs=[ result.DiffOutputConfig( - score=( - case.diff.output.score - if "score" in case.diff.output.model_fields_set - else task_stage.diff.default_score - ), + score=get_diff_attribute("score"), filename="stdout", answer_path=stdout, - force_quit_on_diff=case.diff.output.force_quit, - always_hide=case.diff.output.hide, - compare_space=not case.diff.output.ignore_spaces, - max_diff_length=case.diff.output.max_length, - max_diff_lines=case.diff.output.max_lines, - hide_common_prefix=case.diff.output.hide_common_prefix, + compare_space=not get_diff_attribute("ignore_spaces"), + always_hide=get_diff_attribute("hide"), + force_quit_on_diff=get_diff_attribute("force_quit"), + max_diff_length=get_diff_attribute("max_length"), + max_diff_lines=get_diff_attribute("max_lines"), + hide_common_prefix=get_diff_attribute("hide_common_prefix"), ) ] ) @@ -251,18 +253,20 @@ def fix_diff( for case_name in unspecified_cases: cmd = result.OptionalCmd( stdin=result.LocalFile(src=str(base_dir / f"{case_name}.in")), - cpu_limit=None, - clock_limit=None, - memory_limit=None, - proc_limit=None, ) stage_cases.append(cmd) parser_case = result.DiffCasesConfig( outputs=[ result.DiffOutputConfig( - score=task_stage.diff.default_score, + score=task_stage.diff.score, filename="stdout", answer_path=str(base_dir / f"{case_name}.out"), + compare_space=not task_stage.diff.ignore_spaces, + always_hide=task_stage.diff.hide, + force_quit_on_diff=task_stage.diff.force_quit, + max_diff_length=task_stage.diff.max_length, + max_diff_lines=task_stage.diff.max_lines, + hide_common_prefix=task_stage.diff.hide_common_prefix, ) ] ) diff --git a/tests/convert/basic/task.toml b/tests/convert/basic/task.toml index 2768650..cfbdacd 100644 --- a/tests/convert/basic/task.toml +++ b/tests/convert/basic/task.toml @@ -92,7 +92,7 @@ copy-in-cwd = false files.import = [ "h7/build/ex2-asan" ] limit.mem = "128m" -diff.default-score = 10 +diff.score = 10 parsers = [ "diff", "result-detail" ] result-detail.exit-status = true @@ -101,17 +101,17 @@ result-detail.stderr = true # will be removed as long as the name is fixed case0.limit.cpu = "0.5s" case0.limit.mem = "5m" -case0.diff.output.ignore-spaces = true +case0.diff.ignore-spaces = true #case0.limit.stdout = 8 #case0.command = "./h7/build/ex2" case0.in = "case0.in" -case1.diff.output.score = 5 +case1.diff.score = 5 case1.limit.cpu = "1s" case1.limit.mem = "5m" -case1.diff.output.ignore-spaces = true +case1.diff.ignore-spaces = true #case1.limit.stdout = 8 #case1.command = "./h7/build/ex2" case1.in = "case1.in" -case2.diff.output.hide = true +case2.diff.hide = true diff --git a/tests/convert/diff/task.toml b/tests/convert/diff/task.toml index ea3d0de..61236e1 100644 --- a/tests/convert/diff/task.toml +++ b/tests/convert/diff/task.toml @@ -17,26 +17,26 @@ parsers = [ "diff", "result-detail" ] result-detail.exit-status = true result-detail.stderr = true -diff.default-score = 100 +diff.score = 100 -case0.diff.output.score = 5 +case0.diff.score = 5 case0.limit.cpu = "1s" case0.limit.mem = "2m" -case0.diff.output.ignore-spaces = true +case0.diff.ignore-spaces = true case0.command = "./h7/build/ex2" case0.in = "case0.in" -case1.diff.output.score = 123214122421 +case1.diff.score = 123214122421 case1.limit.cpu = "2s" case1.limit.mem = "4m" -case1.diff.output.ignore-spaces = true +case1.diff.ignore-spaces = true case1.command = "./h7/build/ex2" -case9.diff.output.score = 1232131 +case9.diff.score = 1232131 case9.limit.mem = "10m" -case11.diff.output.score = 92321 +case11.diff.score = 92321 -case10.diff.output.score = 823131 +case10.diff.score = 823131 -case5.diff.output.score = 2590 +case5.diff.score = 2590 -- 2.30.2 From a79f36bc0ebca8d308e5a44245cd18679731d118 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sat, 21 Jun 2025 12:21:04 -0400 Subject: [PATCH 2/3] chore: simpler models --- joj3_config_generator/models/task.py | 11 ++++------- joj3_config_generator/transformers/task.py | 4 ++-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/joj3_config_generator/models/task.py b/joj3_config_generator/models/task.py index 1d9794a..24ee270 100644 --- a/joj3_config_generator/models/task.py +++ b/joj3_config_generator/models/task.py @@ -139,7 +139,7 @@ class Parser(str, Enum): ELF = "elf" -class CaseBase(BaseModel): +class Case(BaseModel): env: List[str] = [] command: str = "" # Command to run files: StageFiles = StageFiles() @@ -149,13 +149,10 @@ class CaseBase(BaseModel): True, validation_alias=AliasChoices("copy-in-cwd", "copy_in_cwd") ) limit: Limit = Limit() - - -class DictCase(CaseBase): diff: ParserDiff = ParserDiff() -class Stage(CaseBase): +class Stage(Case): name: str = "" # stage name skip: List[str] = [] @@ -175,9 +172,9 @@ class Stage(CaseBase): validation_alias=AliasChoices("result-detail", "result_detail"), ) file: ParserFile = ParserFile() - diff: ParserDiff = ParserDiff() + # diff: ParserDiff = ParserDiff() # inherited from Case - cases: Dict[str, DictCase] = {} + cases: Dict[str, Case] = {} model_config = ConfigDict(extra="allow") diff --git a/joj3_config_generator/transformers/task.py b/joj3_config_generator/transformers/task.py index c0ea1c6..b3530a8 100644 --- a/joj3_config_generator/transformers/task.py +++ b/joj3_config_generator/transformers/task.py @@ -276,7 +276,7 @@ def fix_diff( def get_unspecified_cases( - task_root: Path, task_path: Path, cases: Dict[str, task.DictCase] + task_root: Path, task_path: Path, cases: Dict[str, task.Case] ) -> List[str]: testcases = set() for testcases_path in (task_root / task_path).parent.glob("**/*.in"): @@ -306,7 +306,7 @@ def get_unspecified_cases( def get_stdin_stdout( - task_root: Path, task_path: Path, case_name: str, case: task.DictCase + task_root: Path, task_path: Path, case_name: str, case: task.Case ) -> Tuple[result.Stdin, Optional[str]]: case_stdout_name = case.out_ if case.out_ else f"{case_name}.out" stdin: result.Stdin = result.MemoryFile(content="") -- 2.30.2 From f0c840f5492965742bc8c97edd084e6b64ae0010 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sat, 21 Jun 2025 13:55:59 -0400 Subject: [PATCH 3/3] feat: support both new & old style for diff --- joj3_config_generator/models/task.py | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/joj3_config_generator/models/task.py b/joj3_config_generator/models/task.py index 24ee270..6fca240 100644 --- a/joj3_config_generator/models/task.py +++ b/joj3_config_generator/models/task.py @@ -79,6 +79,24 @@ class ParserKeyword(BaseModel): weight: List[int] = [] +class ParserDiffOutputs(BaseModel): + score: int = 0 + ignore_spaces: bool = Field( + True, validation_alias=AliasChoices("ignore-spaces", "ignore_spaces") + ) + hide: bool = False + force_quit: bool = Field( + False, validation_alias=AliasChoices("force-quit", "force_quit") + ) + max_length: int = Field( + 2048, validation_alias=AliasChoices("max-length", "max_length") + ) + max_lines: int = Field(50, validation_alias=AliasChoices("max-lines", "max_lines")) + hide_common_prefix: bool = Field( + False, validation_alias=AliasChoices("hide-common-prefix", "hide_common_prefix") + ) + + class ParserDiff(BaseModel): score: int = DEFAULT_CASE_SCORE ignore_spaces: bool = Field( @@ -95,6 +113,23 @@ class ParserDiff(BaseModel): hide_common_prefix: bool = Field( False, validation_alias=AliasChoices("hide-common-prefix", "hide_common_prefix") ) + # remove below codes when migration is complete + output: ParserDiffOutputs = ParserDiffOutputs() + default_score: int = Field( + DEFAULT_CASE_SCORE, + validation_alias=AliasChoices("default-score", "default_score"), + ) + + @model_validator(mode="after") + def copy_output_fields(self) -> "ParserDiff": + if "default_score" in self.model_fields_set: + self.score = self.default_score + if not isinstance(self.output, ParserDiffOutputs): + return self + for field_name, field_value in self.output.model_dump().items(): + if field_name in self.output.model_fields_set and hasattr(self, field_name): + setattr(self, field_name, field_value) + return self class StageFiles(BaseModel): -- 2.30.2