105 lines
6.6 KiB
Python
105 lines
6.6 KiB
Python
"""정식 결과의 계약·참조·실측·원본 및 복사 파일 해시를 대조한다."""
|
|
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<media['duration_ms']<=5000
|
|
endpoints=context['11-images']['payload']
|
|
end=next(f for f in endpoints['request']['frames'] if f['id']==clip['end_frame_id'])
|
|
assert end['frame_index']-evidence['logical_frames'][-1]==1
|
|
for logical in [clip['start_frame'],(clip['start_frame']+clip['end_frame'])//2,clip['end_frame']-1]:
|
|
frame=path.parent/f'frame-{logical:08}.png'
|
|
assets[frame.relative_to(ROOT).as_posix()]=hashlib.sha256(frame.read_bytes()).hexdigest()
|
|
for file in [path,path.parent/'evidence.json']:
|
|
assets[file.relative_to(ROOT).as_posix()]=hashlib.sha256(file.read_bytes()).hexdigest()
|
|
for file in BASE.glob('*.json'):
|
|
if file.name!='manifest.json':
|
|
assets[file.relative_to(ROOT).as_posix()]=hashlib.sha256(file.read_bytes()).hexdigest()
|
|
if (BASE/'review.md').is_file():
|
|
file=BASE/'review.md'
|
|
assets[file.relative_to(ROOT).as_posix()]=hashlib.sha256(file.read_bytes()).hexdigest()
|
|
value=dict(result='GREEN',stage_id=args.stage,source_sha256=source['sha256'],requested=len(request[key]),actual=len(result[key]),render_profile=request['render_profile'],actual_assets=[dict(path=path,sha256=sha) for path,sha in sorted(assets.items())])
|
|
(BASE/'manifest.json').write_text(json.dumps(value,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
|
|
print('정식 결과 검사 GREEN',args.stage,len(value['actual_assets']))
|