Register event types in the registry; add colors & icons

This commit is contained in:
Jeremy Stretch 2024-07-04 14:18:17 -04:00
parent 8065d7c127
commit 8c0693fb36
9 changed files with 74 additions and 30 deletions

View File

@ -26,7 +26,7 @@ class NotificationSerializer(ValidatedModelSerializer):
class Meta: class Meta:
model = Notification model = Notification
fields = [ fields = [
'id', 'url', 'display', 'object_type', 'object_id', 'object', 'user', 'created', 'read', 'event', 'id', 'url', 'display', 'object_type', 'object_id', 'object', 'user', 'created', 'read', 'event_name',
] ]
brief_fields = ('id', 'url', 'display', 'object_type', 'object_id', 'user', 'event') brief_fields = ('id', 'url', 'display', 'object_type', 'object_id', 'user', 'event')

View File

@ -148,25 +148,6 @@ class JournalEntryKindChoices(ChoiceSet):
] ]
#
# Notifications
#
# TODO: Support dynamic entries from plugins
class NotificationEventChoices(ChoiceSet):
key = 'Notification.event'
OBJECT_CREATED = 'object_created'
OBJECT_CHANGED = 'object_changed'
OBJECT_DELETED = 'object_deleted'
CHOICES = [
(OBJECT_CREATED, _('Object created')),
(OBJECT_CHANGED, _('Object changed')),
(OBJECT_DELETED, _('Object deleted')),
]
# #
# Reports and Scripts # Reports and Scripts
# #

View File

@ -12,8 +12,8 @@ from core.choices import ObjectChangeActionChoices
from core.models import Job 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.events import Event
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
@ -22,6 +22,15 @@ from .models import EventRule
logger = logging.getLogger('netbox.events_processor') logger = logging.getLogger('netbox.events_processor')
EVENT_OBJECT_CREATED = 'object_created'
EVENT_OBJECT_UPDATED = 'object_updated'
EVENT_OBJECT_DELETED = 'object_deleted'
# Register event types
Event(name=EVENT_OBJECT_CREATED, text=_('Object created')).register()
Event(name=EVENT_OBJECT_UPDATED, text=_('Object updated')).register()
Event(name=EVENT_OBJECT_DELETED, text=_('Object deleted')).register()
def serialize_for_event(instance): def serialize_for_event(instance):
""" """

View File

@ -52,7 +52,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(auto_now_add=True)), ('created', models.DateTimeField(auto_now_add=True)),
('read', models.DateTimeField(null=True)), ('read', models.DateTimeField(null=True)),
('object_id', models.PositiveBigIntegerField()), ('object_id', models.PositiveBigIntegerField()),
('event', models.CharField(max_length=30)), ('event_name', models.CharField(max_length=50)),
('object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')), ('object_type', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)),
], ],

View File

@ -1,3 +1,5 @@
from functools import cached_property
from django.conf import settings 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
@ -6,9 +8,9 @@ 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.querysets import NotificationQuerySet from extras.querysets import NotificationQuerySet
from netbox.models import ChangeLoggedModel from netbox.models import ChangeLoggedModel
from netbox.registry import registry
from utilities.querysets import RestrictedQuerySet from utilities.querysets import RestrictedQuerySet
__all__ = ( __all__ = (
@ -44,10 +46,9 @@ class Notification(models.Model):
ct_field='object_type', ct_field='object_type',
fk_field='object_id' fk_field='object_id'
) )
event = models.CharField( event_name = models.CharField(
verbose_name=_('event'), verbose_name=_('event'),
max_length=30, max_length=50
choices=NotificationEventChoices
) )
objects = NotificationQuerySet.as_manager() objects = NotificationQuerySet.as_manager()
@ -89,6 +90,10 @@ class Notification(models.Model):
_("Objects of this type ({type}) do not support notifications.").format(type=self.object_type) _("Objects of this type ({type}) do not support notifications.").format(type=self.object_type)
) )
@cached_property
def event(self):
return registry['events'].get(self.event_name)
class NotificationGroup(ChangeLoggedModel): class NotificationGroup(ChangeLoggedModel):
""" """

View File

