Clean up utility functions

This commit is contained in:
jeremystretch 2023-02-24 10:33:29 -05:00
parent 892fce1518
commit 96a8c72b05
6 changed files with 76 additions and 38 deletions

View File

@ -1,13 +1,14 @@
import uuid
from django.core.exceptions import ObjectDoesNotExist
from netbox.registry import registry
from extras.constants import DEFAULT_DASHBOARD
from extras.models import Dashboard
__all__ = (
'get_dashboard',
'get_default_dashboard_config',
'get_widget_class_and_config',
'get_default_dashboard',
'get_widget_class',
'register_widget',
)
@ -23,32 +24,36 @@ def register_widget(cls):
return cls
def get_widget_class_and_config(dashboard, id):
config = dict(dashboard.config[id]) # Copy to avoid mutating userconfig data
widget_class = registry['widgets'].get(config.pop('class'))
return widget_class, config
def get_widget_class(name):
"""
Return a registered DashboardWidget class identified by its name.
"""
try:
return registry['widgets'][name]
except KeyError:
raise ValueError(f"Unregistered widget class: {name}")
def get_dashboard(user):
"""
Return the dashboard layout for a given User.
Return the Dashboard for a given User if one exists, or generate a default dashboard.
"""
if not user.is_anonymous and hasattr(user, 'dashboard'):
dashboard = user.dashboard
if user.is_anonymous:
dashboard = get_default_dashboard()
else:
dashboard = get_default_dashboard_config()
try:
dashboard = user.dashboard
except ObjectDoesNotExist:
# Create a dashboard for this user
dashboard = get_default_dashboard()
dashboard.user = user
dashboard.save()
widgets = []
for grid_item in dashboard.layout:
widget_class, widget_config = get_widget_class_and_config(dashboard, grid_item['id'])
widget = widget_class(id=grid_item['id'], **widget_config)
widget.set_layout(grid_item)
widgets.append(widget)
return widgets
return dashboard
def get_default_dashboard_config():
def get_default_dashboard():
from extras.models import Dashboard
dashboard = Dashboard(
layout=[],
config={}

View File

@ -61,6 +61,14 @@ class DashboardWidget:
def name(self):
return f'{self.__class__.__module__.split(".")[0]}.{self.__class__.__name__}'
@property
def form_data(self):
return {
'title': self.title,
'color': self.color,
'config': self.config,
}
@register_widget
class NoteWidget(DashboardWidget):

View File

@ -1,6 +1,8 @@
from django.contrib.auth import get_user_model
from django.db import models
from extras.dashboard.utils import get_widget_class
__all__ = (
'Dashboard',
)
@ -18,7 +20,30 @@ class Dashboard(models.Model):
class Meta:
pass
def get_widget(self, id):
"""
Instantiate and return a widget by its ID
"""
id = str(id)
config = dict(self.config[id]) # Copy to avoid mutating instance data
widget_class = get_widget_class(config.pop('class'))
return widget_class(id=id, **config)
def get_layout(self):
"""
Return the dashboard's configured layout, suitable for rendering with gridstack.js.
"""
widgets = []
for grid_item in self.layout:
widget = self.get_widget(grid_item['id'])
widget.set_layout(grid_item)
widgets.append(widget)
return widgets
def add_widget(self, widget, x=None, y=None):
"""
Add a widget to the dashboard, optionally specifying its X & Y coordinates.
"""
id = str(widget.id)
self.config[id] = {
'class': widget.name,
@ -35,6 +60,10 @@ class Dashboard(models.Model):
})
def delete_widget(self, id):
"""
Delete a widget from the dashboard.
"""
id = str(id)
del self.config[id]
self.layout = [
item for item in self.layout if item['id'] != id

View File

@ -10,7 +10,6 @@ from django_rq.queues import get_connection
from rq import Worker
from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
from extras.dashboard.utils import get_widget_class_and_config
from netbox.registry import registry
from netbox.views import generic
from utilities.forms import ConfirmationForm, get_field_value
@ -725,10 +724,9 @@ class DashboardWidgetConfigView(LoginRequiredMixin, View):
template_name = 'extras/dashboard/widget_config.html'
def get(self, request, id):
id = str(id)
widget_class, config = get_widget_class_and_config(request.user.dashboard, id)
widget_form = DashboardWidgetForm(initial=config)
config_form = widget_class.ConfigForm(initial=config.get('config'), prefix='config')
widget = request.user.dashboard.get_widget(id)
widget_form = DashboardWidgetForm(initial=widget.form_data)
config_form = widget.ConfigForm(initial=widget.form_data.get('config'), prefix='config')
if not is_htmx(request):
return redirect('home')
@ -740,15 +738,16 @@ class DashboardWidgetConfigView(LoginRequiredMixin, View):
})
def post(self, request, id):
id = str(id)
widget_class, config = get_widget_class_and_config(request.user.dashboard, id)
widget = request.user.dashboard.get_widget(id)
widget_form = DashboardWidgetForm(request.POST)
config_form = widget_class.ConfigForm(request.POST, prefix='config')
config_form = widget.ConfigForm(request.POST, prefix='config')
if widget_form.is_valid() and config_form.is_valid():
data = widget_form.cleaned_data
data['config'] = config_form.cleaned_data
request.user.dashboard.config[id].update(data)
print(request.user.dashboard.config)
print(data)
request.user.dashboard.config[str(id)].update(data)
request.user.dashboard.save()
response = HttpResponse()
@ -766,15 +765,13 @@ class DashboardWidgetDeleteView(LoginRequiredMixin, View):
template_name = 'generic/object_delete.html'
def get(self, request, id):
id = str(id)
widget_class, config = get_widget_class_and_config(request.user.dashboard, id)
widget = widget_class(**config)
widget = request.user.dashboard.get_widget(id)
form = ConfirmationForm(initial=request.GET)
# If this is an HTMX request, return only the rendered deletion form as modal content
if is_htmx(request):
return render(request, 'htmx/delete_form.html', {
'object_type': widget_class.__name__,
'object_type': widget.__class__.__name__,
'object': widget,
'form': form,
'form_url': reverse('extras:dashboardwidget_delete', kwargs={'id': id})
@ -785,7 +782,6 @@ class DashboardWidgetDeleteView(LoginRequiredMixin, View):
})
def post(self, request, id):
id = str(id)
form = ConfirmationForm(request.POST)
if form.is_valid():

View File

@ -32,8 +32,8 @@ class HomeView(View):
if settings.LOGIN_REQUIRED and not request.user.is_authenticated:
return redirect('login')
# Build custom dashboard from user's config
widgets = get_dashboard(request.user)
# Construct the user's custom dashboard layout
dashboard = get_dashboard(request.user).get_layout()
# Check whether a new release is available. (Only for staff/superusers.)
new_release = None
@ -48,7 +48,7 @@ class HomeView(View):
}
return render(request, self.template_name, {
'widgets': widgets,
'dashboard': dashboard,
'new_release': new_release,
})

View File

@ -25,7 +25,7 @@
{% block content-wrapper %}
{# Render the user's customized dashboard #}
<div class="grid-stack">
{% for widget in widgets %}
{% for widget in dashboard %}
{% include 'extras/dashboard/widget.html' %}
{% endfor %}
</div>