Merge remote-tracking branch 'origin/sub_laptop_4' into main_laptop_1
This commit is contained in:
@@ -13,6 +13,9 @@
|
||||
· 품 다름 ·
|
||||
품 대조 못 함 · 까닭 표지) · 사람(후보 2~3) · 없음(M01 없음 의심 → `보강` = 794-2 근거).
|
||||
자동 바꿈 없음 · 지어 넣는 값 없음(못 읽은 칸 · 글에 없는 감률 · 할증 = 입력필요).
|
||||
794-2c — 같은 말 묶음(SYN) · 옛 번호 → 현행 절(`old_cands` · `M02_San_Basis_Map.OLD`) 번호 후보 ·
|
||||
Q 식 절은 줄 글에서 기호 끝값을 더 읽고(`_fill`) 값 · 제목 길이 품 검산으로 안 받쳐지면 그 기계
|
||||
「작업량당 기계경비」 로직(`by_machine` · 차례 「기계」).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -27,6 +30,7 @@ from M02_MasterTemplete import M02_Hopyo_Solve as solve
|
||||
from M02_MasterTemplete import M02_San_Basis_Check as chk
|
||||
from M02_MasterTemplete import M02_San_Basis_Const as bc
|
||||
from M02_MasterTemplete import M02_San_Basis_Index as bi
|
||||
from M02_MasterTemplete import M02_San_Basis_Map as bm
|
||||
from M02_MasterTemplete import M02_San_Basis_Value as bv
|
||||
|
||||
TITLE_MIN, TITLE_GAP, TITLE_TOP = 0.35, 0.15, 5
|
||||
@@ -48,6 +52,9 @@ SYN = [
|
||||
("콤펙터", "콤팩터"),
|
||||
("레미콘", "레디믹스트콘크리트"),
|
||||
("컨테이너", "콘테이너"),
|
||||
("휠라", "채움재"),
|
||||
("평떼", "평잔디"),
|
||||
("육샹", "육상"),
|
||||
]
|
||||
ORDER = ["확정·전부", "확정·일부", "확정·일부(절 미확정)", "후보 2~3", "없음"]
|
||||
NONE = {"확정": [], "동점": [], "약함": []}
|
||||
@@ -64,6 +71,7 @@ class Ctx:
|
||||
**self.m.index.get("GX", {}),
|
||||
}
|
||||
self.grams = {k: grams(r.get("이름")) for k, r in self.logic.items()}
|
||||
self.mach = bm.machines(self.logic)
|
||||
|
||||
|
||||
_CTX: list = [None, None]
|
||||
@@ -77,11 +85,15 @@ def ctx() -> Ctx:
|
||||
|
||||
|
||||
# ── 제목 → 로직 이름 후보 ───────────────────────────────────────────
|
||||
def grams(text: Any) -> set:
|
||||
def syn(text: Any) -> str:
|
||||
t = str(text or "")
|
||||
for a, b in SYN:
|
||||
t = re.sub(re.escape(a), b, t, flags=re.I)
|
||||
return clause._grams(re.sub(r"\d", " ", t))
|
||||
return t
|
||||
|
||||
|
||||
def grams(text: Any) -> set:
|
||||
return clause._grams(re.sub(r"\d", " ", syn(text)))
|
||||
|
||||
|
||||
def title_cands(c: Ctx, text: str, unit: Any) -> list[tuple[str, float]]:
|
||||
@@ -123,9 +135,12 @@ def parts_of(blk: dict) -> tuple[list[dict], dict, list]:
|
||||
paths = {id(n): p for p, n in clause._paths(nodes)}
|
||||
items = blk.get("항목") or []
|
||||
usable = solve._usable(items)
|
||||
lines = [str(t) for t in blk.get("글") or []]
|
||||
whole = {k: top[k] + solve._gather(nodes, k) for k in ("상수", "식", "선택값")}
|
||||
_fill(whole, lines)
|
||||
if len(works) < 2:
|
||||
one = {"제목": "(블록 통째)", "번": None, "길": None, "항목": usable, **whole}
|
||||
one = {"제목": "(블록 통째)", "번": None, "길": None, "항목": usable, "글줄": lines}
|
||||
one |= whole
|
||||
one["몫"] = works[0].get("몫") if works else None
|
||||
return [one], whole, usable
|
||||
out = []
|
||||
@@ -136,8 +151,10 @@ def parts_of(blk: dict) -> tuple[list[dict], dict, list]:
|
||||
"번": w["번"],
|
||||
"길": paths.get(id(w)),
|
||||
"항목": [items[i] for i in at],
|
||||
"글줄": lines[w["글"][0] : _end(w) + 1] if w.get("글") else [],
|
||||
}
|
||||
part |= {k: top[k] + solve._gather([w], k) for k in ("상수", "식", "선택값")}
|
||||
_fill(part, part["글줄"])
|
||||
part["몫"] = w.get("몫")
|
||||
out.append(part)
|
||||
if not any(p["항목"] for p in out): # STmate — 글행 없음 → 항목 절 이름으로 붙임
|
||||
@@ -156,6 +173,43 @@ def parts_of(blk: dict) -> tuple[list[dict], dict, list]:
|
||||
return out, whole, usable
|
||||
|
||||
|
||||
def _end(node: dict) -> int:
|
||||
return max([node["글"][1], *(_end(n) for n in node.get("아래") or [] if n.get("글"))])
|
||||
|
||||
|
||||
def _fill(part: dict, lines: list[str]) -> None:
|
||||
"""기계 작업량 절(Q 식 줄) — 못 읽은 기호(q · K · f · E · Cm)를 줄 글 끝값으로 더함 · 고르는
|
||||
차례(`is_machine`)는 채우기 전 상수로."""
|
||||
if bm.has_q(lines):
|
||||
part["기계식"] = is_machine(part)
|
||||
have = {chk.base_sym(x.get("기호") or "") for x in part["상수"]}
|
||||
part["상수"] = part["상수"] + [
|
||||
x for x in bm.consts(lines) if chk.base_sym(x["기호"]) not in have
|
||||
]
|
||||
|
||||
|
||||
def old_cands(c: Ctx, basis: dict, title: str, unit: Any) -> list[dict]:
|
||||
"""옛 번호 → 현행 절 로직(`M02_San_Basis_Map`) · 건설품셈(옛) 같은 번호는 같은 절 · 제목과 뜻
|
||||
겹침만 · 결과단위 성격 거름."""
|
||||
mine, have, out = clause.nature(unit), {x["키"] for x in basis["후보"]}, []
|
||||
for book, no in basis["출처"]:
|
||||
if not book.endswith(clause.OLD):
|
||||
continue
|
||||
rows = bm.old_rows(book, no)
|
||||
if not rows and book == "건설품셈" + clause.OLD:
|
||||
rows = [
|
||||
o
|
||||
for o in clause.lookup("건설품셈", no)
|
||||
if o["맞음"] == "같음" and dice(c, o["키"], title) > 0
|
||||
]
|
||||
for o in rows:
|
||||
theirs = clause.nature(o["결과단위"])
|
||||
if o["키"] not in have and not (mine and theirs and mine != theirs):
|
||||
have.add(o["키"])
|
||||
out.append({**o, "절": None})
|
||||
return out
|
||||
|
||||
|
||||
def value_ev(rec: dict | None, parts: list[dict]) -> tuple[dict, list[dict], dict]:
|
||||
"""값 길 — 블록 {확정, 동점, 약함} · 절마다 · 로직별 옵션."""
|
||||
if rec is None:
|
||||
@@ -248,6 +302,22 @@ def resolve(S: list, how: str, check) -> tuple[list, str]:
|
||||
return S, how
|
||||
|
||||
|
||||
def by_machine(c: Ctx, p: dict, S: list, how: str, title: str, check) -> tuple[list, str]:
|
||||
"""기계 작업량 절(Q 식) — 번호 길이 아니고 고른 기초가 품 검산으로 안 받쳐지면 그 기계
|
||||
「작업량당 기계경비」 로직(품 같음 · 또는 고른 것 없고 품 짝 있음)."""
|
||||
if how.startswith("번호") or not bm.has_q(p["글줄"]) or not is_machine(p):
|
||||
return S, how
|
||||
names = " ".join(str(i.get("이름") or "") for i in p["항목"])
|
||||
mk = bm.machine_key(c.mach, syn(f"{p['제목']} {title} {names}"))
|
||||
if not mk or mk in S or (len(S) == 1 and _good(check(S[0]))):
|
||||
return S, how
|
||||
v = check(mk)
|
||||
cnt = v.get("셈") or {}
|
||||
if _good(v) or ("오류" not in v and (cnt.get(solve.SAME) or cnt.get(solve.DIFF))):
|
||||
return [mk], "기계"
|
||||
return S, how
|
||||
|
||||
|
||||
def kind_of(S: list) -> str:
|
||||
return "확정" if len(S) == 1 else "후보 2~3" if 2 <= len(S) <= 3 else "없음"
|
||||
|
||||
@@ -276,6 +346,7 @@ def run_block(c: Ctx, blk: dict, line: dict | None, same: list, rec: dict | None
|
||||
basis = clause.basis(blk)
|
||||
parts, whole, usable = parts_of(blk)
|
||||
title_b = f"{blk.get('이름') or ''} {blk.get('규격') or ''}".strip()
|
||||
basis["후보"] += old_cands(c, basis, title_b, blk.get("단위"))
|
||||
src = {"줄": line, "같은줄": same, "블록": blk}
|
||||
src["제목줄"] = {
|
||||
"글": re.sub(r"[-_/()\[\],~]+", " ", f"{blk.get('이름') or ''} {blk.get('규격') or ''}"),
|
||||
@@ -306,11 +377,14 @@ def run_block(c: Ctx, blk: dict, line: dict | None, same: list, rec: dict | None
|
||||
text = f"{p['제목']} {title_b}"
|
||||
else:
|
||||
N, text = allN, title_b
|
||||
N, mach = list(dict.fromkeys(N)), is_machine(p)
|
||||
N, mach = list(dict.fromkeys(N)), p.get("기계식", is_machine(p))
|
||||
T = title_cands(c, text, blk.get("단위"))
|
||||
S, how = pick(c, N, T, Vb, Vs, mach, len(parts) == 1, text)
|
||||
cons = {x: p[x] for x in ("상수", "식", "선택값")}
|
||||
S, how = resolve(S, how, lambda k, p=p, cons=cons: check(k, p["항목"], cons, share_of(p)))
|
||||
S, how = by_machine(
|
||||
c, p, S, how, title_b, lambda k, p=p, cons=cons: check(k, p["항목"], cons, share_of(p))
|
||||
)
|
||||
secs.append(
|
||||
{
|
||||
"절": p["제목"],
|
||||
|
||||
@@ -20,6 +20,7 @@ from __future__ import annotations
|
||||
import itertools
|
||||
import math
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Bind as hb
|
||||
@@ -113,13 +114,34 @@ def _conv(v: Any) -> Any:
|
||||
return v
|
||||
|
||||
|
||||
_RANGE = re.compile(r"^\s*([\d.]+)\s*(?:[∼~]\s*([\d.]+))?\s*$")
|
||||
_SIZE = re.compile(r"용량|규격|톤|출력")
|
||||
|
||||
|
||||
def _ranges(opts: list, base: dict) -> list:
|
||||
"""고르기가 모두 범위(「0.6∼0.8」 · 수 하나)면 이미 든 용량 · 규격 값이 드는 범위만(794-2c ·
|
||||
버킷용량 → 규격구분) · 하나로 안 좁혀지면 그대로."""
|
||||
spans = [_RANGE.match(str(o)) for o in opts]
|
||||
if not all(spans):
|
||||
return opts
|
||||
nums = [
|
||||
float(v)
|
||||
for n, v in base.items()
|
||||
if _SIZE.search(n) and isinstance(v, (int, float, Decimal))
|
||||
]
|
||||
hit = [
|
||||
o for o, m in zip(opts, spans) if any(float(m[1]) <= x <= float(m[2] or m[1]) for x in nums)
|
||||
]
|
||||
return hit if len(hit) == 1 else opts
|
||||
|
||||
|
||||
def probe(
|
||||
m: Any, key: str, specs: dict, hard: list, base: dict, items: list, share: Any
|
||||
) -> dict | None:
|
||||
"""못 읽은 고르기 칸을 고르기 값마다 넣어 엑셀 품과 맞댐 — 「검산」 · 「근접」 옵션."""
|
||||
if not hard or not all(specs.get(n, {}).get("고르기") for n in hard):
|
||||
return None
|
||||
opts = [specs[n]["고르기"] for n in hard]
|
||||
opts = [_ranges(specs[n]["고르기"], base) for n in hard]
|
||||
if math.prod(len(o) for o in opts) > 60:
|
||||
return None
|
||||
exact, close = [], []
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
"""근거 제안 5 — 옛 번호 대조표 · 기계 작업량 읽기(PLAN 794-2c · 요구 653 · 분석 652-794-2a 3 장) ·
|
||||
읽기만 · 지어 넣는 값 없음.
|
||||
|
||||
옛 번호 → 현행 절 = 대조표 `OLD`(근거 = 원문 표 값 · 실무 글) › 사방품셈(옛) = 지식DB 판 대응표
|
||||
「2018 통합 산림사업 표준품셈」 확정 한 대응 › 건설품셈(옛) 같은 번호 = 현행 절 로직 이름과 블록
|
||||
제목(같은 말 묶음 뒤)이 뜻 낱말을 나눌 때만(엔진 몫).
|
||||
기계 작업량 절 = 글에 「3600 … Cm」 Q 식 · 기호(q · K · f · E · Cm) 끝값을 줄 글에서 읽음(여러 대입
|
||||
한 줄 · 「q (버킷용량) = 0.7」 꼴) · 기계 낱말 → 그 기계 「작업량당 기계경비」 로직(이름에서 뽑음).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import re
|
||||
from functools import lru_cache
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Clause as clause
|
||||
|
||||
# (옛 책, 옛 번호) → (현행 구분, 현행 번호, 근거)
|
||||
OLD = {
|
||||
("건설품셈(옛)", "11-3"): (
|
||||
"건설품셈 공통",
|
||||
"8-2-3",
|
||||
"실무 글 「건설표준품셈 11-3(굴삭기)」(봉화 · 울진대흥 2024) = 현행 8-2-3 굴착기",
|
||||
),
|
||||
("건설품셈(옛)", "3-7"): (
|
||||
"건설품셈 공통",
|
||||
"3-9-1",
|
||||
"현행 3-9-1 벌목 표(5m미만 벌목부 2.14 · 보통인부 0.51 · 굴착기+집게 2.71) = 블록 글"
|
||||
" 「건설표준품셈 3-7」(동부청영월 2024)",
|
||||
),
|
||||
("건설품셈(옛)", "8-2-35"): (
|
||||
"건설품셈 공통",
|
||||
"8-2-32",
|
||||
"현행 8-2-32 이동식 임목파쇄기 93.25㎾ Q 6.0 · 발전기 250㎾ · 보통인부 2인 · 굴착기 0.7㎥"
|
||||
"+집게 = 블록 글 「표준품셈 8-2-35」(동부청영월 2024)",
|
||||
),
|
||||
}
|
||||
_SABANG = "2018 통합 산림사업 표준품셈"
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def sabang_map() -> dict[str, str]:
|
||||
"""사방품셈(옛) 번호 → 현행 산림 번호 — 판 대응표 「2018 통합」 판 · 확정 · 한 대응만."""
|
||||
with open(clause._ROAD_MAP, encoding="utf-8-sig", newline="") as f:
|
||||
rows = list(csv.DictReader(f))
|
||||
out = {}
|
||||
for row in rows:
|
||||
old, new = clause._OLD_NO.match(row["과거_절"].strip()), row["현행_절"].strip()
|
||||
if row["STmate_인용판"] != _SABANG or row["판정"] != "확정" or not old:
|
||||
continue
|
||||
if clause._OLD_NO.fullmatch(new) and " " not in new:
|
||||
out[old.group(1)] = new
|
||||
return out
|
||||
|
||||
|
||||
def old_rows(book: str, number: str) -> list[dict]:
|
||||
"""옛 출처 하나 → 현행 로직 줄(`clause.lookup` 꼴) · 대조 없음 = []."""
|
||||
if (book, number) in OLD:
|
||||
return clause.lookup(*OLD[(book, number)][:2])
|
||||
if book == "사방품셈" + clause.OLD and number in sabang_map():
|
||||
return clause.lookup("산림품셈", sabang_map()[number])
|
||||
return []
|
||||
|
||||
|
||||
# ── 기계 작업량 절 ──────────────────────────────────────────────────
|
||||
_Q = re.compile(r"3,?600\.?\s*[×x*]")
|
||||
_SYM = re.compile(
|
||||
r"(?<![A-Za-z가-힣/*×·])(q|K|k|f|F|E|Cm|CM)\d?\s*(?:\((?:[^()=]|\([^()]*\))*\))?\s*="
|
||||
)
|
||||
_NUM = re.compile(r"\d[\d,]*(?:\.\d*)?")
|
||||
_RATIO = re.compile(r"\s*\d[\d.]*\s*/\s*\d[\d.]*\s*,?\s*")
|
||||
_CANON = {"k": "K", "F": "f", "CM": "Cm"}
|
||||
|
||||
|
||||
def has_q(lines: list[str]) -> bool:
|
||||
"""Q = 3600 × q × K × f × E / Cm 식 줄이 있나."""
|
||||
text = " ".join(lines)
|
||||
return bool(_Q.search(text)) and "Cm" in text
|
||||
|
||||
|
||||
def consts(lines: list[str]) -> list[dict[str, Any]]:
|
||||
"""줄 글 → [{기호, 값, 덧?}] — 기호 마디마다 괄호 글 뺀 마지막 수(「f = 1/1.3 = 0.77」 = 0.77)
|
||||
· Cm 괄호 각도 = 덧 · 첫 값만(같은 기호 둘째부터 뺌)."""
|
||||
out, seen = [], set()
|
||||
for line in lines:
|
||||
hits = list(_SYM.finditer(line))
|
||||
for i, m in enumerate(hits):
|
||||
body = line[m.end() : hits[i + 1].start() if i + 1 < len(hits) else len(line)]
|
||||
plain = re.sub(r"\((?:[^()]|\([^()]*\))*\)", " ", body)
|
||||
nums = [float(x.replace(",", "")) for x in _NUM.findall(plain.split("sec")[0])]
|
||||
sym = _CANON.get(m.group(1), m.group(1))
|
||||
if not nums or sym in seen:
|
||||
continue
|
||||
ratio = _RATIO.fullmatch(plain) # 「f = 1/1.25」 끝값 없는 나눗셈
|
||||
one: dict[str, Any] = {
|
||||
"기호": sym,
|
||||
"값": round(nums[0] / nums[1], 6) if ratio else nums[-1],
|
||||
}
|
||||
if sym == "Cm" and (deg := re.search(r"\(\s*(\d+)\s*˚?\s*\)", body)):
|
||||
one["덧"] = f"{deg.group(1)}˚"
|
||||
seen.add(sym)
|
||||
out.append(one)
|
||||
return out
|
||||
|
||||
|
||||
def machines(logic: dict[str, dict]) -> dict[str, str]:
|
||||
"""{기계 낱말: 「작업량당 기계경비」 로직 키} — 로직 이름 첫 낱말(괄호 앞)."""
|
||||
out = {}
|
||||
for k, r in sorted(logic.items()):
|
||||
name = str(r.get("이름") or "")
|
||||
if "작업량당 기계경비" in name:
|
||||
out.setdefault(re.split(r"[\s(]", name)[0], k)
|
||||
return out
|
||||
|
||||
|
||||
def machine_key(table: dict[str, str], text: str) -> str | None:
|
||||
"""글(같은 말 묶음 뒤)에 기계 낱말이 하나뿐이면 그 로직 키."""
|
||||
found = {k for w, k in table.items() if w in text}
|
||||
return found.pop() if len(found) == 1 else None
|
||||
@@ -5,6 +5,8 @@
|
||||
야면석소운반 GC001140 석재류 · 불량 · 큰돌쌓기 ÷10 근접) · 지어 넣은 값 0 · 보정 자료 = 여덟 파일 ·
|
||||
저장본 길(임시 폴더). 엑셀 = `helper_m02_hopyo_golden.FILES`(읽기만 · 없으면 skip) ·
|
||||
STmate 숨은 G 식 = 올림 길과 같은 `M02_San_Basis_Store.hidden`.
|
||||
794-2c 넓힘 — 없음 85 → 58 · 확정 단 125 → 164 · 손 확인 견본 열(기계 작업량 길 · 섞기 오인 넷 ·
|
||||
옛 번호 대조 · 같은 말 묶음) · 기계 상수 읽기 · 옛 번호 대조표.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -22,6 +24,7 @@ from M02_MasterTemplete import M02_Hopyo_Store as hopyo_store
|
||||
from M02_MasterTemplete import M02_San_Basis as sb
|
||||
from M02_MasterTemplete import M02_San_Basis_Const as bc
|
||||
from M02_MasterTemplete import M02_San_Basis_Index as bi
|
||||
from M02_MasterTemplete import M02_San_Basis_Map as bm
|
||||
from M02_MasterTemplete import M02_San_Basis_Store as store
|
||||
from M02_MasterTemplete import M02_San_Book as san_book
|
||||
|
||||
@@ -81,6 +84,9 @@ def test_counts_at_least_d2():
|
||||
assert kind["확정·전부"] >= 52
|
||||
assert same >= 141
|
||||
assert tier["없음"] <= 94 and kind["없음"] <= 94
|
||||
# 794-2c — 같은 말 묶음 · 옛 번호 대조표 · 기계 작업량 길(전 85 · 125 · 260)
|
||||
assert tier["없음"] <= 58 and tier["확정"] >= 164
|
||||
assert sum(v for k, v in kind.items() if k.startswith("확정")) >= 309
|
||||
|
||||
|
||||
def _first(bid: str) -> dict:
|
||||
@@ -181,3 +187,73 @@ def test_hidden_g_lines():
|
||||
rows = [[None] * 7, [None] * 6 + ["_x0007_L=20"], [None] * 6 + ["_x0007_"], [None] * 6 + ["x"]]
|
||||
assert store.hidden(book, {store.SHEET: rows}) == {"3": ["L=20"]}
|
||||
assert store.hidden({"꼴": "엑셀꼴", "단산": []}, {}) == {}
|
||||
|
||||
|
||||
# ── 794-2c 손 확인 견본 ─────────────────────────────────────────────
|
||||
HAND = [
|
||||
("북부청임도#56", "GC001148", "확정"), # 잔토정리 Q 식 · 제목에 품셈 이름 없음 → 기계 작업량 길
|
||||
("울진대흥#2264", "GC001148", "확정"), # 섞기 오인(GF000132) → 옛 11-3 · 굴착기
|
||||
("울진대흥#2269", "GC001148", "확정"), # 섞기 오인 → 되메우기 굴착기 Q 식
|
||||
("울진대흥#2290", "GC001148", "근접"), # 섞기 오인 → 적상 굴착기(운반 · 정지 절 미확정)
|
||||
("동부청영월#111", "GC001189", "근접"), # 섞기 오인 → 옛 8-2-35 = 현행 8-2-32 임목파쇄기
|
||||
("북부청임도#41", "GC000227", "확정"), # 휠라 = 채움재 · 옛 6-3-10 같은 절
|
||||
("울진소광#1329", "GC000143", "확정"), # 평떼 = 잔디 · 옛 4-1-1 같은 절
|
||||
("울진소광#794", "GF000298", "근접"), # 사방(옛) 4-16-2 → 판 대응표 산림 5-16-1
|
||||
("동부청영월#33", "GC000129", "근접"), # 옛 3-7 → 현행 3-9-1 벌목(표 값 같음)
|
||||
("울진대흥#2277", "GC001148", "확정"), # 옛 11-3 → 8-2-3 · 번호 + 검산
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("bid,key,tier", HAND)
|
||||
def test_794_2c_hand(bid, key, tier):
|
||||
r = results()[0][bid]
|
||||
keys = [c["기초"] for c in r["근거후보"]]
|
||||
assert key in keys and r["단"] == tier, (bid, r["단"], keys)
|
||||
assert "GF000132" not in keys, bid # 섞기(굴착기 0.7) 값 우연 짝 없음
|
||||
|
||||
|
||||
def test_794_2c_no_invented_divisor():
|
||||
"""「/ Q / 3」(1/3 적용) 블록은 검산 못 함 = 사람 · 1/3 을 지어 넣지 않음."""
|
||||
r = results()[0]["봉화#24"]
|
||||
assert r["단"] == "사람" and {c["기초"] for c in r["근거후보"]} == {"GC001147", "GC001148"}
|
||||
assert all(c["옵션"] == {} for c in r["근거후보"])
|
||||
|
||||
|
||||
def test_machine_consts_reader():
|
||||
got = bm.consts(
|
||||
[
|
||||
"q = 0.7 , k = 0.7 , f =1/1.175= 0.85",
|
||||
"E = 0.5 , Cm = 18 sec(90)",
|
||||
"Q = 3600*q*k*E*f/Cm = 41.65 m3/hr",
|
||||
]
|
||||
)
|
||||
assert got == [
|
||||
{"기호": "q", "값": 0.7},
|
||||
{"기호": "K", "값": 0.7},
|
||||
{"기호": "f", "값": 0.85},
|
||||
{"기호": "E", "값": 0.5},
|
||||
{"기호": "Cm", "값": 18.0, "덧": "90˚"},
|
||||
]
|
||||
got = bm.consts(
|
||||
[
|
||||
"q (버킷용량(m3)) = 0.7 , f (체적환산계수) = 1/1.25 , K (버킷계수) = 0.9",
|
||||
"Cm (1회 사이클 시간(초)) = 20 sec (135˚) , E = ( 0.55 + 0.45 ) / 2. - 0.05 = 0.45",
|
||||
]
|
||||
)
|
||||
assert [(x["기호"], x["값"]) for x in got] == [
|
||||
("q", 0.7),
|
||||
("f", 0.8),
|
||||
("K", 0.9),
|
||||
("Cm", 20.0),
|
||||
("E", 0.45),
|
||||
]
|
||||
assert bm.has_q(["3,600. × q × K × f × E", "Cm 20."]) and not bm.has_q(["Q = 6 ㎥/hr"])
|
||||
|
||||
|
||||
def test_old_number_map():
|
||||
assert {o["키"] for o in bm.old_rows("건설품셈(옛)", "11-3")} >= {"GC001147", "GC001148"}
|
||||
assert "GC000129" in {o["키"] for o in bm.old_rows("건설품셈(옛)", "3-7")}
|
||||
assert "GC001189" in {o["키"] for o in bm.old_rows("건설품셈(옛)", "8-2-35")}
|
||||
assert bm.sabang_map()["4-16-2"] == "5-16-1"
|
||||
assert "3-7" not in bm.sabang_map() # 임도(2017) 줄은 사방에 안 씀
|
||||
assert bm.old_rows("건설품셈(옛)", "8-2") == [] # 대조 없는 번호 = 없음(윗마디 안 받음)
|
||||
|
||||
Reference in New Issue
Block a user