8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-27 07:02:26 +00:00

Merge branch 'residence' into 'dev'

Residence

See merge request federez/re2o!412
This commit is contained in:
klafyvel 2019-02-20 23:31:18 +01:00
commit 0bc1734cf3
12 changed files with 336 additions and 13 deletions

View file

@ -194,12 +194,17 @@ class SortTable:
} }
TOPOLOGIE_INDEX_ROOM = { TOPOLOGIE_INDEX_ROOM = {
'room_name': ['name'], 'room_name': ['name'],
'default': ['name'] 'building_name': ['building__name'],
'default': ['building__name', 'name']
} }
TOPOLOGIE_INDEX_BUILDING = { TOPOLOGIE_INDEX_BUILDING = {
'building_name': ['name'], 'building_name': ['name'],
'default': ['name'] 'default': ['name']
} }
TOPOLOGIE_INDEX_DORMITORY = {
'dormitory_name': ['name'],
'default': ['name']
}
TOPOLOGIE_INDEX_BORNE = { TOPOLOGIE_INDEX_BORNE = {
'ap_name': ['interface__domain__name'], 'ap_name': ['interface__domain__name'],
'ap_ip': ['interface__ipv4__ipv4'], 'ap_ip': ['interface__ipv4__ipv4'],

View file

@ -39,6 +39,7 @@ from .models import (
AccessPoint, AccessPoint,
SwitchBay, SwitchBay,
Building, Building,
Dormitory,
PortProfile, PortProfile,
) )
@ -87,6 +88,12 @@ class BuildingAdmin(VersionAdmin):
"""Administration d'un batiment""" """Administration d'un batiment"""
pass pass
class DormitoryAdmin(VersionAdmin):
"""Administration d'une residence"""
pass
class PortProfileAdmin(VersionAdmin): class PortProfileAdmin(VersionAdmin):
"""Administration of a port profile""" """Administration of a port profile"""
pass pass
@ -99,5 +106,6 @@ admin.site.register(Stack, StackAdmin)
admin.site.register(ModelSwitch, ModelSwitchAdmin) admin.site.register(ModelSwitch, ModelSwitchAdmin)
admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin) admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin)
admin.site.register(Building, BuildingAdmin) admin.site.register(Building, BuildingAdmin)
admin.site.register(Dormitory, DormitoryAdmin)
admin.site.register(SwitchBay, SwitchBayAdmin) admin.site.register(SwitchBay, SwitchBayAdmin)
admin.site.register(PortProfile, PortProfileAdmin) admin.site.register(PortProfile, PortProfileAdmin)

View file

@ -54,6 +54,7 @@ from .models import (
AccessPoint, AccessPoint,
SwitchBay, SwitchBay,
Building, Building,
Dormitory,
PortProfile, PortProfile,
ModuleSwitch, ModuleSwitch,
ModuleOnSwitch, ModuleOnSwitch,
@ -259,6 +260,18 @@ class EditBuildingForm(FormRevMixin, ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditBuildingForm, self).__init__(*args, prefix=prefix, **kwargs)
class EditDormitoryForm(FormRevMixin, ModelForm):
"""Enable dormitory edition"""
class Meta:
model = Dormitory
fields = '__all__'
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(EditDormitoryForm, self).__init__(*args, prefix=prefix, **kwargs)
class EditPortProfileForm(FormRevMixin, ModelForm): class EditPortProfileForm(FormRevMixin, ModelForm):
"""Form to edit a port profile""" """Form to edit a port profile"""
class Meta: class Meta:

View file

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2019-02-18 16:43
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import re2o.mixins
class Migration(migrations.Migration):
dependencies = [
('topologie', '0069_auto_20190108_1439'),
]
def create_dormitory(apps, schema_editor):
db_alias = schema_editor.connection.alias
dormitory = apps.get_model("topologie", "Dormitory")
building = apps.get_model("topologie", "Building")
dorm = dormitory.objects.using(db_alias).create(name="Residence")
building.objects.using(db_alias).update(dormitory=dorm)
def delete_dormitory(apps, schema_editor):
pass
operations = [
migrations.CreateModel(
name='Dormitory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
],
options={
'verbose_name': 'dormitory',
'permissions': (('view_dormitory', 'Can view a dormitory object'),),
'verbose_name_plural': 'dormitories',
},
bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model),
),
migrations.AddField(
model_name='building',
name='dormitory',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Dormitory'),
preserve_default=False,
),
migrations.RunPython(create_dormitory, delete_dormitory)
]

