Merge remote-tracking branch 'origin/sub_laptop_4' into sub_laptop_5
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,19 @@
|
||||
{
|
||||
"그룹": "구조물집계표조합",
|
||||
"판": 1,
|
||||
"비고": "구조물집계표 구조물 열(꼴=수)마다 한 줄 · 서버가 집계표 [저장] 때 맞춤(손으로 고치지 않음) · 키 SS 는 한 번 주면 안 바뀌고 지운 열의 키는 다시 안 씀",
|
||||
"비고": "구조물집계표 구조물 열(꼴=수)마다 한 줄 · 서버가 집계표 [저장] 때 맞춤(손으로 고치지 않음) · 키 SS 는 한 번 주면 안 바뀌고 지운 열의 키는 다시 안 씀 · 경로 = 폴더 이름 배열",
|
||||
"다음": 52,
|
||||
"줄": [
|
||||
{
|
||||
"키": "SS000001",
|
||||
"열": "rv",
|
||||
"경로": [
|
||||
"돌기슭막이",
|
||||
"메쌓기",
|
||||
"1.2m",
|
||||
"기초유"
|
||||
],
|
||||
"이름": "돌기슭막이 메쌓기 1.2m 기초유",
|
||||
"종류": "돌기슭막이",
|
||||
"분류1": "메쌓기",
|
||||
"분류2": "1.2m",
|
||||
@@ -72,6 +79,13 @@
|
||||
{
|
||||
"키": "SS000002",
|
||||
"열": "c4",
|
||||
"경로": [
|
||||
"돌기슭막이",
|
||||
"메쌓기",
|
||||
"1.2m",
|
||||
"기초무, 소단"
|
||||
],
|
||||
"이름": "돌기슭막이 메쌓기 1.2m 기초무, 소단",
|
||||
"종류": "돌기슭막이",
|
||||
"분류1": "메쌓기",
|
||||
"분류2": "1.2m",
|
||||
@@ -88,6 +102,13 @@
|
||||
{
|
||||
"키": "SS000003",
|
||||
"열": "c1",
|
||||
"경로": [
|
||||
"돌기슭막이",
|
||||
"메쌓기",
|
||||
"1.5m",
|
||||
"기초유"
|
||||
],
|
||||
"이름": "돌기슭막이 메쌓기 1.5m 기초유",
|
||||
"종류": "돌기슭막이",
|
||||
"분류1": "메쌓기",
|
||||
"분류2": "1.5m",
|
||||
@@ -104,6 +125,11 @@
|
||||
{
|
||||
"키": "SS000004",
|
||||
"열": "c5",
|
||||
"경로": [
|
||||
"돌기슭막이",
|
||||
"찰쌓기"
|
||||
],
|
||||
"이름": "돌기슭막이 찰쌓기",
|
||||
"종류": "돌기슭막이",
|
||||
"분류1": "찰쌓기",
|
||||
"분류2": null,
|
||||
@@ -120,6 +146,12 @@
|
||||
{
|
||||
"키": "SS000005",
|
||||
"열": "pv_b",
|
||||
"경로": [
|
||||
"콘크리트포장",
|
||||
"T=(두께마다)",
|
||||
"B"
|
||||
],
|
||||
"이름": "콘크리트포장 T=(두께마다) B",
|
||||
"종류": "콘크리트포장",
|
||||
"분류1": "T=(두께마다)",
|
||||
"분류2": "B",
|
||||
@@ -154,6 +186,12 @@
|
||||
{
|
||||
"키": "SS000006",
|
||||
"열": "pv_l",
|
||||
"경로": [
|
||||
"콘크리트포장",
|
||||
"T=(두께마다)",
|
||||
"L"
|
||||
],
|
||||
"이름": "콘크리트포장 T=(두께마다) L",
|
||||
"종류": "콘크리트포장",
|
||||
"분류1": "T=(두께마다)",
|
||||
"분류2": "L",
|
||||
@@ -188,6 +226,12 @@
|
||||
{
|
||||
"키": "SS000007",
|
||||
"열": "pv_w",
|
||||
"경로": [
|
||||
"콘크리트포장",
|
||||
"T=(두께마다)",
|
||||
"확폭"
|
||||
],
|
||||
"이름": "콘크리트포장 T=(두께마다) 확폭",
|
||||
"종류": "콘크리트포장",
|
||||
"분류1": "T=(두께마다)",
|
||||
"분류2": "확폭",
|
||||
@@ -222,6 +266,12 @@
|
||||
{
|
||||
"키": "SS000008",
|
||||
"열": "pv_a",
|
||||
"경로": [
|
||||
"콘크리트포장",
|
||||
"T=(두께마다)",
|
||||
"A"
|
||||
],
|
||||
"이름": "콘크리트포장 T=(두께마다) A",
|
||||
"종류": "콘크리트포장",
|
||||
"분류1": "T=(두께마다)",
|
||||
"분류2": "A",
|
||||
@@ -238,6 +288,12 @@
|
||||
{
|
||||
"키": "SS000009",
|
||||
"열": "pv_jt",
|
||||
"경로": [
|
||||
"콘크리트포장",
|
||||
"T=(두께마다)",
|
||||
"수축줄눈"
|
||||
],
|
||||
"이름": "콘크리트포장 T=(두께마다) 수축줄눈",
|
||||
"종류": "콘크리트포장",
|
||||
"분류1": "T=(두께마다)",
|
||||
"분류2": "수축줄눈",
|
||||
@@ -254,6 +310,12 @@
|
||||
{
|
||||
"키": "SS000010",
|
||||
"열": "c3",
|
||||
"경로": [
|
||||
"콘크리트포장",
|
||||
"T=(두께마다)",
|
||||
"새 분류2"
|
||||
],
|
||||
"이름": "콘크리트포장 T=(두께마다) 새 분류2",
|
||||
"종류": "콘크리트포장",
|
||||
"분류1": "T=(두께마다)",
|
||||
"분류2": "새 분류2",
|
||||
@@ -270,6 +332,12 @@
|
||||
{
|
||||
"키": "SS000011",
|
||||
"열": "pp_len",
|
||||
"경로": [
|
||||
"관공",
|
||||
"(관종·관경마다)",
|
||||
"관매설"
|
||||
],
|
||||
"이름": "관공 (관종·관경마다) 관매설",
|
||||
"종류": "관공",
|
||||
"분류1": "(관종·관경마다)",
|
||||
"분류2": "관매설",
|
||||
@@ -307,6 +375,12 @@
|
||||
{
|
||||
"키": "SS000012",
|
||||
"열": "pp_cp",
|
||||
"경로": [
|
||||
"관공",
|
||||
"(관종·관경마다)",
|
||||
"커플링밴드"
|
||||
],
|
||||
"이름": "관공 (관종·관경마다) 커플링밴드",
|
||||
"종류": "관공",
|
||||
"분류1": "(관종·관경마다)",
|
||||
"분류2": "커플링밴드",
|
||||
@@ -323,6 +397,12 @@
|
||||
{
|
||||
"키": "SS000013",
|
||||
"열": "pg_in",
|
||||
"경로": [
|
||||
"관보호공",
|
||||
"(형식마다)",
|
||||
"유입"
|
||||
],
|
||||
"이름": "관보호공 (형식마다) 유입",
|
||||
"종류": "관보호공",
|
||||
"분류1": "(형식마다)",
|
||||
"분류2": "유입",
|
||||
@@ -371,6 +451,12 @@
|
||||
{
|
||||
"키": "SS000014",
|
||||
"열": "bx_len",
|
||||
"경로": [
|
||||
"BOX암거",
|
||||
"(폭×높이마다)",
|
||||
"연장"
|
||||
],
|
||||
"이름": "BOX암거 (폭×높이마다) 연장",
|
||||
"종류": "BOX암거",
|
||||
"분류1": "(폭×높이마다)",
|
||||
"분류2": "연장",
|
||||
@@ -404,6 +490,12 @@
|
||||
{
|
||||
"키": "SS000015",
|
||||
"열": "bx_wing",
|
||||
"경로": [
|
||||
"BOX암거",
|
||||
"(폭×높이마다)",
|
||||
"날개벽"
|
||||
],
|
||||
"이름": "BOX암거 (폭×높이마다) 날개벽",
|
||||
"종류": "BOX암거",
|
||||
"분류1": "(폭×높이마다)",
|
||||
"분류2": "날개벽",
|
||||
@@ -453,6 +545,11 @@
|
||||
{
|
||||
"키": "SS000016",
|
||||
"열": "fb",
|
||||
"경로": [
|
||||
"세월교",
|
||||
"(관경 × 련수마다)"
|
||||
],
|
||||
"이름": "세월교 (관경 × 련수마다)",
|
||||
"종류": "세월교",
|
||||
"분류1": "(관경 × 련수마다)",
|
||||
"분류2": null,
|
||||
@@ -490,6 +587,12 @@
|
||||
{
|
||||
"키": "SS000017",
|
||||
"열": "fp_w",
|
||||
"경로": [
|
||||
"물넘이포장",
|
||||
"T=(두께마다)",
|
||||
"폭"
|
||||
],
|
||||
"이름": "물넘이포장 T=(두께마다) 폭",
|
||||
"종류": "물넘이포장",
|
||||
"분류1": "T=(두께마다)",
|
||||
"분류2": "폭",
|
||||
@@ -524,6 +627,12 @@
|
||||
{
|
||||
"키": "SS000018",
|
||||
"열": "fp_l",
|
||||
"경로": [
|
||||
"물넘이포장",
|
||||
"T=(두께마다)",
|
||||
"길이"
|
||||
],
|
||||
"이름": "물넘이포장 T=(두께마다) 길이",
|
||||
"종류": "물넘이포장",
|
||||
"분류1": "T=(두께마다)",
|
||||
"분류2": "길이",
|
||||
@@ -558,6 +667,12 @@
|
||||
{
|
||||
"키": "SS000019",
|
||||
"열": "od_len",
|
||||
"경로": [
|
||||
"횡단개거",
|
||||
"(규격마다)",
|
||||
"길이"
|
||||
],
|
||||
"이름": "횡단개거 (규격마다) 길이",
|
||||
"종류": "횡단개거",
|
||||
"분류1": "(규격마다)",
|
||||
"분류2": "길이",
|
||||
@@ -589,6 +704,12 @@
|
||||
{
|
||||
"키": "SS000020",
|
||||
"열": "od_cnt",
|
||||
"경로": [
|
||||
"횡단개거",
|
||||
"(규격마다)",
|
||||
"개소"
|
||||
],
|
||||
"이름": "횡단개거 (규격마다) 개소",
|
||||
"종류": "횡단개거",
|
||||
"분류1": "(규격마다)",
|
||||
"분류2": "개소",
|
||||
@@ -629,6 +750,12 @@
|
||||
{
|
||||
"키": "SS000021",
|
||||
"열": "rw",
|
||||
"경로": [
|
||||
"옹벽",
|
||||
"(형식마다)",
|
||||
"H=(높이마다)"
|
||||
],
|
||||
"이름": "옹벽 (형식마다) H=(높이마다)",
|
||||
"종류": "옹벽",
|
||||
"분류1": "(형식마다)",
|
||||
"분류2": "H=(높이마다)",
|
||||
@@ -666,6 +793,12 @@
|
||||
{
|
||||
"키": "SS000022",
|
||||
"열": "ms",
|
||||
"경로": [
|
||||
"돌쌓기",
|
||||
"(찰·메마다)",
|
||||
"H=(높이마다)"
|
||||
],
|
||||
"이름": "돌쌓기 (찰·메마다) H=(높이마다)",
|
||||
"종류": "돌쌓기",
|
||||
"분류1": "(찰·메마다)",
|
||||
"분류2": "H=(높이마다)",
|
||||
@@ -713,6 +846,12 @@
|
||||
{
|
||||
"키": "SS000023",
|
||||
"열": "sg",
|
||||
"경로": [
|
||||
"흙막이",
|
||||
"(형태마다)",
|
||||
"H=(높이마다)"
|
||||
],
|
||||
"이름": "흙막이 (형태마다) H=(높이마다)",
|
||||
"종류": "흙막이",
|
||||
"분류1": "(형태마다)",
|
||||
"분류2": "H=(높이마다)",
|
||||
@@ -750,6 +889,12 @@
|
||||
{
|
||||
"키": "SS000024",
|
||||
"열": "bm_len",
|
||||
"경로": [
|
||||
"큰돌쌓기",
|
||||
"(찰·메 · 돌 크기 · 높이마다)",
|
||||
"길이"
|
||||
],
|
||||
"이름": "큰돌쌓기 (찰·메 · 돌 크기 · 높이마다) 길이",
|
||||
"종류": "큰돌쌓기",
|
||||
"분류1": "(찰·메 · 돌 크기 · 높이마다)",
|
||||
"분류2": "길이",
|
||||
@@ -790,6 +935,12 @@
|
||||
{
|
||||
"키": "SS000025",
|
||||
"열": "bm_area",
|
||||
"경로": [
|
||||
"큰돌쌓기",
|
||||
"(찰·메 · 돌 크기 · 높이마다)",
|
||||
"면적"
|
||||
],
|
||||
"이름": "큰돌쌓기 (찰·메 · 돌 크기 · 높이마다) 면적",
|
||||
"종류": "큰돌쌓기",
|
||||
"분류1": "(찰·메 · 돌 크기 · 높이마다)",
|
||||
"분류2": "면적",
|
||||
@@ -806,6 +957,11 @@
|
||||
{
|
||||
"키": "SS000026",
|
||||
"열": "ec",
|
||||
"경로": [
|
||||
"골막이",
|
||||
"(형태마다)"
|
||||
],
|
||||
"이름": "골막이 (형태마다)",
|
||||
"종류": "골막이",
|
||||
"분류1": "(형태마다)",
|
||||
"분류2": null,
|
||||
@@ -837,6 +993,10 @@
|
||||
{
|
||||
"키": "SS000027",
|
||||
"열": "be",
|
||||
"경로": [
|
||||
"소단"
|
||||
],
|
||||
"이름": "소단",
|
||||
"종류": "소단",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -856,6 +1016,10 @@
|
||||
{
|
||||
"키": "SS000028",
|
||||
"열": "rf",
|
||||
"경로": [
|
||||
"대피소"
|
||||
],
|
||||
"이름": "대피소",
|
||||
"종류": "대피소",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -875,6 +1039,11 @@
|
||||
{
|
||||
"키": "SS000029",
|
||||
"열": "c2",
|
||||
"경로": [
|
||||
"대피소",
|
||||
"새 분류1"
|
||||
],
|
||||
"이름": "대피소 새 분류1",
|
||||
"종류": "대피소",
|
||||
"분류1": "새 분류1",
|
||||
"분류2": null,
|
||||
@@ -891,6 +1060,10 @@
|
||||
{
|
||||
"키": "SS000030",
|
||||
"열": "wy",
|
||||
"경로": [
|
||||
"정차·작업장"
|
||||
],
|
||||
"이름": "정차·작업장",
|
||||
"종류": "정차·작업장",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -910,6 +1083,10 @@
|
||||
{
|
||||
"키": "SS000031",
|
||||
"열": "ta",
|
||||
"경로": [
|
||||
"차돌림곳"
|
||||
],
|
||||
"이름": "차돌림곳",
|
||||
"종류": "차돌림곳",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -929,6 +1106,10 @@
|
||||
{
|
||||
"키": "SS000032",
|
||||
"열": "gr_rail",
|
||||
"경로": [
|
||||
"가드레일"
|
||||
],
|
||||
"이름": "가드레일",
|
||||
"종류": "가드레일",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -951,6 +1132,10 @@
|
||||
{
|
||||
"키": "SS000033",
|
||||
"열": "gr_curb",
|
||||
"경로": [
|
||||
"경계석"
|
||||
],
|
||||
"이름": "경계석",
|
||||
"종류": "경계석",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -973,6 +1158,10 @@
|
||||
{
|
||||
"키": "SS000034",
|
||||
"열": "gr_sign",
|
||||
"경로": [
|
||||
"위험표지"
|
||||
],
|
||||
"이름": "위험표지",
|
||||
"종류": "위험표지",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -995,6 +1184,10 @@
|
||||
{
|
||||
"키": "SS000035",
|
||||
"열": "mr",
|
||||
"경로": [
|
||||
"반사경"
|
||||
],
|
||||
"이름": "반사경",
|
||||
"종류": "반사경",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1014,6 +1207,10 @@
|
||||
{
|
||||
"키": "SS000036",
|
||||
"열": "bg",
|
||||
"경로": [
|
||||
"차단기"
|
||||
],
|
||||
"이름": "차단기",
|
||||
"종류": "차단기",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1033,6 +1230,10 @@
|
||||
{
|
||||
"키": "SS000037",
|
||||
"열": "ps",
|
||||
"경로": [
|
||||
"국가지점번호판"
|
||||
],
|
||||
"이름": "국가지점번호판",
|
||||
"종류": "국가지점번호판",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1052,6 +1253,10 @@
|
||||
{
|
||||
"키": "SS000038",
|
||||
"열": "cs",
|
||||
"경로": [
|
||||
"준공표지석"
|
||||
],
|
||||
"이름": "준공표지석",
|
||||
"종류": "준공표지석",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1071,6 +1276,11 @@
|
||||
{
|
||||
"키": "SS000039",
|
||||
"열": "etc",
|
||||
"경로": [
|
||||
"기타",
|
||||
"(이름마다)"
|
||||
],
|
||||
"이름": "기타 (이름마다)",
|
||||
"종류": "기타",
|
||||
"분류1": "(이름마다)",
|
||||
"분류2": null,
|
||||
@@ -1102,6 +1312,10 @@
|
||||
{
|
||||
"키": "SS000040",
|
||||
"열": "h_intake",
|
||||
"경로": [
|
||||
"취수정"
|
||||
],
|
||||
"이름": "취수정",
|
||||
"종류": "취수정",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1118,6 +1332,11 @@
|
||||
{
|
||||
"키": "SS000041",
|
||||
"열": "h_rip_c",
|
||||
"경로": [
|
||||
"돌붙임",
|
||||
"찰붙임"
|
||||
],
|
||||
"이름": "돌붙임 찰붙임",
|
||||
"종류": "돌붙임",
|
||||
"분류1": "찰붙임",
|
||||
"분류2": null,
|
||||
@@ -1134,6 +1353,11 @@
|
||||
{
|
||||
"키": "SS000042",
|
||||
"열": "h_rip_m",
|
||||
"경로": [
|
||||
"돌붙임",
|
||||
"메붙임"
|
||||
],
|
||||
"이름": "돌붙임 메붙임",
|
||||
"종류": "돌붙임",
|
||||
"분류1": "메붙임",
|
||||
"분류2": null,
|
||||
@@ -1150,6 +1374,10 @@
|
||||
{
|
||||
"키": "SS000043",
|
||||
"열": "h_stone_ch",
|
||||
"경로": [
|
||||
"돌수로"
|
||||
],
|
||||
"이름": "돌수로",
|
||||
"종류": "돌수로",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1166,6 +1394,10 @@
|
||||
{
|
||||
"키": "SS000044",
|
||||
"열": "h_stone_bed",
|
||||
"경로": [
|
||||
"돌조공"
|
||||
],
|
||||
"이름": "돌조공",
|
||||
"종류": "돌조공",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1182,6 +1414,10 @@
|
||||
{
|
||||
"키": "SS000045",
|
||||
"열": "h_pv_rail",
|
||||
"경로": [
|
||||
"포장 난간"
|
||||
],
|
||||
"이름": "포장 난간",
|
||||
"종류": "포장 난간",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1198,6 +1434,10 @@
|
||||
{
|
||||
"키": "SS000046",
|
||||
"열": "h_pg_rail",
|
||||
"경로": [
|
||||
"관보호공 안전난간"
|
||||
],
|
||||
"이름": "관보호공 안전난간",
|
||||
"종류": "관보호공 안전난간",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1214,6 +1454,10 @@
|
||||
{
|
||||
"키": "SS000047",
|
||||
"열": "h_soil_ditch",
|
||||
"경로": [
|
||||
"수로형 토사개거"
|
||||
],
|
||||
"이름": "수로형 토사개거",
|
||||
"종류": "수로형 토사개거",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1230,6 +1474,10 @@
|
||||
{
|
||||
"키": "SS000048",
|
||||
"열": "h_ford_basin",
|
||||
"경로": [
|
||||
"물넘이 집수부"
|
||||
],
|
||||
"이름": "물넘이 집수부",
|
||||
"종류": "물넘이 집수부",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1246,6 +1494,10 @@
|
||||
{
|
||||
"키": "SS000049",
|
||||
"열": "h_rockfall",
|
||||
"경로": [
|
||||
"낙석방지책"
|
||||
],
|
||||
"이름": "낙석방지책",
|
||||
"종류": "낙석방지책",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1262,6 +1514,10 @@
|
||||
{
|
||||
"키": "SS000050",
|
||||
"열": "h_break",
|
||||
"경로": [
|
||||
"콘크리트·포장 깨기"
|
||||
],
|
||||
"이름": "콘크리트·포장 깨기",
|
||||
"종류": "콘크리트·포장 깨기",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
@@ -1278,6 +1534,10 @@
|
||||
{
|
||||
"키": "SS000051",
|
||||
"열": "h_waste",
|
||||
"경로": [
|
||||
"폐기물처리"
|
||||
],
|
||||
"이름": "폐기물처리",
|
||||
"종류": "폐기물처리",
|
||||
"분류1": null,
|
||||
"분류2": null,
|
||||
|
||||
@@ -67,13 +67,14 @@ def test_옛_산출근거를_면적산출로_옮기고_번호_8자리(client, tm
|
||||
def test_새_구조물_도면은_면적_재료_진짜_빈_시트_같은_8자리(client):
|
||||
made1 = client.post("/api/m02/structures/rv").json()["문서"]
|
||||
made2 = client.post("/api/m02/structures/pv_b").json()["문서"]
|
||||
assert made1["도번"] == "SD01010100" and made2["도번"] == "SD02010100"
|
||||
# 새 항목 번호 = 자리 뜻 없는 일련번호(PLAN 26-1)
|
||||
assert made1["도번"] == "SD00000001" and made2["도번"] == "SD00000002"
|
||||
for kind, key, head in (("area", "면적번", "SA"), ("material", "재료번", "SM")):
|
||||
doc = client.get(f"/api/m02/templates/{kind}/pv_b").json()["문서"]
|
||||
assert doc[key] == f"{head}02010100"
|
||||
assert doc[key] == f"{head}00000002"
|
||||
assert doc["시트"][0]["칸"] == {} and doc["서식"] == [{}] # 머리 줄 없음
|
||||
numbers = client.get("/api/m02/structures/numbers").json()
|
||||
assert numbers["pv_b"] == {"도번": "SD02010100", "면적번": "SA02010100", "재료번": "SM02010100"}
|
||||
assert numbers["pv_b"] == {"도번": "SD00000002", "면적번": "SA00000002", "재료번": "SM00000002"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -89,7 +90,7 @@ def test_다른_열_내용을_복사해_가져옴_연결도_같이(client, kind,
|
||||
assert r.status_code == 200
|
||||
ms = client.get(f"/api/m02/templates/{kind}/ms").json()["문서"]
|
||||
assert (ms["시트"][0]["칸"], ms["연결"]) == ({"A1": {"식": "1+1"}}, LINKS)
|
||||
assert (ms[key], ms["열"]) == (f"{head}05010100", "ms") # 번호 · 열은 받는 쪽 그대로
|
||||
assert (ms[key], ms["열"]) == (f"{head}00000010", "ms") # 번호 · 열은 받는 쪽 그대로
|
||||
assert ms["계산값"] == SERVER # 서버가 다시 풂
|
||||
assert client.post(f"/api/m02/{kind}/ms/copy", params={"from": "없는열"}).status_code == 404
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""M02 구조물 도면(structure) — 새로 · 도번 SD+8자리 · 409 · 저장 · 지운 뒤 번호 ·
|
||||
제목행 번호 매김(분류3 까지) · 집계표 저장 때 만들기/지우기 · 복사 가져오기 · 프로젝트 복사.
|
||||
번호 매김(새 항목 = 자리 뜻 없는 일련번호 · PLAN 26-1) · 집계표 저장 때 만들기/지우기 ·
|
||||
복사 가져오기 · 프로젝트 복사.
|
||||
면적산출(area) · 재료산출(material)은 따로 파일 —
|
||||
`resources/tester/spreadsheet/test_spreadsheet_store.py`."""
|
||||
|
||||
@@ -75,7 +76,7 @@ def test_새로_도번_자동_409_없는_열(client):
|
||||
"구조물도면",
|
||||
"structure",
|
||||
"pp_len",
|
||||
"SD01010100",
|
||||
"SD00000001",
|
||||
)
|
||||
# 도각 없는 전용 화면 — 그린 만큼이 설계 영역 · 기본 도면층 하나(없으면 CAD 가 못 엶)
|
||||
assert doc["도면"]["format"] == 6
|
||||
@@ -86,13 +87,13 @@ def test_새로_도번_자동_409_없는_열(client):
|
||||
for kind in ("area", "material"):
|
||||
assert client.get(f"/api/m02/templates/{kind}/pp_len").json()["문서"]["종류"] == "통합문서"
|
||||
|
||||
assert client.post("/api/m02/structures/rv").json()["문서"]["도번"] == "SD02010100"
|
||||
assert client.post("/api/m02/structures/rv").json()["문서"]["도번"] == "SD00000002"
|
||||
assert client.post("/api/m02/structures/pp_len").status_code == 409
|
||||
assert client.post("/api/m02/structures/없는열").status_code == 404
|
||||
assert client.post("/api/m02/structures/no").status_code == 404 # 글 열은 구조물 아님
|
||||
assert client.get("/api/m02/structures/numbers").json() == {
|
||||
"pp_len": _num("01010100"),
|
||||
"rv": _num("02010100"),
|
||||
"pp_len": _num("00000001"),
|
||||
"rv": _num("00000002"),
|
||||
}
|
||||
rows = client.get("/api/m02/templates").json()
|
||||
assert {(r["종류"], r["이름"]) for r in rows if r["종류"] == "structure"} == {
|
||||
@@ -118,7 +119,7 @@ def test_저장_뼈대_거름(client):
|
||||
"/api/m02/templates/structure/pp_len", json={"판": now, "문서": {**doc, "도번": "SD9"}}
|
||||
)
|
||||
got = client.get("/api/m02/templates/structure/pp_len").json()["문서"]
|
||||
assert got["도번"] == "SD01010100" # 번호는 서버 것 — 화면 값을 안 받음
|
||||
assert got["도번"] == "SD00000001" # 번호는 서버 것 — 화면 값을 안 받음
|
||||
|
||||
|
||||
def test_지워도_번호를_당기지_않음_짝은_같은_번호(client):
|
||||
@@ -126,15 +127,15 @@ def test_지워도_번호를_당기지_않음_짝은_같은_번호(client):
|
||||
client.post(f"/api/m02/structures/{column}")
|
||||
assert client.delete("/api/m02/templates/structure/rv").status_code == 200
|
||||
numbers = client.get("/api/m02/structures/numbers").json()
|
||||
assert numbers["rv"] == {**_num("02010100"), "도번": ""} # 면적 · 재료는 남아 번호를 쥠
|
||||
assert numbers["ms"] == _num("03010100") # 다른 열 번호는 그대로
|
||||
# 같은 종류(관공) 새 열 = 그 무리 다음 번호 · 다시 만든 도면은 짝(면적산출) 번호를 이어 받음
|
||||
assert client.post("/api/m02/structures/pp_cp").json()["문서"]["도번"] == "SD01010200"
|
||||
assert client.post("/api/m02/structures/rv").json()["문서"]["도번"] == "SD02010100"
|
||||
assert numbers["rv"] == {**_num("00000002"), "도번": ""} # 면적 · 재료는 남아 번호를 쥠
|
||||
assert numbers["ms"] == _num("00000003") # 다른 열 번호는 그대로
|
||||
# 새 열 = 다음 일련번호(자리 뜻 없음) · 다시 만든 도면은 짝(면적산출) 번호를 이어 받음
|
||||
assert client.post("/api/m02/structures/pp_cp").json()["문서"]["도번"] == "SD00000004"
|
||||
assert client.post("/api/m02/structures/rv").json()["문서"]["도번"] == "SD00000002"
|
||||
|
||||
|
||||
def test_제목행으로_번호_매김_한_번_옮김(client, tmp_path):
|
||||
"""옛 구-NN 파일 · 글 열 파일 → 새 번호(열 차례 = 종류 · 분류1 · 분류2 · 분류3 차례) ·
|
||||
def test_번호_없는_열은_열_차례로_일련번호_한_번_옮김(client, tmp_path):
|
||||
"""옛 구-NN 파일 · 글 열 파일 → 새 번호(번호 없는 열 = 열 차례 일련번호 · 자리 뜻 없음) ·
|
||||
내용 그대로."""
|
||||
for column in ("no", "rv", "pv_l"):
|
||||
doc = {"양식": "구조물도면", "종류": "structure", "판": 1, "열": column, "도번": "구-07"}
|
||||
@@ -146,16 +147,16 @@ def test_제목행으로_번호_매김_한_번_옮김(client, tmp_path):
|
||||
assert done["지움"] == ["structure/no"] # 글 열(NO · 측점 · 비고)은 구조물 아님
|
||||
numbers = client.get("/api/m02/structures/numbers").json()
|
||||
assert set(numbers) == set(store._column_ids()) and "sta" not in numbers
|
||||
expect = {
|
||||
"rv": "01010100", # 돌기슭막이 · (형태마다) · H=(높이마다) · 분류3 없음 = 00
|
||||
"c1": "01010200", # 같은 종류 · 분류1 · 다음 분류2
|
||||
"pv_b": "02010100", # 콘크리트포장 · T=(두께마다) · B
|
||||
"pv_l": "02010200",
|
||||
"pp_len": "03010100",
|
||||
"fb": "04010000", # 세월교 · (관경 × 련수마다) · 분류2 없음 = 00
|
||||
"be": "06000000", # 소단 — 분류 모두 없음
|
||||
"h_rip_c": "07010000", # 돌붙임 · 찰붙임
|
||||
"h_rip_m": "07020000", # 돌붙임 · 메붙임 — 분류1 다음 번호
|
||||
expect = { # 구조물 열 차례 그대로 1 부터(제목행 자리 뜻 없음)
|
||||
"rv": "00000001",
|
||||
"c1": "00000002",
|
||||
"pv_b": "00000003",
|
||||
"pv_l": "00000004",
|
||||
"pp_len": "00000007",
|
||||
"fb": "00000009",
|
||||
"be": "00000011",
|
||||
"h_rip_c": "00000012",
|
||||
"h_rip_m": "00000013",
|
||||
}
|
||||
for column, code in expect.items():
|
||||
assert numbers[column] == _num(code), column
|
||||
@@ -182,24 +183,26 @@ def test_집계표_저장_때_새_열은_만들고_지운_열은_같이_지움(c
|
||||
assert "pv_a" not in numbers and "note2" not in numbers
|
||||
for kind_ in ("structure", "area", "material"):
|
||||
assert client.get(f"/api/m02/templates/{kind_}/pv_a").status_code == 404
|
||||
assert numbers["pv_x"] == _num("02010500") # 지운 pv_a(03)는 비워 둠 · 무리 다음
|
||||
assert numbers["zz"] == _num("08000000") # 새 종류 = 종류 다음 번호
|
||||
assert numbers["pv_y"] == _num("02010101") # pv_b 무리 안 분류3 다음 번호
|
||||
# 새 열 = 열 차례로 다음 일련번호 · 지운 pv_a(5) 는 비워 둠(다른 번호를 안 당김)
|
||||
assert numbers["pv_x"] == _num("00000014")
|
||||
assert numbers["zz"] == _num("00000015")
|
||||
assert numbers["pv_y"] == _num("00000016")
|
||||
assert numbers["pv_b"] == _num("00000003") # 있던 번호는 그대로
|
||||
|
||||
|
||||
def test_지운_열_번호는_다음_저장에도_다시_안_줌(client):
|
||||
"""무리 맨 끝 열(pv_jt 02010400)을 지워 저장한 뒤 새 열을 더해도 04 를 다시 안 받음."""
|
||||
"""가장 큰 번호 열(h_rip_m 13)을 지워 저장한 뒤 새 열을 더해도 13 을 다시 안 받음."""
|
||||
store.renumber()
|
||||
url = "/api/m02/templates/table/구조물집계표"
|
||||
got = client.get(url).json()
|
||||
got["문서"]["열"] = [c for c in got["문서"]["열"] if c["id"] != "pv_jt"]
|
||||
got["문서"]["열"] = [c for c in got["문서"]["열"] if c["id"] != "h_rip_m"]
|
||||
assert client.put(url, json={"판": got["판"], "문서": got["문서"]}).status_code == 200
|
||||
assert client.get("/api/m02/structure-combos").json()["지운번호"] == ["02010400"]
|
||||
assert client.get("/api/m02/structure-combos").json()["지운번호"] == ["00000013"]
|
||||
got = client.get(url).json()
|
||||
new = {"id": "pv_z", "머리": ["콘크리트포장", "T=(두께마다)", "Z", None], "꼴": "수"}
|
||||
got["문서"]["열"].append(new)
|
||||
assert client.put(url, json={"판": got["판"], "문서": got["문서"]}).status_code == 200
|
||||
assert client.get("/api/m02/structures/numbers").json()["pv_z"] == _num("02010500")
|
||||
assert client.get("/api/m02/structures/numbers").json()["pv_z"] == _num("00000014")
|
||||
|
||||
|
||||
def test_다른_열_도면을_복사해_가져옴(client):
|
||||
@@ -211,8 +214,8 @@ def test_다른_열_도면을_복사해_가져옴(client):
|
||||
assert r.status_code == 200
|
||||
ms = client.get("/api/m02/templates/structure/ms").json()["문서"]
|
||||
assert ms["도면"]["entities"] == [{"type": "circle"}]
|
||||
assert (ms["도번"], ms["열"]) == ("SD05010100", "ms") # 번호 · 열은 받는 쪽 그대로
|
||||
assert client.get("/api/m02/templates/structure/rv").json()["문서"]["도번"] == "SD01010100"
|
||||
assert (ms["도번"], ms["열"]) == ("SD00000010", "ms") # 번호 · 열은 받는 쪽 그대로
|
||||
assert client.get("/api/m02/templates/structure/rv").json()["문서"]["도번"] == "SD00000001"
|
||||
bad = client.post("/api/m02/structures/ms/copy", params={"from": "없는열"})
|
||||
assert bad.status_code == 404
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Book as book
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_Template_Layers as layers
|
||||
from M02_MasterTemplete.M02_MasterTemplete_Router import router
|
||||
@@ -97,7 +98,7 @@ def test_집계표_저장_때_새_열_지운_열_고친_열을_맞춤_키_유지
|
||||
def test_SS_머리글은_키대장과_안_겹침():
|
||||
ledger = json.loads((ROOT / "resources/master_data/_키대장.json").read_text("utf-8"))
|
||||
assert "SS" not in ledger["다음"]
|
||||
assert store._COMBO_HEAD == "SS"
|
||||
assert book.HEAD == "SS" # 장부는 `M02_MasterTemplete_Book`(26-2a 나눔)
|
||||
|
||||
|
||||
def test_새_프로젝트_복사에_실림(client, tmp_path, monkeypatch):
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
"""M02 집계 항목 폴더 구조(PLAN 26-1 · 26-2a) — 경로 · 옛 자료 읽기 · 일련번호 · 나무 고침 · 권한.
|
||||
|
||||
① 옛 마스터(실제 `resources/master_template` · 읽기만) 51 항목이 경로로 읽힘 — 개수 · 이름(옛 종류 ·
|
||||
분류1~3 이음과 같음) · 키 차례.
|
||||
② 나무 고침은 임시 storage(회사 · 개인 층)와 견본 시스템 집계표로만 — 빈 층 첫 폴더 · 항목 · 깊이
|
||||
1 · 2 · 5 · 이름 바꾸기 · 옮기기 · 지우기(세 파일 · 입력 연결 같이) · 마지막 항목 · 판 409 ·
|
||||
422 · 404 · 권한 403 · 표 저장과 한 줄기(머리를 고치면 경로가 따라옴) · 번호 일련 · 안 되돌림.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from common_util.common_util_auth import verify_session
|
||||
from config import config_system
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Book as book
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Router_Items as items_router
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Router_Layers as layers_router
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Tree as tree
|
||||
from M02_MasterTemplete import M02_Template_Layers as layers
|
||||
from resources.tester.test_m02_structure import write_sample_table
|
||||
|
||||
TABLE = "table/구조물집계표"
|
||||
MASTER = Path(__file__).resolve().parents[2] / "resources" / "master_template"
|
||||
|
||||
|
||||
def _app(session: dict[str, Any]) -> TestClient:
|
||||
app = FastAPI()
|
||||
app.include_router(layers_router.router)
|
||||
app.include_router(items_router.router)
|
||||
app.dependency_overrides[verify_session] = lambda: session
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
# ── ① 옛 마스터 — 읽기만 ─────────────────────────────
|
||||
|
||||
|
||||
def test_옛_마스터_51_항목이_경로로_읽힘():
|
||||
raw = json.loads((MASTER / store.COMBO_FILE).read_bytes())["줄"]
|
||||
old = [
|
||||
" ".join(str(r[k]) for k in ("종류", "분류1", "분류2", "분류3") if r.get(k)) for r in raw
|
||||
]
|
||||
client = _app({"user_id": 1, "company_id": 1, "role": "USER", "is_master": False})
|
||||
rows = client.get("/api/m02/items").json()
|
||||
assert len(rows) == len(raw) == 51
|
||||
assert [r["key"] for r in rows] == [r["키"] for r in raw]
|
||||
assert [r["이름"] for r in rows] == old
|
||||
assert all(r["경로"] and r["이름"] == " ".join(r["경로"]) for r in rows)
|
||||
got = client.get("/api/m02/layers/system/tree").json()
|
||||
assert len(got["items"]) == 51 and got["판"]
|
||||
assert ["돌기슭막이"] in got["폴더"] and ["돌기슭막이", "메쌓기"] in got["폴더"]
|
||||
# 옛 줄(경로 없음)도 종류 · 분류1~3 에서 경로로(빈 분류 건너뜀)
|
||||
assert tree.row_path({"종류": "대피소", "분류1": None, "분류2": "", "분류3": "x"}) == [
|
||||
"대피소",
|
||||
"x",
|
||||
]
|
||||
|
||||
|
||||
def test_옮기기_함수는_두_번_돌려도_같음(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
|
||||
for kind in store.KINDS:
|
||||
(tmp_path / kind).mkdir()
|
||||
for name in ("table/구조물집계표.json", store.COMBO_FILE):
|
||||
(tmp_path / name).write_bytes((MASTER / name).read_bytes())
|
||||
monkeypatch.setattr(store, "FOLDER", tmp_path)
|
||||
before = json.loads((tmp_path / store.COMBO_FILE).read_bytes())["줄"]
|
||||
store.backfill_missing_structures() # 사본에 세 파일(번호) — 옮기기가 번호를 안 바꾸는지 봄
|
||||
assert book.migrate_paths() is True
|
||||
assert book.migrate_paths() is False
|
||||
after = json.loads((tmp_path / store.COMBO_FILE).read_bytes())["줄"]
|
||||
assert [r["키"] for r in after] == [r["키"] for r in before]
|
||||
assert all(r["경로"] == tree.row_path({**r, "경로": None}) for r in after)
|
||||
table = json.loads((tmp_path / "table/구조물집계표.json").read_bytes())
|
||||
assert table["층"] == ["종류", "분류1", "분류2", "분류3"] and table["폴더"] == []
|
||||
assert all(c["경로"] == tree.path_of(c) for c in table["열"] if tree.is_item(c))
|
||||
|
||||
|
||||
# ── ② 나무 고침 — 임시 storage ──────────────────────────
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def world(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> dict[str, Any]:
|
||||
system = tmp_path / "system"
|
||||
system.mkdir()
|
||||
write_sample_table(system)
|
||||
monkeypatch.setattr(store, "FOLDER", system)
|
||||
monkeypatch.setattr(layers, "SYSTEM_ROOT", system)
|
||||
store.renumber()
|
||||
storage = tmp_path / "storage"
|
||||
storage.mkdir()
|
||||
monkeypatch.setattr(config_system, "STORAGE_BASE_DIR", str(storage))
|
||||
session: dict[str, Any] = {"user_id": 42, "company_id": 7, "role": "USER", "is_master": False}
|
||||
return {"client": _app(session), "session": session, "storage": storage, "system": system}
|
||||
|
||||
|
||||
def _as(world: dict, role: str = "USER", user: int = 42) -> TestClient:
|
||||
world["session"].update(role=role, user_id=user)
|
||||
return world["client"]
|
||||
|
||||
|
||||
class Tree:
|
||||
"""한 층 나무를 판을 따라가며 고침."""
|
||||
|
||||
def __init__(self, client: TestClient, layer: str):
|
||||
self.client, self.layer = client, layer
|
||||
self.url = f"/api/m02/layers/{layer}/tree"
|
||||
self.now = client.get(self.url).json()
|
||||
|
||||
def do(self, want: int = 200, **body: Any) -> dict[str, Any]:
|
||||
res = self.client.post(self.url, json={"판": self.now["판"], **body})
|
||||
assert res.status_code == want, res.text
|
||||
if want == 200:
|
||||
self.now = res.json()
|
||||
assert self.now == {**self.client.get(self.url).json(), "알림": self.now["알림"]}
|
||||
return res.json()
|
||||
|
||||
def item(self, path: list[str]) -> dict[str, Any]:
|
||||
return next(r for r in self.now["items"] if r["경로"] == path)
|
||||
|
||||
def paths(self) -> list[list[str]]:
|
||||
return [r["경로"] for r in self.now["items"]]
|
||||
|
||||
|
||||
def _numbers(root: Path, cid: str) -> set[str]:
|
||||
out = set()
|
||||
for kind, key, _, _ in store.PARTS:
|
||||
doc = json.loads((root / kind / f"{cid}.json").read_bytes())
|
||||
out.add(doc[key][2:])
|
||||
return out
|
||||
|
||||
|
||||
def test_빈_층에서_폴더_항목_깊이_1_2_5_이름_옮기기_지우기(world):
|
||||
t = Tree(_as(world), "personal")
|
||||
root = world["storage"] / "7" / "42" / "templates"
|
||||
assert (t.now["폴더"], t.now["items"]) == ([], []) # 빈 층
|
||||
t.do(op="add-folder", 경로=[], 이름="돌쌓기")
|
||||
assert t.now["폴더"] == [["돌쌓기"]] and t.now["items"] == []
|
||||
t.do(op="add-item", 경로=[], 이름="소단", 단위="m") # 깊이 1
|
||||
t.do(op="add-item", 경로=["돌쌓기"], 이름="찰쌓기") # 깊이 2
|
||||
t.do(op="add-item", 경로=["a", "b", "c", "d"], 이름="e") # 깊이 5(폴더는 저절로)
|
||||
assert t.paths() == [["소단"], ["돌쌓기", "찰쌓기"], ["a", "b", "c", "d", "e"]]
|
||||
deep = t.item(["a", "b", "c", "d", "e"])
|
||||
assert deep["이름"] == "a b c d e"
|
||||
assert (deep["종류"], deep["분류1"], deep["분류2"], deep["분류3"]) == ("a", "b", "c", "d")
|
||||
assert t.item(["소단"])["단위"] == "m"
|
||||
table = json.loads((root / "table/구조물집계표.json").read_bytes())
|
||||
assert table["층"] == ["종류", "분류1", "분류2", "분류3", "분류4"] # 가장 깊은 경로에 맞춤
|
||||
assert all(len(c["머리"]) == 5 for c in table["열"])
|
||||
# 번호 = 자리 뜻 없는 일련번호 · 세 파일 같은 번호
|
||||
codes = [_numbers(root, t.item(p)["열"]) for p in t.paths()]
|
||||
assert codes == [{"00000001"}, {"00000002"}, {"00000003"}]
|
||||
assert ["a", "b", "c", "d"] in t.now["폴더"] and ["돌쌓기"] in t.now["폴더"]
|
||||
# 겹침 409 · 모양 422 · 없는 것 404
|
||||
t.do(409, op="add-item", 경로=["돌쌓기"], 이름="찰쌓기")
|
||||
t.do(409, op="add-folder", 경로=[], 이름="a")
|
||||
for bad in (
|
||||
{"op": "grow"},
|
||||
{"op": "add-item", "이름": " "},
|
||||
{"op": "add-item", "이름": "x" * 61},
|
||||
):
|
||||
t.do(422, **bad)
|
||||
t.do(404, op="rename", 키="SS999999", 이름="x")
|
||||
t.do(404, op="delete", 경로=["없는폴더"])
|
||||
# 이름 바꾸기 — 항목(키 · 열 · 번호 그대로) · 폴더(아래 항목 모두)
|
||||
key = t.item(["돌쌓기", "찰쌓기"])["key"]
|
||||
t.do(op="rename", 키=key, 이름="메쌓기")
|
||||
moved = t.item(["돌쌓기", "메쌓기"])
|
||||
assert moved["key"] == key and _numbers(root, moved["열"]) == {"00000002"}
|
||||
t.do(op="rename", 경로=["a"], 이름="A")
|
||||
assert ["A", "b", "c", "d", "e"] in t.paths() and ["a"] not in t.now["폴더"]
|
||||
t.do(409, op="rename", 경로=["A"], 이름="돌쌓기") # 있는 폴더로 합치기 막음
|
||||
# 옮기기 — 항목 · 폴더 · 제 안으로는 422
|
||||
t.do(op="move", 키=t.item(["소단"])["key"], 새경로=["돌쌓기"])
|
||||
t.do(op="move", 경로=["A", "b"], 새경로=["돌쌓기"])
|
||||
assert t.paths() == [["돌쌓기", "메쌓기"], ["돌쌓기", "소단"], ["돌쌓기", "b", "c", "d", "e"]]
|
||||
t.do(422, op="move", 경로=["돌쌓기"], 새경로=["돌쌓기", "b"])
|
||||
# 지우기 — 항목의 세 파일 · 입력 연결 파일도 같이 · 마지막 항목까지
|
||||
gone = t.item(["돌쌓기", "소단"])
|
||||
(root / "inputs").mkdir(exist_ok=True)
|
||||
(root / "inputs" / f"{gone['열']}.json").write_text('{"판": 1, "줄": []}', encoding="utf-8")
|
||||
note = t.do(op="delete", 키=gone["key"])["알림"]
|
||||
assert note["지운항목"] == [gone["열"]]
|
||||
for part in ("structure", "area", "material", "inputs"):
|
||||
assert not (root / part / f"{gone['열']}.json").is_file()
|
||||
t.do(op="add-item", 경로=["돌쌓기"], 이름="새것") # 지운 번호는 다시 안 줌
|
||||
assert _numbers(root, t.item(["돌쌓기", "새것"])["열"]) == {"00000004"}
|
||||
t.do(op="delete", 경로=["돌쌓기", "b"]) # 깊은 항목이 빠지면 머리 줄 넷으로
|
||||
table = json.loads((root / "table/구조물집계표.json").read_bytes())
|
||||
assert table["층"] == ["종류", "분류1", "분류2", "분류3"]
|
||||
t.do(op="delete", 경로=["돌쌓기"]) # 마지막 항목들까지
|
||||
assert t.now["items"] == [] and t.now["폴더"] == []
|
||||
assert json.loads((root / "table/구조물집계표.json").read_bytes())["열"] == []
|
||||
|
||||
|
||||
def test_판_409_권한_403(world):
|
||||
admin = Tree(_as(world, "ADMIN"), "company")
|
||||
stale = admin.now["판"]
|
||||
admin.do(op="add-folder", 경로=[], 이름="회사폴더")
|
||||
res = admin.client.post(admin.url, json={"판": stale, "op": "add-folder", "이름": "x"})
|
||||
assert res.status_code == 409 and res.json()["detail"]["판"] == admin.now["판"]
|
||||
user = _as(world) # 같은 회사 일반 사용자 — 읽기 200 · 고침 403
|
||||
assert user.get("/api/m02/layers/company/tree").json()["폴더"] == [["회사폴더"]]
|
||||
body = {"판": admin.now["판"], "op": "add-folder", "이름": "y"}
|
||||
assert user.post("/api/m02/layers/company/tree", json=body).status_code == 403
|
||||
assert user.get("/api/m02/layers/personal/tree?owner=7/9").status_code == 403 # 남의 개인
|
||||
system = user.get("/api/m02/layers/system/tree").json()
|
||||
body = {"판": system["판"], "op": "add-folder", "이름": "y"}
|
||||
assert user.post("/api/m02/layers/system/tree", json=body).status_code == 403
|
||||
sysadmin = Tree(_as(world, "SYSTEM_ADMIN"), "system")
|
||||
sysadmin.do(op="add-item", 경로=["돌붙임"], 이름="줄붙임")
|
||||
assert ["돌붙임", "줄붙임"] in sysadmin.paths()
|
||||
|
||||
|
||||
def test_표_저장과_나무가_한_줄기(world):
|
||||
"""집계표 표에서 머리를 고치면 경로가 따라오고, 나무 고침 뒤 표에도 같은 자료가 보임."""
|
||||
admin = _as(world, "SYSTEM_ADMIN")
|
||||
t = Tree(admin, "system")
|
||||
rv = next(r for r in t.now["items"] if r["열"] == "rv")
|
||||
url = f"/api/m02/layers/system/templates/{TABLE}"
|
||||
got = admin.get(url).json()
|
||||
col = next(c for c in got["문서"]["열"] if c["id"] == "rv")
|
||||
col["머리"] = ["돌기슭막이", None, "찰쌓기", None] # 빈 칸은 건너뜀
|
||||
assert admin.put(url, json=got).status_code == 200
|
||||
t = Tree(admin, "system")
|
||||
assert t.item(["돌기슭막이", "찰쌓기"])["key"] == rv["key"]
|
||||
t.do(op="rename", 키=rv["key"], 이름="메쌓기")
|
||||
table = admin.get(url).json()["문서"]
|
||||
col = next(c for c in table["열"] if c["id"] == "rv")
|
||||
assert col["경로"] == ["돌기슭막이", "메쌓기"] and col["머리"] == [
|
||||
"돌기슭막이",
|
||||
"메쌓기",
|
||||
None,
|
||||
None,
|
||||
]
|
||||
# 표에서 열을 지우면 나무에서도 빠지고 파일도 같이
|
||||
table["열"] = [c for c in table["열"] if c["id"] != "rv"]
|
||||
assert (
|
||||
admin.put(url, json={"판": admin.get(url).json()["판"], "문서": table}).status_code == 200
|
||||
)
|
||||
assert "rv" not in [r["열"] for r in Tree(admin, "system").now["items"]]
|
||||
assert not (world["system"] / "structure" / "rv.json").is_file()
|
||||
|
||||
|
||||
def test_프로젝트에서_새_항목도_경로_그대로(world):
|
||||
"""개인에 저장 · 프로젝트에서 새 항목(`M02_Item_Personal`)도 경로를 넷에 접지 않고 그대로 ·
|
||||
같은 경로면 「이름 (2)」."""
|
||||
from M02_MasterTemplete import M02_Item_Personal as personal
|
||||
|
||||
folder = layers.personal_dir(7, 42)
|
||||
made = personal.create_item(folder, ["가", "나", "다", "라"], "마", "m")
|
||||
again = personal.create_item(folder, ["가", "나", "다", "라"], "마", "m")
|
||||
t = Tree(_as(world), "personal")
|
||||
assert t.item(["가", "나", "다", "라", "마"])["key"] == made["key"]
|
||||
assert t.item(["가", "나", "다", "라", "마 (2)"])["key"] == again["key"]
|
||||
assert made["이름"] == "가 나 다 라 마"
|
||||
Reference in New Issue
Block a user