fix(M02 실무자료): 361-1 지역만 다른 자재 = 전국 — 줄 글 지역 · (전국) 품목 · 없으면 키 없는 전국 자리표 · 고르기 창 지역 변형 접음 · 전국 되돌림 · 만들기 본뜬.전국자재
- 맞춤: 줄 글 지역 = 그 지역 변형(꼬리표 「지역 — 줄 글」) · 지역 없는 변형((전국) 품목 먼저) · 없으면(레미콘) 「전국」 자리표(바탕 이름(전국)-관급/사급 · 규격 · 단위 · 키 · 단가 없음 · 꼬리표 「전국 — 지역은 프로젝트에서」) · 옛 「못 가림 — N개 중 첫째」 없앰
- 후보 = 그 품목 변형 모두(`변형`) · 고르기 창 1위 / 전국 / 지역 변형(접힘) · 대상 꼴 `{전국: true}`
- 미리 셈 · 만들기: 전국 대상은 서버가 줄에서 다시 셈 · 일위대가 자재 칸이 M01 키만 받아 사전조합 `본뜬.전국자재` 에 적고 빠진 줄 까닭 · 장부엔 안 남김 · 만들기 몸 꼴에 `전국` 칸
- 시험: 전국 · 줄 글 지역 · (전국) 품목 · 고르기 지역 → 전국 되돌림 · 만들기 결과 · 화면 도우미
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
This commit is contained in:
@@ -6,8 +6,12 @@
|
||||
자재 규격 수에 있는 몫(단위 배 1 · 10 · 1000 · 2 % 허용 · 면목 0.03 m = 30 mm) ·
|
||||
줄 괄호 글 낱말(「몸통」)은 자재 이름에 들어야 함.
|
||||
맞음 = ① 이름 · 규격 같음 하나(`Match._exact`) ② 바탕 이름 · 단위 · 규격 맞음 1 이 한 품목뿐
|
||||
(지역 · 관급/사급만 다른 변형 = 한 품목 · 같은 단위 먼저 · 줄 글의 관급/사급 · 지역 먼저 ·
|
||||
없으면 별도계상 줄 = 관급 · 재료비 줄 = 사급 · 지역 없는 것 · M01 차례 → 꼬리표 「못 가림」).
|
||||
(지역 · 관급/사급만 다른 변형 = 한 품목 · 같은 단위 먼저 · 줄 글의 관급/사급 먼저 · 없으면
|
||||
별도계상 줄 = 관급 · 재료비 줄 = 사급).
|
||||
지역(361-1) = 줄 글에 지역 낱말 → 그 지역 변형(꼬리표 「지역 — 줄 글」) · 없으면 지역 없는
|
||||
변형((전국) 품목) · 그것도 없으면(레미콘) 「전국」 = 키 · 단가 없는 자리표(`national` ·
|
||||
바탕 이름(전국)-관급/사급 · 규격 · 단위 · 꼬리표 「전국 — 지역은 프로젝트에서」) · 자동 고름 없음 ·
|
||||
지역 변형은 고르기 창에서 사용자가.
|
||||
그 밖 = 후보 다섯(변형은 하나로 접고 글에 수). 곳 낱말 = M01 레미콘 이름에서(자료).
|
||||
가져오기 세 칸(`marks`) = 별도계상(표시 → 별도계상 · 아니면 직접계산) · 자재구분(고른
|
||||
자재 이름 · 줄 글) · 운반비(「운반비 미포함 · 별도」 → 미포함). 로직 판정과 무관.
|
||||
@@ -33,6 +37,9 @@ REGIONS = (
|
||||
*"여주 이천 가평 연천".split(),
|
||||
)
|
||||
SKIP_WORDS = frozenset({"조합공통품목"})
|
||||
NATIONAL = "전국"
|
||||
NATIONAL_TAG = "전국 — 지역은 프로젝트에서"
|
||||
REGION_TAG = "지역 — 줄 글"
|
||||
_APART = re.compile(r"\(?\s*별도\s*계상\s*\)?")
|
||||
_NUM = re.compile(r"\d+(?:\.\d+)?")
|
||||
_RANGE = re.compile(r"(\d+(?:\.\d+)?)\s*(<=|≤|<)\s*[A-Za-z]\s*(<=|≤|<)\s*(\d+(?:\.\d+)?)")
|
||||
@@ -60,8 +67,8 @@ def _paren_words(name: str) -> set[str]:
|
||||
return {w for g in re.findall(r"\(([^()]*)\)", name) for w in re.findall(r"[가-힣]{2,}", g)}
|
||||
|
||||
|
||||
def base(name: Any) -> str:
|
||||
"""바탕 이름 — 괄호 글 · 끝 지역 · 관급/사급 꼬리 뗌(정규화)."""
|
||||
def _bare(name: Any) -> str:
|
||||
"""괄호 글 · 끝 지역 · 관급/사급 꼬리 뗀 글(정규화 전 · 「레미콘(서울)-사급」 → 레미콘)."""
|
||||
text = _nfkc(name)
|
||||
while "(" in text and (cut := re.sub(r"\([^()]*\)", "", text)) != text:
|
||||
text = cut
|
||||
@@ -71,7 +78,12 @@ def base(name: Any) -> str:
|
||||
text = text[: m.start()]
|
||||
for w in SUPPLY:
|
||||
text = text[: -len(w)] if text.endswith(w) and len(text) > len(w) else text
|
||||
return mm.plain(text)
|
||||
return text.strip()
|
||||
|
||||
|
||||
def base(name: Any) -> str:
|
||||
"""바탕 이름 — `_bare` 정규화."""
|
||||
return mm.plain(_bare(name))
|
||||
|
||||
|
||||
def clean(line: dict[str, Any]) -> tuple[dict[str, Any], bool]:
|
||||
@@ -166,16 +178,21 @@ def _wants(line: dict[str, Any]) -> tuple[str | None, list[str]]:
|
||||
return supply, [w for w in words if _region(w) and w not in NO_REGION]
|
||||
|
||||
|
||||
def _hit(r: dict[str, Any], regions: list[str]) -> bool:
|
||||
return any(w[:2] == x[:2] for w in regions for x in r["region"])
|
||||
|
||||
|
||||
def _prefer(rows: list[dict[str, Any]], line: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""변형 차례 — 줄 글의 관급/사급 · 지역 먼저 · 지역 없는 것 · 엑셀(M01) 차례."""
|
||||
supply, regions = _wants(line)
|
||||
|
||||
def rank(r: dict[str, Any]) -> tuple:
|
||||
hit = any(w[:2] == x[:2] for w in regions for x in r["region"])
|
||||
hit = _hit(r, regions)
|
||||
return (
|
||||
supply is not None and r["supply"] not in (None, supply),
|
||||
bool(regions) and not hit,
|
||||
bool(r["region"]),
|
||||
not any(w in r["one"]["이름"] for w in NO_REGION), # 「(전국)」 품목 먼저(361-1)
|
||||
r["at"],
|
||||
)
|
||||
|
||||
@@ -201,8 +218,30 @@ def sure(line: dict[str, Any]) -> tuple[list[dict[str, Any]], list[str]]:
|
||||
if not full or len({r["key"] for r in full}) > 1:
|
||||
return [], []
|
||||
full = _prefer(full, line)
|
||||
tags = [f"지역 · 관급/사급 못 가림 — {len(full)}개 중 첫째"] if len(full) > 1 else []
|
||||
return full, tags
|
||||
supply, regions = _wants(line)
|
||||
if any(_hit(r, regions) for r in full):
|
||||
return full, [REGION_TAG]
|
||||
if len(full) == 1 or any(not r["region"] for r in full):
|
||||
return full, []
|
||||
return [_national(full[0], supply), *full], [NATIONAL_TAG]
|
||||
|
||||
|
||||
def _national(row: dict[str, Any], supply: str | None) -> dict[str, Any]:
|
||||
"""「전국」 자리표(361-1) — 지역 변형만 있는 품목 · 키 · 단가 없음 · 이름(전국)-관급/사급."""
|
||||
one = row["one"]
|
||||
name = f"{_bare(one['이름'])}({NATIONAL})" + (f"-{supply}" if supply else "")
|
||||
spec = _nfkc(one["규격"])
|
||||
for w in SKIP_WORDS: # 「조합공통품목, 25-21-120」(관급) → 25-21-120
|
||||
spec = re.sub(rf"{w}[\s,]*", "", spec)
|
||||
return {"one": {"전국": True, "이름": name, "규격": spec.strip(" ,"), "단위": one["단위"]}}
|
||||
|
||||
|
||||
def national(line: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""자재 · 별도계상 줄(다듬기 전)의 「전국」 자리표 — 없으면 None(만들기 · 미리 셈 재검사)."""
|
||||
if line.get("갈래") not in hm.MATERIAL_KINDS:
|
||||
return None
|
||||
rows, _ = sure(clean(line)[0])
|
||||
return next((r["one"] for r in rows if r["one"].get("전국")), None)
|
||||
|
||||
|
||||
def candidates(line: dict[str, Any], limit: int = 5) -> list[dict[str, Any]]:
|
||||
|
||||
Reference in New Issue
Block a user