Spaces:
Sleeping
Sleeping
| """HTML export for visual inspection of MathVision-like records.""" | |
| from __future__ import annotations | |
| from html import escape | |
| from pathlib import Path | |
| from mathvision_explorer.dataset import MathVisionRecord | |
| def export_html(records: list[MathVisionRecord], output: Path) -> None: | |
| """Write a standalone HTML gallery for records.""" | |
| output.parent.mkdir(parents=True, exist_ok=True) | |
| cards = "\n".join(_render_card(record, output_dir=output.parent) for record in records) | |
| html = f"""<!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>MathVision Explorer</title> | |
| <style> | |
| body {{ | |
| margin: 0; | |
| font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| color: #172033; | |
| background: #f7f8fb; | |
| }} | |
| header {{ | |
| padding: 28px 32px 18px; | |
| background: #ffffff; | |
| border-bottom: 1px solid #dde3ee; | |
| }} | |
| h1 {{ | |
| margin: 0 0 6px; | |
| font-size: 28px; | |
| font-weight: 750; | |
| }} | |
| main {{ | |
| display: grid; | |
| gap: 18px; | |
| grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); | |
| padding: 24px 32px 36px; | |
| }} | |
| article {{ | |
| overflow: hidden; | |
| border: 1px solid #d8deea; | |
| border-radius: 8px; | |
| background: #ffffff; | |
| }} | |
| img {{ | |
| display: block; | |
| width: 100%; | |
| aspect-ratio: 3 / 2; | |
| object-fit: contain; | |
| background: #eef2f7; | |
| }} | |
| .body {{ | |
| padding: 16px; | |
| }} | |
| .meta {{ | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 8px; | |
| margin-bottom: 12px; | |
| font-size: 13px; | |
| }} | |
| .tag {{ | |
| padding: 3px 8px; | |
| border: 1px solid #cfd7e6; | |
| border-radius: 999px; | |
| background: #f4f7fb; | |
| }} | |
| h2 {{ | |
| margin: 0 0 12px; | |
| font-size: 16px; | |
| line-height: 1.35; | |
| }} | |
| p {{ | |
| margin: 8px 0 0; | |
| line-height: 1.45; | |
| }} | |
| .answer {{ | |
| font-weight: 700; | |
| }} | |
| </style> | |
| </head> | |
| <body> | |
| <header> | |
| <h1>MathVision Explorer</h1> | |
| <div>{len(records)} visual math records</div> | |
| </header> | |
| <main> | |
| {cards} | |
| </main> | |
| </body> | |
| </html> | |
| """ | |
| output.write_text(html, encoding="utf-8") | |
| def _render_card(record: MathVisionRecord, *, output_dir: Path) -> str: | |
| image_html = "" | |
| if record.image_path is not None: | |
| image_src = _relative_or_absolute_image(record.image_path, output_dir=output_dir) | |
| image_html = f' <img src="{escape(image_src)}" alt="{escape(record.problem_id)}">\n' | |
| meta = [_tag(record.problem_id)] | |
| if record.subject is not None: | |
| meta.append(_tag(record.subject)) | |
| if record.level is not None: | |
| meta.append(_tag(f"level {record.level}")) | |
| options = "" | |
| if record.options: | |
| options = f"<p>Options: {escape(', '.join(record.options))}</p>" | |
| solution = "" | |
| if record.solution: | |
| solution = f"<p>{escape(record.solution)}</p>" | |
| return f""" <article> | |
| {image_html} <div class="body"> | |
| <div class="meta">{''.join(meta)}</div> | |
| <h2>{escape(record.question)}</h2> | |
| <p class="answer">Answer: {escape(record.answer)}</p> | |
| {options} | |
| {solution} | |
| </div> | |
| </article>""" | |
| def _tag(value: str) -> str: | |
| return f'<span class="tag">{escape(value)}</span>' | |
| def _relative_or_absolute_image(image_path: Path, *, output_dir: Path) -> str: | |
| try: | |
| return image_path.resolve().relative_to(output_dir.resolve()).as_posix() | |
| except ValueError: | |
| return image_path.resolve().as_uri() | |