Spaces:
Running
Running
| """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> · {_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>" | |
| ) | |