2424from fastapi .responses import FileResponse , StreamingResponse
2525from fastapi_pagination import Page
2626from pydantic import ValidationError
27- from starlette import status
27+ from starlette import status as http_status
2828from typing_extensions import Literal
2929
3030from waldiez_runner .config import Settings
4747from waldiez_runner .models import TaskStatus
4848from waldiez_runner .schemas .task import (
4949 InputResponse ,
50+ TaskCountResponse ,
5051 TaskCreate ,
5152 TaskResponse ,
5253 TaskUpdate ,
@@ -90,6 +91,10 @@ async def get_client_tasks(
9091 client_id : Annotated [str , Depends (validate_tasks_audience )],
9192 db : Annotated [DatabaseManager , Depends (get_db_manager )],
9293 search : Annotated [str | None , Query (description = "A term to search" )] = None ,
94+ status : Annotated [
95+ TaskStatus | None ,
96+ Query (description = "Filter by task status" ),
97+ ] = None ,
9398 order_by : Annotated [
9499 TaskSort | None ,
95100 Query (description = "The field to sort the results" ),
@@ -107,6 +112,8 @@ async def get_client_tasks(
107112 The client ID.
108113 db : DatabaseManager
109114 The database session manager.
115+ status : TaskStatus | None
116+ The task status to filter the tasks.
110117 search : str | None
111118 A search term to filter the tasks.
112119 order_by : str | None
@@ -125,6 +132,7 @@ async def get_client_tasks(
125132 session ,
126133 client_id ,
127134 params = params ,
135+ status = status ,
128136 search = search ,
129137 order_by = order_by ,
130138 descending = order_type == "desc" ,
@@ -140,6 +148,10 @@ async def get_client_tasks(
140148async def get_all_tasks (
141149 _ : Annotated [str , Depends (validate_admin_audience )],
142150 db : Annotated [DatabaseManager , Depends (get_db_manager )],
151+ status : Annotated [
152+ TaskStatus | None ,
153+ Query (description = "Filter by task status" ),
154+ ] = None ,
143155 search : Annotated [str | None , Query (description = "A term to search" )] = None ,
144156 order_by : Annotated [
145157 TaskSort | None ,
@@ -156,6 +168,8 @@ async def get_all_tasks(
156168 ----------
157169 db : DatabaseManager
158170 The database session manager.
171+ status : TaskStatus | None
172+ The task status to filter the tasks.
159173 search : str | None
160174 A search term to filter the tasks.
161175 order_by : str | None
@@ -173,6 +187,7 @@ async def get_all_tasks(
173187 return await TaskService .get_all_tasks (
174188 session ,
175189 params = params ,
190+ status = status ,
176191 search = search ,
177192 order_by = order_by ,
178193 descending = order_type == "desc" ,
@@ -267,13 +282,13 @@ async def create_task(
267282
268283 if not file and not file_url and not filename :
269284 raise HTTPException (
270- status_code = status .HTTP_400_BAD_REQUEST ,
285+ status_code = http_status .HTTP_400_BAD_REQUEST ,
271286 detail = "Either file, file_url or filename must be provided" ,
272287 )
273288 provided = sum (bool (item ) for item in [file , file_url , filename ])
274289 if provided > 1 :
275290 raise HTTPException (
276- status_code = status .HTTP_400_BAD_REQUEST ,
291+ status_code = http_status .HTTP_400_BAD_REQUEST ,
277292 detail = (
278293 "Only one of `file`, `file_url` or `filename` can be provided"
279294 ),
@@ -405,7 +420,88 @@ async def upload_task_workflow(
405420 except HTTPException :
406421 await storage .delete_file (save_path )
407422 raise
408- return Response (status_code = status .HTTP_204_NO_CONTENT )
423+ return Response (status_code = http_status .HTTP_204_NO_CONTENT )
424+
425+
426+ @task_router .get ("/tasks/count/" , include_in_schema = False )
427+ @task_router .get (
428+ "/tasks/count" ,
429+ summary = "Count tasks." ,
430+ description = "Get the number of tasks, optionally filtered by status." ,
431+ response_model = TaskCountResponse ,
432+ )
433+ async def count_tasks (
434+ client_id : Annotated [str , Depends (validate_tasks_audience )],
435+ db : Annotated [DatabaseManager , Depends (get_db_manager )],
436+ status : Annotated [
437+ TaskStatus | None , Query (description = "Filter by task status" )
438+ ] = None ,
439+ active_only : Annotated [
440+ bool , Query (description = "Only get active tasks" )
441+ ] = False ,
442+ inactive_only : Annotated [
443+ bool , Query (description = "Only get inactive tasks" )
444+ ] = False ,
445+ search : Annotated [str | None , Query (min_length = 1 )] = None ,
446+ ) -> TaskCountResponse :
447+ """Get the number of tasks, optionally filtered by status.
448+
449+ Parameters
450+ ----------
451+ client_id : str
452+ The client ID.
453+ db : DatabaseManager
454+ The database session manager.
455+ status : TaskStatus | None
456+ Optional task status to filter by.
457+ active_only : bool
458+ If True, count only active tasks.
459+ inactive_only : bool
460+ If True, count only inactive tasks.
461+ search : str | None
462+ Optional search term to filter tasks.
463+
464+ Returns
465+ -------
466+ TaskCountResponse
467+ The count of tasks.
468+
469+ Raises
470+ ------
471+ HTTPException
472+ If the request is invalid or cannot be served.
473+ """
474+ if active_only and inactive_only :
475+ raise HTTPException (
476+ status_code = http_status .HTTP_400_BAD_REQUEST ,
477+ detail = "active_only and inactive_only cannot both be true" ,
478+ )
479+ try :
480+ async with db .session () as session :
481+ count = await TaskService .count_client_tasks (
482+ session ,
483+ client_id = client_id ,
484+ status = status ,
485+ active_only = active_only ,
486+ inactive_only = inactive_only ,
487+ search = search ,
488+ )
489+ return TaskCountResponse (count = count )
490+ except ValueError as error :
491+ # Service-level validation errors => 400
492+ raise HTTPException (
493+ status_code = http_status .HTTP_400_BAD_REQUEST ,
494+ detail = str (error ),
495+ ) from error
496+ except HTTPException :
497+ # If something below raises an HTTPException, preserve it
498+ raise
499+ except BaseException as error :
500+ LOG .error ("Error counting tasks: %s" , error )
501+ raise HTTPException (
502+ status_code = http_status .HTTP_500_INTERNAL_SERVER_ERROR ,
503+ detail = "Internal server error" ,
504+ ) from error
409505
410506
411507@task_router .get ("/tasks/{task_id}/" , include_in_schema = False )
@@ -451,7 +547,7 @@ async def get_task(
451547 task = await TaskService .get_task (session , task_id = task_id )
452548 if task is None or (not is_admin and task .client_id != client_id ):
453549 raise HTTPException (
454- status_code = status .HTTP_404_NOT_FOUND , detail = "Task not found"
550+ status_code = http_status .HTTP_404_NOT_FOUND , detail = "Task not found"
455551 )
456552 return TaskResponse .model_validate (task )
457553
@@ -559,17 +655,17 @@ async def on_input_request(
559655 task = await TaskService .get_task (session , task_id = task_id )
560656 except BaseException as e :
561657 raise HTTPException (
562- status_code = status .HTTP_404_NOT_FOUND ,
658+ status_code = http_status .HTTP_404_NOT_FOUND ,
563659 detail = f"Task { task_id } not found" ,
564660 ) from e
565661 if task is None or task .client_id != client_id :
566662 raise HTTPException (
567- status_code = status .HTTP_404_NOT_FOUND ,
663+ status_code = http_status .HTTP_404_NOT_FOUND ,
568664 detail = f"Task { task_id } not found" ,
569665 )
570666 if task .status != TaskStatus .WAITING_FOR_INPUT :
571667 raise HTTPException (
572- status_code = status .HTTP_400_BAD_REQUEST ,
668+ status_code = http_status .HTTP_400_BAD_REQUEST ,
573669 detail = "Invalid input request" ,
574670 )
575671 if message .request_id != task .input_request_id :
@@ -579,15 +675,15 @@ async def on_input_request(
579675 task .input_request_id ,
580676 )
581677 raise HTTPException (
582- status_code = status .HTTP_400_BAD_REQUEST ,
678+ status_code = http_status .HTTP_400_BAD_REQUEST ,
583679 detail = "Invalid input request" ,
584680 )
585681 background_tasks .add_task (
586682 publish_task_input_response ,
587683 task_id = task_id ,
588684 message = message ,
589685 )
590- return Response (status_code = status .HTTP_204_NO_CONTENT )
686+ return Response (status_code = http_status .HTTP_204_NO_CONTENT )
591687
592688
593689@task_router .get (
@@ -860,7 +956,7 @@ async def delete_tasks(
860956 if not ids :
861957 # Require specific task IDs to prevent accidental deletion of all tasks
862958 raise HTTPException (
863- status_code = status .HTTP_400_BAD_REQUEST ,
959+ status_code = http_status .HTTP_400_BAD_REQUEST ,
864960 detail = "Task IDs must be specified for deletion" ,
865961 )
866962
0 commit comments