feat: system messages for task lifecycle (create, assign, status change)
Some checks failed
Deploy Tracker / deploy (push) Failing after 1s
Some checks failed
Deploy Tracker / deploy (push) Failing after 1s
Tracker now auto-generates system messages visible to all members/agents. No more side effects in router — agents see system messages and act via tools.
This commit is contained in:
parent
050d672836
commit
f7ee6d1a7c
@ -80,6 +80,33 @@ class AssignRequest(BaseModel):
|
|||||||
|
|
||||||
# --- Helpers ---
|
# --- Helpers ---
|
||||||
|
|
||||||
|
async def _system_message(db: AsyncSession, task: Task, content: str, project_slug: str = ""):
|
||||||
|
"""Create a system message for a task and broadcast it via WS."""
|
||||||
|
from tracker.ws.manager import manager
|
||||||
|
msg = Message(
|
||||||
|
task_id=task.id,
|
||||||
|
author_type="system",
|
||||||
|
author_slug="system",
|
||||||
|
content=content,
|
||||||
|
mentions=[],
|
||||||
|
)
|
||||||
|
db.add(msg)
|
||||||
|
await db.flush() # get msg.id
|
||||||
|
|
||||||
|
prefix = project_slug[:2].upper() if project_slug else "XX"
|
||||||
|
key = f"{prefix}-{task.number}"
|
||||||
|
|
||||||
|
await manager.broadcast_task_event(str(task.project_id), "message.new", {
|
||||||
|
"id": str(msg.id),
|
||||||
|
"task_id": str(task.id),
|
||||||
|
"task_key": key,
|
||||||
|
"author_type": "system",
|
||||||
|
"author_slug": "system",
|
||||||
|
"content": content,
|
||||||
|
"created_at": msg.created_at.isoformat() if msg.created_at else "",
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def _task_out(t: Task, project_slug: str = "") -> dict:
|
def _task_out(t: Task, project_slug: str = "") -> dict:
|
||||||
prefix = project_slug[:2].upper() if project_slug else "XX"
|
prefix = project_slug[:2].upper() if project_slug else "XX"
|
||||||
return {
|
return {
|
||||||
@ -200,13 +227,28 @@ async def create_task(
|
|||||||
"created_at": task.created_at.isoformat() if task.created_at else "",
|
"created_at": task.created_at.isoformat() if task.created_at else "",
|
||||||
}
|
}
|
||||||
await manager.broadcast_task_event(str(project.id), "task.created", task_data)
|
await manager.broadcast_task_event(str(project.id), "task.created", task_data)
|
||||||
|
|
||||||
return _task_out(task_full, task_full.project.slug if task_full.project else "")
|
# System message
|
||||||
|
slug = project.slug
|
||||||
|
key = f"{slug[:2].upper()}-{task.number}"
|
||||||
|
sys_text = f"Задача {key} создана: {task.title}"
|
||||||
|
if task.assignee_slug:
|
||||||
|
sys_text += f". Назначена на @{task.assignee_slug}"
|
||||||
|
await _system_message(db, task_full, sys_text, slug)
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
return _task_out(task_full, slug)
|
||||||
|
|
||||||
|
|
||||||
@router.patch("/tasks/{task_id}", response_model=TaskOut)
|
@router.patch("/tasks/{task_id}", response_model=TaskOut)
|
||||||
async def update_task(task_id: str, req: TaskUpdate, db: AsyncSession = Depends(get_db)):
|
async def update_task(task_id: str, req: TaskUpdate, db: AsyncSession = Depends(get_db)):
|
||||||
task = await _get_task(task_id, db)
|
task = await _get_task(task_id, db)
|
||||||
|
slug = task.project.slug if task.project else ""
|
||||||
|
prefix = slug[:2].upper() if slug else "XX"
|
||||||
|
key = f"{prefix}-{task.number}"
|
||||||
|
|
||||||
|
old_status = task.status
|
||||||
|
old_assignee = task.assignee_slug
|
||||||
|
|
||||||
if req.title is not None:
|
if req.title is not None:
|
||||||
task.title = req.title
|
task.title = req.title
|
||||||
@ -227,6 +269,15 @@ async def update_task(task_id: str, req: TaskUpdate, db: AsyncSession = Depends(
|
|||||||
if req.position is not None:
|
if req.position is not None:
|
||||||
task.position = req.position
|
task.position = req.position
|
||||||
|
|
||||||
|
# System messages for important changes
|
||||||
|
if req.status is not None and req.status != old_status:
|
||||||
|
await _system_message(db, task, f"{key}: статус изменён {old_status} → {req.status}", slug)
|
||||||
|
if req.assignee_slug is not None and req.assignee_slug != old_assignee:
|
||||||
|
if req.assignee_slug:
|
||||||
|
await _system_message(db, task, f"{key}: назначена на @{req.assignee_slug}", slug)
|
||||||
|
else:
|
||||||
|
await _system_message(db, task, f"{key}: исполнитель снят (был @{old_assignee})", slug)
|
||||||
|
|
||||||
await db.commit()
|
await db.commit()
|
||||||
await db.refresh(task)
|
await db.refresh(task)
|
||||||
|
|
||||||
@ -240,7 +291,7 @@ async def update_task(task_id: str, req: TaskUpdate, db: AsyncSession = Depends(
|
|||||||
}
|
}
|
||||||
await manager.broadcast_task_event(str(task.project_id), "task.updated", task_data)
|
await manager.broadcast_task_event(str(task.project_id), "task.updated", task_data)
|
||||||
|
|
||||||
return _task_out(task, task.project.slug if task.project else "")
|
return _task_out(task, slug)
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/tasks/{task_id}")
|
@router.delete("/tasks/{task_id}")
|
||||||
@ -271,6 +322,13 @@ async def take_task(task_id: str, slug: str = Query(...), db: AsyncSession = Dep
|
|||||||
await db.commit()
|
await db.commit()
|
||||||
await db.refresh(task)
|
await db.refresh(task)
|
||||||
|
|
||||||
|
# System message
|
||||||
|
proj_slug = task.project.slug if task.project else ""
|
||||||
|
prefix = proj_slug[:2].upper() if proj_slug else "XX"
|
||||||
|
key = f"{prefix}-{task.number}"
|
||||||
|
await _system_message(db, task, f"{key}: @{slug} взял задачу в работу", proj_slug)
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
# Broadcast task.assigned event
|
# Broadcast task.assigned event
|
||||||
from tracker.ws.manager import manager
|
from tracker.ws.manager import manager
|
||||||
task_data = {
|
task_data = {
|
||||||
@ -281,7 +339,7 @@ async def take_task(task_id: str, slug: str = Query(...), db: AsyncSession = Dep
|
|||||||
}
|
}
|
||||||
await manager.broadcast_task_event(str(task.project_id), "task.assigned", task_data)
|
await manager.broadcast_task_event(str(task.project_id), "task.assigned", task_data)
|
||||||
|
|
||||||
return _task_out(task, task.project.slug if task.project else "")
|
return _task_out(task, proj_slug)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/tasks/{task_id}/reject")
|
@router.post("/tasks/{task_id}/reject")
|
||||||
@ -323,6 +381,13 @@ async def assign_task(task_id: str, req: AssignRequest, db: AsyncSession = Depen
|
|||||||
await db.commit()
|
await db.commit()
|
||||||
await db.refresh(task)
|
await db.refresh(task)
|
||||||
|
|
||||||
|
# System message
|
||||||
|
proj_slug = task.project.slug if task.project else ""
|
||||||
|
prefix = proj_slug[:2].upper() if proj_slug else "XX"
|
||||||
|
key = f"{prefix}-{task.number}"
|
||||||
|
await _system_message(db, task, f"{key}: назначена на @{req.assignee_slug}", proj_slug)
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
# Broadcast task.assigned event
|
# Broadcast task.assigned event
|
||||||
from tracker.ws.manager import manager
|
from tracker.ws.manager import manager
|
||||||
task_data = {
|
task_data = {
|
||||||
@ -333,7 +398,7 @@ async def assign_task(task_id: str, req: AssignRequest, db: AsyncSession = Depen
|
|||||||
}
|
}
|
||||||
await manager.broadcast_task_event(str(task.project_id), "task.assigned", task_data)
|
await manager.broadcast_task_event(str(task.project_id), "task.assigned", task_data)
|
||||||
|
|
||||||
return _task_out(task, task.project.slug if task.project else "")
|
return _task_out(task, proj_slug)
|
||||||
|
|
||||||
|
|
||||||
@router.post("/tasks/{task_id}/watch")
|
@router.post("/tasks/{task_id}/watch")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user