- 상세 화면(view=project) — 머리 · [⋯] · [작업 열기 — 지금 단계 ›] · 단계 진행(누름 = 그 단계) · 노선 지도(배경 타일 + 선 · 시점 · 종점) · 핵심 숫자 · 기본 정보 · 연결된 것 · 산출물 · 최근 변경(있는 칸만) - 목록 · 상세 숫자 · 썸네일 = 547-1 카드 요약(project_summaries) 한 벌 - 색 = 셸 변수(--b01-*) 이름에 맞춤 - 시험 test_548_project_overview — 칸반 나눔 · 산출물 · 내 것 · 단계 수 짝 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
3.4 KiB
Python
77 lines
3.4 KiB
Python
"""548 프로젝트 목록 · 상세 — 칸반 나눔 규칙 · 산출물 · 내 것 · 단계 수 짝(서버 · 화면 · 요약)."""
|
|
|
|
import re
|
|
from datetime import datetime, timedelta
|
|
from pathlib import Path
|
|
|
|
from B01_Dashboard.B01_Dashboard_Router_Overview import (
|
|
STAGE_TOTAL,
|
|
board_column,
|
|
idle_days,
|
|
is_mine,
|
|
project_outputs,
|
|
)
|
|
from B01_Dashboard.B01_Dashboard_Summary import STAGE_NAMES
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def stages(*states: str) -> list[dict]:
|
|
return [{"stage_no": i, "state": s} for i, s in enumerate(states)]
|
|
|
|
|
|
N, P, C, S, F = "NOT_STARTED", "IN_PROGRESS", "COMPLETE", "STALE", "FAILED"
|
|
|
|
|
|
def test_board_column_road():
|
|
assert board_column("ROAD", stages(N, N, N, N, N, N, N)) == "start"
|
|
assert board_column("ROAD", []) == "start"
|
|
assert board_column("ROAD", stages(P, N, N, N, N, N, N)) == "design"
|
|
assert board_column("ROAD", stages(C, C, C, P, N, N, N)) == "design"
|
|
assert board_column("ROAD", stages(C, C, P, S, N, N, N)) == "review"
|
|
assert board_column("ROAD", stages(C, F, N, N, N, N, N)) == "review"
|
|
assert board_column("ROAD", stages(C, C, C, C, C, C, P)) == "confirm"
|
|
assert board_column("ROAD", stages(C, C, C, C, C, C, N)) == "confirm"
|
|
assert board_column("ROAD", stages(C, C, C, C, C, C, C)) == "done"
|
|
# 마지막만 마침이어도 완료(앞이 STALE 이어도)
|
|
assert board_column("ROAD", stages(C, S, C, C, C, C, C)) == "done"
|
|
|
|
|
|
def test_board_column_other_kinds():
|
|
assert board_column("PLAN", stages(C, C, P)) == "confirm"
|
|
assert board_column("PLAN", stages(C, C, C)) == "done"
|
|
assert board_column("STABILITY", stages(C, P, N, N, N, N)) == "design"
|
|
assert board_column("STABILITY", stages(N, N, N, N, N)) == "start"
|
|
# 모르는 종류(사방) = 단계 없음 → 시작 전 · 설계 중
|
|
assert board_column("EROSION", []) == "start"
|
|
|
|
|
|
def test_outputs_only_when_ready():
|
|
assert project_outputs("PLAN", stages(C, C, C)) == []
|
|
assert project_outputs("ROAD", stages(C, C, C, P, N, N, N)) == []
|
|
keys = lambda st: [o["key"] for o in project_outputs("ROAD", st)] # noqa: E731
|
|
assert keys(stages(C, C, C, C, P, N, N)) == ["drawings"]
|
|
assert keys(stages(C, C, C, C, C, C, P)) == ["drawings", "quantity", "cost"]
|
|
assert keys(stages(C, C, C, C, C, S, S)) == ["drawings", "cost"]
|
|
|
|
|
|
def test_is_mine_and_idle():
|
|
row = {"user_id": 1, "pm_user_id": 2, "field_lead_user_id": None, "designer_user_id": 3}
|
|
assert all(is_mine({**row, "member_user_ids": []}, uid) for uid in (1, 2, 3))
|
|
assert is_mine({**row, "member_user_ids": [9]}, 9)
|
|
assert not is_mine({**row, "member_user_ids": [9]}, 8)
|
|
now = datetime(2026, 10, 26, 12)
|
|
assert idle_days(now - timedelta(days=15, hours=1), now) == 15
|
|
assert idle_days(None, now) is None
|
|
|
|
|
|
def test_stage_total_pairs():
|
|
"""단계 수 = 화면 단계 막대(group_workflow · workflowSteps) = 카드 요약(547-1)."""
|
|
group = (ROOT / "A00_Common/group_workflow.ts").read_text(encoding="utf-8")
|
|
labels = re.findall(r"labels:\s*\[(.*?)\]", group, re.S)
|
|
plan, stab = (len(re.findall(r'"DE_Step_\w+"', block)) for block in labels)
|
|
scaffold = (ROOT / "A00_Common/b_page_scaffold.ts").read_text(encoding="utf-8")
|
|
road = re.search(r"function workflowSteps\(\)[^{]*\{\s*return \[(.*?)\]", scaffold, re.S).group(1)
|
|
assert STAGE_TOTAL == {"ROAD": road.count("L("), "PLAN": plan, "STABILITY": stab}
|
|
assert {k: len(v) for k, v in STAGE_NAMES.items()} == STAGE_TOTAL
|