django-cfg 1.4.106__py3-none-any.whl → 1.4.108__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 (137) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/views/profile.py +19 -9
  3. django_cfg/apps/centrifugo/views/admin_api.py +4 -7
  4. django_cfg/apps/centrifugo/views/monitoring.py +3 -6
  5. django_cfg/apps/centrifugo/views/testing_api.py +3 -6
  6. django_cfg/apps/dashboard/services/system_health_service.py +16 -11
  7. django_cfg/apps/dashboard/views/activity_views.py +3 -5
  8. django_cfg/apps/dashboard/views/apizones_views.py +4 -5
  9. django_cfg/apps/dashboard/views/charts_views.py +4 -5
  10. django_cfg/apps/dashboard/views/overview_views.py +4 -5
  11. django_cfg/apps/dashboard/views/statistics_views.py +4 -5
  12. django_cfg/apps/dashboard/views/system_views.py +4 -5
  13. django_cfg/apps/knowbase/__init__.py +2 -2
  14. django_cfg/apps/knowbase/apps.py +2 -8
  15. django_cfg/apps/knowbase/views/base.py +9 -4
  16. django_cfg/apps/support/views/api.py +16 -7
  17. django_cfg/apps/tasks/__init__.py +61 -2
  18. django_cfg/apps/tasks/admin/__init__.py +3 -10
  19. django_cfg/apps/tasks/admin/config.py +98 -0
  20. django_cfg/apps/tasks/admin/task_log.py +265 -0
  21. django_cfg/apps/tasks/apps.py +7 -9
  22. django_cfg/apps/tasks/filters/__init__.py +10 -0
  23. django_cfg/apps/tasks/filters/task_log.py +121 -0
  24. django_cfg/apps/tasks/migrations/0001_initial.py +196 -0
  25. django_cfg/apps/tasks/models/__init__.py +4 -0
  26. django_cfg/apps/tasks/models/task_log.py +246 -0
  27. django_cfg/apps/tasks/serializers/__init__.py +28 -0
  28. django_cfg/apps/tasks/serializers/task_log.py +249 -0
  29. django_cfg/apps/tasks/services/__init__.py +10 -0
  30. django_cfg/apps/tasks/services/client/__init__.py +7 -0
  31. django_cfg/apps/tasks/services/client/client.py +234 -0
  32. django_cfg/apps/tasks/services/config_helper.py +63 -0
  33. django_cfg/apps/tasks/services/sync.py +204 -0
  34. django_cfg/apps/tasks/urls.py +7 -13
  35. django_cfg/apps/tasks/views/__init__.py +4 -10
  36. django_cfg/apps/tasks/views/task_log.py +41 -0
  37. django_cfg/apps/tasks/views/task_log_base.py +41 -0
  38. django_cfg/apps/tasks/views/task_log_overview.py +100 -0
  39. django_cfg/apps/tasks/views/task_log_related.py +41 -0
  40. django_cfg/apps/tasks/views/task_log_stats.py +91 -0
  41. django_cfg/apps/tasks/views/task_log_timeline.py +81 -0
  42. django_cfg/apps/urls.py +0 -1
  43. django_cfg/cli/commands/info.py +1 -1
  44. django_cfg/cli/utils.py +1 -1
  45. django_cfg/core/base/config_model.py +1 -1
  46. django_cfg/core/builders/apps_builder.py +1 -1
  47. django_cfg/core/generation/integration_generators/__init__.py +1 -1
  48. django_cfg/core/generation/integration_generators/tasks.py +14 -18
  49. django_cfg/core/generation/security_generators/crypto_fields.py +2 -1
  50. django_cfg/core/integration/display/startup.py +1 -1
  51. django_cfg/mixins/__init__.py +12 -0
  52. django_cfg/mixins/admin_api.py +37 -0
  53. django_cfg/mixins/client_api.py +39 -0
  54. django_cfg/models/django/constance.py +2 -8
  55. django_cfg/models/django/crypto_fields.py +13 -48
  56. django_cfg/models/tasks/__init__.py +8 -10
  57. django_cfg/models/tasks/backends.py +76 -207
  58. django_cfg/models/tasks/config.py +20 -127
  59. django_cfg/models/tasks/utils.py +17 -29
  60. django_cfg/modules/django_admin/RESOURCE_CONFIG_ENHANCEMENT.md +350 -0
  61. django_cfg/modules/django_admin/__init__.py +4 -0
  62. django_cfg/modules/django_admin/base/pydantic_admin.py +70 -15
  63. django_cfg/modules/django_admin/config/__init__.py +4 -0
  64. django_cfg/modules/django_admin/config/admin_config.py +13 -1
  65. django_cfg/modules/django_admin/config/background_task_config.py +76 -0
  66. django_cfg/modules/django_admin/config/resource_config.py +129 -0
  67. django_cfg/modules/django_client/management/commands/generate_client.py +13 -1
  68. django_cfg/modules/django_unfold/navigation.py +121 -22
  69. django_cfg/pyproject.toml +2 -2
  70. django_cfg/registry/core.py +1 -1
  71. django_cfg/static/frontend/admin.zip +0 -0
  72. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/METADATA +5 -3
  73. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/RECORD +77 -111
  74. django_cfg/apps/tasks/admin/actions.py +0 -29
  75. django_cfg/apps/tasks/admin/tasks_admin.py +0 -154
  76. django_cfg/apps/tasks/api/serializers.py +0 -82
  77. django_cfg/apps/tasks/api/views.py +0 -571
  78. django_cfg/apps/tasks/serializers.py +0 -82
  79. django_cfg/apps/tasks/static/tasks/css/dashboard-alpine.css +0 -299
  80. django_cfg/apps/tasks/static/tasks/css/dashboard.css +0 -120
  81. django_cfg/apps/tasks/static/tasks/js/alpine/README.md +0 -47
  82. django_cfg/apps/tasks/static/tasks/js/alpine/actions/index.js +0 -8
  83. django_cfg/apps/tasks/static/tasks/js/alpine/actions/management.js +0 -123
  84. django_cfg/apps/tasks/static/tasks/js/alpine/actions/pagination.js +0 -21
  85. django_cfg/apps/tasks/static/tasks/js/alpine/actions/tasks.js +0 -101
  86. django_cfg/apps/tasks/static/tasks/js/alpine/actions/workers.js +0 -59
  87. django_cfg/apps/tasks/static/tasks/js/alpine/computed.js +0 -35
  88. django_cfg/apps/tasks/static/tasks/js/alpine/index.js +0 -148
  89. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/index.js +0 -36
  90. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/overview.js +0 -37
  91. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/queues.js +0 -27
  92. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/tasks.js +0 -32
  93. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/workers.js +0 -21
  94. django_cfg/apps/tasks/static/tasks/js/alpine/state.js +0 -36
  95. django_cfg/apps/tasks/static/tasks/js/alpine/utils/formatters.js +0 -42
  96. django_cfg/apps/tasks/static/tasks/js/alpine/utils/helpers.js +0 -68
  97. django_cfg/apps/tasks/static/tasks/js/dashboard-alpine.js +0 -725
  98. django_cfg/apps/tasks/tasks/__init__.py +0 -10
  99. django_cfg/apps/tasks/tasks/demo_tasks.py +0 -127
  100. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +0 -71
  101. django_cfg/apps/tasks/templates/tasks/components/overview_content.html +0 -94
  102. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +0 -44
  103. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +0 -45
  104. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -151
  105. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +0 -61
  106. django_cfg/apps/tasks/templates/tasks/components/tasks_mjs_integration.html +0 -269
  107. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +0 -60
  108. django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -20
  109. django_cfg/apps/tasks/templates/tasks/pages/dashboard-improved.html +0 -168
  110. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +0 -77
  111. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +0 -40
  112. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +0 -40
  113. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +0 -86
  114. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +0 -90
  115. django_cfg/apps/tasks/urls_admin.py +0 -15
  116. django_cfg/apps/tasks/utils/__init__.py +0 -1
  117. django_cfg/apps/tasks/utils/simulator.py +0 -353
  118. django_cfg/apps/tasks/views/api.py +0 -571
  119. django_cfg/apps/tasks/views/dashboard.py +0 -89
  120. django_cfg/management/commands/rundramatiq.py +0 -24
  121. django_cfg/management/commands/rundramatiq_simulator.py +0 -22
  122. django_cfg/management/commands/task_clear.py +0 -25
  123. django_cfg/management/commands/task_status.py +0 -24
  124. django_cfg/modules/django_tasks/__init__.py +0 -29
  125. django_cfg/modules/django_tasks/dramatiq_setup.py +0 -20
  126. django_cfg/modules/django_tasks/factory.py +0 -127
  127. django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
  128. django_cfg/modules/django_tasks/management/commands/rundramatiq.py +0 -253
  129. django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +0 -436
  130. django_cfg/modules/django_tasks/management/commands/task_clear.py +0 -226
  131. django_cfg/modules/django_tasks/management/commands/task_status.py +0 -257
  132. django_cfg/modules/django_tasks/service.py +0 -281
  133. django_cfg/modules/django_tasks/settings.py +0 -107
  134. /django_cfg/{modules/django_tasks/management → apps/tasks/migrations}/__init__.py +0 -0
  135. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/WHEEL +0 -0
  136. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/entry_points.txt +0 -0
  137. {django_cfg-1.4.106.dist-info → django_cfg-1.4.108.dist-info}/licenses/LICENSE +0 -0
