1
0
mirror of https://github.com/peeringdb/peeringdb.git synced 2024-05-11 05:55:09 +00:00
Files
peeringdb-peeringdb/peeringdb_server/autocomplete_views.py

248 lines
7.4 KiB
Python
Raw Normal View History

2019-02-15 17:46:20 +00:00
import json
2018-11-08 19:45:21 +00:00
from django.db.models import Q
from django import http
from django.utils import html
2019-02-15 17:46:20 +00:00
from django.core.exceptions import ObjectDoesNotExist
from reversion.models import Version
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
from grappelli.views.related import AutocompleteLookup as GrappelliAutocomplete
2018-11-08 19:45:21 +00:00
from dal import autocomplete
2019-12-05 16:57:52 +00:00
from peeringdb_server.models import (
InternetExchange,
Facility,
NetworkFacility,
InternetExchangeFacility,
Organization,
IXLan,
CommandLineTool,
REFTAG_MAP,
)
2018-11-08 19:45:21 +00:00
from peeringdb_server.admin_commandline_tools import TOOL_MAP
June updates (#751) * Add pointer from API docs to tutorial #650 * Sorting by clicking table headers should use local-compare #356 * Mark IXP peering LAN as bogon #352 * Add help text to "Add (Facility, Network, Exchange)" tab #669 * Add Looking Glass field to the IX object #672 * Add read-only Superuser #679 * Make spelling of traffic levels consistent #519 (#723) * Offer 2FA (#290) * Show "Last Updated" fields on fac, ix, org records (#526) * Enable sort and reverse sort of IP column in IX display (#72) * IRR validation not handling unexpected characters gracefully (#712) * Support alternative direction of writing, e.g. Arabic (#618) * Undeleting an ixlan with an emtpy IPv4 XOR IPv6 field throws a silly error (#644) * Changing org while adding net results in 500 #654 * missing delete button for organisations (#121) * When changing owner of an ix admin GUI borks because of "Ixlan for exchange already exists" #666 * Selection should only present undeleted objects (#664) * change default encoding of API calls to 'utf-8' #663 * Posting https://www.peeringdb.com onto social media doesn't select a good preview image #537 * Revert "Add Looking Glass field to the IX object #672" This reverts commit 4daf2520043c241fabe9a521757efa86a274e28a. Conflicts: peeringdb_server/migrations/0037_ix_looking_glass.py peeringdb_server/views.py * 500 Internal Error when creating IX where prefix already exists elsewhere #718 * Fix graceful restore of soft-deleted objects with translation active (#580) * Don't return any POC data with status=deleted #569 Hard delete soft-deleted pocs after grace period #566 * django-peeringdb from github@2.0.0.2-beta Co-authored-by: Stefan Pratter <stefan@20c.com>
2020-06-24 12:55:01 -05:00
class GrappelliHandlerefAutocomplete(GrappelliAutocomplete):
"""
makes sure that the auto-complete fields managed
by grappelli in django admin exclude soft-deleted
objects
"""
def get_queryset(self):
qs = super().get_queryset()
if hasattr(self.model, "HandleRef"):
qs = qs.exclude(status="deleted")
return qs
2018-11-08 19:45:21 +00:00
class AutocompleteHTMLResponse(autocomplete.Select2QuerySetView):
def has_add_permissions(self, request):
return False
def render_to_response(self, context):
2019-12-05 16:57:52 +00:00
q = self.request.GET.get("q", None)
return http.HttpResponse(
"".join([i.get("text") for i in self.get_results(context)]),
content_type="text/html",
)
2018-11-08 19:45:21 +00:00
class ExchangeAutocompleteJSON(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = InternetExchange.objects.filter(status="ok")
if self.q:
qs = qs.filter(name__icontains=self.q)
2019-12-05 16:57:52 +00:00
qs = qs.order_by("name")
2018-11-08 19:45:21 +00:00
return qs
class ExchangeAutocomplete(AutocompleteHTMLResponse):
def get_queryset(self):
qs = InternetExchange.objects.filter(status="ok")
if self.q:
qs = qs.filter(name__icontains=self.q)
2019-12-05 16:57:52 +00:00
qs = qs.order_by("name")
2018-11-08 19:45:21 +00:00
return qs
def get_result_label(self, item):
return '<span data-value="%d"><div class="main">%s</div></span>' % (
2019-12-05 16:57:52 +00:00
item.pk,
html.escape(item.name),
)
2018-11-08 19:45:21 +00:00
class FacilityAutocompleteJSON(autocomplete.Select2QuerySetView):
def get_queryset(self):
qs = Facility.objects.filter(status="ok")
if self.q:
qs = qs.filter(name__icontains=self.q)
2019-12-05 16:57:52 +00:00
qs = qs.order_by("name")
2018-11-08 19:45:21 +00:00
return qs
class FacilityAutocomplete(AutocompleteHTMLResponse):
def get_queryset(self):
qs = Facility.objects.filter(status="ok")
if self.q:
2019-12-05 16:57:52 +00:00
qs = qs.filter(Q(name__icontains=self.q) | Q(address1__icontains=self.q))
qs = qs.order_by("name")
2018-11-08 19:45:21 +00:00
return qs
def get_result_label(self, item):
2019-12-05 16:57:52 +00:00
return (
'<span data-value="%d"><div class="main">%s</div> <div class="sub">%s</div></span>'
2019-12-05 16:57:52 +00:00
% (item.pk, html.escape(item.name), html.escape(item.address1))
)
2018-11-08 19:45:21 +00:00
class FacilityAutocompleteForNetwork(FacilityAutocomplete):
def get_queryset(self):
qs = super(FacilityAutocompleteForNetwork, self).get_queryset()
net_id = self.request.resolver_match.kwargs.get("net_id")
fac_ids = [
nf.facility_id
2019-12-05 16:57:52 +00:00
for nf in NetworkFacility.objects.filter(status="ok", network_id=net_id)
2018-11-08 19:45:21 +00:00
]
qs = qs.exclude(id__in=fac_ids)
return qs
class FacilityAutocompleteForExchange(FacilityAutocomplete):
def get_queryset(self):
qs = super(FacilityAutocompleteForExchange, self).get_queryset()
ix_id = self.request.resolver_match.kwargs.get("ix_id")
fac_ids = [
nf.facility_id
2019-12-05 16:57:52 +00:00
for nf in InternetExchangeFacility.objects.filter(status="ok", ix_id=ix_id)
2018-11-08 19:45:21 +00:00
]
qs = qs.exclude(id__in=fac_ids)
return qs
class OrganizationAutocomplete(AutocompleteHTMLResponse):
def get_queryset(self):
qs = Organization.objects.filter(status="ok")
if self.q:
qs = qs.filter(name__icontains=self.q)
2019-12-05 16:57:52 +00:00
qs = qs.order_by("name")
2018-11-08 19:45:21 +00:00
return qs
def get_result_label(self, item):
return '<span data-value="%d"><div class="main">%s</div></span>' % (
2019-12-05 16:57:52 +00:00
item.pk,
html.escape(item.name),
)
2018-11-08 19:45:21 +00:00
class IXLanAutocomplete(AutocompleteHTMLResponse):
def get_queryset(self):
qs = IXLan.objects.filter(status="ok").select_related("ix")
if self.q:
qs = qs.filter(
2019-12-05 16:57:52 +00:00
Q(ix__name__icontains=self.q) | Q(ix__name_long__icontains=self.q)
)
qs = qs.order_by("ix__name")
2018-11-08 19:45:21 +00:00
return qs
def get_result_label(self, item):
2019-12-05 16:57:52 +00:00
return (
'<span data-value="%d"><div class="main">%s <div class="tiny suffix">%s</div></div> <div class="sub">%s</div></span>'
2019-12-05 16:57:52 +00:00
% (
item.pk,
html.escape(item.ix.name),
html.escape(item.ix.country.code),
html.escape(item.ix.name_long),
)
)
2018-11-08 19:45:21 +00:00
2019-02-15 17:46:20 +00:00
class DeletedVersionAutocomplete(autocomplete.Select2QuerySetView):
"""
Autocomplete that will show reversion versions where an object
was set to deleted
"""
def get_queryset(self):
# Only staff needs to be able to see these
if not self.request.user.is_staff:
return []
# no query supplied, return empty result
if not self.q:
return []
try:
# query is expected to be of format "<reftag> <id>"
# return empty result on parsing failure
reftag, _id = tuple(self.q.split(" "))
except ValueError:
return []
try:
# make sure target object exists, return
# empty result if not
obj = REFTAG_MAP[reftag].objects.get(id=_id)
except (KeyError, ObjectDoesNotExist):
return []
2019-12-05 16:57:52 +00:00
versions = (
Version.objects.get_for_object(obj)
.order_by("revision_id")
.select_related("revision")
)
2019-02-15 17:46:20 +00:00
rv = []
previous = {}
# cycle through all versions of the object and collect the ones where
# status was changed from 'ok' to 'deleted'
#
# order them by most recent first
for version in versions:
data = json.loads(version.serialized_data)[0].get("fields")
if previous.get("status", "ok") == "ok" and data.get("status") == "deleted":
rv.insert(0, version)
previous = data
return rv
def get_result_label(self, item):
# label should be obj representation as well as date of deletion
# we split the date string to remove the ms and tz parts
return "{} - {}".format(item, str(item.revision.date_created).split(".")[0])
2018-11-08 19:45:21 +00:00
class CommandLineToolHistoryAutocomplete(autocomplete.Select2QuerySetView):
"""
Autocomplete for command line tools that were ran via the admin ui
"""
2019-12-05 16:57:52 +00:00
2018-11-08 19:45:21 +00:00
tool = ""
def get_queryset(self):
# Only staff needs to be able to see these
if not self.request.user.is_staff:
return []
2019-12-05 16:57:52 +00:00
qs = CommandLineTool.objects.filter(tool=self.tool).order_by("-created")
2018-11-08 19:45:21 +00:00
if self.q:
qs = qs.filter(description__icontains=self.q)
return qs
def get_result_label(self, item):
2019-12-05 16:57:52 +00:00
return item.description or self.tool
2018-11-08 19:45:21 +00:00
clt_history = {}
# class for each command line tool wrapper that we will map to an auto-complete
# url in urls.py
for tool_id, tool in list(TOOL_MAP.items()):
2018-11-08 19:45:21 +00:00
class ToolHistory(CommandLineToolHistoryAutocomplete):
tool = tool_id
ToolHistory.__name__ = "CLT_{}_Autocomplete".format(tool_id)
clt_history[tool_id] = ToolHistory