Coverage for tools/sel_tools/diff_creation/report.py: 89%
47 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"""Git diff report."""
3from dataclasses import dataclass
4from pathlib import Path
6import pandas as pd
7from pygments import highlight
8from pygments.formatters import HtmlFormatter
9from pygments.lexers.diff import DiffLexer
11MD_WARNING_REPORT = """# Inactive Student Repositories
13The following student repositories have not been updated since the last homework evaluation:
15%s
17Please check if the students are still active.
18If not, remove them from the course and the repo config for the current semester.
19The latter to make sure they are not evaluated again next time.
20"""
23@dataclass
24class Diff:
25 """Git diff model."""
27 hexsha: str
28 author: str
29 message: str
30 patch: str
33@dataclass
34class DiffReport:
35 """Git diff model."""
37 def __init__(self, repo_path: Path, diffs: list[Diff]) -> None:
38 self.repo_path = repo_path
39 self.diffs = diffs
41 @property
42 def has_diffs(self) -> bool:
43 return bool(self.diffs)
45 def generate_overview_table(self) -> pd.DataFrame:
46 return pd.DataFrame(
47 [
48 {
49 "hexsha": diff.hexsha,
50 "author": diff.author,
51 "message": diff.message,
52 }
53 for diff in self.diffs
54 ]
55 )
57 def write_diff_patches(self) -> None:
58 for index, diff in enumerate(self.diffs):
59 base_path = self.repo_path / f"{index}-{diff.hexsha}"
60 try:
61 base_path.with_suffix(".patch").write_text(diff.patch, encoding="utf-8", errors="replace")
62 base_path.with_suffix(".html").write_text(
63 self.highlight_diff(diff.patch), encoding="utf-8", errors="replace"
64 )
65 except UnicodeError:
66 print("UnicodeError: Fallback: clean the patch text by encoding/decoding to remove surrogates")
67 clean_patch = diff.patch.encode("utf-8", errors="replace").decode("utf-8", errors="replace")
68 base_path.with_suffix(".patch").write_text(clean_patch)
69 base_path.with_suffix(".html").write_text(self.highlight_diff(clean_patch))
71 @staticmethod
72 def highlight_diff(patch: str) -> str:
73 return str(highlight(patch, DiffLexer(), HtmlFormatter(full=True, style="manni")))
76def write_diff_reports(reports: list[DiffReport], report_base_name: str) -> None:
77 """Write diff reports to disk."""
78 for report in reports:
79 print(f"Writing diff report for {report.repo_path.name}")
80 report.generate_overview_table().to_csv(report.repo_path.joinpath(report_base_name).with_suffix(".csv"))
81 report.write_diff_patches()
84def write_report_for_inactive_student_repos(reports: list[DiffReport], workspace: Path) -> None:
85 """Check for repos that don't have any diffs since the last homework and write them to a file."""
86 inactive_repos = [f"- {report.repo_path.name}" for report in reports if not report.has_diffs]
87 print(f"Found {len(inactive_repos)} inactive student repositories.")
88 if inactive_repos:
89 workspace.joinpath("inactive_student_repos.md").write_text(MD_WARNING_REPORT % "\n".join(inactive_repos))