29 Commits

Author SHA1 Message Date
Stan
a699578c34 Release v1.10.0 2026-05-01 19:34:38 +02:00
Islam Asabaev
5975a2d081 feat: add ru locale and improve translations (#320)
* Add ru locale

* fix

* fix: remove duplicate

* Add field labels and continue ru locale

* fix: Add ru gettext for queue columns, short result column, and OrmQ field labels

* fix: locale paths in translation files

* chore: format code with ruff

* chore: format code with ruff

* fix: correct time_taken usage in admin

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update django_q/locale/de/LC_MESSAGES/django.po

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update django_q/locale/tr/LC_MESSAGES/django.po

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: clear fuzzy markers

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-25 01:06:07 +02:00
Islam Asabaev
936fdd10b1 Update Python base image to 3.9-slim-bookworm (#325)
* issue: gh-324
2026-04-18 16:48:55 +02:00
Nick Yuan
fab97463c2 Fix unbounded growth of Broker.set_stat cluster master list (#322)
Prune stale entries from the master list on new cluster registration
and only write the list when membership changes (with timeout=None).

Before this fix, set_stat was append-only — dead cluster keys accumulated
forever because the only cleanup path (get_stats) runs only from the
monitor UIs. On DatabaseCache backends this rewrote the growing pickled
list to Postgres on every heartbeat, producing measurable egress.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 00:24:43 +02:00
Sophile
1abdc5b653 Convert queue size and count to string in monitor (#319) 2026-03-27 23:00:35 +01:00
Seba
01df35c923 Don't close DB connection if async_task was called with sync=True (#301) 2026-03-12 15:33:19 +01:00
Niko
573b8da528 Update Django Q2 compatibility information (#316) 2026-02-16 22:59:51 +01:00
Yibo Li
8a563d289d feat:add Simplified Chinese Translation (#314) 2026-01-14 23:58:54 +01:00
Marcin Bachry
1b0f71a39b Fix BadSignature after the default Django cache expires (#311)
* async_iter: fix BadSignature after the default Django cache expires

* lint
2026-01-08 23:27:03 +01:00
prollings
b51575a4b0 Add post_execute_in_worker signal (#309) 2025-12-10 16:54:14 +01:00
xuheng.zong
fd927087f1 fix: Fix incorrect signal import (#308)
* fix: Fix incorrect signal import

* lint

---------

Co-authored-by: Stan Triepels <1939656+GDay@users.noreply.github.com>
2025-12-08 21:10:10 +01:00
Stan
386e04171b Release v1.9.0 2025-12-04 23:04:52 +01:00
Luke Plant
0054535624 Django 6.0 support (#307)
* Remove upper bound on Django version.

This allows it to be installed alongside Django 6 and later.

See https://iscinumpy.dev/post/bound-version-constraints/

* Run tests on Django 6.0 / Python 3.12
2025-12-04 17:15:18 +01:00
Salvo 'LtWorf' Tomaselli
09e65da5b3 Fix to make tests work with redis-py > 5 (#282) 2025-07-04 02:13:46 +02:00
Stan
65d2548e98 Release v1.8.0 2025-04-25 16:30:51 +02:00
Stan Triepels
cbe51f995c Move timeout function from ORM broker into class to allow easy customization (#274)
* move timeout function to class to allow customization

* format

* fix format
2025-04-23 15:44:37 +02:00
Adam Johnson
0090a6f411 Update tested versions, add python 3.13 support and django 5.2 support. Drop python 3.8 support (#271)
* Update tested versions

* Remove f string

* Fix second `format_html`

---------

Co-authored-by: Stan Triepels <1939656+GDay@users.noreply.github.com>
2025-04-16 01:48:51 +02:00
Fernando Karchiloff
a154fd658a Avoid creating task run on Scheduler creation (#268) 2025-03-12 23:31:00 +01:00
Adam Johnson
81f2531e3e Remove disque from CI (#270) 2025-03-12 23:28:59 +01:00
Stan Triepels
c464751ae9 Delete deprecated imports and unwrapping (#261)
* delete old code

* remove imports
2025-01-12 03:40:18 +01:00
Stan
144536cbe4 Release v1.7.6 2025-01-12 02:25:29 +01:00
Stan Triepels
7e72b68c7f Make poetry version fixed in CI (#260) 2025-01-12 02:22:14 +01:00
Stan
404932117e Release v1.7.5 2025-01-12 01:57:13 +01:00
Anthony Hivert
ddc1aa2de1 perf: avoid checking success tasks when save limit is disabled (#255)
- avoid extra queries when checking the count of success in db
2025-01-04 02:31:30 +01:00
mhaehnel
351bf66d71 Fix install path for CHANGELOG.md (#258)
The file should not be installed directly in site-packages. Use the
correct include syntax to include them into the source distribution
package only.
2025-01-02 01:52:07 +01:00
Stan
ccd760a69f Release v1.7.4 2024-11-03 15:26:11 +01:00
Alberto
fb23b6b825 Fix 'receive_message_wait_time_seconds' SQS broker management (#243)
* Fix 'receive_message_wait_time_seconds' SQS broker management

* Remove socket volume from localstack container
2024-11-03 15:04:35 +01:00
P-EB
35c911080b Fix BROKER_CLASS monkeypatch in test_brokers (#239) 2024-10-21 17:51:27 +02:00
P-EB
81058fc1cf Decrease the MAX_RSS set in test_cluster::test_max_rss (#240) 2024-10-21 17:50:30 +02:00
35 changed files with 3325 additions and 676 deletions

View File

@@ -26,25 +26,39 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
django: [ "4.2", "5.0", "5.1"]
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
django:
- "4.2"
- "5.0"
- "5.1"
- "5.2"
- "6.0"
exclude:
# django 5.1 does not support 3.8 and 3.9
- python-version: "3.8"
django: "5.1"
# django 5.2 does not support 3.9
- python-version: "3.9"
django: "5.2"
# django 5.1 does not support 3.9
- python-version: "3.9"
django: "5.1"
# django 5.0 does not support 3.8 and 3.9
- python-version: "3.8"
django: "5.0"
# django 5.0 does not support 3.9
- python-version: "3.9"
django: "5.0"
django: "5.0"
# django 4.2 does not support 3.13
- python-version: "3.13"
django: "4.2"
# django 6.0 does not support earlier than 3.12
- python-version: "3.9"
django: "6.0"
- python-version: "3.10"
django: "6.0"
- python-version: "3.11"
django: "6.0"
services:
disque:
image: efrecon/disque:1.0-rc1
ports:
- '7711:7711/tcp'
mongodb:
image: mongo
ports:
@@ -73,8 +87,8 @@ jobs:
- name: Install dependencies with Django ${{ matrix.django }}
run: |
python -m pip install --upgrade pip
pip install poetry
poetry add "django==${{ matrix.django }}" --python=${{ matrix.python-version }}
pip install poetry==1.8.5
poetry add "django~=${{ matrix.django }}a1" --python=${{ matrix.python-version }}
poetry install -E testing
- name: Run Tests
run: |

View File

@@ -1,5 +1,47 @@
# Changelog
## [v1.10.0](https://github.com/django-q2/django-q2/tree/v1.10.0) (2026-05-01)
- fix: Fix incorrect signal import (#308) https://github.com/django-q2/django-q2/pull/308
- Add post_execute_in_worker signal (#309) https://github.com/django-q2/django-q2/pull/309
- Fix BadSignature after the default Django cache expires (#311) https://github.com/django-q2/django-q2/pull/311
- feat:add Simplified Chinese Translation (#314) https://github.com/django-q2/django-q2/pull/314
- Update Django Q2 compatibility information (#316) https://github.com/django-q2/django-q2/pull/316
- Don't close DB connection if async_task was called with sync=True (#301) https://github.com/django-q2/django-q2/pull/301
- Convert queue size and count to string in monitor (#319) https://github.com/django-q2/django-q2/pull/319
- Fix unbounded growth of Broker.set_stat cluster master list (#322) https://github.com/django-q2/django-q2/pull/322
- Update Python base image to 3.9-slim-bookworm (#325) https://github.com/django-q2/django-q2/pull/325
- feat: add ru locale and improve translations (#320) https://github.com/django-q2/django-q2/pull/320
## [v1.9.0](https://github.com/django-q2/django-q2/tree/v1.9.0) (2025-12-04)
- Django 6.0 support https://github.com/django-q2/django-q2/pull/307
- Fix to make tests work with redis-py > 5 https://github.com/django-q2/django-q2/pull/282
## [v1.8.0](https://github.com/django-q2/django-q2/tree/v1.8.0) (2025-04-25)
- Delete deprecated imports and unwrapping https://github.com/django-q2/django-q2/pull/261
- Remove disque from CI https://github.com/django-q2/django-q2/pull/270
- Avoid creating task run on Scheduler creation https://github.com/django-q2/django-q2/pull/268
- Update tested versions, add python 3.13 support and django 5.2 support. Drop python 3.8 support https://github.com/django-q2/django-q2/pull/271
- Move timeout function from ORM broker into class to allow easy customization https://github.com/django-q2/django-q2/pull/274
## [v1.7.6](https://github.com/django-q2/django-q2/tree/v1.7.6) (2025-01-12)
- Make poetry version fixed in CI https://github.com/django-q2/django-q2/pull/260
## [v1.7.5](https://github.com/django-q2/django-q2/tree/v1.7.5) (2025-01-12)
- perf: avoid checking success tasks when save limit is disabled https://github.com/django-q2/django-q2/pull/255
- Fix install path for CHANGELOG.md https://github.com/django-q2/django-q2/pull/258
## [v1.7.4](https://github.com/django-q2/django-q2/tree/v1.7.4) (2024-11-03)
- Decrease the MAX_RSS set in test_cluster::test_max_rss https://github.com/django-q2/django-q2/pull/240
- Fix BROKER_CLASS monkeypatch in test_brokers https://github.com/django-q2/django-q2/pull/239
- Fix 'receive_message_wait_time_seconds' SQS broker management https://github.com/django-q2/django-q2/pull/243
## [v1.7.3](https://github.com/django-q2/django-q2/tree/v1.7.3) (2024-10-15)
- Catch missing SEGALRM with AttributeError instead of ValueError https://github.com/django-q2/django-q2/pull/223

View File

@@ -1,5 +1,5 @@
# Sets the python version
FROM python:3.9.5-slim
FROM python:3.9-slim-bookworm
# Allows the logs generated by python apps to be rendered in the terminal
ENV PYTHONUNBUFFERED 1
@@ -7,6 +7,10 @@ ENV PYTHONUNBUFFERED 1
# Sets the default shell to bash
ENV SHELL /bin/bash
RUN set -ex \
&& apt update \
&& apt-get install gcc python3-dev --yes
# Upgrades pip
RUN pip install -U pip setuptools

View File

@@ -2,16 +2,16 @@ dev:
docker compose -f web-docker-compose.yaml up
test:
docker-compose -f test-services-docker-compose.yaml run --rm django-q2 poetry run pytest
docker compose -f test-services-docker-compose.yaml run --rm django-q2 poetry run pytest
shell:
docker-compose -f test-services-docker-compose.yaml run --rm django-q2 poetry run python manage.py shell
docker compose -f test-services-docker-compose.yaml run --rm django-q2 poetry run python manage.py shell
makemigrations:
docker-compose -f test-services-docker-compose.yaml run --rm django-q2 poetry run python manage.py makemigrations
docker compose -f test-services-docker-compose.yaml run --rm django-q2 poetry run python manage.py makemigrations
migrate:
docker-compose -f test-services-docker-compose.yaml run --rm django-q2 poetry run python manage.py migrate
docker compose -f test-services-docker-compose.yaml run --rm django-q2 poetry run python manage.py migrate
createsuperuser:
docker compose -f web-docker-compose.yaml run --rm web python manage.py createsuperuser

View File

@@ -38,7 +38,10 @@ Requirements
- `Django <https://www.djangoproject.com>`__ > = 4.2
- `Django-picklefield <https://github.com/gintas/django-picklefield>`__
Tested with: Python 3.8, 3.9, 3.10, 3.11 and 3.12. Works with Django 4.2.X and 5.0.X
Tested with:
* Python 3.9 to 3.13.
* Django 4.2 to 6.0.
Brokers
~~~~~~~

19
containers/localstack/init.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Note that this file needs to have the executable bit set for it to work with later localstack implementations.
export DEFAULT_REGION=us-west-2
create_sqs() {
QUEUE_NAME="$1"
TIMEOUT=${2:-60}
DL_QUEUE_URL=$(awslocal sqs create-queue --queue-name "dl-$QUEUE_NAME" --query QueueUrl --output text)
echo ">>> Created $DL_QUEUE_URL queue!"
DL_QUEUE_ARN=$(awslocal sqs get-queue-attributes --queue-url "$DL_QUEUE_URL" --attribute-names QueueArn --query Attributes.QueueArn --output text)
awslocal sqs create-queue --queue-name "$QUEUE_NAME" --attributes '{
"RedrivePolicy": "{\"deadLetterTargetArn\": \"'"$DL_QUEUE_ARN"'\",\"maxReceiveCount\":\"3\"}",
"VisibilityTimeout": "'"$TIMEOUT"'"
}'
}
# Create SQS queues
create_sqs testing

View File

@@ -1,8 +1,3 @@
import django
VERSION = (1, 7, 3)
if django.VERSION < (3, 2):
default_app_config = "django_q.apps.DjangoQConfig"
VERSION = (1, 10, 0)
__all__ = ["conf", "cluster", "models", "tasks"]

View File

@@ -2,6 +2,7 @@
from django.contrib import admin
from django.db.models.expressions import OuterRef, Subquery
from django.template.defaultfilters import truncatechars
from django.urls import reverse
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
@@ -60,6 +61,10 @@ class TaskAdmin(admin.ModelAdmin):
"""Set all fields readonly."""
return list(self.readonly_fields) + [field.name for field in obj._meta.fields]
@admin.display(description=_("Time Taken"))
def time_taken(self, obj):
return obj.time_taken()
class FailAdmin(admin.ModelAdmin):
"""model admin for failed tasks."""
@@ -87,6 +92,10 @@ class FailAdmin(admin.ModelAdmin):
"""Set all fields readonly."""
return list(self.readonly_fields) + [field.name for field in obj._meta.fields]
@admin.display(description=_("Short result"))
def short_result(self, obj):
return truncatechars(obj.result, 100)
class ScheduleAdmin(admin.ModelAdmin):
"""model admin for schedules"""
@@ -138,7 +147,7 @@ class ScheduleAdmin(admin.ModelAdmin):
url = reverse("admin:django_q_success_change", args=(obj.task_id,))
else:
url = reverse("admin:django_q_failure_change", args=(obj.task_id,))
return format_html(f'<a href="{url}">[{obj.task_name}]</a>')
return format_html('<a href="{}">[{}]</a>', url, obj.task_name)
return None
get_last_run.allow_tags = True

View File

@@ -106,8 +106,13 @@ class Broker:
return
key_list = self.cache.get(Conf.Q_STAT, [])
if key not in key_list:
# Prune stale entries whose per-stat value has expired, so the
# master list cannot grow without bound across cluster restarts.
key_list = [k for k in key_list if self.cache.get(k) is not None]
key_list.append(key)
self.cache.set(Conf.Q_STAT, key_list)
# timeout=None: master list lifetime is managed by membership
# changes, not by TTL refresh on every heartbeat.
self.cache.set(Conf.Q_STAT, key_list, None)
return self.cache.set(key, value, timeout)
def get_stat(self, key: str):

View File

@@ -1,3 +1,5 @@
import copy
from boto3 import Session
from botocore.client import ClientError
@@ -78,15 +80,15 @@ class Sqs(Broker):
@staticmethod
def get_connection(list_key: str = None) -> Session:
config = Conf.SQS
if "aws_region" in config:
config["region_name"] = config["aws_region"]
del config["aws_region"]
config_cloned = copy.deepcopy(Conf.SQS)
if "aws_region" in config_cloned:
config_cloned["region_name"] = config_cloned["aws_region"]
del config_cloned["aws_region"]
if "receive_message_wait_time_seconds" in config:
del config["receive_message_wait_time_seconds"]
if "receive_message_wait_time_seconds" in config_cloned:
del config_cloned["receive_message_wait_time_seconds"]
return Session(**config)
return Session(**config_cloned)
def get_queue(self):
self.sqs = self.connection.resource("sqs")

View File

@@ -10,10 +10,6 @@ from django_q.conf import Conf, logger
from django_q.models import OrmQ
def _timeout():
return timezone.now() + timedelta(seconds=Conf.RETRY)
class ORM(Broker):
@staticmethod
def get_connection(list_key: str = None):
@@ -28,6 +24,9 @@ class ORM(Broker):
logger.debug("Broker in an atomic transaction")
return OrmQ.objects.using(Conf.ORM)
def timeout(self, task):
return timezone.now() + timedelta(seconds=Conf.RETRY)
def queue_size(self) -> int:
return (
self.get_connection()
@@ -75,7 +74,7 @@ class ORM(Broker):
if (
self.get_connection()
.filter(id=task.id, lock=task.lock)
.update(lock=_timeout())
.update(lock=self.timeout(task))
):
task_list.append((task.pk, task.payload))
# else don't process, as another cluster has been faster than us on

View File

@@ -1,9 +1,9 @@
import logging
import os
import signal
import sys
from copy import deepcopy
from multiprocessing import cpu_count
from signal import signal
from warnings import warn
from django.conf import settings

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-26 01:38+0000\n"
"POT-Creation-Date: 2026-04-17 21:10+0000\n"
"PO-Revision-Date: 2018-08-05 18:28+0200\n"
"Last-Translator: Jonas Winkler\n"
"Language-Team: \n"
@@ -17,37 +17,45 @@ msgstr ""
"X-Generator: Poedit 2.1.1\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: admin.py:43
#: admin.py:30
msgid "Resubmit selected tasks to queue"
msgstr "Ausgewählte Aufgaben erneut ausführen"
#: admin.py:107 models.py:293
#: admin.py:64
msgid "Time Taken"
msgstr ""
#: admin.py:95
msgid "Short result"
msgstr ""
#: admin.py:142 models.py:412
#, fuzzy
#| msgid "Success"
msgid "success"
msgstr "erfolg"
#: admin.py:119 models.py:295
#: admin.py:154 models.py:414
msgid "last_run"
msgstr ""
#: cluster.py:79
#: cluster.py:75
#, python-format
msgid "Q Cluster %(name)s starting."
msgstr "Q-Cluster %(name)s wird gestartet."
#: cluster.py:87
#: cluster.py:83
#, fuzzy, python-format
#| msgid "Q Cluster-{} stopping."
msgid "Q Cluster %(name)s stopping."
msgstr "Q-Cluster {name} wird gestoppt."
#: cluster.py:90
#: cluster.py:86
#, python-format
msgid "Q Cluster %(name)s has stopped."
msgstr "Q-Cluster %(name)s wurde gestoppt."
#: cluster.py:97
#: cluster.py:93
#, python-format
msgid "%(name)s got signal %(signal)s"
msgstr "%(name)s erhielt das Signal %(signal)s"
@@ -62,7 +70,7 @@ msgstr "Monitor %(name)s wurde nach unerwartetem Absturz neu gestartet"
msgid "reincarnated pusher %(name)s after sudden death"
msgstr "Pusher %(name)s wurde nach unerwartetem Absturz neu gestartet"
#: cluster.py:250
#: cluster.py:262
#, fuzzy, python-format
#| msgid "reincarnated worker %(name)s after timeout"
msgid ""
@@ -70,123 +78,57 @@ msgid ""
"%(task_name)s"
msgstr "Worker %(name)s wurde nach Zeitüberschreitung neu gestartet"
#: cluster.py:255
#: cluster.py:265
#, python-format
msgid "reincarnated worker %(name)s after timeout"
msgstr "Worker %(name)s wurde nach Zeitüberschreitung neu gestartet"
#: cluster.py:260
#: cluster.py:270
#, python-format
msgid "recycled worker %(name)s"
msgstr "Worker %(name)s wurde wiederverwendet"
#: cluster.py:263
#: cluster.py:273
#, python-format
msgid "reincarnated worker %(name)s after death"
msgstr "Worker %(name)s wurde nach unerwartetem Absturz neu gestartet"
#: cluster.py:287
#: cluster.py:297
#, python-format
msgid "%(name)s guarding cluster %(cluster_name)s"
msgstr "%(name)s beschützt das Cluster %(cluster_name)s"
#: cluster.py:296
#: cluster.py:307
#, python-format
msgid "Q Cluster %(cluster_name)s running."
msgstr "Q-Cluster %(cluster_name)s läuft."
#: cluster.py:332
#: cluster.py:346
#, python-format
msgid "%(name)s stopping cluster processes"
msgstr "%(name)s hält Cluster-Prozesse an"
#: cluster.py:357
#: cluster.py:371
#, python-format
msgid "%(name)s waiting for the monitor."
msgstr "%(name)s wartet auf den Monitor."
#: cluster.py:383
#, fuzzy, python-format
#| msgid "%(process_name)s pushing tasks at %(id)s"
msgid "%(name)s pushing tasks at %(id)s"
msgstr "%(process_name)s veröffentlicht Aufagaben auf %(id)s"
#: cluster.py:407
#, python-format
msgid "queueing from %(list_key)s"
msgstr "Einreihen von %(list_key)s"
#: cluster.py:411
#, python-format
msgid "%(name)s stopped pushing tasks"
msgstr "%(name)s veröffentlicht keine Aufgaben mehr"
#: cluster.py:426
#, python-format
msgid "%(name)s monitoring at %(id)s"
msgstr "%(name)s beobachtet auf %(id)s"
#: cluster.py:445
#, python-format
msgid "Processed '%(info_name)s' (%(task_name)s)"
msgstr "[%(task_name)s] - '%(info_name)s' wurde verarbeitet"
#: cluster.py:451
#, python-format
msgid "Failed '%(info_name)s' (%(task_name)s) - %(task_result)s"
msgstr "'%(info_name)s' (%(task_name)s) ist fehlgeschlagen - %(task_result)s"
#: cluster.py:458
#, python-format
msgid "%(name)s stopped monitoring results"
msgstr "%(name)s überwacht keine Ergebnisse mehr"
#: cluster.py:474
#, python-format
msgid "%(proc_name)s ready for work at %(id)s"
msgstr "%(proc_name)s ist bereit für Arbeit auf %(id)s"
#: cluster.py:494
#, fuzzy, python-format
#| msgid "%(proc_name)s processing '%(func_name)s' (%(task_name)s)"
msgid "%(proc_name)s processing %(task_name)s '%(func_name)s'"
msgstr "%(proc_name)s verarbeitet '%(func_name)s' (%(task_name)s)"
#: cluster.py:546
#, python-format
msgid "%(proc_name)s stopped doing work"
msgstr "%(proc_name)s hat die Arbeit beendet"
#: cluster.py:751
#, python-format
msgid "%(process_name)s failed to create a task from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s konnte keine Aufgabe von Zeitplan [%(schedule)s] erstellen"
#: cluster.py:762
#, fuzzy, python-format
#| msgid "%(process_name)s created a task from schedule [%(schedule)s]"
msgid ""
"%(process_name)s created task %(task_name)s from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s hat eine Aufgabe des Zeitplans [%(schedule)s] erstellt"
#: cluster.py:808
#: cluster.py:394
msgid "Skipping cpu affinity because psutil was not found."
msgstr "Cpu-Affinität wird übersprungen, da psutil nicht gefunden wurde."
#: cluster.py:813
#: cluster.py:399
msgid "Faking cpu affinity because it is not supported on this platform"
msgstr ""
"Vortäuschen von CPU-Affinität, da diese auf dieser Plattform nicht "
"unterstützt wird"
#: cluster.py:835
#: cluster.py:421
#, python-format
msgid "%(pid)s will use cpu %(affinity)s"
msgstr "%(pid)s wird CPU %(affinity)s benutzen"
#: conf.py:90
#: conf.py:122
#, python-format
msgid ""
"SAVE_LIMIT_PER (%(option)s) is not a valid option. Options are: 'group', "
@@ -196,28 +138,28 @@ msgstr ""
"'group', 'name', 'func' und None. Standard ist None."
#. Translators: Cluster status descriptions
#: conf.py:207
#: conf.py:239
msgid "Starting"
msgstr "Wird gestartet"
#: conf.py:208
#: conf.py:240
msgid "Working"
msgstr "Arbeitet"
#: conf.py:209
#: conf.py:241
msgid "Idle"
msgstr "Leerlauf"
#: conf.py:210
#: conf.py:242
msgid "Stopped"
msgstr "Gestoppt"
#: conf.py:211
#: conf.py:243
msgid "Stopping"
msgstr "Wird gestoppt"
#. Translators: help text for qcluster management command
#: management/commands/qcluster.py:9
#: management/commands/qcluster.py:11
msgid "Starts a Django Q Cluster."
msgstr "Startet ein Django-Q-Cluster."
@@ -236,253 +178,479 @@ msgstr "Überwacht die Speichernutzung von Q Cluster"
msgid "Monitors Q Cluster activity"
msgstr "Q-Cluster aktiv überwachen"
#: models.py:125
msgid "Successful task"
msgstr "Erfolgreiche Aufgabe"
#: models.py:30 models.py:469
msgid "Task id"
msgstr ""
#: models.py:126
msgid "Successful tasks"
msgstr "Erfolgreiche Aufgaben"
#: models.py:31
msgid "32-character task identifier."
msgstr ""
#: models.py:141
msgid "Failed task"
msgstr "Fehlgeschlagene Aufgabe"
#: models.py:36 models.py:235 models.py:470
msgid "Name"
msgstr ""
#: models.py:142
msgid "Failed tasks"
msgstr "Fehlgeschlagene Aufgaben"
#: models.py:37
msgid "Optional human-readable name for lookup and display."
msgstr ""
#: models.py:150 models.py:234
msgid "Please install croniter to enable cron expressions"
msgstr "Bitte installieren Sie croniter, um Cron-Ausdrücke zu aktivieren"
#: models.py:41 models.py:240 models.py:468
msgid "Function"
msgstr ""
#: models.py:170
msgid "e.g. 1, 2, 'John'"
msgstr "zum Beispiel 1, 2, 'John'"
#: models.py:42
msgid "Dotted import path to the callable, e.g. myapp.tasks.job."
msgstr ""
#: models.py:172
msgid "e.g. x=1, y=2, name='John'"
msgstr "zum Beispiel x=1, y=2, name='John'"
#: models.py:47 models.py:247
msgid "Hook"
msgstr ""
#: models.py:186
msgid "Once"
msgstr "Einmal"
#: models.py:48
msgid "Optional dotted path to a callback that receives the task result."
msgstr ""
#: models.py:187
msgid "Minutes"
msgstr "Minuten"
#: models.py:53 models.py:253 models.py:472
msgid "Arguments"
msgstr ""
#: models.py:188
msgid "Hourly"
msgstr "Stündlich"
#: models.py:54
msgid "Pickled positional arguments (tuple)."
msgstr ""
#: models.py:189
msgid "Daily"
msgstr "Täglich"
#: models.py:59 models.py:259 models.py:473
msgid "Keyword arguments"
msgstr ""
#: models.py:190
msgid "Weekly"
msgstr "Wöchentlich"
#: models.py:60
msgid "Pickled keyword arguments (dict)."
msgstr ""
#: models.py:191
msgid "Biweekly"
msgstr "Zweiwöchentlich"
#: models.py:65
msgid "Result"
msgstr ""
#: models.py:192
msgid "Monthly"
msgstr "Monatlich"
#: models.py:66
msgid "Pickled return value or error payload."
msgstr ""
#: models.py:193
msgid "Bimonthly"
msgstr "Zweimonatlich"
#: models.py:72 models.py:471
msgid "Group"
msgstr ""
#: models.py:194
msgid "Quarterly"
msgstr "Vierteljährlich"
#: models.py:73
msgid "Optional group id to aggregate related tasks."
msgstr ""
#: models.py:195
msgid "Yearly"
msgstr "Jährlich"
#: models.py:80 models.py:328
#, fuzzy
#| msgid "Clusters"
msgid "Cluster"
msgstr "Cluster"
#: models.py:196
msgid "Cron"
msgstr "Cron"
#: models.py:81
msgid "Name of the cluster that executed this task."
msgstr ""
#: models.py:199
msgid "Schedule Type"
msgstr "Zeitplan-Typ"
#: models.py:85
msgid "Started at"
msgstr ""
#: models.py:202
msgid "Number of minutes for the Minutes type"
msgstr "Anzahl Minuten für den Typ 'Minuten'"
#: models.py:86
msgid "When the worker started executing the task."
msgstr ""
#: models.py:205
msgid "Repeats"
msgstr "Wiederholungen"
#: models.py:90
#, fuzzy
#| msgid "Stopped"
msgid "Stopped at"
msgstr "Gestoppt"
#: models.py:205
msgid "n = n times, -1 = forever"
msgstr "n = n mal, -1 = für immer"
#: models.py:91
msgid "When the worker finished (success or failure)."
msgstr ""
#: models.py:208
msgid "Next Run"
msgstr "Nächste Ausführung"
#: models.py:215
msgid "Cron expression"
msgstr "Cron-Ausdruck"
#: models.py:224
msgid "Name of kwarg to pass intended schedule date"
msgstr "Name des zu passierenden Kwargs vorgesehenes Datum"
#: models.py:299
msgid "Scheduled task"
msgstr "Geplante Aufgabe"
#: models.py:300
msgid "Scheduled tasks"
msgstr "Geplante Aufgaben"
#: models.py:326
msgid "Queued task"
msgstr "Eingereihte Aufgabe"
#: models.py:327
msgid "Queued tasks"
msgstr "Eingereihte Aufgaben"
#: monitor.py:64 monitor.py:348
msgid "Host"
msgstr "Host"
#: monitor.py:68 monitor.py:352 monitor.py:459
msgid "Id"
msgstr "Id"
#: monitor.py:72
msgid "State"
msgstr "Status"
#: monitor.py:76
msgid "Pool"
msgstr "Pool"
#: monitor.py:80
msgid "TQ"
msgstr "TQ"
#: monitor.py:84
msgid "RQ"
msgstr "RQ"
#: monitor.py:88
msgid "RC"
msgstr "RC"
#: monitor.py:92
msgid "Up"
msgstr "Up"
#: monitor.py:172 monitor.py:286
msgid "Queued"
msgstr "Eingereiht"
#: monitor.py:180
#: models.py:96 monitor_terminal.py:180
msgid "Success"
msgstr "Erfolg"
#: monitor.py:190 monitor.py:294
#: models.py:97
msgid "False if the task raised an exception or timed out."
msgstr ""
#: models.py:101
msgid "Attempt count"
msgstr ""
#: models.py:102
msgid "How many times execution was attempted."
msgstr ""
#: models.py:172
msgid "Task"
msgstr ""
#: models.py:173
msgid "Tasks"
msgstr ""
#: models.py:194
msgid "Successful task"
msgstr "Erfolgreiche Aufgabe"
#: models.py:195
msgid "Successful tasks"
msgstr "Erfolgreiche Aufgaben"
#: models.py:210
msgid "Failed task"
msgstr "Fehlgeschlagene Aufgabe"
#: models.py:211
msgid "Failed tasks"
msgstr "Fehlgeschlagene Aufgaben"
#: models.py:219 models.py:347
msgid "Please install croniter to enable cron expressions"
msgstr "Bitte installieren Sie croniter, um Cron-Ausdrücke zu aktivieren"
#: models.py:236
msgid "Optional label to identify this schedule in the admin."
msgstr ""
#: models.py:241
msgid "e.g. module.tasks.function"
msgstr ""
#: models.py:248
msgid "e.g. module.tasks.result_function"
msgstr ""
#: models.py:254
msgid "e.g. 1, 2, 'John'"
msgstr "zum Beispiel 1, 2, 'John'"
#: models.py:260
msgid "e.g. x=1, y=2, name='John'"
msgstr "zum Beispiel x=1, y=2, name='John'"
#: models.py:274
msgid "Once"
msgstr "Einmal"
#: models.py:275 models.py:296
msgid "Minutes"
msgstr "Minuten"
#: models.py:276
msgid "Hourly"
msgstr "Stündlich"
#: models.py:277
msgid "Daily"
msgstr "Täglich"
#: models.py:278
msgid "Weekly"
msgstr "Wöchentlich"
#: models.py:279
msgid "Biweekly"
msgstr "Zweiwöchentlich"
#: models.py:280
msgid "Monthly"
msgstr "Monatlich"
#: models.py:281
msgid "Bimonthly"
msgstr "Zweimonatlich"
#: models.py:282
msgid "Quarterly"
msgstr "Vierteljährlich"
#: models.py:283
msgid "Yearly"
msgstr "Jährlich"
#: models.py:284 models.py:313
msgid "Cron"
msgstr "Cron"
#: models.py:290
msgid "Schedule Type"
msgstr "Zeitplan-Typ"
#: models.py:291
msgid "How often this task should be enqueued."
msgstr ""
#: models.py:297
msgid "Number of minutes for the Minutes type"
msgstr "Anzahl Minuten für den Typ 'Minuten'"
#: models.py:300
msgid "Repeats"
msgstr "Wiederholungen"
#: models.py:300
msgid "n = n times, -1 = forever"
msgstr "n = n mal, -1 = für immer"
#: models.py:303
msgid "Next Run"
msgstr "Nächste Ausführung"
#: models.py:304
msgid "When this schedule runs next (stored in UTC)."
msgstr ""
#: models.py:314
msgid "Cron expression"
msgstr "Cron-Ausdruck"
#: models.py:320
msgid "Last task id"
msgstr ""
#: models.py:321
msgid "Id of the last task spawned from this schedule (read-only)."
msgstr ""
#: models.py:329 models.py:427
msgid "Name of the target cluster"
msgstr ""
#: models.py:336
msgid "Intended date kwarg"
msgstr ""
#: models.py:337
msgid "Name of kwarg to pass intended schedule date"
msgstr "Name des zu passierenden Kwargs vorgesehenes Datum"
#: models.py:418
msgid "Scheduled task"
msgstr "Geplante Aufgabe"
#: models.py:419
msgid "Scheduled tasks"
msgstr "Geplante Aufgaben"
#: models.py:426
#, fuzzy
#| msgid "Clusters"
msgid "Cluster key"
msgstr "Cluster"
#: models.py:430
msgid "Payload"
msgstr ""
#: models.py:431
msgid "Signed serialized task package (do not edit manually)."
msgstr ""
#: models.py:435
msgid "Lock until"
msgstr ""
#: models.py:436
msgid "Prevent any cluster from pulling until"
msgstr ""
#: models.py:474
#, fuzzy
#| msgid "Queued task"
msgid "Queue options"
msgstr "Eingereihte Aufgabe"
#: models.py:478
msgid "Queued task"
msgstr "Eingereihte Aufgabe"
#: models.py:479
msgid "Queued tasks"
msgstr "Eingereihte Aufgaben"
#: monitor.py:41
#, python-format
msgid "%(name)s monitoring at %(id)s"
msgstr "%(name)s beobachtet auf %(id)s"
#: monitor.py:61
#, python-format
msgid "Processed '%(info_name)s' (%(task_name)s)"
msgstr "[%(task_name)s] - '%(info_name)s' wurde verarbeitet"
#: monitor.py:67
#, python-format
msgid "Failed '%(info_name)s' (%(task_name)s) - %(task_result)s"
msgstr "'%(info_name)s' (%(task_name)s) ist fehlgeschlagen - %(task_result)s"
#: monitor.py:74
#, python-format
msgid "%(name)s stopped monitoring results"
msgstr "%(name)s überwacht keine Ergebnisse mehr"
#: monitor_terminal.py:64 monitor_terminal.py:348
msgid "Host"
msgstr "Host"
#: monitor_terminal.py:68 monitor_terminal.py:352 monitor_terminal.py:457
msgid "Id"
msgstr "Id"
#: monitor_terminal.py:72
msgid "State"
msgstr "Status"
#: monitor_terminal.py:76
msgid "Pool"
msgstr "Pool"
#: monitor_terminal.py:80
msgid "TQ"
msgstr "TQ"
#: monitor_terminal.py:84
msgid "RQ"
msgstr "RQ"
#: monitor_terminal.py:88
msgid "RC"
msgstr "RC"
#: monitor_terminal.py:92
msgid "Up"
msgstr "Up"
#: monitor_terminal.py:172 monitor_terminal.py:286
msgid "Queued"
msgstr "Eingereiht"
#: monitor_terminal.py:190 monitor_terminal.py:294
msgid "Failures"
msgstr "Fehlschläge"
#: monitor.py:201 monitor.py:498
#: monitor_terminal.py:201 monitor_terminal.py:496
msgid "[Press q to quit]"
msgstr "[Drücken Sie q zum Beenden]"
#: monitor.py:227
#: monitor_terminal.py:227
msgid "day"
msgstr "Tag"
#: monitor.py:248
#: monitor_terminal.py:248
msgid "second"
msgstr "Sekunde"
#: monitor.py:251
#: monitor_terminal.py:251
msgid "minute"
msgstr "Minute"
#: monitor.py:254
#: monitor_terminal.py:254
msgid "hour"
msgstr "Stunde"
#: monitor.py:263
#: monitor_terminal.py:263
#, python-format
msgid "-- %(prefix)s %(version)s on %(info)s --"
msgstr "-- %(prefix)s %(version)s auf %(info)s --"
#: monitor.py:273
#: monitor_terminal.py:273
msgid "Clusters"
msgstr "Cluster"
#: monitor.py:277
#: monitor_terminal.py:277
msgid "Workers"
msgstr "Arbeiter"
#: monitor.py:281
#: monitor_terminal.py:281
msgid "Restarts"
msgstr "Neustarts"
#: monitor.py:290
#: monitor_terminal.py:290
msgid "Successes"
msgstr "Erfolge"
#: monitor.py:299
#: monitor_terminal.py:299
msgid "Schedules"
msgstr "Zeitpläne"
#: monitor.py:303
#: monitor_terminal.py:303
#, python-format
msgid "Tasks/%(per)s"
msgstr "Aufgaben/%(per)s"
#: monitor.py:307
#: monitor_terminal.py:307
msgid "Avg time"
msgstr "Durchschnittl. Zeit"
#: monitor.py:357
#: monitor_terminal.py:357
msgid "Available (%)"
msgstr "Verfügbar (%)"
#: monitor.py:363
#: monitor_terminal.py:363
msgid "Available (MB)"
msgstr "Verfügbar (MB)"
#: monitor.py:368
#: monitor_terminal.py:368
msgid "Total (MB)"
msgstr "Insgesamt (MB)"
#: monitor.py:373
#: monitor_terminal.py:373
msgid "Sentinel (MB)"
msgstr "Sentinel (MB)"
#: monitor.py:379
#: monitor_terminal.py:379
msgid "Monitor (MB)"
msgstr "Monitor (MB)"
#: monitor.py:385
#: monitor_terminal.py:385
msgid "Workers (MB)"
msgstr "Arbeiter (MB)"
#: monitor.py:487
#: monitor_terminal.py:485
#, python-format
msgid "Available lowest (): %(memory_percent)s ((at)s)"
msgstr "Niedrigste verfügbar (): %(memory_percent)s ((at)s)"
#: monitor.py:509
#: monitor_terminal.py:507
msgid "No clusters appear to be running."
msgstr "Es scheinen keine Cluster zu laufen."
#: pusher.py:40
#, python-format
#| msgid "%(process_name)s pushing tasks at %(id)s"
msgid "%(name)s pushing tasks at %(id)s"
msgstr "%(name)s veröffentlicht Aufagaben auf %(id)s"
#: pusher.py:67
#, python-format
msgid "queueing from %(list_key)s"
msgstr "Einreihen von %(list_key)s"
#: pusher.py:71
#, python-format
msgid "%(name)s stopped pushing tasks"
msgstr "%(name)s veröffentlicht keine Aufgaben mehr"
#: scheduler.py:110
#, python-format
msgid "%(process_name)s failed to create a task from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s konnte keine Aufgabe von Zeitplan [%(schedule)s] erstellen"
#: scheduler.py:121
#, fuzzy, python-format
#| msgid "%(process_name)s created a task from schedule [%(schedule)s]"
msgid ""
"%(process_name)s created task %(task_name)s from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s hat eine Aufgabe des Zeitplans [%(schedule)s] erstellt"
#: signals.py:22
#, python-format
msgid "malformed return hook '%(hook)s' for [%(name)s]"
@@ -493,6 +661,26 @@ msgstr "Ungültiger Return-Hook '%(hook)s' für [%(name)s]"
msgid "return hook %(hook)s failed on [%(name)s] because %(error)s"
msgstr "Return-Hook %(hook)s für [%(name)s] ist gescheitert: %(error)s"
#: timeout.py:30 timeout.py:43
msgid "SIGALARM is not available on your platform"
msgstr ""
#: worker.py:49
#, python-format
msgid "%(proc_name)s ready for work at %(id)s"
msgstr "%(proc_name)s ist bereit für Arbeit auf %(id)s"
#: worker.py:69
#, fuzzy, python-format
#| msgid "%(proc_name)s processing '%(func_name)s' (%(task_name)s)"
msgid "%(proc_name)s processing %(task_name)s '%(func_name)s'"
msgstr "%(proc_name)s verarbeitet '%(func_name)s' (%(task_name)s)"
#: worker.py:135
#, python-format
msgid "%(proc_name)s stopped doing work"
msgstr "%(proc_name)s hat die Arbeit beendet"
#, python-format
#~ msgid ""
#~ "Could not process '%(func_name)s'. Check the location of the function and "

View File

@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-26 01:38+0000\n"
"POT-Creation-Date: 2026-04-17 21:10+0000\n"
"PO-Revision-Date: 2018-08-05 18:28+0200\n"
"Last-Translator: Thierry BOULOGNE <contact@tng-concepts.com>\n"
"Language-Team: \n"
@@ -17,36 +17,44 @@ msgstr ""
"X-Generator: Poedit 2.1.1\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: admin.py:43
#: admin.py:30
msgid "Resubmit selected tasks to queue"
msgstr "Resoumettre les tâches sélectionnées à la file d'attente"
#: admin.py:107 models.py:293
#: admin.py:64
msgid "Time Taken"
msgstr ""
#: admin.py:95
msgid "Short result"
msgstr ""
#: admin.py:142 models.py:412
#, fuzzy
#| msgid "Success"
msgid "success"
msgstr "succès"
#: admin.py:119 models.py:295
#: admin.py:154 models.py:414
msgid "last_run"
msgstr ""
#: cluster.py:79
#: cluster.py:75
#, python-format
msgid "Q Cluster %(name)s starting."
msgstr "Démarrage de Q Cluster-%(name)s."
#: cluster.py:87
#: cluster.py:83
#, python-format
msgid "Q Cluster %(name)s stopping."
msgstr "Arrêt de Q Cluster-%(name)s."
#: cluster.py:90
#: cluster.py:86
#, python-format
msgid "Q Cluster %(name)s has stopped."
msgstr "Q Cluster-%(name)s a été arrêté."
#: cluster.py:97
#: cluster.py:93
#, python-format
msgid "%(name)s got signal %(signal)s"
msgstr "%(name)s à reçu le signal %(signal)s"
@@ -61,7 +69,7 @@ msgstr "surveillant %(name)s réincarné après un arrêt intempestif"
msgid "reincarnated pusher %(name)s after sudden death"
msgstr "répartiteur %(name)s réincarné après un arrêt intempestif"
#: cluster.py:250
#: cluster.py:262
#, python-format
msgid ""
"reincarnated worker %(name)s after timeout while processing task "
@@ -70,122 +78,57 @@ msgstr ""
"processus %(name)s réincarné, délai de traitement dépassé pour la tâche "
"%(task_name)s"
#: cluster.py:255
#: cluster.py:265
#, python-format
msgid "reincarnated worker %(name)s after timeout"
msgstr "processus %(name)s réincarné, délai de traitement dépassé"
#: cluster.py:260
#: cluster.py:270
#, python-format
msgid "recycled worker %(name)s"
msgstr "processus recyclé %(name)s"
#: cluster.py:263
#: cluster.py:273
#, python-format
msgid "reincarnated worker %(name)s after death"
msgstr "processus réintégré %(name)s après arrêt"
#: cluster.py:287
#: cluster.py:297
#, python-format
msgid "%(name)s guarding cluster %(cluster_name)s"
msgstr "%(name)s surveillance du cluster à %(cluster_name)s"
#: cluster.py:296
#: cluster.py:307
#, python-format
msgid "Q Cluster %(cluster_name)s running."
msgstr "Démarrage de Q Cluster-%(cluster_name)s."
#: cluster.py:332
#: cluster.py:346
#, python-format
msgid "%(name)s stopping cluster processes"
msgstr "%(name)s arrêt des processus du cluster"
#: cluster.py:357
#: cluster.py:371
#, python-format
msgid "%(name)s waiting for the monitor."
msgstr "%(name)s en attente du surveillant."
#: cluster.py:383
#, python-format
msgid "%(name)s pushing tasks at %(id)s"
msgstr "%(name)s répartit les tâches %(id)s"
#: cluster.py:407
#, python-format
msgid "queueing from %(list_key)s"
msgstr "mise en file d'attente de %(list_key)s"
#: cluster.py:411
#, python-format
msgid "%(name)s stopped pushing tasks"
msgstr "%(name)s a cessé de répartir les tâches"
#: cluster.py:426
#, python-format
msgid "%(name)s monitoring at %(id)s"
msgstr "%(name)s surveille les résultats %(id)s"
#: cluster.py:445
#, python-format
msgid "Processed '%(info_name)s' (%(task_name)s)"
msgstr "traité '%(info_name)s' (%(task_name)s)"
#: cluster.py:451
#, python-format
msgid "Failed '%(info_name)s' (%(task_name)s) - %(task_result)s"
msgstr "Manqué '%(info_name)s' (%(task_name)s) - %(task_result)s"
#: cluster.py:458
#, python-format
msgid "%(name)s stopped monitoring results"
msgstr "%(name)s a cessé de de surveiller les résultats"
#: cluster.py:474
#, python-format
msgid "%(proc_name)s ready for work at %(id)s"
msgstr "%(proc_name)s prêt pour le travail à %(id)s"
#: cluster.py:494
#, fuzzy, python-format
msgid "%(proc_name)s processing %(task_name)s '%(func_name)s'"
msgstr "%(proc_name)s exécute %(task_name)s '%(func_name)s'"
#: cluster.py:546
#, python-format
msgid "%(proc_name)s stopped doing work"
msgstr "%(proc_name)s a cessé de travailler"
#: cluster.py:751
#, python-format
msgid "%(process_name)s failed to create a task from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s Echec de la création d'une tâche à partir de Schedule "
"[%(schedule)s]"
#: cluster.py:762
#, python-format
msgid ""
"%(process_name)s created task %(task_name)s from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s a créé la tâche %(task_name)s à partir de Schedule "
"[%(schedule)s]"
#: cluster.py:808
#: cluster.py:394
msgid "Skipping cpu affinity because psutil was not found."
msgstr "L'affinité cpu ne sera pas définie car psutil n'a pas été trouvé."
#: cluster.py:813
#: cluster.py:399
msgid "Faking cpu affinity because it is not supported on this platform"
msgstr ""
"Simulation de l'affinité cpu parce qu'elle n'est pas supportée sur cette "
"plateforme."
#: cluster.py:835
#: cluster.py:421
#, python-format
msgid "%(pid)s will use cpu %(affinity)s"
msgstr "%(pid)s utilisera le CPU %(affinity)s"
#: conf.py:90
#: conf.py:122
#, python-format
msgid ""
"SAVE_LIMIT_PER (%(option)s) is not a valid option. Options are: 'group', "
@@ -195,28 +138,28 @@ msgstr ""
"'group', 'name', 'func' et None. La valeur par défaut est None."
#. Translators: Cluster status descriptions
#: conf.py:207
#: conf.py:239
msgid "Starting"
msgstr "Démarrage"
#: conf.py:208
#: conf.py:240
msgid "Working"
msgstr "Actif"
#: conf.py:209
#: conf.py:241
msgid "Idle"
msgstr "En attente"
#: conf.py:210
#: conf.py:242
msgid "Stopped"
msgstr "Arrêté"
#: conf.py:211
#: conf.py:243
msgid "Stopping"
msgstr "En cours darrêt"
#. Translators: help text for qcluster management command
#: management/commands/qcluster.py:9
#: management/commands/qcluster.py:11
msgid "Starts a Django Q Cluster."
msgstr "Démarre un cluster Django Q."
@@ -237,259 +180,485 @@ msgstr "Surveille l'utilisation mémoire du Q cluster"
msgid "Monitors Q Cluster activity"
msgstr "Surveille l'activité de Q cluster"
#: models.py:125
#: models.py:30 models.py:469
msgid "Task id"
msgstr ""
#: models.py:31
msgid "32-character task identifier."
msgstr ""
#: models.py:36 models.py:235 models.py:470
msgid "Name"
msgstr ""
#: models.py:37
msgid "Optional human-readable name for lookup and display."
msgstr ""
#: models.py:41 models.py:240 models.py:468
msgid "Function"
msgstr ""
#: models.py:42
msgid "Dotted import path to the callable, e.g. myapp.tasks.job."
msgstr ""
#: models.py:47 models.py:247
msgid "Hook"
msgstr ""
#: models.py:48
msgid "Optional dotted path to a callback that receives the task result."
msgstr ""
#: models.py:53 models.py:253 models.py:472
msgid "Arguments"
msgstr ""
#: models.py:54
msgid "Pickled positional arguments (tuple)."
msgstr ""
#: models.py:59 models.py:259 models.py:473
msgid "Keyword arguments"
msgstr ""
#: models.py:60
msgid "Pickled keyword arguments (dict)."
msgstr ""
#: models.py:65
msgid "Result"
msgstr ""
#: models.py:66
msgid "Pickled return value or error payload."
msgstr ""
#: models.py:72 models.py:471
msgid "Group"
msgstr ""
#: models.py:73
msgid "Optional group id to aggregate related tasks."
msgstr ""
#: models.py:80 models.py:328
#, fuzzy
#| msgid "Clusters"
msgid "Cluster"
msgstr "Grappes"
#: models.py:81
msgid "Name of the cluster that executed this task."
msgstr ""
#: models.py:85
msgid "Started at"
msgstr ""
#: models.py:86
msgid "When the worker started executing the task."
msgstr ""
#: models.py:90
#, fuzzy
#| msgid "Stopped"
msgid "Stopped at"
msgstr "Arrêté"
#: models.py:91
msgid "When the worker finished (success or failure)."
msgstr ""
#: models.py:96 monitor_terminal.py:180
msgid "Success"
msgstr "Succès"
#: models.py:97
msgid "False if the task raised an exception or timed out."
msgstr ""
#: models.py:101
msgid "Attempt count"
msgstr ""
#: models.py:102
msgid "How many times execution was attempted."
msgstr ""
#: models.py:172
msgid "Task"
msgstr ""
#: models.py:173
msgid "Tasks"
msgstr ""
#: models.py:194
msgid "Successful task"
msgstr "Tâche réussie"
#: models.py:126
#: models.py:195
msgid "Successful tasks"
msgstr "Tâches réussies"
#: models.py:141
#: models.py:210
msgid "Failed task"
msgstr "Tâche échoué"
#: models.py:142
#: models.py:211
msgid "Failed tasks"
msgstr "Tâches échouées"
#: models.py:150 models.py:234
#: models.py:219 models.py:347
msgid "Please install croniter to enable cron expressions"
msgstr "Veuillez installer croniter pour activer les expressions cron."
#: models.py:170
#: models.py:236
msgid "Optional label to identify this schedule in the admin."
msgstr ""
#: models.py:241
msgid "e.g. module.tasks.function"
msgstr ""
#: models.py:248
msgid "e.g. module.tasks.result_function"
msgstr ""
#: models.py:254
msgid "e.g. 1, 2, 'John'"
msgstr "ex. 1, 2, Jean"
#: models.py:172
#: models.py:260
msgid "e.g. x=1, y=2, name='John'"
msgstr "p. ex. x = 1, y = 2, Nom = Jean"
#: models.py:186
#: models.py:274
msgid "Once"
msgstr "Une fois"
#: models.py:187
#: models.py:275 models.py:296
msgid "Minutes"
msgstr "Minutes"
#: models.py:188
#: models.py:276
msgid "Hourly"
msgstr "Toutes les heures"
#: models.py:189
#: models.py:277
msgid "Daily"
msgstr "Quotidien"
#: models.py:190
#: models.py:278
msgid "Weekly"
msgstr "Hebdomadaire"
#: models.py:191
#: models.py:279
#, fuzzy
#| msgid "Weekly"
msgid "Biweekly"
msgstr "Bihebdomadaire"
#: models.py:192
#: models.py:280
msgid "Monthly"
msgstr "Mensuel"
#: models.py:193
#: models.py:281
#, fuzzy
#| msgid "Monthly"
msgid "Bimonthly"
msgstr "Bimestriel"
#: models.py:194
#: models.py:282
msgid "Quarterly"
msgstr "Trimestriel"
#: models.py:195
#: models.py:283
msgid "Yearly"
msgstr "Annuel"
#: models.py:196
#: models.py:284 models.py:313
msgid "Cron"
msgstr "Cron"
#: models.py:199
#: models.py:290
msgid "Schedule Type"
msgstr "Type de plannification"
#: models.py:202
#: models.py:291
msgid "How often this task should be enqueued."
msgstr ""
#: models.py:297
msgid "Number of minutes for the Minutes type"
msgstr "Nombre de minutes pour le type de minutes"
#: models.py:205
#: models.py:300
msgid "Repeats"
msgstr "Répéter"
#: models.py:205
#: models.py:300
msgid "n = n times, -1 = forever"
msgstr "n = n fois,-1 = Toujours"
#: models.py:208
#: models.py:303
msgid "Next Run"
msgstr "Prochaine exécution"
#: models.py:215
#: models.py:304
msgid "When this schedule runs next (stored in UTC)."
msgstr ""
#: models.py:314
msgid "Cron expression"
msgstr "Expression Cron"
#: models.py:224
#: models.py:320
msgid "Last task id"
msgstr ""
#: models.py:321
msgid "Id of the last task spawned from this schedule (read-only)."
msgstr ""
#: models.py:329 models.py:427
msgid "Name of the target cluster"
msgstr ""
#: models.py:336
msgid "Intended date kwarg"
msgstr ""
#: models.py:337
msgid "Name of kwarg to pass intended schedule date"
msgstr "Nom du kwarg à passer Date prévue de l'horaire"
#: models.py:299
#: models.py:418
msgid "Scheduled task"
msgstr "Tâche planifiée"
#: models.py:300
#: models.py:419
msgid "Scheduled tasks"
msgstr "Tâches planifiées"
#: models.py:326
#: models.py:426
#, fuzzy
#| msgid "Clusters"
msgid "Cluster key"
msgstr "Grappes"
#: models.py:430
msgid "Payload"
msgstr ""
#: models.py:431
msgid "Signed serialized task package (do not edit manually)."
msgstr ""
#: models.py:435
msgid "Lock until"
msgstr ""
#: models.py:436
msgid "Prevent any cluster from pulling until"
msgstr ""
#: models.py:474
#, fuzzy
#| msgid "Queued task"
msgid "Queue options"
msgstr "Tâche en file d'attente"
#: models.py:478
msgid "Queued task"
msgstr "Tâche en file d'attente"
#: models.py:327
#: models.py:479
msgid "Queued tasks"
msgstr "Tâches en file d'attente"
#: monitor.py:64 monitor.py:348
#: monitor.py:41
#, python-format
msgid "%(name)s monitoring at %(id)s"
msgstr "%(name)s surveille les résultats %(id)s"
#: monitor.py:61
#, python-format
msgid "Processed '%(info_name)s' (%(task_name)s)"
msgstr "traité '%(info_name)s' (%(task_name)s)"
#: monitor.py:67
#, python-format
msgid "Failed '%(info_name)s' (%(task_name)s) - %(task_result)s"
msgstr "Manqué '%(info_name)s' (%(task_name)s) - %(task_result)s"
#: monitor.py:74
#, python-format
msgid "%(name)s stopped monitoring results"
msgstr "%(name)s a cessé de de surveiller les résultats"
#: monitor_terminal.py:64 monitor_terminal.py:348
msgid "Host"
msgstr "Hôte"
#: monitor.py:68 monitor.py:352 monitor.py:459
#: monitor_terminal.py:68 monitor_terminal.py:352 monitor_terminal.py:457
msgid "Id"
msgstr "Id"
#: monitor.py:72
#: monitor_terminal.py:72
msgid "State"
msgstr "Statut"
#: monitor.py:76
#: monitor_terminal.py:76
msgid "Pool"
msgstr "Piscine"
#: monitor.py:80
#: monitor_terminal.py:80
msgid "TQ"
msgstr "TQ"
#: monitor.py:84
#: monitor_terminal.py:84
msgid "RQ"
msgstr "RQ"
#: monitor.py:88
#: monitor_terminal.py:88
msgid "RC"
msgstr "RC"
#: monitor.py:92
#: monitor_terminal.py:92
msgid "Up"
msgstr "Haut"
#: monitor.py:172 monitor.py:286
#: monitor_terminal.py:172 monitor_terminal.py:286
msgid "Queued"
msgstr "En file d'attente"
#: monitor.py:180
msgid "Success"
msgstr "Succès"
#: monitor.py:190 monitor.py:294
#: monitor_terminal.py:190 monitor_terminal.py:294
msgid "Failures"
msgstr "Défaillances"
#: monitor.py:201 monitor.py:498
#: monitor_terminal.py:201 monitor_terminal.py:496
msgid "[Press q to quit]"
msgstr "[appuyez sur q pour quitter]"
#: monitor.py:227
#: monitor_terminal.py:227
msgid "day"
msgstr "jour"
#: monitor.py:248
#: monitor_terminal.py:248
msgid "second"
msgstr "seconde"
#: monitor.py:251
#: monitor_terminal.py:251
msgid "minute"
msgstr "minute"
#: monitor.py:254
#: monitor_terminal.py:254
msgid "hour"
msgstr "heure"
#: monitor.py:263
#: monitor_terminal.py:263
#, python-format
msgid "-- %(prefix)s %(version)s on %(info)s --"
msgstr "--%(prefix)s %(version)s sur %(info)s --"
#: monitor.py:273
#: monitor_terminal.py:273
msgid "Clusters"
msgstr "Grappes"
#: monitor.py:277
#: monitor_terminal.py:277
msgid "Workers"
msgstr "Processus"
#: monitor.py:281
#: monitor_terminal.py:281
msgid "Restarts"
msgstr "Redémarrages"
#: monitor.py:290
#: monitor_terminal.py:290
msgid "Successes"
msgstr "Succès"
#: monitor.py:299
#: monitor_terminal.py:299
msgid "Schedules"
msgstr "Planifications"
#: monitor.py:303
#: monitor_terminal.py:303
#, python-format
msgid "Tasks/%(per)s"
msgstr "Tâches/%(per)s"
#: monitor.py:307
#: monitor_terminal.py:307
msgid "Avg time"
msgstr "Temps Moyen"
#: monitor.py:357
#: monitor_terminal.py:357
msgid "Available (%)"
msgstr ""
#: monitor.py:363
#: monitor_terminal.py:363
msgid "Available (MB)"
msgstr "Disponible sur (MB)"
#: monitor.py:368
#: monitor_terminal.py:368
msgid "Total (MB)"
msgstr "Total (MB)"
#: monitor.py:373
#: monitor_terminal.py:373
msgid "Sentinel (MB)"
msgstr "Sentinel (MB)"
#: monitor.py:379
#: monitor_terminal.py:379
msgid "Monitor (MB)"
msgstr "Monitor (MB)"
#: monitor.py:385
#: monitor_terminal.py:385
#, fuzzy
#| msgid "Workers"
msgid "Workers (MB)"
msgstr "Processus (MB)"
#: monitor.py:487
#: monitor_terminal.py:485
#, python-format
msgid "Available lowest (): %(memory_percent)s ((at)s)"
msgstr "Disponible le plus bas () : %(memory_percent)s ((at)s)"
#: monitor.py:509
#: monitor_terminal.py:507
msgid "No clusters appear to be running."
msgstr "Aucun cluster ne semble être en cours d'exécution."
#: pusher.py:40
#, python-format
msgid "%(name)s pushing tasks at %(id)s"
msgstr "%(name)s répartit les tâches %(id)s"
#: pusher.py:67
#, python-format
msgid "queueing from %(list_key)s"
msgstr "mise en file d'attente de %(list_key)s"
#: pusher.py:71
#, python-format
msgid "%(name)s stopped pushing tasks"
msgstr "%(name)s a cessé de répartir les tâches"
#: scheduler.py:110
#, python-format
msgid "%(process_name)s failed to create a task from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s Echec de la création d'une tâche à partir de Schedule "
"[%(schedule)s]"
#: scheduler.py:121
#, python-format
msgid ""
"%(process_name)s created task %(task_name)s from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s a créé la tâche %(task_name)s à partir de Schedule "
"[%(schedule)s]"
#: signals.py:22
#, python-format
msgid "malformed return hook '%(hook)s' for [%(name)s]"
@@ -500,6 +669,25 @@ msgstr "hook de retour '%(hook)s' mal formé pour [%(name)s]"
msgid "return hook %(hook)s failed on [%(name)s] because %(error)s"
msgstr "hook de retour %(hook)s a échoué sur [%(name)s] à cause de %(error)s"
#: timeout.py:30 timeout.py:43
msgid "SIGALARM is not available on your platform"
msgstr ""
#: worker.py:49
#, python-format
msgid "%(proc_name)s ready for work at %(id)s"
msgstr "%(proc_name)s prêt pour le travail à %(id)s"
#: worker.py:69
#, fuzzy, python-format
msgid "%(proc_name)s processing %(task_name)s '%(func_name)s'"
msgstr "%(proc_name)s exécute %(task_name)s '%(func_name)s'"
#: worker.py:135
#, python-format
msgid "%(proc_name)s stopped doing work"
msgstr "%(proc_name)s a cessé de travailler"
#, python-format
#~ msgid ""
#~ "Could not process '%(func_name)s'. Check the location of the function and "

View File

@@ -0,0 +1,676 @@
# Russian translation for django-q.
# Copyright (C) Django Q authors
# This file is distributed under the same license as the django-q package.
#
msgid ""
msgstr ""
"Project-Id-Version: django-q\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-17 21:07+0000\n"
"PO-Revision-Date: 2026-04-04 12:00+0000\n"
"Last-Translator: \n"
"Language-Team: Russian\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || "
"(n%100>=11 && n%100<=14)? 2 : 3);\n"
#: django_q/admin.py:30
msgid "Resubmit selected tasks to queue"
msgstr "Повторно отправить выбранные задачи в очередь"
#: django_q/admin.py:64
msgid "Time Taken"
msgstr "Время выполнения"
#: django_q/admin.py:95
msgid "Short result"
msgstr "Краткий результат"
#: django_q/admin.py:142 django_q/models.py:412
msgid "success"
msgstr "успех"
#: django_q/admin.py:154 django_q/models.py:414
msgid "last_run"
msgstr "последний запуск"
#: django_q/cluster.py:75
#, python-format
msgid "Q Cluster %(name)s starting."
msgstr "Запуск кластера Q %(name)s."
#: django_q/cluster.py:83
#, python-format
msgid "Q Cluster %(name)s stopping."
msgstr "Остановка кластера Q %(name)s."
#: django_q/cluster.py:86
#, python-format
msgid "Q Cluster %(name)s has stopped."
msgstr "Кластер Q %(name)s остановлен."
#: django_q/cluster.py:93
#, python-format
msgid "%(name)s got signal %(signal)s"
msgstr "%(name)s получил сигнал %(signal)s"
#: django_q/cluster.py:224
#, python-format
msgid "reincarnated monitor %(name)s after sudden death"
msgstr "монитор %(name)s перезапущен после аварийного завершения"
#: django_q/cluster.py:230
#, python-format
msgid "reincarnated pusher %(name)s after sudden death"
msgstr "пушер %(name)s перезапущен после аварийного завершения"
#: django_q/cluster.py:262
#, python-format
msgid ""
"reincarnated worker %(name)s after timeout while processing task "
"%(task_name)s"
msgstr ""
"воркер %(name)s перезапущен по таймауту при выполнении задачи %(task_name)s"
#: django_q/cluster.py:265
#, python-format
msgid "reincarnated worker %(name)s after timeout"
msgstr "воркер %(name)s перезапущен по таймауту"
#: django_q/cluster.py:270
#, python-format
msgid "recycled worker %(name)s"
msgstr "воркер %(name)s перезапущен (recycle)"
#: django_q/cluster.py:273
#, python-format
msgid "reincarnated worker %(name)s after death"
msgstr "воркер %(name)s перезапущен после сбоя"
#: django_q/cluster.py:297
#, python-format
msgid "%(name)s guarding cluster %(cluster_name)s"
msgstr "%(name)s обслуживает кластер %(cluster_name)s"
#: django_q/cluster.py:307
#, python-format
msgid "Q Cluster %(cluster_name)s running."
msgstr "Кластер Q %(cluster_name)s работает."
#: django_q/cluster.py:346
#, python-format
msgid "%(name)s stopping cluster processes"
msgstr "%(name)s останавливает процессы кластера"
#: django_q/cluster.py:371
#, python-format
msgid "%(name)s waiting for the monitor."
msgstr "%(name)s ожидает монитор."
#: django_q/cluster.py:394
msgid "Skipping cpu affinity because psutil was not found."
msgstr "Привязка к CPU пропущена: не найден psutil."
#: django_q/cluster.py:399
msgid "Faking cpu affinity because it is not supported on this platform"
msgstr "Имитация привязки к CPU: на этой платформе не поддерживается"
#: django_q/cluster.py:421
#, python-format
msgid "%(pid)s will use cpu %(affinity)s"
msgstr "%(pid)s будет использовать CPU %(affinity)s"
#: django_q/conf.py:122
#, python-format
msgid ""
"SAVE_LIMIT_PER (%(option)s) is not a valid option. Options are: 'group', "
"'name', 'func' and None. Default is None."
msgstr ""
"SAVE_LIMIT_PER (%(option)s) — недопустимое значение. Допустимо: «group», "
"«name», «func» и None. По умолчанию None."
#. Translators: Cluster status descriptions
#: django_q/conf.py:239
msgid "Starting"
msgstr "Запуск"
#: django_q/conf.py:240
msgid "Working"
msgstr "Работает"
#: django_q/conf.py:241
msgid "Idle"
msgstr "Простой"
#: django_q/conf.py:242
msgid "Stopped"
msgstr "Остановлен"
#: django_q/conf.py:243
msgid "Stopping"
msgstr "Останавливается"
#. Translators: help text for qcluster management command
#: django_q/management/commands/qcluster.py:11
msgid "Starts a Django Q Cluster."
msgstr "Запускает кластер Django Q."
#. Translators: help text for qinfo management command
#: django_q/management/commands/qinfo.py:11
msgid "General information over all clusters."
msgstr "Общая информация по всем кластерам."
#. Translators: help text for qmemory management command
#: django_q/management/commands/qmemory.py:9
msgid "Monitors Q Cluster memory usage"
msgstr "Мониторинг использования памяти кластером Q"
#. Translators: help text for qmonitor management command
#: django_q/management/commands/qmonitor.py:9
msgid "Monitors Q Cluster activity"
msgstr "Мониторинг активности кластера Q"
#: django_q/models.py:30 django_q/models.py:469
msgid "Task id"
msgstr "Идентификатор задачи"
#: django_q/models.py:31
msgid "32-character task identifier."
msgstr "32-символьный идентификатор задачи."
#: django_q/models.py:36 django_q/models.py:235 django_q/models.py:470
msgid "Name"
msgstr "Имя"
#: django_q/models.py:37
msgid "Optional human-readable name for lookup and display."
msgstr "Необязательное имя для поиска и отображения."
#: django_q/models.py:41 django_q/models.py:240 django_q/models.py:468
msgid "Function"
msgstr "Функция"
#: django_q/models.py:42
msgid "Dotted import path to the callable, e.g. myapp.tasks.job."
msgstr "Путь импорта к вызываемому объекту, напр. myapp.tasks.job."
#: django_q/models.py:47 django_q/models.py:247
msgid "Hook"
msgstr "Обработчик"
#: django_q/models.py:48
msgid "Optional dotted path to a callback that receives the task result."
msgstr "Необязательный путь к функции, получающей результат задачи."
#: django_q/models.py:53 django_q/models.py:253 django_q/models.py:472
msgid "Arguments"
msgstr "Аргументы"
#: django_q/models.py:54
msgid "Pickled positional arguments (tuple)."
msgstr "Сериализованные позиционные аргументы (tuple)."
#: django_q/models.py:59 django_q/models.py:259 django_q/models.py:473
msgid "Keyword arguments"
msgstr "Именованные аргументы"
#: django_q/models.py:60
msgid "Pickled keyword arguments (dict)."
msgstr "Сериализованные именованные аргументы (dict)."
#: django_q/models.py:65
msgid "Result"
msgstr "Результат"
#: django_q/models.py:66
msgid "Pickled return value or error payload."
msgstr "Сериализованный результат или данные об ошибке."
#: django_q/models.py:72 django_q/models.py:471
msgid "Group"
msgstr "Группа"
#: django_q/models.py:73
msgid "Optional group id to aggregate related tasks."
msgstr "Необязательный идентификатор группы связанных задач."
#: django_q/models.py:80 django_q/models.py:328
msgid "Cluster"
msgstr "Кластер"
#: django_q/models.py:81
msgid "Name of the cluster that executed this task."
msgstr "Имя кластера, выполнившего эту задачу."
#: django_q/models.py:85
msgid "Started at"
msgstr "Начало выполнения"
#: django_q/models.py:86
msgid "When the worker started executing the task."
msgstr "Когда воркер начал выполнение задачи."
#: django_q/models.py:90
msgid "Stopped at"
msgstr "Окончание выполнения"
#: django_q/models.py:91
msgid "When the worker finished (success or failure)."
msgstr "Когда воркер завершил выполнение (успех или ошибка)."
#: django_q/models.py:96 django_q/monitor_terminal.py:180
msgid "Success"
msgstr "Успех"
#: django_q/models.py:97
msgid "False if the task raised an exception or timed out."
msgstr "Ложь, если задача завершилась исключением или таймаутом."
#: django_q/models.py:101
msgid "Attempt count"
msgstr "Число попыток"
#: django_q/models.py:102
msgid "How many times execution was attempted."
msgstr "Сколько раз выполнялась попытка запуска."
#: django_q/models.py:172
msgid "Task"
msgstr "Задача"
#: django_q/models.py:173
msgid "Tasks"
msgstr "Задачи"
#: django_q/models.py:194
msgid "Successful task"
msgstr "Успешная задача"
#: django_q/models.py:195
msgid "Successful tasks"
msgstr "Успешные задачи"
#: django_q/models.py:210
msgid "Failed task"
msgstr "Неудачная задача"
#: django_q/models.py:211
msgid "Failed tasks"
msgstr "Неудачные задачи"
#: django_q/models.py:219 django_q/models.py:347
msgid "Please install croniter to enable cron expressions"
msgstr "Установите croniter для поддержки cron-выражений"
#: django_q/models.py:236
msgid "Optional label to identify this schedule in the admin."
msgstr "Необязательная подпись для расписания в админке."
#: django_q/models.py:241
msgid "e.g. module.tasks.function"
msgstr "например: module.tasks.function"
#: django_q/models.py:248
msgid "e.g. module.tasks.result_function"
msgstr "например: module.tasks.result_function "
#: django_q/models.py:254
msgid "e.g. 1, 2, 'John'"
msgstr "например: 1, 2, 'Алимхан'"
#: django_q/models.py:260
msgid "e.g. x=1, y=2, name='John'"
msgstr "например: x=1, y=2, name='Алимхан'"
#: django_q/models.py:274
msgid "Once"
msgstr "Один раз"
#: django_q/models.py:275 django_q/models.py:296
msgid "Minutes"
msgstr "Минуты"
#: django_q/models.py:276
msgid "Hourly"
msgstr "Ежечасно"
#: django_q/models.py:277
msgid "Daily"
msgstr "Ежедневно"
#: django_q/models.py:278
msgid "Weekly"
msgstr "Еженедельно"
#: django_q/models.py:279
msgid "Biweekly"
msgstr "Раз в две недели"
#: django_q/models.py:280
msgid "Monthly"
msgstr "Ежемесячно"
#: django_q/models.py:281
msgid "Bimonthly"
msgstr "Раз в два месяца"
#: django_q/models.py:282
msgid "Quarterly"
msgstr "Ежеквартально"
#: django_q/models.py:283
msgid "Yearly"
msgstr "Ежегодно"
#: django_q/models.py:284 django_q/models.py:313
msgid "Cron"
msgstr "Cron"
#: django_q/models.py:290
msgid "Schedule Type"
msgstr "Тип расписания"
#: django_q/models.py:291
msgid "How often this task should be enqueued."
msgstr "Как часто ставить эту задачу в очередь."
#: django_q/models.py:297
msgid "Number of minutes for the Minutes type"
msgstr "Число минут для типа «Минуты»"
#: django_q/models.py:300
msgid "Repeats"
msgstr "Повторы"
#: django_q/models.py:300
msgid "n = n times, -1 = forever"
msgstr "n — число повторов, -1 — бесконечно"
#: django_q/models.py:303
msgid "Next Run"
msgstr "Следующий запуск"
#: django_q/models.py:304
msgid "When this schedule runs next (stored in UTC)."
msgstr "Когда следующий запуск по расписанию (в UTC)."
#: django_q/models.py:314
msgid "Cron expression"
msgstr "Cron-выражение"
#: django_q/models.py:320
msgid "Last task id"
msgstr "ID последней задачи"
#: django_q/models.py:321
msgid "Id of the last task spawned from this schedule (read-only)."
msgstr "Идентификатор последней задачи по этому расписанию (read-only)."
#: django_q/models.py:329 django_q/models.py:427
msgid "Name of the target cluster"
msgstr "Имя целевого кластера"
#: django_q/models.py:336
msgid "Intended date kwarg"
msgstr "Имя аргумента для даты запуска"
#: django_q/models.py:337
msgid "Name of kwarg to pass intended schedule date"
msgstr "Имя kwargs для передачи запланированной даты"
#: django_q/models.py:418
msgid "Scheduled task"
msgstr "Запланированная задача"
#: django_q/models.py:419
msgid "Scheduled tasks"
msgstr "Запланированные задачи"
#: django_q/models.py:426
msgid "Cluster key"
msgstr "Ключ кластера"
#: django_q/models.py:430
msgid "Payload"
msgstr "Данные задачи"
#: django_q/models.py:431
msgid "Signed serialized task package (do not edit manually)."
msgstr "Подписанный сериализованный пакет задачи (не редактировать вручную)."
#: django_q/models.py:435
msgid "Lock until"
msgstr "Блокировка до"
#: django_q/models.py:436
msgid "Prevent any cluster from pulling until"
msgstr "Не забирать задачи кластерами до"
#: django_q/models.py:474
msgid "Queue options"
msgstr "Дополнительные параметры очереди"
#: django_q/models.py:478
msgid "Queued task"
msgstr "Задача в очереди"
#: django_q/models.py:479
msgid "Queued tasks"
msgstr "Задачи в очереди"
#: django_q/monitor.py:41
#, python-format
msgid "%(name)s monitoring at %(id)s"
msgstr "%(name)s мониторинг на %(id)s"
#: django_q/monitor.py:61
#, python-format
msgid "Processed '%(info_name)s' (%(task_name)s)"
msgstr "Обработано «%(info_name)s» (%(task_name)s)"
#: django_q/monitor.py:67
#, python-format
msgid "Failed '%(info_name)s' (%(task_name)s) - %(task_result)s"
msgstr "Ошибка «%(info_name)s» (%(task_name)s) — %(task_result)s"
#: django_q/monitor.py:74
#, python-format
msgid "%(name)s stopped monitoring results"
msgstr "%(name)s прекратил отслеживание результатов"
#: django_q/monitor_terminal.py:64 django_q/monitor_terminal.py:348
msgid "Host"
msgstr "Хост"
#: django_q/monitor_terminal.py:68 django_q/monitor_terminal.py:352
#: django_q/monitor_terminal.py:457
msgid "Id"
msgstr "Id"
#: django_q/monitor_terminal.py:72
msgid "State"
msgstr "Состояние"
#: django_q/monitor_terminal.py:76
msgid "Pool"
msgstr "Пул"
#: django_q/monitor_terminal.py:80
msgid "TQ"
msgstr "TQ"
#: django_q/monitor_terminal.py:84
msgid "RQ"
msgstr "RQ"
#: django_q/monitor_terminal.py:88
msgid "RC"
msgstr "RC"
#: django_q/monitor_terminal.py:92
msgid "Up"
msgstr "Аптайм"
#: django_q/monitor_terminal.py:172 django_q/monitor_terminal.py:286
msgid "Queued"
msgstr "В очереди"
#: django_q/monitor_terminal.py:190 django_q/monitor_terminal.py:294
msgid "Failures"
msgstr "Сбои"
#: django_q/monitor_terminal.py:201 django_q/monitor_terminal.py:496
msgid "[Press q to quit]"
msgstr "[Нажмите q для выхода]"
#: django_q/monitor_terminal.py:227
msgid "day"
msgstr "день"
#: django_q/monitor_terminal.py:248
msgid "second"
msgstr "секунда"
#: django_q/monitor_terminal.py:251
msgid "minute"
msgstr "минута"
#: django_q/monitor_terminal.py:254
msgid "hour"
msgstr "час"
#: django_q/monitor_terminal.py:263
#, python-format
msgid "-- %(prefix)s %(version)s on %(info)s --"
msgstr "-- %(prefix)s %(version)s на %(info)s --"
#: django_q/monitor_terminal.py:273
msgid "Clusters"
msgstr "Кластеры"
#: django_q/monitor_terminal.py:277
msgid "Workers"
msgstr "Воркеры"
#: django_q/monitor_terminal.py:281
msgid "Restarts"
msgstr "Перезапуски"
#: django_q/monitor_terminal.py:290
msgid "Successes"
msgstr "Успехи"
#: django_q/monitor_terminal.py:299
msgid "Schedules"
msgstr "Расписания"
#: django_q/monitor_terminal.py:303
#, python-format
msgid "Tasks/%(per)s"
msgstr "Задач/%(per)s"
#: django_q/monitor_terminal.py:307
msgid "Avg time"
msgstr "Среднее время"
#: django_q/monitor_terminal.py:357
msgid "Available (%)"
msgstr "Доступно (%)"
#: django_q/monitor_terminal.py:363
msgid "Available (MB)"
msgstr "Доступно (МБ)"
#: django_q/monitor_terminal.py:368
msgid "Total (MB)"
msgstr "Всего (МБ)"
#: django_q/monitor_terminal.py:373
msgid "Sentinel (MB)"
msgstr "Sentinel (МБ)"
#: django_q/monitor_terminal.py:379
msgid "Monitor (MB)"
msgstr "Монитор (МБ)"
#: django_q/monitor_terminal.py:385
msgid "Workers (MB)"
msgstr "Воркеры (МБ)"
#: django_q/monitor_terminal.py:485
#, python-format
msgid "Available lowest (): %(memory_percent)s ((at)s)"
msgstr "Минимум доступно (): %(memory_percent)s ((at)s)"
#: django_q/monitor_terminal.py:507
msgid "No clusters appear to be running."
msgstr "Похоже, ни один кластер не запущен."
#: django_q/pusher.py:40
#, python-format
msgid "%(name)s pushing tasks at %(id)s"
msgstr "%(name)s отправляет задачи на %(id)s"
#: django_q/pusher.py:67
#, python-format
msgid "queueing from %(list_key)s"
msgstr "очередь из %(list_key)s"
#: django_q/pusher.py:71
#, python-format
msgid "%(name)s stopped pushing tasks"
msgstr "%(name)s прекратил отправку задач"
#: django_q/scheduler.py:110
#, python-format
msgid "%(process_name)s failed to create a task from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s не удалось создать задачу по расписанию [%(schedule)s]"
#: django_q/scheduler.py:121
#, python-format
msgid ""
"%(process_name)s created task %(task_name)s from schedule [%(schedule)s]"
msgstr ""
"%(process_name)s создал задачу %(task_name)s по расписанию [%(schedule)s]"
#: django_q/signals.py:22
#, python-format
msgid "malformed return hook '%(hook)s' for [%(name)s]"
msgstr "некорректный return hook «%(hook)s» для [%(name)s]"
#: django_q/signals.py:30
#, python-format
msgid "return hook %(hook)s failed on [%(name)s] because %(error)s"
msgstr "return hook %(hook)s для [%(name)s] завершился ошибкой: %(error)s"
#: django_q/timeout.py:30 django_q/timeout.py:43
msgid "SIGALARM is not available on your platform"
msgstr "SIGALRM недоступен на вашей платформе"
#: django_q/worker.py:49
#, python-format
msgid "%(proc_name)s ready for work at %(id)s"
msgstr "%(proc_name)s готов к работе на %(id)s"
#: django_q/worker.py:69
#, python-format
msgid "%(proc_name)s processing %(task_name)s '%(func_name)s'"
msgstr "%(proc_name)s выполняет %(task_name)s «%(func_name)s»"
#: django_q/worker.py:135
#, python-format
msgid "%(proc_name)s stopped doing work"
msgstr "%(proc_name)s прекратил работу"
#, python-format
#~ msgid ""
#~ "Could not process '%(func_name)s'. Check the location of the function and "
#~ "the args/kwargs."
#~ msgstr ""
#~ "Не удалось обработать «%(func_name)s». Проверьте путь к функции и args/"
#~ "kwargs."

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-26 01:38+0000\n"
"POT-Creation-Date: 2026-04-17 21:10+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Ethem Güner <ethemguener@gmail.com>\n"
"Language-Team: \n"
@@ -18,36 +18,44 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: admin.py:43
#: admin.py:30
msgid "Resubmit selected tasks to queue"
msgstr "Seçili işleri kuyruğa tekrar gönder"
#: admin.py:107 models.py:293
#: admin.py:64
msgid "Time Taken"
msgstr ""
#: admin.py:95
msgid "Short result"
msgstr ""
#: admin.py:142 models.py:412
#, fuzzy
#| msgid "Success"
msgid "success"
msgstr "başarılı olanlar"
#: admin.py:119 models.py:295
#: admin.py:154 models.py:414
msgid "last_run"
msgstr ""
#: cluster.py:79
#: cluster.py:75
#, python-format
msgid "Q Cluster %(name)s starting."
msgstr "Q Cluster %(name)s başlatılıyor."
#: cluster.py:87
#: cluster.py:83
#, python-format
msgid "Q Cluster %(name)s stopping."
msgstr "Q Cluster %(name)s durduruluyor."
#: cluster.py:90
#: cluster.py:86
#, python-format
msgid "Q Cluster %(name)s has stopped."
msgstr "Q Cluster %(name)s durduruldu."
#: cluster.py:97
#: cluster.py:93
#, python-format
msgid "%(name)s got signal %(signal)s"
msgstr "%(name)s, %(signal)s pid'inde izleniyor/monitoring yapılıyor."
@@ -62,7 +70,7 @@ msgstr "Monitor %(name)s ani ölüm sonrası tekrar dirildi"
msgid "reincarnated pusher %(name)s after sudden death"
msgstr "Pusher %(name)s ani ölüm sonrası tekrar dirildi"
#: cluster.py:250
#: cluster.py:262
#, fuzzy, python-format
#| msgid "reincarnated worker %(name)s after timeout"
msgid ""
@@ -70,119 +78,55 @@ msgid ""
"%(task_name)s"
msgstr "Worker %(name)s zaman aşımı sonrası tekrar dirildi"
#: cluster.py:255
#: cluster.py:265
#, python-format
msgid "reincarnated worker %(name)s after timeout"
msgstr "Worker %(name)s zaman aşımı sonrası tekrar dirildi"
#: cluster.py:260
#: cluster.py:270
#, python-format
msgid "recycled worker %(name)s"
msgstr "Worker %(name)s geri döndürüldü"
#: cluster.py:263
#: cluster.py:273
#, python-format
msgid "reincarnated worker %(name)s after death"
msgstr "Worker %(name)s ani ölüm sonrası tekrar dirildi"
#: cluster.py:287
#: cluster.py:297
#, python-format
msgid "%(name)s guarding cluster %(cluster_name)s"
msgstr "%(name)s, %(cluster_name)s cluster'ını koruyor"
#: cluster.py:296
#: cluster.py:307
#, python-format
msgid "Q Cluster %(cluster_name)s running."
msgstr "Q Cluster %(cluster_name)s başlatılıyor."
#: cluster.py:332
#: cluster.py:346
#, python-format
msgid "%(name)s stopping cluster processes"
msgstr "Cluster %(name)s işlemleri durduruluyor."
#: cluster.py:357
#: cluster.py:371
#, python-format
msgid "%(name)s waiting for the monitor."
msgstr "%(name)s monitor için bekliyor."
#: cluster.py:383
#, fuzzy, python-format
#| msgid "%(process_name)s pushing tasks at %(id)s"
msgid "%(name)s pushing tasks at %(id)s"
msgstr "%(process_name)s, işleri %(id)s pid'ine gönderiyor."
#: cluster.py:407
#, python-format
msgid "queueing from %(list_key)s"
msgstr ""
#: cluster.py:411
#, python-format
msgid "%(name)s stopped pushing tasks"
msgstr "%(name)s işleri göndermeyi durdurdu"
#: cluster.py:426
#, python-format
msgid "%(name)s monitoring at %(id)s"
msgstr "%(name)s, %(id)s pid'inde izleniyor/monitoring yapılıyor."
#: cluster.py:445
#, python-format
msgid "Processed '%(info_name)s' (%(task_name)s)"
msgstr "[%(task_name)s] - '%(info_name)s işlendi."
#: cluster.py:451
#, python-format
msgid "Failed '%(info_name)s' (%(task_name)s) - %(task_result)s"
msgstr "[%(task_name)s] - '%(info_name)s' - %(task_result)s başarısız oldu"
#: cluster.py:458
#, python-format
msgid "%(name)s stopped monitoring results"
msgstr "%(name)s sonuçları göstermeyi bıraktı"
#: cluster.py:474
#, python-format
msgid "%(proc_name)s ready for work at %(id)s"
msgstr "%(proc_name)s, %(id)s pid'inde çalışmaya hazır"
#: cluster.py:494
#, fuzzy, python-format
#| msgid "%(proc_name)s processing '%(func_name)s' (%(task_name)s)"
msgid "%(proc_name)s processing %(task_name)s '%(func_name)s'"
msgstr "%(proc_name)s, '%(func_name)s' [%(task_name)s] işlerini işiyor"
#: cluster.py:546
#, python-format
msgid "%(proc_name)s stopped doing work"
msgstr "%(proc_name)s çalışmayı bıraktı"
#: cluster.py:751
#, python-format
msgid "%(process_name)s failed to create a task from schedule [%(schedule)s]"
msgstr "%(process_name)s programdan bir görev oluşturamadı [%(schedule)s]"
#: cluster.py:762
#, fuzzy, python-format
#| msgid "%(process_name)s created a task from schedule [%(schedule)s]"
msgid ""
"%(process_name)s created task %(task_name)s from schedule [%(schedule)s]"
msgstr "%(process_name)s programdan bir görev oluşturamadı [%(schedule)s]"
#: cluster.py:808
#: cluster.py:394
msgid "Skipping cpu affinity because psutil was not found."
msgstr "Psutil bulunamadığı için cpu benzeşimi atlanıyor."
#: cluster.py:813
#: cluster.py:399
msgid "Faking cpu affinity because it is not supported on this platform"
msgstr "Bu platformda desteklenmediği için sahte cpu benzeşimi"
#: cluster.py:835
#: cluster.py:421
#, python-format
msgid "%(pid)s will use cpu %(affinity)s"
msgstr "%(pid)s cpu %(affinity)s kullanacaktır"
#: conf.py:90
#: conf.py:122
#, python-format
msgid ""
"SAVE_LIMIT_PER (%(option)s) is not a valid option. Options are: 'group', "
@@ -192,28 +136,28 @@ msgstr ""
"'group', 'name', 'func' ve None. Varsayılan değer None'dır."
#. Translators: Cluster status descriptions
#: conf.py:207
#: conf.py:239
msgid "Starting"
msgstr "Başlıyor"
#: conf.py:208
#: conf.py:240
msgid "Working"
msgstr "Çalışıyor"
#: conf.py:209
#: conf.py:241
msgid "Idle"
msgstr "Boşta"
#: conf.py:210
#: conf.py:242
msgid "Stopped"
msgstr "Durdu"
#: conf.py:211
#: conf.py:243
msgid "Stopping"
msgstr "Durduruluyor"
#. Translators: help text for qcluster management command
#: management/commands/qcluster.py:9
#: management/commands/qcluster.py:11
msgid "Starts a Django Q Cluster."
msgstr "Bir Django Q Cluster çalıştırır."
@@ -232,257 +176,475 @@ msgstr "Q Cluster'ın bellek kullanımını izler"
msgid "Monitors Q Cluster activity"
msgstr "Q Cluster'ın aktivitelerini izler"
#: models.py:125
#: models.py:30 models.py:469
msgid "Task id"
msgstr ""
#: models.py:31
msgid "32-character task identifier."
msgstr ""
#: models.py:36 models.py:235 models.py:470
msgid "Name"
msgstr ""
#: models.py:37
msgid "Optional human-readable name for lookup and display."
msgstr ""
#: models.py:41 models.py:240 models.py:468
msgid "Function"
msgstr ""
#: models.py:42
msgid "Dotted import path to the callable, e.g. myapp.tasks.job."
msgstr ""
#: models.py:47 models.py:247
msgid "Hook"
msgstr ""
#: models.py:48
msgid "Optional dotted path to a callback that receives the task result."
msgstr ""
#: models.py:53 models.py:253 models.py:472
msgid "Arguments"
msgstr ""
#: models.py:54
msgid "Pickled positional arguments (tuple)."
msgstr ""
#: models.py:59 models.py:259 models.py:473
msgid "Keyword arguments"
msgstr ""
#: models.py:60
msgid "Pickled keyword arguments (dict)."
msgstr ""
#: models.py:65
msgid "Result"
msgstr ""
#: models.py:66
msgid "Pickled return value or error payload."
msgstr ""
#: models.py:72 models.py:471
msgid "Group"
msgstr ""
#: models.py:73
msgid "Optional group id to aggregate related tasks."
msgstr ""
#: models.py:80 models.py:328
msgid "Cluster"
msgstr ""
#: models.py:81
msgid "Name of the cluster that executed this task."
msgstr ""
#: models.py:85
msgid "Started at"
msgstr ""
#: models.py:86
msgid "When the worker started executing the task."
msgstr ""
#: models.py:90
#, fuzzy
#| msgid "Stopped"
msgid "Stopped at"
msgstr "Durdu"
#: models.py:91
msgid "When the worker finished (success or failure)."
msgstr ""
#: models.py:96 monitor_terminal.py:180
msgid "Success"
msgstr "Başarılı olanlar"
#: models.py:97
msgid "False if the task raised an exception or timed out."
msgstr ""
#: models.py:101
msgid "Attempt count"
msgstr ""
#: models.py:102
msgid "How many times execution was attempted."
msgstr ""
#: models.py:172
msgid "Task"
msgstr ""
#: models.py:173
msgid "Tasks"
msgstr ""
#: models.py:194
msgid "Successful task"
msgstr "Başarılı iş"
#: models.py:126
#: models.py:195
msgid "Successful tasks"
msgstr "Başarılı işler"
#: models.py:141
#: models.py:210
msgid "Failed task"
msgstr "Başarısız iş"
#: models.py:142
#: models.py:211
msgid "Failed tasks"
msgstr "Başarısız işler"
#: models.py:150 models.py:234
#: models.py:219 models.py:347
msgid "Please install croniter to enable cron expressions"
msgstr "Cron expressions'ları açmak için croniter yükleyin"
#: models.py:170
#: models.py:236
msgid "Optional label to identify this schedule in the admin."
msgstr ""
#: models.py:241
msgid "e.g. module.tasks.function"
msgstr ""
#: models.py:248
msgid "e.g. module.tasks.result_function"
msgstr ""
#: models.py:254
msgid "e.g. 1, 2, 'John'"
msgstr "Örneğin: 1, 2, 'Melih'"
#: models.py:172
#: models.py:260
msgid "e.g. x=1, y=2, name='John'"
msgstr "Örneğin: x=1, y=2, name='Melih'"
#: models.py:186
#: models.py:274
msgid "Once"
msgstr "Bir kere"
#: models.py:187
#: models.py:275 models.py:296
msgid "Minutes"
msgstr "Dakika"
#: models.py:188
#: models.py:276
msgid "Hourly"
msgstr "Saatlik"
#: models.py:189
#: models.py:277
msgid "Daily"
msgstr "Günlük"
#: models.py:190
#: models.py:278
msgid "Weekly"
msgstr "Haftalık"
#: models.py:191
#: models.py:279
#, fuzzy
#| msgid "Weekly"
msgid "Biweekly"
msgstr "İki haftada bir"
#: models.py:192
#: models.py:280
msgid "Monthly"
msgstr "Aylık"
#: models.py:193
#: models.py:281
#, fuzzy
#| msgid "Monthly"
msgid "Bimonthly"
msgstr "İki ayda bir"
#: models.py:194
#: models.py:282
msgid "Quarterly"
msgstr "Bir Çeyrek (3 Ay)"
#: models.py:195
#: models.py:283
msgid "Yearly"
msgstr "Yıllık"
#: models.py:196
#: models.py:284 models.py:313
msgid "Cron"
msgstr ""
#: models.py:199
#: models.py:290
msgid "Schedule Type"
msgstr "Zamanlama Tipi"
#: models.py:202
#: models.py:291
msgid "How often this task should be enqueued."
msgstr ""
#: models.py:297
msgid "Number of minutes for the Minutes type"
msgstr "Dakika tipine göre dakika sayısı"
#: models.py:205
#: models.py:300
msgid "Repeats"
msgstr "Tekrar eder"
#: models.py:205
#: models.py:300
msgid "n = n times, -1 = forever"
msgstr "n = n kere, -1 = sonsuza kadar"
#: models.py:208
#: models.py:303
msgid "Next Run"
msgstr "Bir dahaki çalışma tarihi"
#: models.py:215
#: models.py:304
msgid "When this schedule runs next (stored in UTC)."
msgstr ""
#: models.py:314
msgid "Cron expression"
msgstr ""
#: models.py:224
#: models.py:320
msgid "Last task id"
msgstr ""
#: models.py:321
msgid "Id of the last task spawned from this schedule (read-only)."
msgstr ""
#: models.py:329 models.py:427
msgid "Name of the target cluster"
msgstr ""
#: models.py:336
msgid "Intended date kwarg"
msgstr ""
#: models.py:337
msgid "Name of kwarg to pass intended schedule date"
msgstr "Geçilecek kwarg'ın adı öngörülen program tarihi"
#: models.py:299
#: models.py:418
msgid "Scheduled task"
msgstr "Zamanlanmış iş"
#: models.py:300
#: models.py:419
msgid "Scheduled tasks"
msgstr "Zamanlanmış işler"
#: models.py:326
#: models.py:426
msgid "Cluster key"
msgstr ""
#: models.py:430
msgid "Payload"
msgstr ""
#: models.py:431
msgid "Signed serialized task package (do not edit manually)."
msgstr ""
#: models.py:435
msgid "Lock until"
msgstr ""
#: models.py:436
msgid "Prevent any cluster from pulling until"
msgstr ""
#: models.py:474
#, fuzzy
#| msgid "Queued task"
msgid "Queue options"
msgstr "Sıraya alınmış iş"
#: models.py:478
msgid "Queued task"
msgstr "Sıraya alınmış iş"
#: models.py:327
#: models.py:479
msgid "Queued tasks"
msgstr "Sıraya alınmış işler"
#: monitor.py:64 monitor.py:348
#: monitor.py:41
#, python-format
msgid "%(name)s monitoring at %(id)s"
msgstr "%(name)s, %(id)s pid'inde izleniyor/monitoring yapılıyor."
#: monitor.py:61
#, python-format
msgid "Processed '%(info_name)s' (%(task_name)s)"
msgstr "[%(task_name)s] - '%(info_name)s işlendi."
#: monitor.py:67
#, python-format
msgid "Failed '%(info_name)s' (%(task_name)s) - %(task_result)s"
msgstr "[%(task_name)s] - '%(info_name)s' - %(task_result)s başarısız oldu"
#: monitor.py:74
#, python-format
msgid "%(name)s stopped monitoring results"
msgstr "%(name)s sonuçları göstermeyi bıraktı"
#: monitor_terminal.py:64 monitor_terminal.py:348
msgid "Host"
msgstr ""
#: monitor.py:68 monitor.py:352 monitor.py:459
#: monitor_terminal.py:68 monitor_terminal.py:352 monitor_terminal.py:457
msgid "Id"
msgstr ""
#: monitor.py:72
#: monitor_terminal.py:72
msgid "State"
msgstr "Durum"
#: monitor.py:76
#: monitor_terminal.py:76
msgid "Pool"
msgstr "Havuz"
#: monitor.py:80
#: monitor_terminal.py:80
msgid "TQ"
msgstr ""
#: monitor.py:84
#: monitor_terminal.py:84
msgid "RQ"
msgstr ""
#: monitor.py:88
#: monitor_terminal.py:88
msgid "RC"
msgstr ""
#: monitor.py:92
#: monitor_terminal.py:92
msgid "Up"
msgstr ""
#: monitor.py:172 monitor.py:286
#: monitor_terminal.py:172 monitor_terminal.py:286
msgid "Queued"
msgstr "Sıraya alınmış"
#: monitor.py:180
msgid "Success"
msgstr "Başarılı olanlar"
#: monitor.py:190 monitor.py:294
#: monitor_terminal.py:190 monitor_terminal.py:294
msgid "Failures"
msgstr "Başarısız olanlar"
#: monitor.py:201 monitor.py:498
#: monitor_terminal.py:201 monitor_terminal.py:496
msgid "[Press q to quit]"
msgstr "[Çıkmak için q'ya basın]"
#: monitor.py:227
#: monitor_terminal.py:227
msgid "day"
msgstr "gün"
#: monitor.py:248
#: monitor_terminal.py:248
msgid "second"
msgstr "saniye"
#: monitor.py:251
#: monitor_terminal.py:251
msgid "minute"
msgstr "dakika"
#: monitor.py:254
#: monitor_terminal.py:254
msgid "hour"
msgstr "saat"
#: monitor.py:263
#: monitor_terminal.py:263
#, python-format
msgid "-- %(prefix)s %(version)s on %(info)s --"
msgstr "-- %(prefix)s %(version)s üzerinde %(info)s --"
#: monitor.py:273
#: monitor_terminal.py:273
msgid "Clusters"
msgstr ""
#: monitor.py:277
#: monitor_terminal.py:277
msgid "Workers"
msgstr ""
#: monitor.py:281
#: monitor_terminal.py:281
msgid "Restarts"
msgstr "Yeniden çalıştırmalar"
#: monitor.py:290
#: monitor_terminal.py:290
msgid "Successes"
msgstr "Başarılı olanlar"
#: monitor.py:299
#: monitor_terminal.py:299
msgid "Schedules"
msgstr "Zamanlanmışlar"
#: monitor.py:303
#: monitor_terminal.py:303
#, python-format
msgid "Tasks/%(per)s"
msgstr "İş/%(per)s"
#: monitor.py:307
#: monitor_terminal.py:307
msgid "Avg time"
msgstr "Ortalama süre"
#: monitor.py:357
#: monitor_terminal.py:357
msgid "Available (%)"
msgstr "Müsait (%) "
#: monitor.py:363
#: monitor_terminal.py:363
msgid "Available (MB)"
msgstr "Müsait (MB)"
#: monitor.py:368
#: monitor_terminal.py:368
msgid "Total (MB)"
msgstr "Toplam (MB)"
#: monitor.py:373
#: monitor_terminal.py:373
msgid "Sentinel (MB)"
msgstr ""
#: monitor.py:379
#: monitor_terminal.py:379
msgid "Monitor (MB)"
msgstr "İzleme (MB)"
#: monitor.py:385
#: monitor_terminal.py:385
msgid "Workers (MB)"
msgstr ""
#: monitor.py:487
#: monitor_terminal.py:485
#, python-format
msgid "Available lowest (): %(memory_percent)s ((at)s)"
msgstr "Mevcut en düşük (): %(memory_percent)s ((at)s)"
#: monitor.py:509
#: monitor_terminal.py:507
msgid "No clusters appear to be running."
msgstr "Hiçbir küme çalışıyor görünmüyor."
#: pusher.py:40
#, python-format
msgid "%(name)s pushing tasks at %(id)s"
msgstr "%(name)s, işleri %(id)s pid'ine gönderiyor."
#: pusher.py:67
#, python-format
msgid "queueing from %(list_key)s"
msgstr "%(list_key)s kuyruğa alınıyor"
#: pusher.py:71
#, python-format
msgid "%(name)s stopped pushing tasks"
msgstr "%(name)s işleri göndermeyi durdurdu"
#: scheduler.py:110
#, python-format
msgid "%(process_name)s failed to create a task from schedule [%(schedule)s]"
msgstr "%(process_name)s programdan bir görev oluşturamadı [%(schedule)s]"
#: scheduler.py:121
#, python-format
msgid ""
"%(process_name)s created task %(task_name)s from schedule [%(schedule)s]"
msgstr "%(process_name)s programdan %(task_name)s görevini oluşturdu [%(schedule)s]"
#: signals.py:22
#, python-format
msgid "malformed return hook '%(hook)s' for [%(name)s]"
@@ -493,6 +655,26 @@ msgstr ""
msgid "return hook %(hook)s failed on [%(name)s] because %(error)s"
msgstr ""
#: timeout.py:30 timeout.py:43
msgid "SIGALARM is not available on your platform"
msgstr ""
#: worker.py:49
#, python-format
msgid "%(proc_name)s ready for work at %(id)s"
msgstr "%(proc_name)s, %(id)s pid'inde çalışmaya hazır"
#: worker.py:69
#, fuzzy, python-format
#| msgid "%(proc_name)s processing '%(func_name)s' (%(task_name)s)"
msgid "%(proc_name)s processing %(task_name)s '%(func_name)s'"
msgstr "%(proc_name)s, '%(func_name)s' [%(task_name)s] işlerini işiyor"
#: worker.py:135
#, python-format
msgid "%(proc_name)s stopped doing work"
msgstr "%(proc_name)s çalışmayı bıraktı"
#, python-format
#~ msgid ""
#~ "Could not process '%(func_name)s'. Check the location of the function and "

View File

@@ -0,0 +1,682 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
msgid ""
msgstr ""
"Project-Id-Version: django-q\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-04-17 21:10+0000\n"
"PO-Revision-Date: 2026-01-14 16:36+0800\n"
"Last-Translator: Yibo Li <lybcyd@outlook.com>\n"
"Language-Team: Simplified Chinese\n"
"Language: zh_Hans\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: \n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: admin.py:30
msgid "Resubmit selected tasks to queue"
msgstr "将选定的任务重新提交到队列"
#: admin.py:64
msgid "Time Taken"
msgstr ""
#: admin.py:95
msgid "Short result"
msgstr ""
#: admin.py:142 models.py:412
#, fuzzy
#| msgid "Success"
msgid "success"
msgstr "成功"
#: admin.py:154 models.py:414
msgid "last_run"
msgstr "最后运行"
#: cluster.py:75
#, python-format
msgid "Q Cluster %(name)s starting."
msgstr "Q 集群 %(name)s 正在启动。"
#: cluster.py:83
#, fuzzy, python-format
#| msgid "Q Cluster-{} stopping."
msgid "Q Cluster %(name)s stopping."
msgstr "Q 集群 %(name)s 正在停止。"
#: cluster.py:86
#, python-format
msgid "Q Cluster %(name)s has stopped."
msgstr "Q 集群 %(name)s 已停止。"
#: cluster.py:93
#, python-format
msgid "%(name)s got signal %(signal)s"
msgstr "%(name)s 收到信号 %(signal)s"
#: cluster.py:224
#, python-format
msgid "reincarnated monitor %(name)s after sudden death"
msgstr "意外宕机后monitor %(name)s 已重生"
#: cluster.py:230
#, python-format
msgid "reincarnated pusher %(name)s after sudden death"
msgstr "意外宕机后pusher %(name)s 已重生"
#: cluster.py:262
#, fuzzy, python-format
#| msgid "reincarnated worker %(name)s after timeout"
msgid ""
"reincarnated worker %(name)s after timeout while processing task "
"%(task_name)s"
msgstr "处理任务 %(task_name)s 时超时worker %(name)s 已重生"
#: cluster.py:265
#, python-format
msgid "reincarnated worker %(name)s after timeout"
msgstr "超时后worker %(name)s 已重生"
#: cluster.py:270
#, python-format
msgid "recycled worker %(name)s"
msgstr "worker %(name)s 已回收"
#: cluster.py:273
#, python-format
msgid "reincarnated worker %(name)s after death"
msgstr "意外宕机后worker %(name)s 已重生"
#: cluster.py:297
#, python-format
msgid "%(name)s guarding cluster %(cluster_name)s"
msgstr "%(name)s 正在保护Cluster %(cluster_name)s"
#: cluster.py:307
#, python-format
msgid "Q Cluster %(cluster_name)s running."
msgstr "Q 集群 %(cluster_name)s 正在运行。"
#: cluster.py:346
#, python-format
msgid "%(name)s stopping cluster processes"
msgstr "%(name)s 正在停止集群进程"
#: cluster.py:371
#, python-format
msgid "%(name)s waiting for the monitor."
msgstr "%(name)s 正在等待监视器。"
#: cluster.py:394
msgid "Skipping cpu affinity because psutil was not found."
msgstr "跳过 CPU 亲和性,因为未找到 psutil。"
#: cluster.py:399
msgid "Faking cpu affinity because it is not supported on this platform"
msgstr "伪造 CPU 亲和性,因为此平台不支持此功能"
#: cluster.py:421
#, python-format
msgid "%(pid)s will use cpu %(affinity)s"
msgstr "%(pid)s 将使用 CPU %(affinity)s"
#: conf.py:122
#, python-format
msgid ""
"SAVE_LIMIT_PER (%(option)s) is not a valid option. Options are: 'group', "
"'name', 'func' and None. Default is None."
msgstr ""
"SAVE_LIMIT_PER (%(option)s) 不是有效的选项。选项包"
"括:'group'、'name'、'func' 和 None。默认值是 None。"
#. Translators: Cluster status descriptions
#: conf.py:239
msgid "Starting"
msgstr "正在启动"
#: conf.py:240
msgid "Working"
msgstr "工作中"
#: conf.py:241
msgid "Idle"
msgstr "空闲"
#: conf.py:242
msgid "Stopped"
msgstr "已停止"
#: conf.py:243
msgid "Stopping"
msgstr "正在停止"
#. Translators: help text for qcluster management command
#: management/commands/qcluster.py:11
msgid "Starts a Django Q Cluster."
msgstr "启动 Django Q 集群。"
#. Translators: help text for qinfo management command
#: management/commands/qinfo.py:11
msgid "General information over all clusters."
msgstr "所有集群的常规信息。"
#. Translators: help text for qmemory management command
#: management/commands/qmemory.py:9
msgid "Monitors Q Cluster memory usage"
msgstr "监视 Q 集群内存使用情况"
#. Translators: help text for qmonitor management command
#: management/commands/qmonitor.py:9
msgid "Monitors Q Cluster activity"
msgstr "监视 Q 集群活动"
#: models.py:30 models.py:469
msgid "Task id"
msgstr ""
#: models.py:31
msgid "32-character task identifier."
msgstr ""
#: models.py:36 models.py:235 models.py:470
msgid "Name"
msgstr ""
#: models.py:37
msgid "Optional human-readable name for lookup and display."
msgstr ""
#: models.py:41 models.py:240 models.py:468
msgid "Function"
msgstr ""
#: models.py:42
msgid "Dotted import path to the callable, e.g. myapp.tasks.job."
msgstr ""
#: models.py:47 models.py:247
msgid "Hook"
msgstr ""
#: models.py:48
msgid "Optional dotted path to a callback that receives the task result."
msgstr ""
#: models.py:53 models.py:253 models.py:472
msgid "Arguments"
msgstr ""
#: models.py:54
msgid "Pickled positional arguments (tuple)."
msgstr ""
#: models.py:59 models.py:259 models.py:473
msgid "Keyword arguments"
msgstr ""
#: models.py:60
msgid "Pickled keyword arguments (dict)."
msgstr ""
#: models.py:65
msgid "Result"
msgstr ""
#: models.py:66
msgid "Pickled return value or error payload."
msgstr ""
#: models.py:72 models.py:471
msgid "Group"
msgstr ""
#: models.py:73
msgid "Optional group id to aggregate related tasks."
msgstr ""
#: models.py:80 models.py:328
#, fuzzy
#| msgid "Clusters"
msgid "Cluster"
msgstr "集群"
#: models.py:81
msgid "Name of the cluster that executed this task."
msgstr ""
#: models.py:85
msgid "Started at"
msgstr ""
#: models.py:86
msgid "When the worker started executing the task."
msgstr ""
#: models.py:90
#, fuzzy
#| msgid "Stopped"
msgid "Stopped at"
msgstr "已停止"
#: models.py:91
msgid "When the worker finished (success or failure)."
msgstr ""
#: models.py:96 monitor_terminal.py:180
msgid "Success"
msgstr "成功"
#: models.py:97
msgid "False if the task raised an exception or timed out."
msgstr ""
#: models.py:101
msgid "Attempt count"
msgstr ""
#: models.py:102
msgid "How many times execution was attempted."
msgstr ""
#: models.py:172
msgid "Task"
msgstr ""
#: models.py:173
msgid "Tasks"
msgstr ""
#: models.py:194
msgid "Successful task"
msgstr "成功的任务"
#: models.py:195
msgid "Successful tasks"
msgstr "成功的任务"
#: models.py:210
msgid "Failed task"
msgstr "失败的任务"
#: models.py:211
msgid "Failed tasks"
msgstr "失败的任务"
#: models.py:219 models.py:347
msgid "Please install croniter to enable cron expressions"
msgstr "请安装 croniter 以启用 cron 表达式"
#: models.py:236
msgid "Optional label to identify this schedule in the admin."
msgstr ""
#: models.py:241
msgid "e.g. module.tasks.function"
msgstr ""
#: models.py:248
msgid "e.g. module.tasks.result_function"
msgstr ""
#: models.py:254
msgid "e.g. 1, 2, 'John'"
msgstr "例如 1, 2, 'John'"
#: models.py:260
msgid "e.g. x=1, y=2, name='John'"
msgstr "例如 x=1, y=2, name='John'"
#: models.py:274
msgid "Once"
msgstr "一次"
#: models.py:275 models.py:296
msgid "Minutes"
msgstr "分钟"
#: models.py:276
msgid "Hourly"
msgstr "每小时"
#: models.py:277
msgid "Daily"
msgstr "每天"
#: models.py:278
msgid "Weekly"
msgstr "每周"
#: models.py:279
msgid "Biweekly"
msgstr "每两周"
#: models.py:280
msgid "Monthly"
msgstr "每月"
#: models.py:281
msgid "Bimonthly"
msgstr "每两个月"
#: models.py:282
msgid "Quarterly"
msgstr "每季度"
#: models.py:283
msgid "Yearly"
msgstr "每年"
#: models.py:284 models.py:313
msgid "Cron"
msgstr "Cron"
#: models.py:290
msgid "Schedule Type"
msgstr "计划类型"
#: models.py:291
msgid "How often this task should be enqueued."
msgstr ""
#: models.py:297
msgid "Number of minutes for the Minutes type"
msgstr "'分钟'类型的分钟数"
#: models.py:300
msgid "Repeats"
msgstr "重复次数"
#: models.py:300
msgid "n = n times, -1 = forever"
msgstr "n = n 次,-1 = 永远"
#: models.py:303
msgid "Next Run"
msgstr "下次运行"
#: models.py:304
msgid "When this schedule runs next (stored in UTC)."
msgstr ""
#: models.py:314
msgid "Cron expression"
msgstr "Cron 表达式"
#: models.py:320
msgid "Last task id"
msgstr ""
#: models.py:321
msgid "Id of the last task spawned from this schedule (read-only)."
msgstr ""
#: models.py:329 models.py:427
msgid "Name of the target cluster"
msgstr ""
#: models.py:336
msgid "Intended date kwarg"
msgstr ""
#: models.py:337
msgid "Name of kwarg to pass intended schedule date"
msgstr "传递预定计划日期的 kwarg 的名称"
#: models.py:418
msgid "Scheduled task"
msgstr "计划任务"
#: models.py:419
msgid "Scheduled tasks"
msgstr "计划任务"
#: models.py:426
#, fuzzy
#| msgid "Clusters"
msgid "Cluster key"
msgstr "集群"
#: models.py:430
msgid "Payload"
msgstr ""
#: models.py:431
msgid "Signed serialized task package (do not edit manually)."
msgstr ""
#: models.py:435
msgid "Lock until"
msgstr ""
#: models.py:436
msgid "Prevent any cluster from pulling until"
msgstr ""
#: models.py:474
#, fuzzy
#| msgid "Queued task"
msgid "Queue options"
msgstr "排队的任务"
#: models.py:478
msgid "Queued task"
msgstr "排队的任务"
#: models.py:479
msgid "Queued tasks"
msgstr "排队的任务"
#: monitor.py:41
#, python-format
msgid "%(name)s monitoring at %(id)s"
msgstr "%(name)s 正在 %(id)s 处监视"
#: monitor.py:61
#, python-format
msgid "Processed '%(info_name)s' (%(task_name)s)"
msgstr "已处理 '%(info_name)s' (%(task_name)s)"
#: monitor.py:67
#, python-format
msgid "Failed '%(info_name)s' (%(task_name)s) - %(task_result)s"
msgstr "'%(info_name)s' (%(task_name)s) 失败 - %(task_result)s"
#: monitor.py:74
#, python-format
msgid "%(name)s stopped monitoring results"
msgstr "%(name)s 已停止监视结果"
#: monitor_terminal.py:64 monitor_terminal.py:348
msgid "Host"
msgstr "主机"
#: monitor_terminal.py:68 monitor_terminal.py:352 monitor_terminal.py:457
msgid "Id"
msgstr "ID"
#: monitor_terminal.py:72
msgid "State"
msgstr "状态"
#: monitor_terminal.py:76
msgid "Pool"
msgstr "连接池"
#: monitor_terminal.py:80
msgid "TQ"
msgstr "TQ"
#: monitor_terminal.py:84
msgid "RQ"
msgstr "RQ"
#: monitor_terminal.py:88
msgid "RC"
msgstr "RC"
#: monitor_terminal.py:92
msgid "Up"
msgstr "运行中"
#: monitor_terminal.py:172 monitor_terminal.py:286
msgid "Queued"
msgstr "已排队"
#: monitor_terminal.py:190 monitor_terminal.py:294
msgid "Failures"
msgstr "失败"
#: monitor_terminal.py:201 monitor_terminal.py:496
msgid "[Press q to quit]"
msgstr "[按 q 键退出]"
#: monitor_terminal.py:227
msgid "day"
msgstr "天"
#: monitor_terminal.py:248
msgid "second"
msgstr "秒"
#: monitor_terminal.py:251
msgid "minute"
msgstr "分钟"
#: monitor_terminal.py:254
msgid "hour"
msgstr "小时"
#: monitor_terminal.py:263
#, python-format
msgid "-- %(prefix)s %(version)s on %(info)s --"
msgstr "-- %(prefix)s %(version)s 在 %(info)s 上 --"
#: monitor_terminal.py:273
msgid "Clusters"
msgstr "集群"
#: monitor_terminal.py:277
msgid "Workers"
msgstr "工作进程"
#: monitor_terminal.py:281
msgid "Restarts"
msgstr "重启次数"
#: monitor_terminal.py:290
msgid "Successes"
msgstr "成功次数"
#: monitor_terminal.py:299
msgid "Schedules"
msgstr "计划"
#: monitor_terminal.py:303
#, python-format
msgid "Tasks/%(per)s"
msgstr "任务/%(per)s"
#: monitor_terminal.py:307
msgid "Avg time"
msgstr "平均时间"
#: monitor_terminal.py:357
msgid "Available (%)"
msgstr "可用 (%)"
#: monitor_terminal.py:363
msgid "Available (MB)"
msgstr "可用 (MB)"
#: monitor_terminal.py:368
msgid "Total (MB)"
msgstr "总计 (MB)"
#: monitor_terminal.py:373
msgid "Sentinel (MB)"
msgstr "哨兵 (MB)"
#: monitor_terminal.py:379
msgid "Monitor (MB)"
msgstr "监视器 (MB)"
#: monitor_terminal.py:385
msgid "Workers (MB)"
msgstr "工作进程 (MB)"
#: monitor_terminal.py:485
#, python-format
msgid "Available lowest (): %(memory_percent)s ((at)s)"
msgstr "最低可用 (): %(memory_percent)s ((at)s)"
#: monitor_terminal.py:507
msgid "No clusters appear to be running."
msgstr "似乎没有集群正在运行。"
#: pusher.py:40
#, python-format
msgid "%(name)s pushing tasks at %(id)s"
msgstr "%(name)s 正在 %(id)s 处推送任务"
#: pusher.py:67
#, python-format
msgid "queueing from %(list_key)s"
msgstr "从 %(list_key)s 排队"
#: pusher.py:71
#, python-format
msgid "%(name)s stopped pushing tasks"
msgstr "%(name)s 已停止推送任务"
#: scheduler.py:110
#, python-format
msgid "%(process_name)s failed to create a task from schedule [%(schedule)s]"
msgstr "%(process_name)s 无法从计划 [%(schedule)s] 创建任务"
#: scheduler.py:121
#, python-format
msgid ""
"%(process_name)s created task %(task_name)s from schedule [%(schedule)s]"
msgstr "%(process_name)s 从计划 [%(schedule)s] 创建了任务 %(task_name)s"
#: signals.py:22
#, python-format
msgid "malformed return hook '%(hook)s' for [%(name)s]"
msgstr "格式错误的返回钩子 '%(hook)s' 用于 [%(name)s]"
#: signals.py:30
#, python-format
msgid "return hook %(hook)s failed on [%(name)s] because %(error)s"
msgstr "返回钩子 %(hook)s 在 [%(name)s] 上失败,原因是 %(error)s"
#: timeout.py:30 timeout.py:43
msgid "SIGALARM is not available on your platform"
msgstr ""
#: worker.py:49
#, python-format
msgid "%(proc_name)s ready for work at %(id)s"
msgstr "%(proc_name)s 已准备好在 %(id)s 处工作"
#: worker.py:69
#, fuzzy, python-format
#| msgid "%(proc_name)s processing '%(func_name)s' (%(task_name)s)"
msgid "%(proc_name)s processing %(task_name)s '%(func_name)s'"
msgstr "%(proc_name)s 正在处理任务 %(task_name)s '%(func_name)s'"
#: worker.py:135
#, python-format
msgid "%(proc_name)s stopped doing work"
msgstr "%(proc_name)s 已停止工作"
#, python-format
#~ msgid ""
#~ "Could not process '%(func_name)s'. Check the location of the function and "
#~ "the args/kwargs."
#~ msgstr "无法处理函数 '%(func_name)s'。检查函数的位置和args/kwargs。"

View File

@@ -0,0 +1,325 @@
# Generated by Django 6.0.4 on 2026-04-17 20:55
import django.utils.timezone
import picklefield.fields
from django.db import migrations, models
import django_q.models
class Migration(migrations.Migration):
dependencies = [
("django_q", "0018_task_success_index"),
]
operations = [
migrations.AlterModelOptions(
name="task",
options={
"ordering": ["-stopped"],
"verbose_name": "Task",
"verbose_name_plural": "Tasks",
},
),
migrations.AlterField(
model_name="ormq",
name="key",
field=models.CharField(
help_text="Name of the target cluster",
max_length=100,
verbose_name="Cluster key",
),
),
migrations.AlterField(
model_name="ormq",
name="lock",
field=models.DateTimeField(
help_text="Prevent any cluster from pulling until",
null=True,
verbose_name="Lock until",
),
),
migrations.AlterField(
model_name="ormq",
name="payload",
field=models.TextField(
help_text="Signed serialized task package (do not edit manually).",
verbose_name="Payload",
),
),
migrations.AlterField(
model_name="schedule",
name="args",
field=models.TextField(
blank=True,
help_text="e.g. 1, 2, 'John'",
null=True,
verbose_name="Arguments",
),
),
migrations.AlterField(
model_name="schedule",
name="cluster",
field=models.CharField(
blank=True,
default=None,
help_text="Name of the target cluster",
max_length=100,
null=True,
verbose_name="Cluster",
),
),
migrations.AlterField(
model_name="schedule",
name="cron",
field=models.CharField(
blank=True,
help_text="Cron expression",
max_length=100,
null=True,
validators=[django_q.models.validate_cron],
verbose_name="Cron",
),
),
migrations.AlterField(
model_name="schedule",
name="func",
field=models.CharField(
help_text="e.g. module.tasks.function",
max_length=256,
verbose_name="Function",
),
),
migrations.AlterField(
model_name="schedule",
name="hook",
field=models.CharField(
blank=True,
help_text="e.g. module.tasks.result_function",
max_length=256,
null=True,
verbose_name="Hook",
),
),
migrations.AlterField(
model_name="schedule",
name="intended_date_kwarg",
field=models.CharField(
blank=True,
help_text="Name of kwarg to pass intended schedule date",
max_length=100,
null=True,
validators=[django_q.models.validate_kwarg],
verbose_name="Intended date kwarg",
),
),
migrations.AlterField(
model_name="schedule",
name="kwargs",
field=models.TextField(
blank=True,
help_text="e.g. x=1, y=2, name='John'",
null=True,
verbose_name="Keyword arguments",
),
),
migrations.AlterField(
model_name="schedule",
name="minutes",
field=models.PositiveSmallIntegerField(
blank=True,
help_text="Number of minutes for the Minutes type",
null=True,
verbose_name="Minutes",
),
),
migrations.AlterField(
model_name="schedule",
name="name",
field=models.CharField(
blank=True,
help_text="Optional label to identify this schedule in the admin.",
max_length=100,
null=True,
verbose_name="Name",
),
),
migrations.AlterField(
model_name="schedule",
name="next_run",
field=models.DateTimeField(
default=django.utils.timezone.now,
help_text="When this schedule runs next (stored in UTC).",
null=True,
verbose_name="Next Run",
),
),
migrations.AlterField(
model_name="schedule",
name="schedule_type",
field=models.CharField(
choices=[
("O", "Once"),
("I", "Minutes"),
("H", "Hourly"),
("D", "Daily"),
("W", "Weekly"),
("BW", "Biweekly"),
("M", "Monthly"),
("BM", "Bimonthly"),
("Q", "Quarterly"),
("Y", "Yearly"),
("C", "Cron"),
],
default="O",
help_text="How often this task should be enqueued.",
max_length=2,
verbose_name="Schedule Type",
),
),
migrations.AlterField(
model_name="schedule",
name="task",
field=models.CharField(
editable=False,
help_text="Id of the last task spawned from this schedule (read-only).",
max_length=100,
null=True,
verbose_name="Last task id",
),
),
migrations.AlterField(
model_name="task",
name="args",
field=picklefield.fields.PickledObjectField(
editable=False,
help_text="Pickled positional arguments (tuple).",
null=True,
protocol=-1,
verbose_name="Arguments",
),
),
migrations.AlterField(
model_name="task",
name="attempt_count",
field=models.IntegerField(
default=0,
help_text="How many times execution was attempted.",
verbose_name="Attempt count",
),
),
migrations.AlterField(
model_name="task",
name="cluster",
field=models.CharField(
blank=True,
default=None,
help_text="Name of the cluster that executed this task.",
max_length=100,
null=True,
verbose_name="Cluster",
),
),
migrations.AlterField(
model_name="task",
name="func",
field=models.CharField(
help_text="Dotted import path to the callable, e.g. myapp.tasks.job.",
max_length=256,
verbose_name="Function",
),
),
migrations.AlterField(
model_name="task",
name="group",
field=models.CharField(
editable=False,
help_text="Optional group id to aggregate related tasks.",
max_length=100,
null=True,
verbose_name="Group",
),
),
migrations.AlterField(
model_name="task",
name="hook",
field=models.CharField(
help_text="Optional dotted path to a callback that receives the task result.",
max_length=256,
null=True,
verbose_name="Hook",
),
),
migrations.AlterField(
model_name="task",
name="id",
field=models.CharField(
editable=False,
help_text="32-character task identifier.",
max_length=32,
primary_key=True,
serialize=False,
verbose_name="Task id",
),
),
migrations.AlterField(
model_name="task",
name="kwargs",
field=picklefield.fields.PickledObjectField(
editable=False,
help_text="Pickled keyword arguments (dict).",
null=True,
protocol=-1,
verbose_name="Keyword arguments",
),
),
migrations.AlterField(
model_name="task",
name="name",
field=models.CharField(
editable=False,
help_text="Optional human-readable name for lookup and display.",
max_length=100,
verbose_name="Name",
),
),
migrations.AlterField(
model_name="task",
name="result",
field=picklefield.fields.PickledObjectField(
editable=False,
help_text="Pickled return value or error payload.",
null=True,
protocol=-1,
verbose_name="Result",
),
),
migrations.AlterField(
model_name="task",
name="started",
field=models.DateTimeField(
editable=False,
help_text="When the worker started executing the task.",
verbose_name="Started at",
),
),
migrations.AlterField(
model_name="task",
name="stopped",
field=models.DateTimeField(
editable=False,
help_text="When the worker finished (success or failure).",
verbose_name="Stopped at",
),
),
migrations.AlterField(
model_name="task",
name="success",
field=models.BooleanField(
default=True,
editable=False,
help_text="False if the task raised an exception or timed out.",
verbose_name="Success",
),
),
]

View File

@@ -2,11 +2,9 @@ from datetime import datetime, timedelta
from keyword import iskeyword
# Django
from django import get_version
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Q
from django.template.defaultfilters import truncatechars
from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
@@ -15,7 +13,6 @@ from django.utils.translation import gettext_lazy as _
# External
from picklefield import PickledObjectField
from picklefield.fields import dbsafe_decode
# Local
from django_q.conf import croniter
@@ -26,19 +23,86 @@ from .utils import get_func_repr
class Task(models.Model):
id = models.CharField(max_length=32, primary_key=True, editable=False)
name = models.CharField(max_length=100, editable=False)
func = models.CharField(max_length=256)
hook = models.CharField(max_length=256, null=True)
args = PickledObjectField(null=True, protocol=-1)
kwargs = PickledObjectField(null=True, protocol=-1)
result = PickledObjectField(null=True, protocol=-1)
group = models.CharField(max_length=100, editable=False, null=True)
cluster = models.CharField(max_length=100, default=None, null=True, blank=True)
started = models.DateTimeField(editable=False)
stopped = models.DateTimeField(editable=False)
success = models.BooleanField(default=True, editable=False)
attempt_count = models.IntegerField(default=0)
id = models.CharField(
max_length=32,
primary_key=True,
editable=False,
verbose_name=_("Task id"),
help_text=_("32-character task identifier."),
)
name = models.CharField(
max_length=100,
editable=False,
verbose_name=_("Name"),
help_text=_("Optional human-readable name for lookup and display."),
)
func = models.CharField(
max_length=256,
verbose_name=_("Function"),
help_text=_("Dotted import path to the callable, e.g. myapp.tasks.job."),
)
hook = models.CharField(
max_length=256,
null=True,
verbose_name=_("Hook"),
help_text=_(
"Optional dotted path to a callback that receives the task result."
),
)
args = PickledObjectField(
null=True,
protocol=-1,
verbose_name=_("Arguments"),
help_text=_("Pickled positional arguments (tuple)."),
)
kwargs = PickledObjectField(
null=True,
protocol=-1,
verbose_name=_("Keyword arguments"),
help_text=_("Pickled keyword arguments (dict)."),
)
result = PickledObjectField(
null=True,
protocol=-1,
verbose_name=_("Result"),
help_text=_("Pickled return value or error payload."),
)
group = models.CharField(
max_length=100,
editable=False,
null=True,
verbose_name=_("Group"),
help_text=_("Optional group id to aggregate related tasks."),
)
cluster = models.CharField(
max_length=100,
default=None,
null=True,
blank=True,
verbose_name=_("Cluster"),
help_text=_("Name of the cluster that executed this task."),
)
started = models.DateTimeField(
editable=False,
verbose_name=_("Started at"),
help_text=_("When the worker started executing the task."),
)
stopped = models.DateTimeField(
editable=False,
verbose_name=_("Stopped at"),
help_text=_("When the worker finished (success or failure)."),
)
success = models.BooleanField(
default=True,
editable=False,
verbose_name=_("Success"),
help_text=_("False if the task raised an exception or timed out."),
)
attempt_count = models.IntegerField(
default=0,
verbose_name=_("Attempt count"),
help_text=_("How many times execution was attempted."),
)
@staticmethod
def get_result(task_id):
@@ -59,7 +123,7 @@ class Task(models.Model):
.exclude(success=False)
.values_list("result", flat=True)
)
return decode_results(values)
return values
def group_result(self, failures=False):
if self.group:
@@ -102,15 +166,13 @@ class Task(models.Model):
def time_taken(self):
return (self.stopped - self.started).total_seconds()
@property
def short_result(self):
return truncatechars(self.result, 100)
def __str__(self):
return f"{self.name or self.id}"
class Meta:
app_label = "django_q"
verbose_name = _("Task")
verbose_name_plural = _("Tasks")
ordering = ["-stopped"]
indexes = [
models.Index(
@@ -168,17 +230,36 @@ def validate_kwarg(value):
class Schedule(models.Model):
name = models.CharField(max_length=100, null=True, blank=True)
func = models.CharField(max_length=256, help_text="e.g. module.tasks.function")
name = models.CharField(
max_length=100,
null=True,
blank=True,
verbose_name=_("Name"),
help_text=_("Optional label to identify this schedule in the admin."),
)
func = models.CharField(
max_length=256,
verbose_name=_("Function"),
help_text=_("e.g. module.tasks.function"),
)
hook = models.CharField(
max_length=256,
null=True,
blank=True,
help_text="e.g. module.tasks.result_function",
verbose_name=_("Hook"),
help_text=_("e.g. module.tasks.result_function"),
)
args = models.TextField(
null=True,
blank=True,
verbose_name=_("Arguments"),
help_text=_("e.g. 1, 2, 'John'"),
)
args = models.TextField(null=True, blank=True, help_text=_("e.g. 1, 2, 'John'"))
kwargs = models.TextField(
null=True, blank=True, help_text=_("e.g. x=1, y=2, name='John'")
null=True,
blank=True,
verbose_name=_("Keyword arguments"),
help_text=_("e.g. x=1, y=2, name='John'"),
)
ONCE = "O"
MINUTES = "I"
@@ -205,30 +286,48 @@ class Schedule(models.Model):
(CRON, _("Cron")),
)
schedule_type = models.CharField(
max_length=2, choices=TYPE, default=TYPE[0][0], verbose_name=_("Schedule Type")
max_length=2,
choices=TYPE,
default=TYPE[0][0],
verbose_name=_("Schedule Type"),
help_text=_("How often this task should be enqueued."),
)
minutes = models.PositiveSmallIntegerField(
null=True, blank=True, help_text=_("Number of minutes for the Minutes type")
null=True,
blank=True,
verbose_name=_("Minutes"),
help_text=_("Number of minutes for the Minutes type"),
)
repeats = models.IntegerField(
default=-1, verbose_name=_("Repeats"), help_text=_("n = n times, -1 = forever")
)
next_run = models.DateTimeField(
verbose_name=_("Next Run"), default=timezone.now, null=True
verbose_name=_("Next Run"),
help_text=_("When this schedule runs next (stored in UTC)."),
default=timezone.now,
null=True,
)
cron = models.CharField(
max_length=100,
null=True,
blank=True,
validators=[validate_cron],
verbose_name=_("Cron"),
help_text=_("Cron expression"),
)
task = models.CharField(max_length=100, null=True, editable=False)
task = models.CharField(
max_length=100,
null=True,
editable=False,
verbose_name=_("Last task id"),
help_text=_("Id of the last task spawned from this schedule (read-only)."),
)
cluster = models.CharField(
max_length=100,
default=None,
null=True,
blank=True,
verbose_name=_("Cluster"),
help_text=_("Name of the target cluster"),
)
intended_date_kwarg = models.CharField(
@@ -236,6 +335,7 @@ class Schedule(models.Model):
null=True,
blank=True,
validators=[validate_kwarg],
verbose_name=_("Intended date kwarg"),
help_text=_("Name of kwarg to pass intended schedule date"),
)
@@ -298,12 +398,18 @@ class Schedule(models.Model):
url = reverse("admin:django_q_success_change", args=(task.id,))
else:
url = reverse("admin:django_q_failure_change", args=(task.id,))
return format_html(f'<a href="{url}">[{task.name}]</a>')
return format_html('<a href="{}">[{}]</a>', url, task.name)
return None
def __str__(self):
return self.func
def save(self, *args, **kwargs):
if self.pk is None and self.schedule_type == self.CRON:
self.next_run = self.calculate_next_run()
return super().save(*args, **kwargs)
success.boolean = True
success.short_description = _("success")
last_run.allow_tags = True
@@ -317,10 +423,19 @@ class Schedule(models.Model):
class OrmQ(models.Model):
key = models.CharField(max_length=100, help_text=_("Name of the target cluster"))
payload = models.TextField()
key = models.CharField(
max_length=100,
verbose_name=_("Cluster key"),
help_text=_("Name of the target cluster"),
)
payload = models.TextField(
verbose_name=_("Payload"),
help_text=_("Signed serialized task package (do not edit manually)."),
)
lock = models.DateTimeField(
null=True, help_text=_("Prevent any cluster from pulling until")
null=True,
verbose_name=_("Lock until"),
help_text=_("Prevent any cluster from pulling until"),
)
@cached_property
@@ -352,15 +467,15 @@ class OrmQ(models.Model):
exclude = {"id", "name", "group", "func", "args", "kwargs"}
return {k: v for k, v in self.task.items() if k not in exclude}
func.short_description = _("Function")
task_id.short_description = _("Task id")
name.short_description = _("Name")
group.short_description = _("Group")
args.short_description = _("Arguments")
kwargs.short_description = _("Keyword arguments")
q_options.short_description = _("Queue options")
class Meta:
app_label = "django_q"
verbose_name = _("Queued task")
verbose_name_plural = _("Queued tasks")
# Backwards compatibility for Django 1.7
def decode_results(values):
if get_version().split(".")[1] == "7":
# decode values in 1.7
return [dbsafe_decode(v) for v in values]
return values

View File

@@ -93,7 +93,8 @@ def save_task(task, broker: Broker):
broker=broker,
)
# SAVE LIMIT > 0: Prune database, SAVE_LIMIT 0: No pruning
close_old_django_connections()
if not task.get("sync", False):
close_old_django_connections()
try:
filters = {}
@@ -107,13 +108,16 @@ def save_task(task, broker: Broker):
value = get_func_repr(value)
filters[Conf.SAVE_LIMIT_PER] = value
with db.transaction.atomic(using=db.router.db_for_write(Success)):
list(Success.objects.filter(**filters).select_for_update())
if (
task["success"]
and 0 < Conf.SAVE_LIMIT <= Success.objects.filter(**filters).count()
):
Success.objects.filter(**filters).last().delete()
# check if we should clean the success tasks
if Conf.SAVE_LIMIT > 0:
with db.transaction.atomic(using=db.router.db_for_write(Success)):
success_tasks_qs = Success.objects.filter(**filters)
success_tasks_pks = [
success_task.pk
for success_task in success_tasks_qs.select_for_update()
]
if task["success"] and len(success_tasks_pks) >= Conf.SAVE_LIMIT:
success_tasks_qs.last().delete()
# check if this task has previous results
try:
@@ -157,7 +161,7 @@ def save_task(task, broker: Broker):
def save_cached(task, broker: Broker):
task_key = f'{broker.list_key}:{task["id"]}'
task_key = f"{broker.list_key}:{task['id']}"
timeout = task["cached"]
if timeout is True:
timeout = None

View File

@@ -173,7 +173,7 @@ def monitor(run_once=False, broker=None):
)
print(
term.move(i, 3 * col_width)
+ term.white_on_cyan(term.center(queue_size, width=col_width))
+ term.white_on_cyan(term.center(str(queue_size), width=col_width))
)
print(
term.move(i, 4 * col_width)
@@ -182,7 +182,7 @@ def monitor(run_once=False, broker=None):
print(
term.move(i, 5 * col_width)
+ term.white_on_cyan(
term.center(models.Success.objects.count(), width=col_width)
term.center(str(models.Success.objects.count()), width=col_width)
)
)
print(
@@ -192,7 +192,7 @@ def monitor(run_once=False, broker=None):
print(
term.move(i, 7 * col_width)
+ term.white_on_cyan(
term.center(models.Failure.objects.count(), width=col_width)
term.center(str(models.Failure.objects.count()), width=col_width)
)
)
# for testing

View File

@@ -43,3 +43,6 @@ pre_execute = Signal()
# args: task
post_execute = Signal()
# args: func, task
post_execute_in_worker = Signal()

View File

@@ -461,7 +461,9 @@ def async_iter(func, args_iter, **kwargs):
# save the original arguments
broker = options["broker"]
broker.cache.set(
f"{broker.list_key}:{iter_group}:args", SignedPackage.dumps(args_iter)
f"{broker.list_key}:{iter_group}:args",
SignedPackage.dumps(args_iter),
timeout=None,
)
for args in args_iter:
if not isinstance(args, tuple):

View File

@@ -116,7 +116,7 @@ CACHES = {
"LOCATION": f"redis://{REDIS_HOST}:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PARSER_CLASS": "redis.connection.HiredisParser",
"PARSER_CLASS": "redis.connection.DefaultParser",
},
}
}

View File

@@ -35,6 +35,57 @@ def test_broker(monkeypatch):
assert broker.get_stats("test:*") is None
def test_broker_set_stat_prunes_stale_master_list():
# regression: set_stat should prune stale master-list entries on register
broker = Broker()
a_key = f"{Conf.Q_STAT}:A"
b_key = f"{Conf.Q_STAT}:B"
broker.cache.delete(Conf.Q_STAT)
broker.set_stat(a_key, "state_a", 3)
assert a_key in broker.cache.get(Conf.Q_STAT)
# Drop A's per-stat value to simulate a dead cluster whose TTL expired,
# leaving the master-list entry as a stale reference.
broker.cache.delete(a_key)
assert broker.get_stat(a_key) is None
assert a_key in broker.cache.get(Conf.Q_STAT)
# Registering B should prune stale A from the master list.
broker.set_stat(b_key, "state_b", 3)
key_list = broker.cache.get(Conf.Q_STAT)
assert a_key not in key_list
assert b_key in key_list
assert broker.get_stat(b_key) == "state_b"
def test_broker_set_stat_skips_master_list_write_on_repeat(monkeypatch):
# set_stat should only write the master list when membership changes
broker = Broker()
a_key = f"{Conf.Q_STAT}:A"
broker.cache.delete(Conf.Q_STAT)
writes = []
orig_set = broker.cache.set
def counting_set(key, value, timeout=None, **kw):
if key == Conf.Q_STAT:
writes.append(key)
return orig_set(key, value, timeout, **kw)
monkeypatch.setattr(broker.cache, "set", counting_set)
# First call adds a new entry, one master-list write expected
broker.set_stat(a_key, "state_a", 3)
assert len(writes) == 1
# Subsequent calls for the same key must not rewrite the master list
broker.set_stat(a_key, "state_a", 3)
broker.set_stat(a_key, "state_a", 3)
broker.set_stat(a_key, "state_a", 3)
assert len(writes) == 1
def test_redis(monkeypatch):
monkeypatch.setattr(Conf, "DJANGO_REDIS", None)
broker = get_broker()
@@ -51,7 +102,7 @@ def test_redis(monkeypatch):
def test_custom(monkeypatch):
monkeypatch.setattr(Conf, "BROKER_CLASS", "brokers.redis_broker.Redis")
monkeypatch.setattr(Conf, "BROKER_CLASS", "django_q.brokers.redis_broker.Redis")
broker = get_broker()
assert broker.ping() is True
assert broker.info() is not None
@@ -124,7 +175,7 @@ def test_ironmq(monkeypatch):
@pytest.mark.skipif(
not os.getenv("AWS_ACCESS_KEY_ID"), reason="requires AWS credentials"
)
def canceled_sqs(monkeypatch):
def test_sqs(monkeypatch):
monkeypatch.setattr(
Conf,
"SQS",
@@ -132,11 +183,13 @@ def canceled_sqs(monkeypatch):
"aws_region": os.getenv("AWS_REGION"),
"aws_access_key_id": os.getenv("AWS_ACCESS_KEY_ID"),
"aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"),
"receive_message_wait_time_seconds": 20,
"receive_message_wait_time_seconds": 5,
},
)
# check broker
broker = get_broker(list_key=uuid()[0])
broker = get_broker(list_key="testing")
assert "receive_message_wait_time_seconds" in Conf.SQS
assert "aws_region" in Conf.SQS
assert broker.ping() is True
assert broker.info() is not None
assert broker.queue_size() == 0
@@ -173,7 +226,7 @@ def canceled_sqs(monkeypatch):
broker.enqueue("test")
while task is None:
task = broker.dequeue()[0]
broker.fail(task[0])
broker.fail(task[0][0])
# bulk test
for _ in range(10):
broker.enqueue("test")

View File

@@ -1,3 +1,5 @@
import time
from copy import deepcopy
from multiprocessing import Event, Value
import pytest
@@ -128,6 +130,27 @@ def test_iter(broker):
assert len(i.result()) == 5
def _sleeping_func(_):
time.sleep(1)
@pytest.mark.django_db
def test_iter_default_cache_timeout(broker, settings):
"""
Test async_iter when it completes after the default Django
cache timeout expires.
"""
cache_settings = deepcopy(settings.CACHES)
cache_settings["default"]["TIMEOUT"] = 1
settings.CACHES = cache_settings
broker.purge_queue()
broker.cache.clear()
it = [i for i in range(2)]
t = async_iter(_sleeping_func, it, sync=True)
result_t = result(t)
assert result_t is not None
@pytest.mark.django_db
def test_chain(broker):
broker.purge_queue()

View File

@@ -19,7 +19,12 @@ from django_q.models import Success, Task
from django_q.monitor import monitor, save_task
from django_q.pusher import pusher
from django_q.queues import Queue
from django_q.signals import post_execute, pre_enqueue, pre_execute
from django_q.signals import (
post_execute,
post_execute_in_worker,
pre_enqueue,
pre_execute,
)
from django_q.status import Stat
from django_q.tasks import (
async_task,
@@ -399,7 +404,7 @@ def test_timeout_task_finishes(broker, cluster_config_timeout, async_task_kwargs
@pytest.mark.django_db
def test_recycle(broker, monkeypatch):
def test_recycle(broker, monkeypatch, django_assert_num_queries):
# set up the Sentinel
broker.list_key = "test_recycle_test:q"
async_task("django_q.tests.tasks.multiply", 2, 2, broker=broker)
@@ -432,7 +437,8 @@ def test_recycle(broker, monkeypatch):
monkeypatch.setattr(Conf, "SAVE_LIMIT", 1)
result_queue.put("STOP")
# run monitor
monitor(result_queue)
with django_assert_num_queries(12):
monitor(result_queue)
assert Success.objects.count() == Conf.SAVE_LIMIT
broker.delete_queue()
@@ -487,7 +493,7 @@ def test_max_rss(broker, monkeypatch):
stop_event = Event()
cluster_id = uuidlib.uuid4()
# override settings
monkeypatch.setattr(Conf, "MAX_RSS", 40000)
monkeypatch.setattr(Conf, "MAX_RSS", 20000)
monkeypatch.setattr(Conf, "WORKERS", 1)
# set a timer to stop the Sentinel
threading.Timer(3, stop_event.set).start()
@@ -759,6 +765,35 @@ class TestSignals:
assert self.task.get("result") == -1
post_execute.disconnect(handler)
@pytest.mark.django_db
def test_post_execute_in_worker_signal(self, broker):
broker.list_key = "post_execute_in_worker_test:q"
broker.delete_queue()
self.signal_was_called: bool = False
self.task: Optional[dict] = None
self.func = None
def handler(sender, task, **kwargs):
self.signal_was_called = True
self.task = task
post_execute_in_worker.connect(handler)
task_id = async_task("math.copysign", 1, -1, broker=broker)
task_queue = Queue()
result_queue = Queue()
event = Event()
event.set()
pusher(task_queue, event, broker=broker)
task_queue.put("STOP")
worker(task_queue, result_queue, Value("f", -1))
result_queue.put("STOP")
monitor(result_queue, broker)
broker.delete_queue()
assert self.signal_was_called is True
assert self.task.get("id") == task_id
assert self.task.get("result") == -1
post_execute_in_worker.disconnect(handler)
@pytest.mark.django_db
def assert_result(task):

View File

@@ -473,6 +473,47 @@ def test_scheduler_atomic_must_specify_the_database_based_on_router_redirection(
mocked_db.atomic.assert_called_with(using="default")
@pytest.mark.django_db
def test_schedule_save_sets_next_run_for_cron():
"""Ensure Schedule.save() sets next_run correctly for CRON schedules."""
cron_expression = "0 12 * * *" # Executes dialy at 12pm
schedule = Schedule(
func="math.sqrt",
schedule_type=Schedule.CRON,
cron=cron_expression,
)
assert schedule.next_run is not None
assert schedule.pk is None
initial_next_run = schedule.next_run
schedule.save()
# After save, next_run must be recalculated based on the CRON expression
assert schedule.next_run > initial_next_run
@pytest.mark.django_db
def test_schedule_save_direct_db(broker):
"""Ensure Schedule.save() updates next_run correctly when created directly in DB."""
cron_expression = "0 12 * * *" # Executes dialy at 12pm
# Creating schedule directly in database
schedule = Schedule.objects.create(
func="math.sqrt",
schedule_type=Schedule.CRON,
cron=cron_expression,
)
# Next run must be defined after execution time
assert schedule.next_run is not None
assert schedule.next_run > timezone.now()
scheduler(broker)
assert broker.queue_size() == 0
def test_localtime():
assert not is_naive(localtime())

View File

@@ -50,9 +50,7 @@ def get_func_repr(func):
if inspect.isfunction(func):
return f"{func.__module__}.{func.__name__}"
elif inspect.ismethod(func) and hasattr(func.__self__, "__name__"):
return (
f"{func.__self__.__module__}." f"{func.__self__.__name__}.{func.__name__}"
)
return f"{func.__self__.__module__}.{func.__self__.__name__}.{func.__name__}"
else:
return str(func) if func else None

View File

@@ -18,7 +18,7 @@ except core.exceptions.AppRegistryNotReady:
from django_q.conf import Conf, error_reporter, logger, resource, setproctitle
from django_q.exceptions import TimeoutException
from django_q.signals import post_spawn, pre_execute
from django_q.signals import post_execute_in_worker, post_spawn, pre_execute
from django_q.timeout import TimeoutHandler
from django_q.utils import close_old_django_connections, get_func_repr
@@ -85,7 +85,8 @@ def worker(
if not callable(f):
# locate() returns None if f cannot be loaded
f = pydoc.locate(f)
close_old_django_connections()
if not task.get("sync", False):
close_old_django_connections()
timer_value = task.pop("timeout", timeout)
# signal execution
pre_execute.send(sender="django_q", func=f, task=task)
@@ -109,6 +110,7 @@ def worker(
if error_reporter:
error_reporter.report()
if task.get("sync", False):
post_execute_in_worker.send(sender="django_q", func=f, task=task)
raise
with timer.get_lock():
@@ -116,6 +118,7 @@ def worker(
task["result"] = result[0]
task["success"] = result[1]
task["stopped"] = timezone.now()
post_execute_in_worker.send(sender="django_q", func=f, task=task)
result_queue.put(task)
if timeout_error:
# force destroy process due to timeout

View File

@@ -73,9 +73,9 @@ author = "Ilan Steemers, Stan Triepels"
# built documents.
#
# The short X.Y version.
version = "1.7"
version = "1.10"
# The full version, including alpha/beta/rc tags.
release = "1.7.3"
release = "1.10.0"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -27,7 +27,7 @@ Features
- Rollbar and Sentry support
Django Q2 is tested with: Python 3.8, 3.9, 3.10, 3.11 and 3.12. Works with Django 4.2.x and 5.0.x
Django Q2 is tested with: Python 3.8, 3.9, 3.10, 3.11 and 3.12. Works with Django 4.2.x, 5.x and 6.0.x
Currently available in English, German and French.

View File

@@ -32,8 +32,14 @@ executed by a worker. This signal provides two arguments:
After executing a task
""""""""""""""""""""""
The ``django_q.signals.post_execute`` signal is emitted after a task is
executed by a worker and processed by the monitor. It included the ``task`` dictionary with the result.
- The ``django_q.signals.post_execute_in_worker`` signal is emitted after a task
is executed by a worker and processed by the **worker**. It included the ``task``
dictionary with the result. Note that this signal is **emitted from, and handled
by, the worker process itself**, not the monitor, unlike the ``post_execute``
signal below.
- The ``django_q.signals.post_execute`` signal is emitted after a task is
executed by a worker and processed by the **monitor**. It included the ``task``
dictionary with the result.
Subscribing to a signal
@@ -56,6 +62,10 @@ signal::
@receiver(post_execute)
def my_post_execute_callback(sender, task, **kwargs):
print(f"Task {task['name']} was executed with result {task['result']}")
@receiver(post_execute_in_worker)
def my_post_execute_in_worker_callback(sender, func, task, **kwargs):
print(f"Task {task['name']} was executed with result {task['result']}")
@receiver(post_spawn)
def my_post_spawn_callback(sender, proc_name, **kwargs):

View File

@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "django-q2"
version = "1.7.3"
version = "1.10.0"
packages = [
{ include = "django_q" },
]
@@ -30,16 +30,19 @@ classifiers = [
'Operating System :: MacOS',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: 3.13',
'Topic :: Internet :: WWW/HTTP',
'Topic :: System :: Distributed Computing',
'Topic :: Software Development :: Libraries :: Python Modules',
]
include = ['CHANGELOG.md']
include = [
{ path = "CHANGELOG.md", format = "sdist" },
]
[tool.poetry.plugins] # Optional super table
@@ -49,8 +52,8 @@ include = ['CHANGELOG.md']
[tool.poetry.dependencies]
python = ">=3.8,<4"
django = ">=4.2, <6"
python = ">=3.9,<4"
django = ">=4.2"
django-picklefield = "^3.1"
blessed = { version = "^1.19.1", optional = true }

View File

@@ -13,15 +13,41 @@ services:
networks:
- main
aws:
container_name: aws
image: localstack/localstack:3.4.0
ports:
- "127.0.0.1:4566:4566" # LocalStack Gateway
- "127.0.0.1:4510-4559:4510-4559" # External services port range
environment:
AWS_DEFAULT_REGION: ${AWS_DEFAULT_REGION:-us-west-2}
DEFAULT_REGION: ${AWS_DEFAULT_REGION:-us-west-2}
SQS_ENDPOINT_STRATEGY: path
SERVICES: sqs
LOCALSTACK_HOST: aws
DEBUG: 1
LS_LOG: trace
volumes:
- ./containers/localstack:/etc/localstack/init/ready.d
networks:
- main
django-q2:
build:
dockerfile: ./Dockerfile.dev
context: .
environment:
AWS_ENDPOINT_URL: http://aws:4566
AWS_REGION: ${AWS_REGION:-us-west-2}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-test}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-test}
AWS_DEFAULT_REGION: ${AWS_DEFAULT_REGION:-us-west-2}
volumes:
- .:/app
depends_on:
- redis
- mongo
- aws
networks:
- main