forked from cardosofelipe/fast-next-template
fix(sprints): move velocity endpoint before {sprint_id} routes
FastAPI processes routes in order, so /velocity must be defined
before /{sprint_id} to prevent "velocity" from being parsed as a UUID.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -384,6 +384,68 @@ async def get_active_sprint(
|
||||
raise
|
||||
|
||||
|
||||
@router.get(
|
||||
"/velocity",
|
||||
response_model=list[SprintVelocity],
|
||||
summary="Get Project Velocity",
|
||||
description="""
|
||||
Get velocity metrics for completed sprints in the project.
|
||||
|
||||
**Authentication**: Required (Bearer token)
|
||||
**Authorization**: Project owner or superuser
|
||||
|
||||
Returns velocity data for the last N completed sprints (default 5).
|
||||
Useful for capacity planning and sprint estimation.
|
||||
|
||||
**Rate Limit**: 60 requests/minute
|
||||
""",
|
||||
operation_id="get_project_velocity",
|
||||
)
|
||||
@limiter.limit(f"{60 * RATE_MULTIPLIER}/minute")
|
||||
async def get_project_velocity(
|
||||
request: Request,
|
||||
project_id: UUID,
|
||||
limit: int = Query(
|
||||
default=5,
|
||||
ge=1,
|
||||
le=20,
|
||||
description="Number of completed sprints to include",
|
||||
),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
Get velocity metrics for completed sprints.
|
||||
|
||||
Returns planned points, actual velocity, and velocity ratio
|
||||
for the last N completed sprints, ordered chronologically.
|
||||
"""
|
||||
# Verify project access
|
||||
await verify_project_ownership(db, project_id, current_user)
|
||||
|
||||
try:
|
||||
velocity_data = await sprint_crud.get_velocity(
|
||||
db, project_id=project_id, limit=limit
|
||||
)
|
||||
|
||||
return [
|
||||
SprintVelocity(
|
||||
sprint_number=item["sprint_number"],
|
||||
sprint_name=item["sprint_name"],
|
||||
planned_points=item["planned_points"],
|
||||
velocity=item["velocity"],
|
||||
velocity_ratio=item["velocity_ratio"],
|
||||
)
|
||||
for item in velocity_data
|
||||
]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Error getting velocity for project {project_id}: {e!s}", exc_info=True
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{sprint_id}",
|
||||
response_model=SprintResponse,
|
||||
@@ -1116,70 +1178,3 @@ async def remove_issue_from_sprint(
|
||||
exc_info=True,
|
||||
)
|
||||
raise
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Sprint Metrics Endpoints
|
||||
# ============================================================================
|
||||
|
||||
|
||||
@router.get(
|
||||
"/velocity",
|
||||
response_model=list[SprintVelocity],
|
||||
summary="Get Project Velocity",
|
||||
description="""
|
||||
Get velocity metrics for completed sprints in the project.
|
||||
|
||||
**Authentication**: Required (Bearer token)
|
||||
**Authorization**: Project owner or superuser
|
||||
|
||||
Returns velocity data for the last N completed sprints (default 5).
|
||||
Useful for capacity planning and sprint estimation.
|
||||
|
||||
**Rate Limit**: 60 requests/minute
|
||||
""",
|
||||
operation_id="get_project_velocity",
|
||||
)
|
||||
@limiter.limit(f"{60 * RATE_MULTIPLIER}/minute")
|
||||
async def get_project_velocity(
|
||||
request: Request,
|
||||
project_id: UUID,
|
||||
limit: int = Query(
|
||||
default=5,
|
||||
ge=1,
|
||||
le=20,
|
||||
description="Number of completed sprints to include",
|
||||
),
|
||||
current_user: User = Depends(get_current_user),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
) -> Any:
|
||||
"""
|
||||
Get velocity metrics for completed sprints.
|
||||
|
||||
Returns planned points, actual velocity, and velocity ratio
|
||||
for the last N completed sprints, ordered chronologically.
|
||||
"""
|
||||
# Verify project access
|
||||
await verify_project_ownership(db, project_id, current_user)
|
||||
|
||||
try:
|
||||
velocity_data = await sprint_crud.get_velocity(
|
||||
db, project_id=project_id, limit=limit
|
||||
)
|
||||
|
||||
return [
|
||||
SprintVelocity(
|
||||
sprint_number=item["sprint_number"],
|
||||
sprint_name=item["sprint_name"],
|
||||
planned_points=item["planned_points"],
|
||||
velocity=item["velocity"],
|
||||
velocity_ratio=item["velocity_ratio"],
|
||||
)
|
||||
for item in velocity_data
|
||||
]
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Error getting velocity for project {project_id}: {e!s}", exc_info=True
|
||||
)
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user