feat(478-2 문서 사이트): 묶음 규칙 · 원고 생성 · mkdocs 설정 · 손글 옮김 · 검사 시험(가짜 추출)
- docs/tools/groups.yml · group.py — 함수를 로직 묶음에 자동 배정(계산 등록부 id · 파일 이름 무늬 · 안 맞음 = 묶음 없음 · 함수 목록 지문) - docs/tools/build.py — 추출 JSON + 손글로 페이지 · 묶음 화면 · 함수 칸 · mermaid 흐름 · 세션 칸 · 서버 주소 · 설명 확인 필요 표시 · 함수 화면은 TypeDoc(markdown) · mkdocstrings(griffe) - docs/mkdocs.yml · nav_hook.py — Material · 한국어 · 검색 · mermaid · 왼쪽 메뉴 페이지 → 묶음 → 함수 - docs/guide/B05_B06 — 8 파일을 page.md(개요 · 규칙 · 사용자 결정) + groups/*.md 17 개로 줄여 옮김 - resources/tester/test_478_docs.py · fixtures/478_docs — 가짜 추출로 배정 · 사이트 strict · 도구 대 추출 · 진짜 추출은 있을 때만 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,71 +0,0 @@
|
||||
---
|
||||
type: concept
|
||||
status: stable
|
||||
related_pages: ["[[design_page/index]]", "[[design_page/values]]", "[[design_page/rules]]"]
|
||||
last_updated: 2026-10-19
|
||||
source: ["docs/raw/조사/2026-10-19_472_구조다시세움_목록.md", "PLAN.md 467"]
|
||||
---
|
||||
|
||||
# 계산 목록 — TS · 파이썬 짝 · Node 묶음 · 실행 자리
|
||||
|
||||
- Node 묶음 — sc = server-calc · co = corridor · ex = b07-extent · dc = design-calc(등록부 입구 `A00_Common/design/logic/design_calc_node.ts` · 파이썬 `common_util_calc_runner.run_calc`) · 다시 지음 `npm run -s build:server-calc|corridor|b07-extent|design-calc`.
|
||||
- 실행 자리 — 짝 = 브라우저 즉시 TS + [저장] 정본 파이썬 · ② = 같은 TS 를 브라우저 · Node 둘 다 · 브라우저 = 입력 도구 · 표시만 · 서버 = 무거운 GIS · 추천.
|
||||
- 줄 더함 · 바꿈 = 새 계산 · 짝 · 묶음이 바뀐 커밋에서.
|
||||
|
||||
| # | 계산 | TS | 파이썬 짝 | Node | 실행 자리 · 거울 시험 |
|
||||
|---|---|---|---|---|---|
|
||||
| R1 | 평면 폴리라인 · 곡선(BC · EC · MC) · 선 자리 x · y 4자리 한 벌(초안 그린 선 = 계획노선 csv = 노선 geojson) | `logic/B05_Profile_UI_RouteEdit_Curve.ts:243` · `logic/B05_Profile_Pipeline_RouteGround.ts:66` draftLine | `common_util_route_polyline.py:579` · `common_util_route_geometry.py` write_route_csv · `B05_Profile_Engine.py:24` _route_geojson | — | 짝 · `test_route_polyline_browser_mirror` · `test_475_1_route_digits` |
|
||||
| R2 | 손잡이 → IP · R | `logic/B05_Profile_UI_RouteEdit_Curve.ts` dragHandleTo | — | — | 브라우저 · `test_route_polyline_handle_drag` |
|
||||
| R3 | 노드 편집 규칙(곡선 한도 · 고정점) | `logic/B05_Profile_UI_RouteEdit_Edits.ts` | `B05_Profile_Route_NodeEdits.py:21` | — | 짝 · `test_430_route_edit_rules` |
|
||||
| R4 | 스냅 맞춤(MC → 일반측점) | `logic/B05_Profile_RouteEdit_StationFit.ts` | — | — | 브라우저 · `test_366_3_station_fit` |
|
||||
| R5 | 측점 틀(법선 · 방위) | `logic/B05_Profile_UI_RouteEdit_Frame.ts:61` | `B05_Profile_Engine_Sections_Core.py:449-519` | — | 짝(입력 선 다름) · `test_b05_routeedit_frame_mirror` |
|
||||
| R6 | 측점 목록 | `logic/B05_Profile_Sections_Stations.ts:276` · `logic/design_pipeline_stations.ts` | `B05_Profile_Engine_Sections_Core.py:449` | — | 짝 · `test_344_3_pipeline_stations_mirror` · `test_407_curve_station` |
|
||||
| R7 | 측점 열쇠 | `common_util_station_key.ts:32` | `common_util_station_key.py:32` | — | 짝 · `test_344_3_station_key_mirror` |
|
||||
| R8 | 측점 갈아끼움 | `logic/B06_Section_Station_Swap.ts:189` | — | — | 브라우저 · `test_346_2_station_swap` · `test_272_swap_keeps_canon` |
|
||||
| G1 | 지반 표본 | `common_util_surface_sampler.ts:65` · `B05_Profile_Api_TerrainGrid.ts:33-99` | `common_util_surface_sampler.py:210`(npz) | ex | 짝 · `test_344_1_surface_sampler_mirror` |
|
||||
| G2 | 미교차 넓힘 표본 | `logic/B05_Profile_Sections_Ground.ts:269` | `B06_Section_Engine_SampleExtend.py:62` | — | 짝 · `test_344_1_sections_ground_mirror` |
|
||||
| G3 | 넓힘(표본 폭 넘음) | — | `B06_Section_Router_Widen.py:80` | — | 서버(저장 밖 DB 씀) |
|
||||
| G4 | 상단측 | `logic/B05_Profile_Pipeline_Detail.ts` uphillSide | `B05_Profile_Engine_Sections_Core.py` uphill_side | — | 짝 · `test_346_2_pipeline_cross_page` |
|
||||
| P1 | 첫 계획선(base PVI) | `logic/B05_Profile_Grade_Ground.ts:114` · `logic/B05_Profile_Pipeline_Grade.ts:285` | `B05_Profile_Engine_Grade_Profile.py:537` | — | 짝 · `test_344_2_route_pairs` |
|
||||
| P2 | 계획선 짓기 | `logic/B05_Profile_UI_Profile_Alignment.ts:401` | `B05_Profile_Engine_Grade_Alignment.py:494` | — | 짝 · `test_426_profile_straight` · `test_213_1_hidden_station_pvi` · `test_438_1_initial_straighten` |
|
||||
| P3 | 종단곡선 법칙(L · R = L/Δ) | `logic/B05_Profile_Profile_CurveLaw.ts` | `B05_Profile_Engine_Grade_CurveLaw.py` | — | 짝 · `test_432_1_curve_law` |
|
||||
| P4 | 직선 판정 | `logic/B05_Profile_Profile_Straight.ts:21` | `B05_Profile_Engine_Grade_Alignment.py` | — | 짝 · `test_426_profile_straight` · `test_271_straighten_pick` |
|
||||
| P5 | 노선 바뀜 계획고 옮김 | `logic/B05_Profile_Grade_Carry.ts:188` | `B05_Profile_Engine_Grade_Carry.py:149` | — | 짝 · `test_400_profile_carry` · `test_349_0v_route_save_span` |
|
||||
| P6 | 옮긴 시설 꼭짓점 세움 | `logic/B05_Profile_Grade_Seat.ts:32` · `common_util_structure_station.ts:356` | `B05_Profile_Router_Confirm.py:220 · 306` | — | 짝 · `test_427_1_follow_seat` · `test_459_route_seat_mirror` |
|
||||
| P7 | 직선화 · 자유지정 · 쉬프트 · 시종점 | `logic/B05_Profile_UI_Profile_Straighten.ts` · `logic/design_profile_free.ts` · `logic/B05_Profile_UI_Profile_Pivot.ts` · `logic/B05_Profile_EndOffsets.ts` | — | — | 브라우저(편집값만 저장 → P2) |
|
||||
| P8 | 종단 경고(기울기 · 법정 L · 최소 R · 피복) | `B05_Profile_UI_Panel_Fields.ts` · `logic/B05_Profile_UI_Profile_Guard.ts` · `B05_Profile_UI_Profile_MinCover.ts` · 경고 글 `logic/B05_Profile_UI_Profile_Alignment.ts` buildAlignment | `B05_Profile_Engine_Grade.py` · `B05_Profile_PageSettings.py` · 경고 글 `B05_Profile_Engine_Grade_Alignment.py` build_alignment | — | 짝 · [저장] · [확정] = `B06_Section_Server_Calc_Prebuild.py` refresh_alignment 가 경고만 다시 씀(기하 그대로) · `test_442_2_grade_limit_mirror` · `test_472_save_warnings` |
|
||||
| C1 | 횡단 설계선 | `common_util_cross_design.ts:267` · `logic/B06_Section_Cross_Stage.ts:93` · `B06_Section_Cross_Refresh.ts:330` | `B06_Section_Engine_Design.py:195`(B07 · 추천 · Route_Save · B05 확정 · 넓힘 찾기) | dc · ex | ② · [저장] · [확정] 다시 셈 = Node(`B06_Section_Engine_Design_Node.py` 측점 묶음 · 파이썬은 입력 조립) · `test_b06_cross_design_mirror` · `test_472_1_golden` 설계선 점 1e-4 |
|
||||
| C2 | 단면적(절 · 성 · 측구 · 층따기) | `common_util_cross_design_areas.ts` | `B06_Section_Engine_Areas.py` | dc · sc · ex | ② · C1 과 한 셈 · 정본 = 표준 · 구조물 측점 모두 Node |
|
||||
| C3 | 확정 보정(포장 · 세월교 · 기울임) | `logic/B06_Section_Cross_Enforce.ts:40` | `B06_Section_Router_Design_Ranges.py` · `B06_Section_Engine_Tilt.py` | dc(다시 세운 설계) | 구간 판정 짝 · `test_349_0n_cross_enforce_mirror` |
|
||||
| C4 | 소단 구간(구조물 → 구간) | `common_util_berm_spans.ts`(시설 줄 · 다시 셈이 부름) | `B06_Section_Engine_Structures_Wall.py:124`(Node 못 부를 때만) | dc | ② · `test_272_berm_source` · `test_472_3_calc_runner` |
|
||||
| C5 | 소단 사면 점 | `common_util_cross_berm.ts` | `common_util_cross_berm.py` | dc · sc · co · ex | C1 과 한 셈 · `test_178_2_berm_fill_mirror` |
|
||||
| C6 | 맹암거 얹기 · 기울임 | `common_util_structure_underdrain.ts` | `B06_Section_Engine_Tilt.py` | — | 짝 · 거울 시험 없음 |
|
||||
| C7 | 반폭 맞춤 · 성토 5 m 경고 | `logic/B06_Section_UI_Cross_Fit.ts` · `logic/B06_Section_UI_Cross_FillSlope_Warn.ts` | — | ex(Fit) | 브라우저 |
|
||||
| F1 | 시설 따라감(노선 바뀜) | `common_util_route_follow.ts:291` · `logic/B05_Profile_Pipeline_Follow.ts` · `logic/B05_Profile_Structure_Follow.ts` | `common_util_route_follow.py:120` | — | 짝 · `test_410_follow_xy` · `test_344_2_route_pairs` |
|
||||
| F2 | 추가 측점 따라감 | `B05_Profile_Api_PlusStations.ts` followPlusDraft | `B05_Profile_Plus_Stations.py:82` | — | 짝 · `test_459_route_seat_mirror` |
|
||||
| F3 | 시설 측점 계획 | `common_util_structure_station.ts` | `common_util_structure_station.py` | — | 짝 · `test_322_3_plan_mirror` |
|
||||
| F4 | 형상 얹기(관 세트 · 벽 · 포장 그림 자료) — 관 = 지금 노선에 얹은 저장분(서버 `route_pipe_points` = 측점 계획 · 관 지점 GET 과 같은 이월) | `common_util_structure_attach.ts:130` | `B06_Section_Engine_Attach.py:114` | — | 짝 · `test_323_4_attach_mirror` · `test_472_d_pipe_route_attach` |
|
||||
| F5 | 관 세트(관경 · 날개벽 · 집수정) | `common_util_culvert_sets.ts` · `logic/B06_Section_UI_Cross_Culvert_Const.ts` | `B06_Section_Engine_Culvert.py` | Const = sc · co · ex | 짝 · `test_b06_culvert_sets_mirror` · `test_b06_pipe_wall_height_mirror` |
|
||||
| F6 | 벽 · 포장 | `common_util_structure_walls.ts` · `common_util_structure_pavement.ts` | `B06_Section_Engine_Structures_Wall.py` · `common_util_structure_pavement.py` | — | 짝 · `test_b06_structure_walls_mirror` · `test_b06_structure_pavement_mirror` |
|
||||
| F7 | 관 길이 · 구조물 면적 · 다단 벽 | `B06_Section_Structure_Layouts.ts:95 · 287` · `design_structure_pipe_calc.ts` · `common_util_cross_structure_areas.ts` | — | sc | ② · `test_b06_pipe_length` · `test_299_ford_span_pipe` |
|
||||
| F8 | 터파기 | `common_util_excavation.ts` | `common_util_excavation.py`(운영 부름 없음) | sc · co · ex | ② · `test_excavation_mirror` |
|
||||
| F9 | 세월교 높이 · 깊이 | `design_structure_ford_calc.ts` | `B04_PreProcess_Router_Basins` ford_height_m · `B08_Quantity_Engine_Hydro` ford_depth | — | 짝 · `test_65_ford_defaults` · `test_73_11_b08_hydro_box_ford` |
|
||||
| E1 | 누가토량 | `common_util_mass_haul.ts:426` | — | sc | ② · `test_309_mass_haul_badge` |
|
||||
| E2 | 유토 배분 · 운반거리 | `common_util_mass_haul_balance.ts:584` | — | sc | 서버 Node 만(`/haul-plan`) |
|
||||
| D1 | 배수유역 세부 나눔 | `logic/B05_Profile_Drainage_Live.ts`(일정만) | `common_util_drainage_detail.py` · `B04_PreProcess_Router_Basins.py:538` | — | 서버 |
|
||||
| D2 | 관 자리 제안 | `logic/B05_Profile_Drainage_Suggest.ts`(모으기만) | suggest_pipe_positions | — | 서버 |
|
||||
| D3 | 3D 길 모형 | `B05_Profile_UI_Corridor_Build.ts` · Worker | — | co | ②(`B05_Profile_Corridor_Prebuild.py`) |
|
||||
| D4 | 평면 발자국 | `logic/B05_Profile_PlanFootprint.ts:160` · `logic/B05_Profile_Footprint_Stage.ts` | `B07_DesignDetail_Engine_Cad_PlanSlope.py`(B07 따로) | — | 브라우저 · `test_346_2_plan_footprint_mirror` |
|
||||
| N1 | 노선 추천 | `B05_Profile_UI_RouteEdit_Initial_Model.ts`(보기만) | `B05_Profile_Engine_RouteInitial*` | — | 서버 · `test_419_1_recommend_engine` |
|
||||
| N2 | 추천 후보 초기 종단(476) — [적용] 체인과 같은 조각을 메모리에서(노선 짜기 · 시설 따라감 · `build_sections` 438 · 켬 = 균형 얹기) · 저장본 `route_recommend/initial` · 화면 펴기 짝 = `flattenServerPlan` ↔ `draft_nodes` | 그리기만(476-2) · `logic/B05_Profile_UI_RouteEdit_Edits.ts` flattenServerPlan | `B05_Profile_Engine_InitialProfile.py` · `B05_Profile_Router_RouteInitial_Initial.py` | — | 서버 · [적용]은 체인이 다시 셈 · `test_476_1_recommend_initial`(체인 대조 0 mm) |
|
||||
|
||||
## 짝인데 값이 갈리는 자리
|
||||
|
||||
| # | 자리 | 브라우저 | 서버 | 처리 |
|
||||
|---|---|---|---|---|
|
||||
| X1 | 단면 모양 기본값 | `"left_cut"`(`B06_Section_Cross_Refresh.ts:438`) | `default_section_modes`(`B06_Section_Router_Design.py:402`) | 사용자 협의 · 손대지 않음 |
|
||||
| X2 | 지반 표본 출처 · 입력 선 | 격자 `.bin` · `/route/plan` planned 선 | npz `sample_xy` · 저장 경로 폴리라인 | 노선 초안 없는 차례 = 정본 표본 유지로 막음 |
|
||||
| X3 | 단면적 두 갈래 | 전부 TS | 모두 Node(472-4 ②) | 풀림 · 남은 차 = 설계선 점 반올림 동점 1e-4 m(파이썬 round 짝수 쪽 · TS 위쪽) |
|
||||
| X4 | 평면 발자국 | TS PlanFootprint | B07 파이썬 PlanSlope | 거울 시험 |
|
||||
| X5 | 경고 기준표 | `B05_Profile_UI_Panel_Fields.ts` 안 표 | `FOREST_ROAD_PROFILE_CRITERIA` | 거울 시험 · 자료 두 벌 |
|
||||
| X6 | 노선 바뀜 넘기는 칸 | `B06_Section_Station_Keys.ts:46` 근처 | `B05_Profile_Route_Save.py:275-279` CARRY_INPUT_KEYS · ROUTE_CARRY_KEYS | 손으로 맞춘 목록 둘 · 거울 없음 |
|
||||
@@ -0,0 +1,3 @@
|
||||
3D 길 모형 — 종단 · 횡단으로 길 몸체를 지음.
|
||||
|
||||
- 노선 초안 · 종단 고침 동안 낡음 표시 · [저장] 뒤 서버 Node 가 다시 지음.
|
||||
@@ -0,0 +1,5 @@
|
||||
횡단 설계선 · 면적 계산 — 브라우저 즉시 · [저장] 때 서버 Node 정본.
|
||||
|
||||
- 설계선 = [[computeCrossDesign]] ↔ 파이썬 짝 · 단면적은 표준 · 구조물 측점 모두 Node.
|
||||
- 다시 셈 입력 물러섬 = 세션 고름 → 저장 정본 설계 → config 기본값.
|
||||
- 소단 구간 = 구조물에서 [[bermSpansFromStructures]] ↔ 서버 [[load_berm_spans]].
|
||||
@@ -0,0 +1,5 @@
|
||||
횡단 화면 — 카드 단추 · 조정창 · 반폭 · 표준횡단 · 상세 표.
|
||||
|
||||
- 카드 단추 = 세션 `crossdesign` 의 바꾼 칸만 · 메모리 상세 설계를 안 덮음.
|
||||
- 조정창 칸 · 암선 · 암 경사 = 세션 칸이 값 주인(메모리 사본 없음) · 빈 칸 = 칸 지움.
|
||||
- 표준횡단 = 저장분 · config 와 다를 때만 칸([[effectiveStandardCross]]).
|
||||
@@ -0,0 +1,5 @@
|
||||
관 · 배수 — 배수판 관 편집 · 배수유역 · 관 옵션.
|
||||
|
||||
- 관 주인 = 가게 `pipes` · 배수 편집기는 숨어도 가게를 따름.
|
||||
- 관 자리 읽기 = 지금 노선에 얹은 관 지점 한 벌([[route_pipe_points]]) · 관 파일 누가거리를 그대로 읽지 않음.
|
||||
- [저장] = 관 차례 `pipe_points` PUT · 관 옵션 예약 있으면 같은 파일 한 번 더.
|
||||
@@ -0,0 +1,4 @@
|
||||
누가토량 · 유토 배분 · 운반거리.
|
||||
|
||||
- 누가토량 = TS 한 벌(브라우저 · Node) · 그릴 때 셈(접혀도 셈).
|
||||
- 유토 배분 = 서버 Node(`/haul-plan`) · 저장본 `_derived/haul` · 입력 지문 다를 때만 다시 셈.
|
||||
@@ -0,0 +1,3 @@
|
||||
평면 발자국 — 횡단 결과로 평면에 깎기 · 쌓기 테두리.
|
||||
|
||||
- 묶음 마지막 단계 · B07 은 따로(파이썬) · 거울 시험.
|
||||
@@ -0,0 +1,4 @@
|
||||
측점마다 원지반 표본 — 브라우저 지형 격자 · 서버 npz.
|
||||
|
||||
- 표본 짝 = 브라우저 격자 ↔ 서버 `sample_xy`(거울 시험) · 입력 선이 달라 mm 차.
|
||||
- 노선 초안 없는 차례 = 정본 표본 유지 · 넓힘(표본 폭 넘음) = 저장 밖 서버.
|
||||
@@ -0,0 +1,6 @@
|
||||
계산 묶음 · 설계 가게 · 상세 가게 — 입력 → 파생 한 방향.
|
||||
|
||||
- 묶음 단계 = 노선 → 따라감 → 측점 → 지반 → 첫 계획선 → 종단 → 횡단 → 발자국 · 차례 번호 · 옛 차례 버림.
|
||||
- 묶음 결과 내보냄 = [[publishPagePipeline]] · 조용한 차례 끝 = 가게 측점 = 지금 상세 측점.
|
||||
- 설계 가게 = [[useDesignStore]] · 멈춘 뒤 1 s 세션 · 상세 가게 = [[loadSectionDetail]] · 새로고침 = [[checkSessionCanon]].
|
||||
- 서버 Node 입구 = 계산 등록부 id → 함수 · 파이썬은 [[run_calc]].
|
||||
@@ -0,0 +1,6 @@
|
||||
종단 계획고 — ▲▼ · 직선화 · 자유지정 · 쉬프트 · 종단 R · 시종점 · 페이지 설정.
|
||||
|
||||
- 편집값 주인 = 가게 `alignmentDraft`(세션 `alignment-draft`) · 지금 값 = 초안 ?? 서버 저장분.
|
||||
- 계획선 짓기 [[buildAlignment]] ↔ 파이썬 짝(거울 시험).
|
||||
- 짝 있는 옮긴 측점 = 꼭짓점 따라감([[followMovedNodes]]) · 짝 없는 새 측점 = 꼭짓점 안 세움.
|
||||
- [저장] = 편집 델타만 보내고 서버가 종단 다시 셈.
|
||||
@@ -0,0 +1,4 @@
|
||||
추천 노선 후보를 서버가 찾고 화면이 보여 줌 · 고른 후보를 [적용].
|
||||
|
||||
- 후보 초기 종단 = [적용] 체인과 같은 조각을 메모리에서 셈 · 저장본 `route_recommend/initial`.
|
||||
- [적용] = 저장 밖 · 즉시 [[replan_route]] · 종단 = 새 규칙 초기 종단 · 옛 손 입력 안 넘김 · 초기값 다시 뜸 · 초안 전부 지움.
|
||||
@@ -0,0 +1,5 @@
|
||||
평면 노선 편집기 — 교점 끌기 · 방향키 · 교점 추가 · 삭제 · 곡선 R · L · 스냅.
|
||||
|
||||
- 조작은 편집기 메모리 → 놓을 때 세션 `route-draft` · 첫 초안이면 초안 전 상세를 hold.
|
||||
- 곡선 선 = [[buildEditedPolyline]] ↔ 파이썬 짝 [[build_planned_polyline]] · 선 자리 x · y 4자리 한 벌.
|
||||
- 노선 초안 차례 = 새 · 옮긴 측점만 브라우저 지반 표본.
|
||||
@@ -0,0 +1,5 @@
|
||||
노선이 바뀐 [저장] — 새 노선 짜기 · 관 · 구조물 · 추가 측점 따라감 · 횡단 고침 넘김.
|
||||
|
||||
- 서버 [[run_route_save_chain]] = 노선 · 측점 · 표본 · 횡단을 처음부터 · 전부 아니면 없음.
|
||||
- 관 · 구조물 초안 다시 얹기 = [[rebasePipeDraft]] · [[rebaseStructureDraft]](좌표 따라감).
|
||||
- 횡단 고침 넘김 = [[carryKeyedStates]] ↔ 서버 [[carry_cross_inputs]] · 자리 칸은 같은 자리([[sameSeat]]) 측점만.
|
||||
@@ -0,0 +1,5 @@
|
||||
[저장] · [확정] · [초기화] · 떠남.
|
||||
|
||||
- [저장] · [확정] = 한 본문 한 요청 · 서버가 차례(노선 → 설정 → 관 → 구조물 → 추가 측점 → 종단 → 관 옵션 → 상단측 → 횡단)로 · 하나라도 실패 = 되돌림.
|
||||
- 횡단 패치 = [[crossPatchesFromCache]] · 노선 바뀐 [저장] = [[routeCarryInputs]] 를 같은 본문에.
|
||||
- [폐기] = 세션 고침 칸만 지움 · 메모리는 떠남이 비움 · [초기화] = DB 노선 행 지우고 초기값으로.
|
||||
@@ -0,0 +1,4 @@
|
||||
페이지 틀 · 보기 전환 · 패널 · 고름.
|
||||
|
||||
- B05 ↔ B06 전환 = 보기 숨김 · 보임만 · 보인 보기가 가게 · 세션을 다시 읽음 · 묶음은 숨은 동안도 셈.
|
||||
- 접기 = 그리기만 건너뜀 · 팝아웃 = 같은 함수 · 같은 가게.
|
||||
@@ -0,0 +1,3 @@
|
||||
서버 창구 · 저장소 · 스키마 — 브라우저 요청을 받아 DB · 정본 파일을 읽고 씀.
|
||||
|
||||
- 정본 쓰기 = [저장] · [확정] · [초기화] · 저장 밖 셋만.
|
||||
@@ -0,0 +1,5 @@
|
||||
측점 목록 · 열쇠 · 갈아끼움 — 노선 · 시설이 바뀌면 측점을 다시 세움.
|
||||
|
||||
- 열쇠 = [[common_util_station_key#stationKey]] 한 벌(파이썬 짝 · 거울 시험).
|
||||
- 갈아끼움 [[swapSectionStations]] — 노선 초안 없는 차례 = 서버 정본 측점 기하 · 표본 유지(같은 자리 0.01 m · 축 1e-3).
|
||||
- 지금 상세에 없던 측점이 정본 자리로 돌아옴(관 지움 ↶) = 정본 설계.
|
||||
@@ -0,0 +1,5 @@
|
||||
구조물 — 추가 · 옮김 · 지움 · 형상 · 소단 · 시설 줄.
|
||||
|
||||
- 주인 = 가게 `structures` · 화면 사본은 가게 알림으로 맞춤([[announceDesignStore]]).
|
||||
- 시설 줄 측점 = 종류 표 · 구조물 · 관 알림이 다 온 뒤만 심음(모름 ≠ 빈 목록).
|
||||
- 소단 사본 쓰는 자리 = 시설 줄 하나 · 같으면 안 씀.
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
type: concept
|
||||
status: stable
|
||||
related_pages: ["[[design_page/operations_b05]]", "[[design_page/operations_b06]]", "[[design_page/operations_common]]", "[[design_page/values]]", "[[design_page/session_keys]]", "[[design_page/calculations]]", "[[design_page/rules]]", "[[design_data_lifecycle]]", "[[workflow_state]]"]
|
||||
last_updated: 2026-10-19
|
||||
source: ["docs/raw/조사/2026-10-19_470_B05B06_데이터흐름.md", "docs/raw/조사/2026-10-19_472_구조다시세움_목록.md", "docs/raw/조사/2026-10-19_465_요구272_값안변함.md", "CLAUDE.md §5"]
|
||||
---
|
||||
|
||||
# B05 · B06 한 페이지 데이터 흐름
|
||||
|
||||
- 대상 — `A00_Common/design/` · `B05_Profile/` · `B06_Section/`(주소 둘 `#/b05-profile` · `#/b06-section` = 페이지 하나).
|
||||
- ⛔ B05 · B06 고치기 전 이 폴더를 읽음 · 조작 · 값 · 계산 흐름을 바꾸면 **같은 커밋에** 해당 표를 고침.
|
||||
- 파일 이름만 적음 — `design_*` = `A00_Common/design/` · `B05_Profile_*` = `B05_Profile/` · `B06_Section_*` = `B06_Section/` · `common_util_*` = `common_util/` · `logic/…` = 그 폴더의 `logic/`.
|
||||
- 「사본 수」 = 같은 값을 쥔 자리 수(메모리 · 가게 · 세션 · 서버) — 줄일수록 좋음.
|
||||
|
||||
## 문서
|
||||
|
||||
| 문서 | 내용 |
|
||||
|---|---|
|
||||
| [[design_page/operations_b05]] | B05 평면 · 시설 · 종단 조작 |
|
||||
| [[design_page/operations_b06]] | B06 횡단 카드 · 조정창 · 저장 밖 조작 |
|
||||
| [[design_page/operations_common]] | ↶↷ · 보기 전환 · 접기 · 팝아웃 · 새로고침 · 떠남 · [저장] · [확정] · [초기화] |
|
||||
| [[design_page/values]] | 값마다 주인 · 사본 · 셈 자리 · 맞추는 때 |
|
||||
| [[design_page/session_keys]] | 세션 열쇠 등록부 · [저장] 차례 |
|
||||
| [[design_page/calculations]] | 계산 40 갈래 · TS · 파이썬 짝 · Node 묶음 · 실행 자리 |
|
||||
| [[design_page/rules]] | 지킬 규칙 · 바꿀 때 할 일 · 남은 틈 |
|
||||
|
||||
## 층 그림
|
||||
|
||||
```
|
||||
사용자 조작(끌기 · 단추 · 칸)
|
||||
▼
|
||||
① 화면 메모리 — 편집기 · 배수판 · 보기 구조물 · 시설 줄
|
||||
② 상세 가게 — Section_Store cache(페이지 수명) · 정본 사본 canon(세션)
|
||||
③ 설계 가게 design_store — stations · profile · footprint 는 메모리 우선(멈춘 뒤 1 s 세션)
|
||||
④ 세션 sessionStorage — aislo:{통}:{이름}:{프로젝트}[:{노선}] · draft · result · pref(localStorage)
|
||||
⑤ 계산 묶음 — route → follow → stations → ground → planline → profile → cross → footprint
|
||||
‖ 뒤: drainage(서버 배수유역) · corridor(3D 낡음)
|
||||
│ 뒤에서(표시용) │ [저장] · [확정] · [초기화] · 저장 밖 셋
|
||||
▼ ▼
|
||||
⑥ 서버 파생 저장본 ⑦ 서버 작업본(정본) — DB · 정본 파일
|
||||
_derived/haul · route_recommend · ▲ [초기화] = 덮어씀
|
||||
04_detail_cache.pkl ⑧ 초기값 initial_snapshot/
|
||||
```
|
||||
|
||||
## 층 표
|
||||
|
||||
| 층 | 위치(파일:줄) | 역할 · 비우는 때 |
|
||||
|---|---|---|
|
||||
| ① 화면 메모리 | `B05_Profile_UI_RouteEdit.ts:124-145` · `B05_Profile_UI_Drainage_Panel.ts:123 · 142` · `B05_Profile_UI_Page_Structures.ts:78` · `B06_Section_UI_Page_Structures_Panel.ts:175-176` · `design_facility_lane.ts:95-105` | 조작 즉시 · 페이지 dispose 로 버림 |
|
||||
| ② 상세 가게 | `B06_Section_Section_Store.ts:68` cache · `:69` pending · `:117` loadSectionDetail · `:431` peekSectionDetail | 상세 횡단(살아 있는 객체) · 떠남 `forgetSectionMemory`(`:424`) · 정본 바뀜 `invalidateSectionDetail`(`:403`) |
|
||||
| ② 정본 사본 · 상세 칸 | `B06_Section_Section_Canon.ts:36` keepCanon · `:59` sessionDetail · `:72` savedDesigns · `:84` forgetCanon · `:107-143` 칸 셋(저장 정본 · 초안 전 hold · 노선 초안 · 472-5) | 서버 정본 설계 · 자리 · 표본 · invalidate 때 비움 · 칸 셋 = 떠남 · invalidate 때 비움 |
|
||||
| ③ 설계 가게 | `design_store.ts:226` useDesignStore · `:151` 메모리 칸 · `:160` 1 s | 읽고 쓰는 창구 · 쓰면 듣는 쪽 부름 |
|
||||
| ④ 세션 | `A00_Common/b_page_state.ts` 등록부 | draft = 사용자 값 · result = 계산 · 서버 사본 · [[design_page/session_keys]] |
|
||||
| ⑤ 계산 묶음 | `logic/design_pipeline.ts:33` 단계 · `:44` 뒤 단계 · `:49` STAGE_DEPS · `design_pipeline_page.ts:187-192` dispose | 차례 번호 · 옛 차례 버림 · 입력 대 묶음 맞춤 최대 3 바퀴(`:116`) |
|
||||
| ⑤ 묶음 → 가게 | `design_pipeline_page_publish.ts` publishPagePipeline · shown · opened · recheck | 측점 · 종단 · 발자국 냄 · 조용한 차례 = 상세 측점 |
|
||||
| ⑥ 서버 파생 | `_derived/haul/*` · `B05_Profile/route_recommend/*` · `04_detail_cache.pkl` | 입력 지문 다를 때만 다시 셈 · 지워도 다시 셈 |
|
||||
| ⑦ 서버 작업본 | DB `routes` · `route_points` · `route_statistics` · `longitudinal_sections` · `cross_sections` · 파일 `pipe_points.json` · `structures.json` · `plus_stations.json` · 노선 파일 · 곡선표 · 종단 파일(`mass_haul`) · 3D corridor | [저장] · [확정] · [초기화]만 씀(예외 = [[design_page/rules]]) |
|
||||
| ⑧ 초기값 | `initial_snapshot/` · `common_util_initial_snapshot.py:38-54 · 266-327` | 자동설계 체인 · 추천 [적용] 직후만 씀 · [초기화]가 읽음 |
|
||||
|
||||
## 묶음 단계
|
||||
|
||||
| 단계 | 기대는 값(STAGE_DEPS) | 모듈 | 낳는 값 |
|
||||
|---|---|---|---|
|
||||
| route | input.route | `logic/B05_Profile_Pipeline_RouteGround.ts` | 노선 선 · 곡선 |
|
||||
| follow | route | `logic/B05_Profile_Pipeline_Grade.ts` · `common_util_route_follow.ts` | 관 · 구조물 · 계획고 · 추가 측점 따라감 |
|
||||
| stations | route · pipes · structures · plusStations | `logic/design_pipeline_stations.ts` | 측점 목록 · 열쇠 |
|
||||
| ground | stations | `logic/B05_Profile_Pipeline_RouteGround.ts` | 측점 지반 표본 |
|
||||
| planline | ground | `logic/B05_Profile_Pipeline_Grade.ts` | 첫 계획선 base PVI |
|
||||
| profile | planline · alignment | `logic/B05_Profile_Pipeline_Grade.ts` | 측점 계획고 |
|
||||
| cross | profile · ground · cross · pipes · structures | `logic/B06_Section_Cross_Stage.ts` · 저장 노선 = `B06_Section_Cross_Refresh.ts` | 횡단 설계선 · 면적 |
|
||||
| footprint | cross · ground | `logic/B05_Profile_Footprint_Stage.ts` | 평면 발자국 |
|
||||
| drainage(뒤) | route · pipes | 배수판 `pipelineStage` → 서버 | 배수유역 |
|
||||
| corridor(뒤) | profile · cross | `design_pipeline_page.ts:355-358` | 3D 낡음 표시 |
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
type: concept
|
||||
status: stable
|
||||
related_pages: ["[[design_page/index]]", "[[design_page/operations_b06]]", "[[design_page/operations_common]]", "[[design_page/values]]", "[[design_page/rules]]"]
|
||||
last_updated: 2026-10-19
|
||||
source: ["docs/raw/조사/2026-10-19_470_B05B06_데이터흐름.md", "docs/raw/조사/2026-10-19_465_요구272_값안변함.md"]
|
||||
---
|
||||
|
||||
# B05 조작 흐름 — 평면 · 시설 · 종단
|
||||
|
||||
- 「묶음 전 단계」 = route → … → footprint 한 바퀴 + 가게 측점 냄 → 상세 갈아끼움(`swapSectionStations` `B06_Section_Section_Store.ts:230`) → 누가토량 다시 그림.
|
||||
- 갈아끼움 표본 규칙 = 노선 초안 차례면 새 · 옮긴 측점만 브라우저 표본 · 아니면 정본 기하 · 표본 그대로([[design_page/rules]]).
|
||||
- 「[저장] 때」 = 서버가 입력으로 정본을 다시 셈 · 브라우저 결과를 받아 적지 않음.
|
||||
|
||||
## 평면(노선)
|
||||
|
||||
| 조작 | 처음 놓이는 곳 | 브라우저 즉시 셈(모듈) | 서버 뒤 셈(API) | [저장]·[확정] 때 | 사본 | 위치(파일:줄) |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 교점 끌기 | 편집기 메모리(planned · curveRadius · curveArc) → 놓을 때 세션 `route-draft` · 첫 초안이면 초안 전 상세를 hold | 묶음 전 단계(노선 초안 차례 · 새 표본) | 지반 대체 `/route/elevations` · 배수 `/drainage/live` · 유토 배분 `/haul-plan` | `/design/save` `route` 칸 — 서버가 노선 · 측점 · 표본 · 횡단 처음부터 · 관 · 구조물 다시 얹기 · 세션 횡단 고침 넘김(Node) · 뒤 차례 = 새 노선 · 전부 아니면 없음 | 11 | `B05_Profile_UI_RouteEdit.ts:126-145` · `B05_Profile_UI_RouteEdit_Pointer.ts:387-430` · `design_pipeline_page.ts:636-659` · `B05_Profile_UI_RouteEdit_Apply.ts:94-135` → `B06_Section_Router_DesignSave.py:128-145` → `B05_Profile_Router_Replan.py:552-605` → `B05_Profile_Route_Save.py:337-510` |
|
||||
| 방향키(0.1 m · Shift 1 m) | 교점 끌기와 같음 | 같음 | 같음 | 같음 | 11 | `B05_Profile_UI_RouteEdit_Keys.ts:61-64 · 118-159` |
|
||||
| 교점 추가 · 삭제 | 편집기 메모리 → `route-draft` | 묶음 전 단계 · 측점 수 바뀜 → 갈아끼움 | 같음 | 같음 | 11 | `B05_Profile_UI_RouteEdit_Input.ts:149-158` · `logic/B05_Profile_UI_RouteEdit_Edits.ts:410-426` |
|
||||
| 곡선 R · L | 편집기 `curveRadius` · `curveArc` → `route-draft` | 묶음 전 단계 · 확폭 · MC 이동 → 곡선 열쇠 옮김(`curveMoves`) | 같음 | `/design/save` `route` 칸 — 넣은 R · L 로 서버가 같은 규칙 | 11 | `B05_Profile_UI_RouteEdit_CurveBar.ts:76-162` · `logic/B05_Profile_Sections_Stations.ts:332-337` · `B05_Profile_Router_Replan.py:582 · 599-600` |
|
||||
| 스냅 켬 · 간격 | 간격 = pref `routeedit-snap-step` · 켬 = `route-draft.stationSnap` | 켬 = 곡선 IP 를 옮김(한 걸음) → 묶음 전 단계 | — | 곡선표 파일 · 서버는 측점만 맞춤(IP 맞춤은 브라우저만) | 3 | `B05_Profile_UI_RouteEdit_SnapStep.ts:74-78` · `logic/design_pipeline_stations.ts:52-59` · `B05_Profile_Engine_Sections_Core.py:457-464` |
|
||||
| 추천 노선 [적용] | result `routeedit-initial` → `route-draft` | 묶음 한 바퀴 | 미리 셈 `/route/initial` → `route_recommend/*` | **저장 밖** — 바로 `/route/replan`(as_initial) · 종단 = 새 규칙 초기 종단 · 횡단 = 기본 설계(옛 계획고 · 편집 · L · 횡단 손 입력 안 옮김 · 476 · 요구 277) · [저장]과 같은 잠금 · 초기값 다시 뜸 · 초안 전부 지움 · 페이지 다시 세움 | 11 | `B05_Profile_UI_RouteEdit_Initial.ts:139 · 161-181` · `B05_Profile_UI_RouteEdit_Apply.ts:211 · 220-245` · `B05_Profile_Router_Replan.py:659-662` |
|
||||
|
||||
## 시설(관 · 구조물 · 추가 측점)
|
||||
|
||||
| 조작 | 처음 놓이는 곳 | 브라우저 즉시 셈(모듈) | 서버 뒤 셈(API) | [저장]·[확정] 때 | 사본 | 위치(파일:줄) |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 구조물 추가 · 옮김 · 지움 | 보기 메모리(B05 `ownStructures` · B06 `structures`) → 시설 줄 `set` → 세션 `structures` | 시설 줄(알약 · 비정규 측점 · 소단 사본 `berm`) · 묶음 stations → 갈아끼움(정본 표본 유지) · 짝 있는 옮김만 종단 꼭짓점 따라감 · 짝 없는 새 측점 = 꼭짓점 안 세움 · cross 면적 | — | `PUT /route/structures`(초안 그대로) → 횡단 차례 측점 세움 · 면적 Node | 11 | `design_shared.ts:303-322` · `B05_Profile_UI_Page_Structures.ts:98-118` · `design_facility_lane.ts:209-217` · `common_util_structure_station.ts:356` · `B05_Profile_Structures_Router.py:386-405` |
|
||||
| 관 추가 · 옮김 · 지움(B05 배수판) | 배수판 메모리(`facilityStore` · `pipeEditor`) → 세션 `pipes` · `pipe-ids` | 구조물과 같음 · 관 길이 다시 셈 · 일반 측점 자리 관 지움 = 갈아끼움 · 관 지움 ↶ = 그 측점 정본 설계 · 관 초안 없는 걸음으로 ↶ = 저장분(배수판 · 시설 줄 · 알약 · 목록 · 배수유역도 함께 · 472 덧) · 열린 관 폼은 ↶ ↷ 로 옮겨진 관을 따라감(`B05_Profile_UI_Structures_Panel.ts` setPipeFacilities) | 배수유역 `/drainage/detail-basins`(저장 안 함) | `PUT /drainage/pipe-points`(관 차례) · 관 옵션 예약 있으면 같은 파일 한 번 더(관 옵션 차례) · 서버 유역 · 측점 세움 | 14 | `B05_Profile_UI_Drainage_Panel.ts:150-153 · 405-421 · 574-655` · `B05_Profile_Api_Pipes_Draft.ts:30-37` · `B04_PreProcess_Router_Basins.py:538 · 574-633` |
|
||||
| 추가 측점 | 세션 `plus-stations` | 시설 줄 · 묶음 stations · 갈아끼움 · 미리보기 `/section/preview-stations` · 꼭짓점 안 세움 | — | `PUT /section/plus-stations` · 서버 x · y · 측점 세움 | 5 | `B05_Profile_UI_Profile_CrossModal.ts:205-246` · `B05_Profile_Api_PlusStations.ts:44-76 · 128-145` · `B06_Section_Router_Stations.py:428-472` |
|
||||
|
||||
## 종단(계획고)
|
||||
|
||||
| 조작 | 처음 놓이는 곳 | 브라우저 즉시 셈(모듈) | 서버 뒤 셈(API) | [저장]·[확정] 때 | 사본 | 위치(파일:줄) |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 계획고 ▲▼ | 가게 `alignmentDraft` → 세션 `alignment-draft`(지금 값 = 초안 ?? 가게 `alignmentSaved` · 모듈 사본 없음) | 종단선 · 묶음 profile → cross(전 측점 횡단 · 면적) → 누가토량 · 알약 | 유토 배분(400 ms 뒤 · Node · `_derived/haul`) | `PUT /route/profile-alignment`(편집 델타) · 파이썬 종단 다시 셈 · 횡단 차례 Node 면적 · 누가토량 | 8 | `design_profile.ts:311-344` · `B05_Profile_UI_Profile_Edit.ts:113-258` · `logic/B05_Profile_UI_Profile_Alignment.ts:566-603` · `B05_Profile_Router.py:416-500` → `B05_Profile_Engine_Grade_Profile.py:537-575` |
|
||||
| 직선화 | ▲▼ 와 같음(R = L/Δ 로 적음) | ▲▼ 와 같음 · 실시간 띠 | 같음 | 같음 | 8 | `B05_Profile_UI_Profile_Panel_Tools.ts:133-158` · `logic/B05_Profile_UI_Profile_Straighten.ts` · `logic/B05_Profile_Profile_CurveLaw.ts:21` |
|
||||
| 자유지정 | 그리기 자체 되돌리기 → 끝나면 ▲▼ 와 같음 | 같음 | 같음 | 같음 | 8 | `logic/design_profile_free.ts:127-170 · 224-313` |
|
||||
| 쉬프트(직선 구간 올림 · 내림) | ▲▼ 와 같음 | 같음 · 붉은 강조 | 같음 | 같음 | 8 | `B05_Profile_UI_Profile_Tools.ts:111-114 · 216-217` |
|
||||
| 종단 R(L 칸) | `curve_radii` → `alignment-draft` | 같음 | 같음 | 같음 · 서버는 R 로 받음 | 8 | `B05_Profile_UI_Profile_Table.ts:360-413` · `logic/B05_Profile_UI_Profile_Alignment.ts:685-695` |
|
||||
| 시 · 종점 계획고 | `alignment-draft`(끝 측점 ▲▼) | 같음 | 같음 | 같음 | 8 | `B05_Profile_UI_Panel_Settings.ts:113-133` · `logic/B05_Profile_EndOffsets.ts:43` |
|
||||
| 페이지 설정(설계속도 · 지형 · 기준 · 최소고 강제) | 가게 `pageSettings` → 세션 `pagesettings`(최소고 강제 = 편집 가드가 이 칸을 바로 읽음) | 최대 기울기만 종단에 얹음 · 횡단 안 셈 · ↶ ↷ 뒤 가게가 폼 · 기울기 한도 · 시작 측점을 다시 부름 | — | `PUT /route/page-settings`(설정 차례) · 서버는 경고만 다시(기하 그대로) | 4 | `B05_Profile_UI_Panel.ts:130-142` · `B05_Profile_Api_PageSettings.ts:47-85` · `B05_Profile_UI_Page_Latest.ts:92-128` · `design_profile.ts:645` · `design_view_profile.ts:355` · `B05_Profile_PageSettings.py:79-107` |
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
type: concept
|
||||
status: stable
|
||||
related_pages: ["[[design_page/index]]", "[[design_page/operations_b05]]", "[[design_page/operations_common]]", "[[design_page/values]]", "[[design_page/rules]]"]
|
||||
last_updated: 2026-10-19
|
||||
source: ["docs/raw/조사/2026-10-19_470_B05B06_데이터흐름.md", "docs/raw/조사/2026-10-19_465_요구272_값안변함.md"]
|
||||
---
|
||||
|
||||
# B06 조작 흐름 — 횡단 카드 · 조정창 · 저장 밖 조작
|
||||
|
||||
- 「횡단 다시 셈」 = 묶음 cross 강제(`forceCross` · `pipelineCross`) → 저장 노선은 `refreshLocally`(`B06_Section_Cross_Refresh.ts:330`) 전 측점 `computeCrossDesign` · 면적 · 누가토량.
|
||||
- 다시 셈 입력 물러섬 = 세션 고름 → 저장 정본 설계(`savedDesigns` `B06_Section_Cross_Refresh.ts:402-408`) → config 기본값.
|
||||
- 횡단 [저장] 한 요청 = `/sections/{노선}/save` 패치(`crossPatchesFromCache` `B06_Section_Section_Store.ts:449`) → 파이썬 설계선 다시 · Node 면적(`B06_Section_Router_Confirm.py:174-187 · 275-357` → `B06_Section_Server_Calc_Prebuild.py:232-384` → `B06_Section_Server_Calc_Node.ts:94-135`).
|
||||
|
||||
## 카드 · 조정
|
||||
|
||||
| 조작 | 처음 놓이는 곳 | 브라우저 즉시 셈(모듈) | 서버 뒤 셈(API) | [저장]·[확정] 때 | 사본 | 위치(파일:줄) |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 카드 단추(지반 · 단면 · 측구 · 포장 · 2단) | 세션 `crossdesign` 만 · 바꾼 칸만(앞서 바꾼 칸에 얹음 · 안 바꾼 지반 · 단면을 안 박음) · 메모리 상세 설계 안 덮음 | 횡단 다시 셈 · 카드 = 다시 셈 알림(`onCrossDesignsUpdated`)이 그림 | — | 횡단 패치 · 파이썬 설계선 다시 | 5 | `B06_Section_UI_Cross_Design.ts:278-289` · `B06_Section_UI_Page_Design_Sync.ts:59-104` · `B06_Section_Cross_Design_Session.ts:44-52` |
|
||||
| 설계선 쉬프트(노견 확장) | 세션 `shoulderext` | 횡단 다시 셈 | — | 횡단 패치 | 5 | `B06_Section_UI_Cross_Design_Pick.ts:402` · `B06_Section_UI_Page_Design_Sync.ts:71-76` · `B06_Section_Cross_Refresh.ts:393-397` |
|
||||
| 소단 구간(구조물) | 구조물 → 시설 줄 `syncBerm` 이 세션 `berm`(같으면 안 씀 · 쓰면 cross 한 번 · B05 만 열어도) | 횡단 다시 셈 · 사본 없으면 저장분 `design.berm` | — | 서버가 `structures.json` 에서(`load_berm_spans`) | 6 | `design_facility_lane.ts:151-166` · `B06_Section_Cross_Refresh.ts:362-368 · 521-522` · `B06_Section_Engine_Structures_Wall.py:124` |
|
||||
| 소단 깊이 ▲▼ | 세션 `bermdepth` | 횡단 다시 셈 | — | 횡단 패치 | 5 | `B06_Section_UI_Cross_Design_Pick.ts:408` · `B06_Section_UI_Page_Design_Sync.ts:78-83` · `B06_Section_Cross_Refresh.ts:513-519` |
|
||||
| 암선 · 암 경사 | 세션 칸 `rockb` · `cutslope`(값 주인 · 읽을 때마다 칸 · 메모리 사본 없음 · ↶ ↷ 가 칸을 바꾸면 카드 · 면적 다시) | 횡단 다시 셈(암 측점 2단 무릎 · 면적) | — | 횡단 패치 · 파이썬 설계선 | 5 | `B06_Section_UI_Cross_Line_Cell.ts:161 · 166` · `B06_Section_UI_Cross_CutSlope.ts:94 · 114` · `B06_Section_Cross_Refresh.ts:229-243 · 306` |
|
||||
| 조정창(흙막이 · 집수정 · 유입 · 경간 · 세월교 · BOX · 연동) | 세션 칸(`revetx` · `revetlink` · `basinadjust` · `inletstruct` · `extrawall` · `extraspan` · `fordadjust` · `boxadjust` · 값 주인 · 메모리 사본 없음 · 창구 = b_page_state 열쇠 `B06_Section_Session_Cells`) · 빈 칸 = 칸 지움 · 관 옵션 = `culvertopt` | 카드 다시 · 구조물 면적 · 누가토량 · ↶ ↷ 가 칸을 바꾸면 상세 얹기(고치기 전 칸 + 지금 칸) · 카드 · 면적 다시 | — | 횡단 패치 · Node 구조물 면적 · 관 옵션 = 관 옵션 차례 `pipe_points` PUT | 6 | `B06_Section_UI_Page_Station_Controls.ts` · `B06_Section_Session_Cells.ts` · `B06_Section_Section_Adjusts.ts` noteAdjustBase · withDraftAdjusts · `design_view_cross.ts` onRestore · `logic/B06_Section_UI_Page_Patches.ts:41-136` |
|
||||
| 반폭 ◀▶ | 측점 = 세션 `crossw` · 전역 = `cross-display` | 표시만 · 횡단 전체 강제 다시 셈(헛일) | — | 측점 반폭만 패치 · `cross-display` 안 감 | 3 | `B06_Section_UI_Cross_View.ts:664-670` · `design_view_cross.ts:251-263 · 448-457` · `B06_Section_Display_Width.ts:20-23` |
|
||||
| 표준횡단 · [전체 반영] | 세션 `std-cross` — 저장분(없으면 config)과 다를 때만 · 열기만 = 칸 없음 · 계산 = 세션 → 저장분 → config([폐기]에도 안 지움) | 전 측점 다시 셈(지문에 표준 포함) | — | `standard_cross_section` 으로 · 서버 설계선 다시 | 3 | `B06_Section_UI_Standard_Panel.ts:303-325 · 450-458` · `B06_Section_Api_Fetch.ts:101-111` · `B06_Section_Router_Confirm.py:157-162` |
|
||||
|
||||
## 시설 · 평면 · 저장 밖
|
||||
|
||||
| 조작 | 처음 놓이는 곳 | 브라우저 즉시 셈(모듈) | 서버 뒤 셈(API) | [저장]·[확정] 때 | 사본 | 위치(파일:줄) |
|
||||
|---|---|---|---|---|---|---|
|
||||
| B06 폼 관 측점 · 옵션 고침 | `pipeFacilities` → 세션 `pipes` · 옮긴 자리(정수 m)로 옵션 · 캐시 얹기 · 집계 찾음 | B05 관 옮김과 같음 | 배수유역 다시 나눔 | 관 차례 `PUT /drainage/pipe-points` | 14 | `B06_Section_UI_Page_Structures_Panel.ts:300-345 · 536-560` |
|
||||
| B06 구조물 패널 | 보기 `structures` → 시설 줄 `set` → 세션 `structures` | B05 구조물과 같음 | — | `PUT /route/structures` | 11 | `B06_Section_UI_Page_Structures_Panel.ts:211-227 · 582-598` |
|
||||
| B06 평면 패널 끌기 | B05 교점 끌기와 같음(같은 편집기를 옮겨 붙임) | 같음 · B06 쪽 갈아끼움이 먼저 | 같음 | `/design/save` `route` 칸 | 11 | `design_plan_peek.ts:61-77` · `B05_Profile_UI_RouteEdit.ts:290-293 · 599-601` · `B06_Section_UI_Page_Station_Swap.ts:35` |
|
||||
| 넓힘(표본 폭 넘음) | 없음 — 확인창 뒤 바로 서버 | 응답 뒤 상세 강제 다시 받음 | `/sections/{노선}/regenerate` | **저장 밖** — DB 바로 씀(이미 정본) | 3 | `B06_Section_UI_Page_Common.ts:32-63` · `B06_Section_Router.py:378-388` · `B06_Section_Router_Widen.py:80` |
|
||||
| [측점 만들기] | 없음 — 바로 서버 | 응답 뒤 상세 비움 · 강제 다시 받음 · 알림 | `/section/missing-stations` | **저장 밖** — 종단 · 횡단 파일 바로 씀 | 3 | `B06_Section_UI_Missing_Stations.ts:86-138` · `B06_Section_Router_Stations.py:236-296 · 365-407` |
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
type: concept
|
||||
status: stable
|
||||
related_pages: ["[[design_page/index]]", "[[design_page/operations_b05]]", "[[design_page/operations_b06]]", "[[design_page/session_keys]]", "[[design_page/rules]]"]
|
||||
last_updated: 2026-10-19
|
||||
source: ["docs/raw/조사/2026-10-19_470_B05B06_데이터흐름.md", "docs/raw/조사/2026-10-19_465_요구272_값안변함.md", "PLAN.md 466 · 467 · 468"]
|
||||
---
|
||||
|
||||
# 공통 조작 흐름 — 되돌리기 · 보기 · 수명 · 저장
|
||||
|
||||
## 조작 표
|
||||
|
||||
| 조작 | 처음 놓이는 곳 | 브라우저 즉시 셈(모듈) | 서버 뒤 셈(API) | [저장]·[확정] 때 | 사본 | 위치(파일:줄) |
|
||||
|---|---|---|---|---|---|---|
|
||||
| ↶ ↷ | 사진첩 = 세션 draft 칸 글(고름 · 관 id 등 뺌) · 빈 칸 「""」·「{}」·「null」 = 칸 없음 · 「[]」 = 값 | 칸 되씀 → 종단 · 시설 · 노선 다시 먹임 · 횡단 입력 물러섬 = 저장 정본 · 관 초안 없는 걸음 = 저장분(배수판이 마지막 저장분 답과 견줘 다르면 다시 읽음) · ↶ ↷ 켜짐 = 쌓임 · 되돌림 알림(`change`)으로 바로 | — | — | 사진 n | `B05_Profile_UI_Profile_History.ts:50 · 57 · 186 · 226-245` · `design_pipeline_facilities.ts:62-69` · `B05_Profile_UI_RouteEdit_History.ts:174-180` · `B05_Profile_UI_Profile_Panel_Tools.ts` restorePipes · `B05_Profile_UI_Drainage_Panel.ts` setPipeChainages |
|
||||
| B05 ↔ B06 전환 | 없음(보기 숨김 · 보임) | 보인 보기가 가게 · 세션 다시 읽음 · 입력 대 묶음 맞춤(최대 3 바퀴) · 시설 줄 · 묶음은 숨은 동안도 셈 | — | — | — | `design_page.ts:209-262` · `design_pipeline_page.ts:559-582` · `design_view_cross.ts:557-565` · `design_view_profile.ts:427` |
|
||||
| 종단 판 접기 | pref `profile-collapsed` | 본문 0 크기 = 파생값(낡음 예약 · 유토 총괄값 · 배분 선반입 · 측점 자료)만 셈 · 그리기만 건너뜀 | — | — | — | `B05_Profile_UI_Profile_Render.ts:131-159 · 211-226` · `B05_Profile_UI_Profile_Drag.ts:43-58` |
|
||||
| 팝아웃 | 없음 — 종단 판 DOM 만 새 창 | 같은 함수 · 같은 가게 | — | — | — | `design_profile_popout.ts:87-132` |
|
||||
| 새로고침 | 세션 남음 · 메모리 전부 잃음 | 세션 상세로 먼저 섬 → 서버 정본과 비교(`checkSessionCanon`) → 다르면 갈아씀 · 노선 초안 있으면 hold · 관 · 구조물 · 추가 측점 초안 있으면 측점 냄 · 없으면 조용히 열고 가게 측점 = 상세 측점 | 상세 GET(정본 확인) | — | — | `B06_Section_Section_Store.ts:117-135` · `design_pipeline_page.ts:528-545` · `design_pipeline_page_publish.ts` opened · shown |
|
||||
| 떠남 [취소] | 없음 — 페이지 그대로 | — | — | — | — | `design_leave.ts:95-123` |
|
||||
| 떠남 [폐기] | 세션 고침 칸 지움(모든 노선 · `NOT_EDITS` · B08 · B09 칸 뺌) → 떠남 = dispose | dispose = 묶음 · 시설 줄 · 페이지 자료(`forgetPageData`) · 상세 메모리(`forgetSectionMemory`) 버림 · 다시 열면 새로고침 길 | — | — | — | `design_leave.ts:27-42 · 85-87` · `design_page.ts:286-295` · `B06_Section_Section_Store.ts:424-428` |
|
||||
| [저장] | — | 앞 단계 `prepare` = 전 측점 강제 다시 셈(서버가 면적 버림) · 화면 잠금 | 조각 한 본문 `POST /design/save`(서버가 아래 차례 · 전부 아니면 없음 · 노선 바뀜도) · 서버 정본 다시 셈 | 끝 = `invalidateSectionDetail` · 두 보기가 새 상세 한 객체 받음 · 노선 바뀜 = 페이지 다시 세움 | — | `design_shared.ts:395-448` · `design_save.ts:183-237` |
|
||||
| [확정] | — | [저장]과 같음 | [저장] + 빈 측점 기본 설계 · 단계 완료 · 측구 쪽을 B05 종단 파일로 되씀 | 끝 = `invalidateSectionDetail` → B07 로 넘어감 · 노선이 바뀐 [확정] = [저장]으로 · 다시 [확정] 알림 | — | `design_shared.ts:426-432` · `design_save.ts:224-228` · `B06_Section_Router_Confirm.py:368-496` |
|
||||
| [초기화] | — | 상세 비움 · 새로고침(메모리 전부 버림) | `/route/reset` | DB 노선 행 지우고 초기값으로 · 파일 트리 복원 · 세션 초안 전부 지움(B08 · B09 뺌) | — | `design_reset.ts:30-62` · `B05_Profile_Router_Lifecycle.py:234-390` · `common_util_initial_snapshot.py:38-54 · 266-327` |
|
||||
|
||||
## [저장] 차례(서버 `B06_Section_Router_DesignSave.py:99-195` · 칸 = `design_save_api.ts` SAVE_PART_KEYS)
|
||||
|
||||
- [저장] · [확정] = 한 본문 한 요청(472-6) — 브라우저가 차례마다 조각(본문 · 저장 뒤 정리)을 모아 `POST /design/save`(`design_save.ts:183-237`) · 서버가 아래 차례로 안쪽 창구 함수를 부름 · 하나라도 실패 = DB 행 · storage 되돌림(`common_util_save_guard.py`) · 차례 이름째 알림 · 다 저장한 뒤에만 조각 정리 · 같은 프로젝트 저장 겹침 = 뒤 요청 기다림.
|
||||
- 노선 바뀐 [저장](6-B) = 같은 본문에 `route`(노선 초안) · `carry`(옛 노선 세션 횡단 고침 원본 `routeCarryInputs` `B06_Section_Station_Keys.ts:314`) · 기본 조각만(화면 몫 건너뜀) · 늘 [저장](노선이 바뀐 [확정] = 노선만 저장 · 다시 [확정] 알림) · 되돌림 범위 + 배수 폴더 · 지표면 확정 · 다 저장하고 페이지를 다시 세운 뒤 옛 노선 번호 칸(노선 범위 초안 `clearRouteDrafts`) · `route-draft-hold` 비움(472-5).
|
||||
|
||||
| 차례 | 브라우저 조각 | 서버 | 노선 바뀐 [저장] | 위치(파일:줄) |
|
||||
|---|---|---|---|---|
|
||||
| route | `pendingRouteSave` → 본문 `route` | `replan_route`(`/route/replan` 체인 · 관 · 구조물 · 추가 측점 좌표 따라감 · 종횡단 · 계획고 이월) | 맨 앞 · 새 노선 id · 관 · 구조물 초안 다시 얹기 = Node `facility.rebase`(`rebasePipeDraft` · `rebaseStructureDraft`) · 구조물 판번호 = 저장 뒤 판 | `B06_Section_Router_DesignSave.py:128-145` · `B06_Section_Router_DesignSave_Route.py` |
|
||||
| settings | `pageSettingsPart` | `save_page_settings` | 그대로 | `design_save.ts:137` |
|
||||
| pipes | `pipesPart` + 화면 몫(배수판 다시 받음 · PUT 없음) | `put_pipe_points`(`confirm` = 배수유역 다시 셈) | 기본 조각만 · 다시 얹은 목록 | `design_save.ts:139` |
|
||||
| structures | `structuresPart` | `write_structures` | 기본 조각만 · 다시 얹은 목록 | `design_save.ts:141` |
|
||||
| plusStations | `plusStationsPart` | `put_plus_stations` | 기본 조각만 | `design_save.ts:143` |
|
||||
| alignment | `alignmentPart` | `save_profile_alignment` | 노선 초안 칸(0) ?? 옛 노선 칸 → 서버가 새 노선 번호로 · 저장 뒤 두 칸 비움 | `design_save.ts:115-126` |
|
||||
| culvertOptions | `culvertOptionsPart`(`culvertopt` · 노선 열쇠) | 저장 관 위에 얹어 `pipe_points.json` 다시 씀 | 옛 노선 열쇠(누가거리)로 읽음 | `B06_Section_Api_Culvert_Options.ts:102-106` |
|
||||
| uphill | `routePartsBody` | `confirm_latest_route`(상단측 · 비정규 측점 횡단) | 안 보냄 · 서버도 안 받음 · 저장 뒤 세션 비우고 수 알림(요구 274) | `design_save.ts:149-155` |
|
||||
| cross | `ctx.crossPart(confirm)` = 횡단 patch | `save_sections` · `confirm_sections` · 재계산 맨 앞 종단 경고 다시 씀 | 옛 노선 세션 칸 → 새 측점 patch = Node `stations.carry`(`routeCarryPatches` `B06_Section_Route_Carry_Patch.ts:368` · 넘김 규칙 `logic/B06_Section_Route_Carry.ts`) · 고침이 없어도 한 번 · 표준 횡단면 · 풍선 = `routeCarryPart` | `B06_Section_Router_DesignSave.py:172-195` · `B05_Profile_UI_RouteEdit_Apply.ts:157` |
|
||||
|
||||
- 저장 중 = `saving` 표시 · 묶음 맞춤은 저장 뒤로 미룸(`design_pipeline_page.ts` saving 분기).
|
||||
@@ -0,0 +1,27 @@
|
||||
## 개요
|
||||
|
||||
- 주소 둘(`#/b05-profile` 종단 · `#/b06-section` 횡단) = 페이지 하나 · 캐시를 같이 쓰고 [저장] 한 번에 함께 나감.
|
||||
- 값의 길: 사용자 조작 → 화면 메모리 → 설계 가게 → 세션(sessionStorage) → 계산 묶음(노선 → 따라감 → 측점 → 지반 → 첫 계획선 → 종단 → 횡단 → 발자국) → [저장] · [확정] 때 서버가 정본을 다시 셈.
|
||||
- 서버 층: 작업본(DB · 정본 파일) · 초기값(`initial_snapshot/`) · 파생 저장본(`_derived/` · 입력 지문 다를 때만 다시 셈).
|
||||
- B05 · B06 을 고치기 전 이 페이지와 고칠 묶음 화면을 읽음 · 흐름을 바꾸면 같은 커밋에 손글(`docs/guide/B05_B06/`)도 고침.
|
||||
|
||||
## 지킬 규칙
|
||||
|
||||
- 파생값 = 입력(저장 정본 + 세션 초안)만의 함수 — 보기 차례 · 접힘 · 메모리 · 새로고침 · 들어온 길로 갈리지 않음.
|
||||
- [저장] · [확정] = 서버가 입력만으로 정본을 다시 셈 · 브라우저 결과(면적 · 표본 · 측점)를 받아 적지 않음.
|
||||
- 자동저장(디바운스 PUT) 없음 · 화면 잠금 = [저장] · [확정] · [초기화] · 추천 [적용]만.
|
||||
- [초기화] = 초기값을 작업본에 덮어씀(다시 셈 아님).
|
||||
- 상세 가게 메모리 = 페이지 수명 — 떠남 = [[forgetSectionMemory]] · [저장] · [확정] · [초기화] · 노선 저장 = [[invalidateSectionDetail]].
|
||||
- 정본 자리 = 값을 낳은 주인 — 노선 시설 = `pipe_points.json` · `structures.json` · 횡단 값 = 횡단 패치.
|
||||
- 측점 열쇠 = 한 벌([[common_util_station_key#stationKey]] · 파이썬 짝) · 노선 바뀜 = 관 · 구조물은 좌표(x · y)로 따라가고 누가거리는 다시 매김.
|
||||
- 되돌리기 걸음 = 사용자 조작으로 값이 실제로 바뀐 때만 · 빈 칸(「""」「{}」「null」) = 칸 없음.
|
||||
- 새 계산 = 셋 중 하나를 먼저 고름 — ① 파이썬 · TS 짝(거울 시험) · ② TS 를 서버 Node 로 · ③ 서버 단독.
|
||||
- 새 세션 칸 = `A00_Common/b_page_state.ts` 등록부 한 줄 + 저장 타입.
|
||||
|
||||
## 사용자 결정
|
||||
|
||||
- 저장 밖 정본 쓰기 셋 — 넓힘 · [측점 만들기] · 추천 [적용](즉시 저장 · 확인창 · 요구 275 ②).
|
||||
- 추천 [적용] = 새 노선 기준 초기화 — 옛 계획고 · 편집 · 횡단 손 입력 안 넘김(요구 277).
|
||||
- 노선이 바뀌면 자리 칸(암 경계 · 2단 경사 · 손 상단측)은 같은 자리 측점만 넘김 · 새 자리 = 기본값(요구 275).
|
||||
- 단면 모양 기본값 짝 다름(브라우저 「left_cut」 · 서버 기본 표) — 사용자 협의 전 손대지 않음.
|
||||
- 속도 목표는 기능 완성 뒤 따로 봄.
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
type: concept
|
||||
status: stable
|
||||
related_pages: ["[[design_page/index]]", "[[design_page/operations_common]]", "[[design_page/values]]", "[[design_page/calculations]]", "[[design_data_lifecycle]]"]
|
||||
last_updated: 2026-10-19
|
||||
source: ["CLAUDE.md §5", "docs/raw/조사/2026-10-19_465_요구272_값안변함.md", "docs/raw/조사/2026-10-19_470_B05B06_데이터흐름.md", "PLAN.md 466 · 467 · 468 · 472"]
|
||||
---
|
||||
|
||||
# B05 · B06 지킬 규칙
|
||||
|
||||
## 불변 규칙
|
||||
|
||||
| 규칙 | 지키는 자리(파일:줄) | 시험 |
|
||||
|---|---|---|
|
||||
| 파생값 = 입력(저장 정본 + 세션 초안)만의 함수 — 보기 차례 · 접힘 · 메모리 · 새로고침 · 들어온 길로 안 갈림 | `logic/design_pipeline.ts:49` STAGE_DEPS · `design_pipeline_page.ts:545-568` reconcile | `test_272_*` |
|
||||
| 상세 가게 메모리 = 페이지 수명 — 떠남 = `forgetSectionMemory` · [저장] · [확정] · [초기화] · 노선 저장 = `invalidateSectionDetail` | `design_page.ts:294` · `design_shared.ts:428 · 435` · `design_reset.ts:41` · `design_view_profile.ts:469` · `B05_Profile_UI_RouteEdit_Apply.ts:171 · 211` | `test_272_detail_memory` |
|
||||
| [폐기] = 세션 고침 칸만 지움 · 메모리는 떠남(dispose)이 비움 | `design_leave.ts:85-87` · `design_page.ts:286-295` | `test_346_4_design_leave` |
|
||||
| 조용한 차례(열 때 · 노선 초안 버림) 끝 = 가게 측점 · 종단 = 지금 상세 측점 · 저장 기준 · 같은 목록은 다시 안 냄 | `design_pipeline_page_publish.ts` shown · opened · `design_pipeline_page.ts:392-393 · 530` | `test_272_drop_store_stations` |
|
||||
| 노선 초안 없는 차례 = 서버 정본 측점 기하 · 표본을 브라우저 값으로 안 덮음(같은 자리 0.01 m · 축 1e-3 → 앞 횡단 → 그 자리 옛 횡단 → 저장 정본) · 지금 상세에 없던 측점이 정본 자리로 돌아옴(관 지움 ↶) = 정본 설계 | `logic/B06_Section_Station_Swap.ts:171-182 · 241-249 · 304-315` · `B06_Section_Section_Store.ts:241-248` | `test_272_swap_keeps_canon` |
|
||||
| 노선 초안 차례 = 새 · 옮긴 측점만 브라우저 지반 표본 | `logic/B06_Section_Station_Swap.ts:249-251` | `test_346_2_station_swap` |
|
||||
| 다시 셈 입력 물러섬 = 세션 고름 → 저장 정본 설계 → config 기본값 · 고름 단추는 메모리 상세 설계를 안 덮음 | `B06_Section_Cross_Refresh.ts:402-408` · `B06_Section_UI_Page_Design_Sync.ts:59-104` | `test_272_choice_undo` |
|
||||
| 소단 사본 쓰는 자리 = 시설 줄 하나(B05 만 열어도) · 같으면 안 씀 · 사본 없으면 저장분 `design.berm` | `design_facility_lane.ts:151-166` · `B06_Section_Cross_Refresh.ts:362-368` | `test_272_berm_source` |
|
||||
| 두 보기가 보는 파생값은 페이지 한 벌이 숨은 동안도 셈 · 보기는 그리기만 · 접힘 = 그리기만 건너뜀 | `design_facility_lane.ts:1-12` · `B05_Profile_UI_Profile_Render.ts:211-226` | `test_272_hidden_summary` · `test_271_facility_lane` |
|
||||
| 되돌리기 걸음 = 사용자 조작으로 값이 실제로 바뀐 때만 · 빈 칸(「""」「{}」「null」) = 칸 없음 · 빈 칸 쓰기 = 칸 지움 | `B05_Profile_UI_Profile_History.ts:50 · 235-236` · `B06_Section_Session_Cells.ts` | `test_272_history_empty_cells` |
|
||||
| 시설 줄 측점 = 종류 표 · 구조물 · 관 알림이 다 온 뒤만 심음 · 그 전 종단도 = 저장 측점 · 저장 기준 그대로(모름 ≠ 빈 목록) | `design_facility_lane.ts` known · `design_profile.ts` planted · `design_profile_base.ts` listedBase · `design_view_profile_sections.ts` stationsReady · `B05_Profile_UI_Profile_Render.ts` deriveProfile | `test_473_lane_ready` |
|
||||
| 짝 없는 새 측점(구조물 · 추가 측점 · 새 관) = 계획선 꼭짓점 안 세움 · 짝 있는 옮긴 측점 = 꼭짓점 따라감 | `common_util_structure_station.ts:356` · `B05_Profile_Router_Confirm.py:306-400` | `test_427_1_follow_seat` · `test_459_route_seat_mirror` |
|
||||
| 구조물 측점 다시 뜸([저장] 상단측 차례 · 측점 맞춤) = 같은 자리 옛 줄의 상단측 출처(`user` 면 값도) · 포장 제안 표기 들고 감 · 새 자리 포장 표기 = 지금 계획선 | `B05_Profile_Router_Confirm.py:426-465` | `test_472_station_user_fields` |
|
||||
| [저장] · [확정] = 서버가 입력만으로 정본 다시 셈 · 브라우저 결과(면적 · 표본 · 측점) 받아 적지 않음 | `design_save.ts:58-68` · `B06_Section_Router_Confirm.py:203-215` | `test_b06_save_recompute` |
|
||||
| 자동저장(디바운스 PUT) 없음 · 화면 잠금 = [저장] · [확정] · [초기화] · 추천 [적용]만 | `design_save.ts:249` | — |
|
||||
| 측점 열쇠 = `common_util_station_key` 한 벌 · 노선 바뀜 = 관 · 구조물 좌표(x · y) 따라감 · 누가거리 다시 매김 | `common_util_station_key.ts:32` · `common_util_route_follow.ts:291` | `test_344_3_station_key_mirror` · `test_410_follow_xy` |
|
||||
| 노선 바뀜 넘김 = 열쇠마다 지반 · 측구 종류 · 측구 선택 · 사면 쉬프트 · 소단 · 소단 깊이 · 자리 칸(`SEAT_KEYS` = 암 경계 · 암 절토 경사 · 2단 경사 · 손 상단측 = 단면 유형 · 측구 방향)은 같은 자리(`sameSeat` · 중심 0.01 m · 축 1e-3) 측점만 · 새 자리 = 기본값 · 세션 값은 쓴 때 자리(`@seat`)로 판정(↶ ↷ 유지) · 끌기 갈아끼움 · 미리보기 · Node 넘김 · 서버 노선 저장 한 규칙 | `logic/B06_Section_Station_Swap.ts` SEAT_KEYS · unseated · `logic/B06_Section_Route_Carry.ts` carryKeyedStates · `B05_Profile/logic/B05_Profile_Pipeline_Detail.ts` crossAtSeat · `B05_Profile_Route_Save_Parts.py` carry_cross_inputs | `test_475_3_seat_carry` |
|
||||
| 노선 초안 ↶(버림) = 차례 하나 · 버림 동안 종단도 그리기 멈춤 · 낡음 다시 셈 미룸 → 되돌림 · 저장 기준 맞춤 · 뒤 차례 끝에 한 번 그림(사이 섞인 유토곡선 없음) | `design_pipeline_page.ts:642 · 403` · `B06_Section_Section_Store.ts:316` | `test_472_5c_drop_once` |
|
||||
| 추천 [적용] = 새 노선 기준 초기화 — 종단 = 새 규칙 초기 종단(끔 · 균형 셈 실패 = 1차 계획선 438 · L 20 · 켬 = 균형 종단을 꼭짓점에 얹음) · 옛 계획고 · 편집 · L · 횡단 손 입력 안 넘김(요구 277 · 측점마다 기본 설계) · 손 노선 저장만 옮김 · 넘김 | `B05_Profile_Route_Save.py` run_route_save_chain `initial` · carries_cross_inputs · 미리 보기 = `B05_Profile_Engine_InitialProfile`(같은 조각) | `test_476_1_apply_initial` · `test_476_1_recommend_initial` |
|
||||
| B05 · B06 캐시 = 한 벌 · [저장] 한 번에 함께 나감 | `design_shared.ts:395-448` · `design_save.ts:237` | — |
|
||||
| 관 · 구조물 주인 = 가게 칸 하나 — 화면 사본은 가게 알림으로 맞춤(제 쓰기는 같은 값 · 남 고침은 다시 실음) | `design_view_profile.ts` 가게 구독 · `design_view_cross.ts` onShop · `B06_Section_UI_Page_Structures_Panel.ts` seen | `test_472_5_shop_owner` |
|
||||
| 정본 자리 = 값을 낳은 주인 — 노선 시설 = `pipe_points.json` · `structures.json` · 횡단 값 = 횡단 패치 | `B05_Profile_Api_Pipes_Draft.ts:30-37` · `B05_Profile_Structures_Router.py:386-405` · `B06_Section_Section_Store.ts:449` | — |
|
||||
| 열기만 = 초안 칸 없음 — 값이 저장분과 같으면 칸 없음(표준 횡단 = 결과 칸 `section-context` 에서 읽음) · 카드 고름 = 바꾼 칸만 · 소단 사본 = 떠남 확인 밖 | `B06_Section_UI_Standard_Panel.ts` effectiveStandardCross · persist · `B06_Section_UI_Cross_Design.ts` emit · `B06_Section_UI_Page_Design_Sync.ts` handleDesignChange · `design_leave.ts` NOT_EDITS | `test_472_open_no_draft` |
|
||||
| 관 자리 읽기 한 벌 = 지금 노선에 얹은 관 지점(측점 계획 · 관 지점 GET · 서버 상세 형상 얹기 · 카드 이름 · 세월교 노면 하강 · 물넘이포장 구간 · B07 관 글 · 화면) — 지문 어긋난 옛 저장분도 같은 자리 · 관 파일 누가거리를 그대로 읽지 않음 | `B05_Profile_Engine_Sections.py:249` route_pipe_points | `test_472_d_pipe_route_attach` · `test_472_d_route_pipe_readers` |
|
||||
|
||||
## 바꿀 때 할 일
|
||||
|
||||
| 경우 | 할 일 |
|
||||
|---|---|
|
||||
| 새 계산 | CLAUDE.md §5 ① 짝 · ② TS → 서버 Node · ③ 서버 단독 중 먼저 고름 · [[design_page/calculations]] 한 줄 |
|
||||
| ① 짝 | 두 파일 머리에 짝 · 거울 시험 이름 · `resources/tester` 거울 시험 |
|
||||
| ② TS → Node | 끌려가는 파일 모듈 맨 위 DOM · window · sessionStorage 금지 · 묶음 다시 지음 |
|
||||
| 조작 · 흐름 바꿈 | 같은 커밋에 [[design_page/operations_b05]] · [[design_page/operations_b06]] · [[design_page/operations_common]] · [[design_page/values]] 고침 |
|
||||
| 새 세션 칸 | `b_page_state.ts` 등록부 · 저장 타입 · [[design_page/session_keys]] |
|
||||
| 새 메모리 사본 | 비우는 때(떠남 · [폐기] · [저장] · [확정] · [초기화])를 정하고 [[design_page/values]] 사본 칸에 적음 |
|
||||
| 줄 번호 | 고친 함수의 줄이 바뀌면 이 폴더 줄 번호도 고침 |
|
||||
|
||||
## 예외 · 남은 틈
|
||||
|
||||
| 내용 | 자리(파일:줄) | 상태 |
|
||||
|---|---|---|
|
||||
| 저장 밖 정본 쓰기 셋 — 넓힘 `/regenerate` · [측점 만들기] · 추천 [적용](즉시 저장 · [저장]과 같은 프로젝트 잠금 · 떠 두고 실패면 되돌림 · 476 ④) | `B06_Section_Router.py:378-388` · `B06_Section_Router_Stations.py:236-296` · `B05_Profile_UI_RouteEdit_Apply.ts:220-245` · `B05_Profile_Router_Replan.py` replan_route | 사용자 확정(요구 275 ②) · 확인창 |
|
||||
| `pipe_points.json` 한 [저장]에 두 번 PUT(관 · 관 옵션) | `B06_Section_Api_Culvert_Options.ts:67-95` | 코드 확인 |
|
||||
| 노선 바뀐 [저장]의 관 옵션 차례 = 옛 노선 열쇠(누가거리 · 관이 따라 옮긴 새 자리와 안 맞으면 안 얹힘) | `design_save.ts:145` · `B06_Section_Router_DesignSave.py` `_patched_points` | 추정 · 옛 길과 같음 |
|
||||
| ↶ ↷ 가 세션을 직접 되씀 — 관 · 구조물 · 추가 측점은 되쓴 뒤 가게 듣는 쪽을 부름(`announceDesignStore` · 472-5) · B06 조정창 = 세션 칸이 주인(사본 없음) · 칸이 바뀐 걸음만 상세 얹기 · 카드 · 면적 다시(472-5 5-D) · 암선 · 암 경사 · 상단측 = 세션 칸이 주인(사본 없음 · 472-5) | `B05_Profile_UI_Profile_History.ts` apply · `design_store.ts` announceDesignStore · `B06_Section_UI_Page_Station_Controls.ts` onRestore · `B06_Section_UI_Page_Persist.ts` readCells | 관 · 구조물 = `test_472_5_shop_owner` · 관 초안 없는 걸음 = 저장분 · ↶ 켜짐 = `test_472_d_b05_pipe_undo` · 조정창 = `test_472_5d_adjust_owner` · 암선 · 암 경사 = `test_472_5r_rock_owner` |
|
||||
| 스냅 켬 · 간격 = 되돌리기 밖 | `B05_Profile_UI_RouteEdit_SnapStep.ts:74-78` | 추정 |
|
||||
| `flushDesignStore` 부름 없음 — 멈춘 뒤 1 s 안 새로고침 = 가게 세션 사본 잃음(다시 셈) | `design_store.ts:179` | 코드 확인 |
|
||||
| 「[]」 — 되돌리기 = 값 · 떠남 확인 = 빈 칸 | `B05_Profile_UI_Profile_History.ts:50` · `design_leave.ts:41-42` | 코드 확인 |
|
||||
| 단면 모양 기본값 짝 다름(X1) | `B06_Section_Cross_Refresh.ts:438` · `B06_Section_Router_Design.py:402` | 사용자 협의 |
|
||||
@@ -1,50 +0,0 @@
|
||||
---
|
||||
type: concept
|
||||
status: stable
|
||||
related_pages: ["[[design_page/index]]", "[[design_page/values]]", "[[design_page/operations_common]]"]
|
||||
last_updated: 2026-10-19
|
||||
source: ["docs/raw/조사/2026-10-19_470_B05B06_데이터흐름.md", "A00_Common/b_page_state.ts"]
|
||||
---
|
||||
|
||||
# 세션 열쇠 등록부 — B05 · B06
|
||||
|
||||
- 등록부 = `A00_Common/b_page_state.ts` `STATE_REGISTRY` · 열쇠 꼴 `aislo:{통}:{이름}:{프로젝트}[:{노선}]` · 없는 이름은 저장 안 됨(콘솔 알림).
|
||||
- 통 — draft = 사용자 값([폐기] · [저장] 대상) · result = 계산 · 서버 사본 · pref = localStorage(화면 자리).
|
||||
- 새 칸 = 등록부 한 줄 + 저장 타입 + 이 표 한 줄 · [폐기] 안 지울 칸은 `NOT_EDITS`(`design_leave.ts:27-34`).
|
||||
|
||||
## draft
|
||||
|
||||
| 열쇠 | 위치(`b_page_state.ts`:줄) | 쓰는 쪽 | [저장] 차례 |
|
||||
|---|---|---|---|
|
||||
| `route-draft` · `route-draft-hold` | `:218` · `:220` | 노선 편집기 다리 · 초안 전 상세 | route · hold 는 안 감 · 노선 저장 뒤 hold 비움 |
|
||||
| `alignment-draft` | `:213` | 종단 편집(노선 · 초안 노선 0) | alignment |
|
||||
| `pagesettings` | `:215` | 페이지 설정 폼 | settings |
|
||||
| `pipes` · `structures` · `plus-stations` | `:136` · `:127` · `:129` | 시설 줄 · 배수판 · B06 패널 · 추가 측점 창 | pipes · structures · plusStations |
|
||||
| `culvertopt` · `kindreset` | `:154` · `:152` | 관 조정창 옵션 예약 · 종류 바꾼 관 | culvertOptions · cross(structure_reset) |
|
||||
| `uphill` | `:131` | 상단측 | uphill(노선 바뀌면 cross 뒤) |
|
||||
| `crossdesign` · `shoulderext` · `bermdepth` | `:196` · `:198` · `:200` | 카드 단추(바꾼 칸만 · 앞서 바꾼 칸에 얹음) · 설계선 쉬프트 · 소단 깊이 | cross |
|
||||
| `rockb` · `cutslope` · `crossw` | `:158` · `:162` · `:184` | 암선 · 암 경사 · 측점 반폭 | cross |
|
||||
| `revetx` · `revetlink` · `basinadjust` · `inletstruct` · `extrawall` · `extraspan` · `fordadjust` · `boxadjust` | `:185-192` | 조정창 칸(값 주인 · 창구 = b_page_state 열쇠 `B06_Section_Session_Cells` · 메모리 사본 없음 · 빈 칸 = 칸 지움) | cross |
|
||||
| `structure-notes` · `structure-note-cads` · `detail-table` | `:121` · `:123` · `:125` | [상세] 모달 | cross |
|
||||
| `std-cross` | `:175` | 표준횡단 패널 — 저장분(`section-context` 의 `stored_standard_cross_section`) · 없으면 config 와 다를 때만 · 같으면 칸 없음([폐기] · [저장]에도 안 지움 · [초기화]만) | cross(`standard_cross_section`) |
|
||||
| `berm` | `:166` | 시설 줄 `syncBerm` 만(구조물 파생 · 열 때 씀 · 떠남 확인 밖) | 안 감 — 서버가 `structures.json` 에서 |
|
||||
| `cross-display` | `:177` | 전역 표시 반폭 | 안 감 |
|
||||
| `selection` · `structure-pick` · `pipe-ids` · `pipe-option-queue` · `structure-type-cache` | `:148` · `:138` · `:144` · `:146` · `:150` | 고름 · 관 id 장부 · 옵션 넘김 · 종류 캐시 | 안 감 · `structure-type-cache` 는 저장 뒤 비움 |
|
||||
|
||||
## result
|
||||
|
||||
| 열쇠 | 위치(`b_page_state.ts`:줄) | 쓰는 쪽 | 비우는 때 |
|
||||
|---|---|---|---|
|
||||
| `latest` | `:224` | 노선 최신 응답 | 노선 저장 · 추천 [적용] 뒤 |
|
||||
| `section-context` · `std-cross-default` · `rock-boundary-default` | `:230` · `:234` · `:240` | 횡단 맥락 · 서버 기본값 | 새로 받을 때 |
|
||||
| `section-detail` | `:232` | 상세 서버 정본 사본(브라우저 셈 안 실음) | `invalidateSectionDetail` |
|
||||
| `routeedit-initial` | `:247` | 추천 노선 | 추천 다시 셈 |
|
||||
| `design-stations` · `design-profile` · `design-footprint` | `:249-251` | 가게 메모리 칸의 세션 사본(멈춘 뒤 1 s) | 가게 칸 지움 |
|
||||
|
||||
## pref(localStorage)
|
||||
|
||||
| 열쇠 | 위치(`b_page_state.ts`:줄) | 쓰는 쪽 |
|
||||
|---|---|---|
|
||||
| `profile-collapsed` · `profile-height` · `plan-peek-height` | `:48` · `:53` · `:55` | 종단 판 접힘 · 높이 · B06 평면 패널 높이 |
|
||||
| `routeedit-view` · `routeedit-locks` · `routeedit-terrain` · `routeedit-snap-step` | `:72-78` | 평면 보기 · 자물쇠 · 지형 · 스냅 간격 |
|
||||
| `masshaul-*` · `table-*` · `section-panel-*` · `cross-columns` · `mass-haul-badge-pos` | `:79-113` | 유토 판 · 표 · 횡단 판 자리 |
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
type: concept
|
||||
status: stable
|
||||
related_pages: ["[[design_page/index]]", "[[design_page/calculations]]", "[[design_page/session_keys]]", "[[design_page/rules]]"]
|
||||
last_updated: 2026-10-19
|
||||
source: ["docs/raw/조사/2026-10-19_470_B05B06_데이터흐름.md", "docs/raw/조사/2026-10-19_472_구조다시세움_목록.md", "PLAN.md 466 · 467 · 468"]
|
||||
---
|
||||
|
||||
# 값별 주인 · 사본 · 셈 자리
|
||||
|
||||
- 주인 = 서버 작업본(정본) 자리 · 브라우저 쪽 창구 = 설계 가게 칸(있으면) · 나머지는 사본.
|
||||
- 셈 자리 — TS = 브라우저 · 파이썬 = 서버 [저장] 정본 · Node = 같은 TS 를 서버 묶음으로 · 짝 = TS ↔ 파이썬 두 벌(거울 시험 필수).
|
||||
|
||||
| 값 | 주인(정본) | 사본 | 셈 자리 · 짝 · 거울 시험 | 맞추는 때 |
|
||||
|---|---|---|---|---|
|
||||
| 평면 선형(교점 · R · L · 스냅) | 노선 파일 · 곡선표 · DB `routes` · `route_points` | 편집기 메모리(`B05_Profile_UI_RouteEdit.ts:124-137`) · 사진첩 · pref `routeedit-locks` · `routeedit-snap-step` · 세션 `route-draft` · `route-draft-hold` · 가게 `routeDraft` · 묶음 입력 · `latest` | TS `logic/B05_Profile_UI_RouteEdit_Curve.ts:243` ↔ 파이썬 `common_util_route_polyline.py:579` · `test_route_polyline_browser_mirror` | 놓을 때 세션 · [저장]은 편집기 메모리를 먼저 읽음 · 저장 뒤 페이지 다시 세움 |
|
||||
| 측점 목록 · 열쇠 | DB `cross_sections` 행 · 종단 파일 stations · 서버 `generate_sections` | 묶음 stations · 가게 `stations`(메모리) → 세션 `design-stations` · 상세 cache · 세션 `section-detail` · canon · `design_pipeline_page.ts` savedStations(sub2 관 · 구조물 줄로 셈 · 남김) · `B06_Section_Station_Keys.ts:92` 목록 | TS `logic/design_pipeline_stations.ts` · `logic/B05_Profile_Sections_Stations.ts:276` ↔ 파이썬 `B05_Profile_Engine_Sections_Core.py:449` · 열쇠 `common_util_station_key.ts:32` ↔ `.py:32` · `test_344_3_pipeline_stations_mirror` · `test_344_3_station_key_mirror` | 가게 `stations` 알림 → 갈아끼움(구독 둘 · 같은 목록이면 안 함) · 조용한 차례 끝 = 상세 측점(`design_pipeline_page_publish.ts` shown) · [저장] 뒤 비움 |
|
||||
| 측점 자리 · 축 | 서버 측점 틀(저장 경로 폴리라인) | 브라우저 측점 틀(`/route/plan` planned 선) · 상세 cache frame | TS `logic/B05_Profile_UI_RouteEdit_Frame.ts:61` ↔ 파이썬 `B05_Profile_Engine_Sections_Core.py:449-519`(같은 식 · 입력 선 다름 · mm 차) · `test_b05_routeedit_frame_mirror` | 노선 초안 없는 차례 = 정본 자리 · 축 유지(`logic/B06_Section_Station_Swap.ts:238-247`) |
|
||||
| 지반 표본 | 서버 `sample_xy`(npz · 저장 노선 선) → `cross_*.json` samples | 지형 격자(`B05_Profile_Api_TerrainGrid.ts:29-31` · IndexedDB) · 묶음 ground · 상세 cache samples | TS `common_util_surface_sampler.ts:65` ↔ 파이썬 `common_util_surface_sampler.py:210` · `test_344_1_surface_sampler_mirror` | 갈아끼움 — 노선 초안 없음 = 같은 자리(0.01 m · 축 1e-3) 앞 횡단 → 그 자리 옛 횡단 → 저장 정본 표본 · 노선 초안 = 새 · 옮긴 측점만 브라우저 표본 |
|
||||
| 계획고 편집(▲▼ · 직선화 · 자유 · 쉬프트 · 시종점) | 종단 파일 `profile_alignment.edits` | ★ 가게 `alignmentDraft`(세션 `alignment-draft`) · 가게 `alignmentSaved`(서버 저장분 · 메모리 · `B05_Profile_UI_Profile_Edit.ts:127 · 235`)(노선 · 초안 노선 0) · 묶음 입력 · 가게 `profile` → `design-profile` · 상세 `longitudinal` | TS `logic/B05_Profile_UI_Profile_Alignment.ts:401` ↔ 파이썬 `B05_Profile_Engine_Grade_Alignment.py:494` · `test_426_profile_straight` · `test_432_1_curve_law` | 커밋마다 세션 · [저장] = 서버 다시 셈 |
|
||||
| 종단 R | 같은 `edits.curve_radii` | 위와 같음 · 칸은 L · 저장은 R | TS `logic/B05_Profile_Profile_CurveLaw.ts` ↔ `B05_Profile_Engine_Grade_CurveLaw.py` · `test_432_1_curve_law` | 위와 같음 · R = L/Δ 로 적어 Δ 바뀌면 L 흐름 |
|
||||
| 계획선 꼭짓점(시설 측점) | 종단 파일 base PVI | 묶음 planline · 종단도 | TS `common_util_structure_station.ts:356` followMovedNodes ↔ 파이썬 `B05_Profile_Router_Confirm.py:306` `_follow_moved_stations` · `:220` `_seat_born_nodes` · `test_427_1_follow_seat` · `test_459_route_seat_mirror` | 짝 있는 옮긴 측점 = 꼭짓점 따라감 · 짝 없는 새 측점 = 안 세움 |
|
||||
| 횡단 입력(카드 · 조정) | DB `cross_sections.design` 패치 칸 | ★ 세션 칸(`crossdesign` · 조정창 칸 등 · 조정 칸 창구 = b_page_state 열쇠 `B06_Section_Session_Cells` · 메모리 사본 없음 · 472-5 5-D) · cache 설계 칸(조작 즉시 얹음 · 얹기 = 고치기 전 칸 + 지금 세션 칸) | 입력 도구(셈 없음) | 조작마다 세션 · [저장] 횡단 패치 |
|
||||
| 횡단 설계 결과(설계선) | DB `cross_sections.design` | ★ 상세 가게 — cache(살아 있는 객체 · 제자리 고침) · 칸 셋(`B06_Section_Section_Canon.ts:93-143` 저장 정본 상세 · 초안 전 hold 상세 · 노선 초안 상세 = `pipelineDetail` · 472-5) · 세션 `section-detail`(서버 사본만) · canon 설계 사본 · 묶음 cross — 계산 묶음 페이지는 지문(노선 · 표본)만 | TS `common_util_cross_design.ts:267` ↔ 파이썬 `B06_Section_Engine_Design.py:195` · `test_b06_cross_design_mirror` | 다시 셈마다 cache 제자리 · 입력 물러섬 = 저장 정본(`B06_Section_Cross_Refresh.ts:402-408`) · 페이지 수명 · [저장]·[확정]·[초기화] 뒤 비움 |
|
||||
| 측점 면적 | DB 면적 — 표준 측점 = 파이썬 · 구조물 측점 = Node | cache 면적 · 묶음 cross | TS `common_util_cross_design_areas.ts` ↔ 파이썬 `B06_Section_Engine_Areas.py` · 구조물 = `B06_Section_Structure_Layouts.ts`(Node 한 벌) | [저장] 때 서버가 브라우저 면적 버리고 다시 셈 |
|
||||
| 누가토량 | 종단 파일 `mass_haul` | 그릴 때 셈 · 배지 · `_derived/haul` · `B05_Profile_Api_HaulPlan.ts:28` kept | TS `common_util_mass_haul.ts:426` 한 벌(브라우저 · Node) | 그릴 때(접혀도 셈) · [저장] |
|
||||
| 구조물 | `structures.json` | `B05_Profile_UI_Page_Structures.ts:78` · `B06_Section_UI_Page_Structures_Panel.ts:175` · 시설 줄 `design_facility_lane.ts:97` · savedStructures · `design_page_data.ts:25` · 세션 `structures` · `structure-type-cache` · cache | 따라감 TS `common_util_route_follow.ts:291` ↔ 파이썬 `common_util_route_follow.py:120` · `test_410_follow_xy` | 주인 = 가게 `structures`(세션) · 사본은 화면 사본 — 가게가 바뀌면(되돌리기 포함 · `announceDesignStore`) 종단 목록 `takeStructures`(보일 때) · 횡단 판 `foreign()` → 다시 실음 · 숨은 보기는 보일 때 load |
|
||||
| 관 | `pipe_points.json` | 배수판 둘(`B05_Profile_UI_Drainage_Panel.ts:123 · 142`) · `B06_Section_UI_Page_Structures_Panel.ts:176` · 시설 줄 `:101` · `design_view_profile.ts:116` · savedPipes · `design_pipe_ids.ts:24` · 세션 `pipes` · `pipe-ids` · `culvertopt` · `kindreset` · `pipe-option-queue` · cache culvert · 유역 pkl | 관 길이 TS `design_structure_pipe_calc.ts` · `B06_Section_Structure_Layouts.ts:95`(Node 한 벌) · `test_b06_pipe_length` | 주인 = 가게 `pipes` · 배수 편집기는 가게가 바뀌면 숨어도 `adoptPipes`(제 쓰기 메아리 빼고) · 횡단 판 `foreign()` → 다시 실음 · 고름 · 알약 관 = 시설 줄(`shownPipes`) · 배수 다시 나눔 응답 · [저장] 뒤 다시 받음 |
|
||||
| 추가 측점 | `plus_stations.json` | `B05_Profile_Api_PlusStations.ts:14 · 74 · 76` · 세션 `plus-stations` | 따라감 TS `followPlusDraft` ↔ 파이썬 `B05_Profile_Plus_Stations.py:82` · `test_459_route_seat_mirror` | 노선 초안 follow 뒤 · [저장] |
|
||||
| 소단 구간 · 깊이 | 구간 = `structures.json` · 깊이 = DB 패치 | 세션 `berm`(시설 줄만 씀) · `bermdepth` · cache `design.berm` · 초안 노선 `bermAt`(`design_shared.ts:248`) | TS `B06_Section_UI_Page_Persist.ts:157` bermSpansFromStructures ↔ 파이썬 `B06_Section_Engine_Structures_Wall.py:124` load_berm_spans · `test_272_berm_source` | 구조물 바뀜 → 시설 줄 `syncBerm`(같으면 안 씀) → cross 한 번 · 사본 없으면 저장분 |
|
||||
| 페이지 설정 | DB 단계 2 설정 · 종단 정책 | ★ 가게 `pageSettings`(세션 `pagesettings`) — 폼 · 종단도 기울기 한도 · 시작 측점이 듣고 최소고 강제는 바로 읽음 | 정책 파이썬 기준표 ↔ TS `B05_Profile_UI_Panel_Fields.ts` · `test_442_2_grade_limit_mirror` | 열 때 두 보기가 얹음 · [저장] 설정 차례 |
|
||||
| 표준 횡단 | 종단 행 `standard_cross_section` | 세션 `std-cross` · `std-cross-default` · `section-context` | 서버 설계선 다시 | [전체 반영] · [저장] |
|
||||
| 3D | corridor 파일(Node) | `B05_Profile_UI_Corridor.ts:45` cache | Node `B05_Profile_Corridor_Prebuild.py` | 노선 초안 동안 낡음 표시 · [저장] 뒤 |
|
||||
| 되돌리기 사진첩 | 없음(메모리 · 탭 수명) | 사진 n 장 = 세션 draft 칸 글 | — | 조작 뒤 `record()` · 같은 사진이면 걸음 아님 |
|
||||
@@ -0,0 +1,52 @@
|
||||
# 프로그램 문서 사이트(PLAN 478) — 원고 = docs/tools/build.py 가 _build/src 에 만듦
|
||||
site_name: Aislo 프로그램 문서
|
||||
docs_dir: _build/src
|
||||
site_dir: _build/site
|
||||
use_directory_urls: false
|
||||
|
||||
theme:
|
||||
name: material
|
||||
language: ko
|
||||
features:
|
||||
- navigation.sections
|
||||
- navigation.indexes
|
||||
- toc.integrate
|
||||
- search.highlight
|
||||
|
||||
plugins:
|
||||
- search:
|
||||
separator: '[\s\-_.]+'
|
||||
- autorefs
|
||||
- mkdocstrings:
|
||||
handlers:
|
||||
python:
|
||||
paths: [..]
|
||||
options:
|
||||
show_source: false
|
||||
show_root_heading: true
|
||||
heading_level: 2
|
||||
members_order: source
|
||||
filters: []
|
||||
docstring_style: null
|
||||
show_signature_annotations: true
|
||||
separate_signature: true
|
||||
|
||||
hooks:
|
||||
- tools/nav_hook.py
|
||||
|
||||
markdown_extensions:
|
||||
- attr_list
|
||||
- md_in_html
|
||||
- admonition
|
||||
- tables
|
||||
- toc:
|
||||
permalink: true
|
||||
- pymdownx.superfences:
|
||||
custom_fences:
|
||||
- name: mermaid
|
||||
class: mermaid
|
||||
format: !!python/name:pymdownx.superfences.fence_code_format
|
||||
|
||||
validation:
|
||||
links:
|
||||
anchors: warn
|
||||
@@ -0,0 +1,6 @@
|
||||
# 문서 사이트 도구 (PLAN 478)
|
||||
|
||||
- 설치 — `npm --prefix docs/tools install` · `venv/Scripts/python.exe -m pip install -r docs/tools/requirements.txt`
|
||||
- 원고 — `venv/Scripts/python.exe docs/tools/build.py` (추출 `docs/_data` → `docs/_build/src` · `--site` = 사이트까지 · `--stamp` = 손글 지문 고침)
|
||||
- 띄우기 — `cd docs && ../venv/Scripts/python.exe -m mkdocs serve -a 127.0.0.1:8090`
|
||||
- 묶음 규칙 — `groups.yml` · 손글 — `docs/guide/<페이지>/page.md` · `groups/<묶음>.md`
|
||||
@@ -0,0 +1,457 @@
|
||||
"""문서 사이트 원고 만들기(PLAN 478-2).
|
||||
|
||||
추출(docs/_data/*.json) + 손글(docs/guide/<페이지>/page.md · groups/<묶음>.md) → docs/_build/src
|
||||
index.md 처음
|
||||
<페이지>/index.md 개요 · 묶음 상자 · 묶음 사이 흐름 그림 · 손글(규칙 · 사용자 결정)
|
||||
<페이지>/<묶음>.md 손글 설명 · 묶음 안 흐름 그림 · 세션 칸 · 서버 주소 · 함수 칸
|
||||
<페이지>/none.md 묶음 없음
|
||||
ts/ · py/ 함수 화면 — TypeDoc(markdown) · mkdocstrings(griffe)가 만듦
|
||||
.nav.json 왼쪽 메뉴(nav_hook.py 가 읽음)
|
||||
함수 목록 지문이 손글 지문과 다른 묶음 = 「설명 확인 필요」 · 맞으면 --stamp.
|
||||
|
||||
venv python docs/tools/build.py [--data D] [--out O] [--site] [--stamp]
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
import griffe
|
||||
import yaml
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
import group as grouping # noqa: E402
|
||||
|
||||
TOOLS = grouping.TOOLS
|
||||
DOCS = TOOLS.parent
|
||||
ROOT = DOCS.parent
|
||||
REF = re.compile(r"\[\[([^\]]+)\]\]")
|
||||
MAX_GRAPH = 40
|
||||
|
||||
|
||||
# ── 읽기 ────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def load_data(data: Path) -> dict:
|
||||
out = {}
|
||||
for name in ("functions", "session_keys", "api", "registry"):
|
||||
path = data / f"{name}.json"
|
||||
out[name] = json.loads(path.read_text(encoding="utf-8")) if path.exists() else []
|
||||
if not out["functions"]:
|
||||
raise SystemExit(f"추출 없음: {data / 'functions.json'}")
|
||||
return out
|
||||
|
||||
|
||||
def read_hand(path: Path) -> tuple[dict, str]:
|
||||
if not path.exists():
|
||||
return {}, ""
|
||||
text = path.read_text(encoding="utf-8")
|
||||
if text.startswith("---\n"):
|
||||
head, _, body = text[4:].partition("\n---\n")
|
||||
return yaml.safe_load(head) or {}, body.strip()
|
||||
return {}, text.strip()
|
||||
|
||||
|
||||
def stamp(path: Path, print_: str) -> None:
|
||||
meta, body = read_hand(path)
|
||||
meta["지문"] = print_
|
||||
head = yaml.safe_dump(meta, allow_unicode=True, sort_keys=False).strip()
|
||||
path.write_text(f"---\n{head}\n---\n\n{body}\n", encoding="utf-8")
|
||||
|
||||
|
||||
# ── 함수 화면(기성 도구) ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
def run_typedoc(ts_files: list[str], out: Path) -> dict[tuple[str, str], str]:
|
||||
"""TypeDoc 으로 ts/ 함수 화면 · (파일, 이름) → 원고 안 경로."""
|
||||
if not ts_files:
|
||||
return {}
|
||||
opts = {
|
||||
"entryPoints": [(ROOT / f).as_posix() for f in ts_files],
|
||||
"tsconfig": (ROOT / "tsconfig.json").as_posix(),
|
||||
"skipErrorChecking": True,
|
||||
"plugin": ["typedoc-plugin-markdown"],
|
||||
"out": (out / "src" / "ts").as_posix(),
|
||||
"json": (out / "typedoc.json").as_posix(),
|
||||
"readme": "none",
|
||||
"entryFileName": "index.md",
|
||||
"logLevel": "Error",
|
||||
}
|
||||
opt_file = out / "typedoc.options.json"
|
||||
opt_file.write_text(json.dumps(opts, ensure_ascii=False), encoding="utf-8")
|
||||
subprocess.run(
|
||||
["node", str(TOOLS / "node_modules/typedoc/bin/typedoc"), "--options", str(opt_file)],
|
||||
cwd=ROOT,
|
||||
check=True,
|
||||
)
|
||||
return typedoc_pages(out)
|
||||
|
||||
|
||||
def typedoc_pages(out: Path) -> dict[tuple[str, str], str]:
|
||||
project = json.loads((out / "typedoc.json").read_text(encoding="utf-8"))
|
||||
modules = [m for m in project.get("children", []) if m["kind"] == 2] or [
|
||||
{**project, "name": ""}
|
||||
]
|
||||
pages = {}
|
||||
for mod in modules:
|
||||
base = f"ts/{mod['name']}/" if mod["name"] else "ts/"
|
||||
for c in mod.get("children", []):
|
||||
folder = {64: "functions", 32: "variables"}.get(c["kind"])
|
||||
if folder and c.get("sources"):
|
||||
rel = f"{base}{folder}/{c['name']}.md"
|
||||
if (out / "src" / rel).exists():
|
||||
pages[(c["sources"][0]["fileName"], c["name"])] = rel
|
||||
return pages
|
||||
|
||||
|
||||
def py_ident(file: str) -> str:
|
||||
return file.removesuffix(".py").replace("/", ".")
|
||||
|
||||
|
||||
def griffe_names(file: str) -> set[str]:
|
||||
"""griffe 가 보는 함수 이름(모듈 함수 · 클래스 메서드 `Class.name`)."""
|
||||
mod = griffe.load(py_ident(file), search_paths=[str(ROOT)], allow_inspection=False)
|
||||
names: set[str] = set()
|
||||
|
||||
def walk(obj, prefix: str) -> None:
|
||||
for name, m in obj.members.items():
|
||||
if m.is_alias:
|
||||
continue
|
||||
if m.is_function:
|
||||
names.add(prefix + name)
|
||||
elif m.is_class:
|
||||
walk(m, f"{prefix}{name}.")
|
||||
|
||||
walk(mod, "")
|
||||
return names
|
||||
|
||||
|
||||
def write_py_pages(py_files: list[str], src: Path) -> dict[tuple[str, str], str]:
|
||||
"""mkdocstrings 원고 · (파일, 이름) → autorefs 식별자."""
|
||||
refs = {}
|
||||
lines = ["# 파이썬 함수 화면", ""]
|
||||
for file in py_files:
|
||||
ident = py_ident(file)
|
||||
(src / "py" / f"{ident}.md").write_text(f"# {ident}\n\n::: {ident}\n", encoding="utf-8")
|
||||
lines.append(f"- [{file}]({ident}.md)")
|
||||
refs.update({(file, n): f"{ident}.{n}" for n in griffe_names(file)})
|
||||
(src / "py" / "index.md").write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
return refs
|
||||
|
||||
|
||||
# ── 원고 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def anchor(fid: str) -> str:
|
||||
return "f-" + hashlib.sha1(fid.encode("utf-8")).hexdigest()[:10]
|
||||
|
||||
|
||||
def first_line(doc: str) -> str:
|
||||
return next((s.strip() for s in (doc or "").splitlines() if s.strip()), "")
|
||||
|
||||
|
||||
def owner_of(item: str, fns_by_id: dict, by_file: dict) -> dict | None:
|
||||
"""세션 칸 · 서버 주소의 `id` · `파일:줄` · `파일` → 함수."""
|
||||
if item in fns_by_id:
|
||||
return fns_by_id[item]
|
||||
if item[:3] in ("ts:", "py:"):
|
||||
item = item[3:]
|
||||
file, _, line = item.split("#")[0].partition(":")
|
||||
cands = by_file.get(file, [])
|
||||
if not cands:
|
||||
return None
|
||||
if line.isdigit():
|
||||
before = [f for f in cands if f.get("line", 0) <= int(line)]
|
||||
return before[-1] if before else cands[0]
|
||||
return cands[0]
|
||||
|
||||
|
||||
class Site:
|
||||
def __init__(self, data: dict, rules: dict, out: Path):
|
||||
self.data, self.rules, self.out = data, rules, out.resolve()
|
||||
self.src = self.out / "src"
|
||||
self.fns = data["functions"]
|
||||
self.by_id = {f["id"]: f for f in self.fns}
|
||||
self.by_file: dict[str, list] = {}
|
||||
for f in sorted(self.fns, key=lambda f: f.get("line", 0)):
|
||||
self.by_file.setdefault(f["file"], []).append(f)
|
||||
self.gid: dict[str, str] = {}
|
||||
self.missing_refs: list[str] = []
|
||||
self.stale: list[str] = []
|
||||
self.ts_pages: dict = {}
|
||||
self.py_refs: dict = {}
|
||||
|
||||
# 링크
|
||||
def fn_link(self, fid: str, label: str | None = None) -> str:
|
||||
f = self.by_id.get(fid)
|
||||
if f is None:
|
||||
return f"`{label or fid.split('#')[-1]}`"
|
||||
return f"[`{label or f['name']}`]({self.gid[fid]}.md#{anchor(fid)})"
|
||||
|
||||
def hand_refs(self, text: str, where: str) -> str:
|
||||
def sub(m: re.Match) -> str:
|
||||
ref = m.group(1).strip()
|
||||
hit = [
|
||||
f
|
||||
for f in self.fns
|
||||
if f["name"] == ref or f"{grouping.stem(f['file'])}#{f['name']}" == ref
|
||||
]
|
||||
if not hit:
|
||||
self.missing_refs.append(f"{where}: {ref}")
|
||||
return f"`{ref}` ⚠"
|
||||
return self.fn_link(hit[0]["id"])
|
||||
|
||||
return REF.sub(sub, text)
|
||||
|
||||
def tool_link(self, f: dict) -> str:
|
||||
if f["lang"] == "ts":
|
||||
rel = self.ts_pages.get((f["file"], f["name"]))
|
||||
return f"[자세히](../{rel})" if rel else ""
|
||||
ref = self.py_refs.get((f["file"], f["name"]))
|
||||
return f"[자세히][{ref}]" if ref else ""
|
||||
|
||||
# 함수 칸
|
||||
def fn_block(self, f: dict) -> str:
|
||||
params = ", ".join(
|
||||
p["name"] if isinstance(p, dict) else str(p) for p in f.get("params", [])
|
||||
)
|
||||
bits = [f"`{f['file']}:{f.get('line', 0)}`"]
|
||||
if params:
|
||||
bits.append(f"입력 `{params}`")
|
||||
if f.get("returns"):
|
||||
bits.append(f"출력 `{f['returns']}`")
|
||||
if link := self.tool_link(f):
|
||||
bits.append(link)
|
||||
lines = [
|
||||
f"### `{f['name']}` {{ #{anchor(f['id'])} }}",
|
||||
"",
|
||||
first_line(f.get("doc")) or "*설명 없음*",
|
||||
"",
|
||||
]
|
||||
lines += [" · ".join(bits), ""]
|
||||
callers = f.get("called_by", [])
|
||||
if callers:
|
||||
shown = " · ".join(self.fn_link(c) for c in callers[:8])
|
||||
more = f" 외 {len(callers) - 8}" if len(callers) > 8 else ""
|
||||
lines += [f"부르는 곳: {shown}{more}", ""]
|
||||
return "\n".join(lines)
|
||||
|
||||
def fn_graph(self, fns: list[dict]) -> str:
|
||||
ids = {f["id"] for f in fns}
|
||||
edges = [(f["id"], c) for f in fns for c in f.get("calls", []) if c in ids and c != f["id"]]
|
||||
if not edges:
|
||||
return ""
|
||||
degree = Counter(a for e in edges for a in e)
|
||||
keep = {i for i, _ in degree.most_common(MAX_GRAPH)}
|
||||
node = {i: f"n{k}" for k, i in enumerate(sorted(keep))}
|
||||
lines = ["```mermaid", "flowchart LR"]
|
||||
lines += [f' {node[i]}["{self.by_id[i]["name"]}"]' for i in sorted(keep)]
|
||||
lines += [
|
||||
f" {node[a]} --> {node[b]}" for a, b in sorted(set(edges)) if a in keep and b in keep
|
||||
]
|
||||
lines.append("```")
|
||||
note = f"\n\n많이 이어진 함수 {MAX_GRAPH} 개만 그림." if len(degree) > MAX_GRAPH else ""
|
||||
return "\n".join(lines) + note
|
||||
|
||||
def group_uses(self, gid: str) -> tuple[list[str], list[str]]:
|
||||
def mine(item: str) -> bool:
|
||||
o = owner_of(item, self.by_id, self.by_file)
|
||||
return o is not None and self.gid[o["id"]] == gid
|
||||
|
||||
keys = []
|
||||
for k in self.data["session_keys"]:
|
||||
reads, writes = (
|
||||
[r for r in k.get("reads", []) if mine(r)],
|
||||
[w for w in k.get("writes", []) if mine(w)],
|
||||
)
|
||||
if reads or writes:
|
||||
keys.append(f"| `{k['key']}` | {len(reads)} | {len(writes)} |")
|
||||
apis = []
|
||||
for a in self.data["api"]:
|
||||
calls = [c for c in a.get("callers", []) if mine(c)]
|
||||
if mine(a.get("handler", "")) or calls:
|
||||
handler = (
|
||||
self.fn_link(a["handler"])
|
||||
if a.get("handler") in self.by_id
|
||||
else f"`{a.get('handler', '')}`"
|
||||
)
|
||||
apis.append(f"| `{a['method']} {a['path']}` | {handler} | {len(calls)} |")
|
||||
return keys, apis
|
||||
|
||||
# 화면
|
||||
def write_group(self, page: dict, g: dict, fns: list[dict], hand_dir: Path) -> str:
|
||||
meta, body = read_hand(hand_dir / f"{g['id']}.md")
|
||||
now = grouping.fingerprint(fns)
|
||||
out = [f"# {g['title']}", ""]
|
||||
if meta.get("지문") != now:
|
||||
self.stale.append(g["id"])
|
||||
out += [
|
||||
'!!! warning "설명 확인 필요"',
|
||||
f" 함수 목록이 바뀜(손글 지문 `{meta.get('지문', '없음')}` · 지금 `{now}`).",
|
||||
"",
|
||||
]
|
||||
out += [self.hand_refs(body, f"groups/{g['id']}.md") or "*설명 없음*", ""]
|
||||
if graph := self.fn_graph(fns):
|
||||
out += ["## 묶음 안 흐름", "", graph, ""]
|
||||
keys, apis = self.group_uses(g["id"])
|
||||
if keys:
|
||||
out += ["## 쓰는 세션 칸", "", "| 칸 | 읽는 곳 | 쓰는 곳 |", "|---|---|---|", *keys, ""]
|
||||
if apis:
|
||||
out += [
|
||||
"## 서버 주소",
|
||||
"",
|
||||
"| 주소 | 받는 함수 | 부르는 곳 |",
|
||||
"|---|---|---|",
|
||||
*apis,
|
||||
"",
|
||||
]
|
||||
out += [f"## 함수 {len(fns)}", "", *(self.fn_block(f) for f in fns)]
|
||||
(self.src / page["id"] / f"{g['id']}.md").write_text("\n".join(out), encoding="utf-8")
|
||||
return first_line(body)
|
||||
|
||||
def write_page(self, page: dict, groups: dict[str, list[dict]]) -> list:
|
||||
hand_dir = DOCS / page["guide"]
|
||||
(self.src / page["id"]).mkdir(parents=True, exist_ok=True)
|
||||
cards, nav = [], [{"개요": f"{page['id']}/index.md"}]
|
||||
for g in page["groups"]:
|
||||
fns = groups[g["id"]]
|
||||
lead = self.write_group(page, g, fns, hand_dir / "groups")
|
||||
warn = " · ⚠ 설명 확인 필요" if g["id"] in self.stale else ""
|
||||
cards += [
|
||||
f"- **[{g['title']}]({g['id']}.md)** · 함수 {len(fns)}{warn}",
|
||||
"",
|
||||
f" {lead}",
|
||||
"",
|
||||
]
|
||||
nav.append({g["title"]: f"{page['id']}/{g['id']}.md"})
|
||||
none = groups[grouping.NONE]
|
||||
none_md = [f"# 묶음 없음 {len(none)}", "", "groups.yml 규칙에 안 맞는 함수.", ""]
|
||||
(self.src / page["id"] / "none.md").write_text(
|
||||
"\n".join(none_md + [self.fn_block(f) for f in none]), encoding="utf-8"
|
||||
)
|
||||
nav.append({"묶음 없음": f"{page['id']}/none.md"})
|
||||
|
||||
_, body = read_hand(hand_dir / "page.md")
|
||||
md = [
|
||||
f"# {page['title']}",
|
||||
"",
|
||||
'<div class="grid cards" markdown>',
|
||||
"",
|
||||
*cards,
|
||||
"</div>",
|
||||
"",
|
||||
]
|
||||
md += [f"묶음 없음 함수 {len(none)} — [목록](none.md)", ""]
|
||||
if graph := self.group_graph(page, groups):
|
||||
md += ["## 묶음 사이 흐름", "", graph, ""]
|
||||
md.append(self.hand_refs(body, "page.md"))
|
||||
(self.src / page["id"] / "index.md").write_text("\n".join(md) + "\n", encoding="utf-8")
|
||||
return nav
|
||||
|
||||
def group_graph(self, page: dict, groups: dict[str, list[dict]]) -> str:
|
||||
edges = Counter(
|
||||
(self.gid[f["id"]], self.gid[c])
|
||||
for fns in groups.values()
|
||||
for f in fns
|
||||
for c in f.get("calls", [])
|
||||
if c in self.gid and self.gid[c] != self.gid[f["id"]]
|
||||
)
|
||||
if not edges:
|
||||
return ""
|
||||
titles = {g["id"]: g["title"] for g in page["groups"]} | {grouping.NONE: "묶음 없음"}
|
||||
used = sorted({a for e in edges for a in e})
|
||||
lines = ["```mermaid", "flowchart LR"] + [f' {g}["{titles[g]}"]' for g in used]
|
||||
lines += [f" {a} -->|{n}| {b}" for (a, b), n in sorted(edges.items())]
|
||||
return "\n".join(lines + ["```"])
|
||||
|
||||
def build(self, typedoc: bool = True) -> dict:
|
||||
if self.src.exists():
|
||||
shutil.rmtree(self.src)
|
||||
(self.src / "py").mkdir(parents=True)
|
||||
files = sorted(self.by_file)
|
||||
if typedoc:
|
||||
self.ts_pages = run_typedoc([f for f in files if f.endswith(".ts")], self.out)
|
||||
self.py_refs = write_py_pages([f for f in files if f.endswith(".py")], self.src)
|
||||
nav = [{"처음": "index.md"}]
|
||||
home = ["# Aislo 프로그램 문서", "", "페이지 → 로직 묶음 → 함수 차례로 눌러 내려감.", ""]
|
||||
for page in self.rules["pages"]:
|
||||
groups = grouping.assign(self.fns, self.data["registry"], page["groups"])
|
||||
self.gid.update({f["id"]: gid for gid, fns in groups.items() for f in fns})
|
||||
nav.append({page["title"]: self.write_page(page, groups)})
|
||||
home.append(f"- [{page['title']}]({page['id']}/index.md)")
|
||||
ts_nav = [{"TypeScript": "ts/index.md"}] if (self.src / "ts" / "index.md").exists() else []
|
||||
nav.append({"함수 화면": [*ts_nav, {"파이썬": "py/index.md"}]})
|
||||
(self.src / "index.md").write_text("\n".join(home) + "\n", encoding="utf-8")
|
||||
(self.src / ".nav.json").write_text(json.dumps(nav, ensure_ascii=False), encoding="utf-8")
|
||||
return self.report()
|
||||
|
||||
def report(self) -> dict:
|
||||
exported = [f for f in self.fns if f.get("exported")]
|
||||
return {
|
||||
"functions": len(self.fns),
|
||||
"groups": sum(len(p["groups"]) for p in self.rules["pages"]),
|
||||
"ungrouped": sum(1 for v in self.gid.values() if v == grouping.NONE),
|
||||
"session_keys": len(self.data["session_keys"]),
|
||||
"api": len(self.data["api"]),
|
||||
"undocumented_exports": sorted(
|
||||
f["id"] for f in exported if not first_line(f.get("doc"))
|
||||
),
|
||||
"missing_refs": self.missing_refs,
|
||||
"stale_groups": self.stale,
|
||||
}
|
||||
|
||||
|
||||
def build_site(out: Path, strict: bool = True) -> None:
|
||||
"""mkdocs build(--strict) — docs_dir = out/src · site_dir = out/site."""
|
||||
from mkdocs.commands.build import build
|
||||
from mkdocs.config import load_config
|
||||
|
||||
cfg = load_config(
|
||||
str(DOCS / "mkdocs.yml"),
|
||||
docs_dir=str(out / "src"),
|
||||
site_dir=str(out / "site"),
|
||||
strict=strict,
|
||||
)
|
||||
build(cfg)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--data", type=Path, default=DOCS / "_data")
|
||||
ap.add_argument("--out", type=Path, default=DOCS / "_build")
|
||||
ap.add_argument("--site", action="store_true", help="mkdocs build --strict 까지")
|
||||
ap.add_argument("--stamp", action="store_true", help="지금 함수 목록 지문을 손글에 적음")
|
||||
args = ap.parse_args()
|
||||
t0 = time.time()
|
||||
data, rules = load_data(args.data), grouping.load_rules()
|
||||
if args.stamp:
|
||||
for page in rules["pages"]:
|
||||
groups = grouping.assign(data["functions"], data["registry"], page["groups"])
|
||||
for g in page["groups"]:
|
||||
path = DOCS / page["guide"] / "groups" / f"{g['id']}.md"
|
||||
if path.exists():
|
||||
stamp(path, grouping.fingerprint(groups[g["id"]]))
|
||||
print("지문 적음")
|
||||
return
|
||||
args.out.mkdir(parents=True, exist_ok=True)
|
||||
out = args.out.resolve()
|
||||
rep = Site(data, rules, out).build()
|
||||
if args.site:
|
||||
build_site(out)
|
||||
summary = {k: (len(v) if isinstance(v, list) else v) for k, v in rep.items()}
|
||||
print(json.dumps(summary, ensure_ascii=False), f"{time.time() - t0:.1f}s")
|
||||
for ref in rep["missing_refs"]:
|
||||
print("손글 함수 없음:", ref)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,81 @@
|
||||
"""함수 → 로직 묶음 자동 배정(PLAN 478-2).
|
||||
|
||||
규칙 = docs/tools/groups.yml · 위에서부터 첫 맞음:
|
||||
registry — 계산 등록부 id 무늬 → 그 줄의 ts · pair 함수
|
||||
files — 파일 이름(확장자 뺀) 무늬
|
||||
어디에도 안 맞는 함수 = 「묶음 없음」(None).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
from fnmatch import fnmatchcase
|
||||
from pathlib import Path, PurePosixPath
|
||||
|
||||
import yaml
|
||||
|
||||
TOOLS = Path(__file__).resolve().parent
|
||||
NONE = "none"
|
||||
|
||||
|
||||
def load_rules(path: Path = TOOLS / "groups.yml") -> dict:
|
||||
return yaml.safe_load(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def stem(file: str) -> str:
|
||||
return PurePosixPath(file.replace("\\", "/")).stem
|
||||
|
||||
|
||||
def registry_ref_id(ref: str | None) -> str | None:
|
||||
"""등록부 칸 `<경로>#<함수>` → 함수 id."""
|
||||
if not ref or "#" not in ref:
|
||||
return None
|
||||
lang = "py" if ref.split("#", 1)[0].endswith(".py") else "ts"
|
||||
return f"{lang}:{ref}"
|
||||
|
||||
|
||||
def registry_owners(registry: list[dict], groups: list[dict]) -> dict[str, str]:
|
||||
"""등록부로 정해지는 함수 id → 묶음 id."""
|
||||
owners: dict[str, str] = {}
|
||||
for row in registry:
|
||||
rid = row.get("id", "")
|
||||
group = next(
|
||||
(g["id"] for g in groups if any(fnmatchcase(rid, p) for p in g.get("registry", []))),
|
||||
None,
|
||||
)
|
||||
if group is None:
|
||||
continue
|
||||
for key in ("ts", "pair"):
|
||||
fid = registry_ref_id(row.get(key))
|
||||
if fid:
|
||||
owners.setdefault(fid, group)
|
||||
return owners
|
||||
|
||||
|
||||
def file_group(file: str, groups: list[dict]) -> str | None:
|
||||
name = stem(file)
|
||||
for g in groups:
|
||||
if any(fnmatchcase(name, p) for p in g.get("files", [])):
|
||||
return g["id"]
|
||||
return None
|
||||
|
||||
|
||||
def assign(
|
||||
functions: list[dict], registry: list[dict], groups: list[dict]
|
||||
) -> dict[str, list[dict]]:
|
||||
"""묶음 id → 함수 목록(파일 · 줄 차례) · 「묶음 없음」 = NONE."""
|
||||
owners = registry_owners(registry, groups)
|
||||
out: dict[str, list[dict]] = {g["id"]: [] for g in groups}
|
||||
out[NONE] = []
|
||||
for fn in functions:
|
||||
gid = owners.get(fn["id"]) or file_group(fn["file"], groups) or NONE
|
||||
out[gid].append(fn)
|
||||
for fns in out.values():
|
||||
fns.sort(key=lambda f: (f["file"], f.get("line", 0)))
|
||||
return out
|
||||
|
||||
|
||||
def fingerprint(fns: list[dict]) -> str:
|
||||
"""함수 목록 지문 — 손글에 적힌 지문과 다르면 「설명 확인 필요」."""
|
||||
text = "\n".join(sorted(f["id"] for f in fns))
|
||||
return hashlib.sha1(text.encode("utf-8")).hexdigest()[:10]
|
||||
@@ -0,0 +1,218 @@
|
||||
# 문서 사이트 묶음 규칙 — 페이지 · 범위 · 로직 묶음
|
||||
# 함수 하나 = 묶음 하나 · 위에서부터 첫 맞음
|
||||
# registry — 계산 등록부 id 무늬(그 줄의 ts · pair 함수)
|
||||
# files — 파일 이름 무늬(확장자 뺀 이름 · fnmatch)
|
||||
# 어디에도 안 맞음 = 「묶음 없음」
|
||||
|
||||
pages:
|
||||
- id: B05_B06
|
||||
title: B05 · B06 종단 · 횡단 설계
|
||||
guide: guide/B05_B06
|
||||
scope:
|
||||
- B05_Profile/
|
||||
- B06_Section/
|
||||
- A00_Common/design/
|
||||
- common_util/
|
||||
groups:
|
||||
- id: recommend
|
||||
title: 추천 노선
|
||||
files:
|
||||
- "*RouteInitial*"
|
||||
- B05_Profile_UI_RouteEdit_Initial*
|
||||
- B05_Profile_UI_RouteEdit_Recommend*
|
||||
- B05_Profile_UI_Panel_Recommend
|
||||
- B05_Profile_Engine_InitialProfile
|
||||
- B05_Profile_Engine_RidgeValley*
|
||||
- B05_Profile_Engine_Skeleton
|
||||
- B05_Profile_Engine_Solver
|
||||
- id: route_edit
|
||||
title: 노선 편집
|
||||
registry: ["route.*"]
|
||||
files:
|
||||
- B05_Profile_UI_RouteEdit*
|
||||
- B05_Profile_RouteEdit_*
|
||||
- B05_Profile_Route_NodeEdits
|
||||
- B05_Profile_ContourLock
|
||||
- design_plan_peek
|
||||
- common_util_route_polyline
|
||||
- common_util_route_geometry
|
||||
- id: route_save
|
||||
title: 노선 저장 · 시설 따라감
|
||||
registry: ["stations.carry", "facility.follow*", "facility.plus_follow"]
|
||||
files:
|
||||
- B05_Profile_Route_Save*
|
||||
- B05_Profile_Router_Replan
|
||||
- B05_Profile_Api_Replan
|
||||
- B06_Section_Router_DesignSave_Route
|
||||
- B06_Section_Route_Carry*
|
||||
- B05_Profile_Pipeline_Follow
|
||||
- B05_Profile_Structure_Follow
|
||||
- design_calc_node_carry
|
||||
- design_calc_node_follow
|
||||
- common_util_route_follow
|
||||
- id: stations
|
||||
title: 측점
|
||||
registry: ["stations.*"]
|
||||
files:
|
||||
- B05_Profile_Sections_Stations
|
||||
- B05_Profile_Engine_Sections*
|
||||
- B05_Profile_UI_Page_Stations
|
||||
- B05_Profile_UI_IrregularStations
|
||||
- B05_Profile_Util_Station
|
||||
- B05_Profile_Plus_Stations
|
||||
- B05_Profile_Api_PlusStations
|
||||
- B06_Section_Station_*
|
||||
- B06_Section_Swap_Bus
|
||||
- B06_Section_Router_Stations
|
||||
- B06_Section_UI_Missing_Stations
|
||||
- B06_Section_UI_Page_Station_Swap
|
||||
- design_pipeline_stations
|
||||
- design_pipeline_probe_station
|
||||
- common_util_station_key
|
||||
- id: ground
|
||||
title: 지반 표본
|
||||
registry: ["ground.*"]
|
||||
files:
|
||||
- B05_Profile_Api_TerrainGrid
|
||||
- B05_Profile_Sections_Ground
|
||||
- B05_Profile_Pipeline_RouteGround
|
||||
- B05_Profile_Router_Terrain
|
||||
- B06_Section_Engine_SampleExtend
|
||||
- B06_Section_Router_Widen
|
||||
- common_util_surface_sampler
|
||||
- id: earthwork
|
||||
title: 토량 · 유토 배분
|
||||
registry: ["earth.*", "haul.*"]
|
||||
files:
|
||||
- "*MassHaul*"
|
||||
- "*HaulPlan*"
|
||||
- B05_Profile_UI_Profile_Balance
|
||||
- common_util_mass_haul*
|
||||
- id: profile
|
||||
title: 종단 계획고
|
||||
registry: ["profile.*"]
|
||||
files:
|
||||
- B05_Profile_UI_Profile_*
|
||||
- B05_Profile_Profile_*
|
||||
- B05_Profile_Grade_*
|
||||
- B05_Profile_Engine_Grade*
|
||||
- B05_Profile_Pipeline_Grade
|
||||
- B05_Profile_EndOffsets
|
||||
- B05_Profile_UI_Panel*
|
||||
- B05_Profile_PageSettings
|
||||
- B05_Profile_Api_PageSettings
|
||||
- design_profile*
|
||||
- id: cross
|
||||
title: 횡단 설계 계산
|
||||
registry: ["cross.*"]
|
||||
files:
|
||||
- B06_Section_Cross_*
|
||||
- B06_Section_Engine_*
|
||||
- B06_Section_Design_Keys
|
||||
- B06_Section_Router_Design
|
||||
- B06_Section_Router_Design_Ranges
|
||||
- B06_Section_Router_Regenerate_Options
|
||||
- B06_Section_Server_Calc*
|
||||
- common_util_cross_*
|
||||
- id: cross_view
|
||||
title: 횡단 화면 · 조정창
|
||||
files:
|
||||
- B06_Section_UI_Cross_*
|
||||
- B06_Section_UI_Standard*
|
||||
- B06_Section_UI_Section_*
|
||||
- B06_Section_UI_Longitudinal
|
||||
- B06_Section_UI_Adjust_Dock
|
||||
- B06_Section_Label_Tiers
|
||||
- B06_Section_Display_Width
|
||||
- B06_Section_Session_Cells
|
||||
- B06_Section_Section_Adjusts
|
||||
- B06_Section_UI_Page_Design_Sync
|
||||
- B06_Section_UI_Page_Station_Controls
|
||||
- B06_Section_UI_Page_Span_Control
|
||||
- B06_Section_UI_Page_Ford_Controls
|
||||
- B06_Section_UI_Page_Link_Session
|
||||
- B06_Section_UI_Page_Pipe_Options
|
||||
- B06_Section_UI_Page_Patches
|
||||
- id: drainage
|
||||
title: 관 · 배수
|
||||
registry: ["drainage.*", "facility.pipe*"]
|
||||
files:
|
||||
- "*Drainage*"
|
||||
- B05_Profile_Api_Pipes_Draft
|
||||
- B06_Section_Api_Culvert_Options
|
||||
- design_pipe_ids
|
||||
- design_structure_pipe*
|
||||
- common_util_drainage*
|
||||
- common_util_culvert*
|
||||
- id: structures
|
||||
title: 구조물
|
||||
registry: ["facility.*"]
|
||||
files:
|
||||
- "*Structures*"
|
||||
- "*Structure_*"
|
||||
- B05_Profile_UI_Structure_Pick_Session
|
||||
- B06_Section_Structure_Layouts
|
||||
- design_structure*
|
||||
- design_facility_lane
|
||||
- common_util_structure_*
|
||||
- common_util_berm*
|
||||
- common_util_excavation
|
||||
- id: corridor
|
||||
title: 3D 길 모형
|
||||
registry: ["corridor.*", "view3d.*"]
|
||||
files:
|
||||
- "*Corridor*"
|
||||
- B05_Profile_UI_Viewer*
|
||||
- design_view_profile_3d*
|
||||
- id: footprint
|
||||
title: 평면 발자국
|
||||
registry: ["plan.*", "footprint.*"]
|
||||
files:
|
||||
- "*Footprint*"
|
||||
- id: pipeline
|
||||
title: 계산 묶음 · 가게
|
||||
files:
|
||||
- design_pipeline*
|
||||
- design_calc*
|
||||
- design_store
|
||||
- B05_Profile_Pipeline_*
|
||||
- B06_Section_Section_*
|
||||
- B06_Section_UI_Page_Persist
|
||||
- common_util_calc_runner
|
||||
- id: save
|
||||
title: 저장 · 확정 · 초기화 · 떠남
|
||||
files:
|
||||
- design_save*
|
||||
- design_reset
|
||||
- design_leave
|
||||
- design_shared
|
||||
- B06_Section_Router_DesignSave
|
||||
- B06_Section_Router_Confirm
|
||||
- B05_Profile_Router_Confirm
|
||||
- B05_Profile_Router_Lifecycle
|
||||
- common_util_initial_snapshot
|
||||
- common_util_save_guard
|
||||
- id: screen
|
||||
title: 페이지 틀 · 보기
|
||||
files:
|
||||
- design_page*
|
||||
- design_view*
|
||||
- design_panel
|
||||
- design_dock
|
||||
- design_pick*
|
||||
- design_selection
|
||||
- design_user_pick
|
||||
- design_sections
|
||||
- B05_Profile_UI*
|
||||
- B06_Section_UI*
|
||||
- id: server
|
||||
title: 서버 창구 · 저장소
|
||||
files:
|
||||
- "*_Router*"
|
||||
- "*_Repository*"
|
||||
- "*_Schema"
|
||||
- "*_Api_*"
|
||||
- B05_Profile_Engine*
|
||||
- B05_Profile
|
||||
- B06_Section
|
||||
- B05_Profile_Debug
|
||||
@@ -0,0 +1,11 @@
|
||||
"""mkdocs 훅 — build.py 가 쓴 왼쪽 메뉴(<docs_dir>/.nav.json)를 얹음."""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def on_config(config):
|
||||
nav = Path(config.docs_dir) / ".nav.json"
|
||||
if nav.exists():
|
||||
config.nav = json.loads(nav.read_text(encoding="utf-8"))
|
||||
return config
|
||||
Generated
+356
@@ -0,0 +1,356 @@
|
||||
{
|
||||
"name": "aislo-docs-tools",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "aislo-docs-tools",
|
||||
"dependencies": {
|
||||
"ts-morph": "^28.0.0",
|
||||
"typedoc": "^0.28.20",
|
||||
"typedoc-plugin-markdown": "^4.14.0",
|
||||
"typescript": "6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@gerrit0/mini-shiki": {
|
||||
"version": "3.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz",
|
||||
"integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/engine-oniguruma": "^3.23.0",
|
||||
"@shikijs/langs": "^3.23.0",
|
||||
"@shikijs/themes": "^3.23.0",
|
||||
"@shikijs/types": "^3.23.0",
|
||||
"@shikijs/vscode-textmate": "^10.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/engine-oniguruma": {
|
||||
"version": "3.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz",
|
||||
"integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "3.23.0",
|
||||
"@shikijs/vscode-textmate": "^10.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/langs": {
|
||||
"version": "3.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz",
|
||||
"integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "3.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/themes": {
|
||||
"version": "3.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz",
|
||||
"integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/types": "3.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/types": {
|
||||
"version": "3.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz",
|
||||
"integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@shikijs/vscode-textmate": "^10.0.2",
|
||||
"@types/hast": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@shikijs/vscode-textmate": {
|
||||
"version": "10.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
|
||||
"integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ts-morph/common": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.29.0.tgz",
|
||||
"integrity": "sha512-35oUmphHbJvQ/+UTwFNme/t2p3FoKiGJ5auTjjpNTop2dyREspirjMy82PLSC1pnDJ8ah1GU98hwpVt64YXQsg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minimatch": "^10.0.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"tinyglobby": "^0.2.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/hast": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
|
||||
"integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/unist": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/unist": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
|
||||
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.12",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
|
||||
"integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/code-block-writer": {
|
||||
"version": "13.0.3",
|
||||
"resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz",
|
||||
"integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz",
|
||||
"integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/markdown-it"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lunr": {
|
||||
"version": "2.3.9",
|
||||
"resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
|
||||
"integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/markdown-it": {
|
||||
"version": "14.3.3",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.3.tgz",
|
||||
"integrity": "sha512-L//tDAkjTjJCU6pBUSBTgs3Kg4InVaZuUZog/b6V12UKhcDQ7S78877ibbCanRExwPuYP1CZD7Gd2dxSy1GXRg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/puzrin"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/markdown-it"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "^4.5.0",
|
||||
"linkify-it": "^5.0.2",
|
||||
"mdurl": "^2.0.0",
|
||||
"punycode.js": "^2.3.1",
|
||||
"uc.micro": "^2.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"markdown-it": "bin/markdown-it.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz",
|
||||
"integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
|
||||
"integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
"integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
|
||||
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.17",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-morph": {
|
||||
"version": "28.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-28.0.0.tgz",
|
||||
"integrity": "sha512-Wp3tnZ2bzwxyTZMtgWVzXDfm7lB1Drz+y9DmmYH/L702PQhPyVrp3pkou3yIz4qjS14GY9kcpmLiOOMvl8oG1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ts-morph/common": "~0.29.0",
|
||||
"code-block-writer": "^13.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc": {
|
||||
"version": "0.28.20",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.20.tgz",
|
||||
"integrity": "sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@gerrit0/mini-shiki": "^3.23.0",
|
||||
"lunr": "^2.3.9",
|
||||
"markdown-it": "^14.3.0",
|
||||
"minimatch": "^10.2.5",
|
||||
"yaml": "^2.9.0"
|
||||
},
|
||||
"bin": {
|
||||
"typedoc": "bin/typedoc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18",
|
||||
"pnpm": ">= 10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc-plugin-markdown": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-4.14.0.tgz",
|
||||
"integrity": "sha512-Y2a3oDV9ekrbkk6RBSGH8GyNkh0xIdemIOOTr3tiRN/33nvFGStOs9nw+6Q84gF1XFkcVZrgM4Ckcsf6e/YpKQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typedoc": "0.28.x"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
|
||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.9.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
|
||||
"integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/eemeli"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"name":"aislo-docs-tools","private":true,"type":"module","dependencies":{"ts-morph":"^28.0.0","typedoc":"^0.28.20","typedoc-plugin-markdown":"^4.14.0","typescript":"6.0"}}
|
||||
@@ -0,0 +1,4 @@
|
||||
mkdocs>=1.6
|
||||
mkdocs-material>=9.5
|
||||
mkdocstrings[python]>=0.26
|
||||
pyyaml>=6
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"method": "POST",
|
||||
"path": "/route/node-edits",
|
||||
"handler": "py:B05_Profile/B05_Profile_Route_NodeEdits.py#node_edits",
|
||||
"callers": [
|
||||
"ts:B05_Profile/logic/B05_Profile_Grade_Seat.ts#seatBornNodes"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,407 @@
|
||||
[
|
||||
{
|
||||
"id": "ts:common_util/common_util_station_key.ts#chainageKey",
|
||||
"lang": "ts",
|
||||
"file": "common_util/common_util_station_key.ts",
|
||||
"line": 24,
|
||||
"name": "chainageKey",
|
||||
"exported": true,
|
||||
"doc": "누가거리 → 측점 열쇠 글.",
|
||||
"params": [
|
||||
{
|
||||
"name": "chainageM",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "string",
|
||||
"calls": [],
|
||||
"called_by": [
|
||||
"ts:common_util/common_util_station_key.ts#stationKey"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ts:common_util/common_util_station_key.ts#stationKey",
|
||||
"lang": "ts",
|
||||
"file": "common_util/common_util_station_key.ts",
|
||||
"line": 32,
|
||||
"name": "stationKey",
|
||||
"exported": true,
|
||||
"doc": "측점 열쇠(grid:k · pipe: · structure:).",
|
||||
"params": [
|
||||
{
|
||||
"name": "kind",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "chainageM",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "string",
|
||||
"calls": [
|
||||
"ts:common_util/common_util_station_key.ts#chainageKey"
|
||||
],
|
||||
"called_by": [
|
||||
"ts:common_util/common_util_station_key.ts#dedupeStationKeys"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ts:common_util/common_util_station_key.ts#dedupeStationKeys",
|
||||
"lang": "ts",
|
||||
"file": "common_util/common_util_station_key.ts",
|
||||
"line": 54,
|
||||
"name": "dedupeStationKeys",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "keys",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "chainages",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "string[]",
|
||||
"calls": [
|
||||
"ts:common_util/common_util_station_key.ts#stationKey"
|
||||
],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "ts:common_util/common_util_station_key.ts#looseChainage",
|
||||
"lang": "ts",
|
||||
"file": "common_util/common_util_station_key.ts",
|
||||
"line": 67,
|
||||
"name": "looseChainage",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "number | null",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "ts:common_util/common_util_station_key.ts#curveMoves",
|
||||
"lang": "ts",
|
||||
"file": "common_util/common_util_station_key.ts",
|
||||
"line": 80,
|
||||
"name": "curveMoves",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "before",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "after",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "Map",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "ts:common_util/common_util_station_key.ts#sameSeat",
|
||||
"lang": "ts",
|
||||
"file": "common_util/common_util_station_key.ts",
|
||||
"line": 116,
|
||||
"name": "sameSeat",
|
||||
"exported": true,
|
||||
"doc": "같은 자리 판정.",
|
||||
"params": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "boolean",
|
||||
"calls": [],
|
||||
"called_by": [
|
||||
"ts:B05_Profile/logic/B05_Profile_Grade_Seat.ts#seatBornNodes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ts:B05_Profile/logic/B05_Profile_Grade_Seat.ts#median",
|
||||
"lang": "ts",
|
||||
"file": "B05_Profile/logic/B05_Profile_Grade_Seat.ts",
|
||||
"line": 24,
|
||||
"name": "median",
|
||||
"exported": false,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "values",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "number",
|
||||
"calls": [],
|
||||
"called_by": [
|
||||
"ts:B05_Profile/logic/B05_Profile_Grade_Seat.ts#seatBornNodes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ts:B05_Profile/logic/B05_Profile_Grade_Seat.ts#seatBornNodes",
|
||||
"lang": "ts",
|
||||
"file": "B05_Profile/logic/B05_Profile_Grade_Seat.ts",
|
||||
"line": 32,
|
||||
"name": "seatBornNodes",
|
||||
"exported": true,
|
||||
"doc": "새 측점에 원지반을 지나는 꼭짓점.",
|
||||
"params": [
|
||||
{
|
||||
"name": "base",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "born",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "held",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "AlignmentNode[]",
|
||||
"calls": [
|
||||
"ts:B05_Profile/logic/B05_Profile_Grade_Seat.ts#median",
|
||||
"ts:common_util/common_util_station_key.ts#sameSeat"
|
||||
],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_station_key.py#_half_up",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_station_key.py",
|
||||
"line": 22,
|
||||
"name": "_half_up",
|
||||
"exported": false,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "value",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "int",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_station_key.py#chainage_key",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_station_key.py",
|
||||
"line": 27,
|
||||
"name": "chainage_key",
|
||||
"exported": true,
|
||||
"doc": "누가거리 → 측점 열쇠 글.",
|
||||
"params": [
|
||||
{
|
||||
"name": "chainage_m",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "str",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_station_key.py#station_key",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_station_key.py",
|
||||
"line": 32,
|
||||
"name": "station_key",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "kind",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "chainage_m",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "str",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_station_key.py#dedupe_station_keys",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_station_key.py",
|
||||
"line": 68,
|
||||
"name": "dedupe_station_keys",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "keys",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "chainages",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "list[str]",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_station_key.py#loose_chainage",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_station_key.py",
|
||||
"line": 79,
|
||||
"name": "loose_chainage",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "key",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "float | None",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_station_key.py#curve_moves",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_station_key.py",
|
||||
"line": 89,
|
||||
"name": "curve_moves",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "before",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "after",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "dict[str, str]",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_station_key.py#station_seat",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_station_key.py",
|
||||
"line": 117,
|
||||
"name": "station_seat",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "station",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "tuple | None",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_station_key.py#same_seat",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_station_key.py",
|
||||
"line": 130,
|
||||
"name": "same_seat",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "bool",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:B05_Profile/B05_Profile_Route_NodeEdits.py#node_edits",
|
||||
"lang": "py",
|
||||
"file": "B05_Profile/B05_Profile_Route_NodeEdits.py",
|
||||
"line": 21,
|
||||
"name": "node_edits",
|
||||
"exported": true,
|
||||
"doc": "노드 편집 규칙.",
|
||||
"params": [
|
||||
{
|
||||
"name": "nodes",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "dict",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_atomic.py#atomic_write_bytes",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_atomic.py",
|
||||
"line": 10,
|
||||
"name": "atomic_write_bytes",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "path",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "payload",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "None",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
},
|
||||
{
|
||||
"id": "py:common_util/common_util_atomic.py#atomic_write_npz",
|
||||
"lang": "py",
|
||||
"file": "common_util/common_util_atomic.py",
|
||||
"line": 34,
|
||||
"name": "atomic_write_npz",
|
||||
"exported": true,
|
||||
"doc": "",
|
||||
"params": [
|
||||
{
|
||||
"name": "path",
|
||||
"type": ""
|
||||
},
|
||||
{
|
||||
"name": "arrays",
|
||||
"type": ""
|
||||
}
|
||||
],
|
||||
"returns": "None",
|
||||
"calls": [],
|
||||
"called_by": []
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"id": "stations.key",
|
||||
"no": "R7",
|
||||
"label": "측점 열쇠",
|
||||
"site": "browser_now",
|
||||
"ts": "common_util/common_util_station_key.ts#stationKey",
|
||||
"pair": "common_util/common_util_station_key.py#station_key"
|
||||
},
|
||||
{
|
||||
"id": "profile.seat",
|
||||
"no": "P6",
|
||||
"label": "옮긴 시설 꼭짓점 세움",
|
||||
"site": "browser_now",
|
||||
"ts": "B05_Profile/logic/B05_Profile_Grade_Seat.ts#seatBornNodes",
|
||||
"pair": null
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
"key": "aislo:draft:route-draft",
|
||||
"reads": [
|
||||
"ts:common_util/common_util_station_key.ts#stationKey"
|
||||
],
|
||||
"writes": [
|
||||
"B05_Profile/logic/B05_Profile_Grade_Seat.ts:32"
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,129 @@
|
||||
"""478-2 문서 사이트 검사 — 묶음 배정 · 손글 함수 · 함수 화면 도구 대 추출 · mkdocs --strict.
|
||||
|
||||
가짜 추출(fixtures/478_docs)은 늘 돎 · 진짜 추출(docs/_data)은 있을 때만.
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT / "docs" / "tools"))
|
||||
|
||||
import build # noqa: E402
|
||||
import group # noqa: E402
|
||||
|
||||
FIXTURE = Path(__file__).parent / "fixtures" / "478_docs"
|
||||
REAL = ROOT / "docs" / "_data"
|
||||
|
||||
|
||||
def make_site(data_dir: Path, out: Path) -> tuple[build.Site, dict]:
|
||||
site = build.Site(build.load_data(data_dir), group.load_rules(), out)
|
||||
return site, site.build()
|
||||
|
||||
|
||||
def shown_anchors(site: build.Site) -> set[str]:
|
||||
text = "".join(p.read_text(encoding="utf-8") for p in (site.src).glob("*/*.md"))
|
||||
return set(re.findall(r"\{ #(f-[0-9a-f]{10}) \}", text))
|
||||
|
||||
|
||||
def typedoc_functions(out: Path) -> set[tuple[str, str]]:
|
||||
project = json.loads((out / "typedoc.json").read_text(encoding="utf-8"))
|
||||
modules = [m for m in project.get("children", []) if m["kind"] == 2] or [project]
|
||||
return {
|
||||
(c["sources"][0]["fileName"], c["name"])
|
||||
for m in modules
|
||||
for c in m.get("children", [])
|
||||
if c["kind"] == 64
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def fake(tmp_path_factory):
|
||||
return make_site(FIXTURE, tmp_path_factory.mktemp("fake"))
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def real(tmp_path_factory):
|
||||
if not (REAL / "functions.json").exists():
|
||||
pytest.skip("추출 없음 — docs/_data")
|
||||
return make_site(REAL, tmp_path_factory.mktemp("real"))
|
||||
|
||||
|
||||
def test_assign_rules():
|
||||
data = build.load_data(FIXTURE)
|
||||
page = group.load_rules()["pages"][0]
|
||||
gid = {
|
||||
f["name"]: g
|
||||
for g, fns in group.assign(data["functions"], data["registry"], page["groups"]).items()
|
||||
for f in fns
|
||||
}
|
||||
assert gid["stationKey"] == "stations" # 등록부 stations.key
|
||||
assert gid["station_key"] == "stations" # 등록부 pair
|
||||
assert gid["seatBornNodes"] == "profile" # 등록부 profile.seat
|
||||
assert gid["median"] == "profile" # 파일 B05_Profile_Grade_*
|
||||
assert gid["node_edits"] == "route_edit" # 파일 B05_Profile_Route_NodeEdits
|
||||
assert gid["atomic_write_bytes"] == group.NONE
|
||||
|
||||
|
||||
def test_fake_site(fake):
|
||||
site, rep = fake
|
||||
assert shown_anchors(site) == {build.anchor(f["id"]) for f in site.fns}
|
||||
assert rep["ungrouped"] == 2
|
||||
assert rep["session_keys"] == 1 and rep["api"] == 1
|
||||
assert "stations" in rep["stale_groups"] # 손글 지문 ≠ 가짜 목록
|
||||
build.build_site(site.out, strict=True)
|
||||
|
||||
|
||||
def test_fake_tools_match_extract(fake):
|
||||
site, _ = fake
|
||||
ts = {(f["file"], f["name"]) for f in site.fns if f["lang"] == "ts" and f["exported"]}
|
||||
assert typedoc_functions(site.out) == ts
|
||||
py = {(f["file"], f["name"]) for f in site.fns if f["lang"] == "py"}
|
||||
assert {(file, n) for file, _ in py for n in build.griffe_names(file)} == py
|
||||
|
||||
|
||||
def test_real_hand_refs(real):
|
||||
_, rep = real
|
||||
assert rep["missing_refs"] == [], "손글이 가리키는 함수가 추출에 없음"
|
||||
|
||||
|
||||
def test_real_every_function_shown(real):
|
||||
site, _ = real
|
||||
assert shown_anchors(site) == {build.anchor(f["id"]) for f in site.fns}, "조용히 빠진 함수"
|
||||
|
||||
|
||||
def test_real_typedoc_matches_extract(real):
|
||||
site, _ = real
|
||||
ts = {(f["file"], f["name"]) for f in site.fns if f["lang"] == "ts" and f["exported"]}
|
||||
got = typedoc_functions(site.out)
|
||||
assert not (ts - got) and not (got - ts), (
|
||||
f"추출만 {sorted(ts - got)[:10]} · TypeDoc 만 {sorted(got - ts)[:10]}"
|
||||
)
|
||||
|
||||
|
||||
def test_real_griffe_matches_extract(real):
|
||||
site, _ = real
|
||||
py = {(f["file"], f["name"]) for f in site.fns if f["lang"] == "py"}
|
||||
got = {(file, n) for file in {f for f, _ in py} for n in build.griffe_names(file)}
|
||||
assert not (py - got) and not (got - py), (
|
||||
f"추출만 {sorted(py - got)[:10]} · griffe 만 {sorted(got - py)[:10]}"
|
||||
)
|
||||
|
||||
|
||||
def test_real_counts(real):
|
||||
_, rep = real
|
||||
if rep["undocumented_exports"]:
|
||||
warnings.warn(f"설명 없는 export 함수 {len(rep['undocumented_exports'])}")
|
||||
if rep["ungrouped"]:
|
||||
warnings.warn(f"묶음 없는 함수 {rep['ungrouped']}")
|
||||
assert rep["ungrouped"] < rep["functions"]
|
||||
|
||||
|
||||
def test_real_strict_build(real):
|
||||
site, _ = real
|
||||
build.build_site(site.out, strict=True)
|
||||
Reference in New Issue
Block a user