Browse Source

allocation & libération des IP

Élie Bouttier 8 years ago
parent
commit
3c9868c545

+ 6 - 5
accounts/forms.py

@@ -1,6 +1,7 @@
 from django.forms import ModelForm
 from django.contrib.auth.models import User
 
+from djadhere.forms import ReadOnlyModelForm
 from .models import Profile
 
 
@@ -10,11 +11,11 @@ class UserCreateForm(ModelForm):
         fields = ('username', 'first_name', 'last_name', 'email',)
 
 
-class UserChangeForm(ModelForm):
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        for key in self.Meta.readonly_fields:
-            self.fields[key].disabled = True
+class UserChangeForm(ReadOnlyModelForm):
+    #def __init__(self, *args, **kwargs):
+    #    super().__init__(*args, **kwargs)
+    #    for key in self.Meta.readonly_fields:
+    #        self.fields[key].disabled = True
 
     class Meta:
         model = User

+ 19 - 12
accounts/templates/accounts/profile.html

@@ -5,16 +5,23 @@
 {% block profiletab %} class="active"{% endblock %}
 
 {% block content %}
-    <div class="row"><div class="col-md-offset-2 col-md-8">
-        {% bootstrap_form_errors user_form layout="horizontal" %}
-        {% bootstrap_form_errors profile_form layout="horizontal" %}
-        <form method="post" class="form-horizontal">
-            {% csrf_token %}
-            {% bootstrap_form user_form %}
-            {% bootstrap_form profile_form %}
-            {% buttons %}
-                <button type="submit" class="btn btn-success">Enregistrer</button>
-            {% endbuttons %}
-        </form>
-    </div></div>
+<div class="panel panel-primary">
+    <div class="panel-heading">
+        <h1>Profil</h1>
+    </div>
+    <div class="panel-body">
+        <div class="col-md-offset-2 col-md-8">
+            {% bootstrap_form_errors user_form layout="horizontal" %}
+            {% bootstrap_form_errors profile_form layout="horizontal" %}
+            <form method="post" class="form-horizontal">
+                {% csrf_token %}
+                {% bootstrap_form user_form %}
+                {% bootstrap_form profile_form %}
+                {% buttons %}
+                    <button type="submit" class="btn btn-success">Enregistrer</button>
+                {% endbuttons %}
+            </form>
+        </div>
+    </div>
+</div>
 {% endblock %}

+ 1 - 1
adhesions/templates/adhesions/_adhesion_detail.html

@@ -7,7 +7,7 @@
     <div class="panel-body">
         {% if adhesion %}
         {% if show_adherent %}
-        <p>Adhérent : <a href="{{ adhesion.get_adherent_detail_url }}">{{ adhesion }}</a></p>
+        <p>Adhérent : <a href="{{ adhesion.get_adherent_detail_url }}">{{ adhesion.get_adherent_name }}</a></p>
         {% endif %}
         <p>Numéro d’adhérent : ADT{{ adhesion.id }}</p>
         {% if adhesion.contribution %}

+ 8 - 0
djadhere/forms.py

@@ -0,0 +1,8 @@
+from django.forms import ModelForm
+
+
+class ReadOnlyModelForm(ModelForm):
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        for key in self.Meta.readonly_fields:
+            self.fields[key].disabled = True

+ 15 - 1
services/forms.py

@@ -1,8 +1,10 @@
 from django.forms import ModelForm
+from django.utils import timezone
 
 from django_select2.forms import Select2Widget
 
-from .models import Service
+from djadhere.forms import ReadOnlyModelForm
+from .models import Service, ResourceAllocation
 
 
 class ServiceForm(ModelForm):
@@ -12,3 +14,15 @@ class ServiceForm(ModelForm):
         widgets = {
             'adhesion': Select2Widget(),
         }
+
+
+class ResourceAllocationForm(ReadOnlyModelForm):
+    def __init__(self, *args, **kwargs):
+        initial = kwargs.get('initial', dict())
+        initial.update({'start': timezone.now()})
+        super().__init__(*args, **kwargs)
+
+    class Meta:
+        model = ResourceAllocation
+        fields = ('service', 'resource', 'start',)
+        readonly_fields = ('start',)

+ 13 - 10
services/models.py

@@ -19,18 +19,14 @@ class IPResource(models.Model):
 
     @property
     def in_use(self):
