""" Data models and business logic for TV_APP Handles all calculations and data structure creation """ from datetime import datetime import random NUM_CAMERAS = 6 class Tournament: """Tournament creation and management""" @staticmethod def create_league(enabled_players, tournament_type): """Create a new league with 5 tournaments""" league_id = f"league_{datetime.now().strftime('%Y%m%d_%H%M%S')}" league_data = { 'league_id': league_id, 'created_at': datetime.now().isoformat(), 'tournament_type': tournament_type, 'total_tournaments': 5, 'current_tournament': 0, 'participants': {}, 'completed_tournaments': [], 'league_finished': False } # Initialize participants for player in enabled_players: league_data['participants'][str(player['id'])] = { 'name': player['name'], 'joker_used': False, 'tournament_results': [], 'total_score': 0, 'final_score': 0, 'tournaments_participated': 0 } return league_data @staticmethod def create_draft(enabled_players, tournament_type='20_targets', league_tournament_number=None): """Create draft groups of 6 players and organize rounds""" if len(enabled_players) < 1: return None # Shuffle players for random grouping players_copy = enabled_players.copy() random.shuffle(players_copy) # Create groups of up to 6 groups = [] for i in range(0, len(players_copy), NUM_CAMERAS): group = players_copy[i:i + NUM_CAMERAS] groups.append(group) # Create rounds rounds = [] for i, group in enumerate(groups): rounds.append({ 'round_number': i + 1, 'players': group, 'status': 'pending' if i == 0 else 'waiting' }) tournament_data = { 'rounds': rounds, 'created_at': datetime.now().isoformat(), 'total_players': len(enabled_players), 'total_rounds': len(rounds), 'current_round': 1, 'tournament_type': tournament_type } if league_tournament_number: tournament_data['league_tournament_number'] = league_tournament_number return tournament_data @staticmethod def create_results_structure(tournament_data): """Create results structure for a tournament""" if not tournament_data: return None tournament_type = tournament_data.get('tournament_type', '20_targets') # Determine target count and shots per target if tournament_type == '40_targets': num_targets = 40 shots_per_target = 2 elif tournament_type == '4_targets': num_targets = 4 shots_per_target = 5 else: # 20_targets (default) num_targets = 20 shots_per_target = 2 results = { 'tournament_id': tournament_data.get('created_at', datetime.now().isoformat()), 'tournament_type': tournament_type, 'participants': {}, 'tournament_finished': False, 'created_at': datetime.now().isoformat() } # Add league info if present if 'league_tournament_number' in tournament_data: results['league_tournament_number'] = tournament_data['league_tournament_number'] # Create structure for each participant all_players = [] for round_data in tournament_data['rounds']: all_players.extend(round_data['players']) for player in all_players: player_id = str(player['id']) # Create target structure based on tournament type targets = {} for i in range(1, num_targets + 1): target = {} for j in range(1, shots_per_target + 1): target[f'shot{j}'] = None targets[str(i)] = target results['participants'][player_id] = { 'name': player['name'], 'targets': targets, 'total_score': 0, 'completed': False } return results class Scoring: """Score calculation and ranking""" @staticmethod def calculate_total_score(targets): """Calculate total score from targets, treating None as 0""" total = 0 for target in targets.values(): for shot_key, shot_value in target.items(): if shot_key.startswith('shot') and shot_value is not None: total += shot_value return total @staticmethod def is_participant_completed(targets): """Check if a participant has completed all targets""" for target in targets.values(): for shot_key, shot_value in target.items(): if shot_key.startswith('shot') and shot_value is None: return False return True @staticmethod def calculate_league_final_scores(league_data): """Calculate final league scores using best 4 tournaments""" for participant_id, participant in league_data['participants'].items(): tournament_scores = [] # Get all tournament scores where player participated for result in participant['tournament_results']: if result['participated']: tournament_scores.append(result['score']) # Sort scores descending and take best 4 tournament_scores.sort(reverse=True) best_scores = tournament_scores[:4] if len(tournament_scores) > 4 else tournament_scores participant['final_score'] = sum(best_scores) participant['tournaments_participated'] = len(tournament_scores) @staticmethod def get_league_final_rankings(league_data): """Get final league rankings sorted by final score""" participants = [] for player_id, data in league_data['participants'].items(): # Calculate total 10s across all tournaments total_tens = sum( result.get('tens_count', 0) for result in data['tournament_results'] if result.get('participated', False) ) participants.append({ 'id': player_id, 'name': data['name'], 'final_score': data['final_score'], 'total_score': data['total_score'], 'tournaments_participated': data['tournaments_participated'], 'joker_used': data['joker_used'], 'tournament_results': data['tournament_results'], 'total_tens': total_tens }) # Sort by final score (best 4 tournaments) descending, then by total 10s participants.sort(key=lambda x: (x['final_score'], x['total_tens']), reverse=True) # Add rankings for i, participant in enumerate(participants): participant['rank'] = i + 1 return participants @staticmethod def calculate_current_league_standings(league_data): """Calculate current league standings during active league""" participants = [] for player_id, data in league_data['participants'].items(): tournament_scores = [] completed_tournaments = 0 total_tens = 0 for result in data['tournament_results']: if result['participated']: tournament_scores.append(result['score']) completed_tournaments += 1 total_tens += result.get('tens_count', 0) # Current score is sum of all completed tournaments current_total = sum(tournament_scores) # For display, show what the final score would be if we took best 4 now tournament_scores.sort(reverse=True) projected_final = sum(tournament_scores[:4]) if len(tournament_scores) >= 4 else sum(tournament_scores) participants.append({ 'id': player_id, 'name': data['name'], 'current_total': current_total, 'projected_final': projected_final, 'tournaments_completed': completed_tournaments, 'joker_used': data['joker_used'], 'tournament_results': data['tournament_results'], 'total_tens': total_tens }) # Sort by current total score (descending), then by total 10s participants.sort(key=lambda x: (x['current_total'], x['total_tens']), reverse=True) # Add rankings for i, participant in enumerate(participants): participant['rank'] = i + 1 return participants class RoundManager: """Tournament round management""" @staticmethod def get_current_round_data(tournament_state): """Get current round data from tournament""" if not tournament_state: return None current_round_num = tournament_state.get('current_round', 1) # Find the current round for round_data in tournament_state['rounds']: if round_data['round_number'] == current_round_num: return round_data return None @staticmethod def get_tournament_format_description(tournament_type): """Get tournament format description""" formats = { '20_targets': '20 Targets, 2 Shots Per Target', '40_targets': '40 Targets, 2 Shots Per Target', '4_targets': '4 Targets, 5 Shots Per Target' } return formats.get(tournament_type, 'Unknown Format')