- 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>
228 lines
6.2 KiB
Markdown
228 lines
6.2 KiB
Markdown
# 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
|
|
|
|
```python
|
|
# 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**
|
|
```python
|
|
name: str = Field(index=True)
|
|
ranked_legal: bool = Field(default=False)
|
|
```
|
|
|
|
✅ **Database relationships**
|
|
```python
|
|
players: List["Player"] = Relationship(back_populates="cardset")
|
|
```
|
|
|
|
✅ **Basic field validation**
|
|
```python
|
|
@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**
|
|
```python
|
|
# 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**
|
|
```python
|
|
# 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**
|
|
```python
|
|
# 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:
|
|
|
|
```python
|
|
# 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
|
|
```bash
|
|
# 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
|
|
```python
|
|
class ModelBase(SQLModel):
|
|
# Only field definitions and basic validation
|
|
|
|
class Model(ModelBase, table=True):
|
|
# Only relationships
|
|
```
|
|
|
|
### 4. Extract Business Logic
|
|
```python
|
|
class ModelService(BaseService):
|
|
def extracted_method(self, model_instance, params):
|
|
# Migrated business logic
|
|
```
|
|
|
|
### 5. Create Comprehensive Tests
|
|
```python
|
|
# 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 validation
|
|
- `pydantic` - Field validation and serialization
|
|
- `sqlalchemy` - Advanced database features
|
|
|
|
Models should NOT depend on:
|
|
- `discord.py` - Platform-specific library
|
|
- `fastapi` - 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:
|
|
|
|
1. **Web-specific models** will be added for session management
|
|
2. **Performance optimization** may require relationship tuning
|
|
3. **Database migrations** will be managed via Alembic
|
|
4. **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. |