Processors Module

Business logic for team processing and playoff calculations.

The processors module handles aggregation of player scores into team totals and NHL playoff bracket generation based on Scrabble scores.

Data processing modules.

class nhl_scrabble.processors.GroupStatistics[source][source]

Bases: TypedDict

Statistics for a group of players.

player_count[source]

Number of players in the group

total_score[source]

Sum of all player scores

average_score[source]

Mean score across all players

min_score[source]

Lowest player score

max_score[source]

Highest player score

top_player[source]

Player with highest score (as dict), or None if no players

player_count: int[source]
total_score: int[source]
average_score: float[source]
min_score: int[source]
max_score: int[source]
top_player: dict[str, int | str] | None[source]
class nhl_scrabble.processors.PlayoffCalculator[source][source]

Bases: object

Calculate NHL-style playoff standings based on Scrabble scores.

This class implements the NHL playoff structure: - Top 3 teams from each division automatically qualify - 2 wild card spots per conference (next best teams by points) - Tiebreaker: average points per player - Status indicators: p (Presidents’ Trophy), z (Conference leader),

y (Division leader), x (Playoff spot), e (Eliminated)

calculate_playoff_standings(team_scores)[source][source]

Calculate complete playoff standings with all teams.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Returns:

{

‘Eastern’: [list of PlayoffTeam objects for Eastern Conference], ‘Western’: [list of PlayoffTeam objects for Western Conference],

}

Return type:

dict[str, list[PlayoffTeam]]

Examples

>>> calculator = PlayoffCalculator()
>>> standings = calculator.calculate_playoff_standings(team_scores)
>>> "Eastern" in standings
True
class nhl_scrabble.processors.TeamProcessor(api_client, scorer, max_workers=5)[source][source]

Bases: object

Process team roster data and calculate aggregate scores.

This class orchestrates fetching roster data for all NHL teams, calculating Scrabble scores for all players, and aggregating statistics at team, division, and conference levels.

Supports concurrent fetching of team rosters to improve performance for I/O-bound operations.

__init__(api_client, scorer, max_workers=5)[source][source]

Initialize the team processor.

Parameters:
  • api_client (APIClientProtocol) – NHL API client for fetching data (APIClientProtocol)

  • scorer (ScorerProtocol) – Scrabble scorer for calculating player scores (ScorerProtocol)

  • max_workers (int) – Maximum number of concurrent API requests (default: 5)

Examples

Initialize with mocked dependencies:

>>> class MockAPIClient:
...     def get_teams(self):
...         return {"TOR": {"division": "Atlantic", "conference": "Eastern"}}
...     def get_team_roster(self, team):
...         return {"forwards": [], "defensemen": [], "goalies": []}
...     def close(self):
...         pass
>>> class MockScorer:
...     def score_player(self, player):
...         return 10
>>> client = MockAPIClient()
>>> scorer = MockScorer()
>>> processor = TeamProcessor(client, scorer, max_workers=3)
>>> processor.max_workers
3
process_all_teams(progress_callback=None, season=None)[source][source]

Process all NHL teams and calculate scores with concurrent fetching.

Uses ThreadPoolExecutor to fetch team rosters concurrently, improving performance for I/O-bound operations. The number of concurrent workers is controlled by max_workers.

Parameters:
  • progress_callback (Callable[[str], None] | None) – Optional callback to report progress after each team. Called with team abbreviation after successfully processing each team.

  • season (str | None) – Optional season to analyze (format: YYYYYYYY, e.g., 20222023). If None, fetches current season data.

Returns:

  • Dictionary mapping team abbreviations to TeamScore objects

  • List of all PlayerScore objects across all teams

  • List of team abbreviations that failed to fetch

Return type:

tuple[dict[str, TeamScore], list[PlayerScore], list[str]]

Examples

>>> client = NHLApiClient()
>>> scorer = ScrabbleScorer()
>>> processor = TeamProcessor(client, scorer, max_workers=5)
>>> teams, players, failed = processor.process_all_teams()
>>> len(teams) > 0
True
>>> teams_2022, players_2022, failed = processor.process_all_teams(season="20222023")
>>> len(teams_2022) > 0
True
calculate_division_standings(team_scores)[source][source]

Calculate division-level standings from team scores.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Return type:

dict[str, DivisionStandings]

Returns:

