docs: 태하 리포트 단일 HTML 내보내기 + export_html.py (링·진행바 점수 고정)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
9ca5c593b8
commit
58fb4a317d
58
docs/reports/Taeha_AI_Answer_Readiness_Report.html
Normal file
58
docs/reports/Taeha_AI_Answer_Readiness_Report.html
Normal file
File diff suppressed because one or more lines are too long
69
docs/reports/export_html.py
Normal file
69
docs/reports/export_html.py
Normal 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}")
|
||||
Loading…
Reference in New Issue
Block a user