Files
Aislo/docs/raw/M01_점검/sub1_2/tools/chapter.py
T

121 lines
6.4 KiB
Python

# 사용: python tmp/M01_점검/sub1_2/tools/chapter.py <부문폴더 03_건축부문|05_유지관리부문> <장NN> [--list] [--secs 1-1-1,1-1-2] [--nologic]
# 원문 md 절과 자료 표(소요량·계수)·로직을 나란히 출력. 마지막에 절별 수치칸 N/M 보조 집계.
import json,re,sys,glob,collections,os
ROOT='resources/knowledge/original/원가계산/건설공사_표준품셈/본문/'
MD='resources/master_data/'
bu,ch=sys.argv[1],sys.argv[2]
args=sys.argv[3:]
secs_want=None
if '--secs' in args: secs_want=set(args[args.index('--secs')+1].split(','))
LIST='--list' in args; NOLOGIC='--nologic' in args
bumap={'03_건축부문':'건축','05_유지관리부문':'유지관리'}
mdir=glob.glob(ROOT+bu+f'/제{ch}장_*')[0]
def key(s): return tuple(map(int,s.split('-')))
# ---- md 절
sections=collections.OrderedDict()
for f in sorted(glob.glob(mdir+'/*.md')):
if os.path.basename(f).startswith('0-00'): continue
txt=open(f,encoding='utf-8').read()
txt=re.sub(r'^---\n.*?\n---\n','',txt,flags=re.S)
parts=re.split(r'^### (\d+-\d+-\d+)[^\n]*\n',txt,flags=re.M)
head=parts[0]
for i in range(1,len(parts),2):
body=parts[i+1]
# 다음 ## 절머리 이전까지만
body=re.split(r'^## ',body,flags=re.M)[0]
sections.setdefault(parts[i],[]).append((os.path.basename(f),parts[i],body.rstrip()))
# 제목 다시 얻기
titles={}
for f in glob.glob(mdir+'/*.md'):
for m in re.finditer(r'^### (\d+-\d+-\d+)([^\n]*)',open(f,encoding='utf-8').read(),flags=re.M): titles[m.group(1)]=m.group(2).strip()
# ---- json
tabs=collections.defaultdict(list); logs=collections.defaultdict(list)
jfiles=[]
for g in ('소요량','계수','로직'):
for f in sorted(glob.glob(MD+f'{g}_건설품셈_{ch}장_*.json')):
d=json.load(open(f,encoding='utf-8'))
if d.get('부문')!=bumap[bu]: continue
jfiles.append(os.path.basename(f))
if g=='로직':
for t in d['줄']: logs[t['원문번호']].append(t)
else:
for t in d['표']: tabs[t['원문번호']].append(t)
allsecs=sorted(set(sections)|set(tabs)|set(logs),key=key)
if LIST:
print('json:',jfiles)
for s in allsecs:
nmd=sum(len([l for l in b.split('\n') if l.startswith('|') and not re.match(r'^\|[-| :]+\|$',l)]) for _,_,b in sections.get(s,[]))
print(f'{s} | md파일 {",".join(sorted({a for a,_,_ in sections.get(s,[])}))} | md표줄 {nmd} | 자료표 {len(tabs.get(s,[]))} | 로직 {len(logs.get(s,[]))} | {titles.get(s,"")[:30]}')
sys.exit()
num=re.compile(r'\d+(?:,\d{3})*(?:\.\d+)?')
def nums(s):
out=[]
for x in num.findall(s):
x=x.replace(',','')
try:
v=float(x); out.append(str(int(v)) if v==int(v) else str(v))
except: out.append(x)
return out
def jn(v,acc):
if isinstance(v,bool) or v is None: return
if isinstance(v,(int,float)): acc+=nums(str(v))
elif isinstance(v,list):
for x in v: jn(x,acc)
elif isinstance(v,dict):
for x in v.values(): jn(x,acc)
elif isinstance(v,str): acc+=nums(v)
def c(r): return ' '.join(f'{k}={v}' if isinstance(v,str) else f'{k}={json.dumps(v,ensure_ascii=False)}' for k,v in r.items())
summary=[]
for s in allsecs:
if secs_want and s not in secs_want: continue
print('='*100); print(f'### 절 {s} {titles.get(s,"")}')
print('--- 원문 md ---')
mdnums=collections.Counter(); ncell=0
for fn,_,b in sections.get(s,[]):
print(f'[파일 {fn}]'); print(b)
for l in b.split('\n'):
if l.startswith('|') and not re.match(r'^\|[-| :]+\|$',l):
ns=nums(l); mdnums.update(ns); ncell+=len(ns)
if s not in sections: print('(원문 md 에 이 절 없음)')
print('--- 자료 표 (소요량·계수) ---')
jnums=collections.Counter()
for t in tabs.get(s,[]):
print(f'## {t["키"]} {t["이름"]} |기준:{t.get("기준")} |로직:{t["용도"].get("로직키")}')
print(' 조건:',json.dumps(t.get('조건'),ensure_ascii=False),' 값칸:',json.dumps(t.get('값칸'),ensure_ascii=False))
for k in t:
if k not in ('키','원문번호','구분','상세구분','이름','기준','출처','용도','조건','값칸','줄'):
print(' ',k,':',json.dumps(t[k],ensure_ascii=False))
for r in t['줄']: print(' -',c(r))
a=[]; jn(t['줄'],a); jnums.update(a)
b=[];
for k in t:
if k not in ('키','원문번호','구분','상세구분','이름','기준','출처','용도','조건','값칸','줄'): jn(t[k],b)
jnums.update(b)
if not tabs.get(s): print('(자료 표 없음)')
miss=mdnums-jnums
matched=ncell-sum(miss.values())
summary.append((s,ncell,matched,dict(miss)))
STRIP=re.compile(r"[\s\.,\-‘’“”'\"「」·∼~〜※|:]")
blob=STRIP.sub('',json.dumps(tabs.get(s,[]),ensure_ascii=False))
nn=[]
for fn,_,b in sections.get(s,[]):
for l in b.splitlines():
l=l.strip()
if re.match(r'^[①-⑳㉑-㉟]',l) or l.startswith('※') or re.match(r'^\|\s*비고',l):
t2=re.sub(r'<br>',' ',l).strip('| ')
t2=re.sub(r'^비고\s*\|?\s*','',t2)
if STRIP.sub('',t2)[:30] not in blob: nn.append(t2[:90])
if nn: print('--- 주·비고 문장이 자료 표에 없어 보임(표로 옮겨졌는지 눈으로 확인) ---'); [print(' ·',x) for x in nn]
print(f'--- 보조집계: 원문 표줄 수치 {ncell}개 중 자료에 있음 {matched}개 · 자료에 없는 원문 수치 {dict(miss)} ---')
if not NOLOGIC and logs.get(s):
print('--- 로직 ---')
for t in logs[s]:
print(f'# {t["키"]} {t["이름"]} |{t.get("결과단위")}')
print(' 비고:',(t.get('비고') or '')[:400])
print(' 입력:',[(i["이름"],i.get("단위") or (i.get("고르기") if len(i.get("고르기",[]))<6 else "지역..")) for i in t.get("입력",[])])
for m in t.get('중간',[]): print(' 중간:',m['이름'],'=',m['식'][:300])
for h in t.get('호표',[]): print(' 호표:',h.get('종류'),json.dumps(h.get('요소'),ensure_ascii=False)[:120],h.get('이름'),h.get('단위'),'|',str(h.get('수량'))[:260],'|',h.get('비목',''))
for h in t.get('덧줄',[]): print(' 덧줄:',h.get('이름'),'|',h.get('식','')[:300],'|',h.get('비목'))
print('='*100); print('절별 집계 (절, 원문수치칸, 자료에 있음)');
for s,n,m,x in summary: print(s,n,m)