diff --git a/sigi/apps/utils/admin.py b/sigi/apps/utils/admin.py index 268c1c0..598da0c 100644 --- a/sigi/apps/utils/admin.py +++ b/sigi/apps/utils/admin.py @@ -65,6 +65,9 @@ class CronjobAdmin(admin.ModelAdmin): "expressao_cron", "get_schedule", "get_runner", + "destinatario_email", + "digest", + "last_digest", ) fields = [ "job_name", @@ -72,8 +75,11 @@ class CronjobAdmin(admin.ModelAdmin): "get_help", "expressao_cron", "manter_logs", + "destinatario_email", + "digest", + "last_digest", ] - readonly_fields = ("job_name", "app_name", "get_help") + readonly_fields = ("job_name", "app_name", "get_help", "last_digest") inlines = [JobScheduleInline] def get_urls(self): diff --git a/sigi/apps/utils/management/jobs.py b/sigi/apps/utils/management/jobs.py index 8638bef..60fa857 100644 --- a/sigi/apps/utils/management/jobs.py +++ b/sigi/apps/utils/management/jobs.py @@ -80,14 +80,14 @@ class JobReportMixin: "output_encoding": "unicode", }, ) - send_mail( - subject=f"JOB: {self.help}", - message=rst, - from_email=settings.SERVER_EMAIL, - recipient_list=Config.get_param("EMAIL_JOBS"), - fail_silently=True, - html_message=html, - ) + # send_mail( + # subject=f"JOB: {self.help}", + # message=rst, + # from_email=settings.SERVER_EMAIL, + # recipient_list=Config.get_param("EMAIL_JOBS"), + # fail_silently=True, + # html_message=html, + # ) print(rst) def prepare_report(self, start_time, end_time): @@ -128,13 +128,13 @@ class JobReportMixin: rst, html = self.prepare_report(start_time, end_time) - if self.send_report_mail: - send_mail( - subject=f"JOB: {self.help}", - message=rst, - from_email=settings.SERVER_EMAIL, - recipient_list=Config.get_param("EMAIL_JOBS"), - fail_silently=True, - html_message=html, - ) + # if self.send_report_mail: + # send_mail( + # subject=f"JOB: {self.help}", + # message=rst, + # from_email=settings.SERVER_EMAIL, + # recipient_list=Config.get_param("EMAIL_JOBS"), + # fail_silently=True, + # html_message=html, + # ) print(rst) diff --git a/sigi/apps/utils/migrations/0005_cronjob_destinatario_email_cronjob_digest_and_more.py b/sigi/apps/utils/migrations/0005_cronjob_destinatario_email_cronjob_digest_and_more.py new file mode 100644 index 0000000..960008a --- /dev/null +++ b/sigi/apps/utils/migrations/0005_cronjob_destinatario_email_cronjob_digest_and_more.py @@ -0,0 +1,41 @@ +# Generated by Django 5.0.6 on 2024-07-30 15:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("utils", "0004_alter_jobschedule_options_cronjob_manter_logs"), + ] + + operations = [ + migrations.AddField( + model_name="cronjob", + name="destinatario_email", + field=models.TextField( + blank=True, + help_text="Insira um endereço de e-mail por linha.", + verbose_name="destinatário(s) de e-mail", + ), + ), + migrations.AddField( + model_name="cronjob", + name="digest", + field=models.CharField( + choices=[ + ("N", "Enviar sem digest"), + ("D", "Enviar com digest diário"), + ("S", "Enviar com digest semanal"), + ], + default="N", + max_length=1, + verbose_name="digest", + ), + ), + migrations.AddField( + model_name="jobschedule", + name="enviado", + field=models.BooleanField(default=False, verbose_name="enviado"), + ), + ] diff --git a/sigi/apps/utils/migrations/0006_remove_jobschedule_enviado_cronjob_last_digest.py b/sigi/apps/utils/migrations/0006_remove_jobschedule_enviado_cronjob_last_digest.py new file mode 100644 index 0000000..fa4e15c --- /dev/null +++ b/sigi/apps/utils/migrations/0006_remove_jobschedule_enviado_cronjob_last_digest.py @@ -0,0 +1,24 @@ +# Generated by Django 5.0.6 on 2024-08-13 13:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("utils", "0005_cronjob_destinatario_email_cronjob_digest_and_more"), + ] + + operations = [ + migrations.RemoveField( + model_name="jobschedule", + name="enviado", + ), + migrations.AddField( + model_name="cronjob", + name="last_digest", + field=models.DateTimeField( + blank=True, null=True, verbose_name="último envio de digest" + ), + ), + ] diff --git a/sigi/apps/utils/models.py b/sigi/apps/utils/models.py index 0642b11..59d720e 100644 --- a/sigi/apps/utils/models.py +++ b/sigi/apps/utils/models.py @@ -9,6 +9,9 @@ from django.utils.formats import localize from django.utils.translation import gettext as _ from django_extensions.management.jobs import get_job, get_jobs from tinymce.models import HTMLField +from django.core.mail import send_mail +from django.conf import settings +from datetime import timedelta class SigiAlert(models.Model): @@ -35,6 +38,17 @@ class SigiAlert(models.Model): class Cronjob(models.Model): + DIGEST_CHOICES = [ + ("N", _("Enviar sem digest")), + ("D", _("Enviar com digest diário")), + ("S", _("Enviar com digest semanal")), + ] + digest = models.CharField( + _("digest"), + max_length=1, + choices=DIGEST_CHOICES, + default="N", + ) app_name = models.CharField(_("app"), max_length=100, editable=False) job_name = models.CharField(_("job"), max_length=100, editable=False) expressao_cron = models.CharField( @@ -58,6 +72,21 @@ class Cronjob(models.Model): ), default=30, ) + destinatario_email = models.TextField( + _("destinatário(s) de e-mail"), + help_text=_("Insira um endereço de e-mail por linha."), + blank=True, + ) + last_digest = models.DateTimeField( + _("último envio de digest"), blank=True, null=True + ) + + def get_emails_list(self): + return [ + email.strip() + for email in self.destinatario_email.splitlines() + if email.strip() + ] class Meta: ordering = ("app_name", "job_name") @@ -190,6 +219,81 @@ class JobSchedule(models.Model): self.tempo_gasto = timezone.localtime() - self.iniciado self.save() + if self.job.destinatario_email == "": + return + + now = timezone.localtime() + + if self.job.digest == "N": + # Envia imediatamente sem acumular + send_mail( + subject=f"JOB: {self.job.job_name}", + message=self.resultado, + from_email=settings.SERVER_EMAIL, + recipient_list=self.job.get_emails_list(), + fail_silently=True, + html_message=self.resultado, + ) + self.job.last_digest = now + self.job.save() + + else: + # Determina o período de digest + if self.job.digest == "D": + period = timedelta(days=1) + elif self.job.digest == "S": + period = timedelta(weeks=1) + else: + raise ValueError( + f"Valor inválido para digest: {self.job.digest}" + ) + + # Se o período foi atingido desde o último digest, envia + if ( + not self.job.last_digest + or now >= self.job.last_digest + period + ): + self.send_digest_email(frequency=self.job.digest) + self.job.last_digest = now + self.job.save() + + def send_digest_email(self, frequency): + """Envia email de digest acumulando jobs desde o último digest.""" + now = timezone.localtime() + + # Determina o período de acumulação baseado no último digest + if self.job.last_digest: + period_start = self.job.last_digest + else: + period_start = ( + now - timedelta(days=1) + if frequency == "D" + else now - timedelta(weeks=1) + ) + + job_schedules = JobSchedule.objects.filter( + job=self.job, + status=JobSchedule.STATUS_CONCLUIDO, + iniciado__gte=period_start, + ) + + if job_schedules.exists(): + message_lines = [] + for js in job_schedules: + message_lines.append( + f"{localize(js.iniciado)}: {js.resultado}" + ) + message = "\n\n".join(message_lines) + + send_mail( + subject=f"Digest JOB: {self.job.job_name} ({frequency})", + message=message, + from_email=settings.SERVER_EMAIL, + recipient_list=self.job.get_emails_list(), + fail_silently=True, + html_message=message, + ) + class Config(models.Model): PARAMETRO_CHOICES = (