diff --git a/cotisations/views.py b/cotisations/views.py
index 6982f590..1dfc6f7b 100644
--- a/cotisations/views.py
+++ b/cotisations/views.py
@@ -58,7 +58,7 @@ def create_cotis(vente, user, duration, date_start=False):
if date_start:
end_adhesion = Cotisation.objects.filter(vente__in=Vente.objects.filter(facture__in=Facture.objects.filter(user=user).exclude(valid=False))).filter(date_start__lt=date_start).aggregate(Max('date_end'))['date_end__max']
else:
- end_adhesion = user.end_adhesion()
+ end_adhesion = user.end_adhesion
date_start = date_start or timezone.now()
end_adhesion = end_adhesion or date_start
date_max = max(end_adhesion, date_start)
@@ -102,7 +102,7 @@ def new_facture(request, userid):
if art_item.cleaned_data['article'].iscotisation:
create_cotis(new_vente, user, art_item.cleaned_data['article'].duration*art_item.cleaned_data['quantity'])
if any(art_item.cleaned_data['article'].iscotisation for art_item in articles if art_item.cleaned_data):
- messages.success(request, "La cotisation a été prolongée pour l'adhérent %s jusqu'au %s" % (user.name, user.end_adhesion()) )
+ messages.success(request, "La cotisation a été prolongée pour l'adhérent %s jusqu'au %s" % (user.name, user.end_adhesion) )
else:
messages.success(request, "La facture a été crée")
return redirect("/users/profil/" + userid)
diff --git a/freeradius_utils/authenticate_filaire.py b/freeradius_utils/authenticate_filaire.py
index ad33b6f5..40c38ef2 100755
--- a/freeradius_utils/authenticate_filaire.py
+++ b/freeradius_utils/authenticate_filaire.py
@@ -73,7 +73,7 @@ def decide_vlan(switch_id, port_number, mac_address):
room_user = User.objects.filter(room=Room.objects.filter(name=port.room))
if not room_user:
return (sw_name, 'Chambre non cotisante', VLAN_NOK)
- elif not room_user[0].has_access():
+ elif not room_user[0].has_access:
return (sw_name, 'Chambre resident desactive', VLAN_NOK)
# else: user OK, on passe à la verif MAC
@@ -82,7 +82,7 @@ def decide_vlan(switch_id, port_number, mac_address):
interface = Interface.objects.filter(mac_address=mac_address)
if not interface:
return (sw_name, 'Machine inconnue', VLAN_NOK)
- elif not interface[0].is_active():
+ elif not interface[0].is_active:
return (sw_name, 'Machine non active / adherent non cotisant', VLAN_NOK)
else:
return (sw_name, 'Machine OK', VLAN_OK)
diff --git a/logs/templates/logs/aff_stats_general.html b/logs/templates/logs/aff_stats_general.html
new file mode 100644
index 00000000..49d067d0
--- /dev/null
+++ b/logs/templates/logs/aff_stats_general.html
@@ -0,0 +1,42 @@
+{% 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 %}
+
+ {% for stats in stats_list %}
+
+
+
+ {% for element in stats.0 %}
+ {{ element }} |
+ {% endfor %}
+
+
+ {% for key, stat in stats.1.items %}
+
+ {% for item in stat %}
+ {{ item }} |
+ {% endfor %}
+
+ {% endfor %}
+
+ {% endfor %}
diff --git a/logs/templates/logs/stats_general.html b/logs/templates/logs/stats_general.html
new file mode 100644
index 00000000..b8590df1
--- /dev/null
+++ b/logs/templates/logs/stats_general.html
@@ -0,0 +1,36 @@
+{% extends "logs/sidebar.html" %}
+{% 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 bootstrap3 %}
+
+{% block title %}Statistiques générales{% endblock %}
+
+{% block content %}
+ Statistiques générales
+ {% include "logs/aff_stats_general.html" with stats_list=stats_list %}
+
+
+
+ {% endblock %}
diff --git a/logs/views.py b/logs/views.py
index 61e6f6e3..26e9d5c5 100644
--- a/logs/views.py
+++ b/logs/views.py
@@ -40,6 +40,7 @@ from reversion.models import Revision
from reversion.models import Version
from users.models import User, ServiceUser, Right, School, ListRight, ListShell, Ban, Whitelist
+from users.models import all_has_access, all_whitelisted, all_baned, all_adherent
from cotisations.models import Facture, Vente, Article, Banque, Paiement, Cotisation
from machines.models import Machine, MachineType, IpType, Extension, Interface, Domain, IpList
from topologie.models import Switch, Port, Room
@@ -107,10 +108,10 @@ def stats_general(request):
'active_users' : ["Users actifs", User.objects.filter(state=User.STATE_ACTIVE).count()],
'inactive_users' : ["Users désactivés", User.objects.filter(state=User.STATE_DISABLED).count()],
'archive_users' : ["Users archivés", User.objects.filter(state=User.STATE_ARCHIVE).count()],
- 'adherent_users' : ["Adhérents à l'association", len([user for user in all_active_users if user.is_adherent()])],
- 'connexion_users' : ["Utilisateurs bénéficiant d'une connexion", len([user for user in all_active_users if user.has_access()])],
- 'ban_users' : ["Utilisateurs bannis", len([user for user in all_active_users if user.is_ban()])],
- 'whitelisted_user' : ["Utilisateurs bénéficiant d'une connexion gracieuse", len([user for user in all_active_users if user.is_whitelisted()])],
+ 'adherent_users' : ["Adhérents à l'association", all_adherent().count()],
+ 'connexion_users' : ["Utilisateurs bénéficiant d'une connexion", all_has_access().count()],
+ 'ban_users' : ["Utilisateurs bannis", all_baned().count()],
+ 'whitelisted_user' : ["Utilisateurs bénéficiant d'une connexion gracieuse", all_whitelisted().count()],
}],
[["Range d'ip", "Nombre d'ip totales", "Nombre d'ip utilisées", "Nombre d'ip libres"] ,ip]
]
diff --git a/machines/models.py b/machines/models.py
index b5775ca6..cf3baf88 100644
--- a/machines/models.py
+++ b/machines/models.py
@@ -27,6 +27,7 @@ from django.forms import ValidationError
from macaddress.fields import MACAddressField
from netaddr import mac_bare, EUI
from django.core.validators import MinValueValidator,MaxValueValidator
+from django.utils.functional import cached_property
from re2o.settings import MAIN_EXTENSION
@@ -101,11 +102,12 @@ class Interface(models.Model):
type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
details = models.CharField(max_length=255, blank=True)
+ @cached_property
def is_active(self):
""" Renvoie si une interface doit avoir accès ou non """
machine = self.machine
user = self.machine.user
- return machine.active and user.has_access()
+ return machine.active and user.has_access
def mac_bare(self):
return str(EUI(self.mac_address, dialect=mac_bare)).lower()
diff --git a/machines/views.py b/machines/views.py
index 62550acf..8957800b 100644
--- a/machines/views.py
+++ b/machines/views.py
@@ -728,7 +728,7 @@ class JSONResponse(HttpResponse):
def mac_ip_list(request):
interf = Interface.objects.select_related('ipv4').select_related('domain__extension').all()
interfaces = list(filter(
- lambda interface: interface.ipv4 and interface.is_active(),
+ lambda interface: interface.ipv4 and interface.is_active,
interf
))
seria = InterfaceSerializer(interfaces, many=True)
diff --git a/topologie/forms.py b/topologie/forms.py
index 277725bc..1ac21907 100644
--- a/topologie/forms.py
+++ b/topologie/forms.py
@@ -30,11 +30,11 @@ class PortForm(ModelForm):
class EditPortForm(ModelForm):
class Meta(PortForm.Meta):
- fields = ['room', 'machine_interface', 'related', 'radius', 'details']
+ fields = ['room', 'related', 'radius', 'details']
- def __init__(self, *args, **kwargs):
- super(EditPortForm, self).__init__(*args, **kwargs)
- self.fields['related'].queryset = Port.objects.all().order_by('switch', 'port')
+# def __init__(self, *args, **kwargs):
+# super(EditPortForm, self).__init__(*args, **kwargs)
+# self.fields['related'].queryset = Port.objects.all().order_by('switch', 'port')
class AddPortForm(ModelForm):
class Meta(PortForm.Meta):
diff --git a/topologie/views.py b/topologie/views.py
index b9b94919..b6c8611e 100644
--- a/topologie/views.py
+++ b/topologie/views.py
@@ -126,7 +126,7 @@ def new_port(request, switch_id):
@permission_required('infra')
def edit_port(request, port_id):
try:
- port_object = Port.objects.get(pk=port_id)
+ port_object = Port.objects.select_related('switch__switch_interface__domain__extension').select_related('machine_interface').select_related('room').select_related('related').get(pk=port_id)
except Port.DoesNotExist:
messages.error(request, u"Port inexistant")
return redirect("/topologie/")
diff --git a/users/models.py b/users/models.py
index 6b5f4082..ee4394df 100644
--- a/users/models.py
+++ b/users/models.py
@@ -26,6 +26,7 @@ from django.forms import ModelForm, Form
from django import forms
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
+from django.utils.functional import cached_property
import ldapdb.models
import ldapdb.models.fields
@@ -88,6 +89,17 @@ def get_admin_right():
admin_right.save()
return admin_right
+def all_adherent():
+ return User.objects.filter(facture__in=Facture.objects.filter(vente__in=Vente.objects.filter(cotisation__in=Cotisation.objects.filter(vente__in=Vente.objects.filter(facture__in=Facture.objects.all().exclude(valid=False))).filter(date_end__gt=timezone.now())))).distinct()
+
+def all_baned():
+ return User.objects.filter(ban__in=Ban.objects.filter(date_end__gt=timezone.now())).distinct()
+
+def all_whitelisted():
+ return User.objects.filter(whitelist__in=Whitelist.objects.filter(date_end__gt=timezone.now())).distinct()
+
+def all_has_access():
+ return User.objects.filter(Q(state=User.STATE_ACTIVE) & ~Q(ban__in=Ban.objects.filter(date_end__gt=timezone.now())) & (Q(whitelist__in=Whitelist.objects.filter(date_end__gt=timezone.now())) | Q(facture__in=Facture.objects.filter(vente__in=Vente.objects.filter(cotisation__in=Cotisation.objects.filter(vente__in=Vente.objects.filter(facture__in=Facture.objects.all().exclude(valid=False))).filter(date_end__gt=timezone.now())))))).distinct()
class UserManager(BaseUserManager):
def _create_user(self, pseudo, name, surname, email, password=None, su=False):
@@ -204,12 +216,14 @@ class User(AbstractBaseUser):
def has_perm(self, perm, obj=None):
return True
+ @cached_property
def end_adhesion(self):
date_max = Cotisation.objects.filter(vente__in=Vente.objects.filter(facture__in=Facture.objects.filter(user=self).exclude(valid=False))).aggregate(models.Max('date_end'))['date_end__max']
return date_max
+ @cached_property
def is_adherent(self):
- end = self.end_adhesion()
+ end = self.end_adhesion
if not end:
return False
elif end < timezone.now():
@@ -217,19 +231,22 @@ class User(AbstractBaseUser):
else:
return True
+ @cached_property
def end_ban(self):
""" Renvoie la date de fin de ban d'un user, False sinon """
date_max = Ban.objects.filter(user=self).aggregate(models.Max('date_end'))['date_end__max']
return date_max
+ @cached_property
def end_whitelist(self):
""" Renvoie la date de fin de whitelist d'un user, False sinon """
date_max = Whitelist.objects.filter(user=self).aggregate(models.Max('date_end'))['date_end__max']
return date_max
+ @cached_property
def is_ban(self):
""" Renvoie si un user est banni ou non """
- end = self.end_ban()
+ end = self.end_ban
if not end:
return False
elif end < timezone.now():
@@ -237,9 +254,10 @@ class User(AbstractBaseUser):
else:
return True
+ @cached_property
def is_whitelisted(self):
""" Renvoie si un user est whitelisté ou non """
- end = self.end_whitelist()
+ end = self.end_whitelist
if not end:
return False
elif end < timezone.now():
@@ -247,23 +265,25 @@ class User(AbstractBaseUser):
else:
return True
+ @cached_property
def has_access(self):
""" Renvoie si un utilisateur a accès à internet """
return self.state == User.STATE_ACTIVE \
- and not self.is_ban() and (self.is_adherent() or self.is_whitelisted())
+ and not self.is_ban and (self.is_adherent or self.is_whitelisted)
+ @cached_property
def end_access(self):
""" Renvoie la date de fin normale d'accès (adhésion ou whiteliste)"""
- if not self.end_adhesion():
- if not self.end_whitelist():
+ if not self.end_adhesion:
+ if not self.end_whitelist:
return None
else:
- return self.end_whitelist()
+ return self.end_whitelist
else:
- if not self.end_whitelist():
- return self.end_adhesion()
+ if not self.end_whitelist:
+ return self.end_adhesion
else:
- return max(self.end_adhesion(), self.end_whitelist())
+ return max(self.end_adhesion, self.end_whitelist)
def user_interfaces(self):
return Interface.objects.filter(machine__in=Machine.objects.filter(user=self, active=True))
@@ -293,7 +313,7 @@ class User(AbstractBaseUser):
if base:
user_ldap.name = self.pseudo
user_ldap.sn = self.pseudo
- user_ldap.dialupAccess = str(self.has_access())
+ user_ldap.dialupAccess = str(self.has_access)
user_ldap.home_directory = '/home/' + self.pseudo
user_ldap.mail = self.email
user_ldap.given_name = str(self.surname).lower() + '_' + str(self.name).lower()[:3]
@@ -303,7 +323,7 @@ class User(AbstractBaseUser):
if self.shell:
user_ldap.login_shell = self.shell.shell
if access_refresh:
- user_ldap.dialupAccess = str(self.has_access())
+ user_ldap.dialupAccess = str(self.has_access)
if mac_refresh:
user_ldap.macs = [inter.mac_bare() for inter in Interface.objects.filter(machine__in=Machine.objects.filter(user=self))]
user_ldap.save()
diff --git a/users/views.py b/users/views.py
index 505828ba..11edd4bc 100644
--- a/users/views.py
+++ b/users/views.py
@@ -272,7 +272,7 @@ def add_ban(request, userid):
reversion.set_comment("Création")
messages.success(request, "Bannissement ajouté")
return redirect("/users/profil/" + userid)
- if user.is_ban():
+ if user.is_ban:
messages.error(
request,
"Attention, cet utilisateur a deja un bannissement actif"
@@ -318,7 +318,7 @@ def add_whitelist(request, userid):
reversion.set_comment("Création")
messages.success(request, "Accès à titre gracieux accordé")
return redirect("/users/profil/" + userid)
- if user.is_whitelisted():
+ if user.is_whitelisted:
messages.error(
request,
"Attention, cet utilisateur a deja un accès gracieux actif"
@@ -463,7 +463,7 @@ def mass_archive(request):
to_archive_list = []
if to_archive_date.is_valid():
date = to_archive_date.cleaned_data['date']
- to_archive_list = [user for user in User.objects.exclude(state=User.STATE_ARCHIVE) if not user.end_access() or user.end_access() < date]
+ to_archive_list = [user for user in User.objects.exclude(state=User.STATE_ARCHIVE) if not user.end_access or user.end_access < date]
if "valider" in request.POST:
for user in to_archive_list:
archive(user)