django-cfg 1.4.107__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 (130) 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_client/management/commands/generate_client.py +13 -1
  61. django_cfg/modules/django_unfold/navigation.py +121 -22
  62. django_cfg/pyproject.toml +2 -2
  63. django_cfg/registry/core.py +1 -1
  64. django_cfg/static/frontend/admin.zip +0 -0
  65. {django_cfg-1.4.107.dist-info → django_cfg-1.4.108.dist-info}/METADATA +3 -3
  66. {django_cfg-1.4.107.dist-info → django_cfg-1.4.108.dist-info}/RECORD +70 -107
  67. django_cfg/apps/tasks/admin/actions.py +0 -29
  68. django_cfg/apps/tasks/admin/tasks_admin.py +0 -154
  69. django_cfg/apps/tasks/api/serializers.py +0 -82
  70. django_cfg/apps/tasks/api/views.py +0 -571
  71. django_cfg/apps/tasks/serializers.py +0 -82
  72. django_cfg/apps/tasks/static/tasks/css/dashboard-alpine.css +0 -299
  73. django_cfg/apps/tasks/static/tasks/css/dashboard.css +0 -120
  74. django_cfg/apps/tasks/static/tasks/js/alpine/README.md +0 -47
  75. django_cfg/apps/tasks/static/tasks/js/alpine/actions/index.js +0 -8
  76. django_cfg/apps/tasks/static/tasks/js/alpine/actions/management.js +0 -123
  77. django_cfg/apps/tasks/static/tasks/js/alpine/actions/pagination.js +0 -21
  78. django_cfg/apps/tasks/static/tasks/js/alpine/actions/tasks.js +0 -101
  79. django_cfg/apps/tasks/static/tasks/js/alpine/actions/workers.js +0 -59
  80. django_cfg/apps/tasks/static/tasks/js/alpine/computed.js +0 -35
  81. django_cfg/apps/tasks/static/tasks/js/alpine/index.js +0 -148
  82. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/index.js +0 -36
  83. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/overview.js +0 -37
  84. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/queues.js +0 -27
  85. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/tasks.js +0 -32
  86. django_cfg/apps/tasks/static/tasks/js/alpine/loaders/workers.js +0 -21
  87. django_cfg/apps/tasks/static/tasks/js/alpine/state.js +0 -36
  88. django_cfg/apps/tasks/static/tasks/js/alpine/utils/formatters.js +0 -42
  89. django_cfg/apps/tasks/static/tasks/js/alpine/utils/helpers.js +0 -68
  90. django_cfg/apps/tasks/static/tasks/js/dashboard-alpine.js +0 -725
  91. django_cfg/apps/tasks/tasks/__init__.py +0 -10
  92. django_cfg/apps/tasks/tasks/demo_tasks.py +0 -127
  93. django_cfg/apps/tasks/templates/tasks/components/management_actions.html +0 -71
  94. django_cfg/apps/tasks/templates/tasks/components/overview_content.html +0 -94
  95. django_cfg/apps/tasks/templates/tasks/components/queues_content.html +0 -44
  96. django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +0 -45
  97. django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -151
  98. django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +0 -61
  99. django_cfg/apps/tasks/templates/tasks/components/tasks_mjs_integration.html +0 -269
  100. django_cfg/apps/tasks/templates/tasks/components/workers_content.html +0 -60
  101. django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -20
  102. django_cfg/apps/tasks/templates/tasks/pages/dashboard-improved.html +0 -168
  103. django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +0 -77
  104. django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +0 -40
  105. django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +0 -40
  106. django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +0 -86
  107. django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +0 -90
  108. django_cfg/apps/tasks/urls_admin.py +0 -15
  109. django_cfg/apps/tasks/utils/__init__.py +0 -1
  110. django_cfg/apps/tasks/utils/simulator.py +0 -353
  111. django_cfg/apps/tasks/views/api.py +0 -571
  112. django_cfg/apps/tasks/views/dashboard.py +0 -89
  113. django_cfg/management/commands/rundramatiq.py +0 -24
  114. django_cfg/management/commands/rundramatiq_simulator.py +0 -22
  115. django_cfg/management/commands/task_clear.py +0 -25
  116. django_cfg/management/commands/task_status.py +0 -24
  117. django_cfg/modules/django_tasks/__init__.py +0 -29
  118. django_cfg/modules/django_tasks/dramatiq_setup.py +0 -20
  119. django_cfg/modules/django_tasks/factory.py +0 -127
  120. django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
  121. django_cfg/modules/django_tasks/management/commands/rundramatiq.py +0 -253
  122. django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +0 -436
  123. django_cfg/modules/django_tasks/management/commands/task_clear.py +0 -226
  124. django_cfg/modules/django_tasks/management/commands/task_status.py +0 -257
  125. django_cfg/modules/django_tasks/service.py +0 -281
  126. django_cfg/modules/django_tasks/settings.py +0 -107
  127. /django_cfg/{modules/django_tasks/management → apps/tasks/migrations}/__init__.py +0 -0
  128. {django_cfg-1.4.107.dist-info → django_cfg-1.4.108.dist-info}/WHEEL +0 -0
  129. {django_cfg-1.4.107.dist-info → django_cfg-1.4.108.dist-info}/entry_points.txt +0 -0
  130. {django_cfg-1.4.107.dist-info → django_cfg-1.4.108.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,196 @@
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
+ ]
@@ -0,0 +1,4 @@
1
+ """Task models."""
2
+ from .task_log import TaskLog
3
+
4
+ __all__ = ["TaskLog"]
@@ -0,0 +1,246 @@
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"])
@@ -0,0 +1,28 @@
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
+ ]