- JDL 줄 = 탭 구분 + \x05/\x07 덧붙임(검사기 0x504d20 · 고정폭 표는 죽은 고리 — 34 §17.9 바로잡음) · 단가 목록 7종·Main·GongJong 머리·Global.Ini 상수 키 · zip 받기 - 구버전 판 가리기(Chk_Data 0x459280) · 7.x/DOS 표별 필드 대응 · B→X 조건 = 단위 X - 엑셀 역등록 PRPT「속성」·PRIN「대상」·PROUT「저장」 = PR/RR 줄만 · 원본 BDQTY 4건 일치 - 자재 = 재료비 수량집계표 엑셀 내보내기(주요건설자재현황 시트) - 그림 코드 = COSTN I 코드 IFORM · SIZE = 그림 클래스 이름 - 지식DB 37번 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83 lines
2.8 KiB
Python
83 lines
2.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""조달청 JDL TEXT 끼워 넣기 API — 묶음 내보내기(JSON · zip) · 묶음 검사(검사기 꼴).
|
|
|
|
근거: jdl_format.py · jdl_export.py 머리(ST_U_Convert Conv_ST8_Jodal · JDLCHK.Tab_33.dll)
|
|
원본 STC·폴더는 읽기만 · 서버에 쓰지 않음(zip 은 응답으로만).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import io
|
|
import zipfile
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
from fastapi.responses import Response
|
|
from jdl_export import export_bundle
|
|
from jdl_format import COUNTS, check_lines, columns_of
|
|
from pydantic import BaseModel
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class JdlReq(BaseModel):
|
|
source_path: str
|
|
series: int | None = None
|
|
|
|
|
|
class JdlCheckReq(BaseModel):
|
|
files: dict[str, str] = {} # 이름 → base64(또는 data: 주소)
|
|
|
|
|
|
def _bundle(req: JdlReq) -> dict:
|
|
try:
|
|
return export_bundle(req.source_path, req.series)
|
|
except FileNotFoundError:
|
|
raise HTTPException(404, f"경로 없음: {req.source_path}")
|
|
except (ValueError, KeyError) as e:
|
|
raise HTTPException(400, str(e))
|
|
|
|
|
|
@router.post("/api/jdl/export")
|
|
def jdl_export(req: JdlReq):
|
|
"""묶음 요약 — 파일별 줄 수 · 검사 결과 · 첫 줄 열 보기 · 못 만든 것(미확인)."""
|
|
r = _bundle(req)
|
|
first = {}
|
|
for name, data in r["files"].items():
|
|
if name in COUNTS and data:
|
|
first[name] = columns_of(name, data.split(b"\r\n", 1)[0])
|
|
ini = r["files"].get("Global.Ini", b"").decode("cp949", "replace")
|
|
keep = ("series", "pclass", "counts", "checks", "missing", "warnings")
|
|
return {k: r[k] for k in keep} | {"first": first, "global_ini": ini}
|
|
|
|
|
|
@router.post("/api/jdl/export.zip")
|
|
def jdl_export_zip(req: JdlReq):
|
|
"""묶음 파일 모음 zip(전송용 · 원본 .JDL 압축 형식은 미확인)."""
|
|
r = _bundle(req)
|
|
buf = io.BytesIO()
|
|
with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as z:
|
|
for name, data in r["files"].items():
|
|
z.writestr(name, data)
|
|
disp = "attachment; filename=JDL_TEXT.zip"
|
|
return Response(
|
|
content=buf.getvalue(), media_type="application/zip", headers={"Content-Disposition": disp}
|
|
)
|
|
|
|
|
|
@router.post("/api/jdl/check")
|
|
def jdl_check(req: JdlCheckReq):
|
|
"""받은 묶음 파일마다 검사기 꼴 검사 — 열 개수 · 코드 글자."""
|
|
out = {}
|
|
for name, b64 in req.files.items():
|
|
key = next((k for k in COUNTS if k.upper() == name.upper().rsplit("/", 1)[-1]), None)
|
|
if not key:
|
|
out[name] = ["검사 대상 아님"]
|
|
continue
|
|
try:
|
|
data = base64.b64decode(b64.split(",", 1)[-1])
|
|
except ValueError:
|
|
raise HTTPException(400, f"base64 아님: {name}")
|
|
out[name] = check_lines(key, data)
|
|
return {"checks": out}
|