"""SSKIM LAB public aggregate checker. Python 3, standard library only. Run: python recompute_policy_summary.py study1-policy-summary.csv No network/API calls. This recomputes aggregate rates, not the original experiment. """ import argparse import csv import json import re FIELDS = ['policy', 'episodes', 'normalized_successes', 'strict_successes', 'recovery_calls'] def summarize(filename): with open(filename, encoding='utf-8-sig', newline='') as source: reader = csv.DictReader(source) if reader.fieldnames != FIELDS: raise ValueError('Unexpected CSV header or order') result, names = [], set() for row in reader: policy = row['policy'] if not policy or policy in names or len(policy) > 40: raise ValueError('Empty, duplicate or overlong policy') names.add(policy) if None in row or any(not re.fullmatch(r'[0-9]+', row[k] or '') for k in FIELDS[1:]): raise ValueError('Counts must be non-negative integers') n, normalized, strict, calls = (int(row[k]) for k in FIELDS[1:]) if n <= 0 or normalized > n or strict > n: raise ValueError('Invalid denominator or success count') result.append(dict(policy=policy, episodes=n, normalized_rate_percent=normalized / n * 100, strict_rate_percent=strict / n * 100, normalized_failures=n - normalized, strict_failures=n - strict, recovery_calls=calls, calls_per_episode=calls / n)) if not result: raise ValueError('No policy rows') return result if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('csv_file') args = parser.parse_args() print(json.dumps(summarize(args.csv_file), ensure_ascii=False, indent=2))