Coverage for tools / gitlab_projects.py: 77%

98 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-02 18:55 +0000

1"""Perform actions on gitlab projects.""" 

2 

3import sys 

4from argparse import ArgumentDefaultsHelpFormatter, Namespace 

5from pathlib import Path 

6 

7from sel_tools.code_evaluation.evaluate_code import evaluate_code 

8from sel_tools.code_evaluation.jobs.factory import EvaluationJobFactory 

9from sel_tools.code_evaluation.report import write_evaluation_report_for_student_comments, write_evaluation_reports 

10from sel_tools.diff_creation.create_diff import create_diff 

11from sel_tools.diff_creation.report import write_diff_reports, write_report_for_inactive_student_repos 

12from sel_tools.file_export.export_item import export_items 

13from sel_tools.file_parsing.slide_parser import get_tasks_from_slides 

14from sel_tools.gitlab_api.add_user import add_users 

15from sel_tools.gitlab_api.comment_issue import comment_issues 

16from sel_tools.gitlab_api.create_commit import commit_changes, upload_files 

17from sel_tools.gitlab_api.create_issue import create_issues 

18from sel_tools.gitlab_api.fetch_repo import fetch_repos 

19from sel_tools.gitlab_api.instance import create_gitlab_instance 

20from sel_tools.utils.args import ArgumentParserFactory 

21from sel_tools.utils.comment import Comment 

22from sel_tools.utils.student_config import read_student_repo_info_from_config_file 

23from sel_tools.utils.task import configure_tasks 

24 

25 

26def edit_create_issues(args: Namespace) -> None: 

27 """Default action for create_issues subcommand.""" 

28 tasks = get_tasks_from_slides(Path(args.issue_md_slides.name)) 

29 tasks = configure_tasks(tasks, args.due_date, args.homework_number) 

30 create_issues( 

31 tasks, 

32 read_student_repo_info_from_config_file(args.student_repo_info_file.name), 

33 create_gitlab_instance(args.gitlab_token), 

34 ) 

35 

36 

37def edit_comment_issue(args: Namespace) -> None: 

38 """Default action for comment_issue subcommand.""" 

39 comment = Comment.create(args.issue_number, args.message, args.state_event) 

40 comment_issues( 

41 comment, 

42 read_student_repo_info_from_config_file(args.student_repo_info_file.name), 

43 create_gitlab_instance(args.gitlab_token), 

44 ) 

45 

46 

47def edit_fetch_code(args: Namespace) -> None: 

48 """Default action for fetch_code subcommand.""" 

49 fetch_repos( 

50 args.workspace, 

51 read_student_repo_info_from_config_file(args.student_repo_info_file.name), 

52 create_gitlab_instance(args.gitlab_token), 

53 ) 

54 

55 

56def edit_evaluate_code(args: Namespace) -> None: 

57 """Default action for evaluate_code subcommand.""" 

58 gitlab_projects = fetch_repos( 

59 args.workspace, 

60 read_student_repo_info_from_config_file(args.student_repo_info_file.name), 

61 create_gitlab_instance(args.gitlab_token), 

62 ) 

63 factory = EvaluationJobFactory.load_factory_from_file(args.job_factory) 

64 evaluation_reports = evaluate_code(factory, gitlab_projects, args.homework_number, args.evaluation_date) 

65 write_evaluation_reports(evaluation_reports, f"homework-{args.homework_number}-report") 

66 write_evaluation_report_for_student_comments(evaluation_reports, args.workspace) 

67 diff_reports = create_diff( 

68 [project.local_path for project in gitlab_projects], 

69 args.date_last_homework, 

70 args.evaluation_date, 

71 ) 

72 write_diff_reports(diff_reports, f"homework-{args.homework_number}-diff") 

73 write_report_for_inactive_student_repos(diff_reports, args.workspace) 

74 

75 

76def edit_upload_files(args: Namespace) -> None: 

77 """Default action for upload_files subcommand.""" 

78 upload_files( 

79 args.source_path, 

80 read_student_repo_info_from_config_file(args.student_repo_info_file.name), 

81 create_gitlab_instance(args.gitlab_token), 

82 ) 

83 

84 

85def edit_commit_changes(args: Namespace) -> None: 

86 """Default action for commit_changes subcommand.""" 

87 gitlab_projects = fetch_repos( 

88 args.workspace, 

89 read_student_repo_info_from_config_file(args.student_repo_info_file.name), 

90 create_gitlab_instance(args.gitlab_token), 

91 ) 

92 student_repos = [project.local_path for project in gitlab_projects] 

93 export_items(args.source_path, student_repos, args.keep_solutions) 

94 commit_changes(student_repos, args.message) 

95 

96 

97def edit_add_users(args: Namespace) -> None: 

98 """Default action for add_users subcommand.""" 

99 add_users( 

100 read_student_repo_info_from_config_file(args.student_repo_info_file.name), 

101 Path(args.student_group_info_file.name), 

102 create_gitlab_instance(args.gitlab_token), 

103 ) 

104 

105 

106def parse_arguments(arguments: list[str]) -> Namespace: 

