Coverage for tools / sel_tools / utils / comment.py: 100%
46 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-21 08:53 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-21 08:53 +0000
1"""Comment module."""
3import re
4from copy import deepcopy
5from dataclasses import dataclass, field, replace
6from pathlib import Path
8from sel_tools.file_parsing.slide_parser import get_attachments
11@dataclass
12class Comment:
13 """Issue Comment."""
15 issue_id: int
16 message: str
17 state_event: str | None = None
18 attachments: list[Path] = field(default_factory=list)
20 @staticmethod
21 def create(issue_id: int, message_or_file_path: str, state_event: str | None) -> "Comment":
22 file_path = Path(message_or_file_path)
23 message = file_path.read_text() if file_path.is_file() else message_or_file_path
24 return Comment(issue_id, message, state_event, get_attachments(message))
27class ProjectCommentParser:
28 """Parse and validate project specific comments from a Comment."""
30 PROJECT_COMMENT_IDENTIFIER_PREFIX = "## Comments for Project"
31 PROJECT_COMMENT_FOOTER = "---"
33 def __init__(self, comment: Comment, student_project_ids: list[int]) -> None:
34 self.__is_same_comment_for_all_projects = True
35 if self.PROJECT_COMMENT_IDENTIFIER_PREFIX in comment.message:
36 has_project_ids = [
37 f"{self.PROJECT_COMMENT_IDENTIFIER_PREFIX} {project_id}" in comment.message
38 for project_id in student_project_ids
39 ]
40 prefix_count = comment.message.count(self.PROJECT_COMMENT_IDENTIFIER_PREFIX)
41 footer_count = comment.message.count(self.PROJECT_COMMENT_IDENTIFIER_PREFIX)
42 if not (all(has_project_ids) and len(has_project_ids) == prefix_count == footer_count):
43 msg = "No exact overlap for project IDs in comment text and student project config!"
44 raise LookupError(msg)
45 self.__is_same_comment_for_all_projects = False
46 self.__comment = comment
47 self.__student_project_ids = student_project_ids
49 @property
50 def is_same_comment_for_all_projects(self) -> bool:
51 return self.__is_same_comment_for_all_projects
53 def get_comment_for_project(self, project_id: int) -> Comment:
54 if self.is_same_comment_for_all_projects:
55 return deepcopy(self.__comment)
57 if project_id not in self.__student_project_ids:
58 msg = f"Project ID {project_id} not in list of expect project IDs."
59 raise LookupError(msg)
61 project_comment_pattern = (
62 rf"{self.PROJECT_COMMENT_IDENTIFIER_PREFIX} {project_id}\n(.*?)\n{self.PROJECT_COMMENT_FOOTER}"
63 )
64 matches = re.findall(project_comment_pattern, self.__comment.message, re.DOTALL)
65 if matches:
66 return replace(self.__comment, message=matches[0])
68 msg = "Invalid project specific comment."
69 raise LookupError(msg)