Dictionary mapping division names to DivisionStandings objects

Examples

>>> standings = processor.calculate_division_standings(teams)
>>> "Atlantic" in standings
True
calculate_conference_standings(team_scores)[source][source]

Calculate conference-level standings from team scores.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Return type:

dict[str, ConferenceStandings]

Returns:

Dictionary mapping conference names to ConferenceStandings objects

Examples

>>> standings = processor.calculate_conference_standings(teams)
>>> "Eastern" in standings
True
nhl_scrabble.processors.calculate_group_statistics(players)[source][source]

Calculate statistics for a group of players.

Parameters:

players (list[PlayerScore]) – List of player scores in the group

Returns:

  • 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

Return type:

GroupStatistics

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'
nhl_scrabble.processors.group_by_birth_country(players)[source][source]

Group players by birth country code (ISO 3166-1 alpha-3).

Parameters:

players (list[PlayerScore]) – List of player scores

Return type:

dict[str, list[PlayerScore]]

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
nhl_scrabble.processors.group_by_conference(players)[source][source]

Group players by conference.

Parameters:

players (list[PlayerScore]) – List of player scores

Return type:

dict[str, list[PlayerScore]]

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
nhl_scrabble.processors.group_by_division(players)[source][source]

Group players by division.

Parameters:

players (list[PlayerScore]) – List of player scores

Return type:

dict[str, list[PlayerScore]]

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
nhl_scrabble.processors.group_by_nationality(players)[source][source]

Group players by nationality (full country name).

Parameters:

players (list[PlayerScore]) – List of player scores

Return type:

dict[str, list[PlayerScore]]

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'
nhl_scrabble.processors.group_by_position(players)[source][source]

Group players by specific position (Center, Left Wing, Right Wing, Defense, Goalie).

Parameters:

players (list[PlayerScore]) – List of player scores

Return type:

dict[str, list[PlayerScore]]

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
nhl_scrabble.processors.group_by_position_code(players)[source][source]

Group players by position code (C, L, R, D, G).

Parameters:

players (list[PlayerScore]) – List of player scores

Return type:

dict[str, list[PlayerScore]]

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
nhl_scrabble.processors.group_by_position_type(players)[source][source]

Group players by position type (Forward, Defense, Goalie).

Parameters:

players (list[PlayerScore]) – List of player scores

Return type:

dict[str, list[PlayerScore]]

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
nhl_scrabble.processors.group_by_team(players)[source][source]

Group players by team abbreviation.

Parameters:

players (list[PlayerScore]) – List of player scores

Return type:

dict[str, list[PlayerScore]]

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

Team Processor

Team data processing module.

class nhl_scrabble.processors.team_processor.TeamProcessor(api_client, scorer, max_workers=5)[source][source]

Bases: object

Process team roster data and calculate aggregate scores.

This class orchestrates fetching roster data for all NHL teams, calculating Scrabble scores for all players, and aggregating statistics at team, division, and conference levels.

Supports concurrent fetching of team rosters to improve performance for I/O-bound operations.

__init__(api_client, scorer, max_workers=5)[source][source]

Initialize the team processor.

Parameters:
  • api_client (APIClientProtocol) – NHL API client for fetching data (APIClientProtocol)

  • scorer (ScorerProtocol) – Scrabble scorer for calculating player scores (ScorerProtocol)

  • max_workers (int) – Maximum number of concurrent API requests (default: 5)

Examples

Initialize with mocked dependencies:

>>> class MockAPIClient:
...     def get_teams(self):
...         return {"TOR": {"division": "Atlantic", "conference": "Eastern"}}
...     def get_team_roster(self, team):
...         return {"forwards": [], "defensemen": [], "goalies": []}
...     def close(self):
...         pass
>>> class MockScorer:
...     def score_player(self, player):
...         return 10
>>> client = MockAPIClient()
>>> scorer = MockScorer()
>>> processor = TeamProcessor(client, scorer, max_workers=3)
>>> processor.max_workers
3
process_all_teams(progress_callback=None, season=None)[source][source]

Process all NHL teams and calculate scores with concurrent fetching.

Uses ThreadPoolExecutor to fetch team rosters concurrently, improving performance for I/O-bound operations. The number of concurrent workers is controlled by max_workers.