-        if self.allocations.filter(get_active_filter()).exists():
-            return True
-        else:
-            return False
+        return self.allocation is not None
 
     @property
-    def services(self):
-        allocations = self.allocations.filter(get_active_filter()).all()
-        if allocations:
-            return ', '.join(allocations)
-        else:
-            return '–'
+    def allocation(self):
+        try:
+            return self.allocations.get(get_active_filter())
+        except ResourceAllocation.DoesNotExist:
+            return None
 
     class Meta:
         verbose_name = 'ressource IP'
@@ -111,6 +107,10 @@ class ResourceAllocation(models.Model):
     start = models.DateTimeField(verbose_name='Début de la période d’allocation')
     end = models.DateTimeField(null=True, blank=True, verbose_name='Fin de la période d’allocation')
 
+    @property
+    def active(self):
+        return ResourceAllocation.objects.filter(pk=self.pk).filter(get_active_filter()).exists()
+
     def clean(self):
         super().clean()
         # Vérification de la cohérence des champs start et end
@@ -129,5 +129,8 @@ class ResourceAllocation(models.Model):
         verbose_name_plural = 'allocations'
         ordering = ['-start']
 
+    def get_absolute_url(self):
+        return reverse('ip-detail', kwargs={'pk': self.resource.pk})
+
     def __str__(self):
         return str(self.resource)

+ 10 - 1
services/templates/services/ipresource_detail.html

@@ -5,6 +5,7 @@
 {% block content %}
 <div class="panel panel-primary">
     <div class="panel-heading">
+        {% if not ipresource.in_use %}<a class="btn btn-success pull-right" href="{% url 'ip-allocate' ipresource.pk %}">Allouer</a>{% endif %}
         <h3>{{ ipresource }}</h3>
         <h4>Historique des allocations</h4>
     </div>
@@ -15,12 +16,20 @@
             <th>Service</th>
             <th>Début</th>
             <th>Fin</th>
+            <th></th>
         </tr>
     {% endif %}
         <tr>
-            <td>{{ allocation.service }}</td>
+            <td><a href="{% url 'service-detail' allocation.service.pk %}">{{ allocation.service }}</a></td>
             <td>{{ allocation.start }}</td>
             <td>{{ allocation.end|default:'–' }}</td>
+            <td class="text-right">
+                {% if allocation.active %}
+                <a href="{% url 'deallocate' allocation.pk %}" class="btn btn-xs btn-danger">
+                    <span class="glyphicon glyphicon-remove"></span>&nbsp;Désallouer
+                </a>
+                {% endif %}
+            </td>
         </tr>
     {% if forloop.last %}
     </table>

+ 16 - 2
services/templates/services/ipresource_list.html

@@ -13,12 +13,26 @@
     <table class="table">
         <tr>
             <th>Adresse</th>
-            <th>Affectations actives</th>
+            <th>Affectation</th>
+            <th></th>
         </tr>
     {% endif %}
         <tr>
+            {% with service=ip.allocation.service %}
             <td><a href="{% url 'ip-detail' ip.pk %}">{{ ip }}</a></td>
-            <td>{{ ip.services }}</td>
+            <td>{% if service %}<a href="{% url 'service-detail' service.pk %}">{{ service }}</a>{% else %}–{% endif %}</td>
+            <td class="text-right">
+                {% if service %}
+                <a href="{% url 'deallocate' ip.allocation.pk %}" class="btn btn-xs btn-danger">
+                    <span class="glyphicon glyphicon-remove"></span>&nbsp;Désallouer
+                </a>
+                {% else %}
+                <a href="{% url 'ip-allocate' ip.pk %}" class="btn btn-xs btn-success">
+                    <span class="glyphicon glyphicon-plus"></span>&nbsp;Allouer
+                </a>
+                {% endif %}
+            </td>
+            {% endwith %}
         </tr>
     {% if forloop.last %}
     </table>

+ 9 - 0
services/templates/services/resourceallocation_form.html

@@ -0,0 +1,9 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+<h1>{% if ipresource %}Modifier une allocation d’IP{% else %}Allouer une IP{% endif %}</h1>
+
+{% include '_form.html' %}
+
+{% endblock %}

+ 5 - 3
services/templates/services/service_detail.html

@@ -6,7 +6,7 @@
 {% block corptab %}{% if service.adhesion != request.user.profile.adhesion %} active{% endif %}{% endblock %}
 
 {% block content %}
-<div class="panel panel-primary">
+<div class="panel panel-{{ service.active|yesno:"success,danger"}}">
     <div class="panel-heading">
         {% if perms.services.change_service %}
         <a class="btn btn-success pull-right" href="{% url 'service-edit' service.pk %}"><span class="glyphicon glyphicon-edit"></span>&nbsp;Éditer</a>
@@ -21,22 +21,24 @@
         <p>Responsable :
             <a href="{% url 'adhesion-detail' service.adhesion.id %}">ADT{{ service.adhesion.id }}</a>
-            <a href="{{ service.adhesion.get_adherent_url }}">{{ service.adhesion.get_adherent_name }}</a>
+            <a href="{{ service.adhesion.get_adherent_detail_url }}">{{ service.adhesion.get_adherent_name }}</a>
         </p>
         {% if service.contribution %}
         <p>Contribution : {{ service.contribution }}</p>
         {% else %}
         <p>Contribution : pas de contribution</p>
         {% endif %}
+        <p>Actif : {{ service.active|yesno:"oui,non" }}</p>
         <p>
             Ressources IP :
             {% for allocation in service.active_allocations %}
             {% if forloop.first %}<ul>{% endif %}
-                <li>{{ allocation.resource }} (depuis le {{ allocation.start }})</li>
+                <li><a href="{% url 'ip-detail' allocation.resource.pk %}">{{ allocation.resource }}</a> (depuis le {{ allocation.start }})</li>
             {% if forloop.last %}</ul>{% endif %}
             {% empty %}
             aucune IP allouée
             {% endfor %}
+            <a href="{% url 'service-allocate' service.pk %}" class="btn btn-success">Allouer une IP</a>
         </p>
     </div>
 </div>

+ 2 - 2
services/templates/services/service_list.html

@@ -19,14 +19,14 @@
             <th></th>
         </tr>
     {% endif %}
-        <tr>
+        <tr class="{{ service.active|yesno:"success,danger" }}">
             <td><a href="{% url 'service-detail' service.pk %}">#{{ service.pk }}</a></td>
             <td>{{ service.service_type }}</td>
             <td>{{ service.label }}</td>
             <td>
                 <a href="{% url 'adhesion-detail' service.adhesion.id %}">ADT{{ service.adhesion.pk }}</a>
-                <a href="{{ service.adhesion.get_adherent_detail_url }}">{{ service.adhesion }}</a>
+                <a href="{{ service.adhesion.get_adherent_detail_url }}">{{ service.adhesion.get_adherent_name }}</a>
             </td>
             <td class="text-right">
                 <a href="{% url 'service-edit' service.pk %}"><span class="glyphicon glyphicon-pencil"></span>&nbsp;Modifier</a>

+ 1 - 1
services/templates/services/servicetype_detail.html

@@ -26,7 +26,7 @@
             <td>
                 <a href="{% url 'adhesion-detail' service.adhesion.id %}">ADT{{ service.adhesion.pk }}</a>
-                <a href="{{ service.adhesion.get_adherent_detail_url }}">{{ service.adhesion }}</a>
+                <a href="{{ service.adhesion.get_adherent_detail_url }}">{{ service.adhesion.get_adherent_name }}</a>
             </td>
             <td class="text-right">
                 <a href="{% url 'service-edit' service.pk %}"><span class="glyphicon glyphicon-pencil"></span>&nbsp;Modifier</a>

+ 3 - 1
services/urls.py

@@ -8,11 +8,13 @@ urlpatterns = [
     url(r'^services/(?P<pk>[0-9]+)/$', views.ServiceDetail.as_view(), name='service-detail'),
     url(r'^services/add/$', views.ServiceCreate.as_view(), name='service-add'),
     url(r'^services/(?P<pk>[0-9]+)/edit/$', views.ServiceUpdate.as_view(), name='service-edit'),
+    url(r'^services/(?P<pk>[0-9]+)/allocate/$', views.ServiceAllocate.as_view(), name='service-allocate'),
     url(r'^servicetypes/$', views.ServiceTypeList.as_view(), name='servicetype-list'),
     url(r'^servicetypes/add/$', views.ServiceTypeCreate.as_view(), name='servicetype-add'),
     url(r'^servicetypes/(?P<pk>[0-9]+)/$', views.ServiceTypeDetail.as_view(), name='servicetype-detail'),
     url(r'^servicetypes/(?P<pk>[0-9]+)/edit/$', views.ServiceTypeUpdate.as_view(), name='servicetype-edit'),
     url(r'^ip/$', views.IPResourceList.as_view(), name='ip-list'),
-    #url(r'^ip/add/$', views.IPResourceCreate.as_view(), name='ip-add'),
     url(r'^ip/(?P<pk>[0-9]+)/$', views.IPResourceDetail.as_view(), name='ip-detail'),
+    url(r'^ip/(?P<pk>[0-9]+)/allocate/$', views.IPResourceAllocate.as_view(), name='ip-allocate'),
+    url(r'^allocations/(?P<pk>[0-9]+)/deallocate/$', views.ResourceAllocationDeallocate.as_view(), name='deallocate'),
 ]

