celery beat
This commit is contained in:
parent
64644a5a67
commit
6795e415f1
7 changed files with 131 additions and 28 deletions
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 5.2.6 on 2025-09-06 13:25
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('emails', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='email',
|
||||
name='extras',
|
||||
field=models.JSONField(blank=True, default=dict, null=True),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EmailQueue',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=255, verbose_name='title')),
|
||||
('last_sent', models.DateTimeField(auto_now_add=True, verbose_name='last sent')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='email',
|
||||
name='queue',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='emails.emailqueue'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
from django.db import models
|
||||
from django.db.models import F
|
||||
from oauth2_provider.settings import oauth2_settings
|
||||
|
||||
from apps.users.models import User
|
||||
|
|
@ -6,6 +7,18 @@ from utils.models import BaseModel
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class EmailQueueQuerySet(models.query.QuerySet):
|
||||
pass
|
||||
|
||||
|
||||
class EmailQueue(models.Model):
|
||||
title = models.CharField(_('title'), max_length=255)
|
||||
user = models.ForeignKey(User, verbose_name=_('user'), on_delete=models.CASCADE)
|
||||
last_sent = models.DateTimeField(_('last sent'), auto_now_add=True)
|
||||
|
||||
objects = EmailQueueQuerySet.as_manager()
|
||||
|
||||
|
||||
class EmailQuerySet(models.QuerySet):
|
||||
pass
|
||||
|
||||
|
|
@ -27,6 +40,8 @@ class Email(BaseModel):
|
|||
priority = models.IntegerField(default=5)
|
||||
extras = models.JSONField(default=dict, null=True, blank=True)
|
||||
stats = models.IntegerField(default=StateChoices.INIT, choices=StateChoices.choices)
|
||||
queue = models.ForeignKey(EmailQueue, on_delete=models.PROTECT, related_name='+', null=True, blank=True)
|
||||
|
||||
|
||||
# bulk = models.ForeignKey("BulkEmail", null=True, blank=True, on_delete=models.PROTECT)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class EmailSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = Email
|
||||
fields = (
|
||||
"queue",
|
||||
"title",
|
||||
"message",
|
||||
"user",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
from django.db.models import F
|
||||
from django.utils import timezone
|
||||
import logging
|
||||
from apps.emails.models import Email, EmailQueue
|
||||
from main import celery_app
|
||||
from utils.clients import gotify
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@celery_app.task
|
||||
def send_email(email_uuid):
|
||||
|
|
@ -10,3 +15,32 @@ def send_email(email_uuid):
|
|||
|
||||
email.state = email.StateChoices.DONE
|
||||
email.save()
|
||||
|
||||
|
||||
@celery_app.task
|
||||
def send_email_for_queued_events():
|
||||
logger.info('Sending email for queued events')
|
||||
for email_queue in EmailQueue.objects.all():
|
||||
qs = Email.objects.filter(queue=email_queue, stats=Email.StateChoices.INIT, created_at_gte=F('queue__last_sent'))
|
||||
if email_queue.last_sent <= timezone.now() - timezone.timedelta(hours=1) and qs.exists():
|
||||
from django.core.mail import send_mail
|
||||
from django.conf import settings
|
||||
send_mail(
|
||||
"you have some queued task(s)",
|
||||
f"you have {qs.count()} queued task(s)",
|
||||
settings.EMAIL_HOST_USER,
|
||||
["xdshia49@gmail.com"],
|
||||
fail_silently=False,
|
||||
)
|
||||
email_queue.last_sent = timezone.now()
|
||||
email_queue.save()
|
||||
|
||||
|
||||
celery_app.conf.beat_schedule = {
|
||||
'print-every-minute': {
|
||||
'task': 'apps.emails.tasks.send_email_for_queued_events',
|
||||
'schedule': 10.0, # هر 60 ثانیه
|
||||
# یا میتوانید از crontab استفاده کنید:
|
||||
# 'schedule': crontab(minute='*/1'),
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ INSTALLED_APPS = [
|
|||
'crispy_bootstrap5',
|
||||
'django_filters',
|
||||
'jalali_date',
|
||||
"django_celery_beat",
|
||||
# 'django_minio_backend.apps.DjangoMinioBackendConfig',
|
||||
|
||||
# local apps
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ celery
|
|||
httpx
|
||||
django-redis
|
||||
django_minio_backend
|
||||
xlrd
|
||||
xlrd
|
||||
django-celery-beat
|
||||
# aiosmtpd #??
|
||||
|
|
@ -1,32 +1,37 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# This file is autogenerated by pip-compile with Python 3.13
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile
|
||||
#
|
||||
aiosmtpd==1.4.6
|
||||
# via -r requirements.in
|
||||
amqp==5.3.1
|
||||
# via kombu
|
||||
anyio==4.9.0
|
||||
anyio==4.10.0
|
||||
# via httpx
|
||||
argon2-cffi==25.1.0
|
||||
# via minio
|
||||
argon2-cffi-bindings==21.2.0
|
||||
argon2-cffi-bindings==25.1.0
|
||||
# via argon2-cffi
|
||||
asgiref==3.8.1
|
||||
asgiref==3.9.1
|
||||
# via
|
||||
# django
|
||||
# django-cors-headers
|
||||
async-timeout==5.0.1
|
||||
# via redis
|
||||
atpublic==6.0.1
|
||||
# via aiosmtpd
|
||||
attrs==25.3.0
|
||||
# via
|
||||
# aiosmtpd
|
||||
# jsonschema
|
||||
# referencing
|
||||
billiard==4.2.1
|
||||
# via celery
|
||||
celery==5.5.3
|
||||
# via -r requirements.in
|
||||
certifi==2025.6.15
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-celery-beat
|
||||
certifi==2025.8.3
|
||||
# via
|
||||
# httpcore
|
||||
# httpx
|
||||
|
|
@ -36,7 +41,7 @@ cffi==1.17.1
|
|||
# via
|
||||
# argon2-cffi-bindings
|
||||
# cryptography
|
||||
charset-normalizer==3.4.2
|
||||
charset-normalizer==3.4.3
|
||||
# via requests
|
||||
click==8.2.1
|
||||
# via
|
||||
|
|
@ -52,12 +57,15 @@ click-repl==0.3.0
|
|||
# via celery
|
||||
crispy-bootstrap5==2025.6
|
||||
# via -r requirements.in
|
||||
cryptography==45.0.4
|
||||
cron-descriptor==2.0.6
|
||||
# via django-celery-beat
|
||||
cryptography==45.0.7
|
||||
# via jwcrypto
|
||||
django==5.2.3
|
||||
django==5.2.6
|
||||
# via
|
||||
# -r requirements.in
|
||||
# crispy-bootstrap5
|
||||
# django-celery-beat
|
||||
# django-cors-headers
|
||||
# django-crispy-forms
|
||||
# django-filter
|
||||
|
|
@ -65,8 +73,11 @@ django==5.2.3
|
|||
# django-minio-backend
|
||||
# django-oauth-toolkit
|
||||
# django-redis
|
||||
# django-timezone-field
|
||||
# djangorestframework
|
||||
# drf-spectacular
|
||||
django-celery-beat==2.8.1
|
||||
# via -r requirements.in
|
||||
django-cors-headers==4.7.0
|
||||
# via -r requirements.in
|
||||
django-crispy-forms==2.4
|
||||
|
|
@ -83,15 +94,17 @@ django-oauth-toolkit==3.0.1
|
|||
# via -r requirements.in
|
||||
django-redis==6.0.0
|
||||
# via -r requirements.in
|
||||
djangorestframework==3.16.0
|
||||
django-timezone-field==7.1
|
||||
# via django-celery-beat
|
||||
djangorestframework==3.16.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# drf-spectacular
|
||||
drf-spectacular==0.28.0
|
||||
# via -r requirements.in
|
||||
gevent==25.5.1
|
||||
gevent==25.8.2
|
||||
# via -r requirements.in
|
||||
greenlet==3.2.3
|
||||
greenlet==3.2.4
|
||||
# via gevent
|
||||
gunicorn==23.0.0
|
||||
# via -r requirements.in
|
||||
|
|
@ -112,7 +125,7 @@ jalali-core==1.0.0
|
|||
# via jdatetime
|
||||
jdatetime==5.2.0
|
||||
# via django-jalali-date
|
||||
jsonschema==4.24.0
|
||||
jsonschema==4.25.1
|
||||
# via drf-spectacular
|
||||
jsonschema-specifications==2025.4.1
|
||||
# via jsonschema
|
||||
|
|
@ -120,7 +133,7 @@ jwcrypto==1.5.6
|
|||
# via django-oauth-toolkit
|
||||
kombu==5.5.4
|
||||
# via celery
|
||||
minio==7.2.15
|
||||
minio==7.2.16
|
||||
# via django-minio-backend
|
||||
oauthlib==3.3.1
|
||||
# via django-oauth-toolkit
|
||||
|
|
@ -128,9 +141,9 @@ packaging==25.0
|
|||
# via
|
||||
# gunicorn
|
||||
# kombu
|
||||
pillow==11.2.1
|
||||
pillow==11.3.0
|
||||
# via -r requirements.in
|
||||
prompt-toolkit==3.0.51
|
||||
prompt-toolkit==3.0.52
|
||||
# via click-repl
|
||||
psycopg[binary,pool]==3.2.9
|
||||
# via -r requirements.in
|
||||
|
|
@ -142,13 +155,15 @@ pycparser==2.22
|
|||
# via cffi
|
||||
pycryptodome==3.23.0
|
||||
# via minio
|
||||
python-crontab==3.3.0
|
||||
# via django-celery-beat
|
||||
python-dateutil==2.9.0.post0
|
||||
# via celery
|
||||
python-decouple==3.8
|
||||
# via -r requirements.in
|
||||
pyyaml==6.0.2
|
||||
# via drf-spectacular
|
||||
redis==6.2.0
|
||||
redis==6.4.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-redis
|
||||
|
|
@ -156,11 +171,11 @@ referencing==0.36.2
|
|||
# via
|
||||
# jsonschema
|
||||
# jsonschema-specifications
|
||||
requests==2.32.4
|
||||
requests==2.32.5
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-oauth-toolkit
|
||||
rpds-py==0.25.1
|
||||
rpds-py==0.27.1
|
||||
# via
|
||||
# jsonschema
|
||||
# referencing
|
||||
|
|
@ -170,16 +185,16 @@ sniffio==1.3.1
|
|||
# via anyio
|
||||
sqlparse==0.5.3
|
||||
# via django
|
||||
typing-extensions==4.14.0
|
||||
typing-extensions==4.15.0
|
||||
# via
|
||||
# anyio
|
||||
# cron-descriptor
|
||||
# jwcrypto
|
||||
# minio
|
||||
# psycopg
|
||||
# psycopg-pool
|
||||
# referencing
|
||||
tzdata==2025.2
|
||||
# via kombu
|
||||
# via
|
||||
# django-celery-beat
|
||||
# kombu
|
||||
uritemplate==4.2.0
|
||||
# via drf-spectacular
|
||||
urllib3==2.5.0
|
||||
|
|
@ -195,7 +210,7 @@ wcwidth==0.2.13
|
|||
# via prompt-toolkit
|
||||
xlrd==2.0.2
|
||||
# via -r requirements.in
|
||||
zope-event==5.1
|
||||
zope-event==5.1.1
|
||||
# via gevent
|
||||
zope-interface==7.2
|
||||
# via gevent
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue