""" 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