From 896c174088086ed7f4a5fc6a798190d233a60dea Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 3 Jul 2024 17:19:15 -0400 Subject: [PATCH] Flesh out NotificationGroup functionality --- .../extras/api/serializers_/notifications.py | 6 +- netbox/extras/choices.py | 2 + netbox/extras/events.py | 6 ++ netbox/extras/forms/model_forms.py | 18 ++++++ .../extras/migrations/0118_notifications.py | 2 + netbox/extras/models/notifications.py | 10 +++- netbox/extras/tables/tables.py | 4 ++ .../templates/extras/notificationgroup.html | 57 +++++++++++++++++++ 8 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 netbox/templates/extras/notificationgroup.html diff --git a/netbox/extras/api/serializers_/notifications.py b/netbox/extras/api/serializers_/notifications.py index 1d78f930c..348da82d2 100644 --- a/netbox/extras/api/serializers_/notifications.py +++ b/netbox/extras/api/serializers_/notifications.py @@ -2,7 +2,7 @@ from drf_spectacular.utils import extend_schema_field from rest_framework import serializers from core.models import ObjectType -from extras.models import Notification, Subscription +from extras.models import Notification, NotificationGroup, Subscription from netbox.api.fields import ContentTypeField, SerializedPKRelatedField from netbox.api.serializers import ValidatedModelSerializer from users.api.serializers_.users import GroupSerializer, UserSerializer @@ -54,9 +54,9 @@ class NotificationGroupSerializer(ValidatedModelSerializer): ) class Meta: - model = Notification + model = NotificationGroup fields = [ - 'id', 'url', 'display', 'name', 'description', 'object', 'groups', 'users', + 'id', 'url', 'display', 'name', 'description', 'groups', 'users', ] brief_fields = ('id', 'url', 'display', 'name', 'description') diff --git a/netbox/extras/choices.py b/netbox/extras/choices.py index d5444eae5..918d6f5fa 100644 --- a/netbox/extras/choices.py +++ b/netbox/extras/choices.py @@ -337,8 +337,10 @@ class EventRuleActionChoices(ChoiceSet): WEBHOOK = 'webhook' SCRIPT = 'script' + NOTIFICATION = 'notification' CHOICES = ( (WEBHOOK, _('Webhook')), (SCRIPT, _('Script')), + (NOTIFICATION, _('Notification')), ) diff --git a/netbox/extras/events.py b/netbox/extras/events.py index dae3f29cf..0c3f6015f 100644 --- a/netbox/extras/events.py +++ b/netbox/extras/events.py @@ -13,6 +13,7 @@ from core.models import Job from netbox.config import get_config from netbox.constants import RQ_QUEUE_DEFAULT from netbox.registry import registry +from users.models import User from utilities.api import get_serializer_for_model from utilities.rqworker import get_rq_retry from utilities.serialization import serialize_object @@ -133,6 +134,11 @@ def process_event_rules(event_rules, model_name, event, data, username=None, sna data=data ) + # Notifications + elif event_rule.action_type == EventRuleActionChoices.NOTIFICATION: + # TODO: Create notifications + pass + else: raise ValueError(_("Unknown action type for an event rule: {action_type}").format( action_type=event_rule.action_type diff --git a/netbox/extras/forms/model_forms.py b/netbox/extras/forms/model_forms.py index 9f8fd8799..d7e8fd1bd 100644 --- a/netbox/extras/forms/model_forms.py +++ b/netbox/extras/forms/model_forms.py @@ -360,6 +360,18 @@ class EventRuleForm(NetBoxModelForm): initial=initial ) + def init_notificationgroup_choice(self): + initial = None + if self.instance.action_type == EventRuleActionChoices.NOTIFICATION: + notificationgroup_id = get_field_value(self, 'action_object_id') + initial = NotificationGroup.objects.get(pk=notificationgroup_id) if notificationgroup_id else None + self.fields['action_choice'] = DynamicModelChoiceField( + label=_('Notification group'), + queryset=NotificationGroup.objects.all(), + required=True, + initial=initial + ) + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['action_object_type'].required = False @@ -372,6 +384,8 @@ class EventRuleForm(NetBoxModelForm): self.init_webhook_choice() elif action_type == EventRuleActionChoices.SCRIPT: self.init_script_choice() + elif action_type == EventRuleActionChoices.NOTIFICATION: + self.init_notificationgroup_choice() def clean(self): super().clean() @@ -388,6 +402,10 @@ class EventRuleForm(NetBoxModelForm): for_concrete_model=False ) self.cleaned_data['action_object_id'] = action_choice.id + # Notification + elif self.cleaned_data.get('action_type') == EventRuleActionChoices.NOTIFICATION: + self.cleaned_data['action_object_type'] = ObjectType.objects.get_for_model(action_choice) + self.cleaned_data['action_object_id'] = action_choice.id return self.cleaned_data diff --git a/netbox/extras/migrations/0118_notifications.py b/netbox/extras/migrations/0118_notifications.py index e81fa43ec..a6765fa33 100644 --- a/netbox/extras/migrations/0118_notifications.py +++ b/netbox/extras/migrations/0118_notifications.py @@ -17,6 +17,8 @@ class Migration(migrations.Migration): name='NotificationGroup', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), + ('created', models.DateTimeField(auto_now_add=True, null=True)), + ('last_updated', models.DateTimeField(auto_now=True, null=True)), ('name', models.CharField(max_length=100, unique=True)), ('description', models.CharField(blank=True, max_length=200)), ('groups', models.ManyToManyField(blank=True, related_name='notification_groups', to='users.group')), diff --git a/netbox/extras/models/notifications.py b/netbox/extras/models/notifications.py index 318c77283..428be3b2a 100644 --- a/netbox/extras/models/notifications.py +++ b/netbox/extras/models/notifications.py @@ -2,11 +2,13 @@ from django.conf import settings from django.contrib.contenttypes.fields import GenericForeignKey from django.core.exceptions import ValidationError from django.db import models +from django.urls import reverse from django.utils.translation import gettext_lazy as _ from core.models import ObjectType from extras.choices import * from extras.querysets import NotificationQuerySet +from netbox.models import ChangeLoggedModel from utilities.querysets import RestrictedQuerySet __all__ = ( @@ -88,7 +90,7 @@ class Notification(models.Model): ) -class NotificationGroup(models.Model): +class NotificationGroup(ChangeLoggedModel): """ A collection of users and/or groups to be informed for certain notifications. """ @@ -122,6 +124,12 @@ class NotificationGroup(models.Model): verbose_name = _('notification group') verbose_name_plural = _('notification groups') + def __str__(self): + return self.name + + def get_absolute_url(self): + return reverse('extras:notificationgroup', args=[self.pk]) + class Subscription(models.Model): """ diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py index 747964b43..f4804874e 100644 --- a/netbox/extras/tables/tables.py +++ b/netbox/extras/tables/tables.py @@ -293,6 +293,10 @@ class NotificationTable(NetBoxTable): class NotificationGroupTable(NetBoxTable): + name = tables.Column( + linkify=True, + verbose_name=_('Name') + ) class Meta(NetBoxTable.Meta): model = NotificationGroup diff --git a/netbox/templates/extras/notificationgroup.html b/netbox/templates/extras/notificationgroup.html new file mode 100644 index 000000000..ab514f8bf --- /dev/null +++ b/netbox/templates/extras/notificationgroup.html @@ -0,0 +1,57 @@ +{% extends 'generic/object.html' %} +{% load helpers %} +{% load plugins %} +{% load render_table from django_tables2 %} +{% load i18n %} + +{% block content %} +
+
+
+
{% trans "Notification Group" %}
+ + + + + + + + + +
{% trans "Name" %} + {{ object.name }} +
{% trans "Description" %} + {{ object.description|placeholder }} +
+
+ {% plugin_left_page object %} +
+
+
+
{% trans "Groups" %}
+
+ {% for group in object.groups.all %} + {{ group }} + {% empty %} +
{% trans "None assigned" %}
+ {% endfor %} +
+
+
+
{% trans "Users" %}
+
+ {% for user in object.users.all %} + {{ user }} + {% empty %} +
{% trans "None assigned" %}
+ {% endfor %} +
+
+ {% plugin_right_page object %} +
+
+
+ {% plugin_full_width_page object %} +
+ +{% endblock %}