Coverage for sel_tools/utils/args.py: 100%
80 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-04 21:22 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-04 21:22 +0000
1"""Argparse helper module."""
3import copy
4from argparse import Action, ArgumentDefaultsHelpFormatter, ArgumentParser, FileType
5from datetime import date
6from pathlib import Path
7from typing import Any
9from sel_tools.config import REPO_DIR
12def dir_path(path_string: str) -> Path:
13 """Argparse type check if path is a directory."""
14 if Path(path_string).is_dir():
15 return Path(path_string)
16 raise NotADirectoryError(path_string)
19def file_path(path_string: str) -> Path:
20 """Argparse type check if path is a file."""
21 if Path(path_string).is_file():
22 return Path(path_string)
23 raise FileNotFoundError(path_string)
26class DateAction(Action):
27 """Parse dates from CLI arguments into datetime.date."""
29 def __call__(self, arg_parser, args, values, option_string=None): # type: ignore[no-untyped-def]
30 due_date = date.fromisoformat(f"{values[0]:04}-{values[1]:02}-{values[2]:02}")
31 setattr(args, self.dest, due_date)
34class ArgumentParserFactory: # pylint: disable=too-many-public-methods
35 """Argument Parser Factory to setup commonly used arguments."""
37 def __init__(self, parser: ArgumentParser) -> None:
38 self.__parser = parser
40 @staticmethod
41 def default_parser(description: str) -> "ArgumentParserFactory":
42 return ArgumentParserFactory(
43 ArgumentParser(
44 description=description,
45 formatter_class=ArgumentDefaultsHelpFormatter,
46 )
47 )
49 @staticmethod
50 def parent_parser() -> "ArgumentParserFactory":
51 return ArgumentParserFactory(ArgumentParser(add_help=False))
53 @staticmethod
54 def create_default_date_arg() -> dict:
55 return {
56 "metavar": ("YEAR", "MONTH", "DAY"),
57 "type": int,
58 "nargs": 3,
59 "action": DateAction,
60 "default": None,
61 }
63 @staticmethod
64 def default_or_required_if_none(default: Any) -> dict:
65 return {"required": True} if default is None else {"default": default}
67 @property
68 def parser(self) -> ArgumentParser:
69 return self.__parser
71 def copy(self) -> "ArgumentParserFactory":
72 return copy.deepcopy(self)
74 def add_gitlab_token(self) -> None:
75 self.__parser.add_argument(
76 "-t",
77 "--gitlab-token",
78 type=str,
79 required=True,
80 help="Private gitlab token",
81 )
83 def add_student_repo_info_file(self) -> None:
84 self.__parser.add_argument(
85 "student_repo_info_file",
86 type=FileType("r"),
87 help="File which contains the student repositories info (name, id)",
88 )
90 def add_homework_number(self) -> None:
91 self.__parser.add_argument(
92 "-n",
93 "--homework-number",
94 type=int,
95 required=True,
96 help="Homework number as integer",
97 )
99 def add_workspace(self) -> None:
100 self.__parser.add_argument(
101 "-w",
102 "--workspace",
103 type=dir_path,
104 default=REPO_DIR / "workspace",
105 help="Path to the workspace where all repositories will be cloned/pulled",
106 )
108 def add_issue_md_slide(self) -> None:
109 self.__parser.add_argument(
110 "-i",
111 "--issue-md-slides",
112 type=FileType("r"),
113 required=True,
114 help="Path to the markdown slides used for creating the issues",
115 )
117 def add_due_date(self) -> None:
118 self.__parser.add_argument(
119 "-d",
120 "--due-date",
121 **ArgumentParserFactory.create_default_date_arg(),
122 help="Due date for the homework's task(s)",
123 )
125 def add_date_sine_last_homework(self) -> None:
126 self.__parser.add_argument(
127 "-d",
128 "--date-last-homework",
129 **ArgumentParserFactory.create_default_date_arg(),
130 help="Date of the last homework used to generate a diff. If no date is provided, no diff will be generated",
131 )
133 def add_message(self, help_text: str) -> None:
134 self.__parser.add_argument(
135 "-m",
136 "--message",
137 type=str,
138 required=True,
139 help=help_text,
140 )
142 def add_issue_number(self) -> None:
143 self.__parser.add_argument(
144 "-i",
145 "--issue-number",
146 type=int,
147 required=True,
148 help="Issue number the comment should be added",
149 )
151 def add_state_event(self) -> None:
152 self.__parser.add_argument(
153 "-s",
154 "--state-event",
155 type=str,
156 choices=["close", "reopen"],
157 default=None,
158 help="Changes the state of the issue to",
159 )
161 def add_source_folder(self, default: Path | None) -> None:
162 self.__parser.add_argument(
163 "-s",
164 "--source-path",
165 type=dir_path,
166 **ArgumentParserFactory.default_or_required_if_none(default),
167 help="Path to the source files",
168 )
170 def add_number_of_repos(self) -> None:
171 self.__parser.add_argument(
172 "-n",
173 "--number-of-repos",
174 type=int,
175 default=1,
176 help="Number of repos to create",
177 )
179 def add_repo_info_dir(self) -> None:
180 self.__parser.add_argument(
181 "-r",
182 "--repo-info-dir",
183 type=dir_path,
184 default=REPO_DIR / "config",
185 help="Folder into which the config file containing the repositories info (name, id) will be saved",
186 )
188 def add_group_id(self) -> None:
189 self.__parser.add_argument(
190 "-g",
191 "--group-id",
192 type=int,
193 required=True,
194 help="ID of an existing GitLab group",
195 )
197 def add_repo_base_name(self) -> None:
198 self.__parser.add_argument(
199 "repo_base_name",
200 type=str,
201 help="Base name of the to-be-created repo(s)",
202 )
204 def add_output_path(self) -> None:
205 self.__parser.add_argument(
206 "-o",
207 "--output-dir",
208 default=REPO_DIR / "export",
209 type=Path,
210 help="Path to export location",
211 )
213 def add_keep_solutions(self) -> None:
214 self.__parser.add_argument(
215 "-k",
216 "--keep-solutions",
217 action="store_true",
218 help="Keep solution blocks from exported files",
219 )
221 def add_publish_solutions(self) -> None:
222 self.__parser.add_argument(
223 "--publish-solutions",
224 action="store_true",
225 help="Publish code that contains solution code",
226 )
228 def add_evaluation_date(self) -> None:
229 self.__parser.add_argument(
230 "-e",
231 "--evaluation-date",
232 **ArgumentParserFactory.create_default_date_arg(),
233 help="Date of the evaluation deadline",
234 )
236 def add_job_factory_path(self) -> None:
237 self.__parser.add_argument(
238 "-j",
239 "--job-factory",
240 type=file_path,
241 default=REPO_DIR / "tools" / "sel_tools" / "code_evaluation" / "jobs" / "sel.py",
242 help="Path to the python module containing the evaluation job factory",
243 )
245 def add_student_group_info_file(self) -> None:
246 self.__parser.add_argument(
247 "student_group_info_file",
248 type=FileType("r"),
249 help="File which contains the student groups info",
250 )