8
0
Fork 0
mirror of https://gitlab.federez.net/re2o/re2o synced 2024-07-04 05:04:06 +00:00

Mise en cache des attributs user (@proprety) + reecriture de methodes directes SQl pour la collecte des set users (has_access, etc)

This commit is contained in:
Gabriel Detraz 2017-05-27 00:43:51 +02:00 committed by root
parent 70259cb0e5
commit 22dcad7599
11 changed files with 131 additions and 30 deletions

View file

@ -58,7 +58,7 @@ def create_cotis(vente, user, duration, date_start=False):
if date_start: 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'] 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: else:
end_adhesion = user.end_adhesion() end_adhesion = user.end_adhesion
date_start = date_start or timezone.now() date_start = date_start or timezone.now()
end_adhesion = end_adhesion or date_start end_adhesion = end_adhesion or date_start
date_max = max(end_adhesion, 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: if art_item.cleaned_data['article'].iscotisation:
create_cotis(new_vente, user, art_item.cleaned_data['article'].duration*art_item.cleaned_data['quantity']) 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): 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: else:
messages.success(request, "La facture a été crée") messages.success(request, "La facture a été crée")
return redirect("/users/profil/" + userid) return redirect("/users/profil/" + userid)

View file

@ -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)) room_user = User.objects.filter(room=Room.objects.filter(name=port.room))
if not room_user: if not room_user:
return (sw_name, 'Chambre non cotisante', VLAN_NOK) 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) return (sw_name, 'Chambre resident desactive', VLAN_NOK)
# else: user OK, on passe à la verif MAC # 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) interface = Interface.objects.filter(mac_address=mac_address)
if not interface: if not interface:
return (sw_name, 'Machine inconnue', VLAN_NOK) 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) return (sw_name, 'Machine non active / adherent non cotisant', VLAN_NOK)
else: else:
return (sw_name, 'Machine OK', VLAN_OK) return (sw_name, 'Machine OK', VLAN_OK)

View file

@ -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 %}
<table class="table table-striped">
<thead>
<tr>
{% for element in stats.0 %}
<th>{{ element }}</th>
{% endfor %}
</tr>
</thead>
{% for key, stat in stats.1.items %}
<tr>
{% for item in stat %}
<td>{{ item }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
{% endfor %}

View file

@ -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 %}
<h2>Statistiques générales</h2>
{% include "logs/aff_stats_general.html" with stats_list=stats_list %}
<br />
<br />
<br />
{% endblock %}

View file

@ -40,6 +40,7 @@ from reversion.models import Revision
from reversion.models import Version from reversion.models import Version
from users.models import User, ServiceUser, Right, School, ListRight, ListShell, Ban, Whitelist 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 cotisations.models import Facture, Vente, Article, Banque, Paiement, Cotisation
from machines.models import Machine, MachineType, IpType, Extension, Interface, Domain, IpList from machines.models import Machine, MachineType, IpType, Extension, Interface, Domain, IpList
from topologie.models import Switch, Port, Room 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()], '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()], '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()], '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()])], 'adherent_users' : ["Adhérents à l'association", all_adherent().count()],
'connexion_users' : ["Utilisateurs bénéficiant d'une connexion", len([user for user in all_active_users if user.has_access()])], 'connexion_users' : ["Utilisateurs bénéficiant d'une connexion", all_has_access().count()],
'ban_users' : ["Utilisateurs bannis", len([user for user in all_active_users if user.is_ban()])], 'ban_users' : ["Utilisateurs bannis", all_baned().count()],
'whitelisted_user' : ["Utilisateurs bénéficiant d'une connexion gracieuse", len([user for user in all_active_users if user.is_whitelisted()])], '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] [["Range d'ip", "Nombre d'ip totales", "Nombre d'ip utilisées", "Nombre d'ip libres"] ,ip]
] ]

View file

