swiftops-backend / src /app /api /v1 /progress_reports.py
kamau1's picture
fix: correct get_db imports and remove duplicate definition
44ca2cd
"""
Progress Report API Endpoints
Handles:
- Creating progress reports with location verification
- Listing reports with filters
- Updating and deleting reports
- Image management via polymorphic linking
- Statistics aggregation
"""
from fastapi import APIRouter, Depends, Query, status
from sqlalchemy.orm import Session
from typing import List, Optional
from uuid import UUID
from app.api.deps import get_db, get_current_user
from app.models.user import User
from app.schemas.ticket_progress import (
TicketProgressReportCreate,
TicketProgressReportUpdate,
TicketProgressReportResponse,
TicketProgressReportListResponse,
ProgressReportStats,
)
from app.services.progress_report_service import ProgressReportService
router = APIRouter(prefix="/progress-reports", tags=["Progress Reports"])
@router.post(
"",
response_model=TicketProgressReportResponse,
status_code=status.HTTP_201_CREATED,
summary="Create progress report",
description="""
Create a new progress report for a task ticket.
**Features:**
- Describes work completed and remaining
- Tracks issues encountered and resolved
- Captures team size and hours worked
- Optional GPS location verification
**Location Verification:**
If latitude/longitude provided, automatically checks if within 100m of ticket location.
**Image Upload:**
After creating the report, upload images using POST /progress-reports/{report_id}/images
and link them with linked_entity_type='progress_report'
""",
)
def create_progress_report(
data: TicketProgressReportCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Create a new progress report"""
return ProgressReportService.create_progress_report(
db=db,
data=data,
reported_by_user_id=current_user.id
)
@router.get(
"",
response_model=TicketProgressReportListResponse,
summary="List progress reports",
description="""
List progress reports with optional filters.
**Filters:**
- ticket_id: Show reports for specific ticket
- reported_by_user_id: Show reports from specific user
- with_issues_only: Show only reports with issues_encountered
**Sorting:**
Always sorted by newest first (created_at DESC)
""",
)
def list_progress_reports(
ticket_id: Optional[UUID] = Query(None, description="Filter by ticket ID"),
reported_by_user_id: Optional[UUID] = Query(None, description="Filter by reporter"),
with_issues_only: bool = Query(False, description="Show only reports with issues"),
skip: int = Query(0, ge=0, description="Pagination offset"),
limit: int = Query(100, ge=1, le=500, description="Pagination limit"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""List progress reports"""
reports, total = ProgressReportService.list_progress_reports(
db=db,
ticket_id=ticket_id,
reported_by_user_id=reported_by_user_id,
with_issues_only=with_issues_only,
skip=skip,
limit=limit
)
return {
"items": reports,
"total": total,
"skip": skip,
"limit": limit,
}
@router.get(
"/stats",
response_model=ProgressReportStats,
summary="Get progress report statistics",
description="""
Get aggregated statistics for progress reports.
**Includes:**
- Total reports count
- Unique tickets with reports
- Average team size on site
- Total hours worked across all reports
- Reports with issues encountered
- Reports with location verification
""",
)
def get_progress_stats(
ticket_id: Optional[UUID] = Query(None, description="Filter by ticket ID"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Get progress report statistics"""
return ProgressReportService.get_progress_stats(db=db, ticket_id=ticket_id)
@router.get(
"/{report_id}",
response_model=TicketProgressReportResponse,
summary="Get progress report by ID",
description="""
Retrieve a specific progress report with all details.
**Includes:**
- All report fields
- Reporter user information
- Ticket information
**To get images:**
Use the ticket_images endpoint with filters:
- linked_entity_type = 'progress_report'
- linked_entity_id = {report_id}
""",
)
def get_progress_report(
report_id: UUID,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Get progress report by ID"""
return ProgressReportService.get_progress_report(db=db, report_id=report_id)
@router.patch(
"/{report_id}",
response_model=TicketProgressReportResponse,
summary="Update progress report",
description="""
Update a progress report.
**Permissions:**
Only the original reporter can update their report.
**Update Strategy:**
Partial updates supported - only include fields you want to change.
""",
)
def update_progress_report(
report_id: UUID,
data: TicketProgressReportUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Update progress report"""
return ProgressReportService.update_progress_report(
db=db,
report_id=report_id,
data=data,
current_user_id=current_user.id
)
@router.delete(
"/{report_id}",
status_code=status.HTTP_204_NO_CONTENT,
summary="Delete progress report",
description="""
Soft delete a progress report.
**Permissions:**
Only the original reporter can delete their report.
**Cascade Behavior:**
Linked images remain but lose their link (linked_entity_id set to null).
""",
)
def delete_progress_report(
report_id: UUID,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
"""Delete progress report"""
ProgressReportService.delete_progress_report(
db=db,
report_id=report_id,
current_user_id=current_user.id
)
return None