kintex/docs/deliverables/_gen/sources.py
zio 6c9891c7e2 feat: mobile harness + design v2.1 (84 screens) + PLANNING v3.1 + deliverables + Stitch screens
- Harness: kintex-mobile-dev agent + kintex-mobile-orchestrator skill (WISE mobile ref, Stitch-first design rule, dual app targets B2B/B2C)
- design.md v2.1: full 84-screen inventory (web 51 / admin 10 / public 8 / mobile 15) with Stitch prompts incl. ticketing (SCR-P7/P8, M14/M15)
- PLANNING v3.1: unified account + split signup tracks (2FA required for staff, light signup/guest for visitors), one codebase / two app targets
- Deliverables: dev plan (21s), user/operator/developer guides (17/14/15s), program spec (44s, 65 programs, 8 flowcharts), DA (DB design 14s + table spec xlsx 35 tables/299 cols)
- Benchmark: ticketing-app-benchmark.md (7 apps) -> IMPLEMENTATION_BACKLOG Phase F (14 items)
- Stitch: 23 generated screens saved (mobile 10, admin 6, web core 5, ticket 2)
- mobile/: Expo scaffold (SDK 51, expo-router, secure store JWT)
- frontend: SCR-13~17 QA fixes, icons.tsx, kintexEvents, V10 seed migration
- ci/: KINTEX CI logo assets

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 23:31:45 +09:00

265 lines
15 KiB
Python
Raw 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.

