paper-dynasty-gameplay-webapp/app/models/player.py
Cal Corum 559fe73f07 CLAUDE: Complete Player model migration with service layer and integration test infrastructure
## 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>
2025-09-29 16:20:29 -05:00

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)