chore: fix all ruff lint errors + format — 0 errors, 306 tests pass
This commit is contained in:
@@ -3,15 +3,15 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import select, func, desc, and_, or_
|
||||
from sqlalchemy import desc, func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.audit import log_audit
|
||||
from app.models.workflow import Workflow, WorkflowInstance, WorkflowStepHistory
|
||||
from app.models.notification import Notification
|
||||
from app.models.workflow import Workflow, WorkflowInstance, WorkflowStepHistory
|
||||
|
||||
|
||||
def _safe_iso(dt) -> str | None:
|
||||
@@ -47,7 +47,12 @@ def _workflow_to_dict(w: Workflow) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _instance_to_dict(i: WorkflowInstance, include_history: bool = False, history: list | None = None, workflow_name: str | None = None) -> dict[str, Any]:
|
||||
def _instance_to_dict(
|
||||
i: WorkflowInstance,
|
||||
include_history: bool = False,
|
||||
history: list | None = None,
|
||||
workflow_name: str | None = None,
|
||||
) -> dict[str, Any]:
|
||||
data = {
|
||||
"id": str(i.id),
|
||||
"workflow_id": str(i.workflow_id),
|
||||
@@ -107,6 +112,7 @@ async def _log_step_history(
|
||||
|
||||
# ─── Workflow CRUD ───
|
||||
|
||||
|
||||
async def create_workflow(
|
||||
db: AsyncSession,
|
||||
tenant_id: uuid.UUID,
|
||||
@@ -134,7 +140,9 @@ async def create_workflow(
|
||||
await db.flush()
|
||||
|
||||
await log_audit(
|
||||
db, tenant_id, user_id,
|
||||
db,
|
||||
tenant_id,
|
||||
user_id,
|
||||
action="create",
|
||||
entity_type="workflow",
|
||||
entity_id=workflow.id,
|
||||
@@ -229,7 +237,9 @@ async def update_workflow(
|
||||
await db.flush()
|
||||
|
||||
await log_audit(
|
||||
db, tenant_id, user_id,
|
||||
db,
|
||||
tenant_id,
|
||||
user_id,
|
||||
action="update",
|
||||
entity_type="workflow",
|
||||
entity_id=workflow.id,
|
||||
@@ -261,7 +271,9 @@ async def delete_workflow(
|
||||
await db.flush()
|
||||
|
||||
await log_audit(
|
||||
db, tenant_id, user_id,
|
||||
db,
|
||||
tenant_id,
|
||||
user_id,
|
||||
action="delete",
|
||||
entity_type="workflow",
|
||||
entity_id=wf_uuid,
|
||||
@@ -272,6 +284,7 @@ async def delete_workflow(
|
||||
|
||||
# ─── Instance Lifecycle ───
|
||||
|
||||
|
||||
async def create_instance(
|
||||
db: AsyncSession,
|
||||
tenant_id: uuid.UUID,
|
||||
@@ -294,7 +307,7 @@ async def create_instance(
|
||||
|
||||
timeout_at = None
|
||||
if timeout_hours:
|
||||
timeout_at = datetime.now(timezone.utc) + timedelta(hours=timeout_hours)
|
||||
timeout_at = datetime.now(UTC) + timedelta(hours=timeout_hours)
|
||||
|
||||
instance = WorkflowInstance(
|
||||
tenant_id=tenant_id,
|
||||
@@ -314,7 +327,9 @@ async def create_instance(
|
||||
steps = workflow.steps or []
|
||||
first_step = steps[0] if steps else {}
|
||||
await _log_step_history(
|
||||
db, tenant_id, instance.id,
|
||||
db,
|
||||
tenant_id,
|
||||
instance.id,
|
||||
step_index=0,
|
||||
step_type=first_step.get("type", "action"),
|
||||
action="entered",
|
||||
@@ -323,7 +338,9 @@ async def create_instance(
|
||||
)
|
||||
|
||||
await log_audit(
|
||||
db, tenant_id, user_id,
|
||||
db,
|
||||
tenant_id,
|
||||
user_id,
|
||||
action="create",
|
||||
entity_type="workflow_instance",
|
||||
entity_id=instance.id,
|
||||
@@ -383,16 +400,18 @@ async def get_instance(
|
||||
return None
|
||||
|
||||
# Get workflow name
|
||||
wf_result = await db.execute(
|
||||
select(Workflow.name).where(Workflow.id == instance.workflow_id)
|
||||
)
|
||||
wf_result = await db.execute(select(Workflow.name).where(Workflow.id == instance.workflow_id))
|
||||
workflow_name = wf_result.scalar_one_or_none()
|
||||
|
||||
# Get step history
|
||||
hist_q = select(WorkflowStepHistory).where(
|
||||
WorkflowStepHistory.instance_id == inst_uuid,
|
||||
WorkflowStepHistory.tenant_id == tenant_id,
|
||||
).order_by(WorkflowStepHistory.created_at)
|
||||
hist_q = (
|
||||
select(WorkflowStepHistory)
|
||||
.where(
|
||||
WorkflowStepHistory.instance_id == inst_uuid,
|
||||
WorkflowStepHistory.tenant_id == tenant_id,
|
||||
)
|
||||
.order_by(WorkflowStepHistory.created_at)
|
||||
)
|
||||
hist_result = await db.execute(hist_q)
|
||||
history = hist_result.scalars().all()
|
||||
|
||||
@@ -430,12 +449,13 @@ async def advance_instance(
|
||||
return None
|
||||
|
||||
if instance.status not in ("pending", "in_progress"):
|
||||
return {"error": f"Cannot advance instance with status {instance.status}", "status_code": 400}
|
||||
return {
|
||||
"error": f"Cannot advance instance with status {instance.status}",
|
||||
"status_code": 400,
|
||||
}
|
||||
|
||||
# Get workflow definition
|
||||
wf_result = await db.execute(
|
||||
select(Workflow).where(Workflow.id == instance.workflow_id)
|
||||
)
|
||||
wf_result = await db.execute(select(Workflow).where(Workflow.id == instance.workflow_id))
|
||||
workflow = wf_result.scalar_one_or_none()
|
||||
if workflow is None:
|
||||
return {"error": "Workflow definition not found", "status_code": 404}
|
||||
@@ -447,9 +467,11 @@ async def advance_instance(
|
||||
|
||||
if decision == "reject":
|
||||
instance.status = "rejected"
|
||||
instance.completed_at = datetime.now(timezone.utc)
|
||||
instance.completed_at = datetime.now(UTC)
|
||||
await _log_step_history(
|
||||
db, tenant_id, instance.id,
|
||||
db,
|
||||
tenant_id,
|
||||
instance.id,
|
||||
step_index=current_idx,
|
||||
step_type=step_type,
|
||||
action="rejected",
|
||||
@@ -470,7 +492,9 @@ async def advance_instance(
|
||||
await db.flush()
|
||||
|
||||
await log_audit(
|
||||
db, tenant_id, user_id,
|
||||
db,
|
||||
tenant_id,
|
||||
user_id,
|
||||
action="reject",
|
||||
entity_type="workflow_instance",
|
||||
entity_id=instance.id,
|
||||
@@ -486,7 +510,9 @@ async def advance_instance(
|
||||
|
||||
# Log approval
|
||||
await _log_step_history(
|
||||
db, tenant_id, instance.id,
|
||||
db,
|
||||
tenant_id,
|
||||
instance.id,
|
||||
step_index=current_idx,
|
||||
step_type=step_type,
|
||||
action="approved",
|
||||
@@ -498,9 +524,11 @@ async def advance_instance(
|
||||
if next_idx >= len(steps):
|
||||
# Workflow complete
|
||||
instance.status = "completed"
|
||||
instance.completed_at = datetime.now(timezone.utc)
|
||||
instance.completed_at = datetime.now(UTC)
|
||||
await _log_step_history(
|
||||
db, tenant_id, instance.id,
|
||||
db,
|
||||
tenant_id,
|
||||
instance.id,
|
||||
step_index=current_idx,
|
||||
step_type="complete",
|
||||
action="completed",
|
||||
@@ -510,7 +538,9 @@ async def advance_instance(
|
||||
instance.current_step_index = next_idx
|
||||
next_step = steps[next_idx]
|
||||
await _log_step_history(
|
||||
db, tenant_id, instance.id,
|
||||
db,
|
||||
tenant_id,
|
||||
instance.id,
|
||||
step_index=next_idx,
|
||||
step_type=next_step.get("type", "action"),
|
||||
action="entered",
|
||||
@@ -520,11 +550,17 @@ async def advance_instance(
|
||||
await db.flush()
|
||||
|
||||
await log_audit(
|
||||
db, tenant_id, user_id,
|
||||
db,
|
||||
tenant_id,
|
||||
user_id,
|
||||
action="advance",
|
||||
entity_type="workflow_instance",
|
||||
entity_id=instance.id,
|
||||
changes={"decision": decision, "step": current_idx, "next_step": next_idx if next_idx < len(steps) else None},
|
||||
changes={
|
||||
"decision": decision,
|
||||
"step": current_idx,
|
||||
"next_step": next_idx if next_idx < len(steps) else None,
|
||||
},
|
||||
)
|
||||
|
||||
return _instance_to_dict(instance)
|
||||
@@ -549,21 +585,26 @@ async def cancel_instance(
|
||||
return None
|
||||
|
||||
if instance.status in ("completed", "rejected", "cancelled"):
|
||||
return {"error": f"Cannot cancel instance with status {instance.status}", "status_code": 400}
|
||||
return {
|
||||
"error": f"Cannot cancel instance with status {instance.status}",
|
||||
"status_code": 400,
|
||||
}
|
||||
|
||||
instance.status = "cancelled"
|
||||
instance.completed_at = datetime.now(timezone.utc)
|
||||
instance.completed_at = datetime.now(UTC)
|
||||
|
||||
# Get current step for history
|
||||
wf_result = await db.execute(
|
||||
select(Workflow).where(Workflow.id == instance.workflow_id)
|
||||
)
|
||||
wf_result = await db.execute(select(Workflow).where(Workflow.id == instance.workflow_id))
|
||||
workflow = wf_result.scalar_one_or_none()
|
||||
steps = workflow.steps if workflow else []
|
||||
current_step = steps[instance.current_step_index] if instance.current_step_index < len(steps) else {}
|
||||
current_step = (
|
||||
steps[instance.current_step_index] if instance.current_step_index < len(steps) else {}
|
||||
)
|
||||
|
||||
await _log_step_history(
|
||||
db, tenant_id, instance.id,
|
||||
db,
|
||||
tenant_id,
|
||||
instance.id,
|
||||
step_index=instance.current_step_index,
|
||||
step_type=current_step.get("type", "action"),
|
||||
action="cancelled",
|
||||
@@ -573,7 +614,9 @@ async def cancel_instance(
|
||||
await db.flush()
|
||||
|
||||
await log_audit(
|
||||
db, tenant_id, user_id,
|
||||
db,
|
||||
tenant_id,
|
||||
user_id,
|
||||
action="cancel",
|
||||
entity_type="workflow_instance",
|
||||
entity_id=instance.id,
|
||||
@@ -591,7 +634,7 @@ async def check_timeout(instance: WorkflowInstance) -> bool:
|
||||
return False
|
||||
if instance.status not in ("pending", "in_progress"):
|
||||
return False
|
||||
return datetime.now(timezone.utc) > instance.timeout_at
|
||||
return datetime.now(UTC) > instance.timeout_at
|
||||
|
||||
|
||||
async def auto_reject_timeout(
|
||||
@@ -601,17 +644,19 @@ async def auto_reject_timeout(
|
||||
) -> dict[str, Any]:
|
||||
"""Auto-reject a timed-out instance."""
|
||||
instance.status = "rejected"
|
||||
instance.completed_at = datetime.now(timezone.utc)
|
||||
instance.completed_at = datetime.now(UTC)
|
||||
|
||||
wf_result = await db.execute(
|
||||
select(Workflow).where(Workflow.id == instance.workflow_id)
|
||||
)
|
||||
wf_result = await db.execute(select(Workflow).where(Workflow.id == instance.workflow_id))
|
||||
workflow = wf_result.scalar_one_or_none()
|
||||
steps = workflow.steps if workflow else []
|
||||
current_step = steps[instance.current_step_index] if instance.current_step_index < len(steps) else {}
|
||||
current_step = (
|
||||
steps[instance.current_step_index] if instance.current_step_index < len(steps) else {}
|
||||
)
|
||||
|
||||
await _log_step_history(
|
||||
db, tenant_id, instance.id,
|
||||
db,
|
||||
tenant_id,
|
||||
instance.id,
|
||||
step_index=instance.current_step_index,
|
||||
step_type=current_step.get("type", "approval"),
|
||||
action="auto_rejected",
|
||||
@@ -665,7 +710,8 @@ async def start_instance_for_event(
|
||||
instances: list[dict[str, Any]] = []
|
||||
for wf in workflows:
|
||||
inst = await create_instance(
|
||||
db, tenant_id,
|
||||
db,
|
||||
tenant_id,
|
||||
user_id or uuid.uuid4(),
|
||||
str(wf.id),
|
||||
context=context,
|
||||
|
||||
Reference in New Issue
Block a user