"""Run an availability experiment on an authored synthetic user-return scenario.""" import argparse import hashlib import json from pathlib import Path from availability import assemble, feature_events ROOT = Path(__file__).resolve().parent def record(): raw = (ROOT / 'scenario.json').read_bytes() scenario = json.loads(raw) observations, events, labels = (scenario[k] for k in ('observations', 'events', 'labels')) current = assemble(observations, events, labels, scenario['assemble_at']) later = assemble(observations, events, labels, scenario['later_assemble_at']) diagnostics = [{'id': row['id'], 'as_known': feature_events(row, events), 'event_time_only': feature_events(row, events, require_arrival=False)} for row in observations if row['id'] in {r['id'] for r in current['rows']}] before = {row['id']: row['feature_event_ids'] for row in current['rows']} after = {row['id']: row['feature_event_ids'] for row in later['rows']} return {'mode': 'synthetic data-availability inspection; no training or predictions', 'input_sha256': hashlib.sha256(raw).hexdigest(), 'current': current, 'later': later, 'diagnostics': diagnostics, 'original_feature_ids_unchanged': all(ids == after[id] for id, ids in before.items())} def main(): parser = argparse.ArgumentParser() parser.add_argument('--save', type=Path) parser.add_argument('--check', action='store_true') args = parser.parse_args() result = record() if args.check and result != json.loads((ROOT / 'result.json').read_text()): raise SystemExit('availability record differs from the bundled result') if args.save: args.save.write_text(json.dumps(result, indent=2) + '\n') print('Synthetic user-return cohort; features use arrival time, labels use readiness time') for name in ('current', 'later'): cohort = result[name] print(f"{cohort['as_of']}: eligible={len(cohort['rows'])}; " f"excluded={len(cohort['excluded'])}; reasons={json.dumps(cohort['exclusion_counts'], sort_keys=True)}") for row in result['diagnostics']: print(f"{row['id']}: known-before-prediction={len(row['as_known'])}; " f"retrospective-event-only={len(row['event_time_only'])}") print('Original feature IDs unchanged when more labels become ready:', result['original_feature_ids_unchanged']) print('Pending or missing labels are excluded, never converted to negative outcomes.') if __name__ == '__main__': main()