mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-12-22 15:03:45 +00:00
Merge branch 'residence' into 'dev'
Residence See merge request federez/re2o!412
This commit is contained in:
commit
0bc1734cf3
12 changed files with 336 additions and 13 deletions
|
@ -194,12 +194,17 @@ class SortTable:
|
|||
}
|
||||
TOPOLOGIE_INDEX_ROOM = {
|
||||
'room_name': ['name'],
|
||||
'default': ['name']
|
||||
'building_name': ['building__name'],
|
||||
'default': ['building__name', 'name']
|
||||
}
|
||||
TOPOLOGIE_INDEX_BUILDING = {
|
||||
'building_name': ['name'],
|
||||
'default': ['name']
|
||||
}
|
||||
TOPOLOGIE_INDEX_DORMITORY = {
|
||||
'dormitory_name': ['name'],
|
||||
'default': ['name']
|
||||
}
|
||||
TOPOLOGIE_INDEX_BORNE = {
|
||||
'ap_name': ['interface__domain__name'],
|
||||
'ap_ip': ['interface__ipv4__ipv4'],
|
||||
|
|
|
@ -39,6 +39,7 @@ from .models import (
|
|||
AccessPoint,
|
||||
SwitchBay,
|
||||
Building,
|
||||
Dormitory,
|
||||
PortProfile,
|
||||
)
|
||||
|
||||
|
@ -87,6 +88,12 @@ class BuildingAdmin(VersionAdmin):
|
|||
"""Administration d'un batiment"""
|
||||
pass
|
||||
|
||||
|
||||
class DormitoryAdmin(VersionAdmin):
|
||||
"""Administration d'une residence"""
|
||||
pass
|
||||
|
||||
|
||||
class PortProfileAdmin(VersionAdmin):
|
||||
"""Administration of a port profile"""
|
||||
pass
|
||||
|
@ -99,5 +106,6 @@ admin.site.register(Stack, StackAdmin)
|
|||
admin.site.register(ModelSwitch, ModelSwitchAdmin)
|
||||
admin.site.register(ConstructorSwitch, ConstructorSwitchAdmin)
|
||||
admin.site.register(Building, BuildingAdmin)
|
||||
admin.site.register(Dormitory, DormitoryAdmin)
|
||||
admin.site.register(SwitchBay, SwitchBayAdmin)
|
||||
admin.site.register(PortProfile, PortProfileAdmin)
|
||||
|
|
|
@ -54,6 +54,7 @@ from .models import (
|
|||
AccessPoint,
|
||||
SwitchBay,
|
||||
Building,
|
||||
Dormitory,
|
||||
PortProfile,
|
||||
ModuleSwitch,
|
||||
ModuleOnSwitch,
|
||||
|
@ -259,6 +260,18 @@ class EditBuildingForm(FormRevMixin, ModelForm):
|
|||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
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):
|
||||
"""Form to edit a port profile"""
|
||||
class Meta:
|
||||
|
|
47
topologie/migrations/0070_auto_20190218_1743.py
Normal file
47
topologie/migrations/0070_auto_20190218_1743.py
Normal 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)
|
||||
]
|
56
topologie/migrations/0071_auto_20190218_1936.py
Normal file
56
topologie/migrations/0071_auto_20190218_1936.py
Normal 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'),
|
||||
)
|
||||
]
|
|
@ -516,11 +516,38 @@ class SwitchBay(AclMixin, RevMixin, models.Model):
|
|||
return self.name
|
||||
|
||||
|
||||
class Building(AclMixin, RevMixin, models.Model):
|
||||
"""Un batiment"""
|
||||
class Dormitory(AclMixin, RevMixin, models.Model):
|
||||
"""A student accomodation/dormitory
|
||||
Une résidence universitaire"""
|
||||
|
||||
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:
|
||||
permissions = (
|
||||
("view_building", _("Can view a building object")),
|
||||
|
@ -532,8 +559,15 @@ class Building(AclMixin, RevMixin, models.Model):
|
|||
"""Returns all ap of the building"""
|
||||
return AccessPoint.all_ap_in(self)
|
||||
|
||||
@cached_property
|
||||
def cached_name(self):
|
||||
return self.__str__()
|
||||
|
||||
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):
|
||||
|
@ -703,19 +737,24 @@ class Port(AclMixin, RevMixin, models.Model):
|
|||
class Room(AclMixin, RevMixin, models.Model):
|
||||
"""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)
|
||||
building = models.ForeignKey(
|
||||
'Building',
|
||||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
ordering = ['building__name']
|
||||
permissions = (
|
||||
("view_room", _("Can view a room object")),
|
||||
)
|
||||
verbose_name = _("room")
|
||||
verbose_name_plural = _("rooms")
|
||||
unique_together = ('name', 'building')
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
return self.building.cached_name + self.name
|
||||
|
||||
|
||||
class PortProfile(AclMixin, RevMixin, models.Model):
|
||||
|
|
|
@ -35,13 +35,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<tr>
|
||||
{% trans "Building" as tr_building %}
|
||||
<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>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for building in building_list %}
|
||||
<tr>
|
||||
<td>{{ building.name }}</td>
|
||||
<td>{{ building.cached_name }}</td>
|
||||
<td>{% for ap in building.all_ap_in %} {{ ap.short_name }} {% endfor %}</td>
|
||||
<td class="text-right">
|
||||
{% can_edit building %}
|
||||
|
|
|
@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<thead>
|
||||
<tr>
|
||||
{% 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>{% trans "Details" %}</th>
|
||||
<th></th>
|
||||
|
@ -41,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
</thead>
|
||||
{% for room in room_list %}
|
||||
<tr>
|
||||
<td>{{ room.building }}</td>
|
||||
<td>{{ room.name }}</td>
|
||||
<td>{{ room.details }}</td>
|
||||
<td class="text-right">
|
||||
|
|
62
topologie/templates/topologie/aff_dormitory.html
Normal file
62
topologie/templates/topologie/aff_dormitory.html
Normal 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 %}
|
||||
|
|
@ -55,5 +55,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<hr>
|
||||
{% acl_end %}
|
||||
{% 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 %}
|
||||
|
||||
|
|
|
@ -108,6 +108,15 @@ urlpatterns = [
|
|||
url(r'^del_building/(?P<buildingid>[0-9]+)$',
|
||||
views.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/$',
|
||||
views.index_port_profile,
|
||||
name='index-port-profile'),
|
||||
|
|
|
@ -84,6 +84,7 @@ from .models import (
|
|||
AccessPoint,
|
||||
SwitchBay,
|
||||
Building,
|
||||
Dormitory,
|
||||
Server,
|
||||
PortProfile,
|
||||
ModuleSwitch,
|
||||
|
@ -103,6 +104,7 @@ from .forms import (
|
|||
EditAccessPointForm,
|
||||
EditSwitchBayForm,
|
||||
EditBuildingForm,
|
||||
EditDormitoryForm,
|
||||
EditPortProfileForm,
|
||||
EditModuleForm,
|
||||
EditSwitchModuleForm,
|
||||
|
@ -254,7 +256,7 @@ def index_ap(request):
|
|||
|
||||
|
||||
@login_required
|
||||
@can_view_all(Stack, Building, SwitchBay)
|
||||
@can_view_all(Stack, Building, Dormitory, SwitchBay)
|
||||
def index_physical_grouping(request):
|
||||
"""Affichage de la liste des stacks (affiche l'ensemble des switches)"""
|
||||
stack_list = (Stack.objects
|
||||
|
@ -262,6 +264,7 @@ def index_physical_grouping(request):
|
|||
'switch_set__interface_set__domain__extension'
|
||||
))
|
||||
building_list = Building.objects.all()
|
||||
dormitory_list = Dormitory.objects.all()
|
||||
switch_bay_list = SwitchBay.objects.select_related('building')
|
||||
stack_list = SortTable.sort(
|
||||
stack_list,
|
||||
|
@ -275,6 +278,12 @@ def index_physical_grouping(request):
|
|||
request.GET.get('order'),
|
||||
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,
|
||||
request.GET.get('col'),
|
||||
|
@ -288,6 +297,7 @@ def index_physical_grouping(request):
|
|||
'stack_list': stack_list,
|
||||
'switch_bay_list': switch_bay_list,
|
||||
'building_list': building_list,
|
||||
'dormitory_list': dormitory_list,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -904,7 +914,8 @@ def del_switch_bay(request, switch_bay, **_kwargs):
|
|||
@login_required
|
||||
@can_create(Building)
|
||||
def new_building(request):
|
||||
"""Nouveau batiment"""
|
||||
"""New Building of a dorm
|
||||
Nouveau batiment"""
|
||||
building = EditBuildingForm(request.POST or None)
|
||||
if building.is_valid():
|
||||
building.save()
|
||||
|
@ -920,7 +931,8 @@ def new_building(request):
|
|||
@login_required
|
||||
@can_edit(Building)
|
||||
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)
|
||||
if building.is_valid():
|
||||
if building.changed_data:
|
||||
|
@ -937,7 +949,8 @@ def edit_building(request, building, **_kwargs):
|
|||
@login_required
|
||||
@can_delete(Building)
|
||||
def del_building(request, building, **_kwargs):
|
||||
""" Suppression d'un batiment"""
|
||||
"""Delete a building
|
||||
Suppression d'un batiment"""
|
||||
if request.method == "POST":
|
||||
try:
|
||||
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
|
||||
@can_create(ConstructorSwitch)
|
||||
def new_constructor_switch(request):
|
||||
|
|
Loading…
Reference in a new issue