Files

86 lines
4.0 KiB
Python

"""Validate archived analysis before allowing deletion of downloaded weights."""
import argparse
import csv
import hashlib
import json
import pathlib
import shutil
import time
def validate(root):
analysis = root / 'analysis'
summary = json.loads((analysis / 'summary.json').read_text())
assert summary['completed']
manifest = json.loads((root / 'manifest.json').read_text())
assert summary['revision'] == manifest['revision']
for mode in ('tensor', 'block'):
objects = json.loads((analysis / f'{mode}_objects.json').read_text())
with (analysis / f'{mode}_pairs.csv').open() as f:
pairs = list(csv.DictReader(f))
coverage = json.loads((analysis / f'{mode}_coverage.json').read_text())
assert len(pairs) == coverage['pairs_verified']
edges = {}
for p in pairs:
a, b = int(p['a']), int(p['b'])
assert objects[a]['shape'] == objects[b]['shape']
assert objects[a]['role'] == objects[b]['role']
edges[(b, a)] = float(p['error_member_to_rep'])
edges[(a, b)] = float(p['error_rep_to_member'])
for file in analysis.glob(f'groups_{mode}_*.json'):
threshold = float(file.stem.rsplit('_', 1)[1])
groups = json.loads(file.read_text())
members = [i for g in groups for i in g['members']]
assert len(set(members)) == len(members), file
for g in groups:
assert g['representative'] in g['members']
for member in g['members']:
if member != g['representative']:
assert edges[(g['representative'], member)] <= threshold + 1e-12
assert g['max_error'] <= threshold + 1e-12
assert (root / 'REPORT.md').is_file()
images = list((root / 'figures').glob('*.png'))
assert len(images) >= 5
for file in images:
assert file.read_bytes()[:8] == b'\x89PNG\r\n\x1a\n'
checksums = {}
for file in sorted(root.rglob('*')):
if (file.is_file() and 'weights' not in file.relative_to(root).parts
and file.name not in ('checksums.json', 'validation.json', 'cleanup.json', 'status.json')
and not file.name.endswith('.log')):
checksums[str(file.relative_to(root))] = hashlib.sha256(file.read_bytes()).hexdigest()
(root / 'checksums.json').write_text(json.dumps(checksums, indent=2))
result = {'validated_unix': time.time(), 'files_checksummed': len(checksums),
'group_edges_checked': True, 'passed': True}
(root / 'validation.json').write_text(json.dumps(result, indent=2))
return result
def main():
p = argparse.ArgumentParser()
p.add_argument('--root', required=True, type=pathlib.Path)
p.add_argument('--cleanup-weights', action='store_true')
args = p.parse_args()
root = args.root.resolve()
result = validate(root)
if args.cleanup_weights:
weights = root / 'weights'
manifest = json.loads((root / 'manifest.json').read_text())
assert not weights.is_symlink()
assert (weights / '.pilot-owned').read_text() == manifest['repo']
expected_names = {entry['name'] for entry in manifest['files']} | {'.pilot-owned'}
assert {file.name for file in weights.iterdir()} == expected_names
assert all(file.is_file() and not file.is_symlink() for file in weights.iterdir())
deleted_bytes = sum(file.stat().st_size for file in weights.iterdir())
shutil.rmtree(weights)
(root / 'cleanup.json').write_text(json.dumps({'deleted_unix': time.time(),
'deleted_bytes': deleted_bytes,
'deleted_directory': 'weights',
'reason': 'User-authorized delete-after-analysis; archived results validated'}, indent=2))
result['weights_removed'] = not weights.exists()
print(json.dumps(result))
if __name__ == '__main__':
main()