django-cfg 1.4.107__py3-none-any.whl → 1.4.109__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.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/views/profile.py +19 -9
- django_cfg/apps/centrifugo/views/admin_api.py +4 -7
- django_cfg/apps/centrifugo/views/monitoring.py +3 -6
- django_cfg/apps/centrifugo/views/testing_api.py +3 -6
- django_cfg/apps/dashboard/services/system_health_service.py +16 -11
- django_cfg/apps/dashboard/views/activity_views.py +3 -5
- django_cfg/apps/dashboard/views/apizones_views.py +4 -5
- django_cfg/apps/dashboard/views/charts_views.py +4 -5
- django_cfg/apps/dashboard/views/overview_views.py +4 -5
- django_cfg/apps/dashboard/views/statistics_views.py +4 -5
- django_cfg/apps/dashboard/views/system_views.py +4 -5
- django_cfg/apps/knowbase/__init__.py +2 -2
- django_cfg/apps/knowbase/apps.py +2 -8
- django_cfg/apps/knowbase/views/base.py +9 -4
- django_cfg/apps/support/views/api.py +16 -7
- django_cfg/apps/tasks/__init__.py +61 -2
- django_cfg/apps/tasks/admin/__init__.py +3 -10
- django_cfg/apps/tasks/admin/config.py +98 -0
- django_cfg/apps/tasks/admin/task_log.py +265 -0
- django_cfg/apps/tasks/apps.py +7 -9
- django_cfg/apps/tasks/filters/__init__.py +10 -0
- django_cfg/apps/tasks/filters/task_log.py +121 -0
- django_cfg/apps/tasks/migrations/0001_initial.py +196 -0
- django_cfg/apps/tasks/models/__init__.py +4 -0
- django_cfg/apps/tasks/models/task_log.py +246 -0
- django_cfg/apps/tasks/serializers/__init__.py +28 -0
- django_cfg/apps/tasks/serializers/task_log.py +249 -0
- django_cfg/apps/tasks/services/__init__.py +10 -0
- django_cfg/apps/tasks/services/client/__init__.py +7 -0
- django_cfg/apps/tasks/services/client/client.py +234 -0
- django_cfg/apps/tasks/services/config_helper.py +63 -0
- django_cfg/apps/tasks/services/sync.py +204 -0
- django_cfg/apps/tasks/urls.py +7 -13
- django_cfg/apps/tasks/views/__init__.py +4 -10
- django_cfg/apps/tasks/views/task_log.py +41 -0
- django_cfg/apps/tasks/views/task_log_base.py +41 -0
- django_cfg/apps/tasks/views/task_log_overview.py +100 -0
- django_cfg/apps/tasks/views/task_log_related.py +41 -0
- django_cfg/apps/tasks/views/task_log_stats.py +91 -0
- django_cfg/apps/tasks/views/task_log_timeline.py +81 -0
- django_cfg/apps/urls.py +0 -1
- django_cfg/cli/commands/info.py +1 -1
- django_cfg/cli/utils.py +1 -1
- django_cfg/core/base/config_model.py +1 -1
- django_cfg/core/builders/apps_builder.py +1 -1
- django_cfg/core/generation/integration_generators/__init__.py +1 -1
- django_cfg/core/generation/integration_generators/tasks.py +14 -18
- django_cfg/core/generation/security_generators/crypto_fields.py +2 -1
- django_cfg/core/integration/display/startup.py +1 -1
- django_cfg/mixins/__init__.py +12 -0
- django_cfg/mixins/admin_api.py +37 -0
- django_cfg/mixins/client_api.py +39 -0
- django_cfg/models/django/constance.py +2 -8
- django_cfg/models/django/crypto_fields.py +13 -48
- django_cfg/models/tasks/__init__.py +8 -10
- django_cfg/models/tasks/backends.py +76 -207
- django_cfg/models/tasks/config.py +20 -127
- django_cfg/models/tasks/utils.py +17 -29
- django_cfg/modules/django_client/management/commands/generate_client.py +13 -1
- django_cfg/modules/django_unfold/navigation.py +121 -22
- django_cfg/pyproject.toml +2 -2
- django_cfg/registry/core.py +1 -1
- django_cfg/static/frontend/admin.zip +0 -0
- {django_cfg-1.4.107.dist-info → django_cfg-1.4.109.dist-info}/METADATA +3 -3
- {django_cfg-1.4.107.dist-info → django_cfg-1.4.109.dist-info}/RECORD +70 -117
- django_cfg/apps/tasks/admin/actions.py +0 -29
- django_cfg/apps/tasks/admin/tasks_admin.py +0 -154
- django_cfg/apps/tasks/api/serializers.py +0 -82
- django_cfg/apps/tasks/api/views.py +0 -571
- django_cfg/apps/tasks/serializers.py +0 -82
- django_cfg/apps/tasks/static/tasks/css/dashboard-alpine.css +0 -299
- django_cfg/apps/tasks/static/tasks/css/dashboard.css +0 -120
- django_cfg/apps/tasks/static/tasks/js/alpine/README.md +0 -47
- django_cfg/apps/tasks/static/tasks/js/alpine/actions/index.js +0 -8
- django_cfg/apps/tasks/static/tasks/js/alpine/actions/management.js +0 -123
- django_cfg/apps/tasks/static/tasks/js/alpine/actions/pagination.js +0 -21
- django_cfg/apps/tasks/static/tasks/js/alpine/actions/tasks.js +0 -101
- django_cfg/apps/tasks/static/tasks/js/alpine/actions/workers.js +0 -59
- django_cfg/apps/tasks/static/tasks/js/alpine/computed.js +0 -35
- django_cfg/apps/tasks/static/tasks/js/alpine/index.js +0 -148
- django_cfg/apps/tasks/static/tasks/js/alpine/loaders/index.js +0 -36
- django_cfg/apps/tasks/static/tasks/js/alpine/loaders/overview.js +0 -37
- django_cfg/apps/tasks/static/tasks/js/alpine/loaders/queues.js +0 -27
- django_cfg/apps/tasks/static/tasks/js/alpine/loaders/tasks.js +0 -32
- django_cfg/apps/tasks/static/tasks/js/alpine/loaders/workers.js +0 -21
- django_cfg/apps/tasks/static/tasks/js/alpine/state.js +0 -36
- django_cfg/apps/tasks/static/tasks/js/alpine/utils/formatters.js +0 -42
- django_cfg/apps/tasks/static/tasks/js/alpine/utils/helpers.js +0 -68
- django_cfg/apps/tasks/static/tasks/js/dashboard-alpine.js +0 -725
- django_cfg/apps/tasks/tasks/__init__.py +0 -10
- django_cfg/apps/tasks/tasks/demo_tasks.py +0 -127
- django_cfg/apps/tasks/templates/tasks/components/management_actions.html +0 -71
- django_cfg/apps/tasks/templates/tasks/components/overview_content.html +0 -94
- django_cfg/apps/tasks/templates/tasks/components/queues_content.html +0 -44
- django_cfg/apps/tasks/templates/tasks/components/tab_navigation.html +0 -45
- django_cfg/apps/tasks/templates/tasks/components/task_details_modal.html +0 -151
- django_cfg/apps/tasks/templates/tasks/components/tasks_content.html +0 -61
- django_cfg/apps/tasks/templates/tasks/components/tasks_mjs_integration.html +0 -269
- django_cfg/apps/tasks/templates/tasks/components/workers_content.html +0 -60
- django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -20
- django_cfg/apps/tasks/templates/tasks/pages/dashboard-improved.html +0 -168
- django_cfg/apps/tasks/templates/tasks/pages/dashboard.html +0 -77
- django_cfg/apps/tasks/templates/tasks/partials/task_row_template.html +0 -40
- django_cfg/apps/tasks/templates/tasks/widgets/task_filters.html +0 -40
- django_cfg/apps/tasks/templates/tasks/widgets/task_footer.html +0 -86
- django_cfg/apps/tasks/templates/tasks/widgets/task_table.html +0 -90
- django_cfg/apps/tasks/urls_admin.py +0 -15
- django_cfg/apps/tasks/utils/__init__.py +0 -1
- django_cfg/apps/tasks/utils/simulator.py +0 -353
- django_cfg/apps/tasks/views/api.py +0 -571
- django_cfg/apps/tasks/views/dashboard.py +0 -89
- django_cfg/management/commands/rundramatiq.py +0 -24
- django_cfg/management/commands/rundramatiq_simulator.py +0 -22
- django_cfg/management/commands/task_clear.py +0 -25
- django_cfg/management/commands/task_status.py +0 -24
- django_cfg/modules/django_client/system/__init__.py +0 -24
- django_cfg/modules/django_client/system/base_generator.py +0 -123
- django_cfg/modules/django_client/system/generate_mjs_clients.py +0 -176
- django_cfg/modules/django_client/system/mjs_generator.py +0 -219
- django_cfg/modules/django_client/system/schema_parser.py +0 -199
- django_cfg/modules/django_client/system/templates/api_client.js.j2 +0 -87
- django_cfg/modules/django_client/system/templates/app_index.js.j2 +0 -13
- django_cfg/modules/django_client/system/templates/base_client.js.j2 +0 -166
- django_cfg/modules/django_client/system/templates/main_index.js.j2 +0 -80
- django_cfg/modules/django_client/system/templates/types.js.j2 +0 -24
- django_cfg/modules/django_tasks/__init__.py +0 -29
- django_cfg/modules/django_tasks/dramatiq_setup.py +0 -20
- django_cfg/modules/django_tasks/factory.py +0 -127
- django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq.py +0 -253
- django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +0 -436
- django_cfg/modules/django_tasks/management/commands/task_clear.py +0 -226
- django_cfg/modules/django_tasks/management/commands/task_status.py +0 -257
- django_cfg/modules/django_tasks/service.py +0 -281
- django_cfg/modules/django_tasks/settings.py +0 -107
- /django_cfg/{modules/django_tasks/management → apps/tasks/migrations}/__init__.py +0 -0
- {django_cfg-1.4.107.dist-info → django_cfg-1.4.109.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.107.dist-info → django_cfg-1.4.109.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.107.dist-info → django_cfg-1.4.109.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,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
|
+
]
|