Spaces:
Sleeping
Sleeping
File size: 4,675 Bytes
fbd1091 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | """
Step 0: menu.csv → data/raw/real_menus.json 변환
실행: python scripts/00_import_csv.py
필터 조건:
- IS_VISIBLE_MENU == "true"
- DEPTH1_NAME 존재
- DEPTH4_NAME에 "팝업" 미포함
menu_id = SCR_{SCREEN_NUM}
menu_path: DEPTH2 == DEPTH1이면 중복 레벨 제거
"""
import csv
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent))
from config import RAW_DIR
CSV_PATH = Path(__file__).parent.parent.parent / "menu.csv"
OUTPUT_PATH = RAW_DIR / "real_menus.json"
def build_menu_path(row: dict) -> str:
"""DEPTH 계층에서 menu_path 생성. DEPTH2 == DEPTH1이면 DEPTH2 제거."""
d1 = (row.get("DEPTH1_NAME") or "").strip()
d2 = (row.get("DEPTH2_NAME") or "").strip()
d3 = (row.get("DEPTH3_NAME") or "").strip()
d4 = (row.get("DEPTH4_NAME") or "").strip()
# DEPTH2가 DEPTH1과 동일하면 중복 레벨 제거
if d2 == d1:
d2 = ""
parts = [p for p in [d1, d2, d3, d4] if p]
return " > ".join(parts)
def parse_keywords(raw: str) -> list:
"""MENU_KEYWORD 콤마 구분 파싱 → List[str]"""
if not raw or not raw.strip():
return []
return [kw.strip() for kw in raw.split(",") if kw.strip()]
def parse_search_count(raw: str) -> int:
"""SEARCH_COUNT 정수 변환 (없으면 0)"""
try:
return int(raw.strip()) if raw and raw.strip() else 0
except ValueError:
return 0
def main():
if not CSV_PATH.exists():
print(f"[오류] menu.csv 파일을 찾을 수 없습니다: {CSV_PATH}")
print("menu.csv를 프로젝트 루트(prototype 상위 폴더)에 위치시켜 주세요.")
sys.exit(1)
RAW_DIR.mkdir(parents=True, exist_ok=True)
menus = []
skipped = {"invisible": 0, "no_depth1": 0, "popup": 0}
screen_num_set = set()
with open(CSV_PATH, encoding="utf-8-sig", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
# 필터 1: IS_VISIBLE_MENU == "true"
if row.get("IS_VISIBLE_MENU", "").strip().lower() != "true":
skipped["invisible"] += 1
continue
# 필터 2: DEPTH1_NAME 존재
d1 = (row.get("DEPTH1_NAME") or "").strip()
if not d1:
skipped["no_depth1"] += 1
continue
# 필터 3: 팝업 제외
d4 = (row.get("DEPTH4_NAME") or "").strip()
if "팝업" in d4:
skipped["popup"] += 1
continue
screen_num = (row.get("SCREEN_NUM") or "").strip()
if not screen_num:
continue
menu_id = f"SCR_{screen_num}"
menu_name = (row.get("DEPTH4_NAME") or row.get("DEPTH3_NAME") or d1).strip()
menu_path = build_menu_path(row)
category = d1
keywords = parse_keywords(row.get("MENU_KEYWORD", ""))
search_count = parse_search_count(row.get("SEARCH_COUNT", ""))
# SCREEN_NUM 중복 체크 (이론상 없어야 하지만 안전 처리)
if screen_num in screen_num_set:
print(f" [경고] SCREEN_NUM 중복: {screen_num} ({menu_name}) 스킵")
continue
screen_num_set.add(screen_num)
menus.append({
"menu_id": menu_id,
"menu_name": menu_name,
"menu_path": menu_path,
"category": category,
"screen_num": screen_num,
"search_count": search_count,
"keywords": keywords,
})
OUTPUT_PATH.write_text(
json.dumps(menus, ensure_ascii=False, indent=2),
encoding="utf-8"
)
print(f"변환 완료: {len(menus)}개 메뉴")
print(f" 스킵 - IS_VISIBLE_MENU 제외: {skipped['invisible']}개")
print(f" 스킵 - DEPTH1 없음: {skipped['no_depth1']}개")
print(f" 스킵 - 팝업 제외: {skipped['popup']}개")
print(f"저장: {OUTPUT_PATH}")
# 카테고리별 집계
from collections import Counter
cat_counts = Counter(m["category"] for m in menus)
print("\n카테고리별 메뉴 수:")
for cat, cnt in cat_counts.most_common():
print(f" {cat}: {cnt}개")
# keywords 보유율
has_kw = sum(1 for m in menus if m["keywords"])
print(f"\nkeywords 보유: {has_kw}/{len(menus)} ({has_kw/len(menus)*100:.1f}%)")
# search_count 보유율
has_sc = sum(1 for m in menus if m["search_count"] > 0)
print(f"search_count > 0: {has_sc}/{len(menus)} ({has_sc/len(menus)*100:.1f}%)")
if __name__ == "__main__":
main()
|