chore: fix all ruff lint errors + format — 0 errors, 306 tests pass

This commit is contained in:
leocrm-bot
2026-06-29 17:43:56 +02:00
parent 316f323ff4
commit a2452cc04b
81 changed files with 2317 additions and 1128 deletions
+92 -46
View File
@@ -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,