165 lines
5.6 KiB
Python
165 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
from .shared import *
|
|
|
|
from ...services.sstc.entity_lookup import _ensure_project, _ensure_task, _parse_due_date
|
|
from ...services.sstc.governance import _require_manager
|
|
from ...services.sstc.signal_core import _parse_node_id, _parse_project_id, _sync_pmflow_rag_async
|
|
|
|
router = APIRouter(tags=["sstc"])
|
|
|
|
class TaskUpdateRequest(BaseModel):
|
|
title: Optional[str] = None
|
|
description: Optional[str] = None
|
|
assignee_id: Optional[int] = None
|
|
required_skill_tag_ids: Optional[list[int]] = None
|
|
priority: Optional[str] = None
|
|
estimated_hours: Optional[float] = None
|
|
due_date: Optional[str] = None
|
|
|
|
@router.patch("/projects/{project_id}/tasks/{task_id}")
|
|
async def patch_task(
|
|
project_id: str,
|
|
task_id: str,
|
|
body: TaskUpdateRequest,
|
|
permission_user: User = Depends(require_project_member_or_manager),
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
_ = permission_user
|
|
_require_manager(current_user)
|
|
pid = _parse_project_id(project_id)
|
|
tid = _parse_node_id(task_id, "task_id")
|
|
tenant_id = int(current_user.tenant_id)
|
|
await _ensure_project(SSTCService(db, tenant_id=tenant_id), pid)
|
|
task = await _ensure_task(db, tenant_id=tenant_id, project_id=pid, task_id=tid)
|
|
|
|
before = {
|
|
"title": task.title,
|
|
"description": task.description,
|
|
"assignee_id": task.assignee_id,
|
|
"priority": task.priority,
|
|
"estimated_hours": float(task.estimated_hours) if task.estimated_hours is not None else None,
|
|
"due_date": task.due_date.isoformat() if task.due_date else None,
|
|
}
|
|
|
|
old_assignee_id = int(task.assignee_id) if task.assignee_id else None
|
|
if body.title is not None:
|
|
task.title = compact_title(body.title, max_len=32)
|
|
if body.description is not None:
|
|
task.description = body.description
|
|
if body.assignee_id is not None:
|
|
task.assignee_id = body.assignee_id
|
|
if body.required_skill_tag_ids is not None:
|
|
task.required_skill_tag_ids = body.required_skill_tag_ids
|
|
if body.priority is not None:
|
|
task.priority = body.priority
|
|
if body.estimated_hours is not None:
|
|
task.estimated_hours = body.estimated_hours
|
|
if body.due_date is not None:
|
|
task.due_date = _parse_due_date(body.due_date)
|
|
|
|
await log_operation(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
user_id=int(current_user.id),
|
|
project_id=pid,
|
|
entity_type="task",
|
|
entity_id=str(tid),
|
|
action="update",
|
|
before_data=before,
|
|
after_data={
|
|
"title": task.title,
|
|
"description": task.description,
|
|
"assignee_id": task.assignee_id,
|
|
"priority": task.priority,
|
|
"estimated_hours": float(task.estimated_hours) if task.estimated_hours is not None else None,
|
|
"due_date": task.due_date.isoformat() if task.due_date else None,
|
|
},
|
|
)
|
|
|
|
touched_user_ids: set[int] = set()
|
|
if old_assignee_id:
|
|
touched_user_ids.add(old_assignee_id)
|
|
if task.assignee_id:
|
|
touched_user_ids.add(int(task.assignee_id))
|
|
|
|
if touched_user_ids:
|
|
for uid in touched_user_ids:
|
|
await workload_service.refresh_user_assigned_hours_from_tasks(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
user_id=uid,
|
|
)
|
|
else:
|
|
await db.commit()
|
|
await context_cache_service.invalidate_system_context(tenant_id=tenant_id)
|
|
for uid in touched_user_ids:
|
|
await context_cache_service.invalidate_user_context(tenant_id=tenant_id, user_id=uid)
|
|
_sync_pmflow_rag_async(
|
|
tenant_id=tenant_id,
|
|
record_type="task",
|
|
record_id=int(task.id),
|
|
project_id=pid,
|
|
title=task.title,
|
|
content=task.description or "",
|
|
)
|
|
|
|
return success_response({"id": str(task.id), "status": task.status})
|
|
|
|
@router.delete("/projects/{project_id}/tasks/{task_id}")
|
|
async def delete_task(
|
|
project_id: str,
|
|
task_id: str,
|
|
permission_user: User = Depends(require_project_member_or_manager),
|
|
db: AsyncSession = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
_ = permission_user
|
|
_require_manager(current_user)
|
|
pid = _parse_project_id(project_id)
|
|
tid = _parse_node_id(task_id, "task_id")
|
|
tenant_id = int(current_user.tenant_id)
|
|
await _ensure_project(SSTCService(db, tenant_id=tenant_id), pid)
|
|
task = await _ensure_task(db, tenant_id=tenant_id, project_id=pid, task_id=tid)
|
|
assignee_id = int(task.assignee_id) if task.assignee_id else None
|
|
|
|
await log_operation(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
user_id=int(current_user.id),
|
|
project_id=pid,
|
|
entity_type="task",
|
|
entity_id=str(tid),
|
|
action="delete",
|
|
before_data={
|
|
"assignee_id": assignee_id,
|
|
"estimated_hours": float(task.estimated_hours) if task.estimated_hours else None,
|
|
"status": task.status,
|
|
},
|
|
)
|
|
await db.execute(
|
|
delete(Task).where(
|
|
and_(
|
|
Task.id == tid,
|
|
Task.project_id == pid,
|
|
Task.tenant_id == tenant_id,
|
|
)
|
|
)
|
|
)
|
|
|
|
if assignee_id:
|
|
await workload_service.refresh_user_assigned_hours_from_tasks(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
user_id=assignee_id,
|
|
)
|
|
else:
|
|
await db.commit()
|
|
await context_cache_service.invalidate_system_context(tenant_id=tenant_id)
|
|
if assignee_id:
|
|
await context_cache_service.invalidate_user_context(tenant_id=tenant_id, user_id=assignee_id)
|
|
|
|
return success_response({"id": str(tid), "deleted": True})
|
|
|