197 lines
7.5 KiB
Markdown
197 lines
7.5 KiB
Markdown
# Design Document
|
|
|
|
## Overview
|
|
|
|
This design implements a database schema enhancement to add a `project_id` column to the shots table, establishing a direct relationship between shots and projects. This change improves data integrity, enables project-scoped shot name uniqueness, and provides better query performance for project-based shot operations.
|
|
|
|
## Architecture
|
|
|
|
The enhancement follows a layered approach:
|
|
|
|
1. **Database Layer**: Add project_id column with foreign key constraint and index
|
|
2. **Model Layer**: Update SQLAlchemy Shot model with project relationship
|
|
3. **Schema Layer**: Update Pydantic schemas to include project_id
|
|
4. **API Layer**: Modify endpoints to handle project_id in requests/responses
|
|
5. **Service Layer**: Update business logic for project-scoped validation
|
|
6. **Frontend Layer**: Update TypeScript interfaces and components
|
|
|
|
## Components and Interfaces
|
|
|
|
### Database Schema Changes
|
|
|
|
```sql
|
|
-- Add project_id column to shots table
|
|
ALTER TABLE shots ADD COLUMN project_id INTEGER;
|
|
|
|
-- Create foreign key constraint
|
|
ALTER TABLE shots ADD CONSTRAINT fk_shots_project_id
|
|
FOREIGN KEY (project_id) REFERENCES projects(id);
|
|
|
|
-- Create index for performance
|
|
CREATE INDEX idx_shots_project_id ON shots(project_id);
|
|
|
|
-- Create composite index for project-scoped name uniqueness
|
|
CREATE UNIQUE INDEX idx_shots_project_name_unique
|
|
ON shots(project_id, name) WHERE deleted_at IS NULL;
|
|
```
|
|
|
|
### Model Updates
|
|
|
|
**Shot Model (backend/models/shot.py)**:
|
|
- Add `project_id` column as non-nullable foreign key
|
|
- Add `project` relationship to Project model
|
|
- Update uniqueness constraints to be project-scoped
|
|
- Maintain backward compatibility with episode relationship
|
|
|
|
**Project Model (backend/models/project.py)**:
|
|
- Add `shots` relationship back-reference
|
|
|
|
### Schema Updates
|
|
|
|
**ShotBase Schema**:
|
|
- Add optional `project_id` field for API flexibility
|
|
- Maintain episode_id as primary relationship identifier
|
|
|
|
**ShotResponse Schema**:
|
|
- Include `project_id` in all response payloads
|
|
- Add computed `project_name` field for frontend convenience
|
|
|
|
### API Endpoint Changes
|
|
|
|
**Shot Creation Endpoints**:
|
|
- Automatically derive `project_id` from `episode_id`
|
|
- Validate project consistency between episode and provided project_id
|
|
- Update uniqueness validation to be project-scoped
|
|
|
|
**Shot Query Endpoints**:
|
|
- Add optional `project_id` filter parameter
|
|
- Include project information in response payloads
|
|
- Maintain existing episode-based filtering
|
|
|
|
## Data Models
|
|
|
|
### Updated Shot Model Structure
|
|
|
|
```python
|
|
class Shot(Base):
|
|
__tablename__ = "shots"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
project_id = Column(Integer, ForeignKey("projects.id"), nullable=False, index=True)
|
|
episode_id = Column(Integer, ForeignKey("episodes.id"), nullable=False)
|
|
name = Column(String, nullable=False, index=True)
|
|
# ... other existing fields
|
|
|
|
# Relationships
|
|
project = relationship("Project", back_populates="shots")
|
|
episode = relationship("Episode", back_populates="shots")
|
|
# ... other existing relationships
|
|
|
|
# Constraints
|
|
__table_args__ = (
|
|
UniqueConstraint('project_id', 'name', name='uq_shot_project_name'),
|
|
)
|
|
```
|
|
|
|
### Frontend Interface Updates
|
|
|
|
```typescript
|
|
interface Shot {
|
|
id: number
|
|
project_id: number
|
|
episode_id: number
|
|
name: string
|
|
// ... other existing fields
|
|
|
|
// Optional computed fields
|
|
project_name?: string
|
|
}
|
|
|
|
interface ShotCreate {
|
|
name: string
|
|
project_id?: number // Optional, derived from episode if not provided
|
|
// ... other existing fields
|
|
}
|
|
```
|
|
|
|
## Correctness Properties
|
|
|
|
*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.*
|
|
|
|
### Property 1: Project-Episode Consistency
|
|
*For any* shot in the system, the project_id must match the project_id of its associated episode
|
|
**Validates: Requirements 1.2, 3.3, 4.2**
|
|
|
|
### Property 2: Project-Scoped Name Uniqueness
|
|
*For any* project, shot names must be unique within that project scope (excluding soft-deleted shots)
|
|
**Validates: Requirements 1.1, 1.4**
|
|
|
|
### Property 3: API Response Completeness
|
|
*For any* shot API response, the response must include both the project_id field and all previously existing fields
|
|
**Validates: Requirements 1.3, 3.1, 4.1**
|
|
|
|
### Property 4: Migration Data Preservation
|
|
*For any* existing shot before migration, the shot data after migration should be identical except for the addition of the correct project_id derived from the episode relationship
|
|
**Validates: Requirements 2.4, 2.5**
|
|
|
|
### Property 5: Project Filtering Accuracy
|
|
*For any* project_id filter parameter, the API should return only shots that belong to that specific project
|
|
**Validates: Requirements 3.4**
|
|
|
|
### Property 6: Bulk Operation Consistency
|
|
*For any* bulk shot creation operation, all created shots must have the same project_id as their target episode
|
|
**Validates: Requirements 3.5**
|
|
|
|
### Property 7: Soft Deletion Project Preservation
|
|
*For any* shot that undergoes soft deletion, the project_id must be preserved for recovery operations
|
|
**Validates: Requirements 4.5**
|
|
|
|
### Property 8: Permission System Continuity
|
|
*For any* shot operation that was previously authorized, the same operation should remain authorized after the schema change
|
|
**Validates: Requirements 4.4**
|
|
|
|
## Error Handling
|
|
|
|
### Migration Errors
|
|
- **Orphaned Episodes**: Handle episodes without valid project references
|
|
- **Data Inconsistency**: Detect and report shots with mismatched episode-project relationships
|
|
- **Constraint Violations**: Handle existing duplicate shot names within projects
|
|
|
|
### Runtime Errors
|
|
- **Invalid Project ID**: Return 400 Bad Request for non-existent project references
|
|
- **Project-Episode Mismatch**: Return 400 Bad Request when provided project_id doesn't match episode's project
|
|
- **Duplicate Shot Names**: Return 409 Conflict for project-scoped name collisions
|
|
|
|
### Frontend Error Handling
|
|
- **Migration Status**: Display migration progress and handle temporary unavailability
|
|
- **Validation Errors**: Show clear messages for project-scoped naming conflicts
|
|
- **Fallback Behavior**: Gracefully handle missing project information during transition
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Tests
|
|
- Test Shot model creation with project_id
|
|
- Test project-scoped uniqueness validation
|
|
- Test API endpoint parameter handling
|
|
- Test schema serialization/deserialization
|
|
|
|
### Property-Based Tests
|
|
- **Property 1 Test**: Generate random shots and verify project-episode consistency
|
|
- **Property 2 Test**: Generate random shot names within projects and verify uniqueness enforcement
|
|
- **Property 3 Test**: Create test data, run migration simulation, verify data preservation
|
|
- **Property 4 Test**: Generate shots with various project_id values and verify foreign key constraints
|
|
- **Property 5 Test**: Generate API requests in old format and verify response compatibility
|
|
|
|
### Integration Tests
|
|
- Test complete shot creation workflow with project validation
|
|
- Test shot querying with project filtering
|
|
- Test bulk shot creation with project consistency
|
|
- Test soft deletion with project_id preservation
|
|
|
|
### Migration Tests
|
|
- Test migration script with various data scenarios
|
|
- Test rollback procedures
|
|
- Test performance with large datasets
|
|
- Test constraint creation and validation
|
|
|
|
The testing approach uses **Pytest** for the Python backend with **Hypothesis** for property-based testing. Each property-based test will run a minimum of 100 iterations to ensure comprehensive coverage of the input space. |