107 """Parse CLI arguments.""" 

108 # pylint: disable=too-many-locals 

109 

110 parser = ArgumentParserFactory.default_parser(__doc__).parser 

111 subparsers = parser.add_subparsers(title="actions", dest="actions", help="sub-command help", required=True) 

112 

113 # Common arguments 

114 factory = ArgumentParserFactory.parent_parser() 

115 factory.add_student_repo_info_file() 

116 factory.add_gitlab_token() 

117 

118 # Create issues parser 

119 create_issue_factory = factory.copy() 

120 create_issue_factory.add_issue_md_slide() 

121 create_issue_factory.add_homework_number() 

122 create_issue_factory.add_due_date() 

123 parser_issues = subparsers.add_parser( 

124 "create_issues", 

125 parents=[create_issue_factory.parser], 

126 formatter_class=ArgumentDefaultsHelpFormatter, 

127 description="Create Gitlab issues from homework slides", 

128 help="Create Gitlab issues from homework slides", 

129 ) 

130 parser_issues.set_defaults(func=edit_create_issues) 

131 

132 # Comment issues parser 

133 comment_issue_factory = factory.copy() 

134 comment_issue_factory.add_issue_number() 

135 comment_issue_factory.add_message("Message or path to a .md file") 

136 comment_issue_factory.add_state_event() 

137 parser_comment = subparsers.add_parser( 

138 "comment_issue", 

139 parents=[comment_issue_factory.parser], 

140 formatter_class=ArgumentDefaultsHelpFormatter, 

141 description="Comment to Gitlab issues via message or markdown slides", 

142 help="Comment to Gitlab issues via message or markdown slides", 

143 ) 

144 parser_comment.set_defaults(func=edit_comment_issue) 

145 

146 # Fetch code parser 

147 fetch_code_factory = factory.copy() 

148 fetch_code_factory.add_workspace() 

149 parser_fetch = subparsers.add_parser( 

150 "fetch_code", 

151 parents=[fetch_code_factory.parser], 

152 formatter_class=ArgumentDefaultsHelpFormatter, 

153 description="Fetch (clone or pull) Gitlab repositories", 

154 help="Fetch (clone or pull) Gitlab repositories", 

155 ) 

156 parser_fetch.set_defaults(func=edit_fetch_code) 

157 

158 # Evaluate code parser 

159 evaluate_code_factory = factory.copy() 

160 evaluate_code_factory.add_homework_number() 

161 evaluate_code_factory.add_job_factory_path() 

162 evaluate_code_factory.add_workspace() 

163 evaluate_code_factory.add_date_sine_last_homework() 

164 evaluate_code_factory.add_evaluation_date() 

165 parser_evaluate = subparsers.add_parser( 

166 "evaluate_code", 

167 parents=[evaluate_code_factory.parser], 

168 formatter_class=ArgumentDefaultsHelpFormatter, 

169 description="Fetch (clone or pull) Gitlab repositories and evaluate code", 

170 help="Fetch (clone or pull) Gitlab repositories and evaluate code", 

171 ) 

172 parser_evaluate.set_defaults(func=edit_evaluate_code) 

173 

174 # Upload files parser 

175 upload_files_factory = factory.copy() 

176 upload_files_factory.add_source_folder(None) 

177 parser_upload_files = subparsers.add_parser( 

178 "upload_files", 

179 parents=[upload_files_factory.parser], 

180 formatter_class=ArgumentDefaultsHelpFormatter, 

181 description="Upload files via commit to Gitlab from source folder", 

182 help="Upload files via commit to Gitlab from source folder", 

183 ) 

184 parser_upload_files.set_defaults(func=edit_upload_files) 

185 

186 # Commit changes parser 

187 commit_changes_factory = factory.copy() 

188 commit_changes_factory.add_source_folder(None) 

189 commit_changes_factory.add_message("Commit message used for all repos") 

190 commit_changes_factory.add_workspace() 

191 commit_changes_factory.add_keep_solutions() 

192 parser_commit_changes = subparsers.add_parser( 

193 "commit_changes", 

194 parents=[commit_changes_factory.parser], 

195 formatter_class=ArgumentDefaultsHelpFormatter, 

196 description="Copy source folder to workspace and commit the changes", 

197 help="Copy source folder to workspace and commit the changes", 

198 ) 

199 parser_commit_changes.set_defaults(func=edit_commit_changes) 

200 

201 # Add users parser 

202 add_users_factory = factory.copy() 

203 add_users_factory.add_student_group_info_file() 

204 parser_add_users = subparsers.add_parser( 

205 "add_users", 

206 parents=[add_users_factory.parser], 

207 formatter_class=ArgumentDefaultsHelpFormatter, 

208 description="Add all users to their respective repositories", 

209 help="Add all users to their respective repositories", 

210 ) 

211 parser_add_users.set_defaults(func=edit_add_users) 

212 

213 return parser.parse_args(arguments[1:]) 

214 

215 

216def main() -> None: 

217 """Main.""" 

218 args = parse_arguments(sys.argv) 

219 args.func(args) 

220 

221 

222if __name__ == "__main__": 

223 main()