{% translate 'Hello' %},
++ {% blocktranslate trimmed %} + Below are all your unattended events. + {% endblocktranslate %} +
+diff --git a/ngen/models/announcement.py b/ngen/models/announcement.py index f23df1bd..b3300322 100644 --- a/ngen/models/announcement.py +++ b/ngen/models/announcement.py @@ -234,6 +234,32 @@ def send_contact_check_submitted(contact, networks, check): }, ) + @staticmethod + def send_event_sla_reminder(events): + """ + Sends an email reminder for events approaching SLA deadlines. + """ + subject = "[%s] %s" % ( + config.TEAM_NAME, + gettext_lazy("SLA Reminder"), + ) + + template = "reports/event_sla_reminder.html" + + Communication.send_mail( + subject, + Communication.render_template( + template, + extra_params={ + "events": events, + }, + ), + { + "to": [config.TEAM_EMAIL], + "from": config.EMAIL_SENDER, + }, + ) + class Announcement( AuditModelMixin, diff --git a/ngen/serializers/tools.py b/ngen/serializers/tools.py index f6940cab..0dc55e46 100644 --- a/ngen/serializers/tools.py +++ b/ngen/serializers/tools.py @@ -141,3 +141,9 @@ class AddressInfoSerializer(serializers.Serializer): with_entity = serializers.BooleanField(default=True) with_events = serializers.BooleanField(default=True) with_cases = serializers.BooleanField(default=True) + + +class TaskRunSerializer(serializers.Serializer): + task_name = serializers.CharField(max_length=255) + params = serializers.DictField(child=serializers.CharField(), required=False) + async_run = serializers.BooleanField(default=True) diff --git a/ngen/tasks.py b/ngen/tasks.py index 79204756..4cc16e22 100644 --- a/ngen/tasks.py +++ b/ngen/tasks.py @@ -549,3 +549,54 @@ def send_contact_check_submitted(check_id): networks=check.contact.networks.all(), check=check, ) + + +@shared_task(ignore_result=True, store_errors_even_if_ignored=True) +def event_sla_reminder(minutes=None): + """ + Envia un mail al team con los eventos que estan por superar el SLA. + """ + + # Get the interval for the periodic task + if minutes is not None: + try: + minutes = int(minutes) + except ValueError: + raise TaskFailure("Minutes parameter must be an integer.") + timedeltavalue = timezone.timedelta(minutes=minutes) + else: + task = PeriodicTask.objects.filter(task="ngen.tasks.event_sla_reminder").first() + if task and task.interval: + interval = task.interval + period = ( + interval.period + ) # 'seconds', 'minutes', 'hours', 'minutes', 'weeks' + every = interval.every + + # Create a timedelta object based on the interval + timedelta_kwargs = {period: every} + timedeltavalue = timezone.timedelta(**timedelta_kwargs) + else: + default_value = 14 + timedeltavalue = timezone.timedelta(minutes=default_value) + logger.warning( + f"No interval found for the periodic task 'ngen.tasks.event_sla_reminder'. Using default value: {default_value} days." + ) + # search attend SLA + events = ( + ngen.models.Event.objects.filter( + case=None + # date__gt=timezone.now() - (F("priority__attend_time") - timedeltavalue), + # date__lte=timezone.now() - F("priority__attend_time"), + ) + .exclude(tags__slug="no_sla") + .order_by("priority__severity") + ) + if events.exists(): + Communication.send_event_sla_reminder(events) + return { + "status": "success", + "message": f"SLA reminder sent for {events.count()} events", + } + else: + return {"status": "success", "message": "No events approaching SLA deadlines"} diff --git a/ngen/templates/reports/event_sla_reminder.html b/ngen/templates/reports/event_sla_reminder.html new file mode 100644 index 00000000..abfee904 --- /dev/null +++ b/ngen/templates/reports/event_sla_reminder.html @@ -0,0 +1,44 @@ +{% extends 'reports/content_base.html' %} +{% load i18n %} +{% language lang %} + + {% block content_header %} +
+ {% blocktranslate trimmed %} + Below are all your unattended events. + {% endblocktranslate %} +
+| {% translate 'Date' %} | +{% translate 'Event' %} | +{% translate 'Priority' %} | +{% translate 'Taxonomy' %} | +{% translate 'Affected resources' %} | +
|---|---|---|---|---|
| {{ event.date | date:"Y-m-d" }} | +{{ event.uuid|stringformat:"s"|slice:":8" }} | +{{ event.priority.name|stringformat:"s"|slice:":8" }} | +{{ event.taxonomy|stringformat:"s"|slice:":12" }} | +{{ event.address|stringformat:"s"|slice:":18" }} | +