Files
Pier_v1/RAG/design-model-box-sample_2/tools/prepare_balanced_tie_review.py
T
ahnyongjooandClaude Sonnet 5.5 964a841de1 chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환
- Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용
- pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관)
- worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md)
- D:\Rhino-MCP 경로 참조를 새 위치로 치환

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 11:20:18 +09:00

64 lines
3.0 KiB
Python

"""Assemble only end-balanced, validated tie rows from local review files.
Never writes a saved job. Publication must run the independent geometry gate.
"""
import argparse
import copy
import json
import sys
from pathlib import Path
root = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(root/'src/backend' if (root/'src/backend').is_dir() else root))
from tie_end_spacing import envelope_margins
def prepare(paths):
reviews = [json.load(open(path)) for path in paths]
result = copy.deepcopy(reviews[0])
selected = {}
rejected = []
for review in reviews:
plan = review['result']
ids = set(plan['quantityDistributionReview']['group_ids'])
rejected.extend(plan['quantityDistributionReview'].get('failures', []))
for group in plan['rebar_groups']:
if group.get('id') not in ids:
continue
rhythm = group['quantity_distribution'].get('rhythm', {})
lengths = rhythm.get('side_lengths_m', [])
counts = rhythm.get('side_counts', [])
margins = rhythm.get('end_margins_m', [])
pitch = rhythm.get('source_cycle_pitch_m', 0)
errors = [abs(envelope_margins(side)[0]-envelope_margins(side)[1]) for side in margins]
reason = None
if len(lengths)!=2 or len(counts)!=2 or len(errors)!=2 or not pitch:
reason = 'missing physical-side balance proof'
elif (rhythm.get('end_spacing_priority') == 'equal_ends' and rhythm.get('end_balance_required',True)
and max(errors)>rhythm.get('end_balance_tolerance_m',1e-6)+1e-8):
reason = 'straight body end residual requires explicit acceptance'
elif abs(lengths[0]-lengths[1])>=rhythm['repeat_m']-1e-6 and counts[0]==counts[1]:
reason = 'longer side still has the same quantity'
if reason:
rejected.append({'block':group['rebar_block'],'mark':group['mark'],
'error':reason,'side_end_imbalance_m':errors})
continue
selected[group['id']] = group
plan = result['result']
plan['rebar_groups'] = [g for g in plan['rebar_groups'] if g.get('id') not in selected]+list(selected.values())
plan['quantityDistributionReview'] = {'group_ids':sorted(selected),'failures':rejected,
'engineering_approved':False,'scope':'body ties only; wing drawing errors preserved'}
result['failures'] = rejected
return result
if __name__=='__main__':
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('reviews',nargs='+')
parser.add_argument('--output',required=True)
args=parser.parse_args()
result=prepare(args.reviews)
with open(args.output,'x') as stream:
json.dump(result,stream,ensure_ascii=False,allow_nan=False)
print(json.dumps({'eligible':len(result['result']['quantityDistributionReview']['group_ids']),
'rejected':result['failures']},ensure_ascii=False))