AlvaroMros's picture
Refactor imports to use absolute paths and clean up scripts
9678fdb
import csv
import os
from datetime import datetime
from ..config import FIGHTS_CSV_PATH, FIGHTERS_CSV_PATH
# --- ELO Configuration ---
INITIAL_ELO = 1500
K_FACTOR = 40
# --- End Configuration ---
def calculate_expected_score(rating1, rating2):
"""Calculates the expected score for player 1 against player 2."""
return 1 / (1 + 10 ** ((rating2 - rating1) / 400))
def update_elo(winner_elo, loser_elo):
"""Calculates the new ELO ratings for a win/loss scenario."""
expected_win = calculate_expected_score(winner_elo, loser_elo)
change = K_FACTOR * (1 - expected_win)
return winner_elo + change, loser_elo - change
def update_elo_draw(elo1, elo2):
"""Calculates the new ELO ratings for a draw."""
expected1 = calculate_expected_score(elo1, elo2)
change1 = K_FACTOR * (0.5 - expected1)
expected2 = calculate_expected_score(elo2, elo1)
change2 = K_FACTOR * (0.5 - expected2)
return elo1 + change1, elo2 + change2
def process_fights_for_elo(fights_data=FIGHTS_CSV_PATH):
"""
Processes fights chronologically to calculate ELO scores.
Accepts either a CSV file path or a pre-loaded list of fights.
"""
fights = []
if isinstance(fights_data, str):
# If a string is passed, treat it as a file path
if not os.path.exists(fights_data):
print(f"Error: Fights data file not found at '{fights_data}'.")
return None
with open(fights_data, 'r', encoding='utf-8') as f:
fights = list(csv.DictReader(f))
elif isinstance(fights_data, list):
# If a list is passed, use it directly
fights = fights_data
else:
print(f"Error: Invalid data type passed to process_fights_for_elo: {type(fights_data)}")
return None
# Sort fights by date to process them in chronological order.
# This is crucial if loading from a file and a good safeguard if a list is passed.
try:
fights.sort(key=lambda x: datetime.strptime(x['event_date'], '%B %d, %Y'))
except (ValueError, KeyError) as e:
print(f"Error sorting fights by date: {e}")
return None
elos = {}
for fight in fights:
fighter1 = fight.get('fighter_1')
fighter2 = fight.get('fighter_2')
winner = fight.get('winner')
# Initialize ELO for new fighters
if fighter1 not in elos: elos[fighter1] = INITIAL_ELO
if fighter2 not in elos: elos[fighter2] = INITIAL_ELO
elo1 = elos[fighter1]
elo2 = elos[fighter2]
if winner == fighter1:
elos[fighter1], elos[fighter2] = update_elo(elo1, elo2)
elif winner == fighter2:
elos[fighter2], elos[fighter1] = update_elo(elo2, elo1)
elif winner == "Draw":
elos[fighter1], elos[fighter2] = update_elo_draw(elo1, elo2)
# NC (No Contest) fights do not affect ELO
return elos
def add_elo_to_fighters_csv(elos, fighters_csv_path=FIGHTERS_CSV_PATH):
"""
Adds the final ELO scores as a new column to the fighters CSV data.
"""
if not elos:
print("No ELO data to process. Aborting.")
return
if not os.path.exists(fighters_csv_path):
print(f"Error: Fighters data file not found at '{fighters_csv_path}'. Cannot add ELO column.")
return
rows = []
with open(fighters_csv_path, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
headers = reader.fieldnames
# Ensure 'elo' column is added if not present
if 'elo' not in headers:
headers.append('elo')
rows = list(reader)
for row in rows:
full_name = f"{row.get('first_name', '')} {row.get('last_name', '')}".strip()
row['elo'] = round(elos.get(full_name, INITIAL_ELO))
with open(fighters_csv_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=headers)
writer.writeheader()
writer.writerows(rows)
print(f"Successfully updated '{fighters_csv_path}' with ELO ratings.")
def main():
print("--- Starting ELO Calculation ---")
final_elos = process_fights_for_elo()
if final_elos:
add_elo_to_fighters_csv(final_elos)
# Sort fighters by ELO and print the top 10
sorted_fighters = sorted(final_elos.items(), key=lambda item: item[1], reverse=True)
print("\n--- Top 10 Fighters by ELO Rating ---")
for i, (fighter, elo) in enumerate(sorted_fighters[:10]):
print(f"{i+1}. {fighter}: {round(elo)}")
print("------------------------------------")