mirror of
https://github.com/dograh-hq/dograh.git
synced 2026-06-13 08:15:21 +02:00
chore: UI enhancements for workflow runs view (#142)
* add local state in filters * feat: add sorting feature by duration * chore: refactor workfow run view
This commit is contained in:
parent
6827744327
commit
5fe1c8ce2f
23 changed files with 1014 additions and 479 deletions
|
|
@ -1,7 +1,8 @@
|
|||
import json
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from api.constants import DEFAULT_CAMPAIGN_RETRY_CONFIG, DEFAULT_ORG_CONCURRENCY_LIMIT
|
||||
|
|
@ -91,6 +92,16 @@ class WorkflowRunResponse(BaseModel):
|
|||
completed_at: Optional[datetime]
|
||||
|
||||
|
||||
class CampaignRunsResponse(BaseModel):
|
||||
"""Paginated response for campaign workflow runs"""
|
||||
|
||||
runs: List[dict] # WorkflowRunResponseSchema from schemas
|
||||
total_count: int
|
||||
page: int
|
||||
limit: int
|
||||
total_pages: int
|
||||
|
||||
|
||||
class CampaignProgressResponse(BaseModel):
|
||||
campaign_id: int
|
||||
state: str
|
||||
|
|
@ -296,21 +307,65 @@ async def pause_campaign(
|
|||
@router.get("/{campaign_id}/runs")
|
||||
async def get_campaign_runs(
|
||||
campaign_id: int,
|
||||
page: int = 1,
|
||||
limit: int = 50,
|
||||
filters: Optional[str] = Query(None, description="JSON-encoded filter criteria"),
|
||||
sort_by: Optional[str] = Query(
|
||||
None, description="Field to sort by (e.g., 'duration', 'created_at')"
|
||||
),
|
||||
sort_order: Optional[str] = Query(
|
||||
"desc", description="Sort order ('asc' or 'desc')"
|
||||
),
|
||||
user: UserModel = Depends(get_user),
|
||||
) -> List[WorkflowRunResponse]:
|
||||
"""Get campaign workflow runs"""
|
||||
runs = await db_client.get_campaign_runs(campaign_id, user.selected_organization_id)
|
||||
) -> CampaignRunsResponse:
|
||||
"""Get campaign workflow runs with pagination, filters and sorting"""
|
||||
offset = (page - 1) * limit
|
||||
|
||||
return [
|
||||
WorkflowRunResponse(
|
||||
id=run.id,
|
||||
workflow_id=run.workflow_id,
|
||||
state="completed" if run.is_completed else "running",
|
||||
created_at=run.created_at,
|
||||
completed_at=run.created_at if run.is_completed else None,
|
||||
# Parse filters if provided
|
||||
filter_criteria = []
|
||||
if filters:
|
||||
try:
|
||||
filter_criteria = json.loads(filters)
|
||||
except json.JSONDecodeError:
|
||||
raise HTTPException(status_code=400, detail="Invalid filter format")
|
||||
|
||||
# Restrict allowed filter attributes for regular users
|
||||
allowed_attributes = {
|
||||
"dateRange",
|
||||
"dispositionCode",
|
||||
"duration",
|
||||
"status",
|
||||
"tokenUsage",
|
||||
}
|
||||
for filter_item in filter_criteria:
|
||||
attribute = filter_item.get("attribute")
|
||||
if attribute and attribute not in allowed_attributes:
|
||||
raise HTTPException(
|
||||
status_code=403, detail=f"Invalid attribute '{attribute}'"
|
||||
)
|
||||
|
||||
try:
|
||||
runs, total_count = await db_client.get_campaign_runs_paginated(
|
||||
campaign_id,
|
||||
user.selected_organization_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
filters=filter_criteria if filter_criteria else None,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
)
|
||||
for run in runs
|
||||
]
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=404, detail=str(e))
|
||||
|
||||
total_pages = (total_count + limit - 1) // limit
|
||||
|
||||
return CampaignRunsResponse(
|
||||
runs=[run.model_dump() for run in runs],
|
||||
total_count=total_count,
|
||||
page=page,
|
||||
limit=limit,
|
||||
total_pages=total_pages,
|
||||
)
|
||||
|
||||
|
||||
@router.post("/{campaign_id}/resume")
|
||||
|
|
|
|||
|
|
@ -105,6 +105,12 @@ async def get_workflow_runs(
|
|||
page: int = Query(1, ge=1, description="Page number (starts from 1)"),
|
||||
limit: int = Query(50, ge=1, le=100, description="Number of items per page"),
|
||||
filters: Optional[str] = Query(None, description="JSON-encoded filter criteria"),
|
||||
sort_by: Optional[str] = Query(
|
||||
None, description="Field to sort by (e.g., 'duration', 'created_at')"
|
||||
),
|
||||
sort_order: Optional[str] = Query(
|
||||
"desc", description="Sort order ('asc' or 'desc')"
|
||||
),
|
||||
user: UserModel = Depends(get_superuser),
|
||||
) -> SuperuserWorkflowRunsListResponse:
|
||||
"""
|
||||
|
|
@ -124,8 +130,16 @@ async def get_workflow_runs(
|
|||
except json.JSONDecodeError:
|
||||
raise HTTPException(status_code=400, detail="Invalid filter format")
|
||||
|
||||
# Validate sort_order
|
||||
if sort_order not in ("asc", "desc"):
|
||||
sort_order = "desc"
|
||||
|
||||
workflow_runs, total_count = await db_client.get_workflow_runs_for_superadmin(
|
||||
limit=limit, offset=offset, filters=filter_criteria
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
filters=filter_criteria,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
)
|
||||
|
||||
total_pages = (total_count + limit - 1) // limit # Ceiling division
|
||||
|
|
|
|||
|
|
@ -666,10 +666,16 @@ async def get_workflow_runs(
|
|||
page: int = 1,
|
||||
limit: int = 50,
|
||||
filters: Optional[str] = Query(None, description="JSON-encoded filter criteria"),
|
||||
sort_by: Optional[str] = Query(
|
||||
None, description="Field to sort by (e.g., 'duration', 'created_at')"
|
||||
),
|
||||
sort_order: Optional[str] = Query(
|
||||
"desc", description="Sort order ('asc' or 'desc')"
|
||||
),
|
||||
user: UserModel = Depends(get_user),
|
||||
) -> WorkflowRunsResponse:
|
||||
"""
|
||||
Get workflow runs with optional filtering.
|
||||
Get workflow runs with optional filtering and sorting.
|
||||
|
||||
Filters should be provided as a JSON-encoded array of filter criteria.
|
||||
Example: [{"attribute": "dateRange", "value": {"from": "2024-01-01", "to": "2024-01-31"}}]
|
||||
|
|
@ -699,23 +705,15 @@ async def get_workflow_runs(
|
|||
status_code=403, detail=f"Invalid attribute '{attribute}'"
|
||||
)
|
||||
|
||||
# Apply filters if any
|
||||
if filter_criteria:
|
||||
runs, total_count = await db_client.get_workflow_runs_by_workflow_id(
|
||||
workflow_id,
|
||||
organization_id=user.selected_organization_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
filters=filter_criteria,
|
||||
)
|
||||
else:
|
||||
# Use existing logic for unfiltered results
|
||||
runs, total_count = await db_client.get_workflow_runs_by_workflow_id(
|
||||
workflow_id,
|
||||
organization_id=user.selected_organization_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
)
|
||||
runs, total_count = await db_client.get_workflow_runs_by_workflow_id(
|
||||
workflow_id,
|
||||
organization_id=user.selected_organization_id,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
filters=filter_criteria if filter_criteria else None,
|
||||
sort_by=sort_by,
|
||||
sort_order=sort_order,
|
||||
)
|
||||
|
||||
total_pages = (total_count + limit - 1) // limit
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue