Commit 13141284 authored by Élie Bouttier's avatar Élie Bouttier
Browse files

restriction allocations

parent cd80a830
Loading
Loading
Loading
Loading
+101 −45
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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())
@@ -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

@@ -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):
@@ -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):
@@ -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):
@@ -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):
@@ -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:
@@ -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',)
@@ -613,7 +669,7 @@ class ActiveAntennaLayer(GeoJSONLayerView):


class AntennaAdmin(admin.ModelAdmin):
    inlines = (ActiveAntennaAllocationInline, InactiveAntennaAllocationInline,)
    #inlines = (ActiveAntennaAllocationInline, InactiveAntennaAllocationInline,)
    list_filter = (
        AntennaPrefixFilter,
        AntennaPositionFilter,
+4 −0
Original line number Diff line number Diff line
@@ -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,
+6 −2
Original line number Diff line number Diff line
@@ -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
@@ -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,))

+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>
&rsaquo; <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
&rsaquo; <a href="{% url opts|admin_urlname:'change' object.pk|admin_urlquote %}">{{ object|truncatewords:"18" }}</a>
&rsaquo; 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 %}
+7 −3
Original line number Diff line number Diff line
@@ -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']

@@ -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])