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
« prev ^ index » next coverage.py v7.10.6, created at 2025-09-02 05:55 +0000
1"""Code evaluation report."""
3import json
4from dataclasses import dataclass
5from pathlib import Path
6from typing import Any
8from sel_tools.utils.repo import GitlabProject
10MD_EVALUATION_REPORT = """# Homework Evaluation Report
12[Repo]({repo_url})
14Overall:
16## Auto Evaluation
18```json
19{evaluation_json}
20```
22## Manual Evaluation
24Use this section for notes when evaluating the code manually.
26## Student Section
28The content of this section and below of this sentence can be shared with the students:
30{student_section}
32"""
34STUDENT_SECTION_TEMPLATE = """Overall score: {score}/{max_score}
36If available, below are a few notes about your code:
37Please note that not all of them are errors.
39{notes}
40"""
43@dataclass(frozen=True)
44class EvaluationResult:
45 """Evaluation result."""
47 name: str
48 score: int
49 max_score: int
50 comment: str = ""
53@dataclass
54class EvaluationReport:
55 """Evaluation report."""
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
64 def to_json(self) -> str:
65 class JsonEncoder(json.JSONEncoder):
66 """Evaluation report json encoder."""
68 def default(self, o: Any) -> str | Any:
69 if isinstance(o, Path):
70 return str(o)
71 return o.__dict__
73 return json.dumps(self, cls=JsonEncoder, indent=4)
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 )
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 )
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())