@ -12,7 +12,6 @@ from django_prometheus.models import model_deletes, model_inserts, model_updates
from core.choices import ObjectChangeActionChoices from core.choices import ObjectChangeActionChoices
from core.models import ObjectChange, ObjectType from core.models import ObjectChange, ObjectType
from core.signals import job_end, job_start from core.signals import job_end, job_start
from extras.choices import NotificationEventChoices
from extras.constants import EVENT_JOB_END, EVENT_JOB_START from extras.constants import EVENT_JOB_END, EVENT_JOB_START
from extras.events import process_event_rules from extras.events import process_event_rules
from extras.models import EventRule, Notification, Subscription from extras.models import EventRule, Notification, Subscription
@ -22,7 +21,7 @@ from netbox.models.features import ChangeLoggingMixin
from netbox.registry import registry from netbox.registry import registry
from netbox.signals import post_clean from netbox.signals import post_clean
from utilities.exceptions import AbortRequest from utilities.exceptions import AbortRequest
from .events import enqueue_object from .events import EVENT_OBJECT_UPDATED, enqueue_object
from .models import CustomField, TaggedItem from .models import CustomField, TaggedItem
from .validators import CustomValidator from .validators import CustomValidator
@ -310,7 +309,7 @@ def notify_object_changed(sender, instance, created, raw, **kwargs):
Notification( Notification(
user_id=sub['user'], user_id=sub['user'],
object=instance, object=instance,
event=NotificationEventChoices.OBJECT_CHANGED event_name=EVENT_OBJECT_UPDATED
) )
for sub in subscriptions for sub in subscriptions
] ]

46
netbox/netbox/events.py Normal file
View File

@ -0,0 +1,46 @@
from dataclasses import dataclass
from netbox.registry import registry
__all__ = (
'EVENT_TYPE_DANGER',
'EVENT_TYPE_INFO',
'EVENT_TYPE_SUCCESS',
'EVENT_TYPE_WARNING',
'Event',
)
EVENT_TYPE_INFO = 'info'
EVENT_TYPE_SUCCESS = 'success'
EVENT_TYPE_WARNING = 'warning'
EVENT_TYPE_DANGER = 'danger'
@dataclass
class Event:
name: str
text: str
type: str = EVENT_TYPE_INFO
def __str__(self):
return self.text
def register(self):
registry['events'][self.name] = self
def color(self):
return {
EVENT_TYPE_INFO: 'blue',
EVENT_TYPE_SUCCESS: 'green',
EVENT_TYPE_WARNING: 'orange',
EVENT_TYPE_DANGER: 'red',
}.get(self.type)
def icon(self):
return {
EVENT_TYPE_INFO: 'mdi mdi-information',
EVENT_TYPE_SUCCESS: 'mdi mdi-check-circle',
EVENT_TYPE_WARNING: 'mdi mdi-alert-box',
EVENT_TYPE_DANGER: 'mdi mdi-alert-octagon',
}.get(self.type)

View File

@ -25,6 +25,7 @@ registry = Registry({
'counter_fields': collections.defaultdict(dict), 'counter_fields': collections.defaultdict(dict),
'data_backends': dict(), 'data_backends': dict(),
'denormalized_fields': collections.defaultdict(list), 'denormalized_fields': collections.defaultdict(list),
'events': dict(),
'model_features': dict(), 'model_features': dict(),
'models': collections.defaultdict(set), 'models': collections.defaultdict(set),
'plugins': dict(), 'plugins': dict(),

View File

@ -3,9 +3,12 @@
{% for notification in notifications %} {% for notification in notifications %}
<div class="list-group-item p-2"> <div class="list-group-item p-2">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-auto text-{{ notification.event.color }} pe-0">
<i class="{{ notification.event.icon }}"></i>
</div>
<div class="col text-truncate"> <div class="col text-truncate">
<a href="{{ notification.get_read_url }}" class="text-body d-block">{{ notification.object }}</a> <a href="{{ notification.get_read_url }}" class="text-body d-block">{{ notification.object }}</a>
<div class="d-block text-secondary fs-5">{{ notification.get_event_display }} {{ notification.created|timesince }} {% trans "ago" %}</div> <div class="d-block text-secondary fs-5">{{ notification.event }} {{ notification.created|timesince }} {% trans "ago" %}</div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<a href="#" hx-get="{{ notification.get_dismiss_url }}" hx-target="closest .notifications" class="list-group-item-actions text-secondary" title="{% trans "Dismiss" %}"> <a href="#" hx-get="{{ notification.get_dismiss_url }}" hx-target="closest .notifications" class="list-group-item-actions text-secondary" title="{% trans "Dismiss" %}">