Files
ai-worker-platform/backend/app/services/project_service.py
T

114 lines
3.8 KiB
Python
Raw Normal View History

"""Project service: CRUD + lifecycle management."""
from sqlalchemy.orm import Session
from typing import List, Optional
from app.models.project import Project
from app.models.task import Task
from app.models.cost import CostLog
from app.models.artifact import Artifact
from app.models.review import Review
from app.schemas.project import ProjectCreate, ProjectUpdate, ProjectStats
class ProjectService:
@staticmethod
def list_projects(
db: Session,
tenant_id: int,
status: Optional[str] = None,
skip: int = 0,
limit: int = 100,
) -> List[Project]:
q = db.query(Project).filter(Project.tenant_id == tenant_id)
if status:
q = q.filter(Project.status == status)
return q.order_by(Project.created_at.desc()).offset(skip).limit(limit).all()
@staticmethod
def get_project(db: Session, tenant_id: int, project_id: int) -> Optional[Project]:
return (
db.query(Project)
.filter(Project.id == project_id, Project.tenant_id == tenant_id)
.first()
)
@staticmethod
def create_project(
db: Session, tenant_id: int, owner_id: int, req: ProjectCreate
) -> Project:
project = Project(
tenant_id=tenant_id,
owner_id=owner_id,
name=req.name,
description=req.description,
start_date=req.start_date,
end_date=req.end_date,
budget_limit_cents=req.budget_limit_cents,
acceptance_criteria=req.acceptance_criteria,
tags=req.tags,
status="draft",
)
db.add(project)
db.commit()
db.refresh(project)
return project
@staticmethod
def update_project(
db: Session, tenant_id: int, project_id: int, req: ProjectUpdate
) -> Project:
project = ProjectService.get_project(db, tenant_id, project_id)
if not project:
raise ValueError(f"Project #{project_id} not found")
for field, value in req.model_dump(exclude_unset=True).items():
setattr(project, field, value)
db.commit()
db.refresh(project)
return project
@staticmethod
def delete_project(db: Session, tenant_id: int, project_id: int) -> bool:
project = ProjectService.get_project(db, tenant_id, project_id)
if not project:
return False
db.delete(project)
db.commit()
return True
@staticmethod
def get_stats(db: Session, tenant_id: int, project_id: int) -> ProjectStats:
tasks = (
db.query(Task)
.filter(Task.project_id == project_id, Task.tenant_id == tenant_id)
.all()
)
cost_logs = (
db.query(CostLog)
.filter(CostLog.project_id == project_id, CostLog.tenant_id == tenant_id)
.all()
)
artifacts = (
db.query(Artifact)
.filter(Artifact.project_id == project_id, Artifact.tenant_id == tenant_id)
.count()
)
pending_reviews = (
db.query(Review)
.filter(
Review.project_id == project_id,
Review.tenant_id == tenant_id,
Review.status == "pending",
)
.count()
)
total_cost = sum(c.cost_cents for c in cost_logs)
return ProjectStats(
total_tasks=len(tasks),
pending_tasks=sum(1 for t in tasks if t.status == "pending"),
in_progress_tasks=sum(1 for t in tasks if t.status == "in_progress"),
review_tasks=sum(1 for t in tasks if t.status == "review"),
done_tasks=sum(1 for t in tasks if t.status == "done"),
total_cost_cents=total_cost,
total_artifacts=artifacts,
pending_reviews=pending_reviews,
)