+ 60 - 5
services/views.py

@@ -1,10 +1,15 @@
-from django.shortcuts import render
-from django.views.generic import ListView, CreateView, DetailView, UpdateView
+from django.shortcuts import render, get_object_or_404
+from django.views.generic import ListView, CreateView, DetailView, UpdateView, RedirectView
+from django.views.generic.detail import SingleObjectMixin
 from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.auth.mixins import PermissionRequiredMixin
+from django.http import HttpResponseGone
+from django.core.exceptions import PermissionDenied
+from django.utils import timezone
 
-from .models import Service, ServiceType, IPResource
-from .forms import ServiceForm
+from djadhere.utils import get_active_filter
+from .models import Service, ServiceType, IPResource, ResourceAllocation
+from .forms import ServiceForm, ResourceAllocationForm
 
 
 class ServiceMixin(PermissionRequiredMixin):
@@ -17,7 +22,6 @@ class ServiceList(ServiceMixin, ListView):
 
 
 class ServiceCreate(ServiceMixin, CreateView):
-    #fields = ('adhesion', 'service_type', 'label', 'notes', 'active',)
     form_class = ServiceForm
 
 
@@ -34,6 +38,20 @@ class ServiceUpdate(ServiceMixin, UpdateView):
     fields = ('label', 'notes', 'active',)
 
 
+class ServiceAllocate(PermissionRequiredMixin, CreateView):
+    model = ResourceAllocation
+    permission_required = 'services.change_resourceallocation'
+
+    def get_form(self):
+        service = get_object_or_404(Service, pk=self.kwargs['pk'])
+        kwargs = super().get_form_kwargs()
+        kwargs['initial'].update({'service': service.pk})
+        form = ResourceAllocationForm(**kwargs)
+        form.fields['service'].disabled = True
+        form.fields['resource'].queryset = IPResource.objects.exclude(get_active_filter('allocation'))
+        return form
+
+
 class ServiceTypeMixin(PermissionRequiredMixin):
     model = ServiceType
     permission_required = 'services.change_servicetype'
@@ -66,3 +84,40 @@ class IPResourceList(IPResourceMixin, ListView):
 
 class IPResourceDetail(IPResourceMixin, DetailView):
     pass
+
+
+class IPResourceAllocate(PermissionRequiredMixin, CreateView):
+    model = ResourceAllocation
+    permission_required = 'services.change_resourceallocation'
+
+    def get(self, request, *args, **kwargs):
+        resource = get_object_or_404(IPResource, pk=self.kwargs['pk'])
+        if resource.in_use:
+            return HttpResponseGone("Cette IP est déjà alloué !")
+        return super().get(request, *args, **kwargs)
+
+    def get_form(self):
+        resource = get_object_or_404(IPResource, pk=self.kwargs['pk'])
+        kwargs = super().get_form_kwargs()
+        kwargs['initial'].update({'resource': resource.pk})
+        form = ResourceAllocationForm(**kwargs)
+        form.fields['resource'].disabled = True
+        return form
+
+
+class ResourceAllocationMixin(PermissionRequiredMixin):
+    model = ResourceAllocation
+    permission_required = 'services.change_resourceallocation'
+
+
+class ResourceAllocationDeallocate(ResourceAllocationMixin, SingleObjectMixin, RedirectView):
+    def get_object(self, queryset=None):
+        obj = super().get_object(queryset)
+        if not obj.active:
+            raise PermissionDenied
+        obj.end = timezone.now()
+        obj.save()
+        return obj
+
+    def get_redirect_url(self, *args, **kwargs):
+        return self.get_object().get_absolute_url()