Merge remote-tracking branches 'origin/sub_laptop_1', 'origin/sub_laptop_2', 'origin/sub_laptop_3' and 'origin/sub_laptop_4' into main_laptop_1
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
"종류": "표",
|
||||
"판": 1,
|
||||
"설명": "측점마다 구조물 수량 한 줄 · 머리 4층(종류 · 공법 · 규격 · 단위) · 설계값 열은 프로젝트 설계에서 채움(화면에서 못 고침 — 설계를 고침) · 손 열은 사용자가 적음 · 계산 열은 식. 종류 정본 = B05_Profile/B05_Profile_Structure_Types.json.",
|
||||
"바인딩규칙": "종류 = type_id · 펼침 = 값마다 열을 나눌 칸 키(앞에 = 이면 고정 글) · 값 = 칸 키 | 개소(1건 1) | 길이(length_m, 없으면 끝-시작) | 관연장(B06 횡단 pipe_length_m) · 묶음 = 같은 펼침으로 함께 나뉘는 열 · 머리틀 = 펼친 머리({0}{1}… = 펼침 값 차례) · 이름 = 설계 값 → 머리 글 · 조건 = {칸: 값}(맞는 것만) · 더함 = 같은 열에 더하는 다른 출처 · 식틀 = 펼친 열의 식({0}… 펼침 값)",
|
||||
"바인딩규칙": "종류 = type_id · 펼침 = 값마다 열을 나눌 칸 키(앞에 = 이면 고정 글) · 값 = 칸 키 | 개소(1건 1) | 길이(length_m, 없으면 끝-시작) | 관연장(B06 횡단 pipe_length_m) · 묶음 = 같은 펼침으로 함께 나뉘는 열 · 머리틀 = 펼친 머리({0}{1}… = 펼침 값 차례) · 이름 = 설계 값 → 머리 글 · 조건 = {칸: 값}(맞는 것만) · 더함 = 같은 열에 더하는 다른 출처 · 식틀 = 펼친 열의 식({0}… 펼침 값) · 값꼴 = 자리마다 머리 글 꼴({} = 값) · 빈값 = 설계값이 빈 자리 머리 글 · 펼침틀 = 계산 열이 같은 묶음으로 따라 나뉨(묶음 · 머리틀 · 값꼴 · 빈값 · 식틀 — 설계값을 받지 않아 바인딩 없음)",
|
||||
"층": ["종류", "공법", "규격"],
|
||||
"변수": {
|
||||
"파형강관_1본_m": 8,
|
||||
@@ -23,7 +23,7 @@
|
||||
"일위대가": "UA000004",
|
||||
"바인딩": {
|
||||
"종류": "revetment", "펼침": ["form", "height_m"], "값": "length_m", "묶음": "rv",
|
||||
"머리틀": ["돌기슭막이", "{0}", "H={1}"],
|
||||
"머리틀": ["돌기슭막이", "{0}", "{1}"], "값꼴": ["{}", "H={}"], "빈값": ["형태 미입력", "높이 미입력"],
|
||||
"이름": { "돌쌓기(찰)": "찰쌓기", "돌쌓기(메)": "메쌓기" },
|
||||
"더함": [
|
||||
{ "종류": "pipe", "펼침": ["inlet_revet_form", "inlet_revet_height_m"], "값": "inlet_revet_length_m", "조건": { "inlet_type": "기슭막이" } },
|
||||
@@ -35,18 +35,18 @@
|
||||
{
|
||||
"id": "pv_b", "머리": ["콘크리트포장", "T=(두께마다)", "B"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 포장 폭",
|
||||
"바인딩": { "종류": "pavement_concrete", "펼침": ["thickness_cm"], "값": "width_m", "묶음": "pv", "머리틀": ["콘크리트포장", "T={0}cm", "B"] }
|
||||
"바인딩": { "종류": "pavement_concrete", "펼침": ["thickness_cm"], "값": "width_m", "묶음": "pv", "머리틀": ["콘크리트포장", "{0}", "B"], "값꼴": ["T={}cm"], "빈값": ["두께 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "pv_l", "머리": ["콘크리트포장", "T=(두께마다)", "L"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 포장 길이",
|
||||
"일위대가": "UA000007",
|
||||
"바인딩": { "종류": "pavement_concrete", "펼침": ["thickness_cm"], "값": "길이", "묶음": "pv", "머리틀": ["콘크리트포장", "T={0}cm", "L"] }
|
||||
"바인딩": { "종류": "pavement_concrete", "펼침": ["thickness_cm"], "값": "길이", "묶음": "pv", "머리틀": ["콘크리트포장", "{0}", "L"], "값꼴": ["T={}cm"], "빈값": ["두께 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "pv_w", "머리": ["콘크리트포장", "T=(두께마다)", "확폭"], "단위": "㎡", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 확폭 면적",
|
||||
"바인딩": { "종류": "pavement_concrete", "펼침": ["thickness_cm"], "값": "widening_area_m2", "묶음": "pv", "머리틀": ["콘크리트포장", "T={0}cm", "확폭"] }
|
||||
"바인딩": { "종류": "pavement_concrete", "펼침": ["thickness_cm"], "값": "widening_area_m2", "묶음": "pv", "머리틀": ["콘크리트포장", "{0}", "확폭"], "값꼴": ["T={}cm"], "빈값": ["두께 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "pv_a", "머리": ["콘크리트포장", "T=(두께마다)", "A"], "단위": "㎡", "꼴": "수", "펼침": true,
|
||||
@@ -54,14 +54,14 @@
|
||||
"끝수": { "자리": 2, "방법": "반올림" },
|
||||
"설명": "계산 — B × L + 확폭",
|
||||
"일위대가": "UA000006",
|
||||
"바인딩": { "종류": "pavement_concrete", "펼침": ["thickness_cm"], "값": "식", "묶음": "pv", "머리틀": ["콘크리트포장", "T={0}cm", "A"] }
|
||||
"펼침틀": { "묶음": "pv", "머리틀": ["콘크리트포장", "{0}", "A"], "값꼴": ["T={}cm"], "빈값": ["두께 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "pv_jt", "머리": ["콘크리트포장", "T=(두께마다)", "수축줄눈"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"식": "IF([pv_l]>0,INT([pv_l]/[$수축줄눈_간격_m])*[pv_b],\"\")",
|
||||
"끝수": { "자리": 2, "방법": "반올림" },
|
||||
"설명": "계산 — 내림(L ÷ 줄눈 간격) × B · 줄눈 간격 = 설계값(기본 6m · 다르면 그 줄 식에 박음)",
|
||||
"바인딩": { "종류": "pavement_concrete", "펼침": ["thickness_cm"], "값": "식", "묶음": "pv", "머리틀": ["콘크리트포장", "T={0}cm", "수축줄눈"] }
|
||||
"펼침틀": { "묶음": "pv", "머리틀": ["콘크리트포장", "{0}", "수축줄눈"], "값꼴": ["T={}cm"], "빈값": ["두께 미입력"] }
|
||||
},
|
||||
|
||||
{
|
||||
@@ -69,14 +69,14 @@
|
||||
"설명": "설계값 — 횡단 관 연장(B06) · 같은 측점에 관이 둘이면 줄을 나눔",
|
||||
"일위대가": "UA000036",
|
||||
"일위대가후보": ["UA000036", "UA000027"],
|
||||
"바인딩": { "종류": "pipe", "펼침": ["pipe_kind", "pipe_diameter_mm"], "값": "관연장", "묶음": "pp", "머리틀": ["관공", "{0} Φ{1}", "관매설"] }
|
||||
"바인딩": { "종류": "pipe", "펼침": ["pipe_kind", "pipe_diameter_mm"], "값": "관연장", "묶음": "pp", "머리틀": ["관공", "{0} {1}", "관매설"], "값꼴": ["{}", "Φ{}"], "빈값": ["관종 미입력", "관경 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "pp_cp", "머리": ["관공", "(관종·관경마다)", "커플링밴드"], "단위": "개소", "꼴": "수", "펼침": true,
|
||||
"식": "IF([pp_len]>0,ROUNDUP([pp_len]/[$파형강관_1본_m],0)-1,\"\")",
|
||||
"설명": "계산 — 올림(L ÷ 1본 길이) − 1 · 관마다(줄마다) 계산 뒤 합 · 1본 길이 = 변수(관종마다 · 빈칸이면 계산 안 함)",
|
||||
"바인딩": {
|
||||
"종류": "pipe", "펼침": ["pipe_kind", "pipe_diameter_mm"], "값": "식", "묶음": "pp", "머리틀": ["관공", "{0} Φ{1}", "커플링밴드"],
|
||||
"펼침틀": {
|
||||
"묶음": "pp", "머리틀": ["관공", "{0} {1}", "커플링밴드"], "값꼴": ["{}", "Φ{}"], "빈값": ["관종 미입력", "관경 미입력"],
|
||||
"식틀": "IF([pp_len]>0,ROUNDUP([pp_len]/[${0}_1본_m],0)-1,\"\")"
|
||||
}
|
||||
},
|
||||
@@ -87,7 +87,7 @@
|
||||
"일위대가": "UA000008",
|
||||
"일위대가후보": ["UA000008", "UA000025", "UA000029"],
|
||||
"바인딩": {
|
||||
"종류": "pipe", "펼침": ["wing_wall_type"], "값": "개소", "묶음": "pg", "머리틀": ["관보호공", "{0}", "유입"],
|
||||
"종류": "pipe", "펼침": ["wing_wall_type"], "값": "개소", "묶음": "pg", "머리틀": ["관보호공", "{0}", "유입"], "빈값": ["형식 미입력"],
|
||||
"이름": { "A-TYPE": "날개벽 A형", "C-TYPE": "날개벽 C형", "A-TYPE+집수정": "날개벽 A형+집수정" },
|
||||
"더함": [ { "종류": "pipe", "펼침": ["inlet_basin_form"], "값": "개소", "조건": { "inlet_type": "집수정" } } ]
|
||||
}
|
||||
@@ -97,14 +97,14 @@
|
||||
"id": "bx_len", "머리": ["BOX암거", "(폭×높이마다)", "연장"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — BOX 연장(계류 방향)",
|
||||
"일위대가": "UA000014",
|
||||
"바인딩": { "종류": "box_culvert", "펼침": ["body_width_m", "body_height_m"], "값": "length_m", "묶음": "bx", "머리틀": ["BOX암거", "{0}×{1}", "연장"] }
|
||||
"바인딩": { "종류": "box_culvert", "펼침": ["body_width_m", "body_height_m"], "값": "length_m", "묶음": "bx", "머리틀": ["BOX암거", "{0}×{1}", "연장"], "빈값": ["폭 미입력", "높이 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "bx_wing", "머리": ["BOX암거", "(폭×높이마다)", "날개벽"], "단위": "개소", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 날개벽 유입·유출 있음 수",
|
||||
"일위대가": "UA000025",
|
||||
"바인딩": {
|
||||
"종류": "box_culvert", "펼침": ["body_width_m", "body_height_m"], "값": "개소", "묶음": "bx", "머리틀": ["BOX암거", "{0}×{1}", "날개벽"],
|
||||
"종류": "box_culvert", "펼침": ["body_width_m", "body_height_m"], "값": "개소", "묶음": "bx", "머리틀": ["BOX암거", "{0}×{1}", "날개벽"], "빈값": ["폭 미입력", "높이 미입력"],
|
||||
"조건": { "wing_in": "있음" },
|
||||
"더함": [ { "종류": "box_culvert", "펼침": ["body_width_m", "body_height_m"], "값": "개소", "조건": { "wing_out": "있음" } } ]
|
||||
}
|
||||
@@ -113,32 +113,32 @@
|
||||
{
|
||||
"id": "fb", "머리": ["세월교", "(관경 × 련수마다)", null], "단위": "개소", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 세월교 개소",
|
||||
"바인딩": { "종류": "ford_bridge", "펼침": ["pipe_diameter_mm", "pipe_count"], "값": "개소", "묶음": "fb", "머리틀": ["세월교", "Φ{0}×{1}련", null] }
|
||||
"바인딩": { "종류": "ford_bridge", "펼침": ["pipe_diameter_mm", "pipe_count"], "값": "개소", "묶음": "fb", "머리틀": ["세월교", "{0}×{1}", null], "값꼴": ["Φ{}", "{}련"], "빈값": ["관경 미입력", "련수 미입력"] }
|
||||
},
|
||||
|
||||
{
|
||||
"id": "fp_w", "머리": ["물넘이포장", "T=(두께마다)", "폭"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 월류 폭",
|
||||
"바인딩": { "종류": "ford_pavement", "펼침": ["thickness_cm"], "값": "ford_width_m", "묶음": "fp", "머리틀": ["물넘이포장", "T={0}cm", "폭"] }
|
||||
"바인딩": { "종류": "ford_pavement", "펼침": ["thickness_cm"], "값": "ford_width_m", "묶음": "fp", "머리틀": ["물넘이포장", "{0}", "폭"], "값꼴": ["T={}cm"], "빈값": ["두께 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "fp_l", "머리": ["물넘이포장", "T=(두께마다)", "길이"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 포장 길이(노폭 방향)",
|
||||
"바인딩": { "종류": "ford_pavement", "펼침": ["thickness_cm"], "값": "length_m", "묶음": "fp", "머리틀": ["물넘이포장", "T={0}cm", "길이"] }
|
||||
"바인딩": { "종류": "ford_pavement", "펼침": ["thickness_cm"], "값": "length_m", "묶음": "fp", "머리틀": ["물넘이포장", "{0}", "길이"], "값꼴": ["T={}cm"], "빈값": ["두께 미입력"] }
|
||||
},
|
||||
|
||||
{
|
||||
"id": "od_len", "머리": ["횡단개거", "(규격마다)", "길이"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 개거 연장",
|
||||
"일위대가": "UA000026",
|
||||
"바인딩": { "종류": "open_ditch", "펼침": ["ditch_spec"], "값": "length_m", "묶음": "od", "머리틀": ["횡단개거", "{0}", "길이"] }
|
||||
"바인딩": { "종류": "open_ditch", "펼침": ["ditch_spec"], "값": "length_m", "묶음": "od", "머리틀": ["횡단개거", "{0}", "길이"], "빈값": ["규격 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "od_cnt", "머리": ["횡단개거", "(규격마다)", "개소"], "단위": "개소", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 개거 · 노출형 횡단수로 개소",
|
||||
"일위대가": "UA000003",
|
||||
"바인딩": {
|
||||
"종류": "open_ditch", "펼침": ["ditch_spec"], "값": "개소", "묶음": "od", "머리틀": ["횡단개거", "{0}", "개소"],
|
||||
"종류": "open_ditch", "펼침": ["ditch_spec"], "값": "개소", "묶음": "od", "머리틀": ["횡단개거", "{0}", "개소"], "빈값": ["규격 미입력"],
|
||||
"더함": [ { "종류": "cross_drain_exposed", "펼침": ["=노출형"], "값": "개소" } ]
|
||||
}
|
||||
},
|
||||
@@ -146,13 +146,13 @@
|
||||
{
|
||||
"id": "rw", "머리": ["옹벽", "(형식마다)", "H=(높이마다)"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 옹벽 길이",
|
||||
"바인딩": { "종류": "retaining_wall", "펼침": ["form", "height_m"], "값": "길이", "묶음": "rw", "머리틀": ["옹벽", "{0}", "H={1}"] }
|
||||
"바인딩": { "종류": "retaining_wall", "펼침": ["form", "height_m"], "값": "길이", "묶음": "rw", "머리틀": ["옹벽", "{0}", "{1}"], "값꼴": ["{}", "H={}"], "빈값": ["형식 미입력", "높이 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "ms", "머리": ["돌쌓기", "(찰·메마다)", "H=(높이마다)"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 돌쌓기 길이",
|
||||
"바인딩": {
|
||||
"종류": "masonry_wet", "펼침": ["=찰쌓기", "height_m"], "값": "길이", "묶음": "ms", "머리틀": ["돌쌓기", "{0}", "H={1}"],
|
||||
"종류": "masonry_wet", "펼침": ["=찰쌓기", "height_m"], "값": "길이", "묶음": "ms", "머리틀": ["돌쌓기", "{0}", "{1}"], "값꼴": ["{}", "H={}"], "빈값": ["", "높이 미입력"],
|
||||
"더함": [ { "종류": "masonry_dry", "펼침": ["=메쌓기", "height_m"], "값": "길이" } ]
|
||||
}
|
||||
},
|
||||
@@ -160,19 +160,19 @@
|
||||
"id": "sg", "머리": ["흙막이", "(형태마다)", "H=(높이마다)"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 흙막이 길이 · 일위대가는 큰돌만",
|
||||
"일위대가": "UA000032",
|
||||
"바인딩": { "종류": "soil_guard", "펼침": ["form", "height_m"], "값": "길이", "묶음": "sg", "머리틀": ["흙막이", "{0}", "H={1}"] }
|
||||
"바인딩": { "종류": "soil_guard", "펼침": ["form", "height_m"], "값": "길이", "묶음": "sg", "머리틀": ["흙막이", "{0}", "{1}"], "값꼴": ["{}", "H={}"], "빈값": ["형식 미입력", "높이 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "bm_len", "머리": ["큰돌쌓기", "(찰·메 · 돌 크기 · 높이마다)", "길이"], "단위": "m", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 큰돌쌓기 길이",
|
||||
"바인딩": { "종류": "boulder_masonry", "펼침": ["bond", "stone_cm", "height_m"], "값": "길이", "묶음": "bm", "머리틀": ["큰돌쌓기", "{0} {1} H={2}", "길이"] }
|
||||
"바인딩": { "종류": "boulder_masonry", "펼침": ["bond", "stone_cm", "height_m"], "값": "길이", "묶음": "bm", "머리틀": ["큰돌쌓기", "{0} {1} {2}", "길이"], "값꼴": ["{}", "{}", "H={}"], "빈값": ["쌓기 방식 미입력", "돌규격 미입력", "높이 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "bm_area", "머리": ["큰돌쌓기", "(찰·메 · 돌 크기 · 높이마다)", "면적"], "단위": "㎡", "꼴": "수", "펼침": true,
|
||||
"설명": "계산 — L × H (H = 펼친 높이)",
|
||||
"일위대가": "UA000031",
|
||||
"바인딩": {
|
||||
"종류": "boulder_masonry", "펼침": ["bond", "stone_cm", "height_m"], "값": "식", "묶음": "bm", "머리틀": ["큰돌쌓기", "{0} {1} H={2}", "면적"],
|
||||
"펼침틀": {
|
||||
"묶음": "bm", "머리틀": ["큰돌쌓기", "{0} {1} {2}", "면적"], "값꼴": ["{}", "{}", "H={}"], "빈값": ["쌓기 방식 미입력", "돌규격 미입력", "높이 미입력"],
|
||||
"식틀": "IF([bm_len]>0,[bm_len]*{2},\"\")"
|
||||
}
|
||||
},
|
||||
@@ -180,7 +180,7 @@
|
||||
{
|
||||
"id": "ec", "머리": ["골막이", "(형태마다)", null], "단위": "개소", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 골막이 개소",
|
||||
"바인딩": { "종류": "erosion_check", "펼침": ["form"], "값": "개소", "묶음": "ec", "머리틀": ["골막이", "{0}", null] }
|
||||
"바인딩": { "종류": "erosion_check", "펼침": ["form"], "값": "개소", "묶음": "ec", "머리틀": ["골막이", "{0}", null], "빈값": ["형식 미입력"] }
|
||||
},
|
||||
{
|
||||
"id": "be", "머리": ["소단", null, null], "단위": "m", "꼴": "수",
|
||||
@@ -200,7 +200,7 @@
|
||||
{
|
||||
"id": "etc", "머리": ["기타", "(이름마다)", null], "단위": "개소", "꼴": "수", "펼침": true,
|
||||
"설명": "설계값 — 기타 구조물 개소",
|
||||
"바인딩": { "종류": "etc", "펼침": ["name"], "값": "개소", "묶음": "etc", "머리틀": ["기타", "{0}", null] }
|
||||
"바인딩": { "종류": "etc", "펼침": ["name"], "값": "개소", "묶음": "etc", "머리틀": ["기타", "{0}", null], "빈값": ["이름 미입력"] }
|
||||
},
|
||||
|
||||
{ "id": "h_intake", "머리": ["취수정", null, null], "단위": "개소", "꼴": "수", "손": true, "설명": "손 입력", "일위대가": "UA000005" },
|
||||
|
||||
@@ -92,10 +92,37 @@ def test_펼침_열은_설계에_쓰인_값만_차례대로(project: Path) -> No
|
||||
assert "rv" not in ids and "ps" in ids # 펼침 마스터 칸은 빠지고 고정 열은 남음
|
||||
# 관 유입·유출 기슭막이는 레지스트리 기본값(찰 · 메 · 높이 없음)으로 열이 섬 · id 는 설계 값
|
||||
assert "rv|돌쌓기(찰)|-" in ids and "rv|돌쌓기(메)|-" in ids
|
||||
assert _col(doc, "rv|돌쌓기(찰)|-")["머리"] == ["돌기슭막이", "찰쌓기", "H=-"]
|
||||
assert _col(doc, "rv|돌쌓기(찰)|-")["머리"] == ["돌기슭막이", "찰쌓기", "높이 미입력"]
|
||||
assert _col(doc, "fb|1000|-")["머리"] == ["세월교", "Φ1000×련수 미입력", None]
|
||||
assert not any(i.startswith("pg_in|") for i in ids) # 날개벽·집수정 형식 없음 → 관보호공 없음
|
||||
|
||||
|
||||
def test_묶음_열은_값마다_한데(project: Path) -> None:
|
||||
ids = [column["id"] for column in _filled(project)["열"]]
|
||||
assert [i for i in ids if i.startswith("pp_")] == [
|
||||
"pp_len|흄관|1000",
|
||||
"pp_cp|흄관|1000",
|
||||
"pp_len|파형강관|1000",
|
||||
"pp_cp|파형강관|1000",
|
||||
]
|
||||
assert [i for i in ids if i.startswith("pv_")] == [
|
||||
f"pv_{k}|20" for k in ("b", "l", "w", "a", "jt")
|
||||
]
|
||||
|
||||
|
||||
def test_계산_열은_설계값_딱지_없음(project: Path) -> None:
|
||||
master = _master()
|
||||
assert not [c["id"] for c in master["열"] if c.get("식") and c.get("바인딩")]
|
||||
doc = _filled(project)
|
||||
for col_id in ("pv_a|20", "pv_jt|20", "pp_cp|파형강관|1000", "bm_area|메쌓기|40~60|2.5"):
|
||||
column = _col(doc, col_id)
|
||||
assert "바인딩" not in column and column["펼침틀"]["원열"]["id"] == col_id.split("|")[0]
|
||||
assert _col(doc, "pp_cp|파형강관|1000")["머리"] == ["관공", "파형강관 Φ1000", "커플링밴드"]
|
||||
# 다시 저장 · 채워도 같은 열(계산 열도 원열로 되돌림)
|
||||
again = fill.fill_table(project, fill.strip_design(doc), {85.0: 14.0})
|
||||
assert [c["id"] for c in again["열"]] == [c["id"] for c in doc["열"]]
|
||||
|
||||
|
||||
def test_계산_열_식은_같은_묶음_열로_바뀌고_빈_변수면_안_셈(project: Path) -> None:
|
||||
doc = _filled(project)
|
||||
assert _col(doc, "pv_a|20")["식"] == 'IF([pv_l|20]>0,[pv_b|20]*[pv_l|20]+[pv_w|20],"")'
|
||||
|
||||
@@ -295,3 +295,44 @@ def test_프로젝트_표_저장은_서버가_설계값을_걸러_씀(world: dic
|
||||
stored = layers.read_template(_root(world, P1) / "templates", "table", "구조물집계표")["문서"]
|
||||
assert stored["줄"][1] == {"id": "s40.00", "값": {"sta": "NO.2", "h_intake": 2}}
|
||||
assert "알림" not in stored and all("|" not in c["id"] for c in stored["열"])
|
||||
|
||||
|
||||
def test_옛_표_틀은_새_판으로_손_값은_지킴(world: dict[str, Any]) -> None:
|
||||
from M02_MasterTemplete import M02_Template_Migrate as migrate
|
||||
|
||||
new = {
|
||||
"판": 2,
|
||||
"열": [{"id": "a", "식": "1", "펼침틀": {"묶음": "g"}}],
|
||||
"줄": [],
|
||||
"변수": {"k": 1},
|
||||
}
|
||||
old = {
|
||||
"판": 1,
|
||||
"열": [{"id": "a", "식": "1", "바인딩": {"종류": "pipe", "값": "식"}}, {"id": "memo"}],
|
||||
"줄": [{"id": "s10.00", "값": {"sta": "NO.0+10", "memo": "손"}}],
|
||||
"변수": {"k": 5},
|
||||
}
|
||||
layers.write_template(layers.system_dir(), "table", "구조물집계표", new)
|
||||
root2, root1 = _root(world, P2, user=43), _root(world, P1)
|
||||
for folder in (root1 / "templates", root1 / "templates/_initial", root2 / "templates"):
|
||||
layers.write_template(folder, "table", "구조물집계표", old)
|
||||
layers.write_template(
|
||||
root2 / "templates", "table", "구조물집계표", {**old, "줄": [], "변수": {"k": 1}}
|
||||
)
|
||||
|
||||
assert migrate.refresh_tables(root1, dry_run=True) == [
|
||||
"templates/table/구조물집계표",
|
||||
"_initial/table/구조물집계표",
|
||||
]
|
||||
migrate.refresh_tables(root1)
|
||||
work = layers.read_template(root1 / "templates", "table", "구조물집계표")["문서"]
|
||||
assert work["열"] == new["열"] and work["변수"] == {"k": 5} # 틀만 새 판 · 변수 · 손 값 지킴
|
||||
assert work["줄"] == old["줄"]
|
||||
system_version = layers.version_of(layers.system_dir() / "table/구조물집계표.json")
|
||||
initial = root1 / "templates/_initial"
|
||||
assert layers.version_of(initial / "table/구조물집계표.json") != system_version # 변수 다름
|
||||
assert layers.read_manifest(root1 / "templates")["table/구조물집계표"]["판"] == system_version
|
||||
assert migrate.refresh_tables(root1) == [] # 다시 돌려도 그대로
|
||||
# 손댄 것 없는 작업본은 시스템 파일 그대로(판이 같음)
|
||||
migrate.refresh_tables(root2)
|
||||
assert layers.version_of(root2 / "templates/table/구조물집계표.json") == system_version
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
"""표 쪽 나눔 — `ui_template_sheet_ops.ts` `pagePlan` 을 Node 로 바로 돌려 봄.
|
||||
|
||||
격자(`_render`)가 쪽을 이 계획대로 그림.
|
||||
|
||||
줄 120 문서 → project 쪽 3(50 · 50 · 20) · 쪽마다 머리(격자가 쪽마다 thead) · 합계는 마지막 쪽만 ·
|
||||
「전구간」 줄은 맨 위 · master 는 한 쪽.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
OPS = ROOT / "ui_template" / "sheet" / "ui_template_sheet_ops.ts"
|
||||
|
||||
pytestmark = pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음")
|
||||
|
||||
SCRIPT = """
|
||||
const { pagePlan } = await import(process.argv[1]);
|
||||
const doc = JSON.parse(process.argv[2]);
|
||||
const out = {};
|
||||
for (const mode of ["project", "master"]) {
|
||||
out[mode] = pagePlan(doc, mode).map((p) => ({
|
||||
ids: p.rows.map((r) => r.id), first: p.first, totals: p.totals,
|
||||
}));
|
||||
}
|
||||
console.log(JSON.stringify(out));
|
||||
"""
|
||||
|
||||
|
||||
def _plan(doc: dict) -> dict:
|
||||
result = subprocess.run(
|
||||
[
|
||||
"node",
|
||||
"--experimental-strip-types",
|
||||
"--no-warnings",
|
||||
"--input-type=module",
|
||||
"-e",
|
||||
SCRIPT,
|
||||
OPS.as_uri(),
|
||||
json.dumps(doc, ensure_ascii=False),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert result.returncode == 0, result.stderr
|
||||
return json.loads(result.stdout)
|
||||
|
||||
|
||||
def _doc(n: int, **head) -> dict:
|
||||
rows = [{"id": f"p{i}", "값": {}} for i in range(1, n)]
|
||||
rows.insert(7, {"id": "all", "값": {}, "고정": "전구간"}) # 문서 가운데 있어도 맨 위로
|
||||
return {"양식": "시험", "종류": "표", "판": 1, "열": [], "줄": rows, **head}
|
||||
|
||||
|
||||
def test_120_rows_make_three_pages_totals_last():
|
||||
plan = _plan(_doc(120))["project"]
|
||||
assert [len(p["ids"]) for p in plan] == [50, 50, 20]
|
||||
assert [p["first"] for p in plan] == [1, 51, 101]
|
||||
assert [p["totals"] for p in plan] == [False, False, True]
|
||||
assert plan[0]["ids"][0] == "all"
|
||||
assert sum((p["ids"] for p in plan), []).count("all") == 1
|
||||
|
||||
|
||||
def test_page_rows_from_doc_and_master_single_page():
|
||||
got = _plan(_doc(55, 쪽줄=20))
|
||||
assert [len(p["ids"]) for p in got["project"]] == [20, 20, 15]
|
||||
assert [len(p["ids"]) for p in got["master"]] == [55]
|
||||
assert got["master"][0]["totals"] is True
|
||||
|
||||
|
||||
def test_empty_doc_still_one_page_with_totals():
|
||||
plan = _plan({"양식": "시험", "종류": "표", "판": 1, "열": [], "줄": []})["project"]
|
||||
assert plan == [{"ids": [], "first": 1, "totals": True}]
|
||||
Reference in New Issue
Block a user