feat: dual system messages — detailed in task, brief in chat
Some checks failed
Deploy Tracker / deploy (push) Failing after 1s
Some checks failed
Deploy Tracker / deploy (push) Failing after 1s
- Task comments: who, what, when (detailed history) - Chat messages: brief notifications - PATCH /tasks accepts ?actor= for attribution
This commit is contained in:
parent
b527e19db1
commit
2f0c4d1636
@ -80,48 +80,83 @@ class AssignRequest(BaseModel):
|
||||
|
||||
# --- 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.
|
||||
async def _get_project_chat_id(db: AsyncSession, project_id) -> str | None:
|
||||
"""Find project chat UUID."""
|
||||
result = await db.execute(
|
||||
select(Chat).where(Chat.project_id == project_id, Chat.kind == "project")
|
||||
)
|
||||
chat = result.scalar_one_or_none()
|
||||
return chat.id if chat else None
|
||||
|
||||
Message goes to the project chat (visible in ChatPanel) with task_id reference.
|
||||
|
||||
async def _system_message(
|
||||
db: AsyncSession,
|
||||
task: Task,
|
||||
chat_text: str,
|
||||
task_text: str | None = None,
|
||||
project_slug: str = "",
|
||||
actor_slug: str = "system",
|
||||
):
|
||||
"""Create system messages: one in project chat + one in task comments.
|
||||
|
||||
chat_text: short message for project chat
|
||||
task_text: detailed message for task history (if None, same as chat_text)
|
||||
"""
|
||||
from tracker.ws.manager import manager
|
||||
|
||||
# Find project chat
|
||||
chat_id = None
|
||||
if task.project_id:
|
||||
result = await db.execute(
|
||||
select(Chat).where(Chat.project_id == task.project_id, Chat.kind == "project")
|
||||
)
|
||||
chat = result.scalar_one_or_none()
|
||||
if chat:
|
||||
chat_id = chat.id
|
||||
|
||||
msg = Message(
|
||||
chat_id=chat_id,
|
||||
task_id=task.id,
|
||||
author_type="system",
|
||||
author_slug="system",
|
||||
content=content,
|
||||
mentions=[],
|
||||
)
|
||||
db.add(msg)
|
||||
await db.flush() # get msg.id
|
||||
import datetime
|
||||
|
||||
prefix = project_slug[:2].upper() if project_slug else "XX"
|
||||
key = f"{prefix}-{task.number}"
|
||||
task_text = task_text or chat_text
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
event_data = {
|
||||
"id": str(msg.id),
|
||||
"chat_id": str(chat_id) if chat_id else None,
|
||||
chat_id = await _get_project_chat_id(db, task.project_id)
|
||||
|
||||
# 1. Task comment — detailed history
|
||||
task_msg = Message(
|
||||
task_id=task.id,
|
||||
author_type="system",
|
||||
author_slug=actor_slug,
|
||||
content=task_text,
|
||||
mentions=[],
|
||||
)
|
||||
db.add(task_msg)
|
||||
|
||||
# 2. Chat message — brief notification
|
||||
chat_msg = None
|
||||
if chat_id:
|
||||
chat_msg = Message(
|
||||
chat_id=chat_id,
|
||||
author_type="system",
|
||||
author_slug=actor_slug,
|
||||
content=chat_text,
|
||||
mentions=[],
|
||||
)
|
||||
db.add(chat_msg)
|
||||
|
||||
await db.flush()
|
||||
|
||||
# Broadcast task comment
|
||||
await manager.broadcast_task_event(str(task.project_id), "message.new", {
|
||||
"id": str(task_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 "",
|
||||
}
|
||||
await manager.broadcast_task_event(str(task.project_id), "message.new", event_data)
|
||||
"author_slug": actor_slug,
|
||||
"content": task_text,
|
||||
"created_at": task_msg.created_at.isoformat() if task_msg.created_at else now.isoformat(),
|
||||
})
|
||||
|
||||
# Broadcast chat message
|
||||
if chat_msg and chat_id:
|
||||
await manager.broadcast_task_event(str(task.project_id), "message.new", {
|
||||
"id": str(chat_msg.id),
|
||||
"chat_id": str(chat_id),
|
||||
"author_type": "system",
|
||||
"author_slug": actor_slug,
|
||||
"content": chat_text,
|
||||
"created_at": chat_msg.created_at.isoformat() if chat_msg.created_at else now.isoformat(),
|
||||
})
|
||||
|
||||
|
||||
def _task_out(t: Task, project_slug: str = "") -> dict:
|
||||
@ -248,21 +283,29 @@ async def create_task(
|
||||
# System message
|
||||
slug = project.slug
|
||||
key = f"{slug[:2].upper()}-{task.number}"
|
||||
sys_text = f"Задача {key} создана: {task.title}"
|
||||
chat_text = f"{key} создана: {task.title}"
|
||||
task_text = f"Задача создана: {task.title}"
|
||||
if task.assignee_slug:
|
||||
sys_text += f". Назначена на @{task.assignee_slug}"
|
||||
await _system_message(db, task_full, sys_text, slug)
|
||||
chat_text += f" → @{task.assignee_slug}"
|
||||
task_text += f". Назначена на @{task.assignee_slug}"
|
||||
await _system_message(db, task_full, chat_text=chat_text, task_text=task_text, project_slug=slug)
|
||||
await db.commit()
|
||||
|
||||
return _task_out(task_full, slug)
|
||||
|
||||
|
||||
@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,
|
||||
actor: Optional[str] = Query(None, description="Who made the change"),
|
||||
db: AsyncSession = Depends(get_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}"
|
||||
who = f"@{actor}" if actor else "Кто-то"
|
||||
|
||||
old_status = task.status
|
||||
old_assignee = task.assignee_slug
|
||||
@ -287,13 +330,33 @@ async def update_task(task_id: str, req: TaskUpdate, db: AsyncSession = Depends(
|
||||
task.position = req.position
|
||||
|
||||
# System messages for important changes
|
||||
import datetime
|
||||
now_str = datetime.datetime.now(datetime.timezone.utc).strftime("%H:%M UTC")
|
||||
if req.status is not None and req.status != old_status:
|
||||
await _system_message(db, task, f"{key}: статус изменён {old_status} → {req.status}", slug)
|
||||
await _system_message(
|
||||
db, task,
|
||||
chat_text=f"{key}: {old_status} → {req.status}",
|
||||
task_text=f"{who} изменил статус: {old_status} → {req.status} ({now_str})",
|
||||
project_slug=slug,
|
||||
actor_slug=actor or "system",
|
||||
)
|
||||
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)
|
||||
await _system_message(
|
||||
db, task,
|
||||
chat_text=f"{key}: назначена на @{req.assignee_slug}",
|
||||
task_text=f"{who} назначил задачу на @{req.assignee_slug} ({now_str})",
|
||||
project_slug=slug,
|
||||
actor_slug=actor or "system",
|
||||
)
|
||||
else:
|
||||
await _system_message(db, task, f"{key}: исполнитель снят (был @{old_assignee})", slug)
|
||||
await _system_message(
|
||||
db, task,
|
||||
chat_text=f"{key}: исполнитель снят",
|
||||
task_text=f"{who} снял исполнителя @{old_assignee} ({now_str})",
|
||||
project_slug=slug,
|
||||
actor_slug=actor or "system",
|
||||
)
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(task)
|
||||
@ -343,7 +406,15 @@ async def take_task(task_id: str, slug: str = Query(...), db: AsyncSession = Dep
|
||||
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)
|
||||
import datetime
|
||||
now_str = datetime.datetime.now(datetime.timezone.utc).strftime("%H:%M UTC")
|
||||
await _system_message(
|
||||
db, task,
|
||||
chat_text=f"{key}: @{slug} взял в работу",
|
||||
task_text=f"@{slug} взял задачу в работу ({now_str})",
|
||||
project_slug=proj_slug,
|
||||
actor_slug=slug,
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
# Broadcast task.assigned event
|
||||
@ -402,7 +473,12 @@ async def assign_task(task_id: str, req: AssignRequest, db: AsyncSession = Depen
|
||||
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 _system_message(
|
||||
db, task,
|
||||
chat_text=f"{key}: назначена на @{req.assignee_slug}",
|
||||
task_text=f"Задача назначена на @{req.assignee_slug}",
|
||||
project_slug=proj_slug,
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
# Broadcast task.assigned event
|
||||
|
||||
Loading…
Reference in New Issue
Block a user