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

1"""Comment module.""" 

2 

3import re 

4from copy import deepcopy 

5from dataclasses import dataclass, field, replace 

6from pathlib import Path 

7 

8from sel_tools.file_parsing.slide_parser import get_attachments 

9 

10 

11@dataclass 

12class Comment: 

13 """Issue Comment.""" 

14 

15 issue_id: int 

16 message: str 

17 state_event: str | None = None 

18 attachments: list[Path] = field(default_factory=list) 

19 

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)) 

25 

26 

27class ProjectCommentParser: 

28 """Parse and validate project specific comments from a Comment.""" 

29 

30 PROJECT_COMMENT_IDENTIFIER_PREFIX = "## Comments for Project" 

31 PROJECT_COMMENT_FOOTER = "---" 

32 

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 

48 

49 @property 

50 def is_same_comment_for_all_projects(self) -> bool: 

51 return self.__is_same_comment_for_all_projects 

52 

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) 

56 

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) 

60 

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]) 

67 

68 msg = "Invalid project specific comment." 

69 raise LookupError(msg)