chore(backend): add black/isort/flake8 + pre-commit

This commit is contained in:
Abhimanyu Saharan
2026-02-02 20:15:38 +05:30
parent 002bd08f33
commit a8f097817d
26 changed files with 299 additions and 67 deletions

View File

@@ -2,16 +2,16 @@ from __future__ import annotations
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
from sqlmodel import Session, select
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from sqlalchemy.exc import IntegrityError
from sqlmodel import Session, select
from app.api.utils import log_activity, get_actor_employee_id
from app.api.utils import get_actor_employee_id, log_activity
from app.db.session import get_session
from app.integrations.notify import NotifyContext, notify_openclaw
from app.models.org import Employee
from app.models.work import Task, TaskComment
from app.schemas.work import TaskCommentCreate, TaskCreate, TaskUpdate
from app.integrations.notify import NotifyContext, notify_openclaw
router = APIRouter(tags=["work"])
@@ -33,7 +33,9 @@ def _validate_task_assignee(session: Session, assignee_employee_id: int) -> None
if emp.status != "active":
raise HTTPException(status_code=400, detail="Cannot assign task to inactive agent")
if not emp.notify_enabled:
raise HTTPException(status_code=400, detail="Cannot assign task to agent with notifications disabled")
raise HTTPException(
status_code=400, detail="Cannot assign task to agent with notifications disabled"
)
if not emp.openclaw_session_key:
raise HTTPException(status_code=400, detail="Cannot assign task to unprovisioned agent")
@@ -47,9 +49,16 @@ def list_tasks(project_id: int | None = None, session: Session = Depends(get_ses
@router.post("/tasks", response_model=Task)
def create_task(payload: TaskCreate, background: BackgroundTasks, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
def create_task(
payload: TaskCreate,
background: BackgroundTasks,
session: Session = Depends(get_session),
actor_employee_id: int = Depends(get_actor_employee_id),
):
if payload.created_by_employee_id is None:
payload = TaskCreate(**{**payload.model_dump(), "created_by_employee_id": actor_employee_id})
payload = TaskCreate(
**{**payload.model_dump(), "created_by_employee_id": actor_employee_id}
)
if payload.assignee_employee_id is not None:
_validate_task_assignee(session, payload.assignee_employee_id)
@@ -58,7 +67,9 @@ def create_task(payload: TaskCreate, background: BackgroundTasks, session: Sessi
if payload.reviewer_employee_id is None and payload.assignee_employee_id is not None:
assignee = session.get(Employee, payload.assignee_employee_id)
if assignee is not None and assignee.manager_id is not None:
payload = TaskCreate(**{**payload.model_dump(), "reviewer_employee_id": assignee.manager_id})
payload = TaskCreate(
**{**payload.model_dump(), "reviewer_employee_id": assignee.manager_id}
)
task = Task(**payload.model_dump())
if task.status not in ALLOWED_STATUSES:
@@ -82,18 +93,32 @@ def create_task(payload: TaskCreate, background: BackgroundTasks, session: Sessi
raise HTTPException(status_code=409, detail="Task create violates constraints")
session.refresh(task)
background.add_task(notify_openclaw, session, NotifyContext(event="task.created", actor_employee_id=actor_employee_id, task=task))
background.add_task(
notify_openclaw,
session,
NotifyContext(event="task.created", actor_employee_id=actor_employee_id, task=task),
)
# Explicitly return a serializable payload (guards against empty {} responses)
return Task.model_validate(task)
@router.patch("/tasks/{task_id}", response_model=Task)
def update_task(task_id: int, payload: TaskUpdate, background: BackgroundTasks, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
def update_task(
task_id: int,
payload: TaskUpdate,
background: BackgroundTasks,
session: Session = Depends(get_session),
actor_employee_id: int = Depends(get_actor_employee_id),
):
task = session.get(Task, task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
before = {"assignee_employee_id": task.assignee_employee_id, "reviewer_employee_id": task.reviewer_employee_id, "status": task.status}
before = {
"assignee_employee_id": task.assignee_employee_id,
"reviewer_employee_id": task.reviewer_employee_id,
"status": task.status,
}
data = payload.model_dump(exclude_unset=True)
if "assignee_employee_id" in data and data["assignee_employee_id"] is not None:
@@ -108,7 +133,14 @@ def update_task(task_id: int, payload: TaskUpdate, background: BackgroundTasks,
try:
session.flush()
log_activity(session, actor_employee_id=actor_employee_id, entity_type="task", entity_id=task.id, verb="updated", payload=data)
log_activity(
session,
actor_employee_id=actor_employee_id,
entity_type="task",
entity_id=task.id,
verb="updated",
payload=data,
)
session.commit()
except IntegrityError:
session.rollback()
@@ -119,19 +151,53 @@ def update_task(task_id: int, payload: TaskUpdate, background: BackgroundTasks,
# notify based on meaningful changes
changed = {}
if before.get("assignee_employee_id") != task.assignee_employee_id:
changed["assignee_employee_id"] = {"from": before.get("assignee_employee_id"), "to": task.assignee_employee_id}
background.add_task(notify_openclaw, session, NotifyContext(event="task.assigned", actor_employee_id=actor_employee_id, task=task, changed_fields=changed))
changed["assignee_employee_id"] = {
"from": before.get("assignee_employee_id"),
"to": task.assignee_employee_id,
}
background.add_task(
notify_openclaw,
session,
NotifyContext(
event="task.assigned",
actor_employee_id=actor_employee_id,
task=task,
changed_fields=changed,
),
)
if before.get("status") != task.status:
changed["status"] = {"from": before.get("status"), "to": task.status}
background.add_task(notify_openclaw, session, NotifyContext(event="status.changed", actor_employee_id=actor_employee_id, task=task, changed_fields=changed))
background.add_task(
notify_openclaw,
session,
NotifyContext(
event="status.changed",
actor_employee_id=actor_employee_id,
task=task,
changed_fields=changed,
),
)
if not changed and data:
background.add_task(notify_openclaw, session, NotifyContext(event="task.updated", actor_employee_id=actor_employee_id, task=task, changed_fields=data))
background.add_task(
notify_openclaw,
session,
NotifyContext(
event="task.updated",
actor_employee_id=actor_employee_id,
task=task,
changed_fields=data,
),
)
return Task.model_validate(task)
@router.delete("/tasks/{task_id}")
def delete_task(task_id: int, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
def delete_task(
task_id: int,
session: Session = Depends(get_session),
actor_employee_id: int = Depends(get_actor_employee_id),
):
task = session.get(Task, task_id)
if not task:
raise HTTPException(status_code=404, detail="Task not found")
@@ -139,7 +205,13 @@ def delete_task(task_id: int, session: Session = Depends(get_session), actor_emp
session.delete(task)
try:
session.flush()
log_activity(session, actor_employee_id=actor_employee_id, entity_type="task", entity_id=task_id, verb="deleted")
log_activity(
session,
actor_employee_id=actor_employee_id,
entity_type="task",
entity_id=task_id,
verb="deleted",
)
session.commit()
except IntegrityError:
session.rollback()
@@ -150,20 +222,35 @@ def delete_task(task_id: int, session: Session = Depends(get_session), actor_emp
@router.get("/task-comments", response_model=list[TaskComment])
def list_task_comments(task_id: int, session: Session = Depends(get_session)):
return session.exec(select(TaskComment).where(TaskComment.task_id == task_id).order_by(TaskComment.id.asc())).all()
return session.exec(
select(TaskComment).where(TaskComment.task_id == task_id).order_by(TaskComment.id.asc())
).all()
@router.post("/task-comments", response_model=TaskComment)
def create_task_comment(payload: TaskCommentCreate, background: BackgroundTasks, session: Session = Depends(get_session), actor_employee_id: int = Depends(get_actor_employee_id)):
def create_task_comment(
payload: TaskCommentCreate,
background: BackgroundTasks,
session: Session = Depends(get_session),
actor_employee_id: int = Depends(get_actor_employee_id),
):
if payload.author_employee_id is None:
payload = TaskCommentCreate(**{**payload.model_dump(), "author_employee_id": actor_employee_id})
payload = TaskCommentCreate(
**{**payload.model_dump(), "author_employee_id": actor_employee_id}
)
c = TaskComment(**payload.model_dump())
session.add(c)
try:
session.flush()
log_activity(session, actor_employee_id=actor_employee_id, entity_type="task", entity_id=c.task_id, verb="commented")
log_activity(
session,
actor_employee_id=actor_employee_id,
entity_type="task",
entity_id=c.task_id,
verb="commented",
)
session.commit()
except IntegrityError:
session.rollback()
@@ -172,5 +259,11 @@ def create_task_comment(payload: TaskCommentCreate, background: BackgroundTasks,
session.refresh(c)
task = session.get(Task, c.task_id)
if task is not None:
background.add_task(notify_openclaw, session, NotifyContext(event="comment.created", actor_employee_id=actor_employee_id, task=task, comment=c))
background.add_task(
notify_openclaw,
session,
NotifyContext(
event="comment.created", actor_employee_id=actor_employee_id, task=task, comment=c
),
)
return TaskComment.model_validate(c)