Closes #4349: Drop support for embedded graphs

This commit is contained in:
Jeremy Stretch
2020-08-21 11:57:46 -04:00
parent 7a0c36ee53
commit dd06bbcaf6
36 changed files with 33 additions and 595 deletions

View File

@@ -2,7 +2,7 @@ from django import forms
from django.contrib import admin
from utilities.forms import LaxURLField
from .models import CustomField, CustomFieldChoice, CustomLink, Graph, ExportTemplate, JobResult, Webhook
from .models import CustomField, CustomFieldChoice, CustomLink, ExportTemplate, JobResult, Webhook
def order_content_types(field):
@@ -150,45 +150,6 @@ class CustomLinkAdmin(admin.ModelAdmin):
form = CustomLinkForm
#
# Graphs
#
class GraphForm(forms.ModelForm):
class Meta:
model = Graph
exclude = ()
help_texts = {
'template_language': "<a href=\"https://jinja.palletsprojects.com\">Jinja2</a> is strongly recommended for "
"new graphs."
}
widgets = {
'source': forms.Textarea,
'link': forms.Textarea,
}
@admin.register(Graph)
class GraphAdmin(admin.ModelAdmin):
fieldsets = (
('Graph', {
'fields': ('type', 'name', 'weight')
}),
('Templates', {
'fields': ('template_language', 'source', 'link'),
'classes': ('monospace',)
})
)
form = GraphForm
list_display = [
'name', 'type', 'weight', 'template_language', 'source',
]
list_filter = [
'type', 'template_language',
]
#
# Export templates
#

View File

