feat(issues): S4b backend — REST task issue fields + dashboard open-issues
Closes the REST gap S4b's UI needs (S2 only extended MCP tools): - routes/tasks.py: create/update accept system_ids (set-semantics) + arose_from_id; GET/create/update return the task's associated systems. kind=issue already flowed via task_kind. Associations set via services/systems (ACL-checked; can_write_note already gated). - services/dashboard.py: _open_issues section (owner-scoped, ranked like other task lists, capped) added to build_dashboard. Dashboard test updated for the new key. Refs plan 825 (S4b, backend half). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ logger = logging.getLogger(__name__)
|
||||
N_PROJECTS = 3 # most-recently-active projects shown
|
||||
TASKS_PER_GROUP = 5 # open-task cap per milestone / no-milestone group
|
||||
RECENT_DONE_LIMIT = 8 # recently-completed tasks shown
|
||||
OPEN_ISSUES_LIMIT = 10 # open issues shown on the dashboard
|
||||
WINDOW_DAYS = 7 # look-back (done) / look-ahead (events) window
|
||||
_OPEN = ["todo", "in_progress"]
|
||||
|
||||
@@ -49,11 +50,42 @@ async def _safe(coro, empty):
|
||||
return empty
|
||||
|
||||
|
||||
async def _open_issues(user_id: int) -> list[dict]:
|
||||
"""Open issues (task_kind='issue', not done/cancelled) across the owner's
|
||||
projects, ranked like the other task lists. Owner-scoped, matching the rest
|
||||
of the dashboard."""
|
||||
async with async_session() as session:
|
||||
rows = await session.execute(
|
||||
select(
|
||||
Note.id, Note.title, Note.status, Note.priority,
|
||||
Note.project_id, Project.title,
|
||||
)
|
||||
.join(Project, Project.id == Note.project_id, isouter=True)
|
||||
.where(
|
||||
Note.user_id == user_id,
|
||||
Note.task_kind == "issue",
|
||||
Note.status.in_(_OPEN),
|
||||
Note.deleted_at.is_(None),
|
||||
)
|
||||
.order_by(*_open_order())
|
||||
.limit(OPEN_ISSUES_LIMIT)
|
||||
)
|
||||
return [
|
||||
{
|
||||
"id": iid, "title": title, "status": status,
|
||||
"priority": priority or "none",
|
||||
"project_id": pid, "project_title": pname,
|
||||
}
|
||||
for iid, title, status, priority, pid, pname in rows.fetchall()
|
||||
]
|
||||
|
||||
|
||||
async def build_dashboard(user_id: int) -> dict:
|
||||
return {
|
||||
"active_projects": await _safe(_active_projects(user_id), []),
|
||||
"recently_completed": await _safe(_recently_completed(user_id), []),
|
||||
"upcoming_events": await _safe(_upcoming_events(user_id), []),
|
||||
"open_issues": await _safe(_open_issues(user_id), []),
|
||||
"week_stats": await _safe(_week_stats(user_id), {}),
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user