Source code for nhl_scrabble.processors.grouping

"""Player grouping utilities for analysis and reporting.

This module provides functions to group players by various attributes such as nationality, country
code, team, division, and conference.
"""

from collections import defaultdict
from typing import TypedDict

from nhl_scrabble.models.player import PlayerScore


[docs] class GroupStatistics(TypedDict): # TypedDict attributes used as type annotations """Statistics for a group of players. Attributes: player_count: Number of players in the group total_score: Sum of all player scores average_score: Mean score across all players min_score: Lowest player score max_score: Highest player score top_player: Player with highest score (as dict), or None if no players """ player_count: int total_score: int average_score: float min_score: int max_score: int top_player: dict[str, int | str] | None
[docs] def group_by_nationality(players: list[PlayerScore]) -> dict[str, list[PlayerScore]]: """Group players by nationality (full country name). Args: players: List of player scores Returns: Dictionary mapping nationality to list of players. Empty string key contains players with unknown nationality. Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western", nationality="Canada" ... ), ... PlayerScore( ... first_name="Auston", last_name="Matthews", ... full_name="Auston Matthews", first_score=18, last_score=22, ... full_score=40, team="TOR", division="Atlantic", ... conference="Eastern", nationality="United States" ... ), ... ] >>> grouped = group_by_nationality(players) >>> len(grouped["Canada"]) 1 >>> grouped["Canada"][0].full_name 'Connor McDavid' """ grouped: dict[str, list[PlayerScore]] = defaultdict(list) for player in players: # Use empty string for unknown nationality key = player.nationality or "Unknown" grouped[key].append(player) return grouped.copy()
[docs] def group_by_birth_country(players: list[PlayerScore]) -> dict[str, list[PlayerScore]]: """Group players by birth country code (ISO 3166-1 alpha-3). Args: players: List of player scores Returns: Dictionary mapping country code to list of players. Empty string key contains players with unknown country. Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western", birth_country="CAN", nationality="Canada" ... ), ... PlayerScore( ... first_name="Leon", last_name="Draisaitl", ... full_name="Leon Draisaitl", first_score=18, last_score=24, ... full_score=42, team="EDM", division="Pacific", ... conference="Western", birth_country="DEU", nationality="Germany" ... ), ... ] >>> grouped = group_by_birth_country(players) >>> len(grouped["CAN"]) 1 >>> len(grouped["DEU"]) 1 """ grouped: dict[str, list[PlayerScore]] = defaultdict(list) for player in players: # Use empty string for unknown country key = player.birth_country or "Unknown" grouped[key].append(player) return grouped.copy()
[docs] def group_by_team(players: list[PlayerScore]) -> dict[str, list[PlayerScore]]: """Group players by team abbreviation. Args: players: List of player scores Returns: Dictionary mapping team abbreviation to list of players Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western" ... ), ... PlayerScore( ... first_name="Auston", last_name="Matthews", ... full_name="Auston Matthews", first_score=18, last_score=22, ... full_score=40, team="TOR", division="Atlantic", ... conference="Eastern" ... ), ... ] >>> grouped = group_by_team(players) >>> len(grouped["EDM"]) 1 >>> len(grouped["TOR"]) 1 """ grouped: dict[str, list[PlayerScore]] = defaultdict(list) for player in players: grouped[player.team].append(player) return grouped.copy()
[docs] def group_by_division(players: list[PlayerScore]) -> dict[str, list[PlayerScore]]: """Group players by division. Args: players: List of player scores Returns: Dictionary mapping division name to list of players Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western" ... ), ... PlayerScore( ... first_name="Auston", last_name="Matthews", ... full_name="Auston Matthews", first_score=18, last_score=22, ... full_score=40, team="TOR", division="Atlantic", ... conference="Eastern" ... ), ... ] >>> grouped = group_by_division(players) >>> len(grouped["Pacific"]) 1 >>> len(grouped["Atlantic"]) 1 """ grouped: dict[str, list[PlayerScore]] = defaultdict(list) for player in players: grouped[player.division].append(player) return grouped.copy()
[docs] def group_by_conference(players: list[PlayerScore]) -> dict[str, list[PlayerScore]]: """Group players by conference. Args: players: List of player scores Returns: Dictionary mapping conference name to list of players Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western" ... ), ... PlayerScore( ... first_name="Auston", last_name="Matthews", ... full_name="Auston Matthews", first_score=18, last_score=22, ... full_score=40, team="TOR", division="Atlantic", ... conference="Eastern" ... ), ... ] >>> grouped = group_by_conference(players) >>> len(grouped["Western"]) 1 >>> len(grouped["Eastern"]) 1 """ grouped: dict[str, list[PlayerScore]] = defaultdict(list) for player in players: grouped[player.conference].append(player) return grouped.copy()
[docs] def group_by_position(players: list[PlayerScore]) -> dict[str, list[PlayerScore]]: """Group players by specific position (Center, Left Wing, Right Wing, Defense, Goalie). Args: players: List of player scores Returns: Dictionary mapping position name to list of players. Empty string key contains players with unknown position. Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western", position="Center", position_code="C", ... position_type="Forward" ... ), ... PlayerScore( ... first_name="Cale", last_name="Makar", ... full_name="Cale Makar", first_score=11, last_score=16, ... full_score=27, team="COL", division="Central", ... conference="Western", position="Defense", position_code="D", ... position_type="Defense" ... ), ... ] >>> grouped = group_by_position(players) >>> len(grouped["Center"]) 1 >>> len(grouped["Defense"]) 1 """ grouped: dict[str, list[PlayerScore]] = defaultdict(list) for player in players: # Use "Unknown" for players without position data key = player.position or "Unknown" grouped[key].append(player) return grouped.copy()
[docs] def group_by_position_type(players: list[PlayerScore]) -> dict[str, list[PlayerScore]]: """Group players by position type (Forward, Defense, Goalie). Args: players: List of player scores Returns: Dictionary mapping position type to list of players. Empty string key contains players with unknown position type. Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western", position="Center", position_code="C", ... position_type="Forward" ... ), ... PlayerScore( ... first_name="Leon", last_name="Draisaitl", ... full_name="Leon Draisaitl", first_score=18, last_score=24, ... full_score=42, team="EDM", division="Pacific", ... conference="Western", position="Left Wing", position_code="L", ... position_type="Forward" ... ), ... PlayerScore( ... first_name="Cale", last_name="Makar", ... full_name="Cale Makar", first_score=11, last_score=16, ... full_score=27, team="COL", division="Central", ... conference="Western", position="Defense", position_code="D", ... position_type="Defense" ... ), ... ] >>> grouped = group_by_position_type(players) >>> len(grouped["Forward"]) 2 >>> len(grouped["Defense"]) 1 """ grouped: dict[str, list[PlayerScore]] = defaultdict(list) for player in players: # Use "Unknown" for players without position type data key = player.position_type or "Unknown" grouped[key].append(player) return grouped.copy()
[docs] def group_by_position_code(players: list[PlayerScore]) -> dict[str, list[PlayerScore]]: """Group players by position code (C, L, R, D, G). Args: players: List of player scores Returns: Dictionary mapping position code to list of players. Empty string key contains players with unknown position code. Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western", position="Center", position_code="C", ... position_type="Forward" ... ), ... PlayerScore( ... first_name="Cale", last_name="Makar", ... full_name="Cale Makar", first_score=11, last_score=16, ... full_score=27, team="COL", division="Central", ... conference="Western", position="Defense", position_code="D", ... position_type="Defense" ... ), ... ] >>> grouped = group_by_position_code(players) >>> len(grouped["C"]) 1 >>> len(grouped["D"]) 1 """ grouped: dict[str, list[PlayerScore]] = defaultdict(list) for player in players: # Use "Unknown" for players without position code data key = player.position_code or "Unknown" grouped[key].append(player) return grouped.copy()
[docs] def calculate_group_statistics(players: list[PlayerScore]) -> GroupStatistics: """Calculate statistics for a group of players. Args: players: List of player scores in the group Returns: Dictionary with group statistics: - player_count: Number of players - total_score: Sum of all player scores - average_score: Mean score across all players - min_score: Lowest player score - max_score: Highest player score - top_player: Player with highest score Examples: >>> from nhl_scrabble.models.player import PlayerScore >>> players = [ ... PlayerScore( ... first_name="Connor", last_name="McDavid", ... full_name="Connor McDavid", first_score=20, last_score=15, ... full_score=35, team="EDM", division="Pacific", ... conference="Western" ... ), ... PlayerScore( ... first_name="Leon", last_name="Draisaitl", ... full_name="Leon Draisaitl", first_score=18, last_score=24, ... full_score=42, team="EDM", division="Pacific", ... conference="Western" ... ), ... ] >>> stats = calculate_group_statistics(players) >>> stats["player_count"] 2 >>> stats["total_score"] 77 >>> stats["average_score"] 38.5 >>> stats["top_player"]["full_name"] 'Leon Draisaitl' """ if not players: return { "player_count": 0, "total_score": 0, "average_score": 0.0, "min_score": 0, "max_score": 0, "top_player": None, } scores = [p.full_score for p in players] total_score = sum(scores) top_player = max(players, key=lambda p: p.full_score) return { "player_count": len(players), "total_score": total_score, "average_score": total_score / len(players), "min_score": min(scores), "max_score": max(scores), "top_player": top_player.to_dict(), }