@@ -7,7 +7,6 @@ from utilities.api import ChoiceField, WritableNestedSerializer
__all__ = [
'NestedConfigContextSerializer',
'NestedExportTemplateSerializer',
'NestedGraphSerializer',
'NestedImageAttachmentSerializer',
'NestedJobResultSerializer',
'NestedTagSerializer',
@@ -30,14 +29,6 @@ class NestedExportTemplateSerializer(WritableNestedSerializer):
fields = ['id', 'url', 'name']
class NestedGraphSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:graph-detail')
class Meta:
model = models.Graph
fields = ['id', 'url', 'name']
class NestedImageAttachmentSerializer(WritableNestedSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:imageattachment-detail')

View File

@@ -10,7 +10,7 @@ from dcim.api.nested_serializers import (
from dcim.models import Device, DeviceRole, Platform, Rack, Region, Site
from extras.choices import *
from extras.models import (
ConfigContext, ExportTemplate, Graph, ImageAttachment, ObjectChange, JobResult, Tag,
ConfigContext, ExportTemplate, ImageAttachment, ObjectChange, JobResult, Tag,
)
from extras.utils import FeatureQuery
from tenancy.api.nested_serializers import NestedTenantSerializer, NestedTenantGroupSerializer
@@ -25,43 +25,6 @@ from virtualization.models import Cluster, ClusterGroup
from .nested_serializers import *
#
# Graphs
#
class GraphSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='extras-api:graph-detail')
type = ContentTypeField(
queryset=ContentType.objects.filter(FeatureQuery('graphs').get_query()),
)
class Meta:
model = Graph
fields = ['id', 'url', 'type', 'weight', 'name', 'template_language', 'source', 'link']
class RenderedGraphSerializer(serializers.ModelSerializer):
embed_url = serializers.SerializerMethodField(
read_only=True
)
embed_link = serializers.SerializerMethodField(
read_only=True
)
type = ContentTypeField(
read_only=True
)
class Meta:
model = Graph
fields = ['id', 'type', 'weight', 'name', 'embed_url', 'embed_link']
def get_embed_url(self, obj):
return obj.embed_url(self.context['graphed_object'])
def get_embed_link(self, obj):
return obj.embed_link(self.context['graphed_object'])
#
# Export templates
#

View File

@@ -8,9 +8,6 @@ router.APIRootView = views.ExtrasRootView
# Custom field choices
router.register('_custom_field_choices', views.CustomFieldChoicesViewSet, basename='custom-field-choice')
# Graphs
router.register('graphs', views.GraphViewSet)
# Export templates
router.register('export-templates', views.ExportTemplateViewSet)

View File

@@ -15,7 +15,7 @@ from rq import Worker
from extras import filters
from extras.choices import JobResultStatusChoices
from extras.models import (
ConfigContext, CustomFieldChoice, ExportTemplate, Graph, ImageAttachment, ObjectChange, JobResult, Tag,
ConfigContext, CustomFieldChoice, ExportTemplate, ImageAttachment, ObjectChange, JobResult, Tag,
)
from extras.reports import get_report, get_reports, run_report
from extras.scripts import get_script, get_scripts, run_script
@@ -98,17 +98,6 @@ class CustomFieldModelViewSet(ModelViewSet):
return super().get_queryset().prefetch_related('custom_field_values__field')
#
# Graphs
#
class GraphViewSet(ModelViewSet):
metadata_class = ContentTypeMetadata
queryset = Graph.objects.all()
serializer_class = serializers.GraphSerializer
filterset_class = filters.GraphFilterSet
#
# Export templates
#

View File

@@ -79,21 +79,6 @@ class ObjectChangeActionChoices(ChoiceSet):
)
#
# ExportTemplates
#
class TemplateLanguageChoices(ChoiceSet):
LANGUAGE_JINJA2 = 'jinja2'
LANGUAGE_DJANGO = 'django'
CHOICES = (
(LANGUAGE_JINJA2, 'Jinja2'),
(LANGUAGE_DJANGO, 'Django (Legacy)'),
)
#
# Log Levels for Reports and Scripts
#

View File

@@ -6,7 +6,6 @@ EXTRAS_FEATURES = [
'custom_fields',
'custom_links',
'export_templates',
'graphs',
'job_results',
'webhooks'
]

View File

@@ -7,7 +7,7 @@ from tenancy.models import Tenant, TenantGroup
from utilities.filters import BaseFilterSet
from virtualization.models import Cluster, ClusterGroup
from .choices import *
from .models import ConfigContext, CustomField, Graph, ExportTemplate, ObjectChange, JobResult, Tag
from .models import ConfigContext, CustomField, ExportTemplate, ObjectChange, JobResult, Tag
__all__ = (
@@ -16,7 +16,6 @@ __all__ = (
'CustomFieldFilter',
'CustomFieldFilterSet',
'ExportTemplateFilterSet',
'GraphFilterSet',
'LocalConfigContextFilterSet',
'ObjectChangeFilterSet',
'TagFilterSet',
@@ -90,13 +89,6 @@ class CustomFieldFilterSet(django_filters.FilterSet):
self.filters['cf_{}'.format(cf.name)] = CustomFieldFilter(field_name=cf.name, custom_field=cf)
class GraphFilterSet(BaseFilterSet):
class Meta:
model = Graph
fields = ['id', 'type', 'name', 'template_language']
class ExportTemplateFilterSet(BaseFilterSet):
class Meta:

View File

@@ -0,0 +1,16 @@
# Generated by Django 3.1 on 2020-08-21 15:47
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('extras', '0048_exporttemplate_remove_template_language'),
]
operations = [
migrations.DeleteModel(
name='Graph',
),
]

View File

@@ -1,7 +1,7 @@
from .change_logging import ChangeLoggedModel, ObjectChange
from .customfields import CustomField, CustomFieldChoice, CustomFieldModel, CustomFieldValue
from .models import (
ConfigContext, ConfigContextModel, CustomLink, ExportTemplate, Graph, ImageAttachment, JobResult, Report, Script,
ConfigContext, ConfigContextModel, CustomLink, ExportTemplate, ImageAttachment, JobResult, Report, Script,
Webhook,
)
from .tags import Tag, TaggedItem
@@ -16,7 +16,6 @@ __all__ = (
'CustomFieldValue',
'CustomLink',
'ExportTemplate',
'Graph',
'ImageAttachment',
'JobResult',
'ObjectChange',

View File

@@ -203,69 +203,6 @@ class CustomLink(models.Model):
return self.name
#
# Graphs
#
class Graph(models.Model):
type = models.ForeignKey(
to=ContentType,
on_delete=models.CASCADE,
limit_choices_to=FeatureQuery('graphs')
)
weight = models.PositiveSmallIntegerField(
default=1000
)
name = models.CharField(
max_length=100,
verbose_name='Name'
)
template_language = models.CharField(
max_length=50,
choices=TemplateLanguageChoices,
default=TemplateLanguageChoices.LANGUAGE_JINJA2
)
source = models.CharField(
max_length=500,
verbose_name='Source URL'
)
link = models.URLField(
blank=True,
verbose_name='Link URL'
)
objects = RestrictedQuerySet.as_manager()
class Meta:
ordering = ('type', 'weight', 'name', 'pk') # (type, weight, name) may be non-unique
def __str__(self):
return self.name
def embed_url(self, obj):
context = {'obj': obj}
if self.template_language == TemplateLanguageChoices.LANGUAGE_DJANGO:
template = Template(self.source)
return template.render(Context(context))
elif self.template_language == TemplateLanguageChoices.LANGUAGE_JINJA2:
return render_jinja2(self.source, context)
def embed_link(self, obj):
if self.link is None:
return ''
context = {'obj': obj}
if self.template_language == TemplateLanguageChoices.LANGUAGE_DJANGO:
template = Template(self.link)
return template.render(Context(context))
elif self.template_language == TemplateLanguageChoices.LANGUAGE_JINJA2:
return render_jinja2(self.link, context)
#
# Export templates
#

View File

@@ -10,7 +10,7 @@ from rq import Worker
from dcim.models import Device, DeviceRole, DeviceType, Manufacturer, Rack, RackGroup, RackRole, Site
from extras.api.views import ReportViewSet, ScriptViewSet
from extras.models import ConfigContext, ExportTemplate, Graph, ImageAttachment, Tag
from extras.models import ConfigContext, ExportTemplate, ImageAttachment, Tag
from extras.reports import Report
from extras.scripts import BooleanVar, IntegerVar, Script, StringVar
from utilities.testing import APITestCase, APIViewTestCases
@@ -29,39 +29,6 @@ class AppTest(APITestCase):
self.assertEqual(response.status_code, 200)
class GraphTest(APIViewTestCases.APIViewTestCase):
model = Graph
brief_fields = ['id', 'name', 'url']
create_data = [
{
'type': 'dcim.site',
'name': 'Graph 4',
'source': 'http://example.com/graphs.py?site={{ obj.name }}&foo=4',
},
{
'type': 'dcim.site',
'name': 'Graph 5',
'source': 'http://example.com/graphs.py?site={{ obj.name }}&foo=5',
},
{
'type': 'dcim.site',
'name': 'Graph 6',
'source': 'http://example.com/graphs.py?site={{ obj.name }}&foo=6',
},
]
@classmethod
def setUpTestData(cls):
ct = ContentType.objects.get_for_model(Site)
graphs = (
Graph(type=ct, name='Graph 1', source='http://example.com/graphs.py?site={{ obj.name }}&foo=1'),
Graph(type=ct, name='Graph 2', source='http://example.com/graphs.py?site={{ obj.name }}&foo=2'),
Graph(type=ct, name='Graph 3', source='http://example.com/graphs.py?site={{ obj.name }}&foo=3'),
)
Graph.objects.bulk_create(graphs)
class ExportTemplateTest(APIViewTestCases.APIViewTestCase):
model = ExportTemplate
brief_fields = ['id', 'name', 'url']

View File

@@ -2,49 +2,12 @@ from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from dcim.models import DeviceRole, Platform, Region, Site
from extras.choices import *
from extras.filters import *
from extras.utils import FeatureQuery
from extras.models import ConfigContext, ExportTemplate, Graph, Tag
from extras.models import ConfigContext, ExportTemplate, Tag
from tenancy.models import Tenant, TenantGroup
from virtualization.models import Cluster, ClusterGroup, ClusterType
class GraphTestCase(TestCase):
queryset = Graph.objects.all()
filterset = GraphFilterSet
@classmethod
def setUpTestData(cls):
# Get the first three available types
content_types = ContentType.objects.filter(FeatureQuery('graphs').get_query())[:3]
graphs = (
Graph(name='Graph 1', type=content_types[0], template_language=TemplateLanguageChoices.LANGUAGE_DJANGO, source='http://example.com/1'),
Graph(name='Graph 2', type=content_types[1], template_language=TemplateLanguageChoices.LANGUAGE_JINJA2, source='http://example.com/2'),
Graph(name='Graph 3', type=content_types[2], template_language=TemplateLanguageChoices.LANGUAGE_JINJA2, source='http://example.com/3'),
)
Graph.objects.bulk_create(graphs)
def test_id(self):
params = {'id': self.queryset.values_list('pk', flat=True)[:2]}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_name(self):
params = {'name': ['Graph 1', 'Graph 2']}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
def test_type(self):
content_type = ContentType.objects.filter(FeatureQuery('graphs').get_query()).first()
params = {'type': content_type.pk}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 1)
def test_template_language(self):
params = {'template_language': TemplateLanguageChoices.LANGUAGE_JINJA2}
self.assertEqual(self.filterset(params, self.queryset).qs.count(), 2)
class ExportTemplateTestCase(TestCase):
queryset = ExportTemplate.objects.all()
filterset = ExportTemplateFilterSet

View File

@@ -1,49 +1,6 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from dcim.models import Site
from extras.choices import TemplateLanguageChoices
from extras.models import Graph, Tag
class GraphTest(TestCase):
def setUp(self):
self.site = Site(name='Site 1', slug='site-1')
def test_graph_render_django(self):
# Using the pluralize filter as a sanity check (it's only available in Django)
TEMPLATE_TEXT = "{{ obj.name|lower }} thing{{ 2|pluralize }}"
RENDERED_TEXT = "site 1 things"
graph = Graph(
type=ContentType.objects.get(app_label='dcim', model='site'),
name='Graph 1',
template_language=TemplateLanguageChoices.LANGUAGE_DJANGO,
source=TEMPLATE_TEXT,
link=TEMPLATE_TEXT
)
self.assertEqual(graph.embed_url(self.site), RENDERED_TEXT)
self.assertEqual(graph.embed_link(self.site), RENDERED_TEXT)
def test_graph_render_jinja2(self):
TEMPLATE_TEXT = "{{ [obj.name, obj.slug]|join(',') }}"
RENDERED_TEXT = "Site 1,site-1"
graph = Graph(
type=ContentType.objects.get(app_label='dcim', model='site'),
name='Graph 1',
template_language=TemplateLanguageChoices.LANGUAGE_JINJA2,
source=TEMPLATE_TEXT,
link=TEMPLATE_TEXT
)
self.assertEqual(graph.embed_url(self.site), RENDERED_TEXT)
self.assertEqual(graph.embed_link(self.site), RENDERED_TEXT)
from extras.models import Tag
class TagTest(TestCase):