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.
@@ -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}"