View file

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2019-02-18 18:36
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('topologie', '0070_auto_20190218_1743'),
]
def transfer_room(apps, schema_editor):
db_alias = schema_editor.connection.alias
room_obj = apps.get_model("topologie", "Room")
building_obj = apps.get_model("topologie", "Building")
dorm_obj = apps.get_model("topologie", "Dormitory")
dorm = dorm_obj.objects.using(db_alias).first()
for room in room_obj.objects.using(db_alias).all():
building, created = building_obj.objects.using(db_alias).get_or_create(name=room.name[0].upper(), dormitory=dorm)
room.building = building
room.name = room.name[1:]
room.save()
def untransfer_room(apps, schema_editor):
pass
operations = [
migrations.AlterField(
model_name='room',
name='name',
field=models.CharField(max_length=255),
),
migrations.AddField(
model_name='room',
name='building',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Building'),
),
migrations.AlterUniqueTogether(
name='room',
unique_together=set([('name', 'building')]),
),
migrations.AlterField(
model_name='building',
name='dormitory',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='topologie.Dormitory'),
),
migrations.RunPython(transfer_room, untransfer_room),
migrations.AlterField(
model_name='room',
name='building',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='topologie.Building'),
)
]

View file

@ -516,11 +516,38 @@ class SwitchBay(AclMixin, RevMixin, models.Model):
return self.name return self.name
class Building(AclMixin, RevMixin, models.Model): class Dormitory(AclMixin, RevMixin, models.Model):
"""Un batiment""" """A student accomodation/dormitory
Une résidence universitaire"""
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
class Meta:
permissions = (
("view_dormitory", _("Can view a dormitory object")),
)
verbose_name = _("dormitory")
verbose_name_plural = _("dormitories")
def all_ap_in(self):
"""Returns all ap of the dorms"""
return AccessPoint.all_ap_in(self.building_set.all())
def __str__(self):
return self.name
class Building(AclMixin, RevMixin, models.Model):
"""A building of a dormitory
Un batiment"""
name = models.CharField(max_length=255)
dormitory = models.ForeignKey(
'Dormitory',
on_delete=models.PROTECT,
)
class Meta: class Meta:
permissions = ( permissions = (
("view_building", _("Can view a building object")), ("view_building", _("Can view a building object")),
@ -532,8 +559,15 @@ class Building(AclMixin, RevMixin, models.Model):
"""Returns all ap of the building""" """Returns all ap of the building"""
return AccessPoint.all_ap_in(self) return AccessPoint.all_ap_in(self)
@cached_property
def cached_name(self):
return self.__str__()
def __str__(self): def __str__(self):
return self.name if Dormitory.objects.count() > 1:
return self.dormitory.name + " : " + self.name
else:
return self.name
class Port(AclMixin, RevMixin, models.Model): class Port(AclMixin, RevMixin, models.Model):
@ -703,19 +737,24 @@ class Port(AclMixin, RevMixin, models.Model):
class Room(AclMixin, RevMixin, models.Model): class Room(AclMixin, RevMixin, models.Model):
"""Une chambre/local contenant une prise murale""" """Une chambre/local contenant une prise murale"""
name = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255)
details = models.CharField(max_length=255, blank=True) details = models.CharField(max_length=255, blank=True)
building = models.ForeignKey(
'Building',
on_delete=models.PROTECT,
)
class Meta: class Meta:
ordering = ['name'] ordering = ['building__name']
permissions = ( permissions = (
("view_room", _("Can view a room object")), ("view_room", _("Can view a room object")),
) )
verbose_name = _("room") verbose_name = _("room")
verbose_name_plural = _("rooms") verbose_name_plural = _("rooms")
unique_together = ('name', 'building')
def __str__(self): def __str__(self):
return self.name return self.building.cached_name + self.name
class PortProfile(AclMixin, RevMixin, models.Model): class PortProfile(AclMixin, RevMixin, models.Model):

View file

@ -35,13 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr> <tr>
{% trans "Building" as tr_building %} {% trans "Building" as tr_building %}
<th>{% include 'buttons/sort.html' with prefix='building' col='name' text=tr_building %}</th> <th>{% include 'buttons/sort.html' with prefix='building' col='name' text=tr_building %}</th>
<th>{% trans "Access points" %}</th> <th>{% trans "Access points" %}</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
{% for building in building_list %} {% for building in building_list %}
<tr> <tr>
<td>{{ building.name }}</td> <td>{{ building.cached_name }}</td>
<td>{% for ap in building.all_ap_in %} {{ ap.short_name }} {% endfor %}</td> <td>{% for ap in building.all_ap_in %} {{ ap.short_name }} {% endfor %}</td>
<td class="text-right"> <td class="text-right">
{% can_edit building %} {% can_edit building %}

View file

@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<thead> <thead>
<tr> <tr>
{% trans "Room" as tr_room %} {% trans "Room" as tr_room %}
{% trans "Building" as tr_building %}
<th>{% include 'buttons/sort.html' with prefix='building' col='name' text=tr_building %}</th>
<th>{% include 'buttons/sort.html' with prefix='room' col='name' text=tr_room %}</th> <th>{% include 'buttons/sort.html' with prefix='room' col='name' text=tr_room %}</th>
<th>{% trans "Details" %}</th> <th>{% trans "Details" %}</th>
<th></th> <th></th>
@ -41,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
</thead> </thead>
{% for room in room_list %} {% for room in room_list %}
<tr> <tr>
<td>{{ room.building }}</td>
<td>{{ room.name }}</td> <td>{{ room.name }}</td>
<td>{{ room.details }}</td> <td>{{ room.details }}</td>
<td class="text-right"> <td class="text-right">

View file

@ -0,0 +1,62 @@
{% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Goulven Kermarec
Copyright © 2017 Augustin Lemesle
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.
{% endcomment %}
{% load acl %}
{% load logs_extra %}
{% load i18n %}
{% if dormitory_list.paginator %}
{% include 'pagination.html' with list=dormitory_list %}
{% endif %}
<table class="table table-striped">
<thead>
<tr>
{% trans "Dormitory" as tr_dormitory %}
<th>{% include 'buttons/sort.html' with prefix='dormitory' col='name' text=tr_dormitory %}</th>
<th>{% trans "Building" %}</th>
<th></th>
</tr>
</thead>
{% for dormitory in dormitory_list %}
<tr>
<td>{{ dormitory.name }}</td>
<td>{% for building in dormitory.building_set.all %} {{ building.name }} {% endfor %}</td>
<td class="text-right">
{% can_edit dormitory %}
{% include 'buttons/edit.html' with href='topologie:edit-dormitory' id=dormitory.id %}
{% acl_end %}
{% history_button dormitory %}
{% can_delete dormitory %}
{% include 'buttons/suppr.html' with href='topologie:del-dormitory' id=dormitory.id %}
{% acl_end %}
</td>
</tr>
{% endfor %}
</table>
{% if dormitory_list.paginator %}
{% include 'pagination.html' with list=dormitory_list %}
{% endif %}

View file

@ -55,5 +55,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<hr> <hr>
{% acl_end %} {% acl_end %}
{% include 'topologie/aff_building.html' with building_list=building_list %} {% include 'topologie/aff_building.html' with building_list=building_list %}
<h2>{% trans "Dormitories" %}</h2>
{% can_create Dormitory %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'topologie:new-dormitory' %}">
<i class="fa fa-plus"></i>{% trans " Add a dormitory" %}
</a>
<hr>
{% acl_end %}
{% include 'topologie/aff_dormitory.html' with dormitory_list=dormitory_list %}
{% endblock %} {% endblock %}

View file

@ -108,6 +108,15 @@ urlpatterns = [
url(r'^del_building/(?P<buildingid>[0-9]+)$', url(r'^del_building/(?P<buildingid>[0-9]+)$',
views.del_building, views.del_building,
name='del-building'), name='del-building'),
url(r'^new_dormitory/$',
views.new_dormitory,
name='new-dormitory'),
url(r'^edit_dormitory/(?P<dormitoryid>[0-9]+)$',
views.edit_dormitory,
name='edit-dormitory'),
url(r'^del_dormitory/(?P<dormitoryid>[0-9]+)$',
views.del_dormitory,
name='del-dormitory'),
url(r'^index_port_profile/$', url(r'^index_port_profile/$',
views.index_port_profile, views.index_port_profile,
name='index-port-profile'), name='index-port-profile'),

View file

@ -84,6 +84,7 @@ from .models import (
AccessPoint, AccessPoint,
SwitchBay, SwitchBay,
Building, Building,
Dormitory,
Server, Server,
PortProfile, PortProfile,
ModuleSwitch, ModuleSwitch,
@ -103,6 +104,7 @@ from .forms import (
EditAccessPointForm, EditAccessPointForm,
EditSwitchBayForm, EditSwitchBayForm,
EditBuildingForm, EditBuildingForm,
EditDormitoryForm,
EditPortProfileForm, EditPortProfileForm,
EditModuleForm, EditModuleForm,
EditSwitchModuleForm, EditSwitchModuleForm,
@ -254,7 +256,7 @@ def index_ap(request):
@login_required @login_required
@can_view_all(Stack, Building, SwitchBay) @can_view_all(Stack, Building, Dormitory, SwitchBay)
def index_physical_grouping(request): def index_physical_grouping(request):
"""Affichage de la liste des stacks (affiche l'ensemble des switches)""" """Affichage de la liste des stacks (affiche l'ensemble des switches)"""
stack_list = (Stack.objects stack_list = (Stack.objects
@ -262,6 +264,7 @@ def index_physical_grouping(request):
'switch_set__interface_set__domain__extension' 'switch_set__interface_set__domain__extension'
)) ))
building_list = Building.objects.all() building_list = Building.objects.all()
dormitory_list = Dormitory.objects.all()
switch_bay_list = SwitchBay.objects.select_related('building') switch_bay_list = SwitchBay.objects.select_related('building')
stack_list = SortTable.sort( stack_list = SortTable.sort(
stack_list, stack_list,
@ -275,6 +278,12 @@ def index_physical_grouping(request):
request.GET.get('order'), request.GET.get('order'),
SortTable.TOPOLOGIE_INDEX_BUILDING SortTable.TOPOLOGIE_INDEX_BUILDING
) )
dormitory_list = SortTable.sort(
dormitory_list,
request.GET.get('col'),
request.GET.get('order'),
SortTable.TOPOLOGIE_INDEX_DORMITORY
)
switch_bay_list = SortTable.sort( switch_bay_list = SortTable.sort(
switch_bay_list, switch_bay_list,
request.GET.get('col'), request.GET.get('col'),
@ -288,6 +297,7 @@ def index_physical_grouping(request):
'stack_list': stack_list, 'stack_list': stack_list,
'switch_bay_list': switch_bay_list, 'switch_bay_list': switch_bay_list,
'building_list': building_list, 'building_list': building_list,
'dormitory_list': dormitory_list,
} }
) )
@ -904,7 +914,8 @@ def del_switch_bay(request, switch_bay, **_kwargs):
@login_required @login_required
@can_create(Building) @can_create(Building)
def new_building(request): def new_building(request):
"""Nouveau batiment""" """New Building of a dorm
Nouveau batiment"""
building = EditBuildingForm(request.POST or None) building = EditBuildingForm(request.POST or None)
if building.is_valid(): if building.is_valid():
building.save() building.save()
@ -920,7 +931,8 @@ def new_building(request):
@login_required @login_required
@can_edit(Building) @can_edit(Building)
def edit_building(request, building, **_kwargs): def edit_building(request, building, **_kwargs):
""" Edition d'un batiment""" """Edit a building
Edition d'un batiment"""
building = EditBuildingForm(request.POST or None, instance=building) building = EditBuildingForm(request.POST or None, instance=building)
if building.is_valid(): if building.is_valid():
if building.changed_data: if building.changed_data:
@ -937,7 +949,8 @@ def edit_building(request, building, **_kwargs):
@login_required @login_required
@can_delete(Building) @can_delete(Building)
def del_building(request, building, **_kwargs): def del_building(request, building, **_kwargs):
""" Suppression d'un batiment""" """Delete a building
Suppression d'un batiment"""
if request.method == "POST": if request.method == "POST":
try: try:
building.delete() building.delete()
@ -956,6 +969,64 @@ def del_building(request, building, **_kwargs):
) )
@login_required
@can_create(Dormitory)
def new_dormitory(request):
"""A new dormitory
Nouvelle residence"""
dormitory = EditDormitoryForm(request.POST or None)
if dormitory.is_valid():
dormitory.save()
messages.success(request, _("The dormitory was created."))
return redirect(reverse('topologie:index-physical-grouping'))
return form(
{'topoform': dormitory, 'action_name': _("Create")},
'topologie/topo.html',
request
)
@login_required
@can_edit(Dormitory)
def edit_dormitory(request, dormitory, **_kwargs):
"""Edit a dormitory
Edition d'une residence"""
dormitory = EditDormitoryForm(request.POST or None, instance=dormitory)
if dormitory.is_valid():
if dormitory.changed_data:
dormitory.save()
messages.success(request, _("The dormitory was edited."))
return redirect(reverse('topologie:index-physical-grouping'))
return form(
{'topoform': dormitory, 'action_name': _("Edit")},
'topologie/topo.html',
request
)
@login_required
@can_delete(Dormitory)
def del_dormitory(request, dormitory, **_kwargs):
"""Delete a dormitory
Suppression d'une residence"""
if request.method == "POST":
try:
dormitory.delete()
messages.success(request, _("The dormitory was deleted."))
except ProtectedError:
messages.error(
request,
(_("The dormitory %s is used by another object, impossible"
" to delete it.") % dormitory)
)
return redirect(reverse('topologie:index-physical-grouping'))
return form(
{'objet': dormitory, 'objet_name': _("Dormitory")},
'topologie/delete.html',
request
)
@login_required @login_required
@can_create(ConstructorSwitch) @can_create(ConstructorSwitch)
def new_constructor_switch(request): def new_constructor_switch(request):