django-cfg 1.5.1__py3-none-any.whl → 1.5.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of django-cfg might be problematic. Click here for more details.

Files changed (121) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/dashboard/TRANSACTION_FIX.md +73 -0
  3. django_cfg/apps/dashboard/serializers/__init__.py +0 -12
  4. django_cfg/apps/dashboard/serializers/activity.py +1 -1
  5. django_cfg/apps/dashboard/services/__init__.py +0 -2
  6. django_cfg/apps/dashboard/services/charts_service.py +4 -3
  7. django_cfg/apps/dashboard/services/statistics_service.py +11 -2
  8. django_cfg/apps/dashboard/services/system_health_service.py +64 -106
  9. django_cfg/apps/dashboard/urls.py +0 -2
  10. django_cfg/apps/dashboard/views/__init__.py +0 -2
  11. django_cfg/apps/dashboard/views/commands_views.py +3 -6
  12. django_cfg/apps/dashboard/views/overview_views.py +14 -13
  13. django_cfg/apps/knowbase/apps.py +2 -2
  14. django_cfg/apps/maintenance/admin/api_key_admin.py +2 -3
  15. django_cfg/apps/newsletter/admin/newsletter_admin.py +12 -11
  16. django_cfg/apps/rq/__init__.py +9 -0
  17. django_cfg/apps/rq/apps.py +80 -0
  18. django_cfg/apps/rq/management/__init__.py +1 -0
  19. django_cfg/apps/rq/management/commands/__init__.py +1 -0
  20. django_cfg/apps/rq/management/commands/rqscheduler.py +31 -0
  21. django_cfg/apps/rq/management/commands/rqstats.py +33 -0
  22. django_cfg/apps/rq/management/commands/rqworker.py +31 -0
  23. django_cfg/apps/rq/management/commands/rqworker_pool.py +27 -0
  24. django_cfg/apps/rq/serializers/__init__.py +40 -0
  25. django_cfg/apps/rq/serializers/health.py +60 -0
  26. django_cfg/apps/rq/serializers/job.py +100 -0
  27. django_cfg/apps/rq/serializers/queue.py +80 -0
  28. django_cfg/apps/rq/serializers/schedule.py +178 -0
  29. django_cfg/apps/rq/serializers/testing.py +139 -0
  30. django_cfg/apps/rq/serializers/worker.py +58 -0
  31. django_cfg/apps/rq/services/__init__.py +25 -0
  32. django_cfg/apps/rq/services/config_helper.py +233 -0
  33. django_cfg/apps/rq/services/models/README.md +417 -0
  34. django_cfg/apps/rq/services/models/__init__.py +30 -0
  35. django_cfg/apps/rq/services/models/event.py +123 -0
  36. django_cfg/apps/rq/services/models/job.py +99 -0
  37. django_cfg/apps/rq/services/models/queue.py +92 -0
  38. django_cfg/apps/rq/services/models/worker.py +104 -0
  39. django_cfg/apps/rq/services/rq_converters.py +183 -0
  40. django_cfg/apps/rq/tasks/__init__.py +23 -0
  41. django_cfg/apps/rq/tasks/demo_tasks.py +284 -0
  42. django_cfg/apps/rq/urls.py +54 -0
  43. django_cfg/apps/rq/views/__init__.py +19 -0
  44. django_cfg/apps/rq/views/jobs.py +882 -0
  45. django_cfg/apps/rq/views/monitoring.py +248 -0
  46. django_cfg/apps/rq/views/queues.py +261 -0
  47. django_cfg/apps/rq/views/schedule.py +400 -0
  48. django_cfg/apps/rq/views/testing.py +761 -0
  49. django_cfg/apps/rq/views/workers.py +195 -0
  50. django_cfg/apps/urls.py +6 -7
  51. django_cfg/core/base/config_model.py +10 -26
  52. django_cfg/core/builders/apps_builder.py +4 -11
  53. django_cfg/core/generation/integration_generators/__init__.py +3 -6
  54. django_cfg/core/generation/integration_generators/django_rq.py +80 -0
  55. django_cfg/core/generation/orchestrator.py +9 -19
  56. django_cfg/core/integration/display/startup.py +6 -20
  57. django_cfg/mixins/__init__.py +2 -0
  58. django_cfg/mixins/superadmin_api.py +59 -0
  59. django_cfg/models/__init__.py +3 -3
  60. django_cfg/models/django/__init__.py +3 -3
  61. django_cfg/models/django/django_rq.py +621 -0
  62. django_cfg/models/django/revolution_legacy.py +1 -1
  63. django_cfg/modules/base.py +4 -6
  64. django_cfg/modules/django_admin/config/background_task_config.py +4 -4
  65. django_cfg/modules/django_admin/utils/html/composition.py +9 -2
  66. django_cfg/modules/django_unfold/navigation.py +1 -26
  67. django_cfg/pyproject.toml +4 -4
  68. django_cfg/registry/core.py +4 -7
  69. django_cfg/static/frontend/admin.zip +0 -0
  70. django_cfg/templates/admin/constance/includes/results_list.html +73 -0
  71. django_cfg/templates/admin/index.html +187 -62
  72. django_cfg/templatetags/django_cfg.py +61 -1
  73. {django_cfg-1.5.1.dist-info → django_cfg-1.5.3.dist-info}/METADATA +5 -6
  74. {django_cfg-1.5.1.dist-info → django_cfg-1.5.3.dist-info}/RECORD +77 -82
  75. django_cfg/apps/dashboard/permissions.py +0 -48
  76. django_cfg/apps/dashboard/serializers/django_q2.py +0 -50
  77. django_cfg/apps/dashboard/services/django_q2_service.py +0 -159
  78. django_cfg/apps/dashboard/views/django_q2_views.py +0 -79
  79. django_cfg/apps/tasks/__init__.py +0 -64
  80. django_cfg/apps/tasks/admin/__init__.py +0 -4
  81. django_cfg/apps/tasks/admin/config.py +0 -98
  82. django_cfg/apps/tasks/admin/task_log.py +0 -238
  83. django_cfg/apps/tasks/apps.py +0 -15
  84. django_cfg/apps/tasks/filters/__init__.py +0 -10
  85. django_cfg/apps/tasks/filters/task_log.py +0 -121
  86. django_cfg/apps/tasks/migrations/0001_initial.py +0 -196
  87. django_cfg/apps/tasks/migrations/0002_delete_tasklog.py +0 -16
  88. django_cfg/apps/tasks/migrations/__init__.py +0 -0
  89. django_cfg/apps/tasks/models/__init__.py +0 -4
  90. django_cfg/apps/tasks/models/task_log.py +0 -246
  91. django_cfg/apps/tasks/serializers/__init__.py +0 -28
  92. django_cfg/apps/tasks/serializers/task_log.py +0 -249
  93. django_cfg/apps/tasks/services/__init__.py +0 -10
  94. django_cfg/apps/tasks/services/client/__init__.py +0 -7
  95. django_cfg/apps/tasks/services/client/client.py +0 -234
  96. django_cfg/apps/tasks/services/config_helper.py +0 -63
  97. django_cfg/apps/tasks/services/sync.py +0 -204
  98. django_cfg/apps/tasks/urls.py +0 -16
  99. django_cfg/apps/tasks/views/__init__.py +0 -10
  100. django_cfg/apps/tasks/views/task_log.py +0 -41
  101. django_cfg/apps/tasks/views/task_log_base.py +0 -41
  102. django_cfg/apps/tasks/views/task_log_overview.py +0 -100
  103. django_cfg/apps/tasks/views/task_log_related.py +0 -41
  104. django_cfg/apps/tasks/views/task_log_stats.py +0 -91
  105. django_cfg/apps/tasks/views/task_log_timeline.py +0 -81
  106. django_cfg/core/generation/integration_generators/django_q2.py +0 -133
  107. django_cfg/core/generation/integration_generators/tasks.py +0 -88
  108. django_cfg/models/django/django_q2.py +0 -514
  109. django_cfg/models/tasks/__init__.py +0 -49
  110. django_cfg/models/tasks/backends.py +0 -122
  111. django_cfg/models/tasks/config.py +0 -209
  112. django_cfg/models/tasks/utils.py +0 -162
  113. django_cfg/modules/django_q2/README.md +0 -140
  114. django_cfg/modules/django_q2/__init__.py +0 -8
  115. django_cfg/modules/django_q2/apps.py +0 -107
  116. django_cfg/modules/django_q2/management/__init__.py +0 -0
  117. django_cfg/modules/django_q2/management/commands/__init__.py +0 -0
  118. django_cfg/modules/django_q2/management/commands/sync_django_q_schedules.py +0 -74
  119. {django_cfg-1.5.1.dist-info → django_cfg-1.5.3.dist-info}/WHEEL +0 -0
  120. {django_cfg-1.5.1.dist-info → django_cfg-1.5.3.dist-info}/entry_points.txt +0 -0
  121. {django_cfg-1.5.1.dist-info → django_cfg-1.5.3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,248 @@
1
+ """
2
+ Django-RQ Monitoring ViewSet.
3
+
4
+ Provides REST API endpoints for RQ health checks and configuration.
5
+ """
6
+
7
+ from datetime import datetime
8
+
9
+ from django_cfg.mixins import AdminAPIMixin
10
+ from django_cfg.modules.django_logging import get_logger
11
+ from drf_spectacular.utils import extend_schema
12
+ from rest_framework import status, viewsets
13
+ from rest_framework.decorators import action
14
+ from rest_framework.response import Response
15
+
16
+ from ..serializers import HealthCheckSerializer, RQConfigSerializer
17
+
18
+ logger = get_logger("rq.monitoring")
19
+
20
+
21
+ class RQMonitorViewSet(AdminAPIMixin, viewsets.ViewSet):
22
+ """
23
+ ViewSet for RQ monitoring and health checks.
24
+
25
+ Provides endpoints for:
26
+ - Health check (cluster status)
27
+ - Configuration view
28
+ - Prometheus metrics
29
+
30
+ Requires admin authentication (JWT, Session, or Basic Auth).
31
+ """
32
+
33
+ @extend_schema(
34
+ tags=["RQ Monitoring"],
35
+ summary="Health check",
36
+ description="Returns RQ cluster health status including worker count and queue status.",
37
+ responses={
38
+ 200: HealthCheckSerializer,
39
+ },
40
+ )
41
+ @action(detail=False, methods=["get"], url_path="health")
42
+ def health(self, request):
43
+ """Health check endpoint."""
44
+ try:
45
+ import django_rq
46
+ from rq import Worker
47
+
48
+ # Check Redis connection
49
+ redis_connected = True
50
+ worker_count = 0
51
+ total_jobs = 0
52
+ queue_count = 0
53
+
54
+ try:
55
+ # Get all queues
56
+ from django.conf import settings
57
+
58
+ queue_names = settings.RQ_QUEUES.keys() if hasattr(settings, 'RQ_QUEUES') else []
59
+ queue_count = len(queue_names)
60
+
61
+ # Count workers per queue (use set to avoid duplicates)
62
+ worker_names = set()
63
+ for queue_name in queue_names:
64
+ try:
65
+ queue = django_rq.get_queue(queue_name)
66
+ # Get workers for this queue's connection
67
+ queue_workers = Worker.all(connection=queue.connection)
68
+ worker_names.update(w.name for w in queue_workers)
69
+ # Count jobs
70
+ total_jobs += queue.count
71
+ except Exception as e:
72
+ logger.debug(f"Failed to get queue {queue_name}: {e}")
73
+
74
+ worker_count = len(worker_names)
75
+
76
+ except Exception as e:
77
+ logger.error(f"Redis connection error: {e}", exc_info=True)
78
+ redis_connected = False
79
+
80
+ # Determine health status
81
+ if not redis_connected:
82
+ health_status = "unhealthy"
83
+ elif worker_count == 0:
84
+ health_status = "degraded"
85
+ else:
86
+ health_status = "healthy"
87
+
88
+ health_data = {
89
+ "status": health_status,
90
+ "worker_count": worker_count,
91
+ "queue_count": queue_count,
92
+ "total_jobs": total_jobs,
93
+ "timestamp": datetime.now().isoformat(),
94
+ "enabled": True,
95
+ "redis_connected": redis_connected,
96
+ "wrapper_url": "", # Empty for now, can be configured later
97
+ "has_api_key": False, # No API key required for RQ
98
+ }
99
+
100
+ serializer = HealthCheckSerializer(data=health_data)
101
+ serializer.is_valid(raise_exception=True)
102
+ return Response(serializer.data)
103
+
104
+ except Exception as e:
105
+ logger.error(f"Health check error: {e}", exc_info=True)
106
+ return Response(
107
+ {"error": "Internal server error"},
108
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
109
+ )
110
+
111
+ @extend_schema(
112
+ tags=["RQ Monitoring"],
113
+ summary="Get RQ configuration",
114
+ description="Returns current RQ configuration from django-cfg.",
115
+ responses={
116
+ 200: RQConfigSerializer,
117
+ },
118
+ )
119
+ @action(detail=False, methods=["get"], url_path="config")
120
+ def config(self, request):
121
+ """Get RQ configuration."""
122
+ try:
123
+ from django.conf import settings
124
+ from ..services import get_rq_config
125
+
126
+ # Get configuration
127
+ queues = {}
128
+ if hasattr(settings, 'RQ_QUEUES'):
129
+ # Sanitize queue config (hide passwords)
130
+ for name, config in settings.RQ_QUEUES.items():
131
+ queues[name] = {
132
+ k: v if k not in ('PASSWORD', 'password') else '***'
133
+ for k, v in config.items()
134
+ }
135
+
136
+ async_mode = True
137
+ show_admin_link = getattr(settings, 'RQ_SHOW_ADMIN_LINK', True)
138
+ api_token_configured = bool(getattr(settings, 'RQ_API_TOKEN', None))
139
+
140
+ # Check prometheus
141
+ prometheus_enabled = True
142
+ try:
143
+ import prometheus_client # noqa: F401
144
+ except ImportError:
145
+ prometheus_enabled = False
146
+
147
+ # Get scheduled tasks from django-cfg config
148
+ schedules = []
149
+ rq_config = get_rq_config()
150
+ if rq_config and hasattr(rq_config, 'schedules'):
151
+ for schedule in rq_config.schedules:
152
+ schedules.append({
153
+ "func": schedule.func,
154
+ "queue": schedule.queue,
155
+ "cron": schedule.cron,
156
+ "interval": schedule.interval,
157
+ "scheduled_time": schedule.scheduled_time,
158
+ "description": schedule.description,
159
+ "timeout": schedule.timeout,
160
+ "result_ttl": schedule.result_ttl,
161
+ "repeat": schedule.repeat,
162
+ })
163
+
164
+ config_data = {
165
+ "enabled": True,
166
+ "queues": queues,
167
+ "async_mode": async_mode,
168
+ "show_admin_link": show_admin_link,
169
+ "prometheus_enabled": prometheus_enabled,
170
+ "api_token_configured": api_token_configured,
171
+ "schedules": schedules,
172
+ }
173
+
174
+ serializer = RQConfigSerializer(data=config_data)
175
+ serializer.is_valid(raise_exception=True)
176
+ return Response(serializer.data)
177
+
178
+ except Exception as e:
179
+ logger.error(f"Config error: {e}", exc_info=True)
180
+ return Response(
181
+ {"error": "Internal server error"},
182
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
183
+ )
184
+
185
+ @extend_schema(
186
+ tags=["RQ Monitoring"],
187
+ summary="Prometheus metrics",
188
+ description="Returns Prometheus metrics for RQ queues and workers.",
189
+ responses={
190
+ 200: {"description": "Prometheus metrics in text format"},
191
+ },
192
+ )
193
+ @action(detail=False, methods=["get"], url_path="metrics")
194
+ def metrics(self, request):
195
+ """
196
+ Prometheus metrics endpoint.
197
+
198
+ Uses django-rq's built-in RQCollector for comprehensive metrics:
199
+ - rq_workers: Worker count by name, state, and queues
200
+ - rq_job_successful_total: Successful job count per worker
201
+ - rq_job_failed_total: Failed job count per worker
202
+ - rq_working_seconds_total: Total working time per worker
203
+ - rq_jobs: Job count by queue and status (queued, started, finished, failed, deferred, scheduled)
204
+ """
205
+ try:
206
+ # Try to import prometheus_client and RQCollector
207
+ try:
208
+ from prometheus_client import CollectorRegistry
209
+ from prometheus_client.exposition import choose_encoder
210
+ except ImportError:
211
+ return Response(
212
+ {"error": "prometheus_client not installed. Install with: pip install django-rq[prometheus]"},
213
+ status=status.HTTP_501_NOT_IMPLEMENTED,
214
+ )
215
+
216
+ try:
217
+ from django_rq.contrib.prometheus import RQCollector
218
+ except ImportError:
219
+ return Response(
220
+ {"error": "RQCollector not available. Ensure prometheus_client is installed."},
221
+ status=status.HTTP_501_NOT_IMPLEMENTED,
222
+ )
223
+
224
+ # Create registry and register RQCollector
225
+ registry = CollectorRegistry(auto_describe=True)
226
+ registry.register(RQCollector())
227
+
228
+ # Choose encoder based on Accept header
229
+ encoder, content_type = choose_encoder(request.META.get('HTTP_ACCEPT', ''))
230
+
231
+ # Support filtering by metric name (Prometheus query param)
232
+ if 'name[]' in request.GET:
233
+ registry = registry.restricted_registry(request.GET.getlist('name[]'))
234
+
235
+ # Generate and return metrics
236
+ metrics_output = encoder(registry)
237
+
238
+ return Response(
239
+ metrics_output,
240
+ content_type=content_type,
241
+ )
242
+
243
+ except Exception as e:
244
+ logger.error(f"Metrics error: {e}", exc_info=True)
245
+ return Response(
246
+ {"error": "Internal server error"},
247
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
248
+ )
@@ -0,0 +1,261 @@
1
+ """
2
+ Django-RQ Queue Management ViewSet.
3
+
4
+ Provides REST API endpoints for managing RQ queues.
5
+ """
6
+
7
+ from django_cfg.mixins import AdminAPIMixin
8
+ from django_cfg.modules.django_logging import get_logger
9
+ from drf_spectacular.types import OpenApiTypes
10
+ from drf_spectacular.utils import OpenApiParameter, extend_schema
11
+ from rest_framework import status, viewsets
12
+ from rest_framework.decorators import action
13
+ from rest_framework.response import Response
14
+
15
+ from ..serializers import QueueStatsSerializer, QueueDetailSerializer
16
+ from ..services import queue_to_model
17
+
18
+ logger = get_logger("rq.queues")
19
+
20
+
21
+ class QueueViewSet(AdminAPIMixin, viewsets.ViewSet):
22
+ """
23
+ ViewSet for RQ queue management.
24
+
25
+ Provides endpoints for:
26
+ - Listing all queues with statistics
27
+ - Getting detailed queue information
28
+ - Emptying queues
29
+ - Getting queue job lists
30
+
31
+ Requires admin authentication (JWT, Session, or Basic Auth).
32
+ """
33
+
34
+ @extend_schema(
35
+ tags=["RQ Queues"],
36
+ summary="List all queues",
37
+ description="Returns list of all configured RQ queues with statistics. Supports filtering by queue name.",
38
+ parameters=[
39
+ OpenApiParameter(
40
+ name="name",
41
+ type=str,
42
+ location=OpenApiParameter.QUERY,
43
+ required=False,
44
+ description="Filter by queue name (exact match or substring)",
45
+ ),
46
+ ],
47
+ responses={
48
+ 200: QueueStatsSerializer(many=True),
49
+ },
50
+ )
51
+ def list(self, request):
52
+ """List all queues with statistics and optional filtering."""
53
+ try:
54
+ import django_rq
55
+ from django.conf import settings
56
+
57
+ if not hasattr(settings, 'RQ_QUEUES'):
58
+ return Response([])
59
+
60
+ # Get query params for filtering
61
+ name_filter = request.query_params.get('name')
62
+
63
+ queues_data = []
64
+
65
+ for queue_name in settings.RQ_QUEUES.keys():
66
+ try:
67
+ # Apply name filter
68
+ if name_filter and name_filter.lower() not in queue_name.lower():
69
+ continue
70
+
71
+ queue = django_rq.get_queue(queue_name)
72
+
73
+ # Convert RQ Queue to Pydantic model
74
+ queue_model = queue_to_model(queue, queue_name)
75
+
76
+ # Convert to dict for DRF serializer (only stats fields)
77
+ queue_dict = {
78
+ "name": queue_model.name,
79
+ "count": queue_model.count,
80
+ "queued_jobs": queue_model.queued_jobs,
81
+ "started_jobs": queue_model.started_jobs,
82
+ "finished_jobs": queue_model.finished_jobs,
83
+ "failed_jobs": queue_model.failed_jobs,
84
+ "deferred_jobs": queue_model.deferred_jobs,
85
+ "scheduled_jobs": queue_model.scheduled_jobs,
86
+ "workers": queue_model.workers,
87
+ }
88
+
89
+ serializer = QueueStatsSerializer(data=queue_dict)
90
+ serializer.is_valid(raise_exception=True)
91
+ queues_data.append(serializer.data)
92
+
93
+ except Exception as e:
94
+ logger.error(f"Failed to get queue {queue_name}: {e}", exc_info=True)
95
+ continue
96
+
97
+ return Response(queues_data)
98
+
99
+ except Exception as e:
100
+ logger.error(f"Queue list error: {e}", exc_info=True)
101
+ return Response(
102
+ {"error": "Internal server error"},
103
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
104
+ )
105
+
106
+ @extend_schema(
107
+ tags=["RQ Queues"],
108
+ summary="Get queue details",
109
+ description="Returns detailed information about a specific queue.",
110
+ responses={
111
+ 200: QueueDetailSerializer,
112
+ 404: {"description": "Queue not found"},
113
+ },
114
+ )
115
+ def retrieve(self, request, pk=None):
116
+ """Get detailed queue information."""
117
+ try:
118
+ import django_rq
119
+
120
+ try:
121
+ queue = django_rq.get_queue(pk)
122
+ except Exception:
123
+ return Response(
124
+ {"error": f"Queue {pk} not found"},
125
+ status=status.HTTP_404_NOT_FOUND,
126
+ )
127
+
128
+ # Convert RQ Queue to Pydantic model
129
+ queue_model = queue_to_model(queue, pk)
130
+
131
+ # Convert to dict for DRF serializer
132
+ queue_dict = {
133
+ "name": queue_model.name,
134
+ "count": queue_model.count,
135
+ "queued_jobs": queue_model.queued_jobs,
136
+ "started_jobs": queue_model.started_jobs,
137
+ "finished_jobs": queue_model.finished_jobs,
138
+ "failed_jobs": queue_model.failed_jobs,
139
+ "deferred_jobs": queue_model.deferred_jobs,
140
+ "scheduled_jobs": queue_model.scheduled_jobs,
141
+ "workers": queue_model.workers,
142
+ "oldest_job_timestamp": queue_model.oldest_job_timestamp,
143
+ "connection_kwargs": {
144
+ 'host': queue_model.connection_host,
145
+ 'port': queue_model.connection_port,
146
+ 'db': queue_model.connection_db,
147
+ },
148
+ "is_async": queue_model.is_async,
149
+ }
150
+
151
+ serializer = QueueDetailSerializer(data=queue_dict)
152
+ serializer.is_valid(raise_exception=True)
153
+ return Response(serializer.data)
154
+
155
+ except Exception as e:
156
+ logger.error(f"Queue detail error: {e}", exc_info=True)
157
+ return Response(
158
+ {"error": "Internal server error"},
159
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
160
+ )
161
+
162
+ @extend_schema(
163
+ tags=["RQ Queues"],
164
+ summary="Empty queue",
165
+ description="Removes all jobs from the specified queue.",
166
+ responses={
167
+ 200: {"description": "Queue emptied successfully"},
168
+ 404: {"description": "Queue not found"},
169
+ },
170
+ )
171
+ @action(detail=True, methods=["post"], url_path="empty")
172
+ def empty(self, request, pk=None):
173
+ """Empty queue (remove all jobs)."""
174
+ try:
175
+ import django_rq
176
+
177
+ try:
178
+ queue = django_rq.get_queue(pk)
179
+ except Exception:
180
+ return Response(
181
+ {"error": f"Queue {pk} not found"},
182
+ status=status.HTTP_404_NOT_FOUND,
183
+ )
184
+
185
+ job_count = queue.count
186
+ queue.empty()
187
+
188
+ return Response({
189
+ "success": True,
190
+ "message": f"Emptied queue '{pk}', removed {job_count} jobs",
191
+ "queue": pk,
192
+ "removed_jobs": job_count,
193
+ })
194
+
195
+ except Exception as e:
196
+ logger.error(f"Queue empty error: {e}", exc_info=True)
197
+ return Response(
198
+ {"error": "Internal server error"},
199
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
200
+ )
201
+
202
+ @extend_schema(
203
+ tags=["RQ Queues"],
204
+ summary="Get queue jobs",
205
+ description="Returns list of job IDs in the queue.",
206
+ parameters=[
207
+ OpenApiParameter(
208
+ name="limit",
209
+ type=OpenApiTypes.INT,
210
+ location=OpenApiParameter.QUERY,
211
+ description="Number of jobs to return (default: 100)",
212
+ required=False,
213
+ ),
214
+ OpenApiParameter(
215
+ name="offset",
216
+ type=OpenApiTypes.INT,
217
+ location=OpenApiParameter.QUERY,
218
+ description="Offset for pagination (default: 0)",
219
+ required=False,
220
+ ),
221
+ ],
222
+ responses={
223
+ 200: {"description": "List of job IDs"},
224
+ 404: {"description": "Queue not found"},
225
+ },
226
+ )
227
+ @action(detail=True, methods=["get"], url_path="jobs")
228
+ def jobs(self, request, pk=None):
229
+ """Get list of jobs in queue."""
230
+ try:
231
+ import django_rq
232
+
233
+ try:
234
+ queue = django_rq.get_queue(pk)
235
+ except Exception:
236
+ return Response(
237
+ {"error": f"Queue {pk} not found"},
238
+ status=status.HTTP_404_NOT_FOUND,
239
+ )
240
+
241
+ # Pagination
242
+ limit = min(int(request.query_params.get("limit", 100)), 500)
243
+ offset = int(request.query_params.get("offset", 0))
244
+
245
+ # Get job IDs
246
+ job_ids = queue.get_job_ids(offset, offset + limit)
247
+
248
+ return Response({
249
+ "queue": pk,
250
+ "total_jobs": queue.count,
251
+ "limit": limit,
252
+ "offset": offset,
253
+ "job_ids": job_ids,
254
+ })
255
+
256
+ except Exception as e:
257
+ logger.error(f"Queue jobs error: {e}", exc_info=True)
258
+ return Response(
259
+ {"error": "Internal server error"},
260
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
261
+ )