Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /swf-monitor/src/monitor_app/panda/api.py was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 """
0002 PanDA REST API endpoints — thin JSON wrappers over monitor_app.panda.queries.
0003 
0004 Under /swf-monitor/api/panda/. Read-only. Intended for external consumers that
0005 need structured PanDA data without the MCP session/streaming protocol overhead
0006 (alarm engines, cron tools, dashboards).
0007 
0008 Response shape matches the MCP tool responses for consistency: items / total_count
0009 / has_more / next_before_id / monitor_urls where applicable. Stability promise:
0010 field names don't change silently; breaking changes would rename the endpoint.
0011 """
0012 from rest_framework.authentication import SessionAuthentication, TokenAuthentication
0013 from rest_framework.decorators import api_view, authentication_classes, permission_classes
0014 from rest_framework.permissions import IsAuthenticated
0015 from rest_framework.response import Response
0016 from rest_framework import status as http_status
0017 
0018 from monitor_app.middleware import TunnelAuthentication
0019 
0020 from . import queries
0021 
0022 
0023 _AUTH = [TunnelAuthentication, SessionAuthentication, TokenAuthentication]
0024 
0025 
0026 def _int_param(request, name, default=None, min_value=None, max_value=None):
0027     """Parse an integer query param with bounds; return (value, error_response)."""
0028     raw = request.query_params.get(name)
0029     if raw is None:
0030         return default, None
0031     try:
0032         val = int(raw)
0033     except (TypeError, ValueError):
0034         return None, Response(
0035             {'error': f'{name} must be an integer'},
0036             status=http_status.HTTP_400_BAD_REQUEST,
0037         )
0038     if min_value is not None and val < min_value:
0039         return None, Response(
0040             {'error': f'{name} must be >= {min_value}'},
0041             status=http_status.HTTP_400_BAD_REQUEST,
0042         )
0043     if max_value is not None and val > max_value:
0044         return None, Response(
0045             {'error': f'{name} must be <= {max_value}'},
0046             status=http_status.HTTP_400_BAD_REQUEST,
0047         )
0048     return val, None
0049 
0050 
0051 @api_view(['GET'])
0052 @authentication_classes(_AUTH)
0053 @permission_classes([IsAuthenticated])
0054 def tasks_list(request):
0055     """GET /api/panda/tasks/ — list JEDI tasks with per-task job counts.
0056 
0057     Query params:
0058         days (int, default 7)           — modificationtime window
0059         status (str)                    — task status filter
0060         username (str, supports %)      — task owner
0061         taskname (str, supports %)      — task name
0062         reqid (int)
0063         workinggroup (str)              — e.g. EIC
0064         processingtype (str, supports %)
0065         limit (int, default 50, max 200)
0066         before_id (int)                 — cursor
0067 
0068     Returns:
0069         { items, total_count, has_more, next_before_id, summary, filters }
0070         Each task includes native JEDI fields (failurerate, progress, ...)
0071         plus nactive / nfinished / nfailed / nrunning / nretries /
0072         nfinalfailed aggregated from job tables, and computed helpers
0073         computed_failurerate (all failures) / computed_finalfailurerate
0074         (retry-exhausted failures only, used by alarms).
0075     """
0076     limit, err = _int_param(request, 'limit', default=50, min_value=1, max_value=200)
0077     if err:
0078         return err
0079     days, err = _int_param(request, 'days', default=7, min_value=1)
0080     if err:
0081         return err
0082     reqid, err = _int_param(request, 'reqid')
0083     if err:
0084         return err
0085     before_id, err = _int_param(request, 'before_id')
0086     if err:
0087         return err
0088 
0089     result = queries.list_tasks(
0090         days=days,
0091         status=request.query_params.get('status'),
0092         username=request.query_params.get('username'),
0093         taskname=request.query_params.get('taskname'),
0094         reqid=reqid,
0095         workinggroup=request.query_params.get('workinggroup'),
0096         processingtype=request.query_params.get('processingtype'),
0097         limit=limit,
0098         before_id=before_id,
0099     )
0100     if 'error' in result:
0101         return Response(result, status=http_status.HTTP_500_INTERNAL_SERVER_ERROR)
0102 
0103     # Reshape to match MCP tool response conventions.
0104     return Response({
0105         'items': result['tasks'],
0106         'total_count': result['total_in_window'],
0107         'has_more': result['pagination']['has_more'],
0108         'next_before_id': result['pagination']['next_before_id'],
0109         'summary': result['summary'],
0110         'filters': result['filters'],
0111     })
0112 
0113 
0114 @api_view(['GET'])
0115 @authentication_classes(_AUTH)
0116 @permission_classes([IsAuthenticated])
0117 def task_detail(request, jeditaskid):
0118     """GET /api/panda/tasks/<jeditaskid>/ — one task with per-task job counts."""
0119     task = queries.get_task(jeditaskid)
0120     if 'error' in task:
0121         if 'not found' in task['error']:
0122             return Response(task, status=http_status.HTTP_404_NOT_FOUND)
0123         return Response(task, status=http_status.HTTP_500_INTERNAL_SERVER_ERROR)
0124     return Response(task)
0125 
0126 
0127 @api_view(['GET'])
0128 @authentication_classes(_AUTH)
0129 @permission_classes([IsAuthenticated])
0130 def activity(request):
0131     """GET /api/panda/activity/ — aggregate counts by task and job status.
0132 
0133     Query params:
0134         days (int, default 1)
0135         username (str, supports %)
0136         site (str, supports %)
0137         workinggroup (str)
0138     """
0139     days, err = _int_param(request, 'days', default=1, min_value=1)
0140     if err:
0141         return err
0142     result = queries.get_activity(
0143         days=days,
0144         username=request.query_params.get('username'),
0145         site=request.query_params.get('site'),
0146         workinggroup=request.query_params.get('workinggroup'),
0147     )
0148     if 'error' in result:
0149         return Response(result, status=http_status.HTTP_500_INTERNAL_SERVER_ERROR)
0150     return Response(result)