Parameters:
  • progress_callback (Callable[[str], None] | None) – Optional callback to report progress after each team. Called with team abbreviation after successfully processing each team.

  • season (str | None) – Optional season to analyze (format: YYYYYYYY, e.g., 20222023). If None, fetches current season data.

Returns:

  • Dictionary mapping team abbreviations to TeamScore objects

  • List of all PlayerScore objects across all teams

  • List of team abbreviations that failed to fetch

Return type:

tuple[dict[str, TeamScore], list[PlayerScore], list[str]]

Examples

>>> client = NHLApiClient()
>>> scorer = ScrabbleScorer()
>>> processor = TeamProcessor(client, scorer, max_workers=5)
>>> teams, players, failed = processor.process_all_teams()
>>> len(teams) > 0
True
>>> teams_2022, players_2022, failed = processor.process_all_teams(season="20222023")
>>> len(teams_2022) > 0
True
calculate_division_standings(team_scores)[source][source]

Calculate division-level standings from team scores.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Return type:

dict[str, DivisionStandings]

Returns:

Dictionary mapping division names to DivisionStandings objects

Examples

>>> standings = processor.calculate_division_standings(teams)
>>> "Atlantic" in standings
True
calculate_conference_standings(team_scores)[source][source]

Calculate conference-level standings from team scores.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Return type:

dict[str, ConferenceStandings]

Returns:

Dictionary mapping conference names to ConferenceStandings objects

Examples

>>> standings = processor.calculate_conference_standings(teams)
>>> "Eastern" in standings
True

TeamProcessor

class nhl_scrabble.processors.team_processor.TeamProcessor(api_client, scorer, max_workers=5)[source][source]

Bases: object

Process team roster data and calculate aggregate scores.

This class orchestrates fetching roster data for all NHL teams, calculating Scrabble scores for all players, and aggregating statistics at team, division, and conference levels.

Supports concurrent fetching of team rosters to improve performance for I/O-bound operations.

__init__(api_client, scorer, max_workers=5)[source][source]

Initialize the team processor.

Parameters:
  • api_client (APIClientProtocol) – NHL API client for fetching data (APIClientProtocol)

  • scorer (ScorerProtocol) – Scrabble scorer for calculating player scores (ScorerProtocol)

  • max_workers (int) – Maximum number of concurrent API requests (default: 5)

Examples

Initialize with mocked dependencies:

>>> class MockAPIClient:
...     def get_teams(self):
...         return {"TOR": {"division": "Atlantic", "conference": "Eastern"}}
...     def get_team_roster(self, team):
...         return {"forwards": [], "defensemen": [], "goalies": []}
...     def close(self):
...         pass
>>> class MockScorer:
...     def score_player(self, player):
...         return 10
>>> client = MockAPIClient()
>>> scorer = MockScorer()
>>> processor = TeamProcessor(client, scorer, max_workers=3)
>>> processor.max_workers
3
process_all_teams(progress_callback=None, season=None)[source][source]

Process all NHL teams and calculate scores with concurrent fetching.

Uses ThreadPoolExecutor to fetch team rosters concurrently, improving performance for I/O-bound operations. The number of concurrent workers is controlled by max_workers.

Parameters:
  • progress_callback (Callable[[str], None] | None) – Optional callback to report progress after each team. Called with team abbreviation after successfully processing each team.

  • season (str | None) – Optional season to analyze (format: YYYYYYYY, e.g., 20222023). If None, fetches current season data.

Returns:

  • Dictionary mapping team abbreviations to TeamScore objects

  • List of all PlayerScore objects across all teams

  • List of team abbreviations that failed to fetch

Return type:

tuple[dict[str, TeamScore], list[PlayerScore], list[str]]

Examples

>>> client = NHLApiClient()
>>> scorer = ScrabbleScorer()
>>> processor = TeamProcessor(client, scorer, max_workers=5)
>>> teams, players, failed = processor.process_all_teams()
>>> len(teams) > 0
True
>>> teams_2022, players_2022, failed = processor.process_all_teams(season="20222023")
>>> len(teams_2022) > 0
True
calculate_division_standings(team_scores)[source][source]

Calculate division-level standings from team scores.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Return type:

dict[str, DivisionStandings]

Returns:

Dictionary mapping division names to DivisionStandings objects

Examples

>>> standings = processor.calculate_division_standings(teams)
>>> "Atlantic" in standings
True
calculate_conference_standings(team_scores)[source][source]

