diff --git a/joj3_config_generator/loader.py b/joj3_config_generator/loader.py index dc8b129..204a8c4 100644 --- a/joj3_config_generator/loader.py +++ b/joj3_config_generator/loader.py @@ -9,17 +9,19 @@ from joj3_config_generator.models import answer, joj1, repo, task def load_joj3_task_toml_answers() -> answer.Answers: - questions = [ - inquirer.Text(name="name", message="What's the task name?"), - inquirer.Checkbox( - "stages", - message="What kind of stages do you need?", - choices=[member.value for member in answer.StageEnum], - default=[answer.StageEnum.COMPILATION], - ), - ] - answers = inquirer.prompt(questions) - return answer.Answers(**answers) + name = inquirer.text("What's the task name?", default="hw0") + language: answer.LanguageInterface = inquirer.list_input( + "What's the language?", choices=answer.LANGUAGES + ) + stages = inquirer.checkbox( + "What's the stages?", + choices=[member.value for member in language.Stage], + default=[member.value for member in language.Stage], + ) + language.set_stages(stages) + attribute = inquirer.prompt(language.get_attribute_questions()) + language.set_attribute(attribute) + return answer.Answers(name=name, language=language) def load_joj1_yaml(yaml_path: Path) -> joj1.Config: diff --git a/joj3_config_generator/main.py b/joj3_config_generator/main.py index 2049475..8d18342 100644 --- a/joj3_config_generator/main.py +++ b/joj3_config_generator/main.py @@ -30,8 +30,6 @@ def create( Create a new JOJ3 task toml config file """ answers = load_joj3_task_toml_answers() - answers_dict = answers.model_dump(mode="json", by_alias=True) - logger.debug(f"Got answers: {answers_dict}") task_model = create_joj3_task_conf(answers) result_dict = task_model.model_dump( mode="json", by_alias=True, exclude_none=True, exclude_unset=True diff --git a/joj3_config_generator/models/answer.py b/joj3_config_generator/models/answer.py index f29591d..b79aebf 100644 --- a/joj3_config_generator/models/answer.py +++ b/joj3_config_generator/models/answer.py @@ -1,16 +1,124 @@ +from abc import ABC, abstractmethod from enum import Enum -from typing import List +from typing import Any, ClassVar, Dict, List -from pydantic import BaseModel +import inquirer +from pydantic import BaseModel, ConfigDict -class StageEnum(str, Enum): - COMPILATION = "Compilation" - CPPCHECK = "Cppcheck" - CPPLINT = "Cpplint" - CLANG_TIDY = "Clang-Tidy" +class LanguageInterface(ABC): + @abstractmethod + def __str__(self) -> str: ... + + @abstractmethod + class Stage(str, Enum): ... + + @abstractmethod + class Attribute(BaseModel): ... + + stages: ClassVar[List[Enum]] + attribute: ClassVar[BaseModel] + + @classmethod + def set_stages(cls, stages: List[str]) -> None: + cls.stages = [cls.Stage(stage) for stage in stages] + + @classmethod + def set_attribute(cls, attribute_dict: Dict[str, Any]) -> None: + cls.attribute = cls.Attribute(**attribute_dict) + + @classmethod + @abstractmethod + def get_attribute_questions(cls) -> List[Any]: ... + + +class Cpp(LanguageInterface): + def __str__(self) -> str: + return "C++" + + class Stage(str, Enum): + COMPILATION = "Compilation" + CPPCHECK = "Cppcheck" + CPPLINT = "Cpplint" + CLANG_TIDY = "Clang-Tidy" + RUN = "Run" + + class Attribute(BaseModel): + compile_command: str = "make" + run_command: str = "./a.out" + + stages = [] + attribute = Attribute() + + @classmethod + def get_attribute_questions(cls) -> List[Any]: + return [ + inquirer.Text( + name="compile_command", + message="Compile command", + default=cls.attribute.compile_command, + ), + inquirer.Text( + name="run_command", + message="Run command", + default=cls.attribute.run_command, + ), + ] + + +class Python(LanguageInterface): + def __str__(self) -> str: + return "Python" + + class Stage(str, Enum): + RUN = "Run" + + class Attribute(BaseModel): + run_command: str = "python3 main.py" + + stages = [] + attribute = Attribute() + + @classmethod + def get_attribute_questions(cls) -> List[Any]: + return [ + inquirer.Text( + name="run_command", + message="Run command", + default=cls.attribute.run_command, + ), + ] + + +class Rust(LanguageInterface): + def __str__(self) -> str: + return "Rust" + + class Stage(str, Enum): + COMPILATION = "Compilation" + CLIPPY = "Clippy" + RUN = "Run" + + class Attribute(BaseModel): + pass + + stages = [] + attribute = Attribute() + + @classmethod + def get_attribute_questions(cls) -> List[Any]: + return [] + + +LANGUAGES = [ + Cpp(), + Python(), + Rust(), +] class Answers(BaseModel): name: str - stages: List[str] + language: LanguageInterface + + model_config = ConfigDict(arbitrary_types_allowed=True) diff --git a/joj3_config_generator/transformers/answer.py b/joj3_config_generator/transformers/answer.py index 0330a66..1b19bc2 100644 --- a/joj3_config_generator/transformers/answer.py +++ b/joj3_config_generator/transformers/answer.py @@ -1,6 +1,52 @@ +from typing import Any, Callable, Dict, List, Type + from joj3_config_generator.models import answer, task -# TODO: implement def get_task_conf_from_answers(answers: answer.Answers) -> task.Config: - return task.Config(task=task.Task(name=answers.name)) + language = answers.language + transformer_dict = get_transformer_dict() + transformer = transformer_dict[type(language)] + stages = transformer(language) + return task.Config(task=task.Task(name=answers.name), stages=stages) + + +def get_transformer_dict() -> Dict[ + Type[Any], + Callable[[Any], List[task.Stage]], +]: + return { + answer.Cpp: get_cpp_stages, + answer.Python: get_python_stages, + answer.Rust: get_rust_stages, + } + + +# TODO: implement +def get_cpp_stages(language: answer.Cpp) -> List[task.Stage]: + stages = language.stages + attribute: answer.Cpp.Attribute = language.attribute + task_stages = [] + if answer.Cpp.Stage.CPPCHECK in stages: + task_stages.append(task.Stage(name=answer.Cpp.Stage.CPPCHECK)) + if answer.Cpp.Stage.CPPLINT in stages: + task_stages.append(task.Stage(name=answer.Cpp.Stage.CPPLINT)) + if answer.Cpp.Stage.CLANG_TIDY in stages: + task_stages.append(task.Stage(name=answer.Cpp.Stage.CLANG_TIDY)) + if answer.Cpp.Stage.RUN in stages: + task_stages.append(task.Stage(name=answer.Cpp.Stage.RUN)) + return task_stages + + +# TODO: implement +def get_python_stages(language: answer.Python) -> List[task.Stage]: + stages = language.stages + attribute: answer.Python.Attribute = language.attribute + return [] + + +# TODO: implement +def get_rust_stages(language: answer.Rust) -> List[task.Stage]: + stages = language.stages + attribute: answer.Rust.Attribute = language.attribute + return [] diff --git a/tests/create/basic/answers.json b/tests/create/basic/answers.json deleted file mode 100644 index d8fbbe7..0000000 --- a/tests/create/basic/answers.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "hw7 ex2", - "stages": ["Compilation"] -} diff --git a/tests/create/basic/task.toml b/tests/create/basic/task.toml deleted file mode 100644 index b6b0557..0000000 --- a/tests/create/basic/task.toml +++ /dev/null @@ -1,2 +0,0 @@ -[task] -name = "hw7 ex2" diff --git a/tests/create/cpp/answers.json b/tests/create/cpp/answers.json new file mode 100644 index 0000000..9f72f76 --- /dev/null +++ b/tests/create/cpp/answers.json @@ -0,0 +1,9 @@ +{ + "name": "hw7 ex2", + "language": "C++", + "stages": ["Compilation", "Cppcheck", "Cpplint", "Clang-Tidy", "Run"], + "attribute": { + "compile_command": "make", + "run_command": "./ex2" + } +} diff --git a/tests/create/cpp/task.toml b/tests/create/cpp/task.toml new file mode 100644 index 0000000..49c379f --- /dev/null +++ b/tests/create/cpp/task.toml @@ -0,0 +1,18 @@ +[task] +name = "hw7 ex2" + +[[stages]] +name = "Cppcheck" +cases = {} + +[[stages]] +name = "Cpplint" +cases = {} + +[[stages]] +name = "Clang-Tidy" +cases = {} + +[[stages]] +name = "Run" +cases = {} diff --git a/tests/create/test_create_cases.py b/tests/create/test_create_cases.py index 16c276d..bdb911a 100644 --- a/tests/create/test_create_cases.py +++ b/tests/create/test_create_cases.py @@ -1,5 +1,5 @@ from tests.create.utils import load_case -def test_basic() -> None: - load_case("basic") +def test_cpp() -> None: + load_case("cpp") diff --git a/tests/create/utils.py b/tests/create/utils.py index 1cac87b..118e340 100644 --- a/tests/create/utils.py +++ b/tests/create/utils.py @@ -11,8 +11,11 @@ def load_case(case_name: str) -> None: root = Path(__file__).resolve().parent answers_json_path = root / case_name / "answers.json" task_toml_path = root / case_name / "task.toml" - answers = answer.Answers(**json.loads(answers_json_path.read_text())) - print(answers) + answers_dict = json.loads(answers_json_path.read_text()) + language = next(x for x in answer.LANGUAGES if str(x) == answers_dict["language"]) + language.set_stages(answers_dict["stages"]) + language.set_attribute(answers_dict["attribute"]) + answers = answer.Answers(name=answers_dict["name"], language=language) expected_result = rtoml.loads(task_toml_path.read_text()) result = create_joj3_task_conf(answers).model_dump( mode="json", by_alias=True, exclude_none=True, exclude_unset=True