From ab7cb1de6c3e274d4924d6684b682ca3cb2246a2 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 13:20:46 +0100 Subject: [PATCH] Add views, forms and urls for autocomplete topologie --- topologie/forms.py | 46 ++++++++++- topologie/urls.py | 8 ++ topologie/views_autocomplete.py | 133 ++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 topologie/views_autocomplete.py diff --git a/topologie/forms.py b/topologie/forms.py index 4044ca3e..348e834e 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -37,7 +37,7 @@ from django.utils.translation import ugettext_lazy as _ from machines.models import Interface from machines.forms import EditMachineForm, NewMachineForm -from re2o.mixins import FormRevMixin +from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin from .models import ( Port, @@ -62,6 +62,23 @@ class PortForm(FormRevMixin, ModelForm): class Meta: model = Port fields = "__all__" + widgets = { + "switch": AutocompleteModelMixin( + url="/topologie/switch-autocomplete", + ), + "room": AutocompleteModelMixin( + url="/topologie/room-autocomplete", + ), + "machine_interface": AutocompleteModelMixin( + url="/machine/machine-autocomplete", + ), + "related": AutocompleteModelMixin( + url="/topologie/port-autocomplete", + ), + "custom_profile": AutocompleteModelMixin( + url="/topologie/portprofile-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -180,6 +197,11 @@ class EditRoomForm(FormRevMixin, ModelForm): class Meta: model = Room fields = "__all__" + widgets = { + "building": AutocompleteModelMixin( + url="/topologie/building-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -196,7 +218,11 @@ class CreatePortsForm(forms.Form): class EditModelSwitchForm(FormRevMixin, ModelForm): """Form used to edit switch models.""" - members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + required=False + ) class Meta: model = ModelSwitch @@ -230,11 +256,20 @@ class EditConstructorSwitchForm(FormRevMixin, ModelForm): class EditSwitchBayForm(FormRevMixin, ModelForm): """Form used to edit switch bays.""" - members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + required=False, + widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + ) class Meta: model = SwitchBay fields = "__all__" + widgets = { + "building": AutocompleteModelMixin( + url="/topologie/building-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -279,6 +314,11 @@ class EditPortProfileForm(FormRevMixin, ModelForm): class Meta: model = PortProfile fields = "__all__" + widgets = { + "vlan_tagged": AutocompleteMultipleModelMixin( + url="/machine/vlan-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) diff --git a/topologie/urls.py b/topologie/urls.py index 9a0ff1e3..c5ac95eb 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -28,6 +28,7 @@ from __future__ import unicode_literals from django.conf.urls import url from . import views +from . import views_autocomplete urlpatterns = [ url(r"^$", views.index, name="index"), @@ -169,4 +170,11 @@ urlpatterns = [ views.del_module_on, name="del-module-on", ), + ### Autocomplete Views + url(r'^room-autocomplete/$', views_autocomplete.RoomAutocomplete.as_view(), name='room-autocomplete',), + url(r'^building-autocomplete/$', views_autocomplete.BuildingAutocomplete.as_view(), name='building-autocomplete',), + url(r'^dormitory-autocomplete/$', views_autocomplete.DormitoryAutocomplete.as_view(), name='dormitory-autocomplete',), + url(r'^switch-autocomplete/$', views_autocomplete.SwitchAutocomplete.as_view(), name='switch-autocomplete',), + url(r'^port-autocomplete/$', views_autocomplete.PortAutocomplete.as_view(), name='profile-autocomplete',), + url(r'^portprofile-autocomplete/$', views_autocomplete.PortProfileAutocomplete.as_view(), name='portprofile-autocomplete',), ] diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py new file mode 100644 index 00000000..36d03c3e --- /dev/null +++ b/topologie/views_autocomplete.py @@ -0,0 +1,133 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Jean-Romain Garnier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +# App de gestion des users pour re2o +# Lara Kermarec, Gabriel Détraz, Lemesle Augustin +# Gplv2 +""" +Django views autocomplete view + +Here are defined the autocomplete class based view. + +""" +from __future__ import unicode_literals + +from django.db.models import Q, Value, CharField +from django.db.models.functions import Concat + +from .models import ( + Room, + Dormitory, + Building, + Switch, + PortProfile, + Port, +) + +from re2o.mixins import AutocompleteViewMixin + +from re2o.acl import ( + can_view_all, +) + + +#@can_view_all(School) +class RoomAutocomplete(AutocompleteViewMixin): + obj_type = Room + + # Override get_queryset to add annotations so search behaves more like users expect it to + def get_queryset(self): + # Suppose we have a dorm named Dorm, a building name B, and rooms from 001 - 999 + # Comments explain what we try to match + qs = self.obj_type.objects.annotate( + full_name=Concat("building__name", Value(" "), "name"), # Match when the user searches "B 001" + full_name_stuck=Concat("building__name", "name"), # Match "B001" + dorm_name=Concat("building__dormitory__name", Value(" "), "name"), # Match "Dorm 001" + dorm_full_name=Concat("building__dormitory__name", Value(" "), "building__name", Value(" "), "name"), # Match "Dorm B 001" + dorm_full_colon_name=Concat("building__dormitory__name", Value(" : "), "building__name", Value(" "), "name"), # Match "Dorm : B 001" (see Room's full_name property) + ).all() + + if self.q: + qs = qs.filter( + Q(full_name__icontains=self.q) + | Q(full_name_stuck__icontains=self.q) + | Q(dorm_name__icontains=self.q) + | Q(dorm_full_name__icontains=self.q) + | Q(dorm_full_colon_name__icontains=self.q) + ) + + return qs + + +#@can_view_all(Dormitory) +class DormitoryAutocomplete(AutocompleteViewMixin): + obj_type = Dormitory + + +#@can_view_all(Building) +class BuildingAutocomplete(AutocompleteViewMixin): + obj_type = Building + + def get_queryset(self): + # We want to be able to filter by dorm so it's easier + qs = self.obj_type.objects.annotate( + full_name=Concat("dormitory__name", Value(" "), "name"), + full_name_colon=Concat("dormitory__name", Value(" : "), "name"), + ).all() + + if self.q: + qs = qs.filter( + Q(full_name__icontains=self.q) + | Q(full_name_colon__icontains=self.q) + ) + + return qs + +class SwitchAutocomplete(AutocompleteViewMixin): + obj_type = Switch + + +class PortAutocomplete(AutocompleteViewMixin): + obj_type = Port + + def get_queryset(self): + # We want to enter the switch name, not just the port number + # Because we're concatenating a CharField and an Integer, we have to sepcify the output_field + qs = self.obj_type.objects.annotate( + full_name=Concat("switch__name", Value(" "), "port", output_field=CharField()), + full_name_stuck=Concat("switch__name", "port", output_field=CharField()), + full_name_dash=Concat("switch__name", Value(" - "), "port", output_field=CharField()), + ).all() + + if self.q: + qs = qs.filter( + Q(full_name__icontains=self.q) + | Q(full_name_stuck__icontains=self.q) + | Q(full_name_dash__icontains=self.q) + ) + + return qs + + + +class PortProfileAutocomplete(AutocompleteViewMixin): + obj_type = PortProfile