django-cfg 1.4.120__py3-none-any.whl → 1.5.2__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 (182) hide show
  1. django_cfg/__init__.py +8 -4
  2. django_cfg/apps/centrifugo/admin/centrifugo_log.py +33 -71
  3. django_cfg/apps/dashboard/TRANSACTION_FIX.md +73 -0
  4. django_cfg/apps/dashboard/serializers/__init__.py +0 -12
  5. django_cfg/apps/dashboard/serializers/activity.py +1 -1
  6. django_cfg/apps/dashboard/services/__init__.py +0 -2
  7. django_cfg/apps/dashboard/services/charts_service.py +4 -3
  8. django_cfg/apps/dashboard/services/statistics_service.py +11 -2
  9. django_cfg/apps/dashboard/services/system_health_service.py +64 -106
  10. django_cfg/apps/dashboard/urls.py +0 -2
  11. django_cfg/apps/dashboard/views/__init__.py +0 -2
  12. django_cfg/apps/dashboard/views/commands_views.py +3 -6
  13. django_cfg/apps/dashboard/views/overview_views.py +14 -13
  14. django_cfg/apps/grpc/__init__.py +9 -0
  15. django_cfg/apps/grpc/admin/__init__.py +11 -0
  16. django_cfg/apps/{tasks → grpc}/admin/config.py +32 -41
  17. django_cfg/apps/grpc/admin/grpc_request_log.py +252 -0
  18. django_cfg/apps/grpc/apps.py +28 -0
  19. django_cfg/apps/grpc/auth/__init__.py +9 -0
  20. django_cfg/apps/grpc/auth/jwt_auth.py +295 -0
  21. django_cfg/apps/grpc/interceptors/__init__.py +19 -0
  22. django_cfg/apps/grpc/interceptors/errors.py +241 -0
  23. django_cfg/apps/grpc/interceptors/logging.py +270 -0
  24. django_cfg/apps/grpc/interceptors/metrics.py +306 -0
  25. django_cfg/apps/grpc/interceptors/request_logger.py +515 -0
  26. django_cfg/apps/grpc/management/__init__.py +1 -0
  27. django_cfg/apps/grpc/management/commands/rungrpc.py +302 -0
  28. django_cfg/apps/grpc/managers/__init__.py +10 -0
  29. django_cfg/apps/grpc/managers/grpc_request_log.py +310 -0
  30. django_cfg/apps/grpc/migrations/0001_initial.py +69 -0
  31. django_cfg/apps/grpc/migrations/0002_rename_django_cfg__service_4c4a8e_idx_django_cfg__service_584308_idx_and_more.py +38 -0
  32. django_cfg/apps/grpc/models/__init__.py +9 -0
  33. django_cfg/apps/grpc/models/grpc_request_log.py +219 -0
  34. django_cfg/apps/grpc/serializers/__init__.py +23 -0
  35. django_cfg/apps/grpc/serializers/health.py +18 -0
  36. django_cfg/apps/grpc/serializers/requests.py +18 -0
  37. django_cfg/apps/grpc/serializers/services.py +50 -0
  38. django_cfg/apps/grpc/serializers/stats.py +22 -0
  39. django_cfg/apps/grpc/services/__init__.py +16 -0
  40. django_cfg/apps/grpc/services/base.py +375 -0
  41. django_cfg/apps/grpc/services/discovery.py +415 -0
  42. django_cfg/apps/grpc/urls.py +23 -0
  43. django_cfg/apps/grpc/utils/__init__.py +13 -0
  44. django_cfg/apps/grpc/utils/proto_gen.py +423 -0
  45. django_cfg/apps/grpc/views/__init__.py +9 -0
  46. django_cfg/apps/grpc/views/monitoring.py +497 -0
  47. django_cfg/apps/knowbase/apps.py +2 -2
  48. django_cfg/apps/maintenance/admin/api_key_admin.py +7 -9
  49. django_cfg/apps/maintenance/admin/site_admin.py +5 -4
  50. django_cfg/apps/newsletter/admin/newsletter_admin.py +12 -11
  51. django_cfg/apps/payments/admin/balance_admin.py +26 -36
  52. django_cfg/apps/payments/admin/payment_admin.py +65 -85
  53. django_cfg/apps/payments/admin/withdrawal_admin.py +65 -100
  54. django_cfg/apps/rq/__init__.py +9 -0
  55. django_cfg/apps/rq/apps.py +80 -0
  56. django_cfg/apps/rq/management/__init__.py +1 -0
  57. django_cfg/apps/rq/management/commands/__init__.py +1 -0
  58. django_cfg/apps/rq/management/commands/rqscheduler.py +31 -0
  59. django_cfg/apps/rq/management/commands/rqstats.py +33 -0
  60. django_cfg/apps/rq/management/commands/rqworker.py +31 -0
  61. django_cfg/apps/rq/management/commands/rqworker_pool.py +27 -0
  62. django_cfg/apps/rq/serializers/__init__.py +40 -0
  63. django_cfg/apps/rq/serializers/health.py +60 -0
  64. django_cfg/apps/rq/serializers/job.py +100 -0
  65. django_cfg/apps/rq/serializers/queue.py +80 -0
  66. django_cfg/apps/rq/serializers/schedule.py +178 -0
  67. django_cfg/apps/rq/serializers/testing.py +139 -0
  68. django_cfg/apps/rq/serializers/worker.py +58 -0
  69. django_cfg/apps/rq/services/__init__.py +25 -0
  70. django_cfg/apps/rq/services/config_helper.py +233 -0
  71. django_cfg/apps/rq/services/models/README.md +417 -0
  72. django_cfg/apps/rq/services/models/__init__.py +30 -0
  73. django_cfg/apps/rq/services/models/event.py +123 -0
  74. django_cfg/apps/rq/services/models/job.py +99 -0
  75. django_cfg/apps/rq/services/models/queue.py +92 -0
  76. django_cfg/apps/rq/services/models/worker.py +104 -0
  77. django_cfg/apps/rq/services/rq_converters.py +183 -0
  78. django_cfg/apps/rq/tasks/__init__.py +23 -0
  79. django_cfg/apps/rq/tasks/demo_tasks.py +284 -0
  80. django_cfg/apps/rq/urls.py +54 -0
  81. django_cfg/apps/rq/views/__init__.py +19 -0
  82. django_cfg/apps/rq/views/jobs.py +882 -0
  83. django_cfg/apps/rq/views/monitoring.py +248 -0
  84. django_cfg/apps/rq/views/queues.py +261 -0
  85. django_cfg/apps/rq/views/schedule.py +400 -0
  86. django_cfg/apps/rq/views/testing.py +761 -0
  87. django_cfg/apps/rq/views/workers.py +195 -0
  88. django_cfg/apps/urls.py +13 -8
  89. django_cfg/config.py +106 -0
  90. django_cfg/core/base/config_model.py +16 -26
  91. django_cfg/core/builders/apps_builder.py +7 -11
  92. django_cfg/core/generation/integration_generators/__init__.py +3 -6
  93. django_cfg/core/generation/integration_generators/django_rq.py +80 -0
  94. django_cfg/core/generation/integration_generators/grpc_generator.py +318 -0
  95. django_cfg/core/generation/orchestrator.py +15 -15
  96. django_cfg/core/integration/display/startup.py +6 -20
  97. django_cfg/mixins/__init__.py +2 -0
  98. django_cfg/mixins/superadmin_api.py +59 -0
  99. django_cfg/models/__init__.py +3 -3
  100. django_cfg/models/api/grpc/__init__.py +59 -0
  101. django_cfg/models/api/grpc/config.py +364 -0
  102. django_cfg/models/django/__init__.py +3 -3
  103. django_cfg/models/django/django_rq.py +621 -0
  104. django_cfg/models/django/revolution_legacy.py +1 -1
  105. django_cfg/modules/base.py +19 -6
  106. django_cfg/modules/django_admin/base/pydantic_admin.py +2 -2
  107. django_cfg/modules/django_admin/config/background_task_config.py +4 -4
  108. django_cfg/modules/django_admin/utils/__init__.py +41 -3
  109. django_cfg/modules/django_admin/utils/badges/__init__.py +13 -0
  110. django_cfg/modules/django_admin/utils/{badges.py → badges/status_badges.py} +3 -3
  111. django_cfg/modules/django_admin/utils/displays/__init__.py +13 -0
  112. django_cfg/modules/django_admin/utils/{displays.py → displays/data_displays.py} +2 -2
  113. django_cfg/modules/django_admin/utils/html/__init__.py +26 -0
  114. django_cfg/modules/django_admin/utils/html/badges.py +47 -0
  115. django_cfg/modules/django_admin/utils/html/base.py +167 -0
  116. django_cfg/modules/django_admin/utils/html/code.py +87 -0
  117. django_cfg/modules/django_admin/utils/html/composition.py +205 -0
  118. django_cfg/modules/django_admin/utils/html/formatting.py +231 -0
  119. django_cfg/modules/django_admin/utils/html/keyvalue.py +219 -0
  120. django_cfg/modules/django_admin/utils/html/markdown_integration.py +108 -0
  121. django_cfg/modules/django_admin/utils/html/progress.py +127 -0
  122. django_cfg/modules/django_admin/utils/html_builder.py +55 -408
  123. django_cfg/modules/django_admin/utils/markdown/__init__.py +21 -0
  124. django_cfg/modules/django_unfold/navigation.py +21 -18
  125. django_cfg/pyproject.toml +4 -6
  126. django_cfg/registry/core.py +4 -7
  127. django_cfg/registry/modules.py +6 -0
  128. django_cfg/static/frontend/admin.zip +0 -0
  129. django_cfg/templates/admin/constance/includes/results_list.html +73 -0
  130. django_cfg/templates/admin/index.html +187 -62
  131. django_cfg/templatetags/django_cfg.py +61 -1
  132. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/METADATA +12 -4
  133. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/RECORD +140 -96
  134. django_cfg/apps/dashboard/permissions.py +0 -48
  135. django_cfg/apps/dashboard/serializers/django_q2.py +0 -50
  136. django_cfg/apps/dashboard/services/django_q2_service.py +0 -159
  137. django_cfg/apps/dashboard/views/django_q2_views.py +0 -79
  138. django_cfg/apps/tasks/__init__.py +0 -64
  139. django_cfg/apps/tasks/admin/__init__.py +0 -4
  140. django_cfg/apps/tasks/admin/task_log.py +0 -265
  141. django_cfg/apps/tasks/apps.py +0 -15
  142. django_cfg/apps/tasks/filters/__init__.py +0 -10
  143. django_cfg/apps/tasks/filters/task_log.py +0 -121
  144. django_cfg/apps/tasks/migrations/0001_initial.py +0 -196
  145. django_cfg/apps/tasks/migrations/0002_delete_tasklog.py +0 -16
  146. django_cfg/apps/tasks/models/__init__.py +0 -4
  147. django_cfg/apps/tasks/models/task_log.py +0 -246
  148. django_cfg/apps/tasks/serializers/__init__.py +0 -28
  149. django_cfg/apps/tasks/serializers/task_log.py +0 -249
  150. django_cfg/apps/tasks/services/__init__.py +0 -10
  151. django_cfg/apps/tasks/services/client/__init__.py +0 -7
  152. django_cfg/apps/tasks/services/client/client.py +0 -234
  153. django_cfg/apps/tasks/services/config_helper.py +0 -63
  154. django_cfg/apps/tasks/services/sync.py +0 -204
  155. django_cfg/apps/tasks/urls.py +0 -16
  156. django_cfg/apps/tasks/views/__init__.py +0 -10
  157. django_cfg/apps/tasks/views/task_log.py +0 -41
  158. django_cfg/apps/tasks/views/task_log_base.py +0 -41
  159. django_cfg/apps/tasks/views/task_log_overview.py +0 -100
  160. django_cfg/apps/tasks/views/task_log_related.py +0 -41
  161. django_cfg/apps/tasks/views/task_log_stats.py +0 -91
  162. django_cfg/apps/tasks/views/task_log_timeline.py +0 -81
  163. django_cfg/core/generation/integration_generators/django_q2.py +0 -133
  164. django_cfg/core/generation/integration_generators/tasks.py +0 -88
  165. django_cfg/models/django/django_q2.py +0 -514
  166. django_cfg/models/tasks/__init__.py +0 -49
  167. django_cfg/models/tasks/backends.py +0 -122
  168. django_cfg/models/tasks/config.py +0 -209
  169. django_cfg/models/tasks/utils.py +0 -162
  170. django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md +0 -396
  171. django_cfg/modules/django_q2/README.md +0 -140
  172. django_cfg/modules/django_q2/__init__.py +0 -8
  173. django_cfg/modules/django_q2/apps.py +0 -107
  174. django_cfg/modules/django_q2/management/commands/__init__.py +0 -0
  175. django_cfg/modules/django_q2/management/commands/sync_django_q_schedules.py +0 -74
  176. /django_cfg/apps/{tasks/migrations → grpc/management/commands}/__init__.py +0 -0
  177. /django_cfg/{modules/django_q2/management → apps/grpc/migrations}/__init__.py +0 -0
  178. /django_cfg/modules/django_admin/utils/{mermaid_plugin.py → markdown/mermaid_plugin.py} +0 -0
  179. /django_cfg/modules/django_admin/utils/{markdown_renderer.py → markdown/renderer.py} +0 -0
  180. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/WHEEL +0 -0
  181. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/entry_points.txt +0 -0
  182. {django_cfg-1.4.120.dist-info → django_cfg-1.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,204 +0,0 @@
1
- """
2
- ReArq to TaskLog sync utilities.
3
-
4
- Helpers for syncing data from ReArq (Tortoise ORM) to TaskLog (Django ORM).
5
- """
6
- from typing import Optional
7
- from django.utils import timezone
8
- from ..models import TaskLog
9
-
10
-
11
- def create_task_log_from_job(
12
- job_id: str,
13
- task_name: str,
14
- queue_name: str = "default",
15
- args: list = None,
16
- kwargs: dict = None,
17
- job_retry: int = 0,
18
- job_retry_after: int = 60,
19
- enqueue_time = None,
20
- expire_time = None,
21
- status: str = "queued",
22
- user=None,
23
- ) -> TaskLog:
24
- """
25
- Create TaskLog entry from ReArq Job data.
26
-
27
- Args:
28
- job_id: Unique job identifier from ReArq
29
- task_name: Name of the task function
30
- queue_name: Queue name (default: "default")
31
- args: Positional arguments
32
- kwargs: Keyword arguments
33
- job_retry: Max retry count from task definition
34
- job_retry_after: Seconds to wait before retry
35
- enqueue_time: When job was enqueued (Job.enqueue_time)
36
- expire_time: When job will expire (Job.expire_time)
37
- status: Job status (default: "queued")
38
- user: Django User who triggered the task
39
-
40
- Returns:
41
- Created TaskLog instance
42
- """
43
- return TaskLog.objects.create(
44
- job_id=job_id,
45
- task_name=task_name,
46
- queue_name=queue_name,
47
- args=args or [],
48
- kwargs=kwargs or {},
49
- job_retry=job_retry,
50
- job_retry_after=job_retry_after,
51
- enqueue_time=enqueue_time or timezone.now(),
52
- expire_time=expire_time,
53
- status=status,
54
- user=user,
55
- )
56
-
57
-
58
- def update_task_log_from_result(
59
- job_id: str,
60
- worker: str,
61
- success: bool,
62
- result: str = None,
63
- start_time = None,
64
- finish_time = None,
65
- ) -> Optional[TaskLog]:
66
- """
67
- Update TaskLog with JobResult data.
68
-
69
- Args:
70
- job_id: Job identifier
71
- worker: Worker that processed the task
72
- success: Whether task succeeded
73
- result: Task result (JSON string)
74
- start_time: When execution started (JobResult.start_time)
75
- finish_time: When execution finished (JobResult.finish_time)
76
-
77
- Returns:
78
- Updated TaskLog instance or None if not found
79
- """
80
- try:
81
- task_log = TaskLog.objects.get(job_id=job_id)
82
- except TaskLog.DoesNotExist:
83
- return None
84
-
85
- # Update fields from JobResult
86
- task_log.worker_id = worker
87
- task_log.success = success
88
- task_log.start_time = start_time
89
- task_log.finish_time = finish_time
90
-
91
- if result is not None:
92
- task_log.result = result
93
-
94
- # Calculate duration
95
- if start_time and finish_time:
96
- delta = finish_time - start_time
97
- task_log.duration_ms = int(delta.total_seconds() * 1000)
98
-
99
- # Update status based on success
100
- if success:
101
- task_log.status = TaskLog.StatusChoices.SUCCESS
102
- else:
103
- task_log.status = TaskLog.StatusChoices.FAILED
104
-
105
- task_log.save()
106
- return task_log
107
-
108
-
109
- def sync_job_status(job_id: str, status: str, job_retries: int = None) -> Optional[TaskLog]:
110
- """
111
- Sync Job status to TaskLog.
112
-
113
- Args:
114
- job_id: Job identifier
115
- status: ReArq JobStatus value
116
- job_retries: Current retry count
117
-
118
- Returns:
119
- Updated TaskLog instance or None if not found
120
- """
121
- try:
122
- task_log = TaskLog.objects.get(job_id=job_id)
123
- except TaskLog.DoesNotExist:
124
- return None
125
-
126
- task_log.status = status
127
- if job_retries is not None:
128
- task_log.job_retries = job_retries
129
-
130
- task_log.save(update_fields=["status", "job_retries", "updated_at"])
131
- return task_log
132
-
133
-
134
- async def sync_from_rearq_job(rearq_job):
135
- """
136
- Sync TaskLog from ReArq Job instance (Tortoise model).
137
-
138
- Usage:
139
- from rearq.server.models import Job
140
- job = await Job.get(job_id="...")
141
- await sync_from_rearq_job(job)
142
-
143
- Args:
144
- rearq_job: ReArq Job instance (Tortoise ORM)
145
-
146
- Returns:
147
- TaskLog instance
148
- """
149
- from asgiref.sync import sync_to_async
150
-
151
- # Extract queue name from full queue path
152
- queue_name = rearq_job.task.split(':')[-1] if ':' in rearq_job.task else "default"
153
-
154
- # Check if TaskLog exists
155
- task_log = await sync_to_async(TaskLog.objects.filter(job_id=rearq_job.job_id).first)()
156
-
157
- if task_log:
158
- # Update existing
159
- task_log.status = rearq_job.status
160
- task_log.job_retries = rearq_job.job_retries
161
- await sync_to_async(task_log.save)(update_fields=["status", "job_retries", "updated_at"])
162
- else:
163
- # Create new
164
- task_log = await sync_to_async(create_task_log_from_job)(
165
- job_id=rearq_job.job_id,
166
- task_name=rearq_job.task,
167
- queue_name=queue_name,
168
- args=rearq_job.args or [],
169
- kwargs=rearq_job.kwargs or {},
170
- job_retry=rearq_job.job_retry,
171
- job_retry_after=rearq_job.job_retry_after,
172
- enqueue_time=rearq_job.enqueue_time,
173
- expire_time=rearq_job.expire_time,
174
- status=rearq_job.status,
175
- )
176
-
177
- return task_log
178
-
179
-
180
- async def sync_from_rearq_result(rearq_result):
181
- """
182
- Sync TaskLog from ReArq JobResult instance (Tortoise model).
183
-
184
- Usage:
185
- from rearq.server.models import JobResult
186
- result = await JobResult.get(job_id="...").prefetch_related("job")
187
- await sync_from_rearq_result(result)
188
-
189
- Args:
190
- rearq_result: ReArq JobResult instance (Tortoise ORM)
191
-
192
- Returns:
193
- TaskLog instance or None
194
- """
195
- from asgiref.sync import sync_to_async
196
-
197
- return await sync_to_async(update_task_log_from_result)(
198
- job_id=rearq_result.job_id,
199
- worker=rearq_result.worker,
200
- success=rearq_result.success,
201
- result=rearq_result.result,
202
- start_time=rearq_result.start_time,
203
- finish_time=rearq_result.finish_time,
204
- )
@@ -1,16 +0,0 @@
1
- """
2
- ReArq Tasks API URLs.
3
-
4
- REST API endpoints for task monitoring and management.
5
- """
6
- from django.urls import path
7
- from rest_framework.routers import DefaultRouter
8
-
9
- from .views import TaskLogViewSet
10
-
11
- app_name = 'django_cfg_tasks'
12
-
13
- router = DefaultRouter()
14
- router.register(r'logs', TaskLogViewSet, basename='tasklog')
15
-
16
- urlpatterns = router.urls
@@ -1,10 +0,0 @@
1
- """
2
- ReArq Tasks API Views.
3
-
4
- ViewSets for task monitoring and management.
5
- """
6
- from .task_log import TaskLogViewSet
7
-
8
- __all__ = [
9
- "TaskLogViewSet",
10
- ]
@@ -1,41 +0,0 @@
1
- """
2
- TaskLog ViewSet - Main Entry Point.
3
-
4
- Combines all task log functionality through mixins.
5
- """
6
- from .task_log_base import TaskLogBaseViewSet
7
- from .task_log_stats import TaskLogStatsMixin
8
- from .task_log_timeline import TaskLogTimelineMixin
9
- from .task_log_overview import TaskLogOverviewMixin
10
- from .task_log_related import TaskLogRelatedMixin
11
-
12
-
13
- class TaskLogViewSet(
14
- TaskLogStatsMixin,
15
- TaskLogTimelineMixin,
16
- TaskLogOverviewMixin,
17
- TaskLogRelatedMixin,
18
- TaskLogBaseViewSet,
19
- ):
20
- """
21
- Complete ViewSet for TaskLog monitoring.
22
-
23
- Provides read-only access to task execution logs with filtering,
24
- searching, and statistics.
25
-
26
- Endpoints:
27
- GET /api/tasks/logs/ - List all task logs
28
- GET /api/tasks/logs/{id}/ - Get task log details
29
- GET /api/tasks/logs/{id}/related/ - Get related task logs
30
- GET /api/tasks/logs/stats/ - Get aggregated statistics
31
- GET /api/tasks/logs/timeline/ - Get task execution timeline
32
- GET /api/tasks/logs/overview/ - Get summary overview
33
-
34
- Mixins:
35
- - TaskLogStatsMixin: Aggregated statistics
36
- - TaskLogTimelineMixin: Time-series data
37
- - TaskLogOverviewMixin: High-level summary
38
- - TaskLogRelatedMixin: Related task lookup
39
- - TaskLogBaseViewSet: Base CRUD operations
40
- """
41
- pass
@@ -1,41 +0,0 @@
1
- """
2
- TaskLog Base ViewSet.
3
-
4
- Base viewset with common configuration for task log endpoints.
5
- """
6
- from rest_framework import viewsets
7
- from rest_framework.filters import SearchFilter, OrderingFilter
8
- from django_filters.rest_framework import DjangoFilterBackend
9
-
10
- from django_cfg.mixins import AdminAPIMixin
11
- from ..models import TaskLog
12
- from ..serializers import (
13
- TaskLogSerializer,
14
- TaskLogListSerializer,
15
- TaskLogDetailSerializer,
16
- )
17
- from ..filters import TaskLogFilter
18
-
19
-
20
- class TaskLogBaseViewSet(AdminAPIMixin, viewsets.ReadOnlyModelViewSet):
21
- """
22
- Base ViewSet for TaskLog monitoring.
23
-
24
- Provides read-only access to task execution logs with filtering and searching.
25
- Extended by mixins for additional functionality (stats, timeline, overview).
26
- Requires admin authentication (JWT, Session, or Basic Auth).
27
- """
28
-
29
- queryset = TaskLog.objects.all().order_by('-enqueue_time')
30
- filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
31
- filterset_class = TaskLogFilter
32
- search_fields = ['task_name', 'job_id', 'queue_name', 'error_message', 'worker_id']
33
- ordering_fields = ['enqueue_time', 'start_time', 'finish_time', 'duration_ms', 'job_retries', 'created_at']
34
-
35
- def get_serializer_class(self):
36
- """Return appropriate serializer based on action."""
37
- if self.action == 'list':
38
- return TaskLogListSerializer
39
- elif self.action == 'retrieve':
40
- return TaskLogDetailSerializer
41
- return TaskLogSerializer
@@ -1,100 +0,0 @@
1
- """
2
- TaskLog Overview Actions.
3
-
4
- Provides high-level summary statistics for the entire task system.
5
- """
6
- from django.db.models import Q, Count
7
- from django.utils import timezone
8
- from datetime import timedelta
9
- from drf_spectacular.utils import extend_schema
10
- from rest_framework.decorators import action
11
- from rest_framework.response import Response
12
-
13
- from ..models import TaskLog
14
- from ..serializers import TaskLogOverviewSerializer
15
-
16
-
17
- class TaskLogOverviewMixin:
18
- """
19
- Mixin for task system overview endpoints.
20
-
21
- Provides high-level statistics:
22
- - Total task counts
23
- - Active queues
24
- - Recent failures
25
- - Distribution by queue and status
26
- """
27
-
28
- @extend_schema(
29
- responses={200: TaskLogOverviewSerializer},
30
- summary="Task System Overview",
31
- description="Get high-level summary statistics for the entire task system"
32
- )
33
- @action(detail=False, methods=['get'])
34
- def overview(self, request):
35
- """
36
- Get summary overview of task system.
37
-
38
- Returns properly structured data validated by TaskLogOverviewSerializer.
39
-
40
- Returns:
41
- {
42
- "total_tasks": 1500,
43
- "active_queues": ["default", "high", "knowledge"],
44
- "recent_failures": 5,
45
- "tasks_by_queue": [
46
- {"queue_name": "default", "count": 800},
47
- {"queue_name": "high", "count": 500},
48
- {"queue_name": "knowledge", "count": 200}
49
- ],
50
- "tasks_by_status": [
51
- {"status": "success", "count": 1450},
52
- {"status": "failed", "count": 45},
53
- {"status": "in_progress", "count": 5}
54
- ]
55
- }
56
- """
57
- # Get all-time statistics
58
- total_tasks = TaskLog.objects.count()
59
-
60
- # Get active queues
61
- active_queues = list(
62
- TaskLog.objects.values_list('queue_name', flat=True)
63
- .distinct()
64
- .order_by('queue_name')
65
- )
66
-
67
- # Recent failures (last 24 hours)
68
- time_threshold = timezone.now() - timedelta(hours=24)
69
- recent_failures = TaskLog.objects.filter(
70
- Q(success=False) | Q(status__in=['failed', 'expired']),
71
- created_at__gte=time_threshold
72
- ).count()
73
-
74
- # Tasks by queue - convert to array of objects
75
- tasks_by_queue = [
76
- {'queue_name': item['queue_name'], 'count': item['count']}
77
- for item in TaskLog.objects.values('queue_name')
78
- .annotate(count=Count('id'))
79
- .order_by('queue_name')
80
- ]
81
-
82
- # Tasks by status - convert to array of objects
83
- tasks_by_status = [
84
- {'status': item['status'], 'count': item['count']}
85
- for item in TaskLog.objects.values('status')
86
- .annotate(count=Count('id'))
87
- .order_by('status')
88
- ]
89
-
90
- overview_data = {
91
- 'total_tasks': total_tasks,
92
- 'active_queues': active_queues,
93
- 'recent_failures': recent_failures,
94
- 'tasks_by_queue': tasks_by_queue,
95
- 'tasks_by_status': tasks_by_status,
96
- }
97
-
98
- # Validate and serialize data
99
- serializer = TaskLogOverviewSerializer(overview_data)
100
- return Response(serializer.data)
@@ -1,41 +0,0 @@
1
- """
2
- TaskLog Related Tasks Actions.
3
-
4
- Provides endpoints for finding related task executions.
5
- """
6
- from django.db.models import Q
7
- from rest_framework.decorators import action
8
- from rest_framework.response import Response
9
-
10
- from ..models import TaskLog
11
- from ..serializers import TaskLogListSerializer
12
-
13
-
14
- class TaskLogRelatedMixin:
15
- """
16
- Mixin for related tasks endpoints.
17
-
18
- Provides functionality to find related task executions:
19
- - Same job_id (retries)
20
- - Same task_name (similar executions)
21
- """
22
-
23
- @action(detail=True, methods=['get'])
24
- def related(self, request, pk=None):
25
- """
26
- Get related task logs (same job_id or task_name).
27
-
28
- Returns tasks that share the same job_id or are retries of the same task.
29
-
30
- Returns:
31
- Array of related TaskLog objects (up to 10 most recent)
32
- """
33
- task_log = self.get_object()
34
-
35
- # Find related tasks
36
- related = TaskLog.objects.filter(
37
- Q(job_id=task_log.job_id) | Q(task_name=task_log.task_name)
38
- ).exclude(id=task_log.id).order_by('-created_at')[:10]
39
-
40
- serializer = TaskLogListSerializer(related, many=True)
41
- return Response(serializer.data)
@@ -1,91 +0,0 @@
1
- """
2
- TaskLog Statistics Actions.
3
-
4
- Provides aggregated statistics and metrics for task execution monitoring.
5
- """
6
- from django.db.models import Q, Avg
7
- from django.utils import timezone
8
- from datetime import timedelta
9
- from drf_spectacular.utils import extend_schema
10
- from rest_framework.decorators import action
11
- from rest_framework.response import Response
12
-
13
- from ..models import TaskLog
14
- from ..serializers import TaskLogStatsSerializer
15
-
16
-
17
- class TaskLogStatsMixin:
18
- """
19
- Mixin for task statistics endpoints.
20
-
21
- Provides aggregated statistics about task execution:
22
- - Success/failure rates
23
- - Average durations
24
- - Task counts by status
25
- """
26
-
27
- @extend_schema(
28
- responses={200: TaskLogStatsSerializer},
29
- summary="Task Execution Statistics",
30
- description="Get aggregated statistics about task execution (success/failure rates, duration)"
31
- )
32
- @action(detail=False, methods=['get'])
33
- def stats(self, request):
34
- """
35
- Get aggregated task statistics.
36
-
37
- Query Parameters:
38
- period_hours (int): Statistics period in hours (default: 24)
39
- task_name (str): Filter by specific task name
40
-
41
- Returns:
42
- {
43
- "total": 150,
44
- "successful": 145,
45
- "failed": 5,
46
- "in_progress": 2,
47
- "success_rate": 96.67,
48
- "avg_duration_ms": 1250,
49
- "avg_duration_seconds": 1.25,
50
- "period_hours": 24
51
- }
52
- """
53
- period_hours = int(request.query_params.get('period_hours', 24))
54
- task_name = request.query_params.get('task_name')
55
-
56
- # Calculate time threshold
57
- time_threshold = timezone.now() - timedelta(hours=period_hours)
58
-
59
- # Base queryset
60
- queryset = TaskLog.objects.filter(created_at__gte=time_threshold)
61
- if task_name:
62
- queryset = queryset.filter(task_name=task_name)
63
-
64
- # Calculate statistics
65
- total = queryset.count()
66
- successful = queryset.filter(success=True).count()
67
- failed = queryset.filter(success=False, status='failed').count()
68
- in_progress = queryset.filter(Q(status='in_progress') | Q(status='queued')).count()
69
-
70
- # Calculate success rate
71
- completed = successful + failed
72
- success_rate = (successful / completed * 100) if completed > 0 else 0.0
73
-
74
- # Calculate average duration (only for completed tasks)
75
- avg_duration = queryset.filter(
76
- duration_ms__isnull=False
77
- ).aggregate(avg=Avg('duration_ms'))['avg'] or 0
78
-
79
- stats_data = {
80
- 'total': total,
81
- 'successful': successful,
82
- 'failed': failed,
83
- 'in_progress': in_progress,
84
- 'success_rate': round(success_rate, 2),
85
- 'avg_duration_ms': int(avg_duration),
86
- 'avg_duration_seconds': round(avg_duration / 1000, 2),
87
- 'period_hours': period_hours,
88
- }
89
-
90
- serializer = TaskLogStatsSerializer(stats_data)
91
- return Response(serializer.data)
@@ -1,81 +0,0 @@
1
- """
2
- TaskLog Timeline Actions.
3
-
4
- Provides time-series data for task execution visualization.
5
- """
6
- from django.utils import timezone
7
- from datetime import timedelta
8
- from drf_spectacular.utils import extend_schema
9
- from rest_framework.decorators import action
10
- from rest_framework.response import Response
11
-
12
- from ..models import TaskLog
13
- from ..serializers import TaskLogTimelineSerializer
14
-
15
-
16
- class TaskLogTimelineMixin:
17
- """
18
- Mixin for task timeline endpoints.
19
-
20
- Provides time-bucketed statistics for visualization:
21
- - Hourly/daily task counts
22
- - Success/failure trends
23
- - Performance metrics over time
24
- """
25
-
26
- @extend_schema(
27
- responses={200: TaskLogTimelineSerializer},
28
- summary="Task Execution Timeline",
29
- description="Get time-series data of task executions grouped by time intervals"
30
- )
31
- @action(detail=False, methods=['get'])
32
- def timeline(self, request):
33
- """
34
- Get task execution timeline grouped by time intervals.
35
-
36
- Returns timeline data wrapped in object with period/interval metadata.
37
-
38
- Query Parameters:
39
- period_hours (int): Timeline period in hours (default: 24)
40
- interval (str): Grouping interval - 'hour', 'day' (default: 'hour')
41
- task_name (str): Filter by specific task name
42
-
43
- Returns:
44
- {
45
- "period_hours": 24,
46
- "interval": "hour",
47
- "data": [
48
- {
49
- "timestamp": "2025-10-30T10:00:00Z",
50
- "total": 15,
51
- "successful": 14,
52
- "failed": 1,
53
- "avg_duration_ms": 1200
54
- },
55
- ...
56
- ]
57
- }
58
- """
59
- period_hours = int(request.query_params.get('period_hours', 24))
60
- interval = request.query_params.get('interval', 'hour')
61
- task_name = request.query_params.get('task_name')
62
-
63
- # Calculate time threshold
64
- time_threshold = timezone.now() - timedelta(hours=period_hours)
65
-
66
- # Base queryset
67
- queryset = TaskLog.objects.filter(created_at__gte=time_threshold)
68
- if task_name:
69
- queryset = queryset.filter(task_name=task_name)
70
-
71
- # TODO: Implement proper time-series grouping with SQL time bucketing
72
- # For now, return empty data array with correct structure
73
- timeline_data = {
74
- 'period_hours': period_hours,
75
- 'interval': interval,
76
- 'data': [] # Empty for now - will be implemented later
77
- }
78
-
79
- # Validate and serialize
80
- serializer = TaskLogTimelineSerializer(timeline_data)
81
- return Response(serializer.data)