merge(마스터): main_desktop_1 재료_유가전력 합침 — 키 대장 「다음」·「폐기」 합치기
- 「다음」 MO 73 채택(저쪽 새 키) · 나머지는 같음 - 「폐기」 는 양쪽 합집합 — 유류 5줄·되풀이 기호·MN 유지 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
@@ -0,0 +1,298 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""오피넷 유가(휘발유·경유·실내등유) + 한전 전기요금표 별표1(C) → 재료_유가전력.json.
|
||||
|
||||
옛 재료_오피넷유가.json 을 잇는 표 — 테이블ID MO 그대로 · 원문번호가 같은 줄은 옛 키를 그대로 씀
|
||||
(`master_keys.issue`). 휘발유·경유 36줄은 옛 판(전국평균 2026-08-14 · 시도별 2026-09-09) 값을
|
||||
그대로 지님 — 여기서 새로 안 받음(로직 계산 결과가 이 작업 전후로 갈리지 않게). 새로 받는 것은
|
||||
① 실내등유(C004, `유가_오피넷/` 최신 전국평균·시도별) ② 전력(한전 전기요금표 별표1(C),
|
||||
`전기요금_한국전력/20250101/`, 저압 위주 주요 계약종별만 — 나머지는 md 원문만 참조).
|
||||
|
||||
쓰기: ./venv/Scripts/python.exe resources/master_data/scripts/build_재료_유가전력.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
import master_keys as mk # noqa: E402
|
||||
from _migrate_base import dump_master # noqa: E402
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MASTER = Path(__file__).resolve().parents[1]
|
||||
OIL_DIR = ROOT / "resources/knowledge/original/원가계산/유가_오피넷"
|
||||
POWER_DIR = ROOT / "resources/knowledge/original/원가계산/전기요금_한국전력/20250101"
|
||||
OUT = MASTER / "재료_유가전력.json"
|
||||
|
||||
OLD_MASTER_BLOB = (
|
||||
"9de61eb2:resources/master_data/재료_오피넷유가.json" # 휘발유·경유 옛 값 원천(git)
|
||||
)
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def latest(pattern: str) -> Path:
|
||||
hits = sorted(OIL_DIR.glob(pattern))
|
||||
if not hits:
|
||||
sys.exit(f"원본 없음: {OIL_DIR}/{pattern}")
|
||||
return hits[-1]
|
||||
|
||||
|
||||
def old_기름_rows() -> list[dict]:
|
||||
"""휘발유·경유 36줄 — 옛 재료_오피넷유가.json(git 9de61eb2) 값 그대로. 키는 뺌(다시 이음)."""
|
||||
import subprocess
|
||||
|
||||
text = subprocess.run(
|
||||
["git", "show", OLD_MASTER_BLOB],
|
||||
cwd=ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
encoding="utf-8",
|
||||
).stdout
|
||||
data = json.loads(text)
|
||||
rows = []
|
||||
for r in data["줄"]:
|
||||
row = {k: v for k, v in r.items() if k != "키"}
|
||||
row["구분"] = "유류" # 전국평균/시도별 → 유류(상세구분이 그 자리를 이음)
|
||||
if row.get("출처", "").startswith("오피넷유가"):
|
||||
row["출처"] = "유가전력 " + row["출처"] # 파일 이름 「유가전력」 출처 모양 규칙
|
||||
rows.append(row)
|
||||
return rows
|
||||
|
||||
|
||||
def 실내등유_rows(national: Path, sido: Path) -> list[dict]:
|
||||
nat = json.loads(national.read_text(encoding="utf-8"))
|
||||
sd = json.loads(sido.read_text(encoding="utf-8"))
|
||||
rows = []
|
||||
national_row = next((r for r in nat.get("전국평균", []) if r["PRODCD"] == "C004"), None)
|
||||
if national_row:
|
||||
rows.append(
|
||||
{
|
||||
"원문번호": "실내등유:전국평균",
|
||||
"구분": "유류",
|
||||
"상세구분": "전국평균",
|
||||
"이름": "실내등유",
|
||||
"규격": nat["수집일"],
|
||||
"단위": "원/L",
|
||||
"값": float(national_row["PRICE"]),
|
||||
"출처": "유가전력 오피넷유가 C004",
|
||||
"비고": None,
|
||||
}
|
||||
)
|
||||
for r in sd.get("시도별", {}).get("C004", []):
|
||||
rows.append(
|
||||
{
|
||||
"원문번호": f"실내등유:{r['SIDONM']}",
|
||||
"구분": "유류",
|
||||
"상세구분": r["SIDONM"],
|
||||
"이름": "실내등유",
|
||||
"규격": sd["수집일"],
|
||||
"단위": "원/L",
|
||||
"값": r["PRICE"],
|
||||
"출처": "유가전력 오피넷유가 C004",
|
||||
"비고": None,
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
POWER_SOURCE = "유가전력 한전 전기요금표 2025-01-01"
|
||||
|
||||
# 별표1(C) 저압 위주 주요 계약종별만(원문 § 1.가 · 2.가(1) · 2.다(1) · 6.가) — 그 밖은 md 참조.
|
||||
POWER_ROWS = [
|
||||
# 주택용 저압(원문 1.가)
|
||||
("주택용저압:기본_200이하", "주택용 저압", "기본요금(200kWh 이하)", "원/호", 910, None),
|
||||
("주택용저압:기본_201~400", "주택용 저압", "기본요금(201~400kWh)", "원/호", 1600, None),
|
||||
("주택용저압:기본_400초과", "주택용 저압", "기본요금(400kWh 초과)", "원/호", 7300, None),
|
||||
(
|
||||
"주택용저압:전력량_처음200",
|
||||
"주택용 저압",
|
||||
"전력량요금(처음 200kWh까지)",
|
||||
"원/kWh",
|
||||
120.0,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"주택용저압:전력량_다음200",
|
||||
"주택용 저압",
|
||||
"전력량요금(다음 200kWh까지)",
|
||||
"원/kWh",
|
||||
214.6,
|
||||
None,
|
||||
),
|
||||
("주택용저압:전력량_400초과", "주택용 저압", "전력량요금(400kWh 초과)", "원/kWh", 307.3, None),
|
||||
("주택용저압:월간최저요금", "주택용 저압", "월간 최저요금", "원/월", 1000, None),
|
||||
# 일반용(갑)Ⅰ 저압(원문 2.가(1))
|
||||
("일반용갑I저압:기본", "일반용(갑)Ⅰ 저압", "기본요금", "원/kW", 6160, None),
|
||||
(
|
||||
"일반용갑I저압:전력량_여름철",
|
||||
"일반용(갑)Ⅰ 저압",
|
||||
"전력량요금(여름철)",
|
||||
"원/kWh",
|
||||
132.4,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"일반용갑I저압:전력량_봄가을철",
|
||||
"일반용(갑)Ⅰ 저압",
|
||||
"전력량요금(봄·가을철)",
|
||||
"원/kWh",
|
||||
91.9,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"일반용갑I저압:전력량_겨울철",
|
||||
"일반용(갑)Ⅰ 저압",
|
||||
"전력량요금(겨울철)",
|
||||
"원/kWh",
|
||||
119.0,
|
||||
None,
|
||||
),
|
||||
# 산업용(갑)Ⅰ 저압(원문 2.다(1))
|
||||
("산업용갑I저압:기본", "산업용(갑)Ⅰ 저압", "기본요금", "원/kW", 5550, None),
|
||||
(
|
||||
"산업용갑I저압:전력량_여름철",
|
||||
"산업용(갑)Ⅰ 저압",
|
||||
"전력량요금(여름철)",
|
||||
"원/kWh",
|
||||
107.7,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"산업용갑I저압:전력량_봄가을철",
|
||||
"산업용(갑)Ⅰ 저압",
|
||||
"전력량요금(봄·가을철)",
|
||||
"원/kWh",
|
||||
85.9,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"산업용갑I저압:전력량_겨울철",
|
||||
"산업용(갑)Ⅰ 저압",
|
||||
"전력량요금(겨울철)",
|
||||
"원/kWh",
|
||||
106.0,
|
||||
None,
|
||||
),
|
||||
# 임시전력(갑)(원문 6.가) — 자체 요율 없이 주택용전력 요금을 그대로 적용
|
||||
(
|
||||
"임시전력갑:기본요금적용",
|
||||
"임시전력(갑)",
|
||||
"기본요금",
|
||||
"-",
|
||||
None,
|
||||
"주택용 저압 요금 그대로 적용(원문 6.가.(1))",
|
||||
),
|
||||
(
|
||||
"임시전력갑:전력량요금적용",
|
||||
"임시전력(갑)",
|
||||
"전력량요금",
|
||||
"-",
|
||||
None,
|
||||
"주택용 저압 요금 그대로 적용(원문 6.가.(2))",
|
||||
),
|
||||
("임시전력갑:월간최저요금", "임시전력(갑)", "월간 최저요금", "원/월", 1000, None),
|
||||
]
|
||||
|
||||
|
||||
def 전력_rows() -> list[dict]:
|
||||
rows = []
|
||||
for 번호, 상세, 이름, 단위, 값, 비고 in POWER_ROWS:
|
||||
rows.append(
|
||||
{
|
||||
"원문번호": 번호,
|
||||
"구분": "전력",
|
||||
"상세구분": 상세,
|
||||
"이름": 이름,
|
||||
"규격": "2025-01-01",
|
||||
"단위": 단위,
|
||||
"값": 값,
|
||||
"출처": POWER_SOURCE,
|
||||
"비고": 비고,
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def main() -> None:
|
||||
nat_path = latest("유가_전국평균_*.json")
|
||||
sido_path = latest("유가_시도별_*.json")
|
||||
power_md = POWER_DIR / "별표1_월간전기요금표(C)_20250101.md"
|
||||
|
||||
rows = old_기름_rows() + 실내등유_rows(nat_path, sido_path) + 전력_rows()
|
||||
|
||||
book = mk.load_book()
|
||||
file = OUT.name
|
||||
out_rows = []
|
||||
for r in rows:
|
||||
key = mk.issue(book, "MO", r["원문번호"], file)
|
||||
out_rows.append({"키": key, **r})
|
||||
mk.finish(book)
|
||||
mk.save_book(book)
|
||||
|
||||
today = json.loads(nat_path.read_text(encoding="utf-8"))["수집일"]
|
||||
head = {
|
||||
"그룹": "재료",
|
||||
"원문": "유가전력",
|
||||
"판": today,
|
||||
"원천": [
|
||||
{
|
||||
"경로": "resources/knowledge/original/원가계산/유가_오피넷/유가_전국평균_2026-08-14.json",
|
||||
"해시": sha256(OIL_DIR / "유가_전국평균_2026-08-14.json"),
|
||||
"역할": "primary(휘발유·경유 전국평균 — 옛 판, 안 바꿈)",
|
||||
},
|
||||
{
|
||||
"경로": "resources/knowledge/original/원가계산/유가_오피넷/유가_시도별_2026-09-09.json",
|
||||
"해시": sha256(OIL_DIR / "유가_시도별_2026-09-09.json"),
|
||||
"역할": "primary(휘발유·경유 시도별 — 옛 판, 안 바꿈)",
|
||||
},
|
||||
{
|
||||
"경로": str(nat_path.relative_to(ROOT)).replace("\\", "/"),
|
||||
"해시": sha256(nat_path),
|
||||
"역할": "primary(실내등유 전국평균 — 새로 받음)",
|
||||
},
|
||||
{
|
||||
"경로": str(sido_path.relative_to(ROOT)).replace("\\", "/"),
|
||||
"해시": sha256(sido_path),
|
||||
"역할": "primary(실내등유 시도별 — 새로 받음)",
|
||||
},
|
||||
{
|
||||
"경로": str(power_md.relative_to(ROOT)).replace("\\", "/"),
|
||||
"해시": sha256(power_md),
|
||||
"역할": "primary(전력)",
|
||||
},
|
||||
],
|
||||
"구분": {
|
||||
"유류": {
|
||||
"원문": "오피넷유가",
|
||||
"기관": "한국석유공사(오피넷)",
|
||||
"판": "휘발유·경유 2026-08-14/09-09 · 실내등유 " + today,
|
||||
"지식DB": "resources/knowledge/original/원가계산/유가_오피넷/",
|
||||
},
|
||||
"전력": {
|
||||
"원문": "한전 전기요금표 별표1(C)",
|
||||
"기관": "한국전력공사",
|
||||
"판": "2025-01-01",
|
||||
"지식DB": "resources/knowledge/original/원가계산/전기요금_한국전력/20250101/",
|
||||
},
|
||||
},
|
||||
"비고": [
|
||||
"시도별 유가 — 품셈 8-1-7 5호 「유류가격은 해당지역의 가격으로 한다」. 코드 00(전국)은 원문 그대로 두되 지역 선택지에서는 전국평균 판이 맡는다. ⚠ 원천이 시도를 17줄로 주는데 그 가름이 행정구역과 다르다 — 코드 20 이 「전남광주」 한 줄이고 07(전남)·16(광주)은 아예 없다. 원문 이름을 그대로 두고 우리가 쪼개지 않는다.",
|
||||
"규격 = 기준일(유류: 수집일 · 전력: 시행일) · 구분 = 유류/전력 · 상세구분 = 유류(지역 이름)·전력(계약종별)",
|
||||
"휘발유·경유는 옛 판(전국평균 2026-08-14 · 시도별 2026-09-09) 값 그대로 — 이번엔 실내등유만 새로 받음(로직 계산 결과가 갈리지 않게). 다음에 유류 전체를 갱신할 땐 이 셋을 한 수집일로 맞출 것.",
|
||||
"전력비 계상 기준 조문 없음 — 계약예규 「예정가격작성기준」 §19③1·§34③1, 건설품셈 1-5-2호, 산림품셈 1-7-2호 모두 「전력비는 실비를 계상」만 정하고 특정 계약종별 지정 없음.",
|
||||
"전력 — 별표1(C) 저압 위주 주요 계약종별(주택용·일반용(갑)Ⅰ·산업용(갑)Ⅰ·임시전력(갑))만 올림. 고압·을종·교육용·농사용·가로등 등 나머지 계약종별과 슈퍼유저요금·하계 특례는 md 원문(전기요금_한국전력/20250101/)만 참조.",
|
||||
],
|
||||
}
|
||||
dump_master(OUT, head, out_rows)
|
||||
print(f"saved {OUT.name} ({len(out_rows)} rows)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -41,7 +41,7 @@ BOOKS = (
|
||||
"SW노임",
|
||||
"산림노임",
|
||||
"준용대상",
|
||||
"오피넷유가",
|
||||
"유가전력",
|
||||
"자재품목",
|
||||
"한국은행환율",
|
||||
"조달청제비율",
|
||||
@@ -132,7 +132,7 @@ COLS = {
|
||||
"비고",
|
||||
"면수",
|
||||
),
|
||||
"오피넷유가": (
|
||||
"유가전력": (
|
||||
"키",
|
||||
"원문번호",
|
||||
"구분",
|
||||
@@ -145,7 +145,7 @@ COLS = {
|
||||
"비고",
|
||||
),
|
||||
}
|
||||
COLS["한국은행환율"] = COLS["오피넷유가"]
|
||||
COLS["한국은행환율"] = COLS["유가전력"]
|
||||
|
||||
|
||||
def _detail(name: str) -> str:
|
||||
|
||||
@@ -19,7 +19,7 @@ KEY = re.compile(r"[A-Z]{2}\d{6}")
|
||||
TABLES = {
|
||||
("인력", None): "LB",
|
||||
("재료", "자재품목"): "MT",
|
||||
("재료", "오피넷유가"): "MO",
|
||||
("재료", "유가전력"): "MO",
|
||||
("재료", "품셈재료"): "MP",
|
||||
("기계", None): "EQ",
|
||||
("소요량", "산림품셈"): "QF",
|
||||
|
||||
Reference in New Issue
Block a user