"""정식 결과의 계약·참조·실측·원본 및 복사 파일 해시를 대조한다.""" import argparse import hashlib import json from pathlib import Path import sys import jsonschema ROOT=Path(__file__).resolve().parents[3] PROJECT=ROOT/'projects/water-hydrogen-bond-surface-tension' sys.path.insert(0,str(ROOT/'contracts')) from check import semantic_errors parser=argparse.ArgumentParser() parser.add_argument('--stage',choices=['10-references','11-images','12-motion'],required=True) args=parser.parse_args() BASE=PROJECT/f'stages/{args.stage}/r2' def read(path): return json.loads(path.read_text(encoding='utf-8')) artifact=read(BASE/'artifact.json') context={s['stage_id']:read(PROJECT.parent.parent/s['artifact_path']) for s in read(PROJECT/'project.json')['stages'] if (ROOT/s['artifact_path']).is_file()} jsonschema.validate(artifact,read(ROOT/'contracts/artifact.schema.json')) assert not semantic_errors(artifact,context,complete=True),semantic_errors(artifact,context,complete=True) request,result=artifact['payload']['request'],artifact['payload']['result'] source=result['scene_file'] if args.stage!='12-motion' else context['11-images']['payload']['result']['scene_file'] assert source['verified'] and hashlib.sha256((ROOT/source['path']).read_bytes()).hexdigest()==source['sha256'] if args.stage!='12-motion': assert (ROOT/source['path']).is_relative_to(BASE) model=read(BASE/'model-check.json') assert model['source_sha256']==source['sha256'] and model['water_containment']['frames']==705 assets={source['path']:source['sha256']} key,id_key={'10-references':('items','reference_id'),'11-images':('frames','frame_id'),'12-motion':('clips','cut_id')}[args.stage] if args.stage=='10-references': assert len(result['items'])==7 and len({i['object_name'] for i in result['items']})==7 assert {i['entity_id'] for i in request['items'] if i['kind']=='background'}=={b['id'] for b in context['09-entities']['payload']['backgrounds']} if args.stage=='11-images': assert len(result['frames'])==62 by_id={f['frame_id']:f['media'] for f in result['frames']} frames={f['id']:f for f in request['frames']} assert len(by_id)==len(frames)==62 anchors=read(BASE/'anchors.json') assert anchors['source_sha256']==source['sha256'] and (anchors['width'],anchors['height'])==(1080,1920) assert anchors['coordinate_system']=='top_left_pixel' and len(anchors['cuts'])==31 for cut,anchor in zip(context['07-cuts']['payload']['cuts'],anchors['cuts']): start,end=cut['start_ms']//20,cut['end_ms']//20 assert (anchor['cut_id'],anchor['start_frame'],anchor['end_frame'])==(cut['id'],start,end) assert frames[cut['id']+'-start']['frame_index']==start and frames[cut['id']+'-end']['frame_index']==end assert frames[cut['id']+'-start']['scene_name']==anchor['scene_name']==frames[cut['id']+'-end']['scene_name'] assert anchor['motion'] and all(track['keys'][0][0]==start and track['keys'][-1][0]==end for track in anchor['motion']) for connection in context['08-scenes']['payload']['connections']: left,right=connection['from_cut_id']+'-end',connection['to_cut_id']+'-start' if connection['mode']=='continuous': assert by_id[left]['path']==by_id[right]['path'] assert (frames[left]['scene_name'],frames[left]['frame_index'])==(frames[right]['scene_name'],frames[right]['frame_index']) else: assert frames[left]['scene_name']!=frames[right]['scene_name'] if args.stage=='12-motion': assert len(result['clips'])==31 and sum(c['end_frame']-c['start_frame'] for c in request['clips'])==5528 assert sum(c['duration_ms'] for c in request['clips'])==110560 if args.stage!='10-references': visual=read(BASE/'visual-check.json') assert visual['source_sha256']==source['sha256'] assert all(v['visual_review']=='checked' for v in visual['views']), '실제 화면 직접 관찰 미완료' assert len(visual['views'])==(9 if args.stage=='11-images' else 31) for view in visual['views']: for asset in [view['sheet']]+view.get('decoded_frames',[])+view.get('render_frames',[]): path=ROOT/asset['path'] assert path.is_relative_to(BASE) and hashlib.sha256(path.read_bytes()).hexdigest()==asset['sha256'] assets[asset['path']]=asset['sha256'] for item in result[key]: media=item['media'] path=ROOT/media['path'] assert path.is_file() and path.is_relative_to(BASE) and media['verified'] assert (media['width'],media['height'])==(1080,1920) evidence=read(path.parent/'evidence.json') assert evidence['scene_file']==source and evidence['render_profile']==request['render_profile'] and evidence['media']==media if args.stage=='10-references': assert evidence['logical_frames'] and len(evidence['logical_frames'])==1 elif args.stage=='11-images': frame=next(f for f in request['frames'] if f['id']==item['frame_id']) assert evidence['scene_name']==frame['scene_name'] and evidence['logical_frames']==[frame['frame_index']] else: clip=next(c for c in request['clips'] if c['cut_id']==item['cut_id']) assert evidence['scene_name']==clip['scene_name'] and evidence['logical_frames']==list(range(clip['start_frame'],clip['end_frame'])) assert media['duration_ms']==clip['duration_ms'] and 0