Fix linting and formatting issues

- Add missing imports: json, csv, io, model_to_dict
- Remove unused imports: Any, Dict, Type, invalidate_cache
- Remove redundant f-string prefixes from static error messages
- Format code with black
- All ruff and black checks pass
- All 76 unit tests pass (9 skipped)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Cal Corum 2026-02-04 08:44:12 -06:00
parent 408b187305
commit 2189aea8da
3 changed files with 299 additions and 242 deletions

View File

@ -3,15 +3,20 @@ Base Service Class - Dependency Injection Version
Provides common functionality with configurable dependencies.
"""
import json
import logging
from typing import Optional, Any, Dict, TypeVar, Type
from typing import Optional, TypeVar
from .interfaces import AbstractPlayerRepository, AbstractTeamRepository, AbstractCacheService
from .interfaces import (
AbstractPlayerRepository,
AbstractTeamRepository,
AbstractCacheService,
)
from .mocks import MockCacheService
logger = logging.getLogger('discord_app')
logger = logging.getLogger("discord_app")
T = TypeVar('T')
T = TypeVar("T")
class ServiceConfig:
@ -66,9 +71,9 @@ class BaseService:
# Lazy imports for defaults (avoids circular imports)
self._using_defaults = (
self._player_repo is None and
self._team_repo is None and
self._cache is None
self._player_repo is None
and self._team_repo is None
and self._cache is None
)
@property
@ -76,6 +81,7 @@ class BaseService:
"""Get player repository, importing from db_engine if not set."""
if self._player_repo is None:
from ..db_engine import Player
class DefaultPlayerRepo:
def select_season(self, season):
return Player.select_season(season)
@ -137,7 +143,7 @@ class BaseService:
"""Get cache service, importing from dependencies if not set."""
if self._cache is None:
try:
from ..dependencies import redis_client, invalidate_cache
from ..dependencies import redis_client
class DefaultCache:
def get(self, key: str):
@ -187,6 +193,7 @@ class BaseService:
if self._using_defaults:
try:
from ..db_engine import db
db.close()
except Exception:
pass # Connection may already be closed
@ -203,13 +210,18 @@ class BaseService:
for pattern in patterns:
self.cache.invalidate_pattern(pattern)
def handle_error(self, operation: str, error: Exception, rethrow: bool = True) -> dict:
def handle_error(
self, operation: str, error: Exception, rethrow: bool = True
) -> dict:
"""Handle errors consistently."""
logger.error(f"{operation}: {error}")
if rethrow:
try:
from fastapi import HTTPException
raise HTTPException(status_code=500, detail=f"{operation}: {str(error)}")
raise HTTPException(
status_code=500, detail=f"{operation}: {str(error)}"
)
except ImportError:
# For testing without FastAPI
raise RuntimeError(f"{operation}: {str(error)}")
@ -227,12 +239,16 @@ class BaseService:
from ..dependencies import valid_token
if not valid_token(token):
logger.warning(f"Unauthorized access attempt with token: {token[:10]}...")
logger.warning(
f"Unauthorized access attempt with token: {token[:10]}..."
)
raise HTTPException(status_code=401, detail="Unauthorized")
except ImportError:
# For testing without FastAPI - accept "valid_token" as test token
if token != "valid_token":
logger.warning(f"Unauthorized access attempt with token: {token[:10] if len(token) >= 10 else token}...")
logger.warning(
f"Unauthorized access attempt with token: {token[:10] if len(token) >= 10 else token}..."
)
error = RuntimeError("Unauthorized")
error.status_code = 401 # Add status_code for test compatibility
raise error
@ -241,21 +257,19 @@ class BaseService:
def format_csv_response(self, headers: list, rows: list) -> str:
"""Format data as CSV."""
from pandas import DataFrame
all_data = [headers] + rows
return DataFrame(all_data).to_csv(header=False, index=False)
def parse_query_params(self, params: dict, remove_none: bool = True) -> dict:
"""Parse and clean query parameters."""
if remove_none:
return {k: v for k, v in params.items() if v is not None and v != [] and v != ""}
return {
k: v for k, v in params.items() if v is not None and v != [] and v != ""
}
return params
def with_cache(
self,
key: str,
ttl: int = 300,
fallback: Optional[callable] = None
):
def with_cache(self, key: str, ttl: int = 300, fallback: Optional[callable] = None):
"""
Decorator-style cache wrapper for methods.
@ -264,6 +278,7 @@ class BaseService:
def get_player(self):
...
"""
def decorator(func):
async def wrapper(*args, **kwargs):
# Try cache first
@ -274,9 +289,10 @@ class BaseService:
# Execute and cache result
result = func(*args, **kwargs)
if result is not None:
import json
self.cache.set(key, json.dumps(result, default=str), ttl)
return result
return wrapper
return decorator

View File

@ -3,6 +3,8 @@ Player Service - Dependency Injection Version
Business logic for player operations with injectable dependencies.
"""
import csv
import io
import logging
from typing import List, Optional, Dict, Any, TYPE_CHECKING
@ -27,18 +29,14 @@ except ImportError:
self.detail = detail
super().__init__(detail)
logger = logging.getLogger('discord_app')
logger = logging.getLogger("discord_app")
class PlayerService(BaseService):
"""Service for player-related operations with dependency injection."""
cache_patterns = [
"players*",
"players-search*",
"player*",
"team-roster*"
]
cache_patterns = ["players*", "players-search*", "player*", "team-roster*"]
# Class-level repository for dependency injection
_injected_repo: Optional[AbstractPlayerRepository] = None
@ -46,8 +44,8 @@ class PlayerService(BaseService):
def __init__(
self,
player_repo: Optional[AbstractPlayerRepository] = None,
config: Optional['ServiceConfig'] = None,
**kwargs
config: Optional["ServiceConfig"] = None,
**kwargs,
):
"""
Initialize PlayerService with optional repository.
@ -75,7 +73,7 @@ class PlayerService(BaseService):
return cls._get_real_repo()
@classmethod
def _get_real_repo(cls) -> 'RealPlayerRepository':
def _get_real_repo(cls) -> "RealPlayerRepository":
"""Get a real DB repository for production use."""
return RealPlayerRepository(Player)
@ -90,7 +88,7 @@ class PlayerService(BaseService):
is_injured: Optional[bool] = None,
sort: Optional[str] = None,
short_output: bool = False,
as_csv: bool = False
as_csv: bool = False,
) -> Dict[str, Any]:
"""
Get players with filtering and sorting.
@ -124,7 +122,7 @@ class PlayerService(BaseService):
pos=pos,
strat_code=strat_code,
name=name,
is_injured=is_injured
is_injured=is_injured,
)
# Apply sorting
@ -137,15 +135,14 @@ class PlayerService(BaseService):
if as_csv:
return cls._format_player_csv(players_data)
else:
return {
"count": len(players_data),
"players": players_data
}
return {"count": len(players_data), "players": players_data}
except Exception as e:
logger.error(f"Error fetching players: {e}")
try:
raise HTTPException(status_code=500, detail=f"Error fetching players: {str(e)}")
raise HTTPException(
status_code=500, detail=f"Error fetching players: {str(e)}"
)
except ImportError:
raise RuntimeError(f"Error fetching players: {str(e)}")
@ -157,7 +154,7 @@ class PlayerService(BaseService):
pos: Optional[List[str]] = None,
strat_code: Optional[List[str]] = None,
name: Optional[str] = None,
is_injured: Optional[bool] = None
is_injured: Optional[bool] = None,
) -> QueryResult:
"""Apply player filters in a repo-agnostic way."""
@ -186,14 +183,14 @@ class PlayerService(BaseService):
if pos:
p_list = [x.upper() for x in pos]
pos_conditions = (
(Player.pos_1 << p_list) |
(Player.pos_2 << p_list) |
(Player.pos_3 << p_list) |
(Player.pos_4 << p_list) |
(Player.pos_5 << p_list) |
(Player.pos_6 << p_list) |
(Player.pos_7 << p_list) |
(Player.pos_8 << p_list)
(Player.pos_1 << p_list)
| (Player.pos_2 << p_list)
| (Player.pos_3 << p_list)
| (Player.pos_4 << p_list)
| (Player.pos_5 << p_list)
| (Player.pos_6 << p_list)
| (Player.pos_7 << p_list)
| (Player.pos_8 << p_list)
)
query = query.where(pos_conditions)
@ -208,25 +205,26 @@ class PlayerService(BaseService):
else:
# Use Python filtering for mocks
def matches(player):
if team_id and player.get('team_id') not in team_id:
if team_id and player.get("team_id") not in team_id:
return False
if strat_code:
code_list = [s.lower() for s in strat_code]
player_code = (player.get('strat_code') or '').lower()
player_code = (player.get("strat_code") or "").lower()
if player_code not in code_list:
return False
if name and (player.get('name') or '').lower() != name.lower():
if name and (player.get("name") or "").lower() != name.lower():
return False
if pos:
p_list = [p.upper() for p in pos]
player_pos = [
player.get(f'pos_{i}') for i in range(1, 9)
if player.get(f'pos_{i}')
player.get(f"pos_{i}")
for i in range(1, 9)
if player.get(f"pos_{i}")
]
if not any(p in p_list for p in player_pos):
return False
if is_injured is not None:
has_injury = player.get('il_return') is not None
has_injury = player.get("il_return") is not None
if is_injured and not has_injury:
return False
if not is_injured and has_injury:
@ -241,9 +239,7 @@ class PlayerService(BaseService):
@classmethod
def _apply_player_sort(
cls,
query: QueryResult,
sort: Optional[str] = None
cls, query: QueryResult, sort: Optional[str] = None
) -> QueryResult:
"""Apply player sorting in a repo-agnostic way."""
@ -272,11 +268,12 @@ class PlayerService(BaseService):
pass
# Use Python sorting for mocks or if DB sort failed
if not hasattr(query, 'order_by') or isinstance(query, InMemoryQueryResult):
if not hasattr(query, "order_by") or isinstance(query, InMemoryQueryResult):
def get_sort_key(player):
name = player.get('name', '')
wara = player.get('wara', 0)
player_id = player.get('id', 0)
name = player.get("name", "")
wara = player.get("wara", 0)
player_id = player.get("id", 0)
if sort == "cost-asc":
return (wara, name, player_id)
@ -290,7 +287,7 @@ class PlayerService(BaseService):
return (player_id,)
# Use reverse for descending name sort
reverse_sort = (sort == "name-desc")
reverse_sort = sort == "name-desc"
sorted_list = sorted(list(query), key=get_sort_key, reverse=reverse_sort)
query = InMemoryQueryResult(sorted_list)
@ -298,9 +295,7 @@ class PlayerService(BaseService):
@classmethod
def _query_to_player_dicts(
cls,
query: QueryResult,
short_output: bool = False
cls, query: QueryResult, short_output: bool = False
) -> List[Dict[str, Any]]:
"""Convert query results to list of player dicts."""
@ -336,7 +331,7 @@ class PlayerService(BaseService):
query_str: str,
season: Optional[int] = None,
limit: int = 10,
short_output: bool = False
short_output: bool = False,
) -> Dict[str, Any]:
"""
Search players by name with fuzzy matching.
@ -363,8 +358,7 @@ class PlayerService(BaseService):
# Convert to dicts if needed
all_player_dicts = cls._query_to_player_dicts(
InMemoryQueryResult(all_players),
short_output=True
InMemoryQueryResult(all_players), short_output=True
)
# Sort by relevance (exact matches first)
@ -372,7 +366,7 @@ class PlayerService(BaseService):
partial_matches = []
for player in all_player_dicts:
name_lower = player.get('name', '').lower()
name_lower = player.get("name", "").lower()
if name_lower == query_lower:
exact_matches.append(player)
@ -381,8 +375,8 @@ class PlayerService(BaseService):
# Sort by season within each group (newest first)
if search_all_seasons:
exact_matches.sort(key=lambda p: p.get('season', 0), reverse=True)
partial_matches.sort(key=lambda p: p.get('season', 0), reverse=True)
exact_matches.sort(key=lambda p: p.get("season", 0), reverse=True)
partial_matches.sort(key=lambda p: p.get("season", 0), reverse=True)
# Combine and limit
results = (exact_matches + partial_matches)[:limit]
@ -391,18 +385,22 @@ class PlayerService(BaseService):
"count": len(results),
"total_matches": len(exact_matches + partial_matches),
"all_seasons": search_all_seasons,
"players": results
"players": results,
}
except Exception as e:
cls.log_error("Error searching players", e)
try:
raise HTTPException(status_code=500, detail=f"Error searching players: {str(e)}")
raise HTTPException(
status_code=500, detail=f"Error searching players: {str(e)}"
)
except ImportError:
raise RuntimeError(f"Error searching players: {str(e)}")
@classmethod
def get_player(cls, player_id: int, short_output: bool = False) -> Optional[Dict[str, Any]]:
def get_player(
cls, player_id: int, short_output: bool = False
) -> Optional[Dict[str, Any]]:
"""Get a single player by ID."""
try:
repo = cls._get_player_repo()
@ -413,7 +411,10 @@ class PlayerService(BaseService):
except Exception as e:
cls.log_error(f"Error fetching player {player_id}", e)
try:
raise HTTPException(status_code=500, detail=f"Error fetching player {player_id}: {str(e)}")
raise HTTPException(
status_code=500,
detail=f"Error fetching player {player_id}: {str(e)}",
)
except ImportError:
raise RuntimeError(f"Error fetching player {player_id}: {str(e)}")
@ -432,7 +433,9 @@ class PlayerService(BaseService):
return dict(player)
@classmethod
def update_player(cls, player_id: int, data: Dict[str, Any], token: str) -> Dict[str, Any]:
def update_player(
cls, player_id: int, data: Dict[str, Any], token: str
) -> Dict[str, Any]:
"""Update a player (full update via PUT)."""
temp_service = cls()
temp_service.require_auth(token)
@ -441,7 +444,9 @@ class PlayerService(BaseService):
# Verify player exists
repo = cls._get_player_repo()
if not repo.get_by_id(player_id):
raise HTTPException(status_code=404, detail=f"Player ID {player_id} not found")
raise HTTPException(
status_code=404, detail=f"Player ID {player_id} not found"
)
# Execute update
repo.update(data, player_id=player_id)
@ -450,10 +455,14 @@ class PlayerService(BaseService):
except Exception as e:
cls.log_error(f"Error updating player {player_id}", e)
raise HTTPException(status_code=500, detail=f"Error updating player {player_id}: {str(e)}")
raise HTTPException(
status_code=500, detail=f"Error updating player {player_id}: {str(e)}"
)
@classmethod
def patch_player(cls, player_id: int, data: Dict[str, Any], token: str) -> Dict[str, Any]:
def patch_player(
cls, player_id: int, data: Dict[str, Any], token: str
) -> Dict[str, Any]:
"""Patch a player (partial update)."""
temp_service = cls()
temp_service.require_auth(token)
@ -462,7 +471,9 @@ class PlayerService(BaseService):
repo = cls._get_player_repo()
player = repo.get_by_id(player_id)
if not player:
raise HTTPException(status_code=404, detail=f"Player ID {player_id} not found")
raise HTTPException(
status_code=404, detail=f"Player ID {player_id} not found"
)
# Apply updates using repo
repo.update(data, player_id=player_id)
@ -471,10 +482,14 @@ class PlayerService(BaseService):
except Exception as e:
cls.log_error(f"Error patching player {player_id}", e)
raise HTTPException(status_code=500, detail=f"Error patching player {player_id}: {str(e)}")
raise HTTPException(
status_code=500, detail=f"Error patching player {player_id}: {str(e)}"
)
@classmethod
def create_players(cls, players_data: List[Dict[str, Any]], token: str) -> Dict[str, Any]:
def create_players(
cls, players_data: List[Dict[str, Any]], token: str
) -> Dict[str, Any]:
"""Create multiple players."""
temp_service = cls()
temp_service.require_auth(token)
@ -484,13 +499,12 @@ class PlayerService(BaseService):
repo = cls._get_player_repo()
for player in players_data:
dupe = repo.get_or_none(
season=player.get("season"),
name=player.get("name")
season=player.get("season"), name=player.get("name")
)
if dupe:
raise HTTPException(
status_code=500,
detail=f"Player {player.get('name')} already exists in Season {player.get('season')}"
detail=f"Player {player.get('name')} already exists in Season {player.get('season')}",
)
# Insert in batches
@ -499,8 +513,10 @@ class PlayerService(BaseService):
return {"message": f"Inserted {len(players_data)} players"}
except Exception as e:
cls.log_error(f"Error creating players", e)
raise HTTPException(status_code=500, detail=f"Error creating players: {str(e)}")
cls.log_error("Error creating players", e)
raise HTTPException(
status_code=500, detail=f"Error creating players: {str(e)}"
)
@classmethod
def delete_player(cls, player_id: int, token: str) -> Dict[str, str]:
@ -511,7 +527,9 @@ class PlayerService(BaseService):
try:
repo = cls._get_player_repo()
if not repo.get_by_id(player_id):
raise HTTPException(status_code=404, detail=f"Player ID {player_id} not found")
raise HTTPException(
status_code=404, detail=f"Player ID {player_id} not found"
)
repo.delete_by_id(player_id)
@ -519,7 +537,9 @@ class PlayerService(BaseService):
except Exception as e:
cls.log_error(f"Error deleting player {player_id}", e)
raise HTTPException(status_code=500, detail=f"Error deleting player {player_id}: {str(e)}")
raise HTTPException(
status_code=500, detail=f"Error deleting player {player_id}: {str(e)}"
)
@classmethod
def _format_player_csv(cls, players: List[Dict]) -> str:
@ -547,11 +567,11 @@ class InMemoryQueryResult:
def __init__(self, items: List[Dict[str, Any]]):
self._items = list(items)
def where(self, *conditions) -> 'InMemoryQueryResult':
def where(self, *conditions) -> "InMemoryQueryResult":
"""Apply filter conditions (no-op for compatibility)."""
return self
def order_by(self, *fields) -> 'InMemoryQueryResult':
def order_by(self, *fields) -> "InMemoryQueryResult":
"""Apply sort (no-op, sorting done by service)."""
return self

View File

@ -10,6 +10,7 @@ import copy
import logging
from typing import List, Optional, Dict, Any, Literal, TYPE_CHECKING
from playhouse.shortcuts import model_to_dict
from .base import BaseService
from .interfaces import AbstractTeamRepository
@ -28,17 +29,14 @@ except ImportError:
self.detail = detail
super().__init__(detail)
logger = logging.getLogger('discord_app')
logger = logging.getLogger("discord_app")
class TeamService(BaseService):
"""Service for team-related operations."""
cache_patterns = [
"teams*",
"team*",
"team-roster*"
]
cache_patterns = ["teams*", "team*", "team-roster*"]
# Class-level repository for dependency injection
_injected_repo: Optional[AbstractTeamRepository] = None
@ -46,8 +44,8 @@ class TeamService(BaseService):
def __init__(
self,
team_repo: Optional[AbstractTeamRepository] = None,
config: Optional['ServiceConfig'] = None,
**kwargs
config: Optional["ServiceConfig"] = None,
**kwargs,
):
"""
Initialize TeamService with optional repository.
@ -75,9 +73,10 @@ class TeamService(BaseService):
return cls._get_real_repo()
@classmethod
def _get_real_repo(cls) -> 'RealTeamRepository':
def _get_real_repo(cls) -> "RealTeamRepository":
"""Get a real DB repository for production use."""
from ..db_engine import Team # Lazy import to avoid loading DB in tests
return RealTeamRepository(Team)
@classmethod
@ -89,7 +88,7 @@ class TeamService(BaseService):
team_abbrev: Optional[List[str]] = None,
active_only: bool = False,
short_output: bool = False,
as_csv: bool = False
as_csv: bool = False,
) -> Dict[str, Any]:
"""
Get teams with filtering.
@ -118,21 +117,27 @@ class TeamService(BaseService):
# Apply filters
if manager_id:
teams_list = [t for t in teams_list
if cls._team_has_manager(t, manager_id)]
teams_list = [
t for t in teams_list if cls._team_has_manager(t, manager_id)
]
if owner_id:
teams_list = [t for t in teams_list
if cls._team_has_owner(t, owner_id)]
teams_list = [t for t in teams_list if cls._team_has_owner(t, owner_id)]
if team_abbrev:
abbrev_list = [x.lower() for x in team_abbrev]
teams_list = [t for t in teams_list
if cls._get_team_field(t, 'abbrev', '').lower() in abbrev_list]
teams_list = [
t
for t in teams_list
if cls._get_team_field(t, "abbrev", "").lower() in abbrev_list
]
if active_only:
teams_list = [t for t in teams_list
if not cls._get_team_field(t, 'abbrev', '').endswith(('IL', 'MiL'))]
teams_list = [
t
for t in teams_list
if not cls._get_team_field(t, "abbrev", "").endswith(("IL", "MiL"))
]
# Convert to dicts
teams_data = [cls._team_to_dict(t, short_output) for t in teams_list]
@ -140,14 +145,11 @@ class TeamService(BaseService):
if as_csv:
return cls._format_team_csv(teams_data)
return {
"count": len(teams_data),
"teams": teams_data
}
return {"count": len(teams_data), "teams": teams_data}
except Exception as e:
temp_service = cls()
temp_service.handle_error(f"Error fetching teams", e)
temp_service.handle_error("Error fetching teams", e)
finally:
temp_service = cls()
temp_service.close_db()
@ -170,10 +172,7 @@ class TeamService(BaseService):
@classmethod
def get_team_roster(
cls,
team_id: int,
which: Literal['current', 'next'],
sort: Optional[str] = None
cls, team_id: int, which: Literal["current", "next"], sort: Optional[str] = None
) -> Dict[str, Any]:
"""
Get team roster with IL lists.
@ -192,26 +191,28 @@ class TeamService(BaseService):
team = Team.get_by_id(team_id)
if which == 'current':
if which == "current":
full_roster = team.get_this_week()
else:
full_roster = team.get_next_week()
# Deep copy and convert to dicts
result = {
'active': {'players': []},
'shortil': {'players': []},
'longil': {'players': []}
"active": {"players": []},
"shortil": {"players": []},
"longil": {"players": []},
}
for section in ['active', 'shortil', 'longil']:
players = copy.deepcopy(full_roster[section]['players'])
result[section]['players'] = [model_to_dict(p) for p in players]
for section in ["active", "shortil", "longil"]:
players = copy.deepcopy(full_roster[section]["players"])
result[section]["players"] = [model_to_dict(p) for p in players]
# Apply sorting
if sort == 'wara-desc':
for section in ['active', 'shortil', 'longil']:
result[section]['players'].sort(key=lambda p: p.get("wara", 0), reverse=True)
if sort == "wara-desc":
for section in ["active", "shortil", "longil"]:
result[section]["players"].sort(
key=lambda p: p.get("wara", 0), reverse=True
)
return result
@ -223,7 +224,9 @@ class TeamService(BaseService):
temp_service.close_db()
@classmethod
def update_team(cls, team_id: int, data: Dict[str, Any], token: str) -> Dict[str, Any]:
def update_team(
cls, team_id: int, data: Dict[str, Any], token: str
) -> Dict[str, Any]:
"""Update a team (partial update)."""
temp_service = cls()
temp_service.require_auth(token)
@ -232,7 +235,9 @@ class TeamService(BaseService):
repo = cls._get_team_repo()
team = repo.get_by_id(team_id)
if not team:
raise HTTPException(status_code=404, detail=f"Team ID {team_id} not found")
raise HTTPException(
status_code=404, detail=f"Team ID {team_id} not found"
)
# Apply updates using repo
repo.update(data, team_id=team_id)
@ -246,7 +251,9 @@ class TeamService(BaseService):
temp_service.close_db()
@classmethod
def create_teams(cls, teams_data: List[Dict[str, Any]], token: str) -> Dict[str, str]:
def create_teams(
cls, teams_data: List[Dict[str, Any]], token: str
) -> Dict[str, str]:
"""Create multiple teams."""
temp_service = cls()
temp_service.require_auth(token)
@ -256,13 +263,12 @@ class TeamService(BaseService):
repo = cls._get_team_repo()
for team in teams_data:
dupe = repo.get_or_none(
season=team.get("season"),
abbrev=team.get("abbrev")
season=team.get("season"), abbrev=team.get("abbrev")
)
if dupe:
raise HTTPException(
status_code=500,
detail=f"Team {team.get('abbrev')} already exists in Season {team.get('season')}"
detail=f"Team {team.get('abbrev')} already exists in Season {team.get('season')}",
)
# Insert teams
@ -271,7 +277,7 @@ class TeamService(BaseService):
return {"message": f"Inserted {len(teams_data)} teams"}
except Exception as e:
temp_service.handle_error(f"Error creating teams", e)
temp_service.handle_error("Error creating teams", e)
finally:
temp_service.invalidate_related_cache(cls.cache_patterns)
temp_service.close_db()
@ -285,7 +291,9 @@ class TeamService(BaseService):
try:
repo = cls._get_team_repo()
if not repo.get_by_id(team_id):
raise HTTPException(status_code=404, detail=f"Team ID {team_id} not found")
raise HTTPException(
status_code=404, detail=f"Team ID {team_id} not found"
)
repo.delete_by_id(team_id)
@ -301,17 +309,25 @@ class TeamService(BaseService):
@classmethod
def _team_has_manager(cls, team, manager_ids: List[int]) -> bool:
"""Check if team has any of the specified managers."""
team_dict = team if isinstance(team, dict) else cls._team_to_dict(team, short_output=True)
manager1 = team_dict.get('manager1_id')
manager2 = team_dict.get('manager2_id')
team_dict = (
team
if isinstance(team, dict)
else cls._team_to_dict(team, short_output=True)
)
manager1 = team_dict.get("manager1_id")
manager2 = team_dict.get("manager2_id")
return manager1 in manager_ids or manager2 in manager_ids
@classmethod
def _team_has_owner(cls, team, owner_ids: List[int]) -> bool:
"""Check if team has any of the specified owners."""
team_dict = team if isinstance(team, dict) else cls._team_to_dict(team, short_output=True)
gmid = team_dict.get('gmid')
gmid2 = team_dict.get('gmid2')
team_dict = (
team
if isinstance(team, dict)
else cls._team_to_dict(team, short_output=True)
)
gmid = team_dict.get("gmid")
gmid2 = team_dict.get("gmid2")
return gmid in owner_ids or gmid2 in owner_ids
@classmethod
@ -341,14 +357,16 @@ class TeamService(BaseService):
from ..db_engine import query_to_csv, Team # Lazy import - CSV needs DB
# Get team IDs from the list
team_ids = [t.get('id') for t in teams if t.get('id')]
team_ids = [t.get("id") for t in teams if t.get("id")]
if not team_ids:
return ""
# Query for CSV formatting
query = Team.select().where(Team.id << team_ids)
return query_to_csv(query, exclude=[Team.division_legacy, Team.mascot, Team.gsheet])
return query_to_csv(
query, exclude=[Team.division_legacy, Team.mascot, Team.gsheet]
)
class RealTeamRepository:
@ -377,11 +395,13 @@ class RealTeamRepository:
def update(self, data: Dict, team_id: int) -> int:
"""Update team."""
from ..db_engine import Team # Lazy import - only used in production
return Team.update(**data).where(Team.id == team_id).execute()
def insert_many(self, data: List[Dict]) -> int:
"""Insert multiple teams."""
from ..db_engine import Team, db # Lazy import - only used in production
with db.atomic():
Team.insert_many(data).on_conflict_ignore().execute()
return len(data)
@ -389,4 +409,5 @@ class RealTeamRepository:
def delete_by_id(self, team_id: int) -> int:
"""Delete team by ID."""
from ..db_engine import Team # Lazy import - only used in production
return Team.delete().where(Team.id == team_id).execute()