from __future__ import annotations import re import unicodedata from html import escape from urllib.parse import quote from .models import ScanReport INVALID_FILENAME_CHARS = re.compile(r'[\x00-\x1f\x7f<>:"/\\|?*]') REPORT_MARK = """""" def safe_report_filename(repository_name: str, extension: str) -> str: cleaned = INVALID_FILENAME_CHARS.sub("_", repository_name).strip(" .") cleaned = cleaned[:100].rstrip(" .") or "repository" return f"repopulse-{cleaned}.{extension}" def content_disposition(filename: str) -> str: fallback = unicodedata.normalize("NFKD", filename).encode("ascii", "ignore").decode("ascii") fallback = INVALID_FILENAME_CHARS.sub("_", fallback).strip(" .") or "repopulse-report" return f"attachment; filename=\"{fallback}\"; filename*=UTF-8''{quote(filename, safe='')}" def render_html_report(report: ScanReport) -> str: category_rows = "".join( f"
Deterministic repository intelligence report
{escape(report.repository_path)} · {escape(report.scanned_at.isoformat())}
| Path | Language | Score | LOC | Churn |
|---|
| Severity | Category | Finding | Location |
|---|