Browse Source

scedule and repeatable task type logic splitted

master
Mohsen Taba 1 week ago
parent
commit
358f481708
  1. 23
      apps/account/migrations/0010_schedulednotification_scheduled_time_and_more.py
  2. 85
      apps/account/models/notification.py
  3. 2
      apps/account/serializers/notification.py
  4. 4
      apps/account/tasks.py

23
apps/account/migrations/0010_schedulednotification_scheduled_time_and_more.py

@ -0,0 +1,23 @@
# Generated by Django 4.2.30 on 2026-07-12 10:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0009_schedulednotification_body_en_and_more'),
]
operations = [
migrations.AddField(
model_name='schedulednotification',
name='scheduled_time',
field=models.DateTimeField(blank=True, null=True, verbose_name='scheduled time'),
),
migrations.AlterField(
model_name='schedulednotification',
name='schedule_type',
field=models.CharField(choices=[('do_not_repeat', 'Do Not Repeat'), ('daily', 'Daily'), ('weekly', 'Weekly'), ('monthly', 'Monthly'), ('cron', 'Custom Cron Expression')], default='do_not_repeat', max_length=20, verbose_name='schedule type'),
),
]

85
apps/account/models/notification.py

@ -58,6 +58,7 @@ class ScheduledNotification(models.Model):
ALL_PROFESSORS = 'all_professors', _('All Professors')
class ScheduleTypeChoices(models.TextChoices):
DO_NOT_REPEAT = 'do_not_repeat', _('Do Not Repeat')
DAILY = 'daily', _('Daily')
WEEKLY = 'weekly', _('Weekly')
MONTHLY = 'monthly', _('Monthly')
@ -76,7 +77,8 @@ class ScheduledNotification(models.Model):
is_active = models.BooleanField(default=True, verbose_name=_('is active'))
schedule_type = models.CharField(max_length=20, choices=ScheduleTypeChoices.choices, default=ScheduleTypeChoices.DAILY, verbose_name=_('schedule type'))
schedule_type = models.CharField(max_length=20, choices=ScheduleTypeChoices.choices, default=ScheduleTypeChoices.DO_NOT_REPEAT, verbose_name=_('schedule type'))
scheduled_time = models.DateTimeField(null=True, blank=True, verbose_name=_('scheduled time'))
time_of_day = models.TimeField(default="09:00:00", verbose_name=_('time of day'))
days_of_week = models.CharField(max_length=50, blank=True, null=True, help_text=_('Comma-separated days (1 for Mon, 6 for Sat, 0 for Sun). E.g. "6,0" for Sat and Sun.'), verbose_name=_('days of week'))
@ -99,38 +101,59 @@ class ScheduledNotification(models.Model):
return self.name
def save(self, *args, **kwargs):
from django_celery_beat.models import CrontabSchedule, PeriodicTask
from django_celery_beat.models import CrontabSchedule, ClockedSchedule, PeriodicTask
import json
from django.conf import settings
# Calculate cron fields based on schedule_type
minute = str(self.time_of_day.minute)
hour = str(self.time_of_day.hour)
day_of_month = '*'
month_of_year = '*'
day_of_week = '*'
if self.schedule_type == self.ScheduleTypeChoices.DAILY:
pass
elif self.schedule_type == self.ScheduleTypeChoices.WEEKLY:
day_of_week = self.days_of_week or '*'
elif self.schedule_type == self.ScheduleTypeChoices.MONTHLY:
day_of_month = self.days_of_month or '1'
elif self.schedule_type == self.ScheduleTypeChoices.CRON:
if self.custom_cron:
parts = self.custom_cron.split()
if len(parts) >= 5:
minute, hour, day_of_month, month_of_year, day_of_week = parts[:5]
schedule, _ = CrontabSchedule.objects.get_or_create(
minute=minute,
hour=hour,
day_of_week=day_of_week,
day_of_month=day_of_month,
month_of_year=month_of_year,
timezone=getattr(settings, 'CELERY_TIMEZONE', 'Asia/Tehran')
)
schedule = None
clocked = None
one_off = False
if self.schedule_type == self.ScheduleTypeChoices.DO_NOT_REPEAT:
if self.scheduled_time:
clocked, _ = ClockedSchedule.objects.get_or_create(
clocked_time=self.scheduled_time
)
one_off = True
else:
if self.periodic_task:
task = self.periodic_task
self.periodic_task = None
super().save(*args, **kwargs)
task.delete()
return
else:
super().save(*args, **kwargs)
return
else:
# Calculate cron fields based on schedule_type
minute = str(self.time_of_day.minute)
hour = str(self.time_of_day.hour)
day_of_month = '*'
month_of_year = '*'
day_of_week = '*'
if self.schedule_type == self.ScheduleTypeChoices.DAILY:
pass
elif self.schedule_type == self.ScheduleTypeChoices.WEEKLY:
day_of_week = self.days_of_week or '*'
elif self.schedule_type == self.ScheduleTypeChoices.MONTHLY:
day_of_month = self.days_of_month or '1'
elif self.schedule_type == self.ScheduleTypeChoices.CRON:
if self.custom_cron:
parts = self.custom_cron.split()
if len(parts) >= 5:
minute, hour, day_of_month, month_of_year, day_of_week = parts[:5]
schedule, _ = CrontabSchedule.objects.get_or_create(
minute=minute,
hour=hour,
day_of_week=day_of_week,
day_of_month=day_of_month,
month_of_year=month_of_year,
timezone=getattr(settings, 'CELERY_TIMEZONE', 'Asia/Tehran')
)
task_name = f"Scheduled Notification: {self.name}"
task_kwargs = json.dumps({
'campaign_id': self.id
@ -140,6 +163,8 @@ class ScheduledNotification(models.Model):
task = self.periodic_task
task.name = task_name
task.crontab = schedule
task.clocked = clocked
task.one_off = one_off
task.enabled = self.is_active
task.kwargs = task_kwargs
task.save()
@ -148,6 +173,8 @@ class ScheduledNotification(models.Model):
name=task_name,
task='apps.account.tasks.run_scheduled_notification_task',
crontab=schedule,
clocked=clocked,
one_off=one_off,
enabled=self.is_active,
kwargs=task_kwargs
)

2
apps/account/serializers/notification.py

@ -56,5 +56,5 @@ class AdminScheduledNotificationSerializer(serializers.ModelSerializer):
'id', 'name', 'template', 'template_name', 'target_group', 'send_to_all',
'user', 'user_fullname', 'user_email', 'title_ru', 'title_en',
'body_ru', 'body_en', 'is_active', 'schedule_type', 'time_of_day',
'days_of_week', 'days_of_month', 'custom_cron'
'days_of_week', 'days_of_month', 'custom_cron', 'scheduled_time'
]

4
apps/account/tasks.py

@ -457,6 +457,10 @@ def run_scheduled_notification_task(campaign_id):
notification_type=notification_type
)
if campaign.schedule_type == 'do_not_repeat':
campaign.is_active = False
campaign.save()

Loading…
Cancel
Save