Merge remote-tracking branches 'origin/sub_laptop_1' and 'origin/sub_laptop_2' into main_laptop_1
This commit is contained in:
@@ -67,6 +67,19 @@ def _words(term) -> tuple[list[str], list[str]]:
|
||||
|
||||
|
||||
def search(master, cond: dict, env: dict | None = None, unit: str | None = None) -> list[str]:
|
||||
"""`_search` 를 마스터 색인 하나에 조건마다 한 번만(792-2) —
|
||||
답은 마스터 · 조건 · 지역 · 단위만 따름."""
|
||||
place = mm.plain(env[mm.REGION]) if (env or {}).get(mm.REGION) else ""
|
||||
memo = getattr(master, "_found", None)
|
||||
if memo is None:
|
||||
memo = master._found = {}
|
||||
key = tuple(str(cond.get(k) or "") for k in ("검색어", "구분", "상세구분")) + (place, str(unit))
|
||||
if key not in memo:
|
||||
memo[key] = _search(master, cond, env, unit)
|
||||
return list(memo[key])
|
||||
|
||||
|
||||
def _search(master, cond: dict, env: dict | None = None, unit: str | None = None) -> list[str]:
|
||||
"""검색어 안 자재품목 키 — 낱말이 모두 이름 또는 규격에 든 줄(요소의 구분 · 상세구분 안에서) ·
|
||||
단위는 같은 성격까지(하나도 없으면 다 — 경고가 붙음) · 고르는 차례:
|
||||
값 있는 줄 → 줄 단위와 똑같은 단위 → 지역 낱말(입력 `자재지역` 포함)과 맞는 줄 → 키 차례."""
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
||||
"""792-2 시트 다시 셈 서버 다듬기 — 자재 색인 · 찾기 답을 M01 색인에 한 번만.
|
||||
|
||||
- 셈마다 새 `_Over` 덮개를 지어도 자재 색인(`_market` · `_by_class`) · 찾기 답(`_found`)은 속 M01
|
||||
색인에 붙음 — 두 번째 셈은 다시 안 지음
|
||||
- M01 파일 자국이 바뀌면 새 색인 = 새로 지음(지문 열쇠 = `M01_MasterData_Store_Cache.loaded`)
|
||||
- 층 섞임 없음 — 기억은 M01 자재품목 키만 · 개인 자재(PM)는 그때 층에서만 찾음
|
||||
- 셈 답 같음 — 기억한 찾기 = 날 찾기 · SK 금액 견본(합판거푸집 51,419 · 공구손료 2 % 51,802)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
from decimal import Decimal
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
from M01_MasterData import M01_MasterData_Store_Cache as cache
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_Material_Own as own
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
||||
from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층
|
||||
|
||||
mm = m01.cm.mf.mp.mm
|
||||
mp = m01.cm.mf.mp
|
||||
F211 = {
|
||||
"자재지역": "포항",
|
||||
"유형": "보통",
|
||||
"사용횟수": "6회",
|
||||
"높이감률": 0,
|
||||
"지붕슬래브": "아님",
|
||||
"폼타이": "아님",
|
||||
"폼타이규격": "12.7㎜",
|
||||
"측압": "3t/㎡",
|
||||
"구멍땜": 0,
|
||||
}
|
||||
|
||||
|
||||
def _sk(**more):
|
||||
doc = {"키": "SK-000001", "이름": "속도", "단위": "식", "바탕": {"로직": "GC000211"}}
|
||||
return {**doc, "입력값": F211, **more}
|
||||
|
||||
|
||||
def test_색인은_M01_색인에_한_번_두번째_셈은_안_지음(monkeypatch):
|
||||
first = calc.calc_doc(_sk())
|
||||
whole = m01.loaded()[1]
|
||||
held = {n: vars(whole)[n] for n in ("_market", "_found")}
|
||||
fresh = mp._search
|
||||
found = []
|
||||
monkeypatch.setattr(mp, "_search", lambda *a: found.append(a) or fresh(*a))
|
||||
again = calc.calc_doc(_sk())
|
||||
assert again["기준"] == first["기준"] and first["기준"]["계"] == 51419
|
||||
assert all(vars(whole)[n] is v for n, v in held.items())
|
||||
assert found == [] # 같은 조건 찾기 = 기억한 답(자재품목 3만 줄을 안 훑음)
|
||||
over = calc._Over(whole, "SK-000001", {})
|
||||
assert over._market is whole._market and "_market" not in vars(over)
|
||||
over.mine = 1 # 그 밖 칸은 덮개에만
|
||||
assert "mine" not in vars(whole)
|
||||
|
||||
|
||||
def test_M01_파일이_바뀌면_새_색인_새로_지음(tmp_path):
|
||||
def write(rows):
|
||||
path = tmp_path / "자재품목.json"
|
||||
path.write_text(json.dumps({"그룹": "재료", "줄": rows}, ensure_ascii=False), "utf-8")
|
||||
return path
|
||||
|
||||
one = {"키": "MT000001", "이름": "합판", "규격": "12mm", "단위": "장", "물가자료": 100}
|
||||
two = {**one, "키": "MT000002", "이름": "각재"}
|
||||
try:
|
||||
write([one])
|
||||
old = cache.loaded(tmp_path)[1]
|
||||
assert [k for *_, k in mm._market_index(old)] == ["MT000001"]
|
||||
path = write([one, two])
|
||||
os.utime(path, ns=(1, 1)) # 자국(시각 · 크기)이 꼭 바뀌게
|
||||
new = cache.loaded(tmp_path)[1]
|
||||
assert new is not old and not hasattr(new, "_market")
|
||||
assert [k for *_, k in mm._market_index(new)] == ["MT000001", "MT000002"]
|
||||
assert cache.loaded(tmp_path)[1] is new # 안 바뀌면 그대로
|
||||
finally:
|
||||
cache.forget() # 다음 시험은 M01 폴더를 다시 읽음
|
||||
|
||||
|
||||
def test_기억한_찾기_답은_날_찾기와_같고_사본을_줌():
|
||||
whole = m01.loaded()[1]
|
||||
conds = [
|
||||
({"검색어": "합판 12mm", "구분": "목재"}, {}, "㎡"),
|
||||
({"검색어": "철선"}, {mm.REGION: "포항"}, "kg"),
|
||||
({"검색어": "레미콘"}, {mm.REGION: "서울"}, None),
|
||||
]
|
||||
for cond, env, unit in conds:
|
||||
got = mp.search(whole, cond, env, unit)
|
||||
assert got == mp._search(whole, cond, env, unit)
|
||||
got.append("MT999999")
|
||||
assert "MT999999" not in mp.search(whole, cond, env, unit)
|
||||
assert mp.search(whole, conds[1][0], {mm.REGION: "서울"}, "kg") == mp._search(
|
||||
whole, conds[1][0], {mm.REGION: "서울"}, "kg"
|
||||
)
|
||||
|
||||
|
||||
def test_층_섞임_없음_개인_자재는_그때_층에서만(world):
|
||||
calc.calc_doc(_sk()) # M01 색인에 기억이 생긴 뒤
|
||||
whole = m01.loaded()[1]
|
||||
assert all(k.startswith(mm.MARKET) for *_, k in whole._market)
|
||||
assert all(k.startswith(mm.MARKET) for keys in whole._found.values() for k in keys)
|
||||
with store.at(world["personal"]):
|
||||
code = own.add(world["personal"], {"이름": "내 자재", "단위": "개", "값": 1234})["키"]
|
||||
mine = labor.material_price(whole, code, "개")
|
||||
other = world["personal"].parent.parent / "43" / world["personal"].name
|
||||
other.mkdir(parents=True)
|
||||
with store.at(other):
|
||||
theirs = labor.material_price(whole, code, "개")
|
||||
assert Decimal(str(mine["기준단가"])) == 1234
|
||||
assert theirs["기준단가"] is None
|
||||
assert code not in whole.index.get(code[:2], {})
|
||||
|
||||
|
||||
def test_상수_덮개_셈_답_전후_같음():
|
||||
got = calc.calc_doc(_sk(상수덮개={"pct:공구손료 및 경장비#0": {"0": 2}}))
|
||||
assert got["기준"]["계"] == 51802
|
||||
assert got["원래"]["계"] == 51419
|
||||
assert got["시트"]["자리"]["pct:공구손료 및 경장비#0"]["덮개"] == ["상수"]
|
||||
@@ -66,6 +66,10 @@ def test_단산_블록_가져온_식_만들기_다시씀_지움(client):
|
||||
with store.at(folder):
|
||||
doc = store.read("sangeun", got["이름"])["문서"]
|
||||
assert doc["바탕"] is None and doc["가져온"]["원본"]["엑셀금액"]["계"] == 30369
|
||||
ten = _open(client, 10).json() # 야면석소운반 — 793-3a 근거 제안 = SK 가져온.근거후보
|
||||
with store.at(folder):
|
||||
cand = store.read("sangeun", ten["이름"])["문서"]["가져온"]["근거후보"]
|
||||
assert cand and cand[0]["기초"] == "GC001140" and cand[0]["단"] in ("확정", "근접", "사람")
|
||||
assert _open(client, 99999).status_code == 404
|
||||
fh._path(folder, KEY).unlink() # 산근 없는 옛 HP = 다시 올림
|
||||
old = _open(client)
|
||||
@@ -188,3 +192,24 @@ def test_우리_기초로_바꾸기_환산_입력_필요(tmp_path):
|
||||
"필요" not in got and round(got["후"]["계"]) == 94227 and got["문서"]["환산"] == {"계수": 1}
|
||||
)
|
||||
assert got["문서"]["바탕"] == {"로직": "GC001140"} and got["문서"][imp.KEY]["바꿈"]["전"]
|
||||
|
||||
|
||||
def test_이_줄에_잇기_옮기기_SK_참조(client):
|
||||
"""[이 줄에 잇기] 고른 것 {갈래 단가산출, 키 SK}(층 · 주인 없음) → 옮기기 = 그 SK 참조 줄."""
|
||||
from M02_MasterTemplete import M02_Hopyo_Judge as judge
|
||||
|
||||
client.app.include_router(sangeun_router.router)
|
||||
assert _post(client).status_code == 200
|
||||
folder = layers.personal_dir(7, 42)
|
||||
judge.solve_all(folder, KEY)
|
||||
sk = _open(client, 10).json()["이름"]
|
||||
pick = {"갈래": "단가산출", "키": sk, "이름": "야면석소운반", "단위": "㎡"} # 화면 skPick 꼴
|
||||
pick |= {"입력값": {}, "출처": {}, "몫": None, "필요": [], "표지": ["가져온 식 있음"]}
|
||||
body = {"층": "personal", "호표": {"2": {"3": [pick]}}, "블록": {}}
|
||||
got = client.post(f"{API}/hopyo/{KEY}/move", json=body)
|
||||
assert got.status_code == 200, got.text
|
||||
sc = got.json()["만듦"][0]["키"]
|
||||
with store.at(folder):
|
||||
lines = store.read("precombo", sc)["문서"]["담은로직"]
|
||||
refs = [x["참조"]["키"] for x in lines if x.get("참조")]
|
||||
assert got.json()["막힘"] == [] and refs == [sk], got.json()["막힘"]
|
||||
|
||||
Reference in New Issue
Block a user