# Shot Task Creation Endpoint ## Overview Added a new backend endpoint to create tasks for shots, enabling AJAX-based task status editing in the frontend shot table. ## Endpoint Details ### POST /shots/{shot_id}/tasks **Purpose**: Create a new task for a specific shot **Location**: `backend/routers/shots.py` (after `get_shot` endpoint) **Authentication**: Requires coordinator or admin role **Parameters**: - `shot_id` (path): The ID of the shot - `task_type` (query): The type of task to create (e.g., "layout", "animation") **Request Example**: ``` POST /shots/1/tasks?task_type=layout Authorization: Bearer {token} ``` **Response** (201 Created): ```json { "task_type": "layout", "status": "not_started", "task_id": 123, "assigned_user_id": null } ``` **Response** (200 OK - if task already exists): ```json { "task_type": "layout", "status": "in_progress", "task_id": 123, "assigned_user_id": 5 } ``` ## Implementation ```python @router.post("/{shot_id}/tasks", response_model=TaskStatusInfo, status_code=status.HTTP_201_CREATED) async def create_shot_task( shot_id: int, task_type: str, db: Session = Depends(get_db), current_user: User = Depends(require_coordinator_or_admin) ): """Create a new task for a shot""" shot = db.query(Shot).filter(Shot.id == shot_id).first() if not shot: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Shot not found" ) # Check episode access episode = check_episode_access(shot.episode_id, current_user, db) # Check if task already exists existing_task = db.query(Task).filter( Task.shot_id == shot_id, Task.task_type == task_type ).first() if existing_task: # Return existing task info instead of error (idempotent) return TaskStatusInfo( task_type=existing_task.task_type, status=existing_task.status, task_id=existing_task.id, assigned_user_id=existing_task.assigned_user_id ) # Create the task task_name = f"{shot.name} - {task_type.title()}" db_task = Task( project_id=episode.project_id, shot_id=shot.id, task_type=task_type, name=task_name, description=f"{task_type.title()} task for {shot.name}", status=TaskStatus.NOT_STARTED ) db.add(db_task) db.commit() db.refresh(db_task) return TaskStatusInfo( task_type=db_task.task_type, status=db_task.status, task_id=db_task.id, assigned_user_id=db_task.assigned_user_id ) ``` ## Key Features ### 1. Idempotent Operation - If task already exists, returns existing task info - No error thrown for duplicate creation attempts - Simplifies frontend logic (no need to check existence first) ### 2. Permission Validation - Requires coordinator or admin role - Uses `require_coordinator_or_admin` dependency - Artists cannot create tasks directly ### 3. Access Control - Validates shot exists - Checks episode access via `check_episode_access` - Ensures user has permission to access the project ### 4. Automatic Task Naming - Format: `{shot_name} - {task_type}` - Example: "SH010 - Layout" - Consistent with asset task naming ### 5. Default Values - Status: `NOT_STARTED` - Description: Auto-generated - Project ID: Inherited from episode - No assignee initially ## Error Responses ### 404 Not Found ```json { "detail": "Shot not found" } ``` ### 403 Forbidden ```json { "detail": "Insufficient permissions" } ``` Or: ```json { "detail": "Access denied to this project" } ``` ## Comparison with Asset Endpoint The shot task creation endpoint mirrors the asset version: | Feature | Asset Endpoint | Shot Endpoint | |---------|---------------|---------------| | Path | `/assets/{asset_id}/tasks` | `/shots/{shot_id}/tasks` | | Permission | Coordinator/Admin | Coordinator/Admin | | Idempotent | ✅ Yes | ✅ Yes | | Access Check | `check_project_access` | `check_episode_access` | | Task Naming | `{asset.name} - {type}` | `{shot.name} - {type}` | | Project ID | From asset | From episode | ## Frontend Integration This endpoint is called by: - `frontend/src/services/task.ts` - `createShotTask()` method - `frontend/src/components/shot/EditableTaskStatus.vue` - When changing status for non-existent task **Usage Flow**: 1. User changes task status in shot table 2. Frontend checks if task exists (via `taskId` prop) 3. If no task exists, calls `createShotTask(shotId, taskType)` 4. Backend creates task and returns task ID 5. Frontend then calls `updateTaskStatus(taskId, newStatus)` 6. Status is updated and table refreshes ## Testing Test file created: `backend/test_shot_task_creation.py` **Manual Testing**: 1. Navigate to project shots tab 2. Switch to table view 3. Click on a task status cell for a shot without that task 4. Select a status 5. Verify: - Task is created - Status is set - No errors in console - Table updates correctly ## Related Files - `backend/routers/shots.py` - Endpoint implementation - `backend/routers/assets.py` - Reference implementation for assets - `backend/schemas/shot.py` - TaskStatusInfo schema - `backend/models/task.py` - Task model - `frontend/src/services/task.ts` - Frontend service calling this endpoint - `frontend/src/components/shot/EditableTaskStatus.vue` - Component using this endpoint ## Future Enhancements Potential improvements: 1. **Bulk Task Creation**: Create multiple tasks at once 2. **Custom Defaults**: Allow project-specific default task settings 3. **Template Support**: Use task templates for consistent setup 4. **Validation**: Validate task_type against allowed types 5. **Webhooks**: Trigger notifications when tasks are created 6. **Audit Log**: Track who created which tasks and when ## Conclusion The shot task creation endpoint successfully enables AJAX-based task status editing in the shot table, providing a seamless user experience without page refreshes. The implementation follows the same pattern as the asset endpoint, ensuring consistency across the application.