# -*- coding: utf-8 -*-
"""
sources.py — 산출물 생성의 단일 데이터 소스 로더.
docs/PLANNING.md · docs/IMPLEMENTATION_BACKLOG.md · docs/FEATURE_BACKLOG_100.md · docs/architecture/*
를 파싱하여 구조화 데이터(모듈·Phase/WBS·기능/요구사항)를 반환한다.
소스 문서가 갱신되면 _gen 재실행만으로 전 산출물이 갱신된다.
파싱 실패 시 임베디드 폴백을 사용해 결정적 산출을 보장한다.
"""
import os, re
DOCS = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) # kintex/docs
ROOT = os.path.abspath(os.path.join(DOCS, "..")) # kintex
META = {
"project": "킨텍스 자동전시시스템",
"project_en": "Exhibition Automation Platform",
"title": "킨텍스 자동전시시스템 구축 사업수행계획서",
"version": "v1.0",
"date": "2026-07-11",
"owner": "지오정보기술(주) — GUARDiA 플랫폼 사업본부",
"client": "킨텍스(KINTEX, 한국국제전시장)",
"dev_domain": "kintex.zioinfo.co.kr(개발)",
"std": "행정안전부 정보화사업 산출물 표준 목차 준용",
}
def _read(rel):
p = os.path.join(DOCS, rel)
try:
with open(p, "r", encoding="utf-8") as f:
return f.read()
except Exception:
return ""
def _table_rows(text, min_cols=2):
"""마크다운 표의 데이터 행(| a | b |)을 셀 리스트로 반환(헤더/구분선 제외)."""
out = []
for line in text.splitlines():
s = line.strip()
if not s.startswith("|"):
continue
if re.match(r"^\|[\s:\-\|]+\|?$", s): # separator
continue
cells = [c.strip() for c in s.strip("|").split("|")]
if len(cells) >= min_cols:
out.append(cells)
return out
# ---------------- 모듈(M1~M18 + §5B) : PLANNING 기능 우선순위 총괄표 ----------------
_MOD_FALLBACK = [
("M2","플로어플랜 스튜디오(부스 배치)","P0"),("M3","부스 설계 스튜디오","P0"),
("M4","유틸리티 설계 자동화(전기·조명/네트워크·급배수)","P0"),("M5","나노바나나 시각화","P0"),
("M1","홀 배정·자동 견적","P1"),("M6","서류·마일스톤 워크플로","P1"),
("M7","등록업체 매칭·견적","P1"),("M9","정산·결제","P1"),
("M15","공사/장치 옥션(역경매·견적서·낙찰)","P1"),("M10","관람객 등록·배지·체크인·리드캡처","P1"),
("M12","마케팅·EDM·공개 홍보 사이트","P1"),("M16","경영분석 BI","P1"),
("M17","CMS(콘텐츠·마이크로사이트·다국어)","P1"),("M18","관리자 백오피스","P1"),
("§5B","공통/시스템관리 레이어 + JWT+2FA","P1"),
("M8","반입/반출 물류","P2"),("M11","비즈니스 매칭","P2"),
("M13","wayfinding·실내 내비","P2"),("M14","현장운영(혼잡·안전·주차·에너지)","P2"),
]
def modules():
text = _read("PLANNING.md")
mods, seen = [], set()
for cells in _table_rows(text, min_cols=4):
mid = cells[0].replace("**","").strip()
if not re.match(r"^(M\d+|§5B)$", mid):
continue
if mid in seen:
continue
name = re.sub(r"\*\*|`", "", cells[1]).strip()
pri = ""
for c in cells:
m = re.search(r"P0|P1|P2", c)
if m: pri = m.group(0); break
if not name or not pri:
continue
seen.add(mid); mods.append((mid, name, pri))
if len(mods) < 15:
return list(_MOD_FALLBACK)
order = {"P0":0,"P1":1,"P2":2}
mods.sort(key=lambda x: order.get(x[2],9))
return mods
# ---------------- Phase / WBS : IMPLEMENTATION_BACKLOG Phase 표 ----------------
_PHASE_FALLBACK = [
("A","아키텍처·거버넌스",[
("A-1","애플리케이션 아키텍처(모듈 경계·API 표준)","아키텍트(AA)","docs/architecture/app.md"),
("A-2","시스템 아키텍처·NFR(확장성·HA·성능·보안영역)","아키텍트(SA)","docs/architecture/system.md"),
("A-3","기술 표준(스택·빌드/배포·관측성·AiTextRouter)","아키텍트(TA)","docs/architecture/tech.md"),
("A-4","데이터 아키텍처(전사 ERD·공간·마스터·BI마트)","아키텍트(DA)","ERD·data.md"),
("A-5","네트워크 아키텍처(DMZ/내부망·방화벽·부하분산)","아키텍트(NA)","network.md"),
("A-6","아키텍처↔PLANNING 정합 검증","reviewer","불일치 0/티켓화"),
]),
("B","공통/시스템관리 레이어(전 모듈 선행)",[
("B-0","스캐폴드(백엔드·프론트·DB·마이그레이션)","BE·FE·DB","빌드·마이그레이션 통과"),
("B-1","인증 JWT+RBAC+2차인증(OTP)+실패잠금+admin env","COM·BE","2FA 등록/검증/초기화 왕복"),
("B-2","시스템관리(사용자·역할·공통코드·메뉴·감사·설정)","COM·ADM·DB","CRUD·RBAC 가드·감사"),
("B-3","공통 업무기능(일지·일정·쪽지·통계·공지·회의·알림)","COM·DB","각 모듈 API 왕복"),
("B-4","공통 컴포넌트·프론트 공통(2FA·그리드·달력·모달)","COM·FE","아키텍처 표준 정합"),
("B-5","점진 QA(2FA·RBAC·PII·경계면)","QA","통과까지 반려"),
]),
("C","P0 부스 시공 코어",[
("C-M2","플로어플랜(배치 3안·병합·규정검증 PostGIS)","DB·BE·AI·FE·QA","3안 생성·병합·규정 재검증"),
("C-M3","부스 설계(조립/독립·규정 사전검증·3안)","BE·AI·FE·QA","위반 사전 플래깅·컨펌"),
("C-M4","유틸리티 배선(전기/네트워크/급배수·견적·위치표시도)","DB·BE·FE·QA","최단 배선·견적·위치표시도"),
("C-M5","나노바나나 시각화(RenderJob·워커·워터마크)","BE·VIZ·FE·QA","실생성/미승인 시 목·워터마크"),
("C-C","역할별 공통 화면(로그인·대시보드·현장 모바일)","FE·QA","역할별 진입·상태 처리"),
]),
("D","P1 도메인(역할별 포털)",[
("D-M15","공사/장치 옥션(견적서 제출·역경매·낙찰)","BID·BE·DB·FE·QA","견적서·역경매·낙찰 왕복"),
("D-M10","관람객·현장(등록·티켓·배지·체크인·리드)","VIS·BE·DB·FE·QA","등록→배지→체크인→리드"),
("D-M12/17","마케팅·공개사이트·CMS(SEO·다국어·EDM)","CMS·BE·FE·QA","공개 조회·게시 워크플로"),
("D-M16","경영분석 BI(가동률·P&L·ROI·수요예측·KPI)","BI·DA·BE·FE·QA","지표 집계·드릴다운"),
("D-M18","관리자 백오피스(마스터데이터·룰셋 버전)","ADM·BE·FE·QA","RBAC·마스터 CRUD·룰셋"),
("D-M1/6/7/9","배정·서류·매칭·정산","BE·FE·DB·QA","견적·마일스톤·정산"),
]),
("E","P2 + 빌드·배포",[
("E-M11","비즈니스 매칭(추천·미팅예약)","VIS·AI","추천·미팅예약"),
("E-M13","wayfinding·실내 내비(M2 재사용)","VIS·FE","부스 검색·경로"),
("E-M14","현장운영(혼잡·안전·주차·에너지)","BE·FE","대시보드·경보"),
("E-DEP","역할별 번들·jar·워커 빌드·CI/CD·systemd","DEV","push→배포"),
]),
]
def phases():
"""[(letter, title, [(id, task, owner, done), ...]), ...]"""
text = _read("IMPLEMENTATION_BACKLOG.md")
if not text:
return _PHASE_FALLBACK
out = []
cur = None
lines = text.splitlines()
hdr_re = re.compile(r"^##\s+Phase\s+([A-E])\s*[—\-]\s*(.+)$")
for line in lines:
hm = hdr_re.match(line.strip())
if hm:
if cur: out.append(cur)
title = re.sub(r"\([^)]*\)", "", hm.group(2))
title = re.sub(r"[★((].*$", "", title).strip()
cur = (hm.group(1), title, [])
continue
s = line.strip()
if cur and s.startswith("|"):
if re.match(r"^\|[\s:\-\|]+\|?$", s):
continue
cells = [c.strip() for c in s.strip("|").split("|")]
if not cells or cells[0] in ("ID","Phase") or not re.match(r"^[A-E]-", cells[0]):
continue
# 표 형태 2종: (ID,작업,담당,완료) 또는 (ID,모듈,작업,담당,완료)
if len(cells) >= 5:
_id, task, owner, done = cells[0], cells[2], cells[3], cells[4]
elif len(cells) == 4:
_id, task, owner, done = cells[0], cells[1], cells[2], cells[3]
else:
continue
cur[2].append((_id, re.sub(r"\*\*|`","",task), re.sub(r"\*\*","",owner), re.sub(r"\*\*","",done)))
if cur: out.append(cur)
ok = [p for p in out if p[2]]
return ok if len(ok) >= 4 else _PHASE_FALLBACK
# 마스터 일정(착수 상대 주차) — 큐레이션(마일스톤)
SCHEDULE = [
("A","아키텍처·거버넌스","W1","W3","M1 아키텍처 베이스라인 승인(W3)"),
("B","공통/시스템관리 레이어","W3","W8","M2 공통 레이어·인증(2FA) 완료(W8)"),
("C","P0 부스 시공 코어","W8","W16","M3 P0 부스 코어 시연·첫 상용 행사 적용(W16)"),
("D","P1 도메인","W16","W24","M4 옥션·관람객·BI·관리자 통합(W24)"),
("E","P2 + 빌드·배포","W24","W28","M5 통합 QA 통과·운영 배포(W28)"),
]
# ---------------- 기능/요구사항 : FEATURE_BACKLOG_100 C1~C12 ----------------
def features():
"""[(fid, name, category, priority, ai, module), ...] — 100대 기능(RTM/요구사항 원천)."""
text = _read("FEATURE_BACKLOG_100.md")
out, seen = [], set()
cur_cat = ""
cat_re = re.compile(r"^###\s+(C\d+)\.\s*(.+?)\s*(?:\(\d+\)|—|$)")
stop = False
for line in text.splitlines():
s = line.strip()
cm = cat_re.match(s)
if cm:
cur_cat = cm.group(1) + " " + re.sub(r"\s*[—-].*$","",cm.group(2)).strip()
stop = False
continue
# C 표가 끝나고 'AI 자동화 맵' 등 다른 섹션이면 중단 표시
if s.startswith("##") and "카테고리" not in s and not s.startswith("###"):
stop = True
if stop:
continue
m = re.match(r"^\|\s*(F\d{3})\s*\|(.+)$", s)
if m and cur_cat:
fid = m.group(1)
if fid in seen:
continue
cells = [c.strip() for c in m.group(2).strip("|").split("|")]
name = re.sub(r"\*\*|`","",cells[0]).strip() if cells else fid
pri = next((re.search(r"P[012]",c).group(0) for c in cells if re.search(r"P[012]",c)), "")
ai = ""
for c in cells:
cc = c.strip()
if cc in ("Y","N","부분"):
ai = cc; break
module = next((re.sub(r"\*\*|`","",c) for c in cells if re.search(r"(M\d+|§5B|§1A|§6)", c)), "")
seen.add(fid)
out.append((fid, name, cur_cat, pri, ai, module))
return out
# ---------------- 산출물 목록(표준 단계별) ----------------
DELIVERABLES = [
# (단계, 산출물명, 형식, 담당, 상태, 비고)
("착수(계획)","사업수행계획서","DOCX","PMO","작성완료","본 산출물(gen_docx)"),
("착수(계획)","WBS 및 마스터 일정","XLSX","PMO","작성완료","gen_xlsx(WBS/일정/투입/산출물)"),
("착수(계획)","산출물 목록","XLSX","PMO","작성완료","gen_xlsx(산출물목록 시트)"),
("착수(계획)","착수보고 자료","PPTX","PMO","작성완료","gen_pptx(착수보고)"),
("분석","요구사항 정의서","DOCX","기획(planner)","작성완료","gen_reqspec(100대 기능 원천)"),
("분석","요구사항 추적표(RTM)","XLSX","QA/기획","작성완료","gen_xlsx(RTM 시트)"),
("분석","기획서(PLANNING)","MD","기획(planner)","기보유","docs/PLANNING.md v3.0"),
("설계","개발계획서","DOCX","PMO/개발리드","작성완료","gen_devplan"),
("설계","화면설계서·UI 스펙(design)","MD/DOCX","디자이너","기보유","docs/design.md"),
("설계","아키텍처 설계서(앱/시스템/기술/데이터/네트워크)","MD","아키텍트","기보유","docs/architecture/*"),
("설계","데이터 모델(전사 ERD·BI 데이터마트)","MD/SQL","DA/DB","진행","설계 트랙"),
("설계","API 설계서(계약)","MD","AA/백엔드","기보유","docs/API_GUIDE.md"),
("구현","소스코드(백엔드·프론트·워커)","Git","개발","진행","src/, tools/nanobanana"),
("구현","DB 스키마·마이그레이션","SQL","DB","진행","Flyway"),
("시험","시험계획서","DOCX","QA","작성완료","gen_testplan"),
("시험","시험케이스·시험결과서","XLSX","QA","작성완료","gen_xlsx(시험케이스 시트)"),
("이관","배포/운영 런북·이관 확인서","MD","DevOps","기보유","docs/OPS_RUNBOOK.md"),
("교육","사용자 지침서·운영자 지침서","DOCX/PPTX","기술문서","예정","완성+QA 후"),
("종료","완료(준공) 보고서","DOCX","PMO","예정","사업 종료 시"),
]
# ---------------- 투입 인력 계획(M/M) ----------------
STAFFING = [
("사업 PM/PMO","1","특급","A~E 상시","6.5"),
("아키텍트(AA/SA/TA)","2","특급","A 집중","3.0"),
("데이터 아키텍트(DA)","1","고급","A·D","2.0"),
("네트워크 아키텍트(NA)","1","고급","A·E","1.0"),
("공통/시스템관리 개발","2","중급","B 집중","4.0"),
("백엔드 개발","3","중·고급","B~D","12.0"),
("프론트엔드 개발","3","중·고급","B~E","12.0"),
("DB 엔지니어(PostGIS)","1","고급","B~D","4.5"),
("AI 개발","1","고급","C~D","3.0"),
("시각화(나노바나나)","1","고급","C","2.0"),
("도메인 개발(옥션/관람객/CMS/BI/관리자)","3","중·고급","D 집중","9.0"),
("QA","2","중급","B~E 상시","8.0"),
("DevOps","1","고급","B·E","2.5"),
("디자이너","1","고급","A~E","3.0"),
]
RISKS = [
("R1","홀 실측 CAD 원본 미확보","배치 정밀도","공개 규격 근사 도면으로 진행, 확보 시 대체"),
("R2","이미지 생성(외부) 승인 게이트","M5 시각화","미승인 시 목/degraded, 승인 후 라이브"),
("R3","외부 시스템(작업신고/PG/KT) 연동 미공개","M6/M9/M4b","제출용 파일·릴레이 방식, 협의 후 연동"),
("R4","등록업체 옥션 참여율·응찰 품질","M15 실효성","등록업체 게이트·AI 자료 공정 비교 유인"),
("R5","개인정보(리드·배지) 규정 위반","법적 리스크","동의·보존정책·감사·최소 수집"),
("R6","AI 시각화 오용(계약 서류)","분쟁 리스크","워터마크·고지 강제(UI·응답·PDF)"),
("R7","공통 레이어 지연","일정","Phase B 인력 선집중·병행 착수"),
("R8","규정·요율 개정","정합성","룰셋 버전 관리·과거 버전 보존"),
]
if __name__ == "__main__":
print("modules:", len(modules()))
ph = phases(); print("phases:", [(p[0], len(p[2])) for p in ph])
print("features:", len(features()))
print("sample feature:", features()[0] if features() else None)