From c3f478a763b4b1a5609382228c081dcb08812a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D9=8A?= Date: Fri, 6 Mar 2026 21:37:36 +0530 Subject: [PATCH] Normalize today workflow task dependencies as arrays --- backend/api/today_workflow.py | 17 ++++++++-- backend/services/today_workflow_service.py | 21 +++++++++++- frontend/src/stores/workflowStore.ts | 38 ++++++++++++++++++++-- 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/backend/api/today_workflow.py b/backend/api/today_workflow.py index 928840b3..2ca792cc 100644 --- a/backend/api/today_workflow.py +++ b/backend/api/today_workflow.py @@ -7,7 +7,7 @@ from sqlalchemy.orm import Session from middleware.auth_middleware import get_current_user from services.database import get_db -from services.today_workflow_service import get_or_create_daily_workflow_plan, update_task_status +from services.today_workflow_service import coerce_dependencies, get_or_create_daily_workflow_plan, update_task_status from models.daily_workflow_models import DailyWorkflowPlan, DailyWorkflowTask import asyncio from services.intelligence.txtai_service import TxtaiIntelligenceService @@ -62,6 +62,19 @@ async def get_today_workflow( tasks = await run_in_threadpool(_fetch_tasks) + def _normalize_legacy_dependencies(task_rows): + rows_updated = False + for row in task_rows: + normalized_dependencies = coerce_dependencies(row.dependencies) + if row.dependencies != normalized_dependencies: + row.dependencies = normalized_dependencies + db.add(row) + rows_updated = True + if rows_updated: + db.commit() + + await run_in_threadpool(_normalize_legacy_dependencies, tasks) + response_tasks = [] for t in tasks: response_tasks.append( @@ -73,7 +86,7 @@ async def get_today_workflow( "status": "skipped" if t.status == "dismissed" else t.status, "priority": t.priority, "estimatedTime": t.estimated_time, - "dependencies": t.dependencies or [], + "dependencies": coerce_dependencies(t.dependencies), "actionUrl": t.action_url, "actionType": t.action_type, "metadata": t.metadata_json or {}, diff --git a/backend/services/today_workflow_service.py b/backend/services/today_workflow_service.py index 1be7cb21..210e5aad 100644 --- a/backend/services/today_workflow_service.py +++ b/backend/services/today_workflow_service.py @@ -29,6 +29,25 @@ def _coerce_status(value: Any) -> str: return "pending" +def coerce_dependencies(value: Any) -> List[str]: + if isinstance(value, list): + return [str(dep).strip() for dep in value if str(dep).strip()] + + if isinstance(value, str): + raw = value.strip() + if not raw: + return [] + try: + parsed = json.loads(raw) + if isinstance(parsed, list): + return [str(dep).strip() for dep in parsed if str(dep).strip()] + except Exception: + pass + return [raw] + + return [] + + def _proposal_priority_rank(priority: str) -> int: return {"low": 0, "medium": 1, "high": 2}.get(str(priority or "").lower(), 1) @@ -523,7 +542,7 @@ async def get_or_create_daily_workflow_plan(db: Session, user_id: str, date: Opt estimated_time=int(t.get("estimatedTime") or 15), action_type=str(t.get("actionType") or "navigate").strip()[:20], action_url=str(t.get("actionUrl") or "").strip(), - dependencies=json.dumps(t.get("dependencies") or []), + dependencies=coerce_dependencies(t.get("dependencies")), metadata_json=t.get("metadata") or {}, enabled=bool(t.get("enabled", True)), created_at=datetime.utcnow(), diff --git a/frontend/src/stores/workflowStore.ts b/frontend/src/stores/workflowStore.ts index fb0c1031..d834b540 100644 --- a/frontend/src/stores/workflowStore.ts +++ b/frontend/src/stores/workflowStore.ts @@ -14,6 +14,39 @@ import { apiClient } from '../api/client'; const isServerWorkflowId = (workflowId: string) => workflowId.startsWith('daily-'); + +const normalizeDependencies = (dependencies: unknown): string[] => { + if (Array.isArray(dependencies)) { + return dependencies.map(dep => String(dep).trim()).filter(Boolean); + } + + if (typeof dependencies === 'string') { + const raw = dependencies.trim(); + if (!raw) return []; + + try { + const parsed = JSON.parse(raw); + if (Array.isArray(parsed)) { + return parsed.map(dep => String(dep).trim()).filter(Boolean); + } + } catch {} + + return [raw]; + } + + return []; +}; + +const normalizeServerWorkflow = (workflow: DailyWorkflow): DailyWorkflow => ({ + ...workflow, + tasks: Array.isArray(workflow.tasks) + ? workflow.tasks.map(task => ({ + ...task, + dependencies: normalizeDependencies(task.dependencies), + })) + : [], +}); + const computeProgressAndNavigation = (workflow: DailyWorkflow): { progress: WorkflowProgress; navigation: NavigationState } => { const tasks = Array.isArray(workflow.tasks) ? workflow.tasks : []; const totalTasks = tasks.length; @@ -118,9 +151,10 @@ export const useWorkflowStore = create()( const resp = await apiClient.get('/api/today-workflow', { params: date ? { date } : {} }); const serverWorkflow = resp?.data?.data?.workflow as DailyWorkflow | undefined; if (serverWorkflow && Array.isArray(serverWorkflow.tasks)) { - const derived = computeProgressAndNavigation(serverWorkflow); + const normalizedWorkflow = normalizeServerWorkflow(serverWorkflow); + const derived = computeProgressAndNavigation(normalizedWorkflow); set({ - currentWorkflow: serverWorkflow, + currentWorkflow: normalizedWorkflow, workflowProgress: derived.progress, navigationState: derived.navigation, isLoading: false