File size: 2,447 Bytes
12bf126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
"""Render a GradeReport as color-coded HTML for the Gradio Space.

This is the presentation layer over the deterministic grader in
space.ocr.grading (spec §7): the diff and score are computed there; here we
only turn the report into spans the learner can read. Pure, no network.
"""

import html

try:  # package context (space.diff_html, e.g. tests at repo root)
    from .ocr.grading import GradeReport
except ImportError:  # flat context (HF Space root, app.py)
    from ocr.grading import GradeReport

# Inline styles (Gradio sanitizes <style> blocks; inline survives).
_CORRECT = "color:#16794a;"
_WRONG = "color:#c0392b;font-weight:600;"
_MISSING = "color:#b9770e;font-style:italic;"
_EXTRA = "color:#8e44ad;font-style:italic;text-decoration:line-through;"


def _verdict(accuracy: float) -> str:
    if accuracy >= 0.95:
        return "Excellent"
    if accuracy >= 0.8:
        return "Good"
    if accuracy >= 0.6:
        return "Keep practicing"
    return "Needs work"


def render_report_html(report: GradeReport) -> str:
    """Color-coded word-level diff plus a score header (spec §7)."""
    s = report.summary
    score = round(s.accuracy * 100)

    spans: list[str] = []
    for w in report.words:
        if w.status == "correct":
            spans.append(f'<span style="{_CORRECT}">{html.escape(w.read or "")}</span>')
        elif w.status == "misspelled":
            wrote = html.escape(w.read or "")
            correct = html.escape(w.expected or "")
            spans.append(
                f'<span style="{_WRONG}" title="{html.escape(w.diff or "")}">'
                f"{wrote}{correct}</span>"
            )
        elif w.status == "missing":
            spans.append(
                f'<span style="{_MISSING}">[missing: {html.escape(w.expected or "")}]</span>'
            )
        else:  # extra
            spans.append(
                f'<span style="{_EXTRA}">[extra: {html.escape(w.read or "")}]</span>'
            )

    body = " ".join(spans) if spans else "<em>No text recognized.</em>"

    return (
        f'<div style="font-size:1.05rem;line-height:1.7;">'
        f'<p style="font-size:1.2rem;margin:0 0 .4rem;">'
        f"<strong>{score}%</strong> &middot; {_verdict(s.accuracy)} "
        f'<span style="color:#666;">({s.correct}/{s.total} correct, '
        f"misspelled {s.misspelled}, missing {s.missing}, extra {s.extra})</span></p>"
        f"<p>{body}</p>"
        f"</div>"
    )