LinkDesk/backend/docs/shot-task-creation-endpoint.md

6.0 KiB

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):

{
  "task_type": "layout",
  "status": "not_started",
  "task_id": 123,
  "assigned_user_id": null
}

Response (200 OK - if task already exists):

{
  "task_type": "layout",
  "status": "in_progress",
  "task_id": 123,
  "assigned_user_id": 5
}

Implementation

@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

{
  "detail": "Shot not found"
}

403 Forbidden

{
  "detail": "Insufficient permissions"
}

Or:

{
  "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
  • 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.