Coverage for tools/sel_tools/code_evaluation/report.py: 100%

38 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-02 05:55 +0000

1"""Code evaluation report.""" 

2 

3import json 

4from dataclasses import dataclass 

5from pathlib import Path 

6from typing import Any 

7 

8from sel_tools.utils.repo import GitlabProject 

9 

10MD_EVALUATION_REPORT = """# Homework Evaluation Report 

11 

12[Repo]({repo_url}) 

13 

14Overall: 

15 

16## Auto Evaluation 

17 

18```json 

19{evaluation_json} 

20``` 

21 

22## Manual Evaluation 

23 

24Use this section for notes when evaluating the code manually. 

25 

26## Student Section 

27 

28The content of this section and below of this sentence can be shared with the students: 

29 

30{student_section} 

31 

32""" 

33 

34STUDENT_SECTION_TEMPLATE = """Overall score: {score}/{max_score} 

35 

36If available, below are a few notes about your code: 

37Please note that not all of them are errors. 

38 

39{notes} 

40""" 

41 

42 

43@dataclass(frozen=True) 

44class EvaluationResult: 

45 """Evaluation result.""" 

46 

47 name: str 

48 score: int 

49 max_score: int 

50 comment: str = "" 

51 

52 

53@dataclass 

54class EvaluationReport: 

55 """Evaluation report.""" 

56 

57 def __init__(self, gitlab_project: GitlabProject, results: list[EvaluationResult]) -> None: 

58 self.repo_path = gitlab_project.local_path 

59 self.url = gitlab_project.gitlab_project.web_url 

60 self.score = sum(result.score for result in set(results)) 

61 self.max_score = sum(result.max_score for result in set(results)) 

62 self.results = results 

63 

64 def to_json(self) -> str: 

65 class JsonEncoder(json.JSONEncoder): 

66 """Evaluation report json encoder.""" 

67 

68 def default(self, o: Any) -> str | Any: 

69 if isinstance(o, Path): 

70 return str(o) 

71 return o.__dict__ 

72 

73 return json.dumps(self, cls=JsonEncoder, indent=4) 

74 

75 def to_md(self) -> str: 

76 return MD_EVALUATION_REPORT.format( 

77 repo_url=self.url, evaluation_json=self.to_json(), student_section=self.print_student_section() 

78 ) 

79 

80 def print_student_section(self) -> str: 

81 return STUDENT_SECTION_TEMPLATE.format( 

82 score=self.score, 

83 max_score=self.max_score, 

84 notes="\n".join(f"- {result.comment}" for result in self.results if result.comment), 

85 ) 

86 

87 

88def write_evaluation_reports(reports: list[EvaluationReport], report_base_name: str) -> None: 

89 """Write evaluation reports to disk.""" 

90 for report in reports: 

91 report_path = report.repo_path / report_base_name 

92 report_path.with_suffix(".md").write_text(report.to_md()) 

93 report_path.with_name(f"{report_base_name}_students.md").write_text(report.print_student_section()) 

94 report_path.with_suffix(".json").write_text(report.to_json())