## Player Model Migration - Migrate Player model from Discord app following Model/Service Architecture pattern - Extract all business logic from Player model to PlayerService - Create pure data model with PostgreSQL relationships (Cardset, PositionRating) - Implement comprehensive PlayerFactory with specialized methods for test data ## PlayerService Implementation - Extract 5 business logic methods from original Player model: - get_batter_card_url() - batting card URL retrieval - get_pitcher_card_url() - pitching card URL retrieval - generate_name_card_link() - markdown link generation - get_formatted_name_with_description() - name formatting - get_player_description() - description from object or dict - Follow BaseService pattern with dependency injection and logging ## Comprehensive Testing - 35 passing Player tests (14 model + 21 service tests) - PlayerFactory with specialized methods (batting/pitching cards, positions) - Test isolation following factory pattern and db_session guidelines - Fix PostgreSQL integer overflow in test ID generation ## Integration Test Infrastructure - Create integration test framework for improving service coverage - Design AIService integration tests targeting uncovered branches - Demonstrate real database query testing with proper isolation - Establish patterns for testing complex game scenarios ## Service Coverage Analysis - Current service coverage: 61% overall - PlayerService: 100% coverage (excellent migration example) - AIService: 60% coverage (improvement opportunities identified) - Integration test strategy designed to achieve 90%+ coverage ## Database Integration - Update Cardset model to include players relationship - Update PositionRating model with proper Player foreign key - Maintain all existing relationships and constraints - Demonstrate data isolation and automatic cleanup in tests ## Test Suite Status - 137 tests passing, 0 failures (maintained 100% pass rate) - Added 35 new tests while preserving all existing functionality - Integration test infrastructure ready for coverage improvements 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
67 lines
2.6 KiB
Python
67 lines
2.6 KiB
Python
"""
|
|
Player Model
|
|
|
|
Pure data model for player information, migrated from Discord app.
|
|
All business logic extracted to PlayerService.
|
|
"""
|
|
import datetime
|
|
from typing import TYPE_CHECKING, Optional
|
|
|
|
from pydantic import field_validator
|
|
from sqlalchemy import BigInteger, Column
|
|
from sqlmodel import Field, Relationship, SQLModel
|
|
|
|
if TYPE_CHECKING:
|
|
from .cardset import Cardset
|
|
# from .card import Card # Will be uncommented when Card model is created
|
|
# from .lineup import Lineup # Will be uncommented when Lineup model is created
|
|
from .position_rating import PositionRating
|
|
|
|
|
|
class PlayerBase(SQLModel):
|
|
"""Base player model with core data fields."""
|
|
|
|
id: Optional[int] = Field(sa_column=Column(BigInteger(), primary_key=True, autoincrement=False))
|
|
name: str
|
|
cost: int
|
|
image: str
|
|
mlbclub: str
|
|
franchise: str
|
|
cardset_id: Optional[int] = Field(default=None, foreign_key='cardset.id')
|
|
set_num: int
|
|
rarity_id: Optional[int] = Field(default=None)
|
|
pos_1: str
|
|
description: str
|
|
quantity: Optional[int] = Field(default=999)
|
|
image2: Optional[str] = Field(default=None)
|
|
pos_2: Optional[str] = Field(default=None)
|
|
pos_3: Optional[str] = Field(default=None)
|
|
pos_4: Optional[str] = Field(default=None)
|
|
pos_5: Optional[str] = Field(default=None)
|
|
pos_6: Optional[str] = Field(default=None)
|
|
pos_7: Optional[str] = Field(default=None)
|
|
pos_8: Optional[str] = Field(default=None)
|
|
headshot: Optional[str] = Field(default=None)
|
|
vanity_card: Optional[str] = Field(default=None)
|
|
strat_code: Optional[str] = Field(default=None)
|
|
bbref_id: Optional[str] = Field(default=None)
|
|
fangr_id: Optional[str] = Field(default=None)
|
|
mlbplayer_id: Optional[int] = Field(default=None)
|
|
created: datetime.datetime = Field(default_factory=datetime.datetime.now, nullable=True)
|
|
|
|
@field_validator('pos_1', 'pos_2', 'pos_3', 'pos_4', 'pos_5', 'pos_6', 'pos_7', 'pos_8')
|
|
def uppercase_strings(cls, value: str) -> str:
|
|
"""Ensure position strings are uppercase."""
|
|
if value is not None:
|
|
return value.upper()
|
|
else:
|
|
return value
|
|
|
|
|
|
class Player(PlayerBase, table=True):
|
|
"""Player model with database relationships."""
|
|
|
|
cardset: "Cardset" = Relationship(back_populates='players')
|
|
# cards: list["Card"] = Relationship(back_populates='player', cascade_delete=True) # Will be uncommented when Card model is created
|
|
# lineups: list["Lineup"] = Relationship(back_populates='player', cascade_delete=True) # Will be uncommented when Lineup model is created
|
|
positions: list["PositionRating"] = Relationship(back_populates='player', cascade_delete=True) |