PMFlow/backend/app/services/context_cache_service/mixin_user_snapshot.py

137 lines
4.7 KiB
Python

from __future__ import annotations
from .shared import *
class ContextCacheUserSnapshotMixin:
async def _build_user_snapshot(
self,
*,
db: AsyncSession,
tenant_id: int,
user_id: int,
) -> dict[str, Any]:
owned_rows = (
await db.execute(
select(Project.id).where(
and_(
Project.tenant_id == tenant_id,
Project.owner_id == user_id,
)
)
)
).scalars().all()
member_rows = (
await db.execute(
select(UserProject.project_id).where(
and_(
UserProject.tenant_id == tenant_id,
UserProject.user_id == user_id,
)
)
)
).scalars().all()
project_ids = sorted({_safe_int(pid, 0) for pid in [*owned_rows, *member_rows] if _safe_int(pid, 0) > 0})
project_rows: list[Any] = []
if project_ids:
project_rows = (
await db.execute(
select(Project.id, Project.name, Project.title, Project.status).where(
and_(
Project.tenant_id == tenant_id,
Project.id.in_(project_ids),
)
)
)
).all()
open_tasks = (
await db.execute(
select(Task.id, Task.project_id, Task.title, Task.status, Task.due_date).where(
and_(
Task.tenant_id == tenant_id,
Task.assignee_id == user_id,
func.lower(Task.status).notin_(DONE_TASK_STATUSES),
)
)
)
).all()
pending_signals: list[dict[str, Any]] = []
if project_ids:
pending_rows = (
await db.execute(
select(Signal.id, Signal.title, Signal.priority, Signal.status, Signal.matched_project_id).where(
and_(
Signal.tenant_id == tenant_id,
Signal.matched_project_id.in_(project_ids),
or_(
Signal.processed.is_(False),
func.lower(Signal.status).in_(PENDING_SIGNAL_STATUSES),
),
)
).limit(60)
)
).all()
pending_signals = [
{
"id": int(row.id),
"title": str(row.title or ""),
"priority": str(row.priority or ""),
"status": str(row.status or ""),
"project_id": _safe_int(row.matched_project_id, 0) or None,
}
for row in pending_rows
]
workload_row = (
await db.execute(
select(UserWorkload.workload_ratio).where(
and_(
UserWorkload.tenant_id == tenant_id,
UserWorkload.user_id == user_id,
)
)
)
).scalar_one_or_none()
if workload_row is None:
fallback = (
await db.execute(
select(User.workload).where(
and_(
User.tenant_id == tenant_id,
User.id == user_id,
)
)
)
).scalar_one_or_none()
workload_ratio = round(float(fallback or 0) / 100.0, 2)
else:
workload_ratio = float(workload_row or 0.0)
return {
"user_id": user_id,
"projects": [
{
"id": int(row.id),
"name": str(row.title or row.name or f"Project#{int(row.id)}"),
"status": str(row.status or "unknown"),
}
for row in project_rows
],
"assigned_tasks": [
{
"id": int(row.id),
"project_id": int(row.project_id),
"title": str(row.title or ""),
"status": str(row.status or ""),
"due_date": row.due_date.isoformat() if row.due_date else None,
}
for row in open_tasks
],
"pending_signals": pending_signals,
"workload_ratio": workload_ratio,
"snapshot_at": now_cn_aware().isoformat(),
}