Files
C.E.L_Slide_test2/scripts/verify_definitions_v2.py
kyeongmin 29f56187c0 Phase P~S 전체 작업물: 검증 스크립트, 블록 템플릿, 설계 문서, 코드 수정
포함 내용:
- Phase P/Q/R/S 설계 문서 (IMPROVEMENT-PHASE-*.md)
- 영역별 검증 스크립트 (scripts/verify_*.py, test_*.py)
- 블록 템플릿 추가 (cards, emphasis 변형)
- 코드 수정: block_search, content_editor, design_director, slide_measurer
- catalog.yaml 블록 목록 업데이트
- CLAUDE.md, PROGRESS.md, README.md 업데이트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 08:38:06 +09:00

176 lines
7.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""검증 A: 용어 정의 재검증 + 검증 B: 본심 (이미지+텍스트+팝업 표)
용어 정의: 참고 이미지 수준 — 부제 + 불릿 2개 + 원본 텍스트 거의 그대로
본심: dx1.png 이미지 + DX vs BIM 관계 텍스트 + 비교표는 details/summary 팝업
"""
from __future__ import annotations
import asyncio, sys, time, datetime, base64, re
from pathlib import Path
ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(ROOT))
async def main():
from src.slide_measurer import capture_slide_screenshot
from src.config import settings
import anthropic
out_dir = ROOT / "data" / "runs" / f"verify_v2_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}"
out_dir.mkdir(parents=True, exist_ok=True)
client = anthropic.AsyncAnthropic(api_key=settings.anthropic_api_key)
t0 = time.time()
# ═══════════════════════════════════════
# 검증 A: 용어 정의 (참고 이미지 수준)
# ═══════════════════════════════════════
print("=== 검증 A: 용어 정의 (참고 이미지 수준) ===")
prompt_a = """다음 3개 용어 정의를 sidebar 카드로 만들어라. 380px × 490px.
## 용어 (원본 텍스트를 한 글자도 바꾸지 말고 그대로 사용)
### BIM (Building Information Modeling) : 디지털 전환을 위한 핵심 기술
- 시설물의 생애주기동안 발생한 모든 정보를 3차원 모델 기반으로 통합·관리하는 정보 관리 도구
- 건설 정보와 절차를 표준화된 방식으로 연계하고 디지털 협업이 가능하도록 하는 핵심 인프라 기술
### 건설산업
- 다양한 시설물을 각 산업마다의 광범위한 기술을 통합 및 융합하여 만들어내는 종합산업
- 목적 시설물의 품질 욕구를 충족시키면서 최단기간 내에 최소 비용으로 편리하고 안전하며 우수한 성능의 시설물 완성을 목표로 함
### 디지털전환 (DX, Digital Transformation) : 산업 패러다임의 변화
- 디지털 기술을 기반으로 산업 전반의 업무 방식과 가치 창출 구조를 전환하는 과정 및 결과
- 단순한 기술 도입이 아닌, 고객 가치와 의사결정 방식의 근본적인 변화로 산업의 새로운 방향을 정립하는 것을 의미
## 디자인 요구사항
1. 상단에 "용어 정의" 구분선 라벨 (좌우 선 + 중앙 텍스트, 13px #64748b)
2. 각 용어를 카드로:
- 배경: #f8fafc, 테두리: 1px solid #e2e8f0, border-radius: 8px, padding: 14px
- 용어명: 14px bold #1e293b (예: "BIM (Building Information Modeling)")
- 부제: 12px #2563eb (예: ": 디지털 전환을 위한 핵심 기술")
- 불릿: 12px #475569, line-height: 1.6, 불릿 마커 ""
- 각 불릿은 원본 텍스트 그대로
3. 카드 간 간격 10px
4. 490px 안에 여유 있게 배치
HTML + inline <style>만 반환. 설명 없이 코드만."""
html_a = await _call(client, prompt_a)
if html_a:
wrapped = _wrap(html_a, 380)
(out_dir / "A_definitions.html").write_text(wrapped, encoding="utf-8")
s = await asyncio.to_thread(capture_slide_screenshot, wrapped)
if s:
(out_dir / "A_definitions.png").write_bytes(base64.b64decode(s))
print(f" [{time.time()-t0:.0f}s] 완료")
# ═══════════════════════════════════════
# 검증 B: 본심 (이미지 + 텍스트 + 팝업 표)
# ═══════════════════════════════════════
print("\n=== 검증 B: 본심 (이미지+텍스트+팝업표) ===")
prompt_b = """다음 콘텐츠를 본심 영역 HTML로 만들어라. 707px × 293px.
## 구조 (정확히 이 구조를 따르라)
1. 제목: "DX와 핵심기술의 올바른 관계" (14px bold #2563eb 가운데)
2. 좌우 2단 레이아웃:
- 왼쪽 (50%): 이미지
<img src="/assets/images/dx1.png" style="width:100%; border-radius:8px; border:1px solid #e2e8f0;">
- 오른쪽 (50%): DX vs BIM 핵심 차이 텍스트
DX (상위개념):
• 기술융합을 통해서만 실현 가능한 상위개념
• Engineering + Management 통합
• 전 생애주기 활용 시스템
• 자체 수행 능력 — 지속가능성 확보
BIM (하위기술):
• Only 3D (형상 구현 중심)
• 기존 2D 설계 방식 유지
• (설계/시공/운영) 분야별 단절
• S/W 제작사 판매 정책에 의존
3. 이미지+텍스트 아래에 <details>/<summary> 팝업:
<summary>📊 DX vs BIM 상세 비교표 보기</summary>
펼치면 표가 보임:
| 기준 | DX | BIM |
| 범위 | BIM << DX (Engineering + Management 통합) | Only 3D (형상 구현 중심) |
| 프로세스 | 근본적 문제의식을 통한 개선 | 기존 2D 설계 방식 유지 |
| 활용 | 설계/시공 생산성 혁신 | 3D 모델에 의한 일반적 이해 향상 |
| 확장성 | 전 생애주기 활용 시스템 | (설계/시공/운영) 분야별 단절 |
| 주체 | 자체 수행 능력 — 지속가능성 확보 | S/W 제작사 판매 정책에 의존 |
4. 맨 아래에 핵심 메시지:
background: #f0f9ff, border: 2px solid #bae6fd, border-radius: 8px, padding: 8px, text-align: center
"BIM ≠ DX — BIM은 DX를 실현하기 위한 핵심 기술 중 하나일 뿐이다"
"BIM ≠ DX" 부분: color: #dc2626, font-weight: 900
## 디자인
- DX 항목 제목: 13px bold #2563eb
- BIM 항목 제목: 13px bold #64748b
- 불릿: 11px #475569
- 표 헤더: background: #1e293b, color: white
- 표 셀: 10px, border-bottom: 1px solid #e2e8f0
- <summary>: cursor: pointer, 12px bold #2563eb
- 이미지가 안 보이면 placeholder 박스(회색 배경 + "DX 관계도" 텍스트)로 대체
HTML + inline <style>만 반환. 설명 없이 코드만."""
html_b = await _call(client, prompt_b)
if html_b:
wrapped = _wrap(html_b, 707)
(out_dir / "B_core.html").write_text(wrapped, encoding="utf-8")
s = await asyncio.to_thread(capture_slide_screenshot, wrapped)
if s:
(out_dir / "B_core.png").write_bytes(base64.b64decode(s))
print(f" [{time.time()-t0:.0f}s] 완료")
print(f"\n총 소요: {time.time()-t0:.0f}")
print(f"결과: {out_dir}")
async def _call(client, prompt):
try:
response = await client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=8192,
messages=[{"role": "user", "content": prompt}],
)
text = response.content[0].text if response.content else ""
match = re.search(r"```html\s*(.*?)```", text, re.DOTALL)
return match.group(1).strip() if match else text.strip()
except Exception as e:
print(f" 오류: {e}")
return None
def _wrap(inner, width):
return f"""<!DOCTYPE html>
<html lang="ko"><head><meta charset="UTF-8">
<style>
@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css');
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
.slide {{
width: 1280px; height: 720px; overflow: hidden; background: white;
font-family: 'Pretendard Variable', sans-serif;
display: flex; align-items: center; justify-content: center;
}}
.test-container {{ width: {width}px; }}
</style>
</head><body>
<div class="slide"><div class="test-container">
{inner}
</div></div>
</body></html>"""
if __name__ == "__main__":
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s", datefmt="%H:%M:%S")
logging.getLogger("selenium").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)
logging.getLogger("httpx").setLevel(logging.WARNING)
asyncio.run(main())