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,121 +0,0 @@
1
- """
2
- TaskLog FilterSet for advanced filtering.
3
-
4
- Provides comprehensive filtering capabilities for task logs.
5
- """
6
- import django_filters
7
- from django.db.models import Q
8
-
9
- from ..models import TaskLog
10
-
11
-
12
- class TaskLogFilter(django_filters.FilterSet):
13
- """
14
- Advanced FilterSet for TaskLog.
15
-
16
- Matches ReArq API query parameters where possible.
17
-
18
- Provides filters for:
19
- - Task name (exact, contains)
20
- - Queue name (exact, in)
21
- - Status (exact, in) - matching ReArq JobStatus
22
- - Date ranges (enqueue, start, finish)
23
- - Duration ranges
24
- - Retry count ranges
25
- - Success/failure flags
26
- """
27
-
28
- # Task name filters (matching ReArq "task" param)
29
- task = django_filters.CharFilter(field_name='task_name', lookup_expr='exact')
30
- task_name = django_filters.CharFilter(lookup_expr='icontains')
31
- task_name_exact = django_filters.CharFilter(field_name='task_name', lookup_expr='exact')
32
-
33
- # Queue filters
34
- queue_name = django_filters.CharFilter(lookup_expr='exact')
35
- queue_name_in = django_filters.BaseInFilter(field_name='queue_name')
36
-
37
- # Status filters (matching ReArq "status" param)
38
- status = django_filters.CharFilter(lookup_expr='exact')
39
- status_in = django_filters.BaseInFilter(field_name='status')
40
-
41
- # Success filter (matching ReArq JobResult)
42
- success = django_filters.BooleanFilter()
43
-
44
- # Boolean flags (computed properties)
45
- is_completed = django_filters.BooleanFilter(method='filter_is_completed')
46
- is_successful = django_filters.BooleanFilter(method='filter_is_successful')
47
- is_failed = django_filters.BooleanFilter(method='filter_is_failed')
48
-
49
- # Date range filters (matching ReArq "start_time" / "end_time")
50
- start_time = django_filters.DateTimeFilter(field_name='enqueue_time', lookup_expr='gte')
51
- end_time = django_filters.DateTimeFilter(field_name='enqueue_time', lookup_expr='lte')
52
-
53
- # Additional date filters
54
- enqueue_after = django_filters.DateTimeFilter(field_name='enqueue_time', lookup_expr='gte')
55
- enqueue_before = django_filters.DateTimeFilter(field_name='enqueue_time', lookup_expr='lte')
56
-
57
- start_after = django_filters.DateTimeFilter(field_name='start_time', lookup_expr='gte')
58
- start_before = django_filters.DateTimeFilter(field_name='start_time', lookup_expr='lte')
59
-
60
- finish_after = django_filters.DateTimeFilter(field_name='finish_time', lookup_expr='gte')
61
- finish_before = django_filters.DateTimeFilter(field_name='finish_time', lookup_expr='lte')
62
-
63
- # Django-specific filters
64
- created_after = django_filters.DateTimeFilter(field_name='created_at', lookup_expr='gte')
65
- created_before = django_filters.DateTimeFilter(field_name='created_at', lookup_expr='lte')
66
-
67
- # Duration filters (milliseconds)
68
- duration_min = django_filters.NumberFilter(field_name='duration_ms', lookup_expr='gte')
69
- duration_max = django_filters.NumberFilter(field_name='duration_ms', lookup_expr='lte')
70
-
71
- # Retry filters (matching ReArq Job fields)
72
- job_retries_min = django_filters.NumberFilter(field_name='job_retries', lookup_expr='gte')
73
- job_retries_max = django_filters.NumberFilter(field_name='job_retries', lookup_expr='lte')
74
-
75
- # Job ID filter (matching ReArq "job_id" param)
76
- job_id = django_filters.CharFilter(lookup_expr='exact')
77
-
78
- # Worker filter (matching ReArq JobResult "worker" param)
79
- worker = django_filters.CharFilter(field_name='worker_id', lookup_expr='exact')
80
-
81
- # Error message search
82
- has_error = django_filters.BooleanFilter(method='filter_has_error')
83
-
84
- class Meta:
85
- model = TaskLog
86
- fields = [
87
- 'task_name',
88
- 'queue_name',
89
- 'status',
90
- 'success',
91
- 'job_id',
92
- 'worker_id',
93
- ]
94
-
95
- def filter_has_error(self, queryset, name, value):
96
- """Filter tasks with/without errors."""
97
- if value:
98
- return queryset.filter(error_message__isnull=False).exclude(error_message='')
99
- else:
100
- return queryset.filter(Q(error_message__isnull=True) | Q(error_message=''))
101
-
102
- def filter_is_completed(self, queryset, name, value):
103
- """Filter by completed status."""
104
- if value:
105
- return queryset.filter(status__in=['success', 'failed', 'expired', 'canceled'])
106
- else:
107
- return queryset.filter(status__in=['deferred', 'queued', 'in_progress'])
108
-
109
- def filter_is_successful(self, queryset, name, value):
110
- """Filter by successful completion."""
111
- if value:
112
- return queryset.filter(status='success', success=True)
113
- else:
114
- return queryset.exclude(status='success', success=True)
115
-
116
- def filter_is_failed(self, queryset, name, value):
117
- """Filter by failed status."""
118
- if value:
119
- return queryset.filter(Q(status__in=['failed', 'expired']) | Q(success=False))
120
- else:
121
- return queryset.exclude(Q(status__in=['failed', 'expired']) | Q(success=False))
@@ -1,196 +0,0 @@
1
- # Generated by Django 5.2.7 on 2025-10-30 11:15
2
-
3
- import django.db.models.deletion
4
- from django.conf import settings
5
- from django.db import migrations, models
6
-
7
-
8
- class Migration(migrations.Migration):
9
- initial = True
10
-
11
- dependencies = [
12
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13
- ]
14
-
15
- operations = [
16
- migrations.CreateModel(
17
- name="TaskLog",
18
- fields=[
19
- (
20
- "id",
21
- models.BigAutoField(
22
- auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
23
- ),
24
- ),
25
- (
26
- "job_id",
27
- models.CharField(
28
- db_index=True,
29
- help_text="Unique job identifier from ReArq",
30
- max_length=100,
31
- unique=True,
32
- ),
33
- ),
34
- (
35
- "task_name",
36
- models.CharField(
37
- db_index=True, help_text="Name of the task function", max_length=200
38
- ),
39
- ),
40
- (
41
- "queue_name",
42
- models.CharField(
43
- db_index=True, help_text="Queue where task was executed", max_length=100
44
- ),
45
- ),
46
- (
47
- "args",
48
- models.JSONField(default=list, help_text="Positional arguments passed to task"),
49
- ),
50
- (
51
- "kwargs",
52
- models.JSONField(default=dict, help_text="Keyword arguments passed to task"),
53
- ),
54
- (
55
- "status",
56
- models.CharField(
57
- choices=[
58
- ("deferred", "Deferred"),
59
- ("queued", "Queued"),
60
- ("in_progress", "In Progress"),
61
- ("success", "Success"),
62
- ("failed", "Failed"),
63
- ("expired", "Expired"),
64
- ("canceled", "Canceled"),
65
- ],
66
- db_index=True,
67
- default="queued",
68
- help_text="Current task status",
69
- max_length=20,
70
- ),
71
- ),
72
- (
73
- "duration_ms",
74
- models.IntegerField(
75
- blank=True, help_text="Task execution duration in milliseconds", null=True
76
- ),
77
- ),
78
- (
79
- "job_retry",
80
- models.IntegerField(
81
- default=0,
82
- help_text="Maximum number of retries allowed (from task definition)",
83
- ),
84
- ),
85
- (
86
- "job_retries",
87
- models.IntegerField(default=0, help_text="Number of retries performed so far"),
88
- ),
89
- (
90
- "job_retry_after",
91
- models.IntegerField(default=60, help_text="Seconds to wait before retry"),
92
- ),
93
- (
94
- "success",
95
- models.BooleanField(
96
- blank=True,
97
- help_text="Whether task completed successfully (null = not finished)",
98
- null=True,
99
- ),
100
- ),
101
- (
102
- "result",
103
- models.TextField(blank=True, help_text="Task result (JSON string)", null=True),
104
- ),
105
- (
106
- "error_message",
107
- models.TextField(
108
- blank=True, help_text="Error message if task failed", null=True
109
- ),
110
- ),
111
- (
112
- "worker_id",
113
- models.CharField(
114
- blank=True,
115
- help_text="ID of worker that processed the task",
116
- max_length=100,
117
- null=True,
118
- ),
119
- ),
120
- (
121
- "enqueue_time",
122
- models.DateTimeField(
123
- db_index=True,
124
- help_text="When job was enqueued to ReArq (from Job.enqueue_time)",
125
- ),
126
- ),
127
- (
128
- "expire_time",
129
- models.DateTimeField(
130
- blank=True,
131
- help_text="When job will expire (from Job.expire_time)",
132
- null=True,
133
- ),
134
- ),
135
- (
136
- "start_time",
137
- models.DateTimeField(
138
- blank=True,
139
- help_text="When task execution started (from JobResult.start_time)",
140
- null=True,
141
- ),
142
- ),
143
- (
144
- "finish_time",
145
- models.DateTimeField(
146
- blank=True,
147
- help_text="When task execution finished (from JobResult.finish_time)",
148
- null=True,
149
- ),
150
- ),
151
- (
152
- "created_at",
153
- models.DateTimeField(
154
- auto_now_add=True, help_text="When TaskLog record was created in Django DB"
155
- ),
156
- ),
157
- (
158
- "updated_at",
159
- models.DateTimeField(
160
- auto_now=True, help_text="When TaskLog record was last updated"
161
- ),
162
- ),
163
- (
164
- "user",
165
- models.ForeignKey(
166
- blank=True,
167
- help_text="User who triggered the task (if applicable)",
168
- null=True,
169
- on_delete=django.db.models.deletion.SET_NULL,
170
- to=settings.AUTH_USER_MODEL,
171
- ),
172
- ),
173
- ],
174
- options={
175
- "verbose_name": "Task Log",
176
- "verbose_name_plural": "Task Logs",
177
- "db_table": "django_cfg_task_log",
178
- "ordering": ["-enqueue_time"],
179
- "indexes": [
180
- models.Index(
181
- fields=["task_name", "-enqueue_time"], name="django_cfg__task_na_e84031_idx"
182
- ),
183
- models.Index(
184
- fields=["status", "-enqueue_time"], name="django_cfg__status_a59726_idx"
185
- ),
186
- models.Index(
187
- fields=["queue_name", "status"], name="django_cfg__queue_n_b8a67b_idx"
188
- ),
189
- models.Index(
190
- fields=["success", "-enqueue_time"], name="django_cfg__success_85e48e_idx"
191
- ),
192
- models.Index(fields=["-created_at"], name="django_cfg__created_4eea05_idx"),
193
- ],
194
- },
195
- ),
196
- ]
@@ -1,16 +0,0 @@
1
- # Generated by Django 5.2.7 on 2025-10-31 05:54
2
-
3
- from django.db import migrations
4
-
5
-
6
- class Migration(migrations.Migration):
7
-
8
- dependencies = [
9
- ("tasks", "0001_initial"),
10
- ]
11
-
12
- operations = [
13
- migrations.DeleteModel(
14
- name="TaskLog",
15
- ),
16
- ]
@@ -1,4 +0,0 @@
1
- """Task models."""
2
- from .task_log import TaskLog
3
-
4
- __all__ = ["TaskLog"]
@@ -1,246 +0,0 @@
1
- """
2
- Task execution log model.
3
-
4
- Stores history of task executions for monitoring and debugging.
5
- """
6
- from django.conf import settings
7
- from django.db import models
8
- from django.utils import timezone
9
-
10
-
11
- class TaskLog(models.Model):
12
- """
13
- Log of task executions.
14
-
15
- Stores execution history from ReArq task queue.
16
- Status values match ReArq's JobStatus enum.
17
- """
18
-
19
- class StatusChoices(models.TextChoices):
20
- """Task status matching ReArq JobStatus enum."""
21
- DEFERRED = "deferred", "Deferred"
22
- QUEUED = "queued", "Queued"
23
- IN_PROGRESS = "in_progress", "In Progress"
24
- SUCCESS = "success", "Success"
25
- FAILED = "failed", "Failed"
26
- EXPIRED = "expired", "Expired"
27
- CANCELED = "canceled", "Canceled"
28
-
29
- # Task identification
30
- job_id = models.CharField(
31
- max_length=100,
32
- unique=True,
33
- db_index=True,
34
- help_text="Unique job identifier from ReArq"
35
- )
36
- task_name = models.CharField(
37
- max_length=200,
38
- db_index=True,
39
- help_text="Name of the task function"
40
- )
41
- queue_name = models.CharField(
42
- max_length=100,
43
- db_index=True,
44
- help_text="Queue where task was executed"
45
- )
46
-
47
- # Arguments
48
- args = models.JSONField(
49
- default=list,
50
- help_text="Positional arguments passed to task"
51
- )
52
- kwargs = models.JSONField(
53
- default=dict,
54
- help_text="Keyword arguments passed to task"
55
- )
56
-
57
- # Status tracking
58
- status = models.CharField(
59
- max_length=20,
60
- choices=StatusChoices.choices,
61
- default=StatusChoices.QUEUED,
62
- db_index=True,
63
- help_text="Current task status"
64
- )
65
-
66
- # Performance metrics
67
- duration_ms = models.IntegerField(
68
- null=True,
69
- blank=True,
70
- help_text="Task execution duration in milliseconds"
71
- )
72
-
73
- # Retry configuration (from ReArq Job)
74
- job_retry = models.IntegerField(
75
- default=0,
76
- help_text="Maximum number of retries allowed (from task definition)"
77
- )
78
- job_retries = models.IntegerField(
79
- default=0,
80
- help_text="Number of retries performed so far"
81
- )
82
- job_retry_after = models.IntegerField(
83
- default=60,
84
- help_text="Seconds to wait before retry"
85
- )
86
-
87
- # Result tracking
88
- success = models.BooleanField(
89
- null=True,
90
- blank=True,
91
- help_text="Whether task completed successfully (null = not finished)"
92
- )
93
- result = models.TextField(
94
- null=True,
95
- blank=True,
96
- help_text="Task result (JSON string)"
97
- )
98
- error_message = models.TextField(
99
- null=True,
100
- blank=True,
101
- help_text="Error message if task failed"
102
- )
103
-
104
- # Worker information
105
- worker_id = models.CharField(
106
- max_length=100,
107
- null=True,
108
- blank=True,
109
- help_text="ID of worker that processed the task"
110
- )
111
-
112
- # Timestamps (matching ReArq Job fields)
113
- enqueue_time = models.DateTimeField(
114
- db_index=True,
115
- help_text="When job was enqueued to ReArq (from Job.enqueue_time)"
116
- )
117
- expire_time = models.DateTimeField(
118
- null=True,
119
- blank=True,
120
- help_text="When job will expire (from Job.expire_time)"
121
- )
122
- start_time = models.DateTimeField(
123
- null=True,
124
- blank=True,
125
- help_text="When task execution started (from JobResult.start_time)"
126
- )
127
- finish_time = models.DateTimeField(
128
- null=True,
129
- blank=True,
130
- help_text="When task execution finished (from JobResult.finish_time)"
131
- )
132
-
133
- # Django-specific timestamps
134
- created_at = models.DateTimeField(
135
- auto_now_add=True,
136
- help_text="When TaskLog record was created in Django DB"
137
- )
138
- updated_at = models.DateTimeField(
139
- auto_now=True,
140
- help_text="When TaskLog record was last updated"
141
- )
142
-
143
- # User tracking (optional)
144
- user = models.ForeignKey(
145
- settings.AUTH_USER_MODEL,
146
- on_delete=models.SET_NULL,
147
- null=True,
148
- blank=True,
149
- help_text="User who triggered the task (if applicable)"
150
- )
151
-
152
- class Meta:
153
- # app_label is derived from apps.py: django_cfg.apps.tasks -> last segment = "tasks"
154
- # To get "django_cfg_tasks" for consistent admin URLs, we explicitly set it:
155
- app_label = "django_cfg_tasks"
156
- db_table = "django_cfg_task_log"
157
- ordering = ["-enqueue_time"]
158
- indexes = [
159
- models.Index(fields=["task_name", "-enqueue_time"]),
160
- models.Index(fields=["status", "-enqueue_time"]),
161
- models.Index(fields=["queue_name", "status"]),
162
- models.Index(fields=["success", "-enqueue_time"]),
163
- models.Index(fields=["-created_at"]), # For Django admin
164
- ]
165
- verbose_name = "Task Log"
166
- verbose_name_plural = "Task Logs"
167
-
168
- def __str__(self) -> str:
169
- return f"{self.task_name} ({self.status})"
170
-
171
- @property
172
- def is_completed(self) -> bool:
173
- """Check if task is in a terminal state."""
174
- return self.status in [
175
- self.StatusChoices.SUCCESS,
176
- self.StatusChoices.FAILED,
177
- self.StatusChoices.EXPIRED,
178
- self.StatusChoices.CANCELED,
179
- ]
180
-
181
- @property
182
- def is_successful(self) -> bool:
183
- """Check if task completed successfully."""
184
- return self.status == self.StatusChoices.SUCCESS and self.success is True
185
-
186
- @property
187
- def is_failed(self) -> bool:
188
- """Check if task failed."""
189
- return self.status in [
190
- self.StatusChoices.FAILED,
191
- self.StatusChoices.EXPIRED,
192
- ] or self.success is False
193
-
194
- def mark_started(self, worker_id: str = None, start_time=None):
195
- """Mark task as started (from ReArq JobResult)."""
196
- self.status = self.StatusChoices.IN_PROGRESS
197
- self.start_time = start_time or timezone.now()
198
- if worker_id:
199
- self.worker_id = worker_id
200
- self.save(update_fields=["status", "start_time", "worker_id", "updated_at"])
201
-
202
- def mark_completed(self, result: str = None, finish_time=None):
203
- """Mark task as completed successfully (from ReArq JobResult)."""
204
- self.status = self.StatusChoices.SUCCESS
205
- self.success = True
206
- self.finish_time = finish_time or timezone.now()
207
- if result is not None:
208
- self.result = result
209
-
210
- # Calculate duration
211
- if self.start_time and self.finish_time:
212
- delta = self.finish_time - self.start_time
213
- self.duration_ms = int(delta.total_seconds() * 1000)
214
-
215
- self.save(update_fields=["status", "success", "finish_time", "result", "duration_ms", "updated_at"])
216
-
217
- def mark_failed(self, error_message: str, finish_time=None):
218
- """Mark task as failed with error message (from ReArq JobResult)."""
219
- self.status = self.StatusChoices.FAILED
220
- self.success = False
221
- self.error_message = error_message
222
- self.finish_time = finish_time or timezone.now()
223
-
224
- # Calculate duration
225
- if self.start_time and self.finish_time:
226
- delta = self.finish_time - self.start_time
227
- self.duration_ms = int(delta.total_seconds() * 1000)
228
-
229
- self.save(update_fields=["status", "success", "error_message", "finish_time", "duration_ms", "updated_at"])
230
-
231
- def mark_expired(self):
232
- """Mark task as expired (from ReArq Job.status)."""
233
- self.status = self.StatusChoices.EXPIRED
234
- self.success = False
235
- self.save(update_fields=["status", "success", "updated_at"])
236
-
237
- def mark_canceled(self):
238
- """Mark task as canceled (from ReArq Job.status)."""
239
- self.status = self.StatusChoices.CANCELED
240
- self.success = False
241
- self.save(update_fields=["status", "success", "updated_at"])
242
-
243
- def increment_retry(self):
244
- """Increment retry counter (from ReArq Job.job_retries)."""
245
- self.job_retries += 1
246
- self.save(update_fields=["job_retries", "updated_at"])
@@ -1,28 +0,0 @@
1
- """
2
- ReArq Tasks API Serializers.
3
-
4
- DRF serializers for TaskLog model and ReArq operations.
5
- """
6
- from .task_log import (
7
- TaskLogSerializer,
8
- TaskLogListSerializer,
9
- TaskLogDetailSerializer,
10
- TaskLogStatsSerializer,
11
- TasksByQueueSerializer,
12
- TasksByStatusSerializer,
13
- TaskLogOverviewSerializer,
14
- TaskLogTimelineItemSerializer,
15
- TaskLogTimelineSerializer,
16
- )
17
-
18
- __all__ = [
19
- "TaskLogSerializer",
20
- "TaskLogListSerializer",
21
- "TaskLogDetailSerializer",
22
- "TaskLogStatsSerializer",
23
- "TasksByQueueSerializer",
24
- "TasksByStatusSerializer",
25
- "TaskLogOverviewSerializer",
26
- "TaskLogTimelineItemSerializer",
27
- "TaskLogTimelineSerializer",
28
- ]