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.PlayoffCalculator[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]

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]

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]

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]

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]

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]

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

Team Processor

Team data processing module.

class nhl_scrabble.processors.team_processor.TeamProcessor(api_client, scorer, max_workers=5)[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]

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]

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]

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]

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]

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]

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]

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]

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]

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]

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]

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]

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]

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}")