Calculate conference-level standings from team scores.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Return type:

dict[str, ConferenceStandings]

Returns:

Dictionary mapping conference names to ConferenceStandings objects

Examples

>>> standings = processor.calculate_conference_standings(teams)
>>> "Eastern" in standings
True

Aggregate player scores into team totals.

Methods:

  • process_teams() - Process all teams and calculate totals

  • get_division_standings() - Group teams by division

  • get_conference_standings() - Group teams by conference

Example:

from nhl_scrabble.processors import TeamProcessor
from nhl_scrabble.scoring import ScrabbleScorer

# Score all players
scorer = ScrabbleScorer()
all_players = []
for team_abbrev, roster in rosters.items():
    all_players.extend(scorer.score_player(p) for p in roster)

# Process teams
processor = TeamProcessor()
team_scores = processor.process_teams(teams, all_players)

# Get standings
division_standings = processor.get_division_standings(team_scores)
conference_standings = processor.get_conference_standings(team_scores)

process_teams

Aggregate player scores by team.

Parameters:

  • teams - List of Team objects

  • player_scores - List of PlayerScore objects (all players)

Returns:

  • List of TeamScore objects sorted by total (descending)

Example:

team_scores = processor.process_teams(teams, all_players)

for team_score in team_scores[:5]:
    print(f"{team_score.team.name}: {team_score.total} points")
    print(f"  Players: {team_score.player_count}")
    print(f"  Average: {team_score.avg_per_player:.1f}")

get_division_standings

Group teams by division with rankings.

Parameters:

  • team_scores - List of TeamScore objects

Returns:

  • Dictionary mapping division names to DivisionStandings objects

Example:

divisions = processor.get_division_standings(team_scores)

for division_name, standings in divisions.items():
    print(f"\n{division_name} Division:")
    for i, team in enumerate(standings.teams, 1):
        print(f"  {i}. {team.team.name}: {team.total}")

get_conference_standings

Group teams by conference with rankings.

Parameters:

  • team_scores - List of TeamScore objects

Returns:

  • Dictionary mapping conference names to ConferenceStandings objects

Example:

conferences = processor.get_conference_standings(team_scores)

for conf_name, standings in conferences.items():
    print(f"\n{conf_name} Conference:")
    print(f"  Total: {standings.total}")
    print(f"  Teams: {len(standings.teams)}")
    print(f"  Average: {standings.avg_per_team:.1f}")

Playoff Calculator

Playoff standings calculator module.

class nhl_scrabble.processors.playoff_calculator.PlayoffCalculator[source][source]

Bases: object

Calculate NHL-style playoff standings based on Scrabble scores.

This class implements the NHL playoff structure: - Top 3 teams from each division automatically qualify - 2 wild card spots per conference (next best teams by points) - Tiebreaker: average points per player - Status indicators: p (Presidents’ Trophy), z (Conference leader),

y (Division leader), x (Playoff spot), e (Eliminated)

calculate_playoff_standings(team_scores)[source][source]

Calculate complete playoff standings with all teams.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Returns:

{

‘Eastern’: [list of PlayoffTeam objects for Eastern Conference], ‘Western’: [list of PlayoffTeam objects for Western Conference],

}

Return type:

dict[str, list[PlayoffTeam]]

Examples

>>> calculator = PlayoffCalculator()
>>> standings = calculator.calculate_playoff_standings(team_scores)
>>> "Eastern" in standings
True

PlayoffCalculator

class nhl_scrabble.processors.playoff_calculator.PlayoffCalculator[source][source]

Bases: object

Calculate NHL-style playoff standings based on Scrabble scores.

This class implements the NHL playoff structure: - Top 3 teams from each division automatically qualify - 2 wild card spots per conference (next best teams by points) - Tiebreaker: average points per player - Status indicators: p (Presidents’ Trophy), z (Conference leader),

y (Division leader), x (Playoff spot), e (Eliminated)

calculate_playoff_standings(team_scores)[source][source]

Calculate complete playoff standings with all teams.

Parameters:

team_scores (dict[str, TeamScore]) – Dictionary of TeamScore objects by team abbreviation

Returns:

{

‘Eastern’: [list of PlayoffTeam objects for Eastern Conference], ‘Western’: [list of PlayoffTeam objects for Western Conference],

}