@ -27,6 +27,7 @@ from django.forms import ValidationError
from macaddress.fields import MACAddressField from macaddress.fields import MACAddressField
from netaddr import mac_bare, EUI from netaddr import mac_bare, EUI
from django.core.validators import MinValueValidator,MaxValueValidator from django.core.validators import MinValueValidator,MaxValueValidator
from django.utils.functional import cached_property
from re2o.settings import MAIN_EXTENSION from re2o.settings import MAIN_EXTENSION
@ -101,11 +102,12 @@ class Interface(models.Model):
type = models.ForeignKey('MachineType', on_delete=models.PROTECT) type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
details = models.CharField(max_length=255, blank=True) details = models.CharField(max_length=255, blank=True)
@cached_property
def is_active(self): def is_active(self):
""" Renvoie si une interface doit avoir accès ou non """ """ Renvoie si une interface doit avoir accès ou non """
machine = self.machine machine = self.machine
user = self.machine.user user = self.machine.user
return machine.active and user.has_access() return machine.active and user.has_access
def mac_bare(self): def mac_bare(self):
return str(EUI(self.mac_address, dialect=mac_bare)).lower() return str(EUI(self.mac_address, dialect=mac_bare)).lower()

View file

@ -728,7 +728,7 @@ class JSONResponse(HttpResponse):
def mac_ip_list(request): def mac_ip_list(request):
interf = Interface.objects.select_related('ipv4').select_related('domain__extension').all() interf = Interface.objects.select_related('ipv4').select_related('domain__extension').all()
interfaces = list(filter( interfaces = list(filter(
lambda interface: interface.ipv4 and interface.is_active(), lambda interface: interface.ipv4 and interface.is_active,
interf interf
)) ))
seria = InterfaceSerializer(interfaces, many=True) seria = InterfaceSerializer(interfaces, many=True)

View file

@ -30,11 +30,11 @@ class PortForm(ModelForm):
class EditPortForm(ModelForm): class EditPortForm(ModelForm):
class Meta(PortForm.Meta): class Meta(PortForm.Meta):
fields = ['room', 'machine_interface', 'related', 'radius', 'details'] fields = ['room', 'related', 'radius', 'details']
def __init__(self, *args, **kwargs): # def __init__(self, *args, **kwargs):
super(EditPortForm, self).__init__(*args, **kwargs) # super(EditPortForm, self).__init__(*args, **kwargs)
self.fields['related'].queryset = Port.objects.all().order_by('switch', 'port') # self.fields['related'].queryset = Port.objects.all().order_by('switch', 'port')
class AddPortForm(ModelForm): class AddPortForm(ModelForm):
class Meta(PortForm.Meta): class Meta(PortForm.Meta):

View file

@ -126,7 +126,7 @@ def new_port(request, switch_id):
@permission_required('infra') @permission_required('infra')
def edit_port(request, port_id): def edit_port(request, port_id):
try: 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: except Port.DoesNotExist:
messages.error(request, u"Port inexistant") messages.error(request, u"Port inexistant")
return redirect("/topologie/") return redirect("/topologie/")

View file

