Files
ai_platform/scripts/build-menu-guide-ppt.py
dsyoon 073a8343dd feat: xavis ai_platform 기능 이전 및 ncue 환경 전환
xavis 소스·DB 스키마·활용사례/F-Scan/프롬프트 라이브러리 등 기능 반영.
@xavis.co.kr → @ncue.net, 관리자 토큰 ncue-admin, 런타임 data/ Git 추적 제외.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 22:27:48 +09:00

286 lines
9.6 KiB
Python

#!/usr/bin/env python3
"""
AI Platform 메뉴 안내 PPT 생성 (python-pptx)
스크린샷: docs/ppt-screenshots/*.png
"""
from __future__ import annotations
import os
from pathlib import Path
from pptx import Presentation
from pptx.dml.color import RGBColor
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
from pptx.util import Inches, Pt
ROOT = Path(__file__).resolve().parent.parent
SHOT_DIR = ROOT / "docs" / "ppt-screenshots"
OUT_PPT = ROOT / "docs" / "XAVIS-AI-Platform-메뉴안내.pptx"
# 슬라이드 정의: (제목, 불릿 목록, 스크린샷 파일명 또는 None)
SLIDES: list[tuple[str, list[str], str | None]] = [
(
"XAVIS AI Platform 메뉴 안내",
[
"사내 AI 도구·학습·과제·활용 사례 통합 포털",
"접속: https://ai.xavis.co.kr/",
"최초 1회: @ncue.net 이메일 → 인증 메일 링크(15분 유효)",
"좌측 메뉴: 회사규정 · WM · AI · 프롬프트 · 학습센터 · 과제신청 · AI 활용 사례 · 대시보드(허용자)",
],
None,
),
(
"1. 서비스 접속 (로그인)",
[
"경로: /login — 미인증 시 자동 이동",
"회사 이메일 입력 → [검증] → 메일 [인증 완료하기] 클릭",
"인증 후 학습센터 등 원하는 화면으로 이동",
"유의: @ncue.net 만 가능 · 링크 만료 시 재검증 · 로그아웃은 좌측 하단",
],
"login.png",
),
(
"2. 회사규정",
[
"좌측 [회사규정] 클릭 → Google NotebookLM (새 탭, 회사 Google 계정 로그인)",
"사내 규정·지침 AI 검색·요약·질의응답",
"캡처: 좌측 메뉴 [회사규정] 위치 (학습센터 화면)",
"AI 답변은 원문 규정·다우오피스 공식 문서와 반드시 대조",
],
"company-policy.png",
),
(
"3. WM",
[
"좌측 [WM] 클릭 → NotebookLM WM 노트북 (새 탭)",
"WM 프로세스·용어·가이드 질의",
"캡처: 좌측 메뉴 [WM] 위치 (AI 화면)",
"공식 매뉴얼·담당 부서 확인 병행",
],
"wm.png",
),
(
"4. AI (메인 허브)",
[
"경로: /ai-explore — AI 서비스 카드 허브",
"검색 + 타입 필터(전체/일반/XScan/FScan)",
"회의록 AI · 업무 체크리스트 · 일반 채팅 · FSCAN 선정도우미",
],
"ai-explore.png",
),
(
"5. 회의록 AI",
[
"텍스트 입력: 회의 원문 붙여넣기 → 회의록 생성 → 저장",
"음성 파일: mp3·m4a·wav(최대 300MB) → 업로드→전사→회의록 정리",
"저장 시 업무 체크리스트 AI와 자동 연동",
"대안: Claude 음성 업로드 / 클로버노트 전사 → 텍스트 입력 탭 붙여넣기",
"유의: 생성 결과 검토 필수 · 개인정보·대외비 주의",
],
"meeting-minutes.png",
),
(
"6. 업무 체크리스트 AI",
[
"회의록 AI 저장분의 액션·체크리스트 통합 관리",
"진행/완료 필터 · 회의별 필터 · 완료 처리 메모",
"흐름: 회의록 AI 저장 → 체크리스트에서 추적",
],
"task-checklist.png",
),
(
"7. 일반 채팅",
[
"ChatGPT 기반 사내 인앱 채팅 (로그인 필요)",
"업무 질의·초안·아이디어 · (설정 시) 웹 검색",
"반복 프롬프트는 [프롬프트] 메뉴 템플릿 활용",
"유의: AI 답변은 실수할 수 있음 · 기밀 입력 자제",
],
"chat.png",
),
(
"8. FSCAN 조사각 선정도우미",
[
"검사 대상물 H/W 치수 입력 → FSCAN 모델 1차 선정",
"영업·기술 검토 시 빠른 선정용",
"최종 스펙은 공식 카탈로그·기술팀 확인 필수",
],
"fscan.png",
),
(
"9. 프롬프트 라이브러리",
[
"공식 템플릿: 회의 요약·이메일·보고·OKR·코드리뷰 등 → 복사",
"워크플로: 4단계 입력 → 맞춤 프롬프트 초안",
"공유하기: 팀 프롬프트·참고 파일 (기밀 제외)",
],
"prompts.png",
),
(
"10. 학습센터",
[
"YouTube · PPT/PDF · 동영상 · 웹·뉴스 링크 강의",
"카테고리: AX 사고 전환 · AI 툴 활용 · AI Agent · 바이브 코딩",
"검색 예: claude, 클로드 → 도구별 강의 찾기",
],
"learning.png",
),
(
"11. AX 과제 신청",
[
"온라인 신청 + Word 양식 다운로드",
"Pain Point · AI 기대 · 데이터·효과 등 작성",
"조회로 본인 신청 확인·수정 · AI 활용 사례 참고",
],
"ax-apply.png",
),
(
"12. AI 활용 사례",
[
"부서별 도입·성과 사례 카드 열람",
"글쓰기: STAR 형식(1.Situation~4.Result)",
"AX 과제·팀 공유 레퍼런스",
],
"ai-cases.png",
),
(
"13. 대시보드 (허용 계정)",
[
"허용 이메일만 좌측 메뉴 표시",
"경영성과: 연도·분기 KPI 차트 + 매출일보 엑셀 업로드",
],
"dashboard.png",
),
(
"14. 경영성과 대시보드",
[
"상단: Chart.js KPI·차트 조회",
"하단: .xlsx 매출일보 업로드 → 스냅샷 저장",
],
"dashboard-business-performance.png",
),
(
"업무별 Quick Reference",
[
"음성 회의록 → AI → 회의록 AI (또는 클로버노트 → 텍스트 입력)",
"할 일 추적 → 업무 체크리스트 AI",
"빠른 질문 → 일반 채팅 · 메일·보고 → 프롬프트",
"규정 → 회사규정 · 학습 → 학습센터 · AI 과제 → 과제신청",
"문의: AI혁신팀",
],
None,
),
]
ACCENT = RGBColor(0x25, 0x63, 0xEB)
DARK = RGBColor(0x11, 0x18, 0x27)
GRAY = RGBColor(0x4B, 0x55, 0x63)
def add_title_slide(prs: Presentation, title: str, bullets: list[str]) -> None:
layout = prs.slide_layouts[6] # blank
slide = prs.slides.add_slide(layout)
slide.background.fill.solid()
slide.background.fill.fore_color.rgb = RGBColor(0xF0, 0xF4, 0xFF)
box = slide.shapes.add_textbox(Inches(0.8), Inches(2.2), Inches(11.5), Inches(1.2))
tf = box.text_frame
tf.text = title
p = tf.paragraphs[0]
p.font.size = Pt(40)
p.font.bold = True
p.font.color.rgb = DARK
p.alignment = PP_ALIGN.CENTER
sub = slide.shapes.add_textbox(Inches(1.2), Inches(3.6), Inches(10.8), Inches(2.5))
stf = sub.text_frame
stf.word_wrap = True
for i, line in enumerate(bullets[:4]):
para = stf.paragraphs[0] if i == 0 else stf.add_paragraph()
para.text = line
para.font.size = Pt(18)
para.font.color.rgb = GRAY
para.space_after = Pt(8)
para.alignment = PP_ALIGN.CENTER
def add_content_slide(
prs: Presentation,
title: str,
bullets: list[str],
shot_name: str | None,
) -> None:
layout = prs.slide_layouts[6]
slide = prs.slides.add_slide(layout)
# 제목
title_box = slide.shapes.add_textbox(Inches(0.5), Inches(0.25), Inches(12.3), Inches(0.6))
ttf = title_box.text_frame
ttf.text = title
tp = ttf.paragraphs[0]
tp.font.size = Pt(26)
tp.font.bold = True
tp.font.color.rgb = ACCENT
has_shot = shot_name and (SHOT_DIR / shot_name).is_file()
text_left = Inches(0.5)
text_top = Inches(0.95)
text_w = Inches(5.8) if has_shot else Inches(12.3)
text_h = Inches(6.2)
body = slide.shapes.add_textbox(text_left, text_top, text_w, text_h)
btf = body.text_frame
btf.word_wrap = True
for i, line in enumerate(bullets):
para = btf.paragraphs[0] if i == 0 else btf.add_paragraph()
para.text = f"{line}"
para.font.size = Pt(14)
para.font.color.rgb = DARK
para.space_after = Pt(6)
para.level = 0
if has_shot:
shot_path = str(SHOT_DIR / shot_name)
slide.shapes.add_picture(
shot_path,
Inches(6.6),
Inches(0.85),
width=Inches(6.2),
)
elif shot_name:
note = slide.shapes.add_textbox(Inches(6.6), Inches(2.5), Inches(6.0), Inches(1.0))
ntf = note.text_frame
ntf.text = f"(캡처 없음: {shot_name})"
ntf.paragraphs[0].font.size = Pt(12)
ntf.paragraphs[0].font.color.rgb = GRAY
# 슬라이드 번호
num = len(prs.slides)
footer = slide.shapes.add_textbox(Inches(12.0), Inches(7.05), Inches(0.8), Inches(0.3))
ftf = footer.text_frame
ftf.text = str(num)
ftf.paragraphs[0].font.size = Pt(10)
ftf.paragraphs[0].font.color.rgb = GRAY
ftf.paragraphs[0].alignment = PP_ALIGN.RIGHT
def build() -> Path:
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
for idx, (title, bullets, shot) in enumerate(SLIDES):
if idx == 0:
add_title_slide(prs, title, bullets)
else:
add_content_slide(prs, title, bullets, shot)
OUT_PPT.parent.mkdir(parents=True, exist_ok=True)
prs.save(str(OUT_PPT))
return OUT_PPT
if __name__ == "__main__":
out = build()
print(f"PPT saved: {out}")