Return type:

dict[str, list[PlayoffTeam]]

Examples

>>> calculator = PlayoffCalculator()
>>> standings = calculator.calculate_playoff_standings(team_scores)
>>> "Eastern" in standings
True

Calculate NHL playoff bracket based on Scrabble scores.

Follows NHL playoff structure:

  • 8 teams per conference (16 total)

  • 3 division leaders per conference

  • 2 wild card teams per conference

  • Playoff indicators: y (clinched), x (wild card), z (conference leader), p (Presidents’ Trophy)

Example:

from nhl_scrabble.processors import PlayoffCalculator

calculator = PlayoffCalculator()
bracket = calculator.calculate_playoff_bracket(team_scores)

print("Eastern Conference Playoffs:")
for team in bracket["Eastern"][:8]:
    indicator = team.get("playoff_indicator", "")
    print(f"  {indicator} {team.team.name}: {team.total}")

calculate_playoff_bracket

Generate playoff bracket with seeding and indicators.

Parameters:

  • team_scores - List of TeamScore objects

Returns:

  • Dictionary with playoff structure: * Conference brackets * Division leaders * Wild card teams * Seeding information

Example:

bracket = calculator.calculate_playoff_bracket(team_scores)

# Access playoff teams
eastern_playoffs = bracket["Eastern"][:8]
western_playoffs = bracket["Western"][:8]

# Find Presidents' Trophy winner
all_teams = eastern_playoffs + western_playoffs
presidents_trophy = max(all_teams, key=lambda t: t.total)

Playoff Indicators

Teams receive playoff indicators based on standing:

Indicator

Meaning

p

Presidents’ Trophy (best overall record)

z

Conference leader (best in conference)

y

Division leader (top 3 per division)

x

Wild card team (4th & 5th best in conference)

e

Eliminated (not in playoff position)

Example:

for team_score in team_scores:
    indicator = team_score.playoff_indicator
    if indicator == "p":
        print(f"{team_score.team.name} - Presidents' Trophy!")
    elif indicator == "y":
        print(f"{team_score.team.name} - Division Leader")
    elif indicator == "x":
        print(f"{team_score.team.name} - Wild Card")

Tiebreakers

When teams have equal total scores, tiebreakers are applied:

  1. Average points per player (higher is better)

  2. Alphabetical order by team abbreviation

Example:

# Two teams with same total
team_a = TeamScore(total=2000, player_count=25, avg_per_player=80.0)
team_b = TeamScore(total=2000, player_count=24, avg_per_player=83.3)

# team_b wins tiebreaker (higher average)

Usage Patterns

Complete Workflow:

from nhl_scrabble.api import NHLClient
from nhl_scrabble.scoring import ScrabbleScorer
from nhl_scrabble.processors import TeamProcessor, PlayoffCalculator
import asyncio


async def analyze():
    # Fetch data
    async with NHLClient() as client:
        teams = await client.fetch_all_teams()
        rosters = await client.fetch_all_rosters()

    # Score players
    scorer = ScrabbleScorer()
    all_players = []
    for roster in rosters.values():
        all_players.extend(scorer.score_player(p) for p in roster)

    # Process teams
    processor = TeamProcessor()
    team_scores = processor.process_teams(teams, all_players)

    # Calculate playoffs
    calculator = PlayoffCalculator()
    bracket = calculator.calculate_playoff_bracket(team_scores)

    return team_scores, bracket


team_scores, bracket = asyncio.run(analyze())

Division Analysis:

processor = TeamProcessor()
divisions = processor.get_division_standings(team_scores)

# Find strongest division
strongest = max(divisions.items(), key=lambda x: x[1].total)
print(f"Strongest division: {strongest[0]} ({strongest[1].total} points)")

Playoff Matchups:

calculator = PlayoffCalculator()
bracket = calculator.calculate_playoff_bracket(team_scores)

# First round matchups (simplified)
eastern = bracket["Eastern"][:8]
print("Eastern Conference First Round:")
print(f"  {eastern[0].team.name} vs {eastern[7].team.name}")
print(f"  {eastern[1].team.name} vs {eastern[6].team.name}")
print(f"  {eastern[2].team.name} vs {eastern[5].team.name}")
print(f"  {eastern[3].team.name} vs {eastern[4].team.name}")