You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
6.7 KiB
155 lines
6.7 KiB
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
|
|
class Notification(models.Model):
|
|
class ServiceChoices(models.TextChoices):
|
|
IMAM_JAVAD = 'imam-javad', _('Imam Javad')
|
|
DOBOODI = 'doboodi', _('Doboodi')
|
|
|
|
title = models.CharField(max_length=255, verbose_name=_('title'))
|
|
message = models.TextField(max_length=512, verbose_name=_('message'))
|
|
user = models.ForeignKey("account.User", on_delete=models.CASCADE, verbose_name=_('user'), related_name='notifications')
|
|
is_read = models.BooleanField(default=False, verbose_name=_('is read'))
|
|
service = models.CharField(
|
|
max_length=20,
|
|
choices=ServiceChoices.choices,
|
|
default=ServiceChoices.IMAM_JAVAD,
|
|
verbose_name=_('service')
|
|
)
|
|
notification_type = models.CharField(max_length=50, null=True, blank=True, verbose_name=_('notification type'))
|
|
action = models.CharField(max_length=50, default='navigate', verbose_name=_('action'))
|
|
navigate_to = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('navigate to'))
|
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'), null=True)
|
|
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'), null=True)
|
|
|
|
class Meta:
|
|
verbose_name = _('Notification')
|
|
verbose_name_plural = _('Notifications')
|
|
|
|
def __str__(self):
|
|
return self.title
|
|
|
|
|
|
class NotificationTemplate(models.Model):
|
|
notification_type = models.CharField(max_length=50, unique=True, verbose_name=_('notification type'))
|
|
name = models.CharField(max_length=100, verbose_name=_('name'))
|
|
is_active = models.BooleanField(default=True, verbose_name=_('is active'))
|
|
|
|
title_ru = models.CharField(max_length=255, default="", verbose_name=_('title (RU)'))
|
|
title_en = models.CharField(max_length=255, default="", verbose_name=_('title (EN)'))
|
|
body_ru = models.TextField(max_length=1024, default="", verbose_name=_('body (RU)'))
|
|
body_en = models.TextField(max_length=1024, default="", verbose_name=_('body (EN)'))
|
|
|
|
placeholders_info = models.CharField(max_length=255, blank=True, null=True, verbose_name=_('allowed placeholders description'))
|
|
|
|
class Meta:
|
|
verbose_name = _('Notification Template')
|
|
verbose_name_plural = _('Notification Templates')
|
|
|
|
def __str__(self):
|
|
return f"{self.name} ({self.notification_type})"
|
|
|
|
|
|
class ScheduledNotification(models.Model):
|
|
class TargetGroupChoices(models.TextChoices):
|
|
ALL_STUDENTS = 'all_students', _('All Students')
|
|
INACTIVE_STUDENTS = 'inactive_students', _('Students Inactive for > 7 Days')
|
|
ALL_PROFESSORS = 'all_professors', _('All Professors')
|
|
|
|
class ScheduleTypeChoices(models.TextChoices):
|
|
DAILY = 'daily', _('Daily')
|
|
WEEKLY = 'weekly', _('Weekly')
|
|
MONTHLY = 'monthly', _('Monthly')
|
|
CRON = 'cron', _('Custom Cron Expression')
|
|
|
|
name = models.CharField(max_length=100, verbose_name=_('campaign name'))
|
|
template = models.ForeignKey(NotificationTemplate, on_delete=models.CASCADE, verbose_name=_('template'))
|
|
target_group = models.CharField(max_length=50, choices=TargetGroupChoices.choices, verbose_name=_('target group'))
|
|
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'))
|
|
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'))
|
|
days_of_month = models.CharField(max_length=50, blank=True, null=True, help_text=_('Comma-separated days of month (1-31). E.g. "1" for first day of the month.'), verbose_name=_('days of month'))
|
|
custom_cron = models.CharField(max_length=100, blank=True, null=True, help_text=_('Standard 5-field cron expression: minute hour day_of_month month day_of_week'), verbose_name=_('custom cron expression'))
|
|
|
|
periodic_task = models.OneToOneField(
|
|
'django_celery_beat.PeriodicTask',
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='scheduled_notification'
|
|
)
|
|
|
|
class Meta:
|
|
verbose_name = _('Scheduled Notification')
|
|
verbose_name_plural = _('Scheduled Notifications')
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
def save(self, *args, **kwargs):
|
|
from django_celery_beat.models import CrontabSchedule, 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')
|
|
)
|
|
|
|
task_name = f"Scheduled Notification: {self.name}"
|
|
task_kwargs = json.dumps({
|
|
'campaign_id': self.id
|
|
})
|
|
|
|
if self.periodic_task:
|
|
task = self.periodic_task
|
|
task.name = task_name
|
|
task.crontab = schedule
|
|
task.enabled = self.is_active
|
|
task.kwargs = task_kwargs
|
|
task.save()
|
|
else:
|
|
task = PeriodicTask.objects.create(
|
|
name=task_name,
|
|
task='apps.account.tasks.run_scheduled_notification_task',
|
|
crontab=schedule,
|
|
enabled=self.is_active,
|
|
kwargs=task_kwargs
|
|
)
|
|
self.periodic_task = task
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
def delete(self, *args, **kwargs):
|
|
if self.periodic_task:
|
|
self.periodic_task.delete()
|
|
super().delete(*args, **kwargs)
|
|
|
|
|