feat: enrich member/comment/propose APIs with usernames
- ProjectMemberResponse now includes username and full_name - Comment list endpoint returns author_username - ProposeResponse now includes created_by_username - All serializers resolve User objects to surface human-readable names - Supports frontend code-first migration (TODO §3.1/3.2)
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user