projects.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. """Project API router."""
  2. from fastapi import APIRouter, Depends, HTTPException, Query
  3. from sqlalchemy.orm import Session
  4. from ..database import get_db
  5. from ..models.project import Project
  6. from ..schemas.project import ProjectCreate, ProjectUpdate, ProjectResponse, ProjectListResponse
  7. router = APIRouter(prefix="/api/projects", tags=["projects"])
  8. def _project_to_response(db: Session, project: Project) -> ProjectResponse:
  9. plan_count = len(project.plans) if project.plans else 0
  10. return ProjectResponse(
  11. id=project.id,
  12. name=project.name,
  13. description=project.description or "",
  14. topology=project.topology or "SSSR",
  15. model_path=project.model_path or "",
  16. status=project.status or "draft",
  17. boundary_conditions=project.get_boundary_conditions(),
  18. plan_count=plan_count,
  19. created_at=project.created_at,
  20. updated_at=project.updated_at,
  21. )
  22. @router.get("", response_model=ProjectListResponse)
  23. def list_projects(
  24. skip: int = Query(0, ge=0),
  25. limit: int = Query(50, ge=1, le=200),
  26. topology: str | None = None,
  27. db: Session = Depends(get_db),
  28. ):
  29. """List all projects with optional topology filter."""
  30. query = db.query(Project)
  31. if topology:
  32. query = query.filter(Project.topology == topology)
  33. total = query.count()
  34. projects = query.order_by(Project.updated_at.desc()).offset(skip).limit(limit).all()
  35. return ProjectListResponse(
  36. total=total,
  37. items=[_project_to_response(db, p) for p in projects],
  38. )
  39. @router.post("", response_model=ProjectResponse, status_code=201)
  40. def create_project(data: ProjectCreate, db: Session = Depends(get_db)):
  41. """Create a new project."""
  42. project = Project(
  43. name=data.name,
  44. description=data.description,
  45. topology=data.topology,
  46. model_path=data.model_path,
  47. )
  48. project.set_boundary_conditions(data.boundary_conditions)
  49. db.add(project)
  50. db.commit()
  51. db.refresh(project)
  52. return _project_to_response(db, project)
  53. @router.get("/{project_id}", response_model=ProjectResponse)
  54. def get_project(project_id: int, db: Session = Depends(get_db)):
  55. """Get a project by ID."""
  56. project = db.query(Project).filter(Project.id == project_id).first()
  57. if not project:
  58. raise HTTPException(status_code=404, detail="Project not found")
  59. return _project_to_response(db, project)
  60. @router.put("/{project_id}", response_model=ProjectResponse)
  61. def update_project(project_id: int, data: ProjectUpdate, db: Session = Depends(get_db)):
  62. """Update a project."""
  63. project = db.query(Project).filter(Project.id == project_id).first()
  64. if not project:
  65. raise HTTPException(status_code=404, detail="Project not found")
  66. update_data = data.model_dump(exclude_unset=True)
  67. if "boundary_conditions" in update_data:
  68. project.set_boundary_conditions(update_data.pop("boundary_conditions"))
  69. for key, value in update_data.items():
  70. setattr(project, key, value)
  71. db.commit()
  72. db.refresh(project)
  73. return _project_to_response(db, project)
  74. @router.delete("/{project_id}", status_code=204)
  75. def delete_project(project_id: int, db: Session = Depends(get_db)):
  76. """Delete a project and all its plans/results."""
  77. project = db.query(Project).filter(Project.id == project_id).first()
  78. if not project:
  79. raise HTTPException(status_code=404, detail="Project not found")
  80. db.delete(project)
  81. db.commit()
  82. return None