"""Generate comprehensive form guide for all horses racing today.""" import json from pathlib import Path from collections import defaultdict from datetime import datetime DATA_DIR = Path('/home/brettanthonysjoberg179/trifecta-bro-hf-space/data') CACHE_DIR = DATA_DIR / 'cache' RESULTS_DIR = DATA_DIR / 'results' REPORTS_DIR = DATA_DIR / 'reports' VAULT_DIR = Path('/home/brettanthonysjoberg179/Documents/Obsidian Vault') DATABANK_DIR = VAULT_DIR / 'Horse Form Databank' FORM_GUIDES_DIR = DATABANK_DIR / 'Form-Guides' def ensure_dirs(): FORM_GUIDES_DIR.mkdir(parents=True, exist_ok=True) def load_all_cached_races(date: str) -> list[dict]: races = [] for f in CACHE_DIR.glob('*.json'): try: with open(f) as fp: data = json.load(fp) if data.get('date') == date and 'track' in data: races.append(data) except: continue return races def parse_num(s): return int(str(s).replace('e', '').replace('E', '')) def generate_horse_form_guide(horse: dict) -> str: """Generate comprehensive form guide for a single horse.""" name = horse.get('name', 'Unknown') number = horse.get('number', 0) barrier = horse.get('barrier', 0) weight = horse.get('weight', 0) age = horse.get('age', 0) sex = horse.get('sex', '') sire = horse.get('sire', '') dam = horse.get('dam', '') jockey = horse.get('jockey', '') trainer = horse.get('trainer', '') form = horse.get('form', '') last20 = horse.get('last20_starts', '') career_prize = horse.get('career_prize_money', '$0') stats = horse.get('stats', {}) overall = stats.get('overall', {}) track = stats.get('track', {}) distance = stats.get('distance', {}) track_dist = stats.get('trackDistance', {}) condition = stats.get('condition', {}) first_up = stats.get('firstUp', {}) second_up = stats.get('secondUp', {}) # Get all condition stats cond_stats = stats.get('conditions', {}) note = f"""# {name} ## Race Details - **Number**: {number} - **Barrier**: {barrier} - **Weight**: {weight}kg - **Jockey**: {jockey} - **Trainer**: {trainer} - **Age/Sex**: {age}yo {sex} - **Sire**: {sire} - **Dam**: {dam} - **Career Prize**: {career_prize} ## Career Statistics | Metric | Overall | Track | Distance | Track+Dist | Condition | |--------|---------|-------|----------|------------|-----------| | Starts | {overall.get('starts', 0)} | {track.get('starts', 0)} | {distance.get('starts', 0)} | {track_dist.get('starts', 0)} | {condition.get('starts', 0)} | | Wins | {overall.get('wins', 0)} | {track.get('wins', 0)} | {distance.get('wins', 0)} | {track_dist.get('wins', 0)} | {condition.get('wins', 0)} | | Places | {overall.get('places', 0)} | {track.get('places', 0)} | {distance.get('places', 0)} | {track_dist.get('places', 0)} | {condition.get('places', 0)} | | Win % | {overall.get('winPercent', 0)*100:.1f}% | {track.get('winPercent', 0)*100:.1f}% | {distance.get('winPercent', 0)*100:.1f}% | {track_dist.get('winPercent', 0)*100:.1f}% | {condition.get('winPercent', 0)*100:.1f}% | | Place % | {overall.get('placePercent', 0)*100:.1f}% | {track.get('placePercent', 0)*100:.1f}% | {distance.get('placePercent', 0)*100:.1f}% | {track_dist.get('placePercent', 0)*100:.1f}% | {condition.get('placePercent', 0)*100:.1f}% | ## Fitness Profile | First-Up | Second-Up | |----------|-----------| | {first_up.get('starts', 0)} starts, {first_up.get('wins', 0)} wins ({first_up.get('winPercent', 0)*100:.1f}%) | {second_up.get('starts', 0)} starts, {second_up.get('wins', 0)} wins ({second_up.get('winPercent', 0)*100:.1f}%) | ## Condition Performance | Condition | Starts | Wins | Win % | Place % | |-----------|--------|------|-------|---------| """ for cond_name, cond_data in sorted(cond_stats.items()): if cond_data.get('starts', 0) > 0: note += f"| {cond_name} | {cond_data.get('starts', 0)} | {cond_data.get('wins', 0)} | {cond_data.get('winPercent', 0)*100:.1f}% | {cond_data.get('placePercent', 0)*100:.1f}% |\n" note += f""" ## Last 20 Starts ``` {last20} ``` ## Form Analysis ``` {form} ``` ### Form Key - **1-3** = Finished in top 3 - **4-9** = Finished 4th-9th - **0** = Finished 10th or worse - **x** = Spell (not raced recently) - **f** = Fell - **p** = Pulled up --- ## Notes [Add your observations here] ## Related Horses [Link to related horses: sire, dam, stablemates] """ return note def generate_race_form_guide(race_data: dict, horses: list[dict]) -> str: """Generate comprehensive form guide for a race.""" date = race_data.get('date', '') track = race_data.get('track', '') race_num = race_data.get('raceNumber', 0) race_name = race_data.get('raceName', '') distance = race_data.get('distance', '') condition = race_data.get('condition', '') race_class = race_data.get('raceClass', '') prize = race_data.get('prizeMoney', '') start_time = race_data.get('startTime', '') num_runners = race_data.get('numberOfRunners', 0) note = f"""# {track} Race {race_num} — {date} ## Race Details - **Name**: {race_name} - **Distance**: {distance} - **Condition**: {condition} - **Class**: {race_class} - **Prize**: {prize} - **Start**: {start_time} - **Runners**: {num_runners} ## Form Guide | # | Horse | Jockey | Wgt | Barrier | Form | Win% | Place% | Track Win% | Dist Win% | |---|-------|--------|-----|---------|------|------|--------|------------|-----------| """ for horse in horses: stats = horse.get('stats', {}) overall = stats.get('overall', {}) track = stats.get('track', {}) distance = stats.get('distance', {}) note += f"| {horse['number']} | [[{horse['name']}]] | {horse.get('jockey', '')} | {horse.get('weight', 0)}kg | {horse.get('barrier', 0)} | {horse.get('form', '')} | {overall.get('winPercent', 0)*100:.1f}% | {overall.get('placePercent', 0)*100:.1f}% | {track.get('winPercent', 0)*100:.1f}% | {distance.get('winPercent', 0)*100:.1f}% |\n" note += """ ## Result [Add official result here] ## Analysis [Add race analysis here] """ return note def main(): date = "2026-08-15" print(f"Generating comprehensive form guides for {date}...") print("=" * 60) ensure_dirs() # Load all cached races races = load_all_cached_races(date) print(f"Loaded {len(races)} races") total_horses = 0 total_races = 0 for race_data in races: track = race_data.get('track', '') race_num = race_data.get('raceNumber', 0) # Extract horses horses = [] for r in race_data.get('runners', []): if r.get('scratched'): continue stats = r.get('stats', {}) overall = stats.get('overall', {}) track_stats = stats.get('track', {}) distance = stats.get('distance', {}) track_dist = stats.get('trackDistance', {}) cond_stats = stats.get('conditions', {}) first_up = stats.get('firstUp', {}) second_up = stats.get('secondUp', {}) # Get condition-specific stats cond_key = None for key in cond_stats: if cond_stats[key].get('starts', 0) > 0: cond_key = key break cond_data = cond_stats.get(cond_key, {}) if cond_key else {} horse = { 'number': r['number'], 'name': r.get('name', 'Unknown'), 'jockey': r.get('jockey', ''), 'trainer': r.get('trainer', ''), 'weight': r.get('weight', 0), 'barrier': r.get('barrier', 0), 'age': r.get('age', 0), 'sex': r.get('sex', ''), 'sire': r.get('sire', ''), 'dam': r.get('dam', ''), 'form': r.get('form', ''), 'last20_starts': r.get('last20Starts', ''), 'career_prize_money': r.get('careerPrizeMoney', '$0'), 'stats': { 'overall': { 'starts': overall.get('starts', 0), 'wins': overall.get('wins', 0), 'places': overall.get('places', 0), 'winPercent': overall.get('winPercent', 0), 'placePercent': overall.get('placePercent', 0), }, 'track': { 'starts': track_stats.get('starts', 0), 'wins': track_stats.get('wins', 0), 'winPercent': track_stats.get('winPercent', 0), 'placePercent': track_stats.get('placePercent', 0), }, 'distance': { 'starts': distance.get('starts', 0), 'wins': distance.get('wins', 0), 'winPercent': distance.get('winPercent', 0), 'placePercent': distance.get('placePercent', 0), }, 'trackDistance': { 'starts': track_dist.get('starts', 0), 'wins': track_dist.get('wins', 0), 'winPercent': track_dist.get('winPercent', 0), 'placePercent': track_dist.get('placePercent', 0), }, 'condition': { 'starts': cond_data.get('starts', 0), 'wins': cond_data.get('wins', 0), 'winPercent': cond_data.get('winPercent', 0), 'placePercent': cond_data.get('placePercent', 0), }, 'firstUp': { 'starts': first_up.get('starts', 0), 'wins': first_up.get('wins', 0), 'winPercent': first_up.get('winPercent', 0), }, 'secondUp': { 'starts': second_up.get('starts', 0), 'wins': second_up.get('wins', 0), 'winPercent': second_up.get('winPercent', 0), }, 'conditions': cond_stats, }, } horses.append(horse) if not horses: continue # Generate race form guide race_guide = generate_race_form_guide(race_data, horses) race_file = FORM_GUIDES_DIR / f"{date}-{track}-R{race_num}.md" with open(race_file, 'w') as f: f.write(race_guide) total_races += 1 total_horses += len(horses) # Generate individual horse form guides for horse in horses: horse_guide = generate_horse_form_guide(horse) horse_file = FORM_GUIDES_DIR / f"{date}-{horse['name'].replace(' ', '-')}.md" with open(horse_file, 'w') as f: f.write(horse_guide) print(f"Generated {total_races} race form guides") print(f"Generated {total_horses} horse form guides") print(f"Saved to: {FORM_GUIDES_DIR}") print("=" * 60) print("Complete!") if __name__ == "__main__": main()