Flesh out NotificationGroup functionality

This commit is contained in:
Jeremy Stretch 2024-07-03 17:19:15 -04:00
parent b0e37bc12d
commit 896c174088
8 changed files with 101 additions and 4 deletions

View File

@ -2,7 +2,7 @@ from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers from rest_framework import serializers
from core.models import ObjectType 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.fields import ContentTypeField, SerializedPKRelatedField
from netbox.api.serializers import ValidatedModelSerializer from netbox.api.serializers import ValidatedModelSerializer
from users.api.serializers_.users import GroupSerializer, UserSerializer from users.api.serializers_.users import GroupSerializer, UserSerializer
@ -54,9 +54,9 @@ class NotificationGroupSerializer(ValidatedModelSerializer):
) )
class Meta: class Meta:
model = Notification model = NotificationGroup
fields = [ fields = [
'id', 'url', 'display', 'name', 'description', 'object', 'groups', 'users', 'id', 'url', 'display', 'name', 'description', 'groups', 'users',
] ]
brief_fields = ('id', 'url', 'display', 'name', 'description') brief_fields = ('id', 'url', 'display', 'name', 'description')

View File

@ -337,8 +337,10 @@ class EventRuleActionChoices(ChoiceSet):
WEBHOOK = 'webhook' WEBHOOK = 'webhook'
SCRIPT = 'script' SCRIPT = 'script'
NOTIFICATION = 'notification'
CHOICES = ( CHOICES = (
(WEBHOOK, _('Webhook')), (WEBHOOK, _('Webhook')),
(SCRIPT, _('Script')), (SCRIPT, _('Script')),
(NOTIFICATION, _('Notification')),
) )

View File

@ -13,6 +13,7 @@ from core.models import Job
from netbox.config import get_config from netbox.config import get_config
from netbox.constants import RQ_QUEUE_DEFAULT from netbox.constants import RQ_QUEUE_DEFAULT
from netbox.registry import registry from netbox.registry import registry
from users.models import User
from utilities.api import get_serializer_for_model from utilities.api import get_serializer_for_model
from utilities.rqworker import get_rq_retry from utilities.rqworker import get_rq_retry
from utilities.serialization import serialize_object 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 data=data
) )
# Notifications
elif event_rule.action_type == EventRuleActionChoices.NOTIFICATION:
# TODO: Create notifications
pass
else: else:
raise ValueError(_("Unknown action type for an event rule: {action_type}").format( raise ValueError(_("Unknown action type for an event rule: {action_type}").format(
action_type=event_rule.action_type action_type=event_rule.action_type

View File

@ -360,6 +360,18 @@ class EventRuleForm(NetBoxModelForm):
initial=initial 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): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['action_object_type'].required = False self.fields['action_object_type'].required = False
@ -372,6 +384,8 @@ class EventRuleForm(NetBoxModelForm):
self.init_webhook_choice() self.init_webhook_choice()
elif action_type == EventRuleActionChoices.SCRIPT: elif action_type == EventRuleActionChoices.SCRIPT:
self.init_script_choice() self.init_script_choice()
elif action_type == EventRuleActionChoices.NOTIFICATION:
self.init_notificationgroup_choice()
def clean(self): def clean(self):
super().clean() super().clean()
@ -388,6 +402,10 @@ class EventRuleForm(NetBoxModelForm):
for_concrete_model=False for_concrete_model=False
) )
self.cleaned_data['action_object_id'] = action_choice.id 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 return self.cleaned_data

View File

@ -17,6 +17,8 @@ class Migration(migrations.Migration):
name='NotificationGroup', name='NotificationGroup',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)), ('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)), ('name', models.CharField(max_length=100, unique=True)),
('description', models.CharField(blank=True, max_length=200)), ('description', models.CharField(blank=True, max_length=200)),
('groups', models.ManyToManyField(blank=True, related_name='notification_groups', to='users.group')), ('groups', models.ManyToManyField(blank=True, related_name='notification_groups', to='users.group')),

View File

@ -2,11 +2,13 @@ from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.fields import GenericForeignKey
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from core.models import ObjectType from core.models import ObjectType
from extras.choices import * from extras.choices import *
from extras.querysets import NotificationQuerySet from extras.querysets import NotificationQuerySet
from netbox.models import ChangeLoggedModel
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
__all__ = ( __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. 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 = _('notification group')
verbose_name_plural = _('notification groups') 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): class Subscription(models.Model):
""" """

View File

@ -293,6 +293,10 @@ class NotificationTable(NetBoxTable):
class NotificationGroupTable(NetBoxTable): class NotificationGroupTable(NetBoxTable):
name = tables.Column(
linkify=True,
verbose_name=_('Name')
)
class Meta(NetBoxTable.Meta): class Meta(NetBoxTable.Meta):
model = NotificationGroup model = NotificationGroup

View File

@ -0,0 +1,57 @@
{% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col col-md-6">
<div class="card">
<h5 class="card-header">{% trans "Notification Group" %}</h5>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>
{{ object.name }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>
{{ object.description|placeholder }}
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-md-6">
<div class="card">
<h5 class="card-header">{% trans "Groups" %}</h5>
<div class="list-group list-group-flush">
{% for group in object.groups.all %}
<a href="{{ group.get_absolute_url }}" class="list-group-item list-group-item-action">{{ group }}</a>
{% empty %}
<div class="list-group-item text-muted">{% trans "None assigned" %}</div>
{% endfor %}
</div>
</div>
<div class="card">
<h5 class="card-header">{% trans "Users" %}</h5>
<div class="list-group list-group-flush">
{% for user in object.users.all %}
<a href="{{ user.get_absolute_url }}" class="list-group-item list-group-item-action">{{ user }}</a>
{% empty %}
<div class="list-group-item text-muted">{% trans "None assigned" %}</div>
{% endfor %}
</div>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}