Merge dev-2026-03-22 into main #12
@@ -50,9 +50,22 @@ def create_comment(comment: schemas.CommentCreate, db: Session = Depends(get_db)
|
||||
return db_comment
|
||||
|
||||
|
||||
@router.get("/tasks/{task_id}/comments", response_model=List[schemas.CommentResponse])
|
||||
@router.get("/tasks/{task_id}/comments")
|
||||
def list_comments(task_id: int, db: Session = Depends(get_db)):
|
||||
return db.query(models.Comment).filter(models.Comment.task_id == task_id).all()
|
||||
comments = db.query(models.Comment).filter(models.Comment.task_id == task_id).all()
|
||||
result = []
|
||||
for c in comments:
|
||||
author = db.query(models.User).filter(models.User.id == c.author_id).first()
|
||||
result.append({
|
||||
"id": c.id,
|
||||
"content": c.content,
|
||||
"task_id": c.task_id,
|
||||
"author_id": c.author_id,
|
||||
"author_username": author.username if author else None,
|
||||
"created_at": c.created_at,
|
||||
"updated_at": c.updated_at,
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
@router.patch("/comments/{comment_id}", response_model=schemas.CommentResponse)
|
||||
|
||||
@@ -332,9 +332,12 @@ def list_project_members(project_id: str, db: Session = Depends(get_db)):
|
||||
role = db.query(Role).filter(Role.id == m.role_id).first()
|
||||
if role:
|
||||
role_name = role.name
|
||||
user = db.query(models.User).filter(models.User.id == m.user_id).first()
|
||||
result.append({
|
||||
"id": m.id,
|
||||
"user_id": m.user_id,
|
||||
"username": user.username if user else None,
|
||||
"full_name": user.full_name if user else None,
|
||||
"project_id": m.project_id,
|
||||
"role": role_name
|
||||
})
|
||||
|
||||
@@ -17,6 +17,24 @@ from app.services.activity import log_activity
|
||||
router = APIRouter(prefix="/projects/{project_id}/proposes", tags=["Proposes"])
|
||||
|
||||
|
||||
def _serialize_propose(db: Session, propose: Propose) -> dict:
|
||||
"""Serialize propose with created_by_username."""
|
||||
creator = db.query(models.User).filter(models.User.id == propose.created_by_id).first() if propose.created_by_id else None
|
||||
return {
|
||||
"id": propose.id,
|
||||
"title": propose.title,
|
||||
"description": propose.description,
|
||||
"propose_code": propose.propose_code,
|
||||
"status": propose.status.value if hasattr(propose.status, "value") else propose.status,
|
||||
"project_id": propose.project_id,
|
||||
"created_by_id": propose.created_by_id,
|
||||
"created_by_username": creator.username if creator else None,
|
||||
"feat_task_id": propose.feat_task_id,
|
||||
"created_at": propose.created_at,
|
||||
"updated_at": propose.updated_at,
|
||||
}
|
||||
|
||||
|
||||
def _find_project(db, identifier):
|
||||
"""Look up project by numeric id or project_code."""
|
||||
try:
|
||||
@@ -92,7 +110,7 @@ def list_proposes(
|
||||
.order_by(Propose.id.desc())
|
||||
.all()
|
||||
)
|
||||
return proposes
|
||||
return [_serialize_propose(db, p) for p in proposes]
|
||||
|
||||
|
||||
@router.post("", response_model=schemas.ProposeResponse, status_code=status.HTTP_201_CREATED)
|
||||
@@ -123,7 +141,7 @@ def create_propose(
|
||||
|
||||
log_activity(db, "create", "propose", propose.id, user_id=current_user.id, details={"title": propose.title})
|
||||
|
||||
return propose
|
||||
return _serialize_propose(db, propose)
|
||||
|
||||
|
||||
@router.get("/{propose_id}", response_model=schemas.ProposeResponse)
|
||||
@@ -140,7 +158,7 @@ def get_propose(
|
||||
propose = _find_propose(db, propose_id, project.id)
|
||||
if not propose:
|
||||
raise HTTPException(status_code=404, detail="Propose not found")
|
||||
return propose
|
||||
return _serialize_propose(db, propose)
|
||||
|
||||
|
||||
@router.patch("/{propose_id}", response_model=schemas.ProposeResponse)
|
||||
@@ -177,7 +195,7 @@ def update_propose(
|
||||
|
||||
log_activity(db, "update", "propose", propose.id, user_id=current_user.id, details=data)
|
||||
|
||||
return propose
|
||||
return _serialize_propose(db, propose)
|
||||
|
||||
|
||||
# ---- Actions ----
|
||||
@@ -256,7 +274,7 @@ def accept_propose(
|
||||
"task_code": task_code,
|
||||
})
|
||||
|
||||
return propose
|
||||
return _serialize_propose(db, propose)
|
||||
|
||||
|
||||
class RejectRequest(schemas.BaseModel):
|
||||
@@ -293,7 +311,7 @@ def reject_propose(
|
||||
"reason": body.reason if body else None,
|
||||
})
|
||||
|
||||
return propose
|
||||
return _serialize_propose(db, propose)
|
||||
|
||||
|
||||
@router.post("/{propose_id}/reopen", response_model=schemas.ProposeResponse)
|
||||
@@ -323,4 +341,4 @@ def reopen_propose(
|
||||
|
||||
log_activity(db, "reopen", "propose", propose.id, user_id=current_user.id)
|
||||
|
||||
return propose
|
||||
return _serialize_propose(db, propose)
|
||||
|
||||
@@ -208,6 +208,8 @@ class ProjectMemberCreate(ProjectMemberBase):
|
||||
class ProjectMemberResponse(BaseModel):
|
||||
id: int
|
||||
user_id: int
|
||||
username: Optional[str] = None
|
||||
full_name: Optional[str] = None
|
||||
project_id: int
|
||||
role: str = "dev"
|
||||
|
||||
@@ -290,6 +292,7 @@ class ProposeResponse(ProposeBase):
|
||||
status: ProposeStatusEnum
|
||||
project_id: int
|
||||
created_by_id: Optional[int] = None
|
||||
created_by_username: Optional[str] = None
|
||||
feat_task_id: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
Reference in New Issue
Block a user