refactor(776-3): 지문 형식 모름 — 단위 줄 투영 한 벌 · 몫 한 벌 · 블록 단가 = 항목 · clause 대체 몸 · 오타 정규식 걷음 · 맞춤 캐시 = HP 지문 칸
- Finger — 풀이 두 벌(_text · _stmate) 걷고 블록 단위 줄 투영 하나(_units) · 글은 조건 · 옛 판 · 계수 · 「…의 N %」 만 · 셈 밖 줄 안 씀 - Shares — 몫 = 단위 줄을 절(제목 · 윗절)로 묶음 한 벌 · 식 다시 풀기(qty_of · sections_stm) 걷음 · trade = ReadEst.trade - Elem.prices = 단위 줄 단가(글 다시 안 풂 · machines 갈래 안 건드림) - Match.clause = Basis.clause 만 · 대체 몸 · _TYPO_CLAUSE 걷음(오타는 읽기 ReadEst.sources) · fix_typo = 사본(Line 이 부름) - Fit.cache_path — HP 지문 칸(올린 엑셀 sha256 + 판) · 파일 그대로면 다시 안 읽음(lru) · 붙여넣기 HP 만 파일 해시 - 지문 전후 = 셈 단위 275 중 150 같음 · 다름 = 장비 절 · 글(쓰는 곳 없음) 100 · 고침(STmate 기계 두 묶음 · 거창 자재 · 끝 점 직종 · 옛 몫 금액 ÷ 단가 엉터리 · 제외금액 줄) — 골든 1위 줄마다 · 불리는 블록 206 갈래 · 1위 · 상태 전후 같음(162/177 · 기초 80 · SK 37 · 대기 89) - S1 다시 굳힘 = 봉화 · 울진대흥 · 울진소광 자원(같은 코드 두 묶음 따로) · S2 다시 굳힘 125/275(위 까닭) - 시험 고침 — finger(블록 = ReadEst.items · STmate 단위 줄 · 셈 밖) · fit 옛 HP(항목 · 글 없음) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
노임 = LB 이름 글자 묶음 닮음 · 단가 = LB 값
|
||||
기계 = EQ 갈래 겹침 · 규격 수(같음 1 · 겹침 0.5) · 이름 낱말 덤 · 괄호 낱말 어긋남 깎음 ·
|
||||
단가 없음(기계경비는 로직 셈) · `machine` = 하나로 가름(778-1)
|
||||
차례 = 점수(0.1 단위로 접음) › 엑셀 단가에 가까운 것(엑셀 단가 = 블록 글 「이름 단가 × 식 = 금액」 · 모르면 점수만).
|
||||
차례 = 점수(0.1 단위로 접음) › 엑셀 단가에 가까운 것(엑셀 단가 = 블록 단위 줄 단가 · 모르면 점수만).
|
||||
서버 단독 · 읽기만.
|
||||
"""
|
||||
|
||||
@@ -17,10 +17,10 @@ import re
|
||||
from typing import Any
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
from M02_MasterTemplete import M02_Hopyo_Finger as finger
|
||||
from M02_MasterTemplete import M02_Hopyo_Match as hm
|
||||
from M02_MasterTemplete import M02_Hopyo_Material as hmat
|
||||
from M02_MasterTemplete.M02_Hopyo_Words import mtypes, nfkc, parse_rhs, specnums
|
||||
from M02_MasterTemplete import M02_Hopyo_UnitLine as unit_line
|
||||
from M02_MasterTemplete.M02_Hopyo_Words import mtypes, nfkc, specnums
|
||||
|
||||
TOP = 3
|
||||
POOL = 5
|
||||
@@ -35,16 +35,12 @@ PARTS = ("자동덮개", "부착용", "콤팩터", "습지")
|
||||
|
||||
|
||||
def prices(block: dict[str, Any] | None) -> dict[str, float]:
|
||||
"""블록 글 → `{이름: 엑셀 단가}`(직종 줄 「직종 : 단가 × 식」 · 자재 줄 「이름 단가 × 식 = 금액」)."""
|
||||
"""블록 단위 줄(노무 · 자재 · 값) → `{이름: 엑셀 단가}`(첫 것 · 776-3 — 글 다시 안 풂)."""
|
||||
out: dict[str, float] = {}
|
||||
for raw in (block or {}).get("글") or []:
|
||||
row = str(raw).rstrip()
|
||||
cm = finger._COLON.match(row)
|
||||
mm = None if cm else finger._MAT.match(row)
|
||||
name = (cm or mm).group("name").strip() if (cm or mm) else ""
|
||||
got = parse_rhs((cm or mm).group("rhs")) if name else None
|
||||
if got and got.get("단가"):
|
||||
out.setdefault(re.sub(r"^[0-9가-힣]\.", "", name).strip(), float(got["단가"]))
|
||||
for one in (block or {}).get("항목") or []:
|
||||
price = unit_line.money(one.get("단가")) if one.get("묶음") in ("L", "M", "E") else None
|
||||
if price:
|
||||
out.setdefault(re.sub(r"^[0-9가-힣]\.", "", str(one.get("이름") or "")).strip(), price)
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@@ -1,102 +1,45 @@
|
||||
"""M02 일위대가 실무자료 엑셀 지문(PLAN 338-1 · 설계 2-2) — 호표 줄 + 이어진 단가산출 블록 →
|
||||
직종 품 · 장비 시간 · 자재 수량 · 율 · 계수 · 조건 · 조항 · 몫. 금액 안 씀(식 맨 앞 단가는 뗌).
|
||||
"""M02 일위대가 실무자료 엑셀 지문(PLAN 338-1 · 설계 2-2 · 776-3) — 호표 줄 + 이어진 단가산출 블록 →
|
||||
직종 품 · 장비 시간 · 자재 수량 · 율 · 계수 · 조건 · 조항 · 몫. 금액 안 씀.
|
||||
|
||||
엑셀꼴 = 블록 `글`(머리 뺀 몸 줄)을 줄마다 풂 — 절 `^\\d+\\.` · 작은 절 `^[가-하]\\.` ·
|
||||
직종 줄 「직종 : 단가 × 식 = 금액」(우리 인력 이름 · 옛이름) · 장비 = 「노무비 · 경비 · 재료비 :
|
||||
단가 ÷ Q」
|
||||
+ 지금 장비(노무비 > 경비 > 재료비) · 자재 = 「이름 단가 × 수량 = 금액」 · 「: 별도계상」 ·
|
||||
못 풀면 금액 ÷ 단가(「역산」 · 품 되찾기만).
|
||||
STmate = 블록 `자원`(L · X · M · S) 품이 곧 품(노무비 · 재료비 · 경비 세 줄 같은 품은 한 번) ·
|
||||
섞음 = 이름 · 규격 몫 둘(작은 쪽 인력 · 큰 쪽 장비).
|
||||
몫(조합용 · 설계 2-7) = 맨 윗 절마다 노임 · 장비 벡터(`M02_Hopyo_Shares`).
|
||||
형식 모름 — 블록 단위 줄(`항목` · `M02_Hopyo_UnitLine`)의 투영:
|
||||
L 노무 = 직종 품 · 식이 있으면 맨 인자가 섞음 비율 후보(호표 이름 · 규격 · 절 제목의 (N%) ·
|
||||
8:2)와 같을 때 율로 가름 · 식 없는 줄(QTY)은 호표 이름 비율 둘 이상이면 작은 쪽(인력)
|
||||
X 기계 = 장비 {갈래, 규격, 시간, 밑시간, 율, 절, 글} · 식 없는 줄은 큰 비율(장비)로 가름
|
||||
M 자재 = 식(수량) · 별도계상(품 없음) · E 값 줄에 수량이 있으면 장비 글이면 장비 · 아니면 자재
|
||||
셈 밖 · 율 · 하위 줄 = 안 씀(율 % 는 글에서).
|
||||
글(블록 머리 「N.이름 [단위]」 + 몸 글) = 조건 · 옛 판 · 계수(q · K · f …) · 글 속 「…의 N %」.
|
||||
몫(조합용 · 설계 2-7) = 절마다 노임 · 장비 벡터(`M02_Hopyo_Shares.sections`).
|
||||
블록 없는 기계(hr) 줄 = `machine` · 노임 · 장비를 바로 적은 호표 = `direct`.
|
||||
값 · 규칙은 336 실험(C4 `s1_excel_fp` · K5 `stmate`) 그대로.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections import Counter
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Basis as hopyo_basis
|
||||
from M02_MasterTemplete import M02_Hopyo_ReadEst as hopyo_est
|
||||
from M02_MasterTemplete import M02_Hopyo_Shares as shares
|
||||
from M02_MasterTemplete.M02_Hopyo_Shares import trade
|
||||
from M02_MasterTemplete.M02_Hopyo_Words import conds, mtypes, parse_rhs, specnums
|
||||
from M02_MasterTemplete.M02_Hopyo_Words import conds, mtypes, specnums
|
||||
|
||||
COST = {"노무비", "재료비", "경비"}
|
||||
SKIPW = {
|
||||
"소계",
|
||||
"계",
|
||||
"합계",
|
||||
"인부소계",
|
||||
"자재소계",
|
||||
"주자재소계",
|
||||
"운반",
|
||||
"상차",
|
||||
"기계",
|
||||
"다짐",
|
||||
"적사",
|
||||
"살수",
|
||||
"집토",
|
||||
"암절취",
|
||||
"채집",
|
||||
"인력",
|
||||
"장비",
|
||||
"작업조건",
|
||||
"1일시공량",
|
||||
"작업면적",
|
||||
"1시간작업량",
|
||||
"기계채집",
|
||||
"구조물터파기",
|
||||
}
|
||||
_COLON = re.compile(
|
||||
r"^\s*(?:\d+\)|[0-9가-힣]\.)?\s*(?P<name>[^::=]{1,40}?)\s*[::]\s*(?P<rhs>.*)$"
|
||||
)
|
||||
_MAT = re.compile(
|
||||
r"^\s*(?P<name>[^\d\s::=][^::=]*?[A-Za-z가-힣)\]])\s+(?P<rhs>[\d,]+(?:\.\d+)?\s*[×xX*÷/].*=.*)$"
|
||||
)
|
||||
_PCT = re.compile(r"([가-힣]+)의\s*([\d.]+)\s*%")
|
||||
_COEF = re.compile(
|
||||
r"(?<![A-Za-z가-힣])(q|qs|q1|K|k|f|f1|f2|E|Es|Cm|L|V1|V2|T|rt|n|t1|t2|t3|t4)\s*=\s*"
|
||||
r"(\d+(?:\.\d+)?)(?![\d.]*\s*[×x*÷/])"
|
||||
)
|
||||
_HEAD = re.compile(r"^\s*(\d+)\.\s*(.*)$")
|
||||
_SUB = re.compile(r"^\s*([가-하])\.\s*(.*)$")
|
||||
_SECTION = re.compile(r"^\s*(\d+\.|[가-하]\.)")
|
||||
OLD = re.compile(
|
||||
r"((?<![\d,.])20(0\d|1\d|20)(?![\d,.])|(?<!\d)(1\d|20)년|임도품셈|임도표준품셈|사방품셈|사방사업표준품셈)"
|
||||
)
|
||||
_LASTNUM = re.compile(r"=\s*([\d,]+(?:\.\d+)?)\s*$")
|
||||
_HOURS = re.compile(r"(hr|시간)", re.I)
|
||||
TRADE_UNITS = ("인",)
|
||||
rates_in = hopyo_est.rates_in
|
||||
|
||||
|
||||
def _flat(s: Any) -> str:
|
||||
return re.sub(r"\s+", "", str(s or ""))
|
||||
|
||||
|
||||
def _ratio(rhs: str) -> float | None:
|
||||
"""식을 못 풀 때만 — 금액 ÷ 단가(품 되찾기용)."""
|
||||
m = re.match(r"^\s*([\d,]+(?:\.\d+)?)", rhs)
|
||||
n = _LASTNUM.search(rhs)
|
||||
if not m or not n:
|
||||
return None
|
||||
p, a = float(m.group(1).replace(",", "")), float(n.group(1).replace(",", ""))
|
||||
return a / p if p else None
|
||||
|
||||
|
||||
def rates_in(text: Any) -> set[float]:
|
||||
"""글 속 섞음 비율 후보 — (N%) · 8:2 · 9:1."""
|
||||
t = str(text or "")
|
||||
out = {round(float(n) / 100, 4) for n in re.findall(r"(\d+(?:\.\d+)?)\s*%", t)}
|
||||
out = {v for v in out if 0 < v < 1}
|
||||
for a, b in re.findall(r"(\d+)\s*:\s*(\d+)", t):
|
||||
a, b = float(a), float(b)
|
||||
if a and b and a + b <= 20:
|
||||
out |= {round(a / (a + b), 4), round(b / (a + b), 4)}
|
||||
return out
|
||||
|
||||
|
||||
def empty(line: dict[str, Any], block: dict[str, Any] | None = None) -> dict[str, Any]:
|
||||
block = block or {}
|
||||
basis = line.get("근거") or {}
|
||||
@@ -133,12 +76,10 @@ def _round(fp: dict[str, Any]) -> dict[str, Any]:
|
||||
|
||||
|
||||
def excel(block: dict[str, Any] | None, line: dict[str, Any]) -> dict[str, Any]:
|
||||
"""HP 블록(설계 3-1) + 그 줄(이름 · 규격 · 단위) → 지문 · 블록이 없거나 글 · 자원이 없으면
|
||||
"""HP 블록(설계 3-1) + 그 줄(이름 · 규격 · 단위) → 지문 · 블록이 없거나 단위 줄 · 글이 없으면
|
||||
기계(hr) 줄 지문(장비 1 시간) 또는 빈 지문."""
|
||||
if block and block.get("자원"):
|
||||
return _round(_stmate(block, line))
|
||||
if block and block.get("글"):
|
||||
return _round(_text(block, line))
|
||||
if block and (block.get("항목") or block.get("글")):
|
||||
return _round(_units(block, line))
|
||||
return machine(line)
|
||||
|
||||
|
||||
@@ -155,124 +96,59 @@ def machine(line: dict[str, Any]) -> dict[str, Any]:
|
||||
return fp
|
||||
|
||||
|
||||
def _text(block: dict[str, Any], line: dict[str, Any]) -> dict[str, Any]:
|
||||
"""엑셀꼴 — 블록 글 줄마다 풂(C4 `north`)."""
|
||||
fp = empty(line, block)
|
||||
def _text(fp: dict[str, Any], block: dict[str, Any]) -> None:
|
||||
"""블록 글 → 조건(머리 + 앞 넉 줄 + ※ · 작업조건 줄) · 옛 판 · 계수 · 「…의 N %」."""
|
||||
head = f"{block.get('번', '')}.{block.get('이름', '')} [{block.get('단위', '')}]"
|
||||
text = [head, *[str(t) for t in block.get("글") or []]]
|
||||
front = [t for t in text[:5]] + [t for t in text if "※" in t or "작업조건" in t]
|
||||
head_text = " ".join([fp["이름"], fp["규격"], *front])
|
||||
fp["조건"] = {k: sorted(v) for k, v in conds(head_text).items()}
|
||||
front = text[:5] + [t for t in text if "※" in t or "작업조건" in t]
|
||||
got = conds(" ".join([fp["이름"], fp["규격"], *front]))
|
||||
fp["조건"] = {k: sorted(v) for k, v in got.items()}
|
||||
fp["옛판"] = bool(OLD.search(" ".join(text[:6])))
|
||||
sec, sub, mach, label = 0, "", None, ""
|
||||
got_cost: dict[tuple, tuple] = {}
|
||||
item_rates = rates_in(f"{fp['이름']} {fp['규격']}")
|
||||
sec_rates: set[float] = set()
|
||||
|
||||
def unmix(got: dict | None) -> dict | None:
|
||||
"""율 메모가 없는데 맨 인자가 섞음 비율 후보와 같으면 율로 가름."""
|
||||
if got and not got["율"]:
|
||||
for v in got.get("인자") or []:
|
||||
if round(v, 4) in item_rates | sec_rates and 0 < v < 1:
|
||||
return {**got, "밑품": got["밑품"] / v, "율": [[round(v, 4), "섞음"]]}
|
||||
return got
|
||||
|
||||
for raw in text[1:]:
|
||||
row = raw.rstrip()
|
||||
h, s = _HEAD.match(row), _SUB.match(row)
|
||||
if h or s:
|
||||
if h:
|
||||
sec, sub = int(h.group(1)), ""
|
||||
sec_rates = rates_in(h.group(2))
|
||||
else:
|
||||
sub = s.group(1)
|
||||
sec_rates = sec_rates | rates_in(s.group(2))
|
||||
title = (h or s).group(2)
|
||||
ts = mtypes(title)
|
||||
mach = (ts, specnums(title)) if ts else None
|
||||
label = title.strip() if ts else ""
|
||||
for k, v in _PCT.findall(row):
|
||||
fp["율"].append([k, float(v)])
|
||||
cm = _COLON.match(row)
|
||||
name = re.sub(r"^[0-9가-힣]\.", "", _flat(cm.group("name"))) if cm else ""
|
||||
who = hopyo_basis.trade_names().get(name) if cm else None
|
||||
if who:
|
||||
rhs = cm.group("rhs")
|
||||
got = unmix(parse_rhs(rhs))
|
||||
if got:
|
||||
_labor(fp, who, got["품"], got["밑품"], got["율"], "식")
|
||||
elif r := _ratio(rhs):
|
||||
_labor(fp, who, r, r, [], "역산")
|
||||
continue
|
||||
if cm and name in COST:
|
||||
got = unmix(parse_rhs(cm.group("rhs")))
|
||||
if got and mach:
|
||||
key = (sec, sub, label)
|
||||
prio = 0 if (name == "노무비" and got["단가"] > 0) else 1 if name == "경비" else 2
|
||||
if key not in got_cost or prio < got_cost[key][0]:
|
||||
got_cost[key] = (prio, mach, got)
|
||||
continue
|
||||
if cm and name not in SKIPW and re.search(r"(별도|별산)", cm.group("rhs")):
|
||||
fp["자재"].append({"이름": cm.group("name").strip(), "품": None, "밑품": None})
|
||||
continue
|
||||
mm = _MAT.match(row)
|
||||
if mm and not cm and (got := parse_rhs(mm.group("rhs"))):
|
||||
fp["자재"].append(
|
||||
{"이름": mm.group("name").strip(), "품": got["품"], "밑품": got["밑품"]}
|
||||
)
|
||||
continue
|
||||
if not (h or s):
|
||||
ts = mtypes(row)
|
||||
if ts and "%" not in row and not cm:
|
||||
mach, label = (ts, specnums(row)), row.strip()
|
||||
for row in text[1:]:
|
||||
fp["율"] += [[k, float(v)] for k, v in _PCT.findall(row)]
|
||||
if not _SECTION.match(row):
|
||||
for k, v in _COEF.findall(row):
|
||||
fp["계수"].setdefault(k, float(v))
|
||||
for (sec_, sub_, lab), (_, (ts, sp), got) in got_cost.items():
|
||||
where = f"{sec_}.{sub_}" if sub_ else str(sec_)
|
||||
one = {"갈래": sorted(ts), "규격": sorted(sp), "시간": got["품"], "밑시간": got["밑품"]}
|
||||
fp["장비"].append({**one, "율": got["율"], "절": where, "글": lab})
|
||||
fp["몫"] = shares.sections(text[1:])
|
||||
return fp
|
||||
|
||||
|
||||
def _stmate(block: dict[str, Any], line: dict[str, Any]) -> dict[str, Any]:
|
||||
"""STmate — 자원 품이 곧 품(K5 `stmate`)."""
|
||||
def _unmix(one: dict[str, Any], rates: set[float]) -> tuple[float, list]:
|
||||
"""(밑품, 율) — 율 메모가 없는데 맨 인자가 섞음 비율 후보와 같으면 율로 가름."""
|
||||
if not one.get("율"):
|
||||
for v in one.get("인자") or []:
|
||||
if round(v, 4) in rates and 0 < v < 1:
|
||||
return one["밑품"] / v, [[round(v, 4), "섞음"]]
|
||||
return one["밑품"], one.get("율") or []
|
||||
|
||||
|
||||
def _units(block: dict[str, Any], line: dict[str, Any]) -> dict[str, Any]:
|
||||
fp = empty(line, block)
|
||||
text = [str(t) for t in block.get("글") or []][:60]
|
||||
fp["조건"] = {
|
||||
k: sorted(v) for k, v in conds(" ".join([fp["이름"], fp["규격"], *text[:4]])).items()
|
||||
}
|
||||
fp["옛판"] = bool(OLD.search(" ".join(text[:4])))
|
||||
for t in text:
|
||||
for k, v in _COEF.findall(t):
|
||||
fp["계수"].setdefault(k, float(v))
|
||||
for k, v in _PCT.findall(t):
|
||||
fp["율"].append([k, float(v)])
|
||||
rates = rates_in(f"{fp['이름']} {fp['규격']}")
|
||||
lab_rate = min(rates) if len(rates) >= 2 else None
|
||||
mach_rate = max(rates) if len(rates) >= 2 else None
|
||||
seen: Counter = Counter()
|
||||
for x in block.get("자원") or []:
|
||||
kind, q = x.get("갈래"), x.get("품")
|
||||
nm, sp = str(x.get("이름") or ""), str(x.get("규격") or "")
|
||||
if not q:
|
||||
_text(fp, block)
|
||||
item_rates = rates_in(f"{fp['이름']} {fp['규격']}")
|
||||
pair = sorted(item_rates) if len(item_rates) >= 2 else []
|
||||
lab_rate, mach_rate = (pair[0], pair[-1]) if pair else (None, None)
|
||||
for one in block.get("항목") or []:
|
||||
kind, q, how = one.get("묶음"), one.get("품"), one.get("어떻게")
|
||||
if one.get("밖") or (q is None and how != "별도"):
|
||||
continue
|
||||
seen[(x.get("코드"), round(float(q), 9))] += 1
|
||||
if seen[(x.get("코드"), round(float(q), 9))] > 1: # 노무비 · 재료비 · 경비 세 줄 같은 품
|
||||
continue
|
||||
q = float(q)
|
||||
ts = mtypes(f"{nm} {sp}")
|
||||
if kind == "L":
|
||||
if who := trade(nm):
|
||||
base = q / lab_rate if lab_rate else q
|
||||
_labor(fp, who, q, base, [[lab_rate, "섞음"]] if lab_rate else [], "QTY")
|
||||
elif kind == "X" or (kind == "S" and ts):
|
||||
one = {"갈래": sorted(ts), "규격": sorted(specnums(sp) | specnums(nm)), "시간": q}
|
||||
base = q / mach_rate if mach_rate else q
|
||||
fp["장비"].append({**one, "밑시간": base, "율": [], "절": "0", "글": f"{nm} {sp}"})
|
||||
elif kind in ("M", "S"):
|
||||
fp["자재"].append({"이름": f"{nm} {sp}".strip(), "품": q, "밑품": q})
|
||||
fp["몫"] = shares.sections_stm(block.get("자원") or [], text)
|
||||
text = f"{one.get('이름') or ''} {one.get('규격') or ''}".strip()
|
||||
rates = item_rates | set(one.get("절율") or [])
|
||||
if kind == "L" and (who := one.get("직종") or trade(one.get("이름"))):
|
||||
if how == "QTY":
|
||||
base, r = (q / lab_rate, [[lab_rate, "섞음"]]) if lab_rate else (q, [])
|
||||
else:
|
||||
base, r = _unmix(one, rates) if how == "식" else (one["밑품"], one["율"])
|
||||
_labor(fp, who, q, base, r, how)
|
||||
elif kind == "X" or (kind == "E" and mtypes(text)):
|
||||
if how == "QTY":
|
||||
base, r = (q / mach_rate if mach_rate else q), []
|
||||
else:
|
||||
base, r = _unmix(one, rates)
|
||||
sizes = specnums(one.get("규격") or "") | specnums(one.get("이름") or "")
|
||||
got = {"갈래": sorted(mtypes(text)), "규격": sorted(sizes), "시간": q}
|
||||
fp["장비"].append({**got, "밑시간": base, "율": r, "절": one.get("절"), "글": text})
|
||||
elif kind in ("M", "E") and (how in ("식", "별도", "QTY")):
|
||||
fp["자재"].append({"이름": text, "품": q, "밑품": one.get("밑품", q)})
|
||||
fp["몫"] = shares.sections(block.get("항목") or [])
|
||||
return fp
|
||||
|
||||
|
||||
@@ -282,7 +158,7 @@ def direct(hopyo: dict[str, Any]) -> dict[str, Any] | None:
|
||||
fp = empty({k: hopyo.get(k) for k in ("이름", "규격", "단위")})
|
||||
texts = [str(t) for t in hopyo.get("근거글") or []]
|
||||
texts += [f"{x.get('이름')} {x.get('규격')}" for x in lines if x.get("갈래") == "근거글"]
|
||||
fp["출처"] = hopyo_basis.sources(" ".join(texts))
|
||||
fp["출처"] = hopyo_est.sources(" ".join(texts))
|
||||
hit = False
|
||||
for x in lines:
|
||||
q = x.get("수량")
|
||||
|
||||
@@ -20,6 +20,7 @@ import hashlib
|
||||
import json
|
||||
import queue
|
||||
import threading
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -295,9 +296,21 @@ def _hp_path(folder: Path, key: str) -> Path:
|
||||
return hopyo_store._path(folder, key)
|
||||
|
||||
|
||||
@lru_cache(maxsize=64)
|
||||
def _hp_mark(path: str, stamp: tuple[int, int]) -> str:
|
||||
"""HP 지문 칸(올린 엑셀 sha256 + 읽기 판 · 776-3) — 없으면(붙여넣기) 파일 해시 · 파일이 그대로면
|
||||
(`stamp` = 바뀐 때 · 크기) 다시 안 읽음."""
|
||||
raw = Path(path).read_bytes()
|
||||
doc = json.loads(raw)
|
||||
mark = doc.get("지문") if isinstance(doc, dict) else None
|
||||
return f"{mark[:16]}{doc.get('판')}" if mark else hashlib.sha256(raw).hexdigest()[:16]
|
||||
|
||||
|
||||
def cache_path(folder: Path, key: str) -> Path:
|
||||
hp = hashlib.sha256(_hp_path(folder, key).read_bytes()).hexdigest()[:16]
|
||||
return ours.folder() / f"맞춤_{hp}_{ours.version()}_{CALC_VERSION}.json"
|
||||
hp = _hp_path(folder, key)
|
||||
got = hp.stat()
|
||||
mark = _hp_mark(str(hp), (got.st_mtime_ns, got.st_size))
|
||||
return ours.folder() / f"맞춤_{mark}_{ours.version()}_{CALC_VERSION}.json"
|
||||
|
||||
|
||||
def units(doc: dict[str, Any]) -> list[tuple[str, dict | None, dict]]:
|
||||
|
||||
@@ -343,7 +343,6 @@ def name_cands(line: dict[str, Any], got: dict[str, Any] | None = None) -> list[
|
||||
|
||||
# ── 인용 조항 · 이름 · 근거 차례(338 · 설계 2-4 B 차례) ─────────────────────
|
||||
_OLD_TEXT = re.compile(r"(20(1\d|2[0-4])|1\d년|2[0-4]년|임도품셈|임도표준품셈|사방품셈)")
|
||||
_TYPO_CLAUSE = re.compile(r"^(\d+)-(\d)(\d)(\d)$") # 13-114 → 13-11-4
|
||||
_GENERIC = frozenset(
|
||||
"인력 기계 장비 소계 합계 노무비 재료비 경비 적용 기준 별도계상 별도 인부 보통인부 특별인부 "
|
||||
"설치 해체 운반 작업 시공 사용 포함 공구손료 경장비 손료 기계경비 재료 인건비 품셈 표준품셈 "
|
||||
@@ -352,32 +351,14 @@ _GENERIC = frozenset(
|
||||
|
||||
|
||||
def fix_typo(theirs: list) -> list[list[str]]:
|
||||
"""엑셀 인용 조항 오타 — 「13-114」 → 「13-11-4」."""
|
||||
out = []
|
||||
for book, n in theirs or []:
|
||||
m = _TYPO_CLAUSE.match(str(n))
|
||||
out.append([book, f"{m.group(1)}-{m.group(2)}{m.group(3)}-{m.group(4)}" if m else n])
|
||||
return out
|
||||
"""엑셀 인용 조항 사본 — 오타(「13-114」 → 13-11-4)는 읽기(`ReadEst.sources`)가 이미 고침."""
|
||||
return [list(x) for x in theirs or []]
|
||||
|
||||
|
||||
def clause(theirs: list, our: dict[str, Any]) -> tuple[float, list[str], str]:
|
||||
"""(점수, 맞은 우리 조항 글, 꼬리표) — 같음 1 · 윗마디 0.9(엑셀 6-2 ↔ 우리 6-2-2) · 앞 마디 0.5
|
||||
·
|
||||
나 창 `Basis.clause` 가 있으면 그것."""
|
||||
if hasattr(hopyo_basis, "clause"):
|
||||
return hopyo_basis.clause(theirs, our)
|
||||
nums = [n for book, n in theirs if book == our["책"]]
|
||||
same = sorted(n for n in nums if n in our["조항"])
|
||||
if same:
|
||||
return 1.0, [f"{our['구분']} {n}" for n in same], "조항 같음"
|
||||
kids = sorted(c for c in our["조항"] for n in nums if c.startswith(n + "-"))
|
||||
if kids:
|
||||
return 0.9, [f"{our['구분']} {n}" for n in kids], "조항 같음"
|
||||
heads = {n.rsplit("-", 1)[0] for n in nums}
|
||||
near_ = sorted(n for n in our["조항"] if n.rsplit("-", 1)[0] in heads)
|
||||
if near_:
|
||||
return 0.5, [f"{our['구분']} {n}" for n in near_], "앞 마디 같음"
|
||||
return 0.0, [], ""
|
||||
"""(점수, 맞은 우리 조항 글, 꼬리표) — `M02_Hopyo_Basis.clause`(같음 1 · 윗마디 0.9 ·
|
||||
앞 마디 0.5)."""
|
||||
return hopyo_basis.clause(theirs, our)
|
||||
|
||||
|
||||
def _strong_basis(line: dict[str, Any], cand: dict[str, Any]) -> bool:
|
||||
@@ -393,9 +374,6 @@ def b_order(line: dict[str, Any], got: dict[str, Any] | None = None, text: Any =
|
||||
근거: {로직: 근거}}` — 이름 후보 + 근거 후보 합침(접힘 없음) · 「같음」도 근거 검사(인용 조항이
|
||||
다른 로직을 가리키면 후보) · 옛 판 글이면 같음 그대로 · 씨앗은 이름 같음 자리."""
|
||||
got = got or targets()
|
||||
basis_ = dict(line.get("근거") or {})
|
||||
if basis_.get("출처"):
|
||||
line = {**line, "근거": {**basis_, "출처": fix_typo(basis_["출처"])}}
|
||||
basis = hopyo_basis.candidates(line, got)
|
||||
named = candidates(line, "로직", got, None)
|
||||
merged_c = [c for c in hopyo_basis.merge(basis, named) if c.get("로직")]
|
||||
|
||||
@@ -1,45 +1,18 @@
|
||||
"""M02 일위대가 맞춤 몫(PLAN 338-1 · 설계 2-2 · 2-7) — 단가산출 블록을 맨 윗 절(1. 2. 3.)
|
||||
· 작은 절(가. 나.)로 갈라 몫마다 노임(직종 → 인) · 장비(「굴착기|0.6」 → 시간) 벡터.
|
||||
|
||||
엑셀꼴 = 「직종 : 노임 × 식 = 금액」(식을 못 풀면 금액 ÷ 노임) · 장비 이름 줄 뒤 「노무비 : 단가 ÷ Q
|
||||
× 몫」 ·
|
||||
소계 · 합계 절 뺌. STmate = 자원 L · X(절 = 자원 `절` · 없으면 한 몫). 금액 안 씀.
|
||||
규칙은 336 실험(C5 `c01_trees.sections` · `sections_stm`) 그대로 · 장비 갈래는
|
||||
`M02_Hopyo_Words.mtypes`.
|
||||
"""M02 일위대가 맞춤 몫(PLAN 338-1 · 설계 2-2 · 2-7 · 776-3) — 단가산출 블록 단위 줄을 절(맨 윗 절 ·
|
||||
작은 절)마다 묶어 노임(직종 → 인) · 장비(「굴착기|0.6」 → 시간) 벡터. 형식 모름(단위 줄만 ·
|
||||
`M02_Hopyo_UnitLine`) · 셈 밖 줄 뺌 · 금액 안 씀 · 장비 갈래 = `M02_Hopyo_Words.mtypes`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import operator
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Basis as hopyo_basis
|
||||
from M02_MasterTemplete.M02_Hopyo_ReadEst import trade # 노임 이름 → 우리 인력(B09 도 씀)
|
||||
from M02_MasterTemplete.M02_Hopyo_Words import mtypes, nfkc
|
||||
|
||||
__all__ = ["eq_key", "sections", "trade"]
|
||||
_SIZE = re.compile(r"(\d+(?:\.\d+)?)\s*(㎥|m3|톤|ton|t\b|kw|hp)", re.I)
|
||||
_OPS = {
|
||||
ast.Mult: operator.mul,
|
||||
ast.Div: operator.truediv,
|
||||
ast.Add: operator.add,
|
||||
ast.Sub: operator.sub,
|
||||
}
|
||||
_TRADE = re.compile(
|
||||
r"^[\s*·\-○◇]*([가-힣][가-힣\s]*?)\s*:\s*([\d,]+(?:\.\d+)?)\s*(.*?)(?:=\s*([\d,]+(?:\.\d+)?))?\s*$"
|
||||
)
|
||||
_SECTION = re.compile(r"^\s*(\d+)\s*\.\s*(.*)$")
|
||||
_EQ_COST = re.compile(r"^\s*노\s*무\s*비\s*:\s*([\d,]+(?:\.\d+)?)\s*(.*?)(?:=\s*[\d,.]+)?\s*$")
|
||||
_SKIP_SEC = re.compile(r"^(합\s*계|소\s*계|계)")
|
||||
_SUBLABEL = re.compile(r"^\s*[가-하]\s*\.\s*(.*)$")
|
||||
_COSTLINE = re.compile(r"^\s*(노\s*무\s*비|재\s*료\s*비|경\s*비)\s*:")
|
||||
|
||||
|
||||
def trade(name: Any) -> str | None:
|
||||
"""노임 이름 → 우리 인력 이름(빈칸 · 옛이름 · 괄호 글 풀이) · 아니면 None."""
|
||||
names = hopyo_basis.trade_names()
|
||||
flat = re.sub(r"\s+", "", str(name or ""))
|
||||
return names.get(flat) or names.get(re.sub(r"\(.*?\)", "", flat))
|
||||
|
||||
|
||||
def eq_key(text: Any) -> str | None:
|
||||
@@ -54,124 +27,23 @@ def eq_key(text: Any) -> str | None:
|
||||
return f"{'+'.join(sorted(kinds))}|{size}"
|
||||
|
||||
|
||||
def _eval(node: ast.AST) -> float:
|
||||
if isinstance(node, ast.Expression):
|
||||
return _eval(node.body)
|
||||
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
|
||||
return float(node.value)
|
||||
if isinstance(node, ast.BinOp) and type(node.op) in _OPS:
|
||||
return _OPS[type(node.op)](_eval(node.left), _eval(node.right))
|
||||
if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
|
||||
return -_eval(node.operand)
|
||||
raise ValueError("식 아님")
|
||||
|
||||
|
||||
def qty_of(expr: str) -> float | None:
|
||||
"""「273,540 × 3인 ÷ 55㎥ × 2」 → 3/55×2 · 「59,020 ÷ 23.1 × 90%」 → 1/23.1×0.9."""
|
||||
s = re.sub(r"\([^()0-9]*\)", "", str(expr)).replace(",", "")
|
||||
s = re.sub(r"(\d+(?:\.\d+)?)\s*%", r"(\1/100)", s)
|
||||
s = s.replace("×", "*").replace("÷", "/").replace("X", "*").replace("x", "*")
|
||||
s = re.sub(r"/\s*(?=[^\d(\s.])", " ", s) # 「인/㎥」 의 / 는 단위
|
||||
s = re.sub(r"[^\d.+\-*/() ]", " ", s)
|
||||
s = re.sub(r"(?<=\d)\.(?!\d)", "", s) # 「4.」 → 4
|
||||
s = re.sub(r"\(\s*\)", " ", s).strip()
|
||||
m = re.match(r"\s*\d+(?:\.\d+)?", s)
|
||||
if not m:
|
||||
return None
|
||||
s = "1" + s[m.end() :]
|
||||
s = re.sub(r"(?<=[\d)])\s+(?=[\d(])", "*", s) # 빈칸으로 붙은 수는 곱
|
||||
try:
|
||||
v = _eval(ast.parse(s, mode="eval"))
|
||||
except (SyntaxError, ValueError, ZeroDivisionError):
|
||||
return None
|
||||
return v if 0 < v < 1e4 else None
|
||||
|
||||
|
||||
def _part(title: str, parent: str) -> dict[str, Any]:
|
||||
return {"제목": title, "절": parent, "노임": {}, "장비": {}}
|
||||
|
||||
|
||||
def sections(texts: list[str]) -> list[dict[str, Any]]:
|
||||
"""블록 몸 글 → 몫마다 {제목, 절, 노임, 장비} — 노임 · 장비가 하나도 없는 몫은 뺌."""
|
||||
out: list[dict[str, Any]] = []
|
||||
cur: dict[str, Any] | None = None
|
||||
eq, parent = None, ""
|
||||
for raw in texts:
|
||||
line = str(raw).rstrip()
|
||||
sec = _SECTION.match(line)
|
||||
if sec and not re.match(r"^\s*\d+\.\d", line):
|
||||
title = sec.group(2).strip()
|
||||
if _SKIP_SEC.match(title.replace(" ", "")):
|
||||
cur = None
|
||||
continue
|
||||
parent = title
|
||||
cur = _part(title, title)
|
||||
out.append(cur)
|
||||
eq = eq_key(title)
|
||||
def sections(units: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""블록 단위 줄(`M02_Hopyo_UnitLine`) → 몫마다 {제목 = 줄 절, 절 = 윗절, 노임, 장비} — 셈 밖 ·
|
||||
매개값 없는 줄 뺌 · 장비 = 시간 50 아래 · 노임 · 장비가 하나도 없는 몫은 뺌."""
|
||||
out: dict[str, dict[str, Any]] = {}
|
||||
for one in units:
|
||||
q, kind = one.get("품"), one.get("묶음")
|
||||
if one.get("밖") or not q or kind not in ("L", "X"):
|
||||
continue
|
||||
sub = _SUBLABEL.match(line) if cur is not None else None
|
||||
if sub and not _SKIP_SEC.match(sub.group(1).replace(" ", "")):
|
||||
cur = _part(f"{parent} · {sub.group(1).strip()}", parent)
|
||||
out.append(cur)
|
||||
if cur is None:
|
||||
if out or not line.strip():
|
||||
continue
|
||||
cur = _part("", "")
|
||||
out.append(cur)
|
||||
if re.search(r"(소계|합계|인부소계)", line.replace(" ", "")):
|
||||
eq = None
|
||||
continue
|
||||
sub = _SUBLABEL.match(line)
|
||||
if sub:
|
||||
eq = eq_key(sub.group(1))
|
||||
elif not _COSTLINE.match(line) and (k := eq_key(line)):
|
||||
eq = k
|
||||
m = _EQ_COST.match(line)
|
||||
if m and eq:
|
||||
q = qty_of(f"{m.group(1)} {m.group(2)}")
|
||||
if q and q < 50:
|
||||
cur["장비"][eq] = cur["장비"].get(eq, 0) + q
|
||||
continue
|
||||
t = _TRADE.match(line)
|
||||
if t and (who := trade(t.group(1))):
|
||||
q = qty_of(f"{t.group(2)} {t.group(3)}")
|
||||
if q is None and t.group(4): # 식을 못 풀면 금액 ÷ 노임(품 되찾기만)
|
||||
q = float(t.group(4).replace(",", "")) / float(t.group(2).replace(",", ""))
|
||||
if q:
|
||||
cur["노임"][who] = cur["노임"].get(who, 0) + q
|
||||
return [s for s in out if s["노임"] or s["장비"]]
|
||||
|
||||
|
||||
def sections_stm(res: list[dict[str, Any]], texts: list[str]) -> list[dict[str, Any]]:
|
||||
"""STmate 자원(L · X) → 몫(자원 `절` = 글 줄 차례면 그 앞 맨 윗 절 · 글이면 그 제목 · 없으면 한
|
||||
몫)."""
|
||||
marks = []
|
||||
for at, t in enumerate(texts):
|
||||
sec = _SECTION.match(t)
|
||||
if sec and not re.match(r"^\s*\d+\.\d", t):
|
||||
if not _SKIP_SEC.match(sec.group(2).replace(" ", "")):
|
||||
marks.append((at, sec.group(2).strip()))
|
||||
|
||||
def which(where: Any) -> str:
|
||||
if isinstance(where, str):
|
||||
return where
|
||||
if isinstance(where, int) and not isinstance(where, bool):
|
||||
got = [m for m in marks if m[0] <= where]
|
||||
return got[-1][1] if got else ""
|
||||
return ""
|
||||
|
||||
secs: dict[str, dict[str, Any]] = {}
|
||||
seen = set()
|
||||
for x in res:
|
||||
q, kind = x.get("품"), x.get("갈래")
|
||||
if not q or kind not in ("L", "X") or (x.get("코드"), q, x.get("절")) in seen:
|
||||
continue
|
||||
seen.add((x.get("코드"), q, x.get("절")))
|
||||
title = which(x.get("절"))
|
||||
s = secs.setdefault(title, _part(title, title))
|
||||
if kind == "L" and (who := trade(x.get("이름"))):
|
||||
s["노임"][who] = s["노임"].get(who, 0) + float(q)
|
||||
elif kind == "X":
|
||||
k = eq_key(f"{x.get('이름') or ''} {x.get('규격') or ''}") or f"?{x.get('코드')}"
|
||||
s["장비"][k] = s["장비"].get(k, 0) + float(q)
|
||||
return [s for s in secs.values() if s["노임"] or s["장비"]]
|
||||
title = str(one.get("절") or "")
|
||||
part = out.setdefault(title, _part(title, str(one.get("윗절") or title)))
|
||||
if kind == "L" and (who := one.get("직종") or trade(one.get("이름"))):
|
||||
part["노임"][who] = part["노임"].get(who, 0) + float(q)
|
||||
elif kind == "X" and q < 50:
|
||||
k = eq_key(f"{one.get('이름') or ''} {one.get('규격') or ''}") or f"?{one.get('코드')}"
|
||||
part["장비"][k] = part["장비"].get(k, 0) + float(q)
|
||||
return [p for p in out.values() if p["노임"] or p["장비"]]
|
||||
|
||||
@@ -26,56 +26,56 @@
|
||||
}
|
||||
},
|
||||
"S2": {
|
||||
"단산:46|레미콘타설(장비사용)|철근구조물-소량타설|m3": "14b94bd44235",
|
||||
"단산:46|레미콘타설(장비사용)|철근구조물-소량타설|m3": "fe6524b2bf88",
|
||||
"단산:24|합판거푸집|4회|m2": "dc9227913b7d",
|
||||
"단산:33|기초잡석채집,운반,다짐|발파암활용,덤프15톤,l=500m|m3": "569f93218071",
|
||||
"단산:33|기초잡석채집,운반,다짐|발파암활용,덤프15톤,l=500m|m3": "53d20022ff32",
|
||||
"단산:47|철근가공조립(현장)|type-i(소량시공)|톤": "f333a56a3d0e",
|
||||
"단산:6|야면석채집및운반(계곡내)|굴삭기0.6m3,35cm,4.5톤,l=500m|m2": "06a8c10d89e7",
|
||||
"단산:3|고임돌채집및운반|굴삭기0.6m3(90%),4.5톤,l=500m|m3": "4c1b4b4cb599",
|
||||
"단산:10|야면석소운반|l=35cm|m2": "e96d400b173b",
|
||||
"단산:39|야면석찰붙임|l3=35cm적용,장비사용|m2": "e781e3163b81",
|
||||
"단산:6|야면석채집및운반(계곡내)|굴삭기0.6m3,35cm,4.5톤,l=500m|m2": "c0521e16ff75",
|
||||
"단산:3|고임돌채집및운반|굴삭기0.6m3(90%),4.5톤,l=500m|m3": "2521254f68a8",
|
||||
"단산:10|야면석소운반|l=35cm|m2": "50eb93121621",
|
||||
"단산:39|야면석찰붙임|l3=35cm적용,장비사용|m2": "cf4739ae6e8c",
|
||||
"단산:93|구조물터파기(인력100%)|토사|m3": "c523b43c617b",
|
||||
"단산:12|구조물되메우기(인력100%)||m3": "559c125ef42e",
|
||||
"단산:13|잔토처리(인력100%)||m3": "eb0395c76e02",
|
||||
"단산:22|레미콘타설(장비사용)|무근구조물|m3": "0c3cebbe76de",
|
||||
"단산:22|레미콘타설(장비사용)|무근구조물|m3": "12555a58e991",
|
||||
"단산:26|유로폼|보통,0~7m|m2": "a301adcd84cd",
|
||||
"단산:18|문양거푸집||m2": "9f4efb5510bf",
|
||||
"단산:37|p.v.c파이프설치|ø50mm|m": "a408f8e02b28",
|
||||
"단산:38|다웰바설치|ø32x1000|개": "13410747be6e",
|
||||
"단산:41|휠라설치|t=15mm,1000x1500mm|m2": "4df1b309aca4",
|
||||
"단산:41|휠라설치|t=15mm,1000x1500mm|m2": "23bf202fc7a4",
|
||||
"단산:29|철근가공조립(현장)|연결철근,type-ix30%적용|톤": "2b96b0b322d2",
|
||||
"단산:30|큰돌채집운반|공사중발생석활용50%적용|m2": "a0d7114b5599",
|
||||
"단산:32|큰돌쌓기(메쌓기)|직경40cm이상~60cm미만|m2": "06881edcaad6",
|
||||
"단산:2|뒷채움돌채집운반뒷채움(발파)|굴삭기0.6m3(90%),4.5톤,l=500m|m3": "8d60db527bae",
|
||||
"단산:31|큰돌쌓기(찰쌓기)_배수공|직경40cm이상~60cm미만|m2": "c0610580cae7",
|
||||
"단산:30|큰돌채집운반|공사중발생석활용50%적용|m2": "66cb28bdab1c",
|
||||
"단산:32|큰돌쌓기(메쌓기)|직경40cm이상~60cm미만|m2": "0d884c7f218d",
|
||||
"단산:2|뒷채움돌채집운반뒷채움(발파)|굴삭기0.6m3(90%),4.5톤,l=500m|m3": "ec132e6186ae",
|
||||
"단산:31|큰돌쌓기(찰쌓기)_배수공|직경40cm이상~60cm미만|m2": "b4e9d2ee8233",
|
||||
"단산:21|레미콘타설(인력운반)|무근구조물-소량타설|m3": "7ee6fe1e8d9d",
|
||||
"단산:25|합판거푸집|6회|m2": "b5b6ec2a0beb",
|
||||
"단산:42|도로반사경설치|d=1000(sts)|개소": "e521922cb4ba",
|
||||
"단산:88|야면석메붙임|l3=35cm내외,장비사용|m2": "91ac9e8d76f7",
|
||||
"단산:92|큰돌메붙임|직경40cm이상~60cm미만|m2": "26e6c1b118c5",
|
||||
"단산:91|큰돌찰붙임|직경40cm이상~60cm미만|m2": "bd288a8d830f",
|
||||
"단산:57|레미콘타설(장비사용)|소형구조물|m3": "b4bee2367968",
|
||||
"단산:71|야면석찰쌓기|l3=35cm적용,장비사용|m2": "1649ce3151fb",
|
||||
"단산:43|면고르기||m2": "f45d3e3af178",
|
||||
"단산:42|도로반사경설치|d=1000(sts)|개소": "3c2ee04632d3",
|
||||
"단산:88|야면석메붙임|l3=35cm내외,장비사용|m2": "fbc297bd41b0",
|
||||
"단산:92|큰돌메붙임|직경40cm이상~60cm미만|m2": "d1fad346524d",
|
||||
"단산:91|큰돌찰붙임|직경40cm이상~60cm미만|m2": "269c2c76c667",
|
||||
"단산:57|레미콘타설(장비사용)|소형구조물|m3": "9038aaf1131e",
|
||||
"단산:71|야면석찰쌓기|l3=35cm적용,장비사용|m2": "e74767626ed4",
|
||||
"단산:43|면고르기||m2": "ec9f5d99380f",
|
||||
"줄:대형브레이카|+굴착기(0.7m3)|시간": "fc11668f29eb",
|
||||
"단산:60|와이어메시바닥깔기|#6100x100|m2": "567c16874a89",
|
||||
"단산:62|원형돌망태설치및돌채움|#8,45x45cm|m2": "9ad93f86f385",
|
||||
"단산:61|조약돌채집및운반|굴삭기0.6m3(90%),4.5톤,l=500m|m3": "ab7d8e14de22",
|
||||
"단산:63|돌망태형옹벽설치|사각돌망태|m2": "6487dbf12ab6",
|
||||
"단산:65|콘크리트포장(인력)|t=20cm,종단15%미만|m3": "2c48d8cc2468",
|
||||
"단산:61|조약돌채집및운반|굴삭기0.6m3(90%),4.5톤,l=500m|m3": "b07780f6fab7",
|
||||
"단산:63|돌망태형옹벽설치|사각돌망태|m2": "61535c440543",
|
||||
"단산:65|콘크리트포장(인력)|t=20cm,종단15%미만|m3": "14fb1320b388",
|
||||
"단산:64|비탈면보호공|평떼|m2": "5d9d68fa8679",
|
||||
"단산:66|콘크리트포장(인력)|t=20cm,종단15%이상|m3": "2374789b6ae0",
|
||||
"단산:70|고화매트설치||m2": "80be3cc78047",
|
||||
"단산:90|큰돌쌓기(찰쌓기)|직경40cm이상~60cm미만|m2": "52a0f47bb751",
|
||||
"단산:23|레미콘타설(장비사용)|철근구조물|m3": "fe0d22d627da",
|
||||
"단산:66|콘크리트포장(인력)|t=20cm,종단15%이상|m3": "4739d20909b5",
|
||||
"단산:70|고화매트설치||m2": "61d7220158c9",
|
||||
"단산:90|큰돌쌓기(찰쌓기)|직경40cm이상~60cm미만|m2": "8efce35c90b3",
|
||||
"단산:23|레미콘타설(장비사용)|철근구조물|m3": "4814d3d0bb6f",
|
||||
"단산:73|강관동바리|암거용-1개월사용|공/m3": "bc3f5092d701",
|
||||
"단산:72|철근가공조립(현장)|type-ll|톤": "75dda2f569a2",
|
||||
"단산:74|강관비계|10m이하,3개월사용|m2": "90ce7f0da95c",
|
||||
"단산:49|흙깍기(토사)|굴삭기1.0m3,보통토사|m3": "f9aea56b06bf",
|
||||
"단산:56|잔토정리|굴삭기0.6m3|m3": "9f76f0e32710",
|
||||
"단산:49|흙깍기(토사)|굴삭기1.0m3,보통토사|m3": "a893fe169785",
|
||||
"단산:56|잔토정리|굴삭기0.6m3|m3": "63177d8c31eb",
|
||||
"단산:82|잡철물제작설치|규격철물설치(일반철재)|톤": "531650fd115f",
|
||||
"단산:95|도장(철재면2회)|방청1,유성2|m2": "92867210fce0",
|
||||
"단산:81|임도안전표지판설치|삼각,900mm|개소": "ee09c0727967"
|
||||
"단산:81|임도안전표지판설치|삼각,900mm|개소": "b28761e99520"
|
||||
}
|
||||
},
|
||||
"북부청사방": {
|
||||
@@ -106,8 +106,8 @@
|
||||
}
|
||||
},
|
||||
"S2": {
|
||||
"단산:22|레미콘타설(장비사용)|철근구조물|m3": "e2940f403c6c",
|
||||
"단산:20|레미콘타설(장비사용)|무근구조물|m3": "d0347d9e519e",
|
||||
"단산:22|레미콘타설(장비사용)|철근구조물|m3": "a35e3f7819d4",
|
||||
"단산:20|레미콘타설(장비사용)|무근구조물|m3": "6caceddb40b1",
|
||||
"단산:38|유로폼(euroform)|6회,보통|m2": "0cd2081fa29f",
|
||||
"단산:53|철근가공조립(현장)|type-ii|톤": "d6d92c499d78",
|
||||
"단산:4|강관비계|(10m이하)|m2": "7c1c9f2d283a",
|
||||
@@ -115,44 +115,44 @@
|
||||
"단산:37|유로폼(euroform)|6회,간단|m2": "d6ac6fccfcfd",
|
||||
"단산:52|철근가공조립(현장)|type-i(소량)|톤": "e3d8470f9851",
|
||||
"단산:49|철강재운반(할증포함)|(구역화물)|톤": "a619ce7fe816",
|
||||
"단산:51|철골설치||톤": "8e292d7859f8",
|
||||
"단산:50|철골가공조립(공장)|(대형)|톤": "b386a69202ef",
|
||||
"단산:51|철골설치||톤": "f8b5b9b49bf5",
|
||||
"단산:50|철골가공조립(공장)|(대형)|톤": "ebe8740e0a1e",
|
||||
"단산:42|잡철물제작||톤": "ef730a79e03f",
|
||||
"단산:67|합판거푸집(6회)||m2": "b3ba20147436",
|
||||
"단산:33|와이어메시바닥깔기||m2": "2f898ef1909d",
|
||||
"단산:11|구조물터파기(토사)||m3": "5b2c549e3ec2",
|
||||
"단산:8|구조물되메우기및다짐||m3": "184c85a346db",
|
||||
"단산:39|잔토처리||m3": "147afeac7acd",
|
||||
"단산:21|레미콘타설(장비사용)|무근구조물-소량타설|m3": "6b7edafbfd22",
|
||||
"단산:11|구조물터파기(토사)||m3": "d104c4f35461",
|
||||
"단산:8|구조물되메우기및다짐||m3": "4b834cdae99f",
|
||||
"단산:39|잔토처리||m3": "62e430cfb337",
|
||||
"단산:21|레미콘타설(장비사용)|무근구조물-소량타설|m3": "32bb4fda75e2",
|
||||
"단산:66|합판거푸집(4회)||m2": "39bdcddca57c",
|
||||
"단산:12|기초잡석다짐및채집운반|(9:1)(0.6m3+4.5ton)|m3": "dc7a46817506",
|
||||
"단산:26|배수관부설|(d=1000mm)|m": "e6f492785d36",
|
||||
"단산:12|기초잡석다짐및채집운반|(9:1)(0.6m3+4.5ton)|m3": "24dcf4cf1ac0",
|
||||
"단산:26|배수관부설|(d=1000mm)|m": "7c06bc3e3a91",
|
||||
"호표:7": "9238de708278",
|
||||
"단산:19|레미콘타설(인력운반)|무근구조물-소량타설|m3": "728a28bc9224",
|
||||
"단산:9|구조물터파기|인력|m3": "06855a4c159a",
|
||||
"단산:7|구조물되메우기|인력|m3": "8d23c8175496",
|
||||
"단산:40|잔토처리|인력|m3": "e62dedcec317",
|
||||
"단산:40|잔토처리|인력|m3": "e488fa518c11",
|
||||
"단산:35|원형돌망태조립설치|산림사업품셈|조": "05d490e21236",
|
||||
"단산:36|원형돌망태채움|산림사업품셈|조": "a4b290a399fd",
|
||||
"단산:43|조약돌채집운반|(9:1)(0.6m3+4.5ton)|m3": "3d10f7bb409b",
|
||||
"단산:10|구조물터파기(암)||m3": "884a1241cb59",
|
||||
"단산:27|배수관부설|(d=500mm)|m": "e00f055d8b21",
|
||||
"단산:18|레미콘타설(무근펌프카)(130m3)|41m(80-95m3/hr)|m3": "07d3e7ce5cf4",
|
||||
"단산:36|원형돌망태채움|산림사업품셈|조": "c8e1f0314e45",
|
||||
"단산:43|조약돌채집운반|(9:1)(0.6m3+4.5ton)|m3": "1cc5362dc3b4",
|
||||
"단산:10|구조물터파기(암)||m3": "0d821feb9c44",
|
||||
"단산:27|배수관부설|(d=500mm)|m": "964f47b3dd45",
|
||||
"단산:18|레미콘타설(무근펌프카)(130m3)|41m(80-95m3/hr)|m3": "6da4e3284f73",
|
||||
"줄:콘크리트펌프차|41m,80~95m3/hr|시간": "6dfaed1dd20a",
|
||||
"단산:25|문양거푸집||m2": "fba1f776a83c",
|
||||
"단산:46|지수판설치|산림사업품셈|m": "3ddb72dcd81d",
|
||||
"단산:1|p.v.c파이프설치|산림사업품셈|m": "ec1cd531e2e6",
|
||||
"단산:17|떼붙임(평떼)|산림사업품셈|m2": "fdd2e6bd02f0",
|
||||
"단산:60|큰돌메쌓기|40cm-60cm,(산림사업품셈)|m2": "b44908334fae",
|
||||
"단산:5|고임돌채집및운반|(9:1)(0.6m3+4.5ton)|m3": "6d67aa013e60",
|
||||
"단산:16|뒷채움잡석채집운반다짐|(9:1)(0.6m3+4.5ton)|m3": "2f37d4203fdc",
|
||||
"단산:62|큰돌찰쌓기|40cm-60cm,(산림사업품셈)|m2": "6b65c0b531df",
|
||||
"단산:63|큰돌채집운반|산림사업품셈|m2": "09662a8f8d36",
|
||||
"단산:13|뒷채움잡석구입운반||m3": "797df59d7756",
|
||||
"단산:15|뒷채움잡석뒷채움||m3": "012a67631d07",
|
||||
"단산:14|뒷채움잡석구입운반|(창봉기번3)(2차)|m3": "42b81776b3e4",
|
||||
"단산:59|큰돌메붙임|40cm-60cm,(산림사업품셈)|m2": "cc09e80f4533",
|
||||
"단산:61|큰돌찰붙임|40cm-60cm,(산림사업품셈)|m2": "469d3c56b621"
|
||||
"단산:17|떼붙임(평떼)|산림사업품셈|m2": "0319c3fc3b39",
|
||||
"단산:60|큰돌메쌓기|40cm-60cm,(산림사업품셈)|m2": "83f1a9a2b201",
|
||||
"단산:5|고임돌채집및운반|(9:1)(0.6m3+4.5ton)|m3": "6f841c019f4e",
|
||||
"단산:16|뒷채움잡석채집운반다짐|(9:1)(0.6m3+4.5ton)|m3": "eb182b275f99",
|
||||
"단산:62|큰돌찰쌓기|40cm-60cm,(산림사업품셈)|m2": "3045ba240634",
|
||||
"단산:63|큰돌채집운반|산림사업품셈|m2": "35a75e3957bb",
|
||||
"단산:13|뒷채움잡석구입운반||m3": "c425998ad058",
|
||||
"단산:15|뒷채움잡석뒷채움||m3": "f9d5674557e8",
|
||||
"단산:14|뒷채움잡석구입운반|(창봉기번3)(2차)|m3": "8a37a388d59a",
|
||||
"단산:59|큰돌메붙임|40cm-60cm,(산림사업품셈)|m2": "1aefe1f21b3b",
|
||||
"단산:61|큰돌찰붙임|40cm-60cm,(산림사업품셈)|m2": "b140bb188ade"
|
||||
}
|
||||
},
|
||||
"거창": {
|
||||
@@ -188,26 +188,26 @@
|
||||
"호표:1": "1f6345e0085e",
|
||||
"호표:2": "dacbaeeaa6c9",
|
||||
"호표:3": "03d873100e35",
|
||||
"단산:30|유로폼|보통,0~7m|m2": "e278a75b7213",
|
||||
"단산:38|철근가공및조립(현장)|type-ii-1|톤": "4ea51eae487f",
|
||||
"단산:39|강관비계|10m이하(3개월)|m2": "769cb5147b54",
|
||||
"단산:29|유로폼|간단,0~7m|m2": "dcf229abaa91",
|
||||
"단산:30|유로폼|보통,0~7m|m2": "8e45789b4266",
|
||||
"단산:38|철근가공및조립(현장)|type-ii-1|톤": "f65df34c4c3e",
|
||||
"단산:39|강관비계|10m이하(3개월)|m2": "a96380c10324",
|
||||
"단산:29|유로폼|간단,0~7m|m2": "cb2624477569",
|
||||
"호표:10": "fc2cc4cd2868",
|
||||
"호표:11": "998a0263fed9",
|
||||
"단산:36|레미콘타설(장비사용타설)|무근구조물|m3": "39982206412d",
|
||||
"단산:40|레미콘타설(장비사용타설)|철근구조물|m3": "41c11b60b1e1",
|
||||
"단산:41|합판거푸집|4회|m2": "dfd5ab302d05",
|
||||
"단산:36|레미콘타설(장비사용타설)|무근구조물|m3": "c0b9cfa60d77",
|
||||
"단산:40|레미콘타설(장비사용타설)|철근구조물|m3": "198ef508476c",
|
||||
"단산:41|합판거푸집|4회|m2": "965f0578c65a",
|
||||
"단산:31|철근가공및조립(현장)|type-i-1(소형)|톤": "a98e998b35c0",
|
||||
"단산:42|강관동바리|2.5m이하(1개월)|공m3": "99d65ebd3dcc",
|
||||
"단산:43|잡철물제작및설치(현장)|일반철재|톤": "71c1f965449f",
|
||||
"단산:44|안내표지판설치|단주식|개소": "38427ebd6d28",
|
||||
"단산:42|강관동바리|2.5m이하(1개월)|공m3": "15e8a1fae939",
|
||||
"단산:43|잡철물제작및설치(현장)|일반철재|톤": "9803bb826c42",
|
||||
"단산:44|안내표지판설치|단주식|개소": "3c6f75841d3b",
|
||||
"단산:37|레미콘타설(인력운반타설)|소형구조물|m3": "6c2df8e4fd6a",
|
||||
"단산:45|합판거푸집|6회,간단|m2": "85dcc3e13e28"
|
||||
"단산:45|합판거푸집|6회,간단|m2": "ec2e16734886"
|
||||
}
|
||||
},
|
||||
"봉화": {
|
||||
"S1": {
|
||||
"해시": "fcf03906734a",
|
||||
"해시": "4c3ba7796291",
|
||||
"꼴": "STmate",
|
||||
"호표": 45,
|
||||
"줄": 209,
|
||||
@@ -236,36 +236,36 @@
|
||||
"S2": {
|
||||
"줄:굴삭기(타이어)|0.6m3,(부착용집게)|시간": "7ba959ec2e9d",
|
||||
"호표:1": "ee8a116be0f0",
|
||||
"단산:96|레미콘타설(장비)|무근,소형|m3": "fcbfc0642dc2",
|
||||
"단산:96|레미콘타설(장비)|무근,소형|m3": "fed15560be74",
|
||||
"단산:102|합판거푸집(간단)|간단|m2": "70042bc48cb6",
|
||||
"단산:4|글자새김|자당6x6|자": "ff7e1309d192",
|
||||
"단산:2|구조물터파기(토사)|굴삭기0.2m3|m3": "2ce888963761",
|
||||
"단산:7|구조물되메우기|굴삭기0.2m3|m3": "c72b4a1494d6",
|
||||
"단산:49|잔토처리|굴삭기0.2m3|m3": "800b67112b8d",
|
||||
"단산:2|구조물터파기(토사)|굴삭기0.2m3|m3": "cbead3149b3c",
|
||||
"단산:7|구조물되메우기|굴삭기0.2m3|m3": "3ff7d6c09304",
|
||||
"단산:49|잔토처리|굴삭기0.2m3|m3": "bc633bad0c98",
|
||||
"호표:5": "566b3ec95fef",
|
||||
"단산:43|깬잡석채집|l=45cm내외|m2": "6c67d6e7d946",
|
||||
"단산:5|고임돌채집|기계(0.7m3,100%)|m3": "304739062d51",
|
||||
"단산:6|막자갈채집|기계(0.7m3,100%)|m3": "b149f96f7918",
|
||||
"단산:8|기초다짐및뒤채움|소형장비사용시공|m3": "d1e2a67c4989",
|
||||
"단산:43|깬잡석채집|l=45cm내외|m2": "9382675c6d66",
|
||||
"단산:5|고임돌채집|기계(0.7m3,100%)|m3": "e6a8fdb7c927",
|
||||
"단산:6|막자갈채집|기계(0.7m3,100%)|m3": "d862c41aa425",
|
||||
"단산:8|기초다짐및뒤채움|소형장비사용시공|m3": "77b469a4fd17",
|
||||
"단산:93|유로폼설치및해체|간단|m2": "5174b34972ac",
|
||||
"단산:95|레미콘타설(장비)|무근|m3": "1e45fdfca996",
|
||||
"단산:89|콘크리트믹서사용|0.45m3|m3": "71825ddc35da",
|
||||
"단산:95|레미콘타설(장비)|무근|m3": "bff32ab35cc6",
|
||||
"단산:89|콘크리트믹서사용|0.45m3|m3": "1538c2627ebb",
|
||||
"단산:94|유로폼설치및해체|보통|m2": "1985ee7ba47c",
|
||||
"단산:98|철근가공및조립|type-i-1,현장가공조립|톤": "f91f3febebf1",
|
||||
"호표:9": "992b00a50c65",
|
||||
"호표:13": "2ecc59f87b37",
|
||||
"단산:51|깬잡석채집|l=30cm내외|m2": "d23b63b9635a",
|
||||
"단산:12|콘크리트(기계비빔)|무근구조물|m3": "c9ca9357a44a",
|
||||
"단산:51|깬잡석채집|l=30cm내외|m2": "e2b6831be198",
|
||||
"단산:12|콘크리트(기계비빔)|무근구조물|m3": "c74e9c324b2c",
|
||||
"호표:17": "b50496d3f922",
|
||||
"호표:18": "d796d9bbd4bd",
|
||||
"호표:22": "2c27c0d9b1e1",
|
||||
"단산:23|구조물터파기(육상토사0~1m)|기계90%+인력10%(굴삭기0.7m3)|m3": "1e4b4e03372f",
|
||||
"단산:24|잔토처리|굴삭기0.7m3|m3": "b34905b7e0c2",
|
||||
"단산:23|구조물터파기(육상토사0~1m)|기계90%+인력10%(굴삭기0.7m3)|m3": "db3c5678df4f",
|
||||
"단산:24|잔토처리|굴삭기0.7m3|m3": "d315c818fc0f",
|
||||
"줄:굴삭기(타이어)|0.6m3|시간": "733bdb0730f2",
|
||||
"호표:24": "24b7a61d2cac",
|
||||
"단산:3|깬잡석채집|l=35cm내외|m2": "6fa38565606b",
|
||||
"단산:3|깬잡석채집|l=35cm내외|m2": "77cbf6e4a2ff",
|
||||
"호표:26": "253290e88b9f",
|
||||
"단산:52|깬잡석채집|l=60cm내외|m2": "daab119d679a",
|
||||
"단산:52|깬잡석채집|l=60cm내외|m2": "44b9b3dcffc3",
|
||||
"호표:28": "10cf341d2ca3",
|
||||
"단산:81|철근가공및조립(현장)|일반(type-i-1)|톤": "39d3a7e2e7a9",
|
||||
"호표:31": "82b74ab253f3",
|
||||
@@ -273,11 +273,11 @@
|
||||
"호표:33": "35144fc92a5c",
|
||||
"호표:34": "9b6cffd83699",
|
||||
"호표:35": "4eb05246e0c3",
|
||||
"단산:10|레미콘타설(장비사용)|소형구조물|m3": "8717ad5b8652",
|
||||
"단산:10|레미콘타설(장비사용)|소형구조물|m3": "640411e0f0a8",
|
||||
"단산:90|평떼붙임||m2": "13cce5a5166b",
|
||||
"단산:91|단끊기||m": "8701e94849ea",
|
||||
"단산:97|레미콘타설(장비)|철근|m3": "6c8931bf2865",
|
||||
"단산:103|레미콘타설(장비)|무근,버림|m3": "c99fd33aad21",
|
||||
"단산:97|레미콘타설(장비)|철근|m3": "d419cafa4932",
|
||||
"단산:103|레미콘타설(장비)|무근,버림|m3": "773debae71b8",
|
||||
"단산:99|철근가공및조립|type-ii-1,현장가공조립|톤": "e6dc6a25683b",
|
||||
"단산:113|강관동바리|암거용-3개월사용|공/m3": "55bd7c4f030a",
|
||||
"단산:112|강관비계|3개월사용|m2": "2390793aeb4b",
|
||||
@@ -320,41 +320,41 @@
|
||||
"단산:9|잔토처리|인력100%|m3": "aef7e1b84e30",
|
||||
"줄:크레인(타이어)|10톤|시간": "96a1f07e863b",
|
||||
"호표:3": "6c59b8689986",
|
||||
"단산:3|구조물터파기|토사(b/h90%+인력10%)|m3": "acefa16cc78a",
|
||||
"단산:10|잔토처리|토사(b/h100%)|m3": "c425e33125c6",
|
||||
"단산:37|콘크리트타설|임도품셈5-5|m3": "65c3e427122f",
|
||||
"단산:3|구조물터파기|토사(b/h90%+인력10%)|m3": "7953545f58b8",
|
||||
"단산:10|잔토처리|토사(b/h100%)|m3": "fa9015056847",
|
||||
"단산:37|콘크리트타설|임도품셈5-5|m3": "2d0b3ca0824f",
|
||||
"단산:59|유로폼설치,해체(복잡)|수직고7.0m이하|m2": "6b60ee0cf532",
|
||||
"단산:60|철근가공조립(현장)|typei|톤": "d1c00f23be62",
|
||||
"단산:600|기초잡석채집및운반부설|4.5톤(l=500)|m3": "91acb80ac602",
|
||||
"단산:1926|임도안전표지판설치|삼각,d=900|개소": "de7833d5056a",
|
||||
"단산:411|야면석채집운반|(25x25x35cm)장비|m2": "aeaa86e728d4",
|
||||
"단산:644|야면석찰붙임|뒷길이35cm|m2": "6771a23d197b",
|
||||
"단산:1361|고임돌채집운반|4.5톤덤프(l=500)|m3": "203a9315635b",
|
||||
"단산:1928|잡석운반및다짐||m3": "3a7b79a11b47",
|
||||
"단산:1927|면고르기||m2": "3d642227dc7b",
|
||||
"단산:600|기초잡석채집및운반부설|4.5톤(l=500)|m3": "4784bc1b14b0",
|
||||
"단산:1926|임도안전표지판설치|삼각,d=900|개소": "e05b7a689c1d",
|
||||
"단산:411|야면석채집운반|(25x25x35cm)장비|m2": "545af7826ba4",
|
||||
"단산:644|야면석찰붙임|뒷길이35cm|m2": "20cfc54d1cb3",
|
||||
"단산:1361|고임돌채집운반|4.5톤덤프(l=500)|m3": "09f75762e692",
|
||||
"단산:1928|잡석운반및다짐||m3": "7c507cfd7d53",
|
||||
"단산:1927|면고르기||m2": "5d24952044bf",
|
||||
"단산:1942|구조물터파기|인력(경질토사)|m3": "b25a97237575",
|
||||
"단산:1759|도로반사경설치|d=1000(sts)|개소": "8f206fca6ae6",
|
||||
"단산:2071|철근콘크리트타설|장비사용타설|m3": "7a4603d42158",
|
||||
"단산:2071|철근콘크리트타설|장비사용타설|m3": "998daf7e771a",
|
||||
"단산:58|유로폼설치,해체(보통)|수직고7.0m이하|m2": "de90b05e1449",
|
||||
"단산:1854|지수판설치(pvc용접)|200x5t|m": "3aec0042517c",
|
||||
"단산:1855|배수관부설및접합(p.v.c)|ø50mm-vg2일반관|m": "7200d398af99",
|
||||
"단산:1466|야면석소운반(l=35cm)|(l=20m)|m2": "79a4f66ac624",
|
||||
"단산:450|콘크리트소운반|(l=20m)|m3": "9d06f1876010",
|
||||
"단산:1843|야면석메붙임|뒷길이35cm|m2": "2e66aa2c2197",
|
||||
"단산:258|측구수로관부설|500∼700kg미만|m": "56683ae7c5bb",
|
||||
"단산:1844|콘크리트타설|장비사용타설(소형)|m3": "cdd8f38898be",
|
||||
"단산:1843|야면석메붙임|뒷길이35cm|m2": "e366c880bdb0",
|
||||
"단산:258|측구수로관부설|500∼700kg미만|m": "d7100836ca16",
|
||||
"단산:1844|콘크리트타설|장비사용타설(소형)|m3": "2d506172eebd",
|
||||
"단산:1841|합판거푸집(4회)|보통|m2": "154931aee1b7",
|
||||
"단산:2001|혼합골재부설|영월-산불|m3": "3c98157c0505",
|
||||
"단산:2001|혼합골재부설|영월-산불|m3": "64a7980290a9",
|
||||
"단산:1748|콘크리트포장(인력)|t=20cm|m3": "68e42fbf6871",
|
||||
"단산:1752|콘크리트포장절단공|임도|m": "50eb38496dbf",
|
||||
"단산:1519|뒷채움돌채집운반부설|4.5톤덤프트럭(l=500m)|m3": "17e1fe46f3c8",
|
||||
"단산:81|큰돌메쌓기|뒷길이0.6~0.8m|m2": "99f8c85542df",
|
||||
"단산:1810|큰돌채집운반(뒷길이0.6~0.8m)|장비(80%)+인력(20%)|m2": "8c9b791dab4b"
|
||||
"단산:1752|콘크리트포장절단공|임도|m": "35f09be77f1b",
|
||||
"단산:1519|뒷채움돌채집운반부설|4.5톤덤프트럭(l=500m)|m3": "4bcbbc780730",
|
||||
"단산:81|큰돌메쌓기|뒷길이0.6~0.8m|m2": "3c6626a3d944",
|
||||
"단산:1810|큰돌채집운반(뒷길이0.6~0.8m)|장비(80%)+인력(20%)|m2": "374e7a1f93a3"
|
||||
}
|
||||
},
|
||||
"울진대흥": {
|
||||
"S1": {
|
||||
"해시": "a2052e0b170c",
|
||||
"해시": "2a378597aa15",
|
||||
"꼴": "STmate",
|
||||
"호표": 34,
|
||||
"줄": 171,
|
||||
@@ -387,26 +387,26 @@
|
||||
"호표:4": "9b0429327796",
|
||||
"호표:5": "5e88ce0e4c68",
|
||||
"호표:6": "31bd5eea19c8",
|
||||
"단산:75|콘크리트믹서사용|0.45m3|m3": "bb028865ae10",
|
||||
"단산:75|콘크리트믹서사용|0.45m3|m3": "b470c79eb976",
|
||||
"호표:7": "3476f12e55bf",
|
||||
"호표:8": "b4f5087c15f6",
|
||||
"호표:9": "51afe3b087b1",
|
||||
"호표:10": "fb1dd2cf4c6a",
|
||||
"줄:굴삭기(0.7m3)||시간": "9c5792c9e5eb",
|
||||
"호표:11": "af2de3ed3f1c",
|
||||
"단산:1124|고임돌채집|기계|m3": "d92fb83f5e54",
|
||||
"단산:159|뒷채움|150m/m내외|m3": "6c3ab2cbc163",
|
||||
"단산:959|막자갈채집|기계|m3": "f84d5f824e2d",
|
||||
"단산:1124|고임돌채집|기계|m3": "ae817a2ac5a0",
|
||||
"단산:159|뒷채움|150m/m내외|m3": "08709d597e93",
|
||||
"단산:959|막자갈채집|기계|m3": "eacff8bea289",
|
||||
"단산:2200|콘크리트비빔|채움재|m3": "91472fd9fe57",
|
||||
"줄:트럭탑재형크레인|5톤|시간": "71cbed958600",
|
||||
"호표:17": "ad84568a1c33",
|
||||
"호표:18": "6f1c4c076fd4",
|
||||
"단산:1129|야면석채집|l3=45cm내외|m2": "629829a9d347",
|
||||
"단산:2119|야면석채집|l3=35cm내외|m2": "245dfe68e79f",
|
||||
"단산:1343|구조물터파기(토사)|굴삭기0.7m3|m3": "aa20475c3d30",
|
||||
"단산:2139|다지기|굴삭기0.7m3|m3": "e1a21a5a7744",
|
||||
"단산:1129|야면석채집|l3=45cm내외|m2": "a59e92e86b67",
|
||||
"단산:2119|야면석채집|l3=35cm내외|m2": "b0e17ce1bcaf",
|
||||
"단산:1343|구조물터파기(토사)|굴삭기0.7m3|m3": "72abcf573fe3",
|
||||
"단산:2139|다지기|굴삭기0.7m3|m3": "2718acaec93b",
|
||||
"단산:2195|유로폼설치및해체|간단|m2": "2ada005f71d9",
|
||||
"단산:2262|레미콘타설|무근(장비사용타설)|m3": "0011fde861d6",
|
||||
"단산:2262|레미콘타설|무근(장비사용타설)|m3": "fca8b8959999",
|
||||
"호표:28": "fd2ef8bc220b",
|
||||
"줄:굴삭기(타이어)|0.6m3,(부착용집게,0.7~0.8m3)|시간": "94a580e075dd",
|
||||
"호표:29": "02e551e5e700",
|
||||
@@ -448,7 +448,7 @@
|
||||
"호표:6": "e3b9e469607e",
|
||||
"호표:7": "1cd3bb8c5e6d",
|
||||
"호표:8": "f5f08c081e83",
|
||||
"단산:150|고임돌채집|기계|m3": "6156ce99f6a7",
|
||||
"단산:150|고임돌채집|기계|m3": "7de261f760cd",
|
||||
"호표:10": "c5b7c43c3ee1",
|
||||
"단산:1453|인력운반|기타|톤": "355f5eac9936",
|
||||
"호표:12": "c46a6d7e568f",
|
||||
@@ -457,7 +457,7 @@
|
||||
},
|
||||
"울진소광": {
|
||||
"S1": {
|
||||
"해시": "af7a97c94ed7",
|
||||
"해시": "bd1310dccd12",
|
||||
"꼴": "STmate",
|
||||
"호표": 25,
|
||||
"줄": 117,
|
||||
@@ -498,17 +498,17 @@
|
||||
"호표:12": "39f4044d3634",
|
||||
"줄:굴삭기(타이어)|0.6m3|시간": "733bdb0730f2",
|
||||
"호표:13": "af5f52dc7ecb",
|
||||
"단산:1456|구조물터파기(토사)|굴착기0.6m3|m3": "5369cb770862",
|
||||
"단산:1474|구조물되메우기|굴착기0.6m3|m3": "c42bf66f9e58",
|
||||
"단산:1475|구조물잔토처리|굴착기0.6m3|m3": "ea935c401ec0",
|
||||
"단산:492|채움콘크리트|기계비빔|m3": "76df4f4c944d",
|
||||
"단산:91|고임돌채집|굴삭기0.7m3|m3": "445470e48368",
|
||||
"단산:408|막자갈(파쇄석)채집||m3": "6f29521df826",
|
||||
"단산:867|막자갈채우기|150mm내외|m3": "f77710424c09",
|
||||
"단산:1336|레미콘타설|무근(장비)|m3": "5ff7079ff5a0",
|
||||
"단산:1456|구조물터파기(토사)|굴착기0.6m3|m3": "0973406f3e2a",
|
||||
"단산:1474|구조물되메우기|굴착기0.6m3|m3": "d7cddaa3d1ba",
|
||||
"단산:1475|구조물잔토처리|굴착기0.6m3|m3": "5827fbbcc4b8",
|
||||
"단산:492|채움콘크리트|기계비빔|m3": "07f036449ef1",
|
||||
"단산:91|고임돌채집|굴삭기0.7m3|m3": "d46bfcbe8564",
|
||||
"단산:408|막자갈(파쇄석)채집||m3": "946bdf1420ed",
|
||||
"단산:867|막자갈채우기|150mm내외|m3": "d2df9375e449",
|
||||
"단산:1336|레미콘타설|무근(장비)|m3": "9991557bff22",
|
||||
"단산:918|채움con'c|기계비빔|m3": "0c75dd9a29d2",
|
||||
"단산:1448|파쇄석채집|l3=35cm내외|m2": "5d1cf0f37342",
|
||||
"단산:1030|고임돌채집|굴삭기0.2m3|m3": "f483cf51e53b",
|
||||
"단산:1448|파쇄석채집|l3=35cm내외|m2": "8f7299995319",
|
||||
"단산:1030|고임돌채집|굴삭기0.2m3|m3": "669ef7170a82",
|
||||
"단산:1354|콘크리포장(t=20cm)포설|인력포설|m3": "ec05dd9a542f",
|
||||
"단산:794|단끊기|절취없음|m": "6aa07c8cba17",
|
||||
"단산:1329|평떼붙이기||m2": "0522e960d244",
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
"""338-1 엑셀 지문(`M02_Hopyo_Finger` · `_Words` · `_Shares`) — 식 풀기 일곱 꼴 · 합판 지문 · 고임돌
|
||||
섞음 ·
|
||||
몫 · STmate 자원 · 기계 줄 · 직접 줄 호표. 실물 = 북부청 임도(읽기만)."""
|
||||
섞음 · 몫 · STmate 단위 줄 · 기계 줄 · 직접 줄 호표. 지문 = 블록 단위 줄 투영(776-3 · 형식 모름).
|
||||
실물 = 북부청 임도(읽기만)."""
|
||||
|
||||
import pytest
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Finger as finger
|
||||
from M02_MasterTemplete import M02_Hopyo_ReadEst as est
|
||||
from M02_MasterTemplete import M02_Hopyo_Shares as shares
|
||||
from M02_MasterTemplete.M02_Hopyo_Words import conds, mtypes, parse_rhs
|
||||
from resources.tester.helper_m02_hopyo_golden import line_of
|
||||
@@ -12,6 +13,11 @@ from resources.tester.helper_m02_hopyo_golden import line_of
|
||||
approx = pytest.approx
|
||||
|
||||
|
||||
def _est(text: list[str]) -> dict:
|
||||
"""EST 글 → 블록(단위 줄 = 어댑터 `ReadEst.items`)."""
|
||||
return {"번": 1, "이름": "시험", "단위": "㎥", "글": text, "항목": est.items(text)}
|
||||
|
||||
|
||||
# ── 식 풀기 ────────────────────────────────────────────────────────────
|
||||
def test_식_단가_떼고_품():
|
||||
got = parse_rhs("273,540 × 3인 ÷ 55㎥ × 2 = 29,840.7")
|
||||
@@ -44,7 +50,7 @@ def test_식_횟수_품할증():
|
||||
|
||||
def test_식_못_풀면_역산():
|
||||
assert parse_rhs("273,540 × P = 30,000") is None
|
||||
block = {"번": 1, "이름": "시험", "단위": "㎥", "글": [" 특별인부 : 273,540 × P = 27,354"]}
|
||||
block = _est([" 특별인부 : 273,540 × P = 27,354"])
|
||||
fp = finger.excel(block, {"이름": "시험", "규격": "", "단위": "㎥"})
|
||||
assert fp["인력"]["특별인부"]["품"] == approx(0.1) and fp["인력"]["특별인부"]["어떻게"] == [
|
||||
"역산"
|
||||
@@ -94,28 +100,21 @@ def test_몫_절마다_노임_장비():
|
||||
" 3. 합 계",
|
||||
" 보통인부 : 172,068 × 1 = 172,068",
|
||||
]
|
||||
parts = shares.sections(texts)
|
||||
parts = shares.sections(est.items(texts))
|
||||
assert [p["제목"] for p in parts] == ["굴착기 0.6㎥ 채집(90%)", "인력채집(10%)"]
|
||||
assert parts[0]["장비"] == {"굴착기|0.6": approx(0.9 / 23.1)}
|
||||
assert parts[1]["노임"] == {"보통인부": approx(0.03)}
|
||||
|
||||
|
||||
def test_STmate_자원_지문():
|
||||
def test_STmate_단위_줄_지문():
|
||||
qty = {"율": [], "인자": [], "어떻게": "QTY"}
|
||||
block = {
|
||||
"글": ["잔토처리 (기계100%) 건설표준품셈9-3", "q = 0.7 , K = 0.9"],
|
||||
"자원": [
|
||||
{"갈래": "L", "코드": "L00002", "이름": "보통인부", "규격": "", "품": 0.2},
|
||||
*[
|
||||
{
|
||||
"갈래": "X",
|
||||
"코드": "X00205",
|
||||
"이름": "굴삭기(무한궤도)",
|
||||
"규격": "0.7㎥",
|
||||
"품": 0.0229,
|
||||
}
|
||||
]
|
||||
* 3,
|
||||
{"갈래": "M", "코드": "M00001", "이름": "모래", "규격": "", "품": 1.2},
|
||||
"항목": [
|
||||
{"묶음": "L", "이름": "보통인부", "규격": "", "직종": "보통인부", "품": 0.2, **qty},
|
||||
{"묶음": "X", "이름": "굴삭기(무한궤도)", "규격": "0.7㎥", "품": 0.0229, **qty},
|
||||
{"묶음": "M", "이름": "모래", "규격": "", "품": 1.2, "밑품": 1.2, **qty},
|
||||
{"묶음": "L", "이름": "보통인부", "규격": "", "품": 0.5, "밖": True, **qty},
|
||||
],
|
||||
}
|
||||
fp = finger.excel(block, {"이름": "잔토처리", "규격": "토사", "단위": "M3"})
|
||||
|
||||
@@ -87,7 +87,7 @@ def test_조합_근거_안_닿은_덧은_후보(got):
|
||||
|
||||
def test_숫자_없음_옛_HP(got):
|
||||
block, line = line_of("N000")
|
||||
r = verdict.block({**block, "글": []}, line, got)
|
||||
r = verdict.block({**block, "글": [], "항목": []}, line, got) # 단위 줄 · 글 없음
|
||||
assert r["판정"]["상태"] == "후보" and r["판정"]["글"] == fit.NO_NUMBER
|
||||
assert r["차례"][0] == "GC000198" # 이름 · 근거 차례 그대로
|
||||
|
||||
|
||||
Reference in New Issue
Block a user