Loading services/admin.py +101 −45 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ from django.db import models from django.db.models import Q from django.forms import ModelForm, BaseInlineFormSet from django.utils import timezone from django.urls import reverse from django.urls import reverse, path from django.utils.html import format_html from django.conf.urls import url from django.template.response import TemplateResponse Loading @@ -30,7 +30,7 @@ from .models import Service, ServiceType, IPPrefix, IPResource, IPResourceState, ServiceAllocation, Antenna, AntennaAllocation, Allocation, \ Route, Tunnel, Switch, Port from .utils.notifications import notify_allocation from .forms import AntennaForm from .forms import AntennaForm, StopAllocationForm ### Filters Loading Loading @@ -206,14 +206,17 @@ class AllocationInline(admin.TabularInline): return False class NewAllocationMixin: verbose_name_plural = 'Nouvelle allocation' max_num = 1 def get_queryset(self, request): return super().get_queryset(request).model.objects.none() class ActiveAllocationMixin: verbose_name_plural = 'Allocations actives' def get_max_num(self, request, obj=None, **kwargs): existing = obj.allocations.count() if obj else 0 # pour simplifier la validation, on ajoute qu’une allocation à la fois # il faudrait surcharger la méthode clean du formset pour supprimer cette limite return existing + 1 max_num = 0 def get_queryset(self, request): return super().get_queryset(request).filter(get_active_filter()) Loading @@ -239,46 +242,54 @@ class ServiceAllocationMixin: return qs class AntennaAllocationMixin: model = AntennaAllocation fields = ('id', 'antenna', 'resource', 'start', 'end') raw_id_fields = ('resource',) autocomplete_fields = ('antenna',) #class AntennaAllocationMixin: # model = AntennaAllocation # fields = ('id', 'antenna', 'resource', 'start', 'end') # raw_id_fields = ('resource',) # autocomplete_fields = ('antenna',) # # def get_queryset(self, request): # qs = super().get_queryset(request) # qs = qs.select_related('antenna') # return qs def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.select_related('antenna') return qs class NewServiceAllocationInline(ServiceAllocationMixin, NewAllocationMixin, AllocationInline): fields = ('id', 'service', 'resource', 'route',) class ActiveServiceAllocationInline(ServiceAllocationMixin, ActiveAllocationMixin, AllocationInline): pass fields = ('id', 'service', 'resource', 'route', 'start', 'stop',) readonly_fields = ('service', 'start', 'resource', 'stop',) def stop(self, obj): return format_html('<a href="{}" class="deletelink">Terminer</a>', reverse('admin:stop-allocation', kwargs={'resource': obj.resource.ip})) stop.short_description = 'Terminer l’allocation' class InactiveServiceAllocationInline(ServiceAllocationMixin, InactiveAllocationMixin, AllocationInline): pass fields = ('id', 'service', 'resource', 'route', 'start', 'end') readonly_fields = ('service', 'resource', 'route', 'start', 'end') class ActiveAntennaAllocationInline(AntennaAllocationMixin, ActiveAllocationMixin, AllocationInline): pass #class ActiveAntennaAllocationInline(AntennaAllocationMixin, ActiveAllocationMixin, AllocationInline): # pass class InactiveAntennaAllocationInline(AntennaAllocationMixin, InactiveAllocationMixin, AllocationInline): pass #class InactiveAntennaAllocationInline(AntennaAllocationMixin, InactiveAllocationMixin, AllocationInline): # pass class IPResourceStateInline(admin.TabularInline): model = IPResourceState verbose_name_plural = 'Historique des changements d’état' verbose_name_plural = 'Historique des derniers changements d’état' fields = ['date'] readonly_fields = ['date'] ordering = ['-date'] def has_add_permission(self, request): return False def has_change_permission(self, request, obj): return False def has_delete_permission(self, request, obj=None): return False Loading Loading @@ -310,15 +321,6 @@ class ServicePortInline(PortInline): readonly_fields = ('switch', 'port', 'up',) ### Actions def ends_resource(resource, request, queryset): now = timezone.now() queryset.exclude(start__lte=now, end__isnull=False).update(end=now) # TODO: send mail ends_resource.short_description = 'Terminer les allocations sélectionnées' ### ModelAdmin class ServiceAdmin(admin.ModelAdmin): Loading Loading @@ -353,7 +355,11 @@ class ServiceAdmin(admin.ModelAdmin): inlines = [] if obj and obj.ports.exists(): inlines += [ServicePortInline] inlines += [ActiveServiceAllocationInline, InactiveServiceAllocationInline] inlines += [NewServiceAllocationInline] if obj and obj.active_allocations.exists(): inlines += [ActiveServiceAllocationInline] if obj and obj.inactive_allocations.exists(): inlines += [InactiveServiceAllocationInline] return [inline(self.model, self.admin_site) for inline in inlines] def get_actions(self, request): Loading Loading @@ -429,13 +435,14 @@ class IPResourceAdmin(admin.ModelAdmin): def get_inline_instances(self, request, obj=None): super_inlines = super().get_inline_instances(request, obj) if obj: if obj.category == 0: inlines = (ActiveServiceAllocationInline, InactiveServiceAllocationInline,) elif obj.category == 1: inlines = (ActiveAntennaAllocationInline, InactiveAntennaAllocationInline,) inlines = [] if obj and obj.category == IPResource.CATEGORY_PUBLIC: if obj.allocations.filter(get_active_filter()).exists(): inlines += [ActiveServiceAllocationInline] else: inlines = () inlines += [NewServiceAllocationInline] if obj.allocations.exclude(get_active_filter()).exists(): inlines += [InactiveServiceAllocationInline] return [inline(self.model, self.admin_site) for inline in inlines] + super_inlines def get_queryset(self, request): Loading Loading @@ -495,6 +502,7 @@ class IPResourceAdmin(admin.ModelAdmin): else: return label ping.short_description = 'ping' #ping.admin_order_field = 'last_state__date' ping.admin_order_field = 'downtime' def route(self, obj): Loading @@ -518,6 +526,35 @@ class IPResourceAdmin(admin.ModelAdmin): return HttpResponseRedirect(reverse('admin:contact-adherents') + "?pk=%s" % pk) contact_ip_owners.short_description = 'Contacter les adhérents' def stop_allocation(self, request, resource): resource = self.get_object(request, resource) allocation = resource.allocations.filter(get_active_filter()).first() if not allocation: # L’IP n’est pas allouée return HttpResponseRedirect(reverse('admin:services_ipresource_change', args=[resource.pk])) form = StopAllocationForm(request.POST or None) if request.method == 'POST' and form.is_valid(): self.message_user(request, 'Allocation stoppée.') allocation.end = timezone.now() allocation.save() notify_allocation(request, allocation) # Il faudrait rajouter un redirect dans l’URL pour rediriger vers l’IP ou le Service return HttpResponseRedirect(reverse('admin:services_ipresource_change', args=[resource.pk])) context = self.admin_site.each_context(request) context.update({ 'opts': self.model._meta, 'title': 'Stopper une allocation', 'object': resource, 'media': self.media, 'form': form, }) return TemplateResponse(request, "admin/services/ipresource/stop_allocation.html", context) def get_urls(self): my_urls = [ path('<resource>/stop/', self.admin_site.admin_view(self.stop_allocation), name='stop-allocation'), ] return my_urls + super().get_urls() def get_actions(self, request): actions = super().get_actions(request) if 'delete_selected' in actions: Loading @@ -531,6 +568,25 @@ class IPResourceAdmin(admin.ModelAdmin): return False class IPResourceStateAdmin(admin.ModelAdmin): list_display = ('ip', 'date', 'state',) def get_actions(self, request): actions = super().get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] return actions def has_add_permission(self, request, obj=None): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False class RouteAdmin(admin.ModelAdmin): list_display = ('name',) search_fields = ('name',) Loading Loading @@ -613,7 +669,7 @@ class ActiveAntennaLayer(GeoJSONLayerView): class AntennaAdmin(admin.ModelAdmin): inlines = (ActiveAntennaAllocationInline, InactiveAntennaAllocationInline,) #inlines = (ActiveAntennaAllocationInline, InactiveAntennaAllocationInline,) list_filter = ( AntennaPrefixFilter, AntennaPositionFilter, Loading services/forms.py +4 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,10 @@ from django.contrib.gis.geos import Point from .models import Antenna class StopAllocationForm(forms.Form): pass class AntennaForm(forms.ModelForm): longitude = forms.FloatField( min_value=-180, Loading services/models.py +6 −2 Original line number Diff line number Diff line Loading @@ -109,9 +109,9 @@ class IPResource(models.Model): @property def allocations(self): if self.category == 0: if self.category == self.CATEGORY_PUBLIC: return self.service_allocations if self.category == 1: if self.category == self.CATEGORY_ANTENNA: return self.antenna_allocations @property Loading Loading @@ -198,6 +198,10 @@ class Service(models.Model): def active_allocations(self): return self.allocations.filter(get_active_filter()) @property def inactive_allocations(self): return self.allocations.exclude(get_active_filter()) def get_absolute_url(self): return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=(self.pk,)) Loading services/templates/admin/services/ipresource/stop_allocation.html 0 → 100644 +31 −0 Original line number Diff line number Diff line {% extends "admin/base_site.html" %} {% load i18n admin_urls static %} {% block extrahead %} {{ block.super }} {{ media }} <script type="text/javascript" src="{% static 'admin/js/cancel.js' %}"></script> {% endblock %} {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} {% block breadcrumbs %} <div class="breadcrumbs"> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> › <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a> › <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a> › <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a> › Stopper l’allocation </div> {% endblock %} {% block content %} <p>Voulez-vous vraiment stopper l’allocation de l’IP « {{ object }} » ?</p> <form method="post">{% csrf_token %} <div> <input type="hidden" name="post" value="yes"> <input type="submit" value="{% trans "Yes, I'm sure" %}"> <a href="#" class="button cancel-link">{% trans "No, take me back" %}</a> </div> </form> {% endblock %} services/utils/notifications.py +7 −3 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ from django.conf import settings from djadhere.utils import send_notification # À simplifier : seul la route peut changer maintenant def notify_allocation(request, new_alloc, old_alloc=None): fields = ['resource', 'service', 'route', 'start', 'end', 'notes'] Loading Loading @@ -32,12 +33,15 @@ def notify_allocation(request, new_alloc, old_alloc=None): message += '\n\nVoir : ' + url if old_alloc and diff: sujet = 'Modification d’une allocation' sujet = 'Modification d’allocation' elif not old_alloc: if new_alloc.end: sujet = 'Fin d’allocation' else: sujet = 'Nouvelle allocation' else: sujet = None if sujet: sujet += ' ADT%d' % new_alloc.service.adhesion.pk sujet += ' {} sur {}'.format(new_alloc.resource, new_alloc.route) send_notification(sujet, message, settings.ALLOCATIONS_EMAILS, cc=[benevole]) Loading
services/admin.py +101 −45 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ from django.db import models from django.db.models import Q from django.forms import ModelForm, BaseInlineFormSet from django.utils import timezone from django.urls import reverse from django.urls import reverse, path from django.utils.html import format_html from django.conf.urls import url from django.template.response import TemplateResponse Loading @@ -30,7 +30,7 @@ from .models import Service, ServiceType, IPPrefix, IPResource, IPResourceState, ServiceAllocation, Antenna, AntennaAllocation, Allocation, \ Route, Tunnel, Switch, Port from .utils.notifications import notify_allocation from .forms import AntennaForm from .forms import AntennaForm, StopAllocationForm ### Filters Loading Loading @@ -206,14 +206,17 @@ class AllocationInline(admin.TabularInline): return False class NewAllocationMixin: verbose_name_plural = 'Nouvelle allocation' max_num = 1 def get_queryset(self, request): return super().get_queryset(request).model.objects.none() class ActiveAllocationMixin: verbose_name_plural = 'Allocations actives' def get_max_num(self, request, obj=None, **kwargs): existing = obj.allocations.count() if obj else 0 # pour simplifier la validation, on ajoute qu’une allocation à la fois # il faudrait surcharger la méthode clean du formset pour supprimer cette limite return existing + 1 max_num = 0 def get_queryset(self, request): return super().get_queryset(request).filter(get_active_filter()) Loading @@ -239,46 +242,54 @@ class ServiceAllocationMixin: return qs class AntennaAllocationMixin: model = AntennaAllocation fields = ('id', 'antenna', 'resource', 'start', 'end') raw_id_fields = ('resource',) autocomplete_fields = ('antenna',) #class AntennaAllocationMixin: # model = AntennaAllocation # fields = ('id', 'antenna', 'resource', 'start', 'end') # raw_id_fields = ('resource',) # autocomplete_fields = ('antenna',) # # def get_queryset(self, request): # qs = super().get_queryset(request) # qs = qs.select_related('antenna') # return qs def get_queryset(self, request): qs = super().get_queryset(request) qs = qs.select_related('antenna') return qs class NewServiceAllocationInline(ServiceAllocationMixin, NewAllocationMixin, AllocationInline): fields = ('id', 'service', 'resource', 'route',) class ActiveServiceAllocationInline(ServiceAllocationMixin, ActiveAllocationMixin, AllocationInline): pass fields = ('id', 'service', 'resource', 'route', 'start', 'stop',) readonly_fields = ('service', 'start', 'resource', 'stop',) def stop(self, obj): return format_html('<a href="{}" class="deletelink">Terminer</a>', reverse('admin:stop-allocation', kwargs={'resource': obj.resource.ip})) stop.short_description = 'Terminer l’allocation' class InactiveServiceAllocationInline(ServiceAllocationMixin, InactiveAllocationMixin, AllocationInline): pass fields = ('id', 'service', 'resource', 'route', 'start', 'end') readonly_fields = ('service', 'resource', 'route', 'start', 'end') class ActiveAntennaAllocationInline(AntennaAllocationMixin, ActiveAllocationMixin, AllocationInline): pass #class ActiveAntennaAllocationInline(AntennaAllocationMixin, ActiveAllocationMixin, AllocationInline): # pass class InactiveAntennaAllocationInline(AntennaAllocationMixin, InactiveAllocationMixin, AllocationInline): pass #class InactiveAntennaAllocationInline(AntennaAllocationMixin, InactiveAllocationMixin, AllocationInline): # pass class IPResourceStateInline(admin.TabularInline): model = IPResourceState verbose_name_plural = 'Historique des changements d’état' verbose_name_plural = 'Historique des derniers changements d’état' fields = ['date'] readonly_fields = ['date'] ordering = ['-date'] def has_add_permission(self, request): return False def has_change_permission(self, request, obj): return False def has_delete_permission(self, request, obj=None): return False Loading Loading @@ -310,15 +321,6 @@ class ServicePortInline(PortInline): readonly_fields = ('switch', 'port', 'up',) ### Actions def ends_resource(resource, request, queryset): now = timezone.now() queryset.exclude(start__lte=now, end__isnull=False).update(end=now) # TODO: send mail ends_resource.short_description = 'Terminer les allocations sélectionnées' ### ModelAdmin class ServiceAdmin(admin.ModelAdmin): Loading Loading @@ -353,7 +355,11 @@ class ServiceAdmin(admin.ModelAdmin): inlines = [] if obj and obj.ports.exists(): inlines += [ServicePortInline] inlines += [ActiveServiceAllocationInline, InactiveServiceAllocationInline] inlines += [NewServiceAllocationInline] if obj and obj.active_allocations.exists(): inlines += [ActiveServiceAllocationInline] if obj and obj.inactive_allocations.exists(): inlines += [InactiveServiceAllocationInline] return [inline(self.model, self.admin_site) for inline in inlines] def get_actions(self, request): Loading Loading @@ -429,13 +435,14 @@ class IPResourceAdmin(admin.ModelAdmin): def get_inline_instances(self, request, obj=None): super_inlines = super().get_inline_instances(request, obj) if obj: if obj.category == 0: inlines = (ActiveServiceAllocationInline, InactiveServiceAllocationInline,) elif obj.category == 1: inlines = (ActiveAntennaAllocationInline, InactiveAntennaAllocationInline,) inlines = [] if obj and obj.category == IPResource.CATEGORY_PUBLIC: if obj.allocations.filter(get_active_filter()).exists(): inlines += [ActiveServiceAllocationInline] else: inlines = () inlines += [NewServiceAllocationInline] if obj.allocations.exclude(get_active_filter()).exists(): inlines += [InactiveServiceAllocationInline] return [inline(self.model, self.admin_site) for inline in inlines] + super_inlines def get_queryset(self, request): Loading Loading @@ -495,6 +502,7 @@ class IPResourceAdmin(admin.ModelAdmin): else: return label ping.short_description = 'ping' #ping.admin_order_field = 'last_state__date' ping.admin_order_field = 'downtime' def route(self, obj): Loading @@ -518,6 +526,35 @@ class IPResourceAdmin(admin.ModelAdmin): return HttpResponseRedirect(reverse('admin:contact-adherents') + "?pk=%s" % pk) contact_ip_owners.short_description = 'Contacter les adhérents' def stop_allocation(self, request, resource): resource = self.get_object(request, resource) allocation = resource.allocations.filter(get_active_filter()).first() if not allocation: # L’IP n’est pas allouée return HttpResponseRedirect(reverse('admin:services_ipresource_change', args=[resource.pk])) form = StopAllocationForm(request.POST or None) if request.method == 'POST' and form.is_valid(): self.message_user(request, 'Allocation stoppée.') allocation.end = timezone.now() allocation.save() notify_allocation(request, allocation) # Il faudrait rajouter un redirect dans l’URL pour rediriger vers l’IP ou le Service return HttpResponseRedirect(reverse('admin:services_ipresource_change', args=[resource.pk])) context = self.admin_site.each_context(request) context.update({ 'opts': self.model._meta, 'title': 'Stopper une allocation', 'object': resource, 'media': self.media, 'form': form, }) return TemplateResponse(request, "admin/services/ipresource/stop_allocation.html", context) def get_urls(self): my_urls = [ path('<resource>/stop/', self.admin_site.admin_view(self.stop_allocation), name='stop-allocation'), ] return my_urls + super().get_urls() def get_actions(self, request): actions = super().get_actions(request) if 'delete_selected' in actions: Loading @@ -531,6 +568,25 @@ class IPResourceAdmin(admin.ModelAdmin): return False class IPResourceStateAdmin(admin.ModelAdmin): list_display = ('ip', 'date', 'state',) def get_actions(self, request): actions = super().get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] return actions def has_add_permission(self, request, obj=None): return False def has_change_permission(self, request, obj=None): return False def has_delete_permission(self, request, obj=None): return False class RouteAdmin(admin.ModelAdmin): list_display = ('name',) search_fields = ('name',) Loading Loading @@ -613,7 +669,7 @@ class ActiveAntennaLayer(GeoJSONLayerView): class AntennaAdmin(admin.ModelAdmin): inlines = (ActiveAntennaAllocationInline, InactiveAntennaAllocationInline,) #inlines = (ActiveAntennaAllocationInline, InactiveAntennaAllocationInline,) list_filter = ( AntennaPrefixFilter, AntennaPositionFilter, Loading
services/forms.py +4 −0 Original line number Diff line number Diff line Loading @@ -4,6 +4,10 @@ from django.contrib.gis.geos import Point from .models import Antenna class StopAllocationForm(forms.Form): pass class AntennaForm(forms.ModelForm): longitude = forms.FloatField( min_value=-180, Loading
services/models.py +6 −2 Original line number Diff line number Diff line Loading @@ -109,9 +109,9 @@ class IPResource(models.Model): @property def allocations(self): if self.category == 0: if self.category == self.CATEGORY_PUBLIC: return self.service_allocations if self.category == 1: if self.category == self.CATEGORY_ANTENNA: return self.antenna_allocations @property Loading Loading @@ -198,6 +198,10 @@ class Service(models.Model): def active_allocations(self): return self.allocations.filter(get_active_filter()) @property def inactive_allocations(self): return self.allocations.exclude(get_active_filter()) def get_absolute_url(self): return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=(self.pk,)) Loading
services/templates/admin/services/ipresource/stop_allocation.html 0 → 100644 +31 −0 Original line number Diff line number Diff line {% extends "admin/base_site.html" %} {% load i18n admin_urls static %} {% block extrahead %} {{ block.super }} {{ media }} <script type="text/javascript" src="{% static 'admin/js/cancel.js' %}"></script> {% endblock %} {% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation{% endblock %} {% block breadcrumbs %} <div class="breadcrumbs"> <a href="{% url 'admin:index' %}">{% trans 'Home' %}</a> › <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a> › <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a> › <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a> › Stopper l’allocation </div> {% endblock %} {% block content %} <p>Voulez-vous vraiment stopper l’allocation de l’IP « {{ object }} » ?</p> <form method="post">{% csrf_token %} <div> <input type="hidden" name="post" value="yes"> <input type="submit" value="{% trans "Yes, I'm sure" %}"> <a href="#" class="button cancel-link">{% trans "No, take me back" %}</a> </div> </form> {% endblock %}
services/utils/notifications.py +7 −3 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ from django.conf import settings from djadhere.utils import send_notification # À simplifier : seul la route peut changer maintenant def notify_allocation(request, new_alloc, old_alloc=None): fields = ['resource', 'service', 'route', 'start', 'end', 'notes'] Loading Loading @@ -32,12 +33,15 @@ def notify_allocation(request, new_alloc, old_alloc=None): message += '\n\nVoir : ' + url if old_alloc and diff: sujet = 'Modification d’une allocation' sujet = 'Modification d’allocation' elif not old_alloc: if new_alloc.end: sujet = 'Fin d’allocation' else: sujet = 'Nouvelle allocation' else: sujet = None if sujet: sujet += ' ADT%d' % new_alloc.service.adhesion.pk sujet += ' {} sur {}'.format(new_alloc.resource, new_alloc.route) send_notification(sujet, message, settings.ALLOCATIONS_EMAILS, cc=[benevole])