@ -26,6 +26,7 @@ from django.forms import ModelForm, Form
from django import forms from django import forms
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.functional import cached_property
import ldapdb.models import ldapdb.models
import ldapdb.models.fields import ldapdb.models.fields
@ -88,6 +89,17 @@ def get_admin_right():
admin_right.save() admin_right.save()
return admin_right 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): class UserManager(BaseUserManager):
def _create_user(self, pseudo, name, surname, email, password=None, su=False): 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): def has_perm(self, perm, obj=None):
return True return True
@cached_property
def end_adhesion(self): 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'] 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 return date_max
@cached_property
def is_adherent(self): def is_adherent(self):
end = self.end_adhesion() end = self.end_adhesion
if not end: if not end:
return False return False
elif end < timezone.now(): elif end < timezone.now():
@ -217,19 +231,22 @@ class User(AbstractBaseUser):
else: else:
return True return True
@cached_property
def end_ban(self): def end_ban(self):
""" Renvoie la date de fin de ban d'un user, False sinon """ """ 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'] date_max = Ban.objects.filter(user=self).aggregate(models.Max('date_end'))['date_end__max']
return date_max return date_max
@cached_property
def end_whitelist(self): def end_whitelist(self):
""" Renvoie la date de fin de whitelist d'un user, False sinon """ """ 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'] date_max = Whitelist.objects.filter(user=self).aggregate(models.Max('date_end'))['date_end__max']
return date_max return date_max
@cached_property
def is_ban(self): def is_ban(self):
""" Renvoie si un user est banni ou non """ """ Renvoie si un user est banni ou non """
end = self.end_ban() end = self.end_ban
if not end: if not end:
return False return False
elif end < timezone.now(): elif end < timezone.now():
@ -237,9 +254,10 @@ class User(AbstractBaseUser):
else: else:
return True return True
@cached_property
def is_whitelisted(self): def is_whitelisted(self):
""" Renvoie si un user est whitelisté ou non """ """ Renvoie si un user est whitelisté ou non """
end = self.end_whitelist() end = self.end_whitelist
if not end: if not end:
return False return False
elif end < timezone.now(): elif end < timezone.now():
@ -247,23 +265,25 @@ class User(AbstractBaseUser):
else: else:
return True return True
@cached_property
def has_access(self): def has_access(self):
""" Renvoie si un utilisateur a accès à internet """ """ Renvoie si un utilisateur a accès à internet """
return self.state == User.STATE_ACTIVE \ 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): def end_access(self):
""" Renvoie la date de fin normale d'accès (adhésion ou whiteliste)""" """ Renvoie la date de fin normale d'accès (adhésion ou whiteliste)"""
if not self.end_adhesion(): if not self.end_adhesion:
if not self.end_whitelist(): if not self.end_whitelist:
return None return None
else: else:
return self.end_whitelist() return self.end_whitelist
else: else:
if not self.end_whitelist(): if not self.end_whitelist:
return self.end_adhesion() return self.end_adhesion
else: else:
return max(self.end_adhesion(), self.end_whitelist()) return max(self.end_adhesion, self.end_whitelist)
def user_interfaces(self): def user_interfaces(self):
return Interface.objects.filter(machine__in=Machine.objects.filter(user=self, active=True)) return Interface.objects.filter(machine__in=Machine.objects.filter(user=self, active=True))
@ -293,7 +313,7 @@ class User(AbstractBaseUser):
if base: if base:
user_ldap.name = self.pseudo user_ldap.name = self.pseudo
user_ldap.sn = 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.home_directory = '/home/' + self.pseudo
user_ldap.mail = self.email user_ldap.mail = self.email
user_ldap.given_name = str(self.surname).lower() + '_' + str(self.name).lower()[:3] user_ldap.given_name = str(self.surname).lower() + '_' + str(self.name).lower()[:3]
@ -303,7 +323,7 @@ class User(AbstractBaseUser):
if self.shell: if self.shell:
user_ldap.login_shell = self.shell.shell user_ldap.login_shell = self.shell.shell
if access_refresh: if access_refresh:
user_ldap.dialupAccess = str(self.has_access()) user_ldap.dialupAccess = str(self.has_access)
if mac_refresh: if mac_refresh:
user_ldap.macs = [inter.mac_bare() for inter in Interface.objects.filter(machine__in=Machine.objects.filter(user=self))] user_ldap.macs = [inter.mac_bare() for inter in Interface.objects.filter(machine__in=Machine.objects.filter(user=self))]
user_ldap.save() user_ldap.save()

View file

@ -272,7 +272,7 @@ def add_ban(request, userid):
reversion.set_comment("Création") reversion.set_comment("Création")
messages.success(request, "Bannissement ajouté") messages.success(request, "Bannissement ajouté")
return redirect("/users/profil/" + userid) return redirect("/users/profil/" + userid)
if user.is_ban(): if user.is_ban:
messages.error( messages.error(
request, request,
"Attention, cet utilisateur a deja un bannissement actif" "Attention, cet utilisateur a deja un bannissement actif"
@ -318,7 +318,7 @@ def add_whitelist(request, userid):
reversion.set_comment("Création") reversion.set_comment("Création")
messages.success(request, "Accès à titre gracieux accordé") messages.success(request, "Accès à titre gracieux accordé")
return redirect("/users/profil/" + userid) return redirect("/users/profil/" + userid)
if user.is_whitelisted(): if user.is_whitelisted:
messages.error( messages.error(
request, request,
"Attention, cet utilisateur a deja un accès gracieux actif" "Attention, cet utilisateur a deja un accès gracieux actif"
@ -463,7 +463,7 @@ def mass_archive(request):
to_archive_list = [] to_archive_list = []
if to_archive_date.is_valid(): if to_archive_date.is_valid():
date = to_archive_date.cleaned_data['date'] 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: if "valider" in request.POST:
for user in to_archive_list: for user in to_archive_list:
archive(user) archive(user)