@@ -1,82 +0,0 @@
1
- """
2
- Serializers for Django CFG Tasks app.
3
-
4
- Provides DRF serializers for task management API endpoints.
5
- """
6
-
7
-
8
- from rest_framework import serializers
9
-
10
-
11
- class QueueStatusSerializer(serializers.Serializer):
12
- """Serializer for queue status data."""
13
-
14
- queues = serializers.DictField(
15
- child=serializers.DictField(
16
- child=serializers.IntegerField()
17
- ),
18
- help_text="Queue information with pending/failed counts"
19
- )
20
- workers = serializers.IntegerField(help_text="Number of active workers")
21
- redis_connected = serializers.BooleanField(help_text="Redis connection status")
22
- timestamp = serializers.CharField(help_text="Current timestamp")
23
- error = serializers.CharField(required=False, help_text="Error message if any")
24
-
25
-
26
- class TaskStatisticsSerializer(serializers.Serializer):
27
- """Serializer for task statistics data."""
28
-
29
- statistics = serializers.DictField(
30
- child=serializers.IntegerField(),
31
- help_text="Task count statistics"
32
- )
33
- recent_tasks = serializers.ListField(
34
- child=serializers.DictField(),
35
- help_text="List of recent tasks"
36
- )
37
- timestamp = serializers.CharField(help_text="Current timestamp")
38
- error = serializers.CharField(required=False, help_text="Error message if any")
39
-
40
-
41
- class WorkerActionSerializer(serializers.Serializer):
42
- """Serializer for worker management actions."""
43
-
44
- action = serializers.ChoiceField(
45
- choices=['start', 'stop', 'restart'],
46
- help_text="Action to perform on workers"
47
- )
48
- processes = serializers.IntegerField(
49
- default=1,
50
- min_value=1,
51
- max_value=10,
52
- help_text="Number of worker processes"
53
- )
54
- threads = serializers.IntegerField(
55
- default=2,
56
- min_value=1,
57
- max_value=20,
58
- help_text="Number of threads per process"
59
- )
60
-
61
-
62
- class QueueActionSerializer(serializers.Serializer):
63
- """Serializer for queue management actions."""
64
-
65
- action = serializers.ChoiceField(
66
- choices=['clear', 'clear_all', 'purge', 'purge_failed', 'flush'],
67
- help_text="Action to perform on queues"
68
- )
69
- queue_names = serializers.ListField(
70
- child=serializers.CharField(),
71
- required=False,
72
- help_text="Specific queues to target (empty = all queues)"
73
- )
74
-
75
-
76
- class APIResponseSerializer(serializers.Serializer):
77
- """Standard API response serializer."""
78
-
79
- success = serializers.BooleanField(help_text="Operation success status")
80
- message = serializers.CharField(required=False, help_text="Success message")
81
- error = serializers.CharField(required=False, help_text="Error message")
82
- data = serializers.DictField(required=False, help_text="Response data")
@@ -1,571 +0,0 @@
1
- """
2
- API Views for Django CFG Tasks app.
3
-
4
- Provides DRF ViewSets for task management with nested router structure.
5
- """
6
-
7
- import logging
8
- from typing import Any, Dict
9
-
10
- from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
11
- from rest_framework import status, viewsets
12
- from rest_framework.authentication import BasicAuthentication, SessionAuthentication
13
- from rest_framework.decorators import action
14
- from rest_framework.permissions import IsAdminUser
15
- from rest_framework.response import Response
16
-
17
- from django_cfg.modules.django_tasks import DjangoTasks
18
-
19
- from .serializers import (
20
- APIResponseSerializer,
21
- QueueActionSerializer,
22
- QueueStatusSerializer,
23
- TaskStatisticsSerializer,
24
- WorkerActionSerializer,
25
- )
26
-
27
- logger = logging.getLogger(__name__)
28
-
29
-
30
- class TaskManagementViewSet(viewsets.GenericViewSet):
31
- """
32
- Main ViewSet for comprehensive task management.
33
-
34
- Provides all task-related operations in a single ViewSet with nested actions.
35
- """
36
-
37
- authentication_classes = [SessionAuthentication, BasicAuthentication]
38
- permission_classes = [IsAdminUser]
39
- serializer_class = APIResponseSerializer # Default serializer for the viewset
40
-
41
- def get_serializer_class(self):
42
- """Return the appropriate serializer class based on the action."""
43
- if self.action == 'queue_status':
44
- return QueueStatusSerializer
45
- elif self.action == 'queue_manage':
46
- return QueueActionSerializer
47
- elif self.action == 'worker_manage':
48
- return WorkerActionSerializer
49
- elif self.action == 'task_statistics':
50
- return TaskStatisticsSerializer
51
- return APIResponseSerializer
52
-
53
- @action(detail=False, methods=['get'], url_path='queues/status')
54
- @extend_schema(
55
- summary="Get queue status",
56
- description="Retrieve current status of all task queues including pending and failed task counts",
57
- responses={
58
- 200: OpenApiResponse(response=QueueStatusSerializer, description="Queue status retrieved successfully"),
59
- 500: OpenApiResponse(response=APIResponseSerializer, description="Internal server error")
60
- },
61
- tags=["Queue Management"]
62
- )
63
- def queue_status(self, request):
64
- """Get current status of all queues."""
65
- try:
66
- from ..utils.simulator import TaskSimulator
67
- simulator = TaskSimulator()
68
- status_data = simulator.get_current_queue_status()
69
-
70
- return Response({
71
- 'success': True,
72
- 'data': status_data
73
- })
74
-
75
- except Exception as e:
76
- logger.error(f"Queue status API error: {e}")
77
- return Response({
78
- 'success': False,
79
- 'error': str(e)
80
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
81
-
82
- @action(detail=False, methods=['post'], url_path='queues/manage')
83
- @extend_schema(
84
- summary="Manage queues",
85
- description="Perform management operations on queues (clear, purge, etc.)",
86
- request=QueueActionSerializer,
87
- responses={
88
- 200: OpenApiResponse(response=APIResponseSerializer, description="Queue operation completed successfully"),
89
- 400: OpenApiResponse(response=APIResponseSerializer, description="Invalid request data"),
90
- 500: OpenApiResponse(response=APIResponseSerializer, description="Internal server error")
91
- },
92
- tags=["Queue Management"]
93
- )
94
- def queue_manage(self, request):
95
- """Manage queue operations (clear, purge, etc.)."""
96
- try:
97
- serializer = QueueActionSerializer(data=request.data)
98
- if not serializer.is_valid():
99
- return Response({
100
- 'success': False,
101
- 'error': 'Invalid request data',
102
- 'details': serializer.errors
103
- }, status=status.HTTP_400_BAD_REQUEST)
104
-
105
- action_type = serializer.validated_data['action']
106
- queue_name = serializer.validated_data.get('queue_name')
107
-
108
- tasks_service = DjangoTasks()
109
-
110
- if action_type == 'clear_all':
111
- result = self._clear_all_queues(tasks_service)
112
- elif action_type == 'clear_queue' and queue_name:
113
- result = self._clear_queue(tasks_service, queue_name)
114
- elif action_type == 'purge_failed':
115
- result = self._purge_failed_tasks(tasks_service, queue_name)
116
- else:
117
- return Response({
118
- 'success': False,
119
- 'error': 'Invalid action or missing queue_name'
120
- }, status=status.HTTP_400_BAD_REQUEST)
121
-
122
- return Response({
123
- 'success': True,
124
- 'data': result
125
- })
126
-
127
- except Exception as e:
128
- logger.error(f"Queue management API error: {e}")
129
- return Response({
130
- 'success': False,
131
- 'error': str(e)
132
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
133
-
134
- @action(detail=False, methods=['get'], url_path='tasks/stats')
135
- @extend_schema(
136
- summary="Get task statistics",
137
- description="Retrieve comprehensive task execution statistics and recent task history",
138
- responses={
139
- 200: OpenApiResponse(response=TaskStatisticsSerializer, description="Task statistics retrieved successfully"),
140
- 500: OpenApiResponse(response=APIResponseSerializer, description="Internal server error")
141
- },
142
- tags=["Task Management"]
143
- )
144
- def task_statistics(self, request):
145
- """Get task execution statistics."""
146
- try:
147
- from ..utils.simulator import TaskSimulator
148
- simulator = TaskSimulator()
149
- stats_data = simulator.get_current_task_statistics()
150
-
151
- return Response({
152
- 'success': True,
153
- 'data': stats_data
154
- })
155
-
156
- except Exception as e:
157
- logger.error(f"Task statistics API error: {e}")
158
- return Response({
159
- 'success': False,
160
- 'error': str(e)
161
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
162
-
163
- @action(detail=False, methods=['get'], url_path='tasks/list')
164
- @extend_schema(
165
- summary="Get task list",
166
- description="Retrieve paginated list of tasks with optional filtering",
167
- parameters=[
168
- OpenApiParameter(name='status', description='Filter by task status', required=False, type=str),
169
- OpenApiParameter(name='actor', description='Filter by actor name', required=False, type=str),
170
- OpenApiParameter(name='page', description='Page number', required=False, type=int),
171
- OpenApiParameter(name='page_size', description='Items per page', required=False, type=int),
172
- ],
173
- responses={
174
- 200: OpenApiResponse(response=APIResponseSerializer, description="Task list retrieved successfully"),
175
- 500: OpenApiResponse(response=APIResponseSerializer, description="Internal server error")
176
- },
177
- tags=["Task Management"]
178
- )
179
- def task_list(self, request):
180
- """Get paginated task list with filtering."""
181
- try:
182
- from ..utils.simulator import TaskSimulator
183
- simulator = TaskSimulator()
184
-
185
- # For now, return mock task data since we don't have real task list in simulator
186
- # This could be enhanced later to support real task data
187
- mock_data = {
188
- 'tasks': [
189
- {
190
- 'id': 1,
191
- 'actor_name': 'send_email_task',
192
- 'status': 'completed',
193
- 'queue_name': 'default',
194
- 'created_at': '2025-09-29T12:30:00Z',
195
- 'updated_at': '2025-09-29T12:30:05Z',
196
- 'message_id': 'msg_001'
197
- },
198
- {
199
- 'id': 2,
200
- 'actor_name': 'process_payment',
201
- 'status': 'running',
202
- 'queue_name': 'payments',
203
- 'created_at': '2025-09-29T12:45:00Z',
204
- 'updated_at': '2025-09-29T12:46:00Z',
205
- 'message_id': 'msg_002'
206
- },
207
- {
208
- 'id': 3,
209
- 'actor_name': 'generate_report',
210
- 'status': 'pending',
211
- 'queue_name': 'background',
212
- 'created_at': '2025-09-29T13:00:00Z',
213
- 'updated_at': '2025-09-29T13:00:00Z',
214
- 'message_id': 'msg_003'
215
- },
216
- {
217
- 'id': 4,
218
- 'actor_name': 'sync_data',
219
- 'status': 'failed',
220
- 'queue_name': 'high',
221
- 'created_at': '2025-09-29T11:30:00Z',
222
- 'updated_at': '2025-09-29T11:35:00Z',
223
- 'message_id': 'msg_004'
224
- }
225
- ],
226
- 'pagination': {
227
- 'page': 1,
228
- 'page_size': 20,
229
- 'total_pages': 1,
230
- 'total_count': 4,
231
- 'has_next': False,
232
- 'has_previous': False
233
- },
234
- 'simulated': True
235
- }
236
-
237
- return Response({
238
- 'success': True,
239
- 'data': mock_data
240
- })
241
-
242
- except Exception as e:
243
- logger.error(f"Task list API error: {e}")
244
- return Response({
245
- 'success': False,
246
- 'error': str(e)
247
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
248
-
249
- @action(detail=False, methods=['get'], url_path='workers/list')
250
- @extend_schema(
251
- summary="Get workers list",
252
- description="Retrieve detailed list of active workers",
253
- responses={
254
- 200: OpenApiResponse(response=APIResponseSerializer, description="Workers list retrieved successfully"),
255
- 500: OpenApiResponse(response=APIResponseSerializer, description="Internal server error")
256
- },
257
- tags=["Worker Management"]
258
- )
259
- def workers_list(self, request):
260
- """Get detailed list of workers."""
261
- try:
262
- from ..utils.simulator import TaskSimulator
263
- simulator = TaskSimulator()
264
- workers_data = simulator.get_current_workers_list()
265
-
266
- return Response({
267
- 'success': True,
268
- 'data': workers_data
269
- })
270
-
271
- except Exception as e:
272
- logger.error(f"Workers list API error: {e}")
273
- return Response({
274
- 'success': False,
275
- 'error': str(e)
276
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
277
-
278
- @action(detail=False, methods=['post'], url_path='workers/manage')
279
- @extend_schema(
280
- summary="Manage workers",
281
- description="Perform management operations on workers (restart, stop, etc.)",
282
- request=WorkerActionSerializer,
283
- responses={
284
- 200: OpenApiResponse(response=APIResponseSerializer, description="Worker operation completed successfully"),
285
- 400: OpenApiResponse(response=APIResponseSerializer, description="Invalid request data"),
286
- 500: OpenApiResponse(response=APIResponseSerializer, description="Internal server error")
287
- },
288
- tags=["Worker Management"]
289
- )
290
- def worker_manage(self, request):
291
- """Manage worker operations."""
292
- try:
293
- serializer = WorkerActionSerializer(data=request.data)
294
- if not serializer.is_valid():
295
- return Response({
296
- 'success': False,
297
- 'error': 'Invalid request data',
298
- 'details': serializer.errors
299
- }, status=status.HTTP_400_BAD_REQUEST)
300
-
301
- action_type = serializer.validated_data['action']
302
- worker_id = serializer.validated_data.get('worker_id')
303
-
304
- # Worker management operations would go here
305
- # For now, return a placeholder response
306
- return Response({
307
- 'success': True,
308
- 'data': {
309
- 'message': f'Worker {action_type} operation initiated',
310
- 'worker_id': worker_id,
311
- 'action': action_type
312
- }
313
- })
314
-
315
- except Exception as e:
316
- logger.error(f"Worker management API error: {e}")
317
- return Response({
318
- 'success': False,
319
- 'error': str(e)
320
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
321
-
322
- @action(detail=False, methods=['post'], url_path='simulate')
323
- @extend_schema(
324
- summary="Simulate test data",
325
- description="Create test data in Redis for dashboard testing",
326
- request=None,
327
- responses={
328
- 200: OpenApiResponse(response=APIResponseSerializer, description="Simulation completed successfully"),
329
- 500: OpenApiResponse(response=APIResponseSerializer, description="Simulation failed")
330
- },
331
- tags=["Task Management"]
332
- )
333
- def simulate_data(self, request):
334
- """Simulate test data for dashboard testing."""
335
- try:
336
- from ..utils.simulator import TaskSimulator
337
-
338
- simulator = TaskSimulator()
339
- result = simulator.run_simulation(workers=3, clear_first=True)
340
-
341
- return Response({
342
- 'success': True,
343
- 'data': {
344
- 'message': 'Test data simulation completed successfully',
345
- 'details': result
346
- }
347
- })
348
-
349
- except Exception as e:
350
- logger.error(f"Simulation API error: {e}")
351
- return Response({
352
- 'success': False,
353
- 'error': str(e)
354
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
355
-
356
- @action(detail=False, methods=['post'], url_path='clear')
357
- @extend_schema(
358
- summary="Clear test data",
359
- description="Clear all test data from Redis",
360
- request=None,
361
- responses={
362
- 200: OpenApiResponse(response=APIResponseSerializer, description="Data cleared successfully"),
363
- 500: OpenApiResponse(response=APIResponseSerializer, description="Clear operation failed")
364
- },
365
- tags=["Task Management"]
366
- )
367
- def clear_data(self, request):
368
- """Clear all test data from Redis."""
369
- try:
370
- from ..utils.simulator import TaskSimulator
371
-
372
- simulator = TaskSimulator()
373
- simulator.clear_all_data()
374
-
375
- return Response({
376
- 'success': True,
377
- 'data': {
378
- 'message': 'All test data cleared successfully'
379
- }
380
- })
381
-
382
- except Exception as e:
383
- logger.error(f"Clear data API error: {e}")
384
- return Response({
385
- 'success': False,
386
- 'error': str(e)
387
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
388
-
389
- @action(detail=False, methods=['post'], url_path='clear-queues')
390
- @extend_schema(
391
- summary="Clear all queues",
392
- description="Clear all tasks from all Dramatiq queues",
393
- request=None,
394
- responses={
395
- 200: OpenApiResponse(response=APIResponseSerializer, description="Queues cleared successfully"),
396
- 500: OpenApiResponse(response=APIResponseSerializer, description="Clear operation failed")
397
- },
398
- tags=["Queue Management"]
399
- )
400
- def clear_all_queues(self, request):
401
- """Clear all tasks from all Dramatiq queues."""
402
- try:
403
- from django_cfg.modules.django_tasks import DjangoTasks
404
-
405
- tasks_service = DjangoTasks()
406
- redis_client = tasks_service.get_redis_client()
407
-
408
- if not redis_client:
409
- return Response({
410
- 'success': False,
411
- 'error': 'Redis connection not available'
412
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
413
-
414
- # Get all Dramatiq queue keys
415
- queue_keys = redis_client.keys('dramatiq:*.msgs') # Main queues
416
- failed_keys = redis_client.keys('dramatiq:*.DQ') # Failed queues
417
- ack_keys = redis_client.keys('dramatiq:__acks__*') # Acknowledgments
418
-
419
- cleared_count = 0
420
-
421
- # Clear main queues
422
- for key in queue_keys:
423
- redis_client.delete(key)
424
- cleared_count += 1
425
-
426
- # Clear failed queues
427
- for key in failed_keys:
428
- redis_client.delete(key)
429
- cleared_count += 1
430
-
431
- # Clear acknowledgments
432
- for key in ack_keys:
433
- redis_client.delete(key)
434
- cleared_count += 1
435
-
436
- return Response({
437
- 'success': True,
438
- 'message': f'Cleared {cleared_count} Dramatiq keys from Redis'
439
- })
440
-
441
- except Exception as e:
442
- logger.error(f"Clear queues API error: {e}")
443
- return Response({
444
- 'success': False,
445
- 'error': str(e)
446
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
447
-
448
- @action(detail=False, methods=['post'], url_path='purge-failed')
449
- @extend_schema(
450
- summary="Purge failed tasks",
451
- description="Remove all failed tasks from queues",
452
- request=None,
453
- responses={
454
- 200: OpenApiResponse(response=APIResponseSerializer, description="Failed tasks purged successfully"),
455
- 500: OpenApiResponse(response=APIResponseSerializer, description="Purge operation failed")
456
- },
457
- tags=["Queue Management"]
458
- )
459
- def purge_failed_tasks(self, request):
460
- """Purge all failed tasks from queues."""
461
- try:
462
- from django_cfg.modules.django_tasks import DjangoTasks
463
-
464
- tasks_service = DjangoTasks()
465
- redis_client = tasks_service.get_redis_client()
466
-
467
- if not redis_client:
468
- return Response({
469
- 'success': False,
470
- 'error': 'Redis connection not available'
471
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
472
-
473
- # Get only failed queue keys (DQ = Dead Queue)
474
- failed_keys = redis_client.keys('dramatiq:*.DQ')
475
-
476
- cleared_count = 0
477
-
478
- # Clear only failed queues
479
- for key in failed_keys:
480
- failed_count = redis_client.llen(key)
481
- if failed_count > 0:
482
- redis_client.delete(key)
483
- cleared_count += failed_count
484
-
485
- return Response({
486
- 'success': True,
487
- 'message': f'Purged {cleared_count} failed tasks from queues'
488
- })
489
-
490
- except Exception as e:
491
- logger.error(f"Purge failed tasks API error: {e}")
492
- return Response({
493
- 'success': False,
494
- 'error': str(e)
495
- }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
496
-
497
- # Helper methods
498
- def _clear_all_queues(self, tasks_service: DjangoTasks) -> Dict[str, Any]:
499
- """Clear all queues."""
500
- try:
501
- redis_client = tasks_service.get_redis_client()
502
- if not redis_client:
503
- return {'error': 'Redis connection not available'}
504
-
505
- # Get all queue keys and clear them
506
- queue_keys = redis_client.keys("dramatiq:queue:*")
507
- cleared_count = 0
508
-
509
- for key in queue_keys:
510
- redis_client.delete(key)
511
- cleared_count += 1
512
-
513
- return {
514
- 'message': f'Cleared {cleared_count} queues',
515
- 'cleared_count': cleared_count
516
- }
517
-
518
- except Exception as e:
519
- return {'error': str(e)}
520
-
521
- def _clear_queue(self, tasks_service: DjangoTasks, queue_name: str) -> Dict[str, Any]:
522
- """Clear specific queue."""
523
- try:
524
- redis_client = tasks_service.get_redis_client()
525
- if not redis_client:
526
- return {'error': 'Redis connection not available'}
527
-
528
- queue_key = f"dramatiq:queue:{queue_name}"
529
- cleared_count = redis_client.delete(queue_key)
530
-
531
- return {
532
- 'message': f'Cleared queue {queue_name}',
533
- 'queue_name': queue_name,
534
- 'cleared': cleared_count > 0
535
- }
536
-
537
- except Exception as e:
538
- return {'error': str(e)}
539
-
540
- def _purge_failed_tasks(self, tasks_service: DjangoTasks, queue_name: str = None) -> Dict[str, Any]:
541
- """Purge failed tasks."""
542
- try:
543
- redis_client = tasks_service.get_redis_client()
544
- if not redis_client:
545
- return {'error': 'Redis connection not available'}
546
-
547
- if queue_name:
548
- # Clear specific queue's failed tasks
549
- failed_key = f"dramatiq:queue:{queue_name}.DLQ"
550
- cleared_count = redis_client.delete(failed_key)
551
- return {
552
- 'message': f'Purged failed tasks from {queue_name}',
553
- 'queue_name': queue_name,
554
- 'cleared_count': cleared_count
555
- }
556
- else:
557
- # Clear all failed task queues
558
- failed_keys = redis_client.keys("dramatiq:queue:*.DLQ")
559
- cleared_count = 0
560
-
561
- for key in failed_keys:
562
- redis_client.delete(key)
563
- cleared_count += 1
564
-
565
- return {
566
- 'message': f'Purged failed tasks from {cleared_count} queues',
567
- 'cleared_count': cleared_count
568
- }
569
-
570
- except Exception as e:
571
- return {'error': str(e)}