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:
objectCalculate 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:
objectProcess 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:
- 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:
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:
- 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:
- 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:
objectProcess 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:
- 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:
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:
- 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:
- 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:
objectProcess 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:
- 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:
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:
- 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:
- 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 totalsget_division_standings()- Group teams by divisionget_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 objectsplayer_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:
objectCalculate 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:
objectCalculate 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:
Average points per player (higher is better)
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}")