- Fix TypeError in check_steal_opportunity by properly mocking catcher defense - Correct tag_from_third test calculation to account for all adjustment conditions - Fix pitcher replacement test by setting appropriate allowed runners threshold - Add comprehensive test coverage for AI service business logic - Implement VS Code testing panel configuration with pytest integration - Create pytest.ini for consistent test execution and warning management - Add test isolation guidelines and factory pattern implementation - Establish 102 passing tests with zero failures 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
6.2 KiB
6.2 KiB
Models Directory
This directory contains pure data models for the Paper Dynasty web app, migrated from the Discord app following the Model/Service Architecture pattern.
Architecture Principle
Models = Pure Data | Services = Business Logic
- Models: Field definitions, relationships, basic validators only
- Services: Complex logic, UI formatting, game management, AI decisions
Migration Status
✅ Completed Models
| Model | Status | Description | Business Logic Extracted |
|---|---|---|---|
ManagerAi |
✅ Complete | AI configuration data | → AIService (9 methods) |
Cardset |
✅ Complete | Card set metadata | None (pure data) |
🚧 In Progress
| Model | Status | Description | Business Logic to Extract |
|---|---|---|---|
Team |
📋 Next | Team identity data | → UIService (embed property) |
Player |
📋 Planned | Player metadata | → UIService (Discord markdown) |
📋 Future Phases
- Phase 3: Game structure (Game, Play models)
- Phase 4: Card and rating models
- Phase 5: Web-specific models (sessions, preferences)
Model Patterns
Pure Data Model Structure
# Base model for validation and field definitions
class ModelBase(SQLModel):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True, description="Field description")
@field_validator('name')
@classmethod
def validate_name(cls, v: str) -> str:
# Basic validation only
if not v or not v.strip():
raise ValueError("Name cannot be empty")
return v
# Table model for database operations
class Model(ModelBase, table=True):
# relationships: List["RelatedModel"] = Relationship(...)
pass
What STAYS in Models
✅ Field definitions and types
name: str = Field(index=True)
ranked_legal: bool = Field(default=False)
✅ Database relationships
players: List["Player"] = Relationship(back_populates="cardset")
✅ Basic field validation
@field_validator('name')
def validate_name_not_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError("Name cannot be empty")
return v
What MOVES to Services
❌ Complex business logic
# BEFORE (in model)
def check_steal_opportunity(self, game, to_base):
# Complex AI decision logic...
# AFTER (in service)
def check_steal_opportunity(self, manager_ai, game, to_base):
# Same logic but in AIService
❌ UI formatting
# BEFORE (in model)
@property
def embed(self) -> discord.Embed:
# Discord-specific formatting...
# AFTER (in service)
def format_team_display(self, team) -> dict:
# Platform-agnostic formatting
❌ Game mechanics
# BEFORE (in model)
def initialize_play(self, session):
# Complex game setup logic...
# AFTER (in service)
def initialize_game(self, game_id) -> Play:
# Same logic but in GameService
Testing Strategy
All models use the factory pattern with transaction rollback:
# test_model.py
def test_model_creation(db_session):
model = ModelFactory.create(db_session, field="value")
assert model.field == "value"
# Automatic rollback ensures isolation
See tests/README.md for complete testing documentation.
File Organization
models/
├── __init__.py # Export all models
├── manager_ai.py # ✅ AI configuration (complete)
├── cardset.py # ✅ Card set metadata (complete)
├── team.py # 🚧 Team identity (next)
├── player.py # 📋 Player metadata (planned)
├── game.py # 📋 Game structure (planned)
├── play.py # 📋 Gameplay state (planned)
└── ai_responses.py # AI decision response models
Migration Guidelines
When migrating a model from ../discord-app/:
1. Analyze Original Model
# Find the model in Discord app
grep -r "class ModelName" ../discord-app/
2. Separate Data from Logic
- Keep: Field definitions, relationships, basic validation
- Extract: Methods, computed properties, complex logic
3. Create Pure Data Model
class ModelBase(SQLModel):
# Only field definitions and basic validation
class Model(ModelBase, table=True):
# Only relationships
4. Extract Business Logic
class ModelService(BaseService):
def extracted_method(self, model_instance, params):
# Migrated business logic
5. Create Comprehensive Tests
# Validation tests (no database)
def test_model_validation():
model = ModelFactory.build(invalid_field="bad")
# Test validation
# Database tests (with rollback)
def test_model_persistence(db_session):
model = ModelFactory.create(db_session)
# Test database operations
6. Update Migration Plan
- Mark model as complete in
.claude/model-migration-plan.md - Update this README with new model status
Dependencies
Models depend on:
sqlmodel- Database ORM and validationpydantic- Field validation and serializationsqlalchemy- Advanced database features
Models should NOT depend on:
discord.py- Platform-specific libraryfastapi- Web framework- Service classes - Business logic layer
Best Practices
DO:
- ✅ Keep models as simple data containers
- ✅ Use descriptive field documentation
- ✅ Add basic validation for data integrity
- ✅ Follow naming conventions from original models
- ✅ Create comprehensive factory-based tests
DON'T:
- ❌ Add business logic methods to models
- ❌ Include platform-specific dependencies
- ❌ Create computed properties with complex logic
- ❌ Hard-code values that belong in services
- ❌ Skip validation or tests
Future Considerations
As we complete the migration:
- Web-specific models will be added for session management
- Performance optimization may require relationship tuning
- Database migrations will be managed via Alembic
- API serialization will use Pydantic's serialization features
The goal is to have a clean, testable, platform-agnostic data layer that can support web, mobile, and future interfaces.