#!/usr/bin/env python3 """ Script pour générer un récapitulatif complet d'un athlète Utilise les fichiers CSV pour extraire toutes les informations """ import pandas as pd import os import sys import argparse import logging from datetime import datetime from collections import defaultdict import re logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) def parse_time(time_str): """Convertir un temps en secondes pour comparaison""" if not time_str or pd.isna(time_str): return None # Format HH:MM:SS if ':' in time_str: parts = time_str.split(':') if len(parts) == 3: try: h, m, s = parts return int(h) * 3600 + int(m) * 60 + float(s) except: pass elif len(parts) == 2: try: m, s = parts return int(m) * 60 + float(s) except: pass # Format en secondes ou minutes try: # Enlever les non-numériques clean = re.sub(r'[^\d.,]', '', str(time_str)) return float(clean.replace(',', '.')) except: return None def format_time(seconds): """Formater des secondes en HH:MM:SS""" if not seconds: return "N/A" hours = int(seconds // 3600) minutes = int((seconds % 3600) // 60) secs = seconds % 60 if hours > 0: return f"{hours}:{minutes:02d}:{secs:05.2f}" elif minutes > 0: return f"{minutes}:{secs:05.2f}" else: return f"{secs:.2f}s" def get_athlete_summary(nom, prenom=None, data_dir="data"): """Générer un récapitulatif complet d'un athlète""" results_path = os.path.join(data_dir, 'resultats', 'results.csv') courses_path = os.path.join(data_dir, 'courses', 'courses_list.csv') if not os.path.exists(results_path): logging.error(f"Fichier de résultats introuvable: {results_path}") return None try: df_results = pd.read_csv(results_path, encoding='utf-8-sig') # Filtre par nom mask = df_results['nom'].str.contains(nom, case=False, na=False) if prenom: mask &= df_results['prenom'].str.contains(prenom, case=False, na=False) results = df_results[mask] if results.empty: return None # Charger les courses pour plus d'info courses_df = None if os.path.exists(courses_path): courses_df = pd.read_csv(courses_path, encoding='utf-8-sig') # Créer le récapitulatif summary = { 'nom': results.iloc[0]['nom'], 'prenom': results.iloc[0]['prenom'], 'club': results.iloc[0]['club'], 'total_courses': len(results), 'categories': sorted(results['categorie'].dropna().unique().tolist()), 'results': [], 'statistics': {} } # Calculer les statistiques places = [] times = [] podiums = 0 victoires = 0 courses_by_year = defaultdict(int) courses_by_type = defaultdict(int) for idx, result in results.iterrows(): # Extraire les places try: place = int(result['place']) places.append(place) if place == 1: victoires += 1 podiums += 1 elif place <= 3: podiums += 1 except: pass # Extraire les temps time_seconds = parse_time(result.get('temps', result.get('resultat', ''))) if time_seconds: times.append(time_seconds) # Extraire les infos de course si disponible course_info = {} if courses_df is not None and 'course_url' in result: course_match = courses_df[courses_df['lien'] == result['course_url']] if not course_match.empty: course_info = course_match.iloc[0].to_dict() # Extraire l'année de la course if 'date' in result and pd.notna(result['date']): try: year = str(result['date']).split('-')[0] courses_by_year[year] += 1 except: pass elif course_info.get('date'): try: year = str(course_info['date']).split('-')[0] courses_by_year[year] += 1 except: pass # Extraire le type de course if course_info.get('type'): courses_by_type[course_info['type']] += 1 # Ajouter le résultat détaillé detailed_result = result.to_dict() detailed_result['course_details'] = course_info summary['results'].append(detailed_result) # Calculer les statistiques finales summary['statistics'] = { 'victoires': victoires, 'podiums': podiums, 'places_moyenne': sum(places) / len(places) if places else 0, 'meilleure_place': min(places) if places else None, 'pire_place': max(places) if places else None, 'meilleur_temps': min(times) if times else None, 'temps_moyen': sum(times) / len(times) if times else None, 'courses_par_annee': dict(courses_by_year), 'courses_par_type': dict(courses_by_type), 'categories': summary['categories'] } return summary except Exception as e: logging.error(f"Erreur lors de la génération du récapitulatif: {e}") return None def display_athlete_summary(summary, show_full_results=False): """Afficher le récapitulatif de l'athlète""" if not summary: print("\n❌ Impossible de générer le récapitulatif") return stats = summary['statistics'] print(f"\n{'='*80}") print(f"📊 RÉCAPITULATIF DE {summary['prenom']} {summary['nom']}") print(f"{'='*80}\n") # Informations générales print(f"🏟️ Club: {summary['club']}") print(f"🏃 Total des courses: {summary['total_courses']}") print() # Statistiques de performance print(f"🏆 STATISTIQUES DE PERFORMANCE") print(f"{'─'*40}") print(f"Victoires: {stats['victoires']}") print(f"Podiums (top 3): {stats['podiums']}") print(f"Meilleure place: {stats['meilleure_place']}") print(f"Place moyenne: {stats['places_moyenne']:.2f}") print() # Statistiques de temps if stats['meilleur_temps']: print(f"⏱️ STATISTIQUES DE TEMPS") print(f"{'─'*40}") print(f"Meilleur temps: {format_time(stats['meilleur_temps'])}") print(f"Temps moyen: {format_time(stats['temps_moyen'])}") print() # Répartition par année if stats['courses_par_annee']: print(f"📅 RÉPARTITION PAR ANNÉE") print(f"{'─'*40}") for year, count in sorted(stats['courses_par_annee'].items()): print(f"{year}: {count} course(s)") print() # Répartition par type if stats['courses_par_type']: print(f"🏷️ RÉPARTITION PAR TYPE DE COURSE") print(f"{'─'*40}") for course_type, count in sorted(stats['courses_par_type'].items(), key=lambda x: x[1], reverse=True): print(f"{course_type}: {count} course(s)") print() # Catégories if stats['categories']: print(f"📊 CATÉGORIES") print(f"{'─'*40}") print(", ".join(cat for cat in stats['categories'] if cat)) print() print(f"{'='*80}\n") # Résultats détaillés if show_full_results: print(f"📋 LISTE COMPLÈTE DES RÉSULTATS") print(f"{'='*80}\n") for i, result in enumerate(summary['results'], 1): print(f"{i}. {result.get('course_details', {}).get('nom', 'Inconnu')}") if result.get('course_details', {}).get('date'): print(f" 📅 {result['course_details']['date']}") if result.get('course_details', {}).get('lieu'): print(f" 📍 {result['course_details']['lieu']}") print(f" 🏆 Place: {result.get('place', 'N/A')}") temps = result.get('temps', result.get('resultat', 'N/A')) print(f" ⏱️ Temps: {temps}") if result.get('categorie'): print(f" 🏷️ Catégorie: {result['categorie']}") if result.get('points'): print(f" 🎯 Points: {result['points']}") print() print(f"{'='*80}\n") def export_summary_txt(summary, output_dir="data"): """Exporter le récapitulatif en fichier texte""" os.makedirs(os.path.join(output_dir, 'exports'), exist_ok=True) filename = f"summary_{summary['nom']}_{summary['prenom']}.txt" filepath = os.path.join(output_dir, 'exports', filename.replace(" ", "_")) with open(filepath, 'w', encoding='utf-8') as f: f.write("="*80 + "\n") f.write(f"RÉCAPITULATIF DE {summary['prenom']} {summary['nom']}\n") f.write("="*80 + "\n\n") stats = summary['statistics'] # Informations générales f.write(f"Club: {summary['club']}\n") f.write(f"Total des courses: {summary['total_courses']}\n\n") # Statistiques f.write("STATISTIQUES DE PERFORMANCE\n") f.write("-"*40 + "\n") f.write(f"Victoires: {stats['victoires']}\n") f.write(f"Podiums: {stats['podiums']}\n") f.write(f"Meilleure place: {stats['meilleure_place']}\n") f.write(f"Place moyenne: {stats['places_moyenne']:.2f}\n\n") # Temps if stats['meilleur_temps']: f.write("STATISTIQUES DE TEMPS\n") f.write("-"*40 + "\n") f.write(f"Meilleur temps: {format_time(stats['meilleur_temps'])}\n") f.write(f"Temps moyen: {format_time(stats['temps_moyen'])}\n\n") # Répartition if stats['courses_par_annee']: f.write("RÉPARTITION PAR ANNÉE\n") f.write("-"*40 + "\n") for year, count in sorted(stats['courses_par_annee'].items()): f.write(f"{year}: {count}\n") f.write("\n") if stats['courses_par_type']: f.write("RÉPARTITION PAR TYPE\n") f.write("-"*40 + "\n") for course_type, count in sorted(stats['courses_par_type'].items(), key=lambda x: x[1], reverse=True): f.write(f"{course_type}: {count}\n") f.write("\n") # Liste des courses f.write("LISTE DES COURSES\n") f.write("="*80 + "\n\n") for i, result in enumerate(summary['results'], 1): f.write(f"{i}. {result.get('course_details', {}).get('nom', 'Inconnu')}\n") f.write(f" Date: {result.get('course_details', {}).get('date', 'N/A')}\n") f.write(f" Place: {result.get('place', 'N/A')}\n") f.write(f" Temps: {result.get('temps', result.get('resultat', 'N/A'))}\n") f.write("\n") logging.info(f"Exporté le récapitulatif dans {filepath}") return filepath def main(): parser = argparse.ArgumentParser(description='Générer un récapitulatif complet d\'un athlète') parser.add_argument('nom', help='Nom de l\'athlète') parser.add_argument('--prenom', help='Prénom de l\'athlète') parser.add_argument('--data-dir', default='data', help='Répertoire des données CSV') parser.add_argument('--full', action='store_true', help='Afficher la liste complète des résultats') parser.add_argument('--export', action='store_true', help='Exporter le récapitulatif en fichier texte') args = parser.parse_args() # Générer le récapitulatif print(f"\n📊 Génération du récapitulatif pour {args.prenom or ''} {args.nom}...") summary = get_athlete_summary(args.nom, args.prenom, args.data_dir) if summary: display_athlete_summary(summary, show_full_results=args.full) if args.export: filepath = export_summary_txt(summary, args.data_dir) print(f"💾 Exporté dans: {filepath}") else: print("\n❌ Aucun résultat trouvé pour cet athlète") print("💡 Vérifiez que les données ont été scrapées avec:") print(" python ffa_cli.py scrape --fetch-details") if __name__ == "__main__": main()