"""MDX 03: slide-base + prerequisites-3col(상단) + process-product-2col(하단) + pill(결론). 블록 CSS 글씨 크기 그대로. font-size override 금지. 텍스트 원문 그대로. 요약/추론 금지. """ import base64, re from pathlib import Path from jinja2 import Environment, FileSystemLoader BLOCKS = Path("templates/blocks") SVG = BLOCKS / "svg" OUT = Path("data/runs/mdx03_final") OUT.mkdir(parents=True, exist_ok=True) env = Environment(loader=FileSystemLoader(str(BLOCKS)), autoescape=False) def b64(f): p = SVG / f if not p.exists(): return "" ext = "svg+xml" if f.endswith(".svg") else "png" return f"data:image/{ext};base64," + base64.b64encode(p.read_bytes()).decode() def clean(h): return re.sub(r'', '', h, flags=re.DOTALL).strip() # ══════════════════════════════════════════ # 상단: prerequisites-3col — MDX 원문 그대로 # ══════════════════════════════════════════ class _Item: def __init__(self, heading, desc): self.heading = heading; self.desc = desc class _Col: def __init__(self, **kw): for k, v in kw.items(): setattr(self, k, v) # 블록 CSS만 가져오고, HTML은 콘텐츠 items 수에 맞게 동적 생성 # prerequisites-3col의 CSS를 추출 _p3c_raw = (BLOCKS / "new" / "prerequisites-3col.html").read_text(encoding="utf-8") _p3c_css = re.search(r'', _p3c_raw, re.DOTALL).group(0) # MDX 원문 그대로 — 각 열의 항목 수가 다름 cols_data = [ { "name": "기술", "sub": "디지털", "bar": "linear-gradient(180deg, #0D78D0 0%, #023056 100%)", "hgrad": "linear-gradient(180deg, #0D78D0 0%, #134D7F 100%)", "items": [ ("Digital 기술(S/W, H/W)과 업무 Process의 통합", [ "기존 업무 프로세스에 다양한 디지털 기술을 접목하여 업무 수행", "프로젝트 전반에 걸친 업무 프로세스의 연결 및 조율", ]), ("분야별 전문 지식(설계, 시공, 유지관리 등) 보유", [ "건설 전 단계에 대한 근본적인 이해와 지식 및 경험", "최신 토목 기술 트랜드 및 표준 기준 등에 대한 높은 지식", ]), ], }, { "name": "사람", "sub": "역량", "bar": "linear-gradient(180deg, #FF9A23 0%, #CC5200 100%)", "hgrad": "linear-gradient(180deg, #CC5200 0%, #883700 100%)", "items": [ ("혁신적 사고방식과 창의적 문제 해결 능력", [ "기존 수행 방식과 관습적 사고 등에 의한 접근 방식 탈피", "디지털 기술을 활용한 창의적, 혁신적인 솔루션 제시", ]), ("사용자 중심 사고와 DX 수행 경험", [ "사용자의 요구와 기대를 충족시키는 설계 및 구현", "시행착오를 포함한 수행 경험과 사용자 경험(UX)을 반영한 해결 방안 제시", ]), ], }, { "name": "자연", "sub": "여건", "bar": "linear-gradient(180deg, #39BE49 0%, #23742C 100%)", "hgrad": "linear-gradient(180deg, #39BE49 0%, #1E6328 100%)", "items": [ ("지속적인 투자 및 실행 의지", [ "기술 도입 초기 단계에 필요한 인력·기간·비용 등의 대규모 투자", "기술 고도화를 위한 지속적인 개선 및 투자 체계 구축", "변화와 혁신을 통해 부가가치를 창출하려는 실행 의지와 추진력", ]), ], }, ] # 동적 HTML 생성 — 블록 CSS 클래스 사용, items 수 동적 cols_html = "" for col in cols_data: items_html = "" n = len(col["items"]) for i, (heading, bullets) in enumerate(col["items"]): pct_h = int(95 / n) pct_top = int(3 + i * (95 / n)) bul = "".join(f'
• {b}
' for b in bullets) items_html += f"""
{heading}
{bul}
""" if i < n - 1: line_top = pct_top + pct_h items_html += f'
' cols_html += f"""
{col['name']}
{col['sub']}
{items_html}
""" top = f'
{cols_html}
\n{_p3c_css}' # ══════════════════════════════════════════ # 하단: process-product-2col — MDX 원문 그대로 # ══════════════════════════════════════════ # process-product-2col: 블록 CSS 사용 + HTML은 좌우 소제목이 같은 행에 오도록 Grid 재구성 _pp2_raw = (BLOCKS / "BEPs" / "process-product-2col.html").read_text(encoding="utf-8") _pp2_css = re.search(r'', _pp2_raw, re.DOTALL).group(0) arrow_uri = b64("arrow_asis_tobe.png") # 좌우 소제목을 행 단위로 대응시킴 # 행1: Analogue 기반 업무의 Digital화 (As-is→To-be) | Copy & Paste로 인해... 품질 향상 # 행2: GIS + BIM의 연계 | Analogue 기반 도서 외 Digital 기반 정보물 추가 # 행3: 사용자 중심의 Solution 제공 | Solution을 활용한 업무 효율화 # 2열 grid + ::before로 열 배경 gradient → 행 높이 동기화 + gradient 유지 bottom = f"""
과정(Process)의 혁신
결과(Product)의 변화
Analogue 기반 업무의 Digital화
개념·문서·행정 절차 중심
2D 도면, 전문가, 규정
업무 구분(단절), 책임
→
시각화된 목적물, 소통, 투명성 중심
3D 모델, 참여자, 실체
협업(융·복합), 창의성
Copy & Paste로 인해 하향 평준화된 기존 성과물의 품질 향상
• 과거 수작업으로 시행하면서 발생하던 오류 등의 최소화
• 정확한 Data에 기반한 계획으로 고품질 성과물 도출
GIS + BIM의 연계
• 지리·지형·지반 등 위치정보(GIS)와 3D모델(형상, 속성정보) 기반의 건설 정보를 포함하는 BIM의 연계를 통한 업무 프로세스의 혁신
Analogue 기반 도서 외 Digital 기반 정보물 추가
• 기존 성과물(도면, 수량, 계산서, 시방서 등)에 3D 모델, Simulation 등의 Digital 기반 정보물 추가
사용자 중심의 Solution 제공
• 서로 다른 S/W로 작성되어 분절화된 Analogue 방식의 성과물과 정보물을 연계할 수 있는 설계·시공 Solution 제공
Solution을 활용한 업무 효율화
• Engn. Solution을 통해 성과물에 관한 이슈를 함께 검토·논의하는 협업 환경 조성
• 건설 단계별 정보를 디지털 데이터로 축적하여, 건설 전 과정을 통합관리
{_pp2_css} """ # ══════════════════════════════════════════ # slide-base 조립 # ══════════════════════════════════════════ title = "DX 실행 체계 구축 방안" ft = 'DX는 필요한 요건과 체계를 갖춘 후 시행해야만 그 효과를 기대할 수 있다' # font_hierarchy: key_msg=14, core=12, bg=12, sidebar=11 # zone제목=13px, 블록 heading=12px, 블록 desc=11px font_override = """""" body = f""" {font_override}
DX 시행을 위한 필수 요건
{top}
Process의 혁신과 Product의 변화
{bottom}
""" sb = clean((BLOCKS / "slide-base.html").read_text(encoding="utf-8")) r = sb.replace('{{ title|default("슬라이드") }}', title) r = r.replace('{{ title|default("슬라이드 제목") }}', title) r = r.replace('{% block body %}{% endblock %}', body) pill = b64("pill_scroll.png") r = r.replace('{% if footer_text %}', '').replace('{% if footer_pill_bg %}', '') r = r.replace('{{ footer_pill_bg }}', pill).replace('{% else %}', '') r = r.replace('', '') li = r.rfind('{% endif %}') if li > 0: r = r[:li] + r[li + len('{% endif %}'):] r = r.replace('{% endif %}', '').replace('{{ footer_text|safe }}', ft) r = r.replace('src="svg/bg_slide_texture.png"', f'src="{b64("bg_slide_texture.png")}"') r = r.replace('src="svg/line_divider.svg"', f'src="{b64("line_divider.svg")}"') out = OUT / "final.html" out.write_text(r, encoding="utf-8") print(f"저장: {out} ({out.stat().st_size:,} bytes)") # ══════════════════════════════════════════ # Selenium 검증 # ══════════════════════════════════════════ from selenium import webdriver from selenium.webdriver.chrome.options import Options import time options = Options() options.add_argument('--headless') options.add_argument('--window-size=1400,900') options.add_argument('--force-device-scale-factor=2') driver = webdriver.Chrome(options=options) driver.get(f"file:///{out.resolve()}") time.sleep(2) driver.save_screenshot(str(out).replace(".html", ".png")) driver.quit() # 텍스트 검증 c = re.sub(r'data:image/[^;]+;base64,[A-Za-z0-9+/=]+', 'I', r) overrides = re.findall(r'font-size.*?!important', c) j = re.findall(r'\{[%{].*?[%}]\}', c) ts = ['기술','사람','자연','디지털','역량','여건', 'Digital 기술','혁신적 사고방식','지속적인 투자', 'Process의 혁신','Product의 변화', 'Analogue','Digital','GIS','BIM','Solution', '기대할 수 있다'] m = [t for t in ts if t not in c] print(f"Jinja:{len(j)} font-override:{len(overrides)} 텍스트:{len(ts)-len(m)}/{len(ts)}") if m: print(f" MISSING: {m}") else: print(" ALL OK")