docs: 태하 리포트 단일 HTML 내보내기 + export_html.py (링·진행바 점수 고정)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Haewon Kam 2026-08-28 11:51:50 +09:00
parent 9ca5c593b8
commit 58fb4a317d
2 changed files with 127 additions and 0 deletions

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,69 @@
"""
/discovery/:id 리포트 페이지를 단일 HTML 파일로 내보낸다.
npm run build && npx vite preview --port 3012
python3 docs/reports/export_html.py taeha docs/reports/Taeha_AI_Answer_Readiness_Report.html
- 헤드리스 Chrome으로 렌더된 DOM을 덤프하고 빌드 CSS를 인라인한다
- Navbar·Footer·스크립트 제거, motion 애니메이션 고정
- ScoreRing 오프셋과 카테고리 진행 폭을 점수 텍스트에서 다시 계산한다
"""
import glob, re, subprocess, sys
CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
BASE = "http://localhost:3012"
slug, out = sys.argv[1], sys.argv[2]
title = sys.argv[3] if len(sys.argv) > 3 else f"{slug} AI Answer Readiness Report"
dom = subprocess.run(
[CHROME, "--headless=new", "--disable-gpu", "--window-size=1440,900", "--virtual-time-budget=20000",
"--dump-dom", f"{BASE}/discovery/{slug}"],
capture_output=True, text=True).stdout
css = open(glob.glob("dist/assets/*.css")[0], encoding="utf-8").read()
m = re.search(r'<div id="root">(.*)</div>\s*</body>', dom, re.S)
body = m.group(1)
body = re.sub(r"<script.*?</script>", "", body, flags=re.S)
body = re.sub(r"<nav\b.*?</nav>", "", body, flags=re.S, count=1)
body = re.sub(r"<footer\b.*?</footer>", "", body, flags=re.S)
# ScoreRing: dashoffset = circumference * (1 - score/100)
def fix_ring(mm):
circ = float(mm.group(2)); score = int(mm.group(5))
return f"{mm.group(1)}{circ * (1 - min(score, 100) / 100):.4f}{mm.group(4)}{score}{mm.group(6)}"
body, n_ring = re.subn(
r'(stroke-dasharray="([\d.]+)"\s+stroke-dashoffset=")([\d.]+)("></circle></svg><div class="absolute inset-0 flex items-center justify-center"><span[^>]*>)(\d+)(</span>)',
fix_ring, body)
# 카테고리 바: <div class="h-full rounded-full" style="background: X; width: 0%;"> … <span class="font-semibold">NN%</span>
def fix_bar(mm):
return f'{mm.group(1)}width: {mm.group(3)}%;{mm.group(4)}{mm.group(3)}%{mm.group(5)}'
body, n_bar = re.subn(
r'(<div class="h-full rounded-full" style="[^"]*?)width:\s*[\d.]+%;?([^"]*"[^>]*></div></div><div class="flex justify-between mt-2 text-sm"><span class="font-semibold">)(\d+)(%</span>)',
lambda mm: f'{mm.group(1)}width: {mm.group(3)}%;{mm.group(2)}{mm.group(3)}{mm.group(4)}', body)
html = f"""<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,900;1,400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css">
<style>{css}</style>
<style>
[style*="opacity"], [style*="transform"] {{ opacity: 1 !important; transform: none !important; }}
html, body {{ background: #fff; }}
</style>
</head>
<body>
<div class="min-h-screen bg-white">
{body}
</div>
</body>
</html>"""
open(out, "w", encoding="utf-8").write(html)
print(f"saved {out} ({len(html):,} bytes) rings={n_ring} bars={n_bar}")