"""검증 프로젝트 찾기 — 읽기만 하는 시험이 필요한 파일을 가진 storage 프로젝트 하나를 고름(507-4). 옛 시험은 지워진 검증_sub7 의 uuid 를 박아 늘 건너뜀 → 지금 storage 에 있는 프로젝트 가운데 OWNERS.md 의 검증_subN 가운데 필요한 파일이 다 있고 가장 오래 안 바뀐 것을 씀(쓰는 중인 프로젝트 피함). `AISLO_TEST_PROJECT=` 로 고정 가능. 없으면 `NONE` 경로(존재하지 않음) 를 돌려줘 시험이 `SKIP_REASON` 글로 건너뜀. 쓰는 시험 금지(읽기 전용). """ from __future__ import annotations import os import re from pathlib import Path ROOT = Path(__file__).resolve().parents[2] NONE = ROOT / "storage" / "_no_verify_project" SKIP_REASON = "검증 프로젝트 없음 — storage/*/*/ 에 필요한 파일(격자 · 종단 · 노선 · 관)을 다 가진 프로젝트가 없음" #: B05 노선 · B06 상세 · 관 · 격자를 쓰는 349 계열 시험이 읽는 파일 ROUTE_SET = ( "B04_PreProcess/models/dtm_classification_smooth.npz", "B06_Section/longitudinal/longitudinal.json", "B05_Profile/route/planned_route_curves.json", "B05_Profile/route/planned_route.csv", "B05_Profile/route/planned_route_nodes.csv", "B04_PreProcess/drainage/edits/pipe_points.json", ) def _verify_ids() -> set[str]: """OWNERS.md 표의 검증_subN 프로젝트 uuid — 사용자 프로젝트(엄상준_검증 등)는 시험이 안 열음.""" text = (ROOT / "OWNERS.md").read_text(encoding="utf-8") return set(re.findall(r"`검증_sub\d+`\s*\|\s*`([0-9a-f-]{36})`", text)) def _touched(project: Path, rels: tuple[str, ...]) -> float: """가장 늦게 바뀐 때 — 다른 창이 쓰는 중인 프로젝트(최근)는 뒤로.""" sections = project / "B06_Section" / "cross_sections" stamps = [(project / rel).stat().st_mtime for rel in rels] + [sections.stat().st_mtime] return max(stamps + [f.stat().st_mtime for f in sections.glob("*.json")]) def find_project(*rels: str) -> Path: pinned = os.environ.get("AISLO_TEST_PROJECT") candidates = ( list((ROOT / "storage").glob(f"*/*/{pinned}")) if pinned else [p for p in (ROOT / "storage").glob("*/*/*") if p.name in _verify_ids()] ) found = [ project for project in candidates if all((project / rel).is_file() for rel in rels) and (project / "B06_Section" / "cross_sections").is_dir() ] return min(found, key=lambda project: _touched(project, rels), default=NONE) def own_ports() -> tuple[str, str]: """OWNERS.md 표에서 이 폴더 줄의 (API, vite) 주소 — 줄이 없으면 빈 글 둘(시험이 안 돎 · 508-1a).""" owners = ROOT / "OWNERS.md" for line in owners.read_text(encoding="utf-8-sig").splitlines() if owners.is_file() else []: cells = [cell.strip().strip("`") for cell in line.strip().strip("|").split("|")] if len(cells) >= 5 and Path(cells[2]).resolve() == ROOT.resolve(): ports = re.findall(r"\d{4,5}", cells[4]) if len(ports) == 2: return f"http://localhost:{ports[0]}", f"http://localhost:{ports[1]}" return "", ""