Spaces:
Running
Running
| from pathlib import Path | |
| import json | |
| import py_compile | |
| import sys | |
| from PIL import Image | |
| ROOT = Path(__file__).resolve().parent | |
| REQUIRED = [ | |
| 'app.py', 'requirements.txt', 'packages.txt', 'README.md', | |
| 'template.jpg', 'template_lossless.png', 'template.json', | |
| 'marker_config.json', 'template_A4_print.pdf', 'marker_reference.png', | |
| 'samples/sample_phone_perspective.jpg', | |
| 'samples/sample_phone_shadow_rescue.jpg', | |
| 'samples/sample_phone_missing_tl.jpg', | |
| ] | |
| errors = [] | |
| for filename in REQUIRED: | |
| if not (ROOT / filename).is_file(): | |
| errors.append(f'Missing: {filename}') | |
| try: | |
| py_compile.compile(str(ROOT / 'app.py'), doraise=True) | |
| except Exception as error: | |
| errors.append(f'app.py syntax validation failed: {error}') | |
| for image_name in ('template.jpg', 'template_lossless.png'): | |
| try: | |
| image = Image.open(ROOT / image_name) | |
| if image.size != (3093, 4374): | |
| errors.append(f'{image_name} size is {image.size}, expected (3093, 4374)') | |
| except Exception as error: | |
| errors.append(f'{image_name} cannot be read: {error}') | |
| try: | |
| template = json.loads((ROOT / 'template.json').read_text(encoding='utf-8')) | |
| markers = json.loads((ROOT / 'marker_config.json').read_text(encoding='utf-8')) | |
| if template.get('pageDimensions') != [3093, 4374]: | |
| errors.append('template.json pageDimensions mismatch') | |
| if markers.get('pageDimensions') != [3093, 4374]: | |
| errors.append('marker_config.json pageDimensions mismatch') | |
| if markers.get('largeMarkerOuterSize') != 144: | |
| errors.append('V3.1 largeMarkerOuterSize must remain 144') | |
| if markers.get('strictMarkerMode') is not True: | |
| errors.append('strictMarkerMode must default to true') | |
| if 'smallGroupMarkers' in markers or 'smallMarkerOuterSize' in markers: | |
| errors.append('Small group marker settings must not exist in this variant') | |
| rescue = markers.get('localRescue') or {} | |
| if rescue.get('enabled') is not True: | |
| errors.append('V3.1 localRescue.enabled must be true') | |
| if int(rescue.get('minimumGlobalCandidates', 0)) < 2: | |
| errors.append('minimumGlobalCandidates must be at least 2') | |
| if int(rescue.get('maxRescueCorners', 99)) > 2: | |
| errors.append('maxRescueCorners must not exceed 2') | |
| threshold = float(rescue.get('templateMatchThreshold', 0)) | |
| if not 0.35 <= threshold <= 0.65: | |
| errors.append('templateMatchThreshold outside expected safe range') | |
| except Exception as error: | |
| errors.append(f'JSON validation failed: {error}') | |
| if errors: | |
| print('PROJECT VALIDATION FAILED') | |
| for error in errors: | |
| print('-', error) | |
| sys.exit(1) | |
| print('PROJECT VALIDATION PASSED') | |
| print('V3.1 four-large-marker-only template, local-shadow rescue settings and required files are present.') | |