Spaces:
Running on Zero
Running on Zero
File size: 4,480 Bytes
f8d4986 d5999d0 f8d4986 | 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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | """Export a forged case+note to downloadable files.
Per the MVP cut-lines, PDF is optional and Markdown is the floor. We ship two
zero-dependency formats: a clean **Markdown** file (the canonical artifact) and a
self-contained **printable HTML** the instructor can open and Print-to-PDF —
covering the PDF use case without a PDF toolchain in the Space.
"""
from __future__ import annotations
import re
import tempfile
from pathlib import Path
def _slug(title: str) -> str:
s = re.sub(r"[^\w\s-]", "", (title or "case").lower()).strip()
s = re.sub(r"[\s_-]+", "-", s)
return (s or "case")[:60]
def _combined_markdown(case_md: str, note_md: str) -> str:
return f"{(case_md or '').strip()}\n\n---\n\n{(note_md or '').strip()}\n"
def _md_inline(text: str) -> str:
"""Minimal inline Markdown → HTML: **bold** and escaping."""
import html
text = html.escape(text)
return re.sub(r"\*\*(.+?)\*\*", r"<strong>\1</strong>", text)
def _md_to_html_body(md: str) -> str:
"""Tiny Markdown→HTML for our own controlled output (headings, lists, quotes)."""
lines = md.splitlines()
html_lines: list[str] = []
list_type: str | None = None # 'ul' | 'ol'
def close_list():
nonlocal list_type
if list_type:
html_lines.append(f"</{list_type}>")
list_type = None
for raw in lines:
line = raw.rstrip()
if not line.strip():
close_list()
continue
if line.startswith("# "):
close_list(); html_lines.append(f"<h1>{_md_inline(line[2:])}</h1>")
elif line.startswith("## "):
close_list(); html_lines.append(f"<h2>{_md_inline(line[3:])}</h2>")
elif line.startswith("> "):
close_list(); html_lines.append(f"<blockquote>{_md_inline(line[2:])}</blockquote>")
elif line.strip() == "---":
close_list(); html_lines.append("<hr>")
elif line.startswith("- "):
if list_type != "ul":
close_list(); html_lines.append("<ul>"); list_type = "ul"
html_lines.append(f"<li>{_md_inline(line[2:])}</li>")
elif re.match(r"^\d+\.\s", line):
if list_type != "ol":
close_list(); html_lines.append("<ol>"); list_type = "ol"
item = re.sub(r"^\d+\.\s", "", line) # backslash kept out of the f-string (py3.10)
html_lines.append(f"<li>{_md_inline(item)}</li>")
else:
close_list(); html_lines.append(f"<p>{_md_inline(line)}</p>")
close_list()
return "\n".join(html_lines)
_HTML_SHELL = """<!doctype html>
<html lang="{lang}"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{title}</title>
<style>
body {{ font-family: Georgia, "Times New Roman", serif; max-width: 760px;
margin: 40px auto; padding: 0 24px; color: #1a1a2e; line-height: 1.55; }}
h1 {{ font-size: 1.7rem; border-bottom: 2px solid #6d5dfc; padding-bottom: 6px; }}
h2 {{ font-size: 1.15rem; color: #4a3fb0; margin-top: 1.6em; }}
blockquote {{ border-left: 4px solid #b16cea; margin: 1em 0; padding: .4em 1em;
background: #f5f2ff; font-style: italic; }}
hr {{ border: none; border-top: 1px dashed #ccc; margin: 2.5em 0; }}
ul, ol {{ padding-left: 1.4em; }}
li {{ margin: .25em 0; }}
footer {{ margin-top: 3em; font-size: .8rem; color: #888; text-align: center;
font-family: sans-serif; }}
@media print {{ body {{ margin: 0; max-width: none; }} }}
</style></head>
<body>
{body}
<footer>Forged with Case Forge · Build Small Hackathon</footer>
</body></html>
"""
def to_markdown_file(case_md: str, note_md: str, title: str = "case") -> str:
"""Write the (possibly edited) case+note Markdown to a downloadable file."""
path = Path(tempfile.gettempdir()) / f"{_slug(title)}.md"
path.write_text(_combined_markdown(case_md, note_md), encoding="utf-8")
return str(path)
def to_html_file(case_md: str, note_md: str, title: str = "case",
lang: str = "pt") -> str:
"""Render the (possibly edited) Markdown into a self-contained printable HTML."""
body = _md_to_html_body(_combined_markdown(case_md, note_md))
doc = _HTML_SHELL.format(lang=lang, title=title or "Case", body=body)
path = Path(tempfile.gettempdir()) / f"{_slug(title)}.html"
path.write_text(doc, encoding="utf-8")
return str(path)
__all__ = ["to_markdown_file", "to_html_file"]
|