from fastapi import APIRouter, Depends, status, Query from sqlalchemy.orm import Session from app.db.session import get_db from app.services.project_service import ProjectService from app.schemas.project import ProjectCreate, ProjectOut from app.schemas.project_detail import ProjectDetailOut, ProjectCustomerOut, ProjectDetailNoCustomersOut from app.schemas.paginated_response import PaginatedResponse from typing import List, Optional router = APIRouter(prefix="/api/v1/projects", tags=["projects"]) @router.get("/", response_model=PaginatedResponse[ProjectOut]) def list_projects( customer_type: Optional[int] = Query(0, description="Customer type filter (0 for all types)", ge=0), status: Optional[int] = Query(None, description="Status filter (None excludes status=3, pass specific value to include all)"), order_by: Optional[str] = Query("project_no", description="Field to order by"), order_direction: Optional[str] = Query("asc", description="Order direction: asc or desc", regex="^(asc|desc)$"), page: Optional[int] = Query(1, description="Page number (1-indexed)", ge=1), page_size: Optional[int] = Query(10, description="Number of records per page", ge=1, le=100), db: Session = Depends(get_db) ): """ Get paginated list of projects with filtering and sorting. - **customer_type**: Filter by customer type (0 = all types) - **status**: Filter by status (None = exclude status 3, pass specific value to include all) - **order_by**: Field name to sort by (project_no, project_name, etc.) - **order_direction**: Sort direction (asc or desc) - **page**: Page number starting from 1 - **page_size**: Number of records per page (max 100) """ service = ProjectService(db) return service.list_projects( customer_type=customer_type, status=status, order_by=order_by, order_direction=order_direction, page=page, page_size=page_size ) @router.get("/{project_no}", response_model=ProjectDetailNoCustomersOut) def get_project(project_no: int, db: Session = Depends(get_db)): """Get a specific project by ProjectNo without customers collection For customer data, use `/api/v1/projects/{project_no}/customers` instead. """ service = ProjectService(db) # Use lean method that does not fetch customers or notes for performance return service.get_detail_no_customers(project_no) @router.get( "/{project_no}/customers", response_model=List[ProjectCustomerOut], response_model_exclude={"barrier_sizes", "contacts", "bidder_notes"} ) def get_project_customers( project_no: int, page: Optional[int] = Query(1, description="Page number (1-indexed)", ge=1), page_size: Optional[int] = Query(25, description="Number of records per page", ge=1, le=1000), last_id: Optional[int] = Query(None, description="Keyset pagination anchor: last seen bidder Id"), db: Session = Depends(get_db) ): """Get customers associated with a specific project (by ProjectNo) Returns a paginated list of customers for the project. Pagination defaults to page=1 and page_size=100 to avoid very large responses. """ service = ProjectService(db) # Use the dedicated service method to fetch only customers for the project return service.get_customers(project_no, page=page, page_size=page_size, last_id=last_id) @router.get( "/{project_no}/customers/{customer_id}", response_model=ProjectCustomerOut, response_model_exclude={"barrier_sizes", "contacts", "bidder_notes"} ) def get_project_customer_detail( project_no: int, customer_id: str, db: Session = Depends(get_db) ): """Get detailed bidder information for a specific customer on a project Returns the complete bidder details including barrier sizes, contacts, and notes for the specified project number and customer ID combination. - **project_no**: The project number - **customer_id**: The customer ID (CustId from Bidders table) """ service = ProjectService(db) return service.get_project_customer_detail(project_no, customer_id) @router.post("/", response_model=ProjectOut, status_code=status.HTTP_201_CREATED) def create_project(project_in: ProjectCreate, db: Session = Depends(get_db)): """ Create a new project using stored procedure Creates a new project record using the spProjectsInsert stored procedure. All project fields including billing, shipping, payment, and project details can be provided in the request body. Returns the created project with the generated ProjectNo. """ service = ProjectService(db) return service.create(project_in.model_dump()) @router.put("/{project_no}", response_model=ProjectOut) def update_project(project_no: int, project_in: ProjectCreate, db: Session = Depends(get_db)): """ Update an existing project using stored procedure Updates an existing project record using the spProjectsUpdate stored procedure. All project fields including billing, shipping, payment, and project details can be updated in the request body. Returns the updated project data. """ service = ProjectService(db) return service.update(project_no, project_in.model_dump()) @router.delete("/{project_no}", status_code=status.HTTP_204_NO_CONTENT) def delete_project(project_no: int, db: Session = Depends(get_db)): """Delete a project""" service = ProjectService(db) service.delete(project_no) return None