django-database-task 0.1.0__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.
- django_database_task/__init__.py +27 -0
- django_database_task/admin.py +211 -0
- django_database_task/apps.py +7 -0
- django_database_task/backends.py +211 -0
- django_database_task/executor.py +181 -0
- django_database_task/locale/ja/LC_MESSAGES/django.mo +0 -0
- django_database_task/locale/ja/LC_MESSAGES/django.po +170 -0
- django_database_task/management/__init__.py +0 -0
- django_database_task/management/commands/__init__.py +0 -0
- django_database_task/management/commands/purge_completed_database_tasks.py +88 -0
- django_database_task/management/commands/run_database_tasks.py +98 -0
- django_database_task/migrations/0001_initial.py +190 -0
- django_database_task/migrations/__init__.py +0 -0
- django_database_task/models.py +120 -0
- django_database_task/urls.py +67 -0
- django_database_task/views.py +205 -0
- django_database_task-0.1.0.dist-info/METADATA +506 -0
- django_database_task-0.1.0.dist-info/RECORD +21 -0
- django_database_task-0.1.0.dist-info/WHEEL +5 -0
- django_database_task-0.1.0.dist-info/licenses/LICENSE +21 -0
- django_database_task-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Japanese translations for django-database-task.
|
|
2
|
+
# Copyright (C) 2025 Shinya Okano
|
|
3
|
+
# This file is distributed under the same license as the django-database-task package.
|
|
4
|
+
# Shinya Okano <tokibito@gmail.com>, 2025.
|
|
5
|
+
#
|
|
6
|
+
msgid ""
|
|
7
|
+
msgstr ""
|
|
8
|
+
"Project-Id-Version: django-database-task 0.1.0\n"
|
|
9
|
+
"Report-Msgid-Bugs-To: \n"
|
|
10
|
+
"POT-Creation-Date: 2025-12-27 07:13+0000\n"
|
|
11
|
+
"PO-Revision-Date: 2025-12-27 00:00+0000\n"
|
|
12
|
+
"Last-Translator: Shinya Okano <tokibito@gmail.com>\n"
|
|
13
|
+
"Language-Team: Japanese\n"
|
|
14
|
+
"Language: ja\n"
|
|
15
|
+
"MIME-Version: 1.0\n"
|
|
16
|
+
"Content-Type: text/plain; charset=UTF-8\n"
|
|
17
|
+
"Content-Transfer-Encoding: 8bit\n"
|
|
18
|
+
"Plural-Forms: nplurals=1; plural=0;\n"
|
|
19
|
+
|
|
20
|
+
#: django_database_task/admin.py:116
|
|
21
|
+
msgid "Run selected tasks"
|
|
22
|
+
msgstr "選択したタスクを実行"
|
|
23
|
+
|
|
24
|
+
#: django_database_task/admin.py:159
|
|
25
|
+
msgid "Retry failed tasks"
|
|
26
|
+
msgstr "失敗したタスクを再実行"
|
|
27
|
+
|
|
28
|
+
#: django_database_task/models.py:14
|
|
29
|
+
msgid "task path"
|
|
30
|
+
msgstr "タスクパス"
|
|
31
|
+
|
|
32
|
+
#: django_database_task/models.py:15
|
|
33
|
+
msgid "Module path of the task function"
|
|
34
|
+
msgstr "タスク関数のモジュールパス"
|
|
35
|
+
|
|
36
|
+
#: django_database_task/models.py:20
|
|
37
|
+
msgid "queue name"
|
|
38
|
+
msgstr "キュー名"
|
|
39
|
+
|
|
40
|
+
#: django_database_task/models.py:21
|
|
41
|
+
msgid "Queue name"
|
|
42
|
+
msgstr "キュー名"
|
|
43
|
+
|
|
44
|
+
#: django_database_task/models.py:25
|
|
45
|
+
msgid "priority"
|
|
46
|
+
msgstr "優先度"
|
|
47
|
+
|
|
48
|
+
#: django_database_task/models.py:26
|
|
49
|
+
msgid "Priority (-100 to 100)"
|
|
50
|
+
msgstr "優先度 (-100 から 100)"
|
|
51
|
+
|
|
52
|
+
#: django_database_task/models.py:30
|
|
53
|
+
msgid "arguments"
|
|
54
|
+
msgstr "引数"
|
|
55
|
+
|
|
56
|
+
#: django_database_task/models.py:31
|
|
57
|
+
msgid "Arguments (JSON format)"
|
|
58
|
+
msgstr "引数 (JSON形式)"
|
|
59
|
+
|
|
60
|
+
#: django_database_task/models.py:35
|
|
61
|
+
msgid "keyword arguments"
|
|
62
|
+
msgstr "キーワード引数"
|
|
63
|
+
|
|
64
|
+
#: django_database_task/models.py:36
|
|
65
|
+
msgid "Keyword arguments (JSON format)"
|
|
66
|
+
msgstr "キーワード引数 (JSON形式)"
|
|
67
|
+
|
|
68
|
+
#: django_database_task/models.py:42
|
|
69
|
+
msgid "status"
|
|
70
|
+
msgstr "ステータス"
|
|
71
|
+
|
|
72
|
+
#: django_database_task/models.py:43
|
|
73
|
+
msgid "Task status"
|
|
74
|
+
msgstr "タスクのステータス"
|
|
75
|
+
|
|
76
|
+
#: django_database_task/models.py:48
|
|
77
|
+
msgid "run after"
|
|
78
|
+
msgstr "実行開始日時"
|
|
79
|
+
|
|
80
|
+
#: django_database_task/models.py:49
|
|
81
|
+
msgid "Earliest execution time"
|
|
82
|
+
msgstr "最も早い実行日時"
|
|
83
|
+
|
|
84
|
+
#: django_database_task/models.py:52
|
|
85
|
+
msgid "enqueued at"
|
|
86
|
+
msgstr "キュー登録日時"
|
|
87
|
+
|
|
88
|
+
#: django_database_task/models.py:53
|
|
89
|
+
msgid "Time when task was enqueued"
|
|
90
|
+
msgstr "タスクがキューに登録された日時"
|
|
91
|
+
|
|
92
|
+
#: django_database_task/models.py:58
|
|
93
|
+
msgid "started at"
|
|
94
|
+
msgstr "開始日時"
|
|
95
|
+
|
|
96
|
+
#: django_database_task/models.py:59
|
|
97
|
+
msgid "Execution start time"
|
|
98
|
+
msgstr "実行開始日時"
|
|
99
|
+
|
|
100
|
+
#: django_database_task/models.py:64
|
|
101
|
+
msgid "finished at"
|
|
102
|
+
msgstr "終了日時"
|
|
103
|
+
|
|
104
|
+
#: django_database_task/models.py:65
|
|
105
|
+
msgid "Execution end time"
|
|
106
|
+
msgstr "実行終了日時"
|
|
107
|
+
|
|
108
|
+
#: django_database_task/models.py:70
|
|
109
|
+
msgid "last attempted at"
|
|
110
|
+
msgstr "最終試行日時"
|
|
111
|
+
|
|
112
|
+
#: django_database_task/models.py:71
|
|
113
|
+
msgid "Last attempt time"
|
|
114
|
+
msgstr "最終試行日時"
|
|
115
|
+
|
|
116
|
+
#: django_database_task/models.py:76
|
|
117
|
+
msgid "return value"
|
|
118
|
+
msgstr "戻り値"
|
|
119
|
+
|
|
120
|
+
#: django_database_task/models.py:77
|
|
121
|
+
msgid "Return value (JSON format)"
|
|
122
|
+
msgstr "戻り値 (JSON形式)"
|
|
123
|
+
|
|
124
|
+
#: django_database_task/models.py:81
|
|
125
|
+
msgid "errors"
|
|
126
|
+
msgstr "エラー"
|
|
127
|
+
|
|
128
|
+
#: django_database_task/models.py:82
|
|
129
|
+
msgid "Error information (JSON format)"
|
|
130
|
+
msgstr "エラー情報 (JSON形式)"
|
|
131
|
+
|
|
132
|
+
#: django_database_task/models.py:86
|
|
133
|
+
msgid "worker IDs"
|
|
134
|
+
msgstr "ワーカーID"
|
|
135
|
+
|
|
136
|
+
#: django_database_task/models.py:87
|
|
137
|
+
msgid "List of worker IDs (JSON format)"
|
|
138
|
+
msgstr "ワーカーIDのリスト (JSON形式)"
|
|
139
|
+
|
|
140
|
+
#: django_database_task/models.py:91
|
|
141
|
+
msgid "backend name"
|
|
142
|
+
msgstr "バックエンド名"
|
|
143
|
+
|
|
144
|
+
#: django_database_task/models.py:92
|
|
145
|
+
msgid "Backend name"
|
|
146
|
+
msgstr "バックエンド名"
|
|
147
|
+
|
|
148
|
+
#: django_database_task/models.py:96
|
|
149
|
+
msgid "created at"
|
|
150
|
+
msgstr "作成日時"
|
|
151
|
+
|
|
152
|
+
#: django_database_task/models.py:97
|
|
153
|
+
msgid "Record creation time"
|
|
154
|
+
msgstr "レコード作成日時"
|
|
155
|
+
|
|
156
|
+
#: django_database_task/models.py:101
|
|
157
|
+
msgid "updated at"
|
|
158
|
+
msgstr "更新日時"
|
|
159
|
+
|
|
160
|
+
#: django_database_task/models.py:102
|
|
161
|
+
msgid "Record update time"
|
|
162
|
+
msgstr "レコード更新日時"
|
|
163
|
+
|
|
164
|
+
#: django_database_task/models.py:116
|
|
165
|
+
msgid "database task"
|
|
166
|
+
msgstr "データベースタスク"
|
|
167
|
+
|
|
168
|
+
#: django_database_task/models.py:117
|
|
169
|
+
msgid "database tasks"
|
|
170
|
+
msgstr "データベースタスク"
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from datetime import timedelta
|
|
2
|
+
|
|
3
|
+
from django.core.management.base import BaseCommand
|
|
4
|
+
from django.tasks.base import TaskResultStatus
|
|
5
|
+
from django.utils import timezone
|
|
6
|
+
|
|
7
|
+
from django_database_task.models import DatabaseTask
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Command(BaseCommand):
|
|
11
|
+
help = "Delete completed task records from the database"
|
|
12
|
+
|
|
13
|
+
def add_arguments(self, parser):
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"--days",
|
|
16
|
+
type=int,
|
|
17
|
+
default=0,
|
|
18
|
+
help="Delete tasks completed more than N days ago (0=all, default: 0)",
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument(
|
|
21
|
+
"--status",
|
|
22
|
+
type=str,
|
|
23
|
+
default="SUCCESSFUL,FAILED",
|
|
24
|
+
help="Target statuses, comma-separated (default: SUCCESSFUL,FAILED)",
|
|
25
|
+
)
|
|
26
|
+
parser.add_argument(
|
|
27
|
+
"--batch-size",
|
|
28
|
+
type=int,
|
|
29
|
+
default=1000,
|
|
30
|
+
help="Number of tasks to delete at once (default: 1000)",
|
|
31
|
+
)
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--dry-run",
|
|
34
|
+
action="store_true",
|
|
35
|
+
help="Show count only without deleting",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def handle(self, *args, **options):
|
|
39
|
+
days = options["days"]
|
|
40
|
+
status_str = options["status"]
|
|
41
|
+
batch_size = options["batch_size"]
|
|
42
|
+
dry_run = options["dry_run"]
|
|
43
|
+
|
|
44
|
+
# Parse statuses
|
|
45
|
+
statuses = [s.strip().upper() for s in status_str.split(",")]
|
|
46
|
+
valid_statuses = [TaskResultStatus.SUCCESSFUL, TaskResultStatus.FAILED]
|
|
47
|
+
statuses = [s for s in statuses if s in [v.value for v in valid_statuses]]
|
|
48
|
+
|
|
49
|
+
if not statuses:
|
|
50
|
+
self.stdout.write(self.style.ERROR("No valid statuses specified"))
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
self.stdout.write(f"Target statuses: {', '.join(statuses)}")
|
|
54
|
+
|
|
55
|
+
# Build query
|
|
56
|
+
queryset = DatabaseTask.objects.filter(status__in=statuses)
|
|
57
|
+
|
|
58
|
+
if days > 0:
|
|
59
|
+
cutoff_date = timezone.now() - timedelta(days=days)
|
|
60
|
+
queryset = queryset.filter(finished_at__lt=cutoff_date)
|
|
61
|
+
self.stdout.write(f"Cutoff date: {cutoff_date}")
|
|
62
|
+
|
|
63
|
+
total_count = queryset.count()
|
|
64
|
+
self.stdout.write(f"Found {total_count} tasks to delete")
|
|
65
|
+
|
|
66
|
+
if dry_run:
|
|
67
|
+
self.stdout.write(self.style.WARNING("Dry run mode - no tasks deleted"))
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
if total_count == 0:
|
|
71
|
+
self.stdout.write("No tasks to delete")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
# Batch delete
|
|
75
|
+
deleted_total = 0
|
|
76
|
+
while True:
|
|
77
|
+
# Get batch of IDs and delete
|
|
78
|
+
task_ids = list(queryset.values_list("id", flat=True)[:batch_size])
|
|
79
|
+
if not task_ids:
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
deleted_count, _ = DatabaseTask.objects.filter(id__in=task_ids).delete()
|
|
83
|
+
deleted_total += deleted_count
|
|
84
|
+
self.stdout.write(f"Deleted {deleted_total}/{total_count} tasks...")
|
|
85
|
+
|
|
86
|
+
self.stdout.write(
|
|
87
|
+
self.style.SUCCESS(f"Successfully deleted {deleted_total} tasks")
|
|
88
|
+
)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
import time
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from django.core.management.base import BaseCommand
|
|
6
|
+
from django.tasks import task_backends
|
|
7
|
+
from django.tasks.base import TaskResultStatus
|
|
8
|
+
|
|
9
|
+
from django_database_task.executor import fetch_task
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Command(BaseCommand):
|
|
13
|
+
help = "Execute tasks queued in the database"
|
|
14
|
+
|
|
15
|
+
def add_arguments(self, parser):
|
|
16
|
+
parser.add_argument(
|
|
17
|
+
"--queue",
|
|
18
|
+
type=str,
|
|
19
|
+
default=None,
|
|
20
|
+
help="Queue name to process (all queues if not specified)",
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"--backend",
|
|
24
|
+
type=str,
|
|
25
|
+
default="default",
|
|
26
|
+
help="Backend name (default: default)",
|
|
27
|
+
)
|
|
28
|
+
parser.add_argument(
|
|
29
|
+
"--continuous",
|
|
30
|
+
action="store_true",
|
|
31
|
+
help="Continuous mode (keep polling even when no tasks)",
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--interval",
|
|
35
|
+
type=float,
|
|
36
|
+
default=5.0,
|
|
37
|
+
help="Polling interval in seconds for continuous mode (default: 5)",
|
|
38
|
+
)
|
|
39
|
+
parser.add_argument(
|
|
40
|
+
"--max-tasks",
|
|
41
|
+
type=int,
|
|
42
|
+
default=0,
|
|
43
|
+
help="Maximum number of tasks to process (0=unlimited, default: 0)",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
def handle(self, *args, **options):
|
|
47
|
+
queue_name = options["queue"]
|
|
48
|
+
backend_name = options["backend"]
|
|
49
|
+
continuous = options["continuous"]
|
|
50
|
+
interval = options["interval"]
|
|
51
|
+
max_tasks = options["max_tasks"]
|
|
52
|
+
|
|
53
|
+
worker_id = f"{socket.gethostname()}-{uuid.uuid4().hex[:8]}"
|
|
54
|
+
self.stdout.write(f"Worker ID: {worker_id}")
|
|
55
|
+
self.stdout.write(f"Backend: {backend_name}")
|
|
56
|
+
if queue_name:
|
|
57
|
+
self.stdout.write(f"Queue: {queue_name}")
|
|
58
|
+
if continuous:
|
|
59
|
+
self.stdout.write(f"Continuous mode: interval={interval}s")
|
|
60
|
+
if max_tasks:
|
|
61
|
+
self.stdout.write(f"Max tasks: {max_tasks}")
|
|
62
|
+
|
|
63
|
+
backend = task_backends[backend_name]
|
|
64
|
+
tasks_processed = 0
|
|
65
|
+
|
|
66
|
+
while True:
|
|
67
|
+
task = fetch_task(queue_name=queue_name, backend_name=backend_name)
|
|
68
|
+
|
|
69
|
+
if task is None:
|
|
70
|
+
if continuous:
|
|
71
|
+
self.stdout.write(".", ending="")
|
|
72
|
+
self.stdout.flush()
|
|
73
|
+
time.sleep(interval)
|
|
74
|
+
continue
|
|
75
|
+
else:
|
|
76
|
+
self.stdout.write("No more tasks to process.")
|
|
77
|
+
break
|
|
78
|
+
|
|
79
|
+
self.stdout.write(f"\nProcessing task: {task.id} ({task.task_path})")
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
result = backend.run_task(task, worker_id=worker_id)
|
|
83
|
+
if result.status == TaskResultStatus.SUCCESSFUL:
|
|
84
|
+
self.stdout.write(
|
|
85
|
+
self.style.SUCCESS(" Task completed successfully")
|
|
86
|
+
)
|
|
87
|
+
else:
|
|
88
|
+
self.stdout.write(self.style.ERROR(" Task failed"))
|
|
89
|
+
except Exception as e:
|
|
90
|
+
self.stdout.write(self.style.ERROR(f" Error running task: {e}"))
|
|
91
|
+
|
|
92
|
+
tasks_processed += 1
|
|
93
|
+
|
|
94
|
+
if max_tasks and tasks_processed >= max_tasks:
|
|
95
|
+
self.stdout.write(f"\nReached max tasks limit: {max_tasks}")
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
self.stdout.write(f"\nTotal tasks processed: {tasks_processed}")
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Generated by Django 6.0 on 2025-12-27 06:51
|
|
2
|
+
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from django.db import migrations, models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
initial = True
|
|
10
|
+
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.CreateModel(
|
|
15
|
+
name="DatabaseTask",
|
|
16
|
+
fields=[
|
|
17
|
+
(
|
|
18
|
+
"id",
|
|
19
|
+
models.UUIDField(
|
|
20
|
+
default=uuid.uuid4,
|
|
21
|
+
editable=False,
|
|
22
|
+
primary_key=True,
|
|
23
|
+
serialize=False,
|
|
24
|
+
),
|
|
25
|
+
),
|
|
26
|
+
(
|
|
27
|
+
"task_path",
|
|
28
|
+
models.CharField(
|
|
29
|
+
help_text="Module path of the task function",
|
|
30
|
+
max_length=512,
|
|
31
|
+
verbose_name="task path",
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
(
|
|
35
|
+
"queue_name",
|
|
36
|
+
models.CharField(
|
|
37
|
+
default="default",
|
|
38
|
+
help_text="Queue name",
|
|
39
|
+
max_length=255,
|
|
40
|
+
verbose_name="queue name",
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
(
|
|
44
|
+
"priority",
|
|
45
|
+
models.IntegerField(
|
|
46
|
+
default=0,
|
|
47
|
+
help_text="Priority (-100 to 100)",
|
|
48
|
+
verbose_name="priority",
|
|
49
|
+
),
|
|
50
|
+
),
|
|
51
|
+
(
|
|
52
|
+
"args_json",
|
|
53
|
+
models.JSONField(
|
|
54
|
+
default=list,
|
|
55
|
+
help_text="Arguments (JSON format)",
|
|
56
|
+
verbose_name="arguments",
|
|
57
|
+
),
|
|
58
|
+
),
|
|
59
|
+
(
|
|
60
|
+
"kwargs_json",
|
|
61
|
+
models.JSONField(
|
|
62
|
+
default=dict,
|
|
63
|
+
help_text="Keyword arguments (JSON format)",
|
|
64
|
+
verbose_name="keyword arguments",
|
|
65
|
+
),
|
|
66
|
+
),
|
|
67
|
+
(
|
|
68
|
+
"status",
|
|
69
|
+
models.CharField(
|
|
70
|
+
choices=[
|
|
71
|
+
("READY", "Ready"),
|
|
72
|
+
("RUNNING", "Running"),
|
|
73
|
+
("FAILED", "Failed"),
|
|
74
|
+
("SUCCESSFUL", "Successful"),
|
|
75
|
+
],
|
|
76
|
+
default="READY",
|
|
77
|
+
help_text="Task status",
|
|
78
|
+
max_length=20,
|
|
79
|
+
verbose_name="status",
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
(
|
|
83
|
+
"run_after",
|
|
84
|
+
models.DateTimeField(
|
|
85
|
+
blank=True,
|
|
86
|
+
help_text="Earliest execution time",
|
|
87
|
+
null=True,
|
|
88
|
+
verbose_name="run after",
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
(
|
|
92
|
+
"enqueued_at",
|
|
93
|
+
models.DateTimeField(
|
|
94
|
+
help_text="Time when task was enqueued",
|
|
95
|
+
verbose_name="enqueued at",
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
(
|
|
99
|
+
"started_at",
|
|
100
|
+
models.DateTimeField(
|
|
101
|
+
blank=True,
|
|
102
|
+
help_text="Execution start time",
|
|
103
|
+
null=True,
|
|
104
|
+
verbose_name="started at",
|
|
105
|
+
),
|
|
106
|
+
),
|
|
107
|
+
(
|
|
108
|
+
"finished_at",
|
|
109
|
+
models.DateTimeField(
|
|
110
|
+
blank=True,
|
|
111
|
+
help_text="Execution end time",
|
|
112
|
+
null=True,
|
|
113
|
+
verbose_name="finished at",
|
|
114
|
+
),
|
|
115
|
+
),
|
|
116
|
+
(
|
|
117
|
+
"last_attempted_at",
|
|
118
|
+
models.DateTimeField(
|
|
119
|
+
blank=True,
|
|
120
|
+
help_text="Last attempt time",
|
|
121
|
+
null=True,
|
|
122
|
+
verbose_name="last attempted at",
|
|
123
|
+
),
|
|
124
|
+
),
|
|
125
|
+
(
|
|
126
|
+
"return_value_json",
|
|
127
|
+
models.JSONField(
|
|
128
|
+
blank=True,
|
|
129
|
+
help_text="Return value (JSON format)",
|
|
130
|
+
null=True,
|
|
131
|
+
verbose_name="return value",
|
|
132
|
+
),
|
|
133
|
+
),
|
|
134
|
+
(
|
|
135
|
+
"errors_json",
|
|
136
|
+
models.JSONField(
|
|
137
|
+
default=list,
|
|
138
|
+
help_text="Error information (JSON format)",
|
|
139
|
+
verbose_name="errors",
|
|
140
|
+
),
|
|
141
|
+
),
|
|
142
|
+
(
|
|
143
|
+
"worker_ids_json",
|
|
144
|
+
models.JSONField(
|
|
145
|
+
default=list,
|
|
146
|
+
help_text="List of worker IDs (JSON format)",
|
|
147
|
+
verbose_name="worker IDs",
|
|
148
|
+
),
|
|
149
|
+
),
|
|
150
|
+
(
|
|
151
|
+
"backend_name",
|
|
152
|
+
models.CharField(
|
|
153
|
+
help_text="Backend name",
|
|
154
|
+
max_length=255,
|
|
155
|
+
verbose_name="backend name",
|
|
156
|
+
),
|
|
157
|
+
),
|
|
158
|
+
(
|
|
159
|
+
"created_at",
|
|
160
|
+
models.DateTimeField(
|
|
161
|
+
auto_now_add=True,
|
|
162
|
+
help_text="Record creation time",
|
|
163
|
+
verbose_name="created at",
|
|
164
|
+
),
|
|
165
|
+
),
|
|
166
|
+
(
|
|
167
|
+
"updated_at",
|
|
168
|
+
models.DateTimeField(
|
|
169
|
+
auto_now=True,
|
|
170
|
+
help_text="Record update time",
|
|
171
|
+
verbose_name="updated at",
|
|
172
|
+
),
|
|
173
|
+
),
|
|
174
|
+
],
|
|
175
|
+
options={
|
|
176
|
+
"verbose_name": "database task",
|
|
177
|
+
"verbose_name_plural": "database tasks",
|
|
178
|
+
"ordering": ["-priority", "enqueued_at"],
|
|
179
|
+
"indexes": [
|
|
180
|
+
models.Index(
|
|
181
|
+
fields=["status", "run_after", "priority"],
|
|
182
|
+
name="ddt_status_run_priority_idx",
|
|
183
|
+
),
|
|
184
|
+
models.Index(fields=["queue_name"], name="ddt_queue_name_idx"),
|
|
185
|
+
models.Index(fields=["status"], name="ddt_status_idx"),
|
|
186
|
+
models.Index(fields=["created_at"], name="ddt_created_at_idx"),
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
),
|
|
190
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
|
|
3
|
+
from django.db import models
|
|
4
|
+
from django.tasks.base import TaskResultStatus
|
|
5
|
+
from django.utils.translation import gettext_lazy as _
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DatabaseTask(models.Model):
|
|
9
|
+
"""Task model persisted in the database."""
|
|
10
|
+
|
|
11
|
+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
12
|
+
task_path = models.CharField(
|
|
13
|
+
max_length=512,
|
|
14
|
+
verbose_name=_("task path"),
|
|
15
|
+
help_text=_("Module path of the task function"),
|
|
16
|
+
)
|
|
17
|
+
queue_name = models.CharField(
|
|
18
|
+
max_length=255,
|
|
19
|
+
default="default",
|
|
20
|
+
verbose_name=_("queue name"),
|
|
21
|
+
help_text=_("Queue name"),
|
|
22
|
+
)
|
|
23
|
+
priority = models.IntegerField(
|
|
24
|
+
default=0,
|
|
25
|
+
verbose_name=_("priority"),
|
|
26
|
+
help_text=_("Priority (-100 to 100)"),
|
|
27
|
+
)
|
|
28
|
+
args_json = models.JSONField(
|
|
29
|
+
default=list,
|
|
30
|
+
verbose_name=_("arguments"),
|
|
31
|
+
help_text=_("Arguments (JSON format)"),
|
|
32
|
+
)
|
|
33
|
+
kwargs_json = models.JSONField(
|
|
34
|
+
default=dict,
|
|
35
|
+
verbose_name=_("keyword arguments"),
|
|
36
|
+
help_text=_("Keyword arguments (JSON format)"),
|
|
37
|
+
)
|
|
38
|
+
status = models.CharField(
|
|
39
|
+
max_length=20,
|
|
40
|
+
choices=TaskResultStatus.choices,
|
|
41
|
+
default=TaskResultStatus.READY,
|
|
42
|
+
verbose_name=_("status"),
|
|
43
|
+
help_text=_("Task status"),
|
|
44
|
+
)
|
|
45
|
+
run_after = models.DateTimeField(
|
|
46
|
+
null=True,
|
|
47
|
+
blank=True,
|
|
48
|
+
verbose_name=_("run after"),
|
|
49
|
+
help_text=_("Earliest execution time"),
|
|
50
|
+
)
|
|
51
|
+
enqueued_at = models.DateTimeField(
|
|
52
|
+
verbose_name=_("enqueued at"),
|
|
53
|
+
help_text=_("Time when task was enqueued"),
|
|
54
|
+
)
|
|
55
|
+
started_at = models.DateTimeField(
|
|
56
|
+
null=True,
|
|
57
|
+
blank=True,
|
|
58
|
+
verbose_name=_("started at"),
|
|
59
|
+
help_text=_("Execution start time"),
|
|
60
|
+
)
|
|
61
|
+
finished_at = models.DateTimeField(
|
|
62
|
+
null=True,
|
|
63
|
+
blank=True,
|
|
64
|
+
verbose_name=_("finished at"),
|
|
65
|
+
help_text=_("Execution end time"),
|
|
66
|
+
)
|
|
67
|
+
last_attempted_at = models.DateTimeField(
|
|
68
|
+
null=True,
|
|
69
|
+
blank=True,
|
|
70
|
+
verbose_name=_("last attempted at"),
|
|
71
|
+
help_text=_("Last attempt time"),
|
|
72
|
+
)
|
|
73
|
+
return_value_json = models.JSONField(
|
|
74
|
+
null=True,
|
|
75
|
+
blank=True,
|
|
76
|
+
verbose_name=_("return value"),
|
|
77
|
+
help_text=_("Return value (JSON format)"),
|
|
78
|
+
)
|
|
79
|
+
errors_json = models.JSONField(
|
|
80
|
+
default=list,
|
|
81
|
+
verbose_name=_("errors"),
|
|
82
|
+
help_text=_("Error information (JSON format)"),
|
|
83
|
+
)
|
|
84
|
+
worker_ids_json = models.JSONField(
|
|
85
|
+
default=list,
|
|
86
|
+
verbose_name=_("worker IDs"),
|
|
87
|
+
help_text=_("List of worker IDs (JSON format)"),
|
|
88
|
+
)
|
|
89
|
+
backend_name = models.CharField(
|
|
90
|
+
max_length=255,
|
|
91
|
+
verbose_name=_("backend name"),
|
|
92
|
+
help_text=_("Backend name"),
|
|
93
|
+
)
|
|
94
|
+
created_at = models.DateTimeField(
|
|
95
|
+
auto_now_add=True,
|
|
96
|
+
verbose_name=_("created at"),
|
|
97
|
+
help_text=_("Record creation time"),
|
|
98
|
+
)
|
|
99
|
+
updated_at = models.DateTimeField(
|
|
100
|
+
auto_now=True,
|
|
101
|
+
verbose_name=_("updated at"),
|
|
102
|
+
help_text=_("Record update time"),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
class Meta:
|
|
106
|
+
ordering = ["-priority", "enqueued_at"]
|
|
107
|
+
indexes = [
|
|
108
|
+
models.Index(
|
|
109
|
+
fields=["status", "run_after", "priority"],
|
|
110
|
+
name="ddt_status_run_priority_idx",
|
|
111
|
+
),
|
|
112
|
+
models.Index(fields=["queue_name"], name="ddt_queue_name_idx"),
|
|
113
|
+
models.Index(fields=["status"], name="ddt_status_idx"),
|
|
114
|
+
models.Index(fields=["created_at"], name="ddt_created_at_idx"),
|
|
115
|
+
]
|
|
116
|
+
verbose_name = _("database task")
|
|
117
|
+
verbose_name_plural = _("database tasks")
|
|
118
|
+
|
|
119
|
+
def __str__(self):
|
|
120
|
+
return f"{self.task_path} ({self.status}) - {self.id}"
|