Statistiques sur les membres (préparation pour la déréservation des IPs). Début du système de déconnexion globale pour les AGs (ne fonctionne pas pour le moment). Amélioration du calcul de fin d'adhésion. Ce code a été insuffisamment testé pour le moment : NE PAS DÉPLOYER EN PRODUCTION.

This commit is contained in:
Yohan Bataille 2014-01-24 04:48:51 +01:00
parent 2661a73ae7
commit 93362f4f3c
6 changed files with 197 additions and 34 deletions

View File

@ -792,3 +792,71 @@ class RoomChangeMemberController(AuthenticatedRestController):
redirect("/edit/room/index/" + residence + "/" + room_uid)
#end def
#end def
# TODO :
""" Controller REST de gestion de la deconnexion globale. """
class AllMembersDisableController(AuthenticatedRestController):
require_group = groups_enum.admin
""" Gestion des requêtes post sur ce controller """
@expose()
def post(self, residence):
residence_dn = Residences.get_dn_by_name(self.user, residence)
# Récupération du membre et de la machine
# Note : on cherche la machine seulement sur le membre (member.dn)
members = Member.get_all(self.user, residence_dn)
for member in members:
member = Member.get_by_uid(self.user, residence_dn, member_uid)
if member is None:
raise Exception('membre inconnu')
#end if
dhcps = Machine.get_dhcps(self.user, member.dn)
machine_membre_tag = "machine_membre" # FIXME move to config
for dhcp_item in dhcps:
if dhcp_item.uid.first() == machine_membre_tag:
dhcp_item.uid.replace(machine_membre_tag, machine_membre_tag + "_disabled")
self.user.ldap_bind.save(dhcp_item)
#end if
#end for
# On redirige sur la page d'édition du membre
redirect("/edit/member/" + residence + "/" + member_uid)
#end def
# TODO :
""" Controller REST de gestion de la reconnexion globale. """
class AllMembersEnableController(AuthenticatedRestController):
require_group = groups_enum.admin
""" Gestion des requêtes post sur ce controller """
@expose()
def post(self, residence):
residence_dn = Residences.get_dn_by_name(self.user, residence)
# Récupération du membre et de la machine
# Note : on cherche la machine seulement sur le membre (member.dn)
member = Member.get_by_uid(self.user, residence_dn, member_uid)
if member is None:
raise Exception('membre inconnu')
#end if
dhcps = Machine.get_dhcps(self.user, member.dn)
machine_membre_tag = "machine_membre" # FIXME move to config
machine_membre_disabled = machine_membre_tag + "_disabled" # FIXME move to config
for dhcp_item in dhcps:
if dhcp_item.uid.first() == machine_membre_disabled:
dhcp_item.uid.replace(machine_membre_disabled, machine_membre_tag)
self.user.ldap_bind.save(dhcp_item)
#end if
#end for
# On redirige sur la page d'édition du membre
redirect("/edit/member/" + residence + "/" + member_uid)
#end def

View File

@ -49,7 +49,7 @@ class RoomsController(AuthenticatedBaseController):
if residence_dn is None:
raise Exception("unknown residence")
#end if
status = CotisationComputes.members_status_from_residence(self.user, residence_dn)
for area in Room.get_areas(self.user, residence_dn):
areas[area] = dict()
@ -78,6 +78,7 @@ class RoomsController(AuthenticatedBaseController):
"reverse_sorted_name" : self.reverse_sort_name,
"sorted_name" : self.sort_name,
"stats" : stats,
"status" : status,
"residence" : residence_name
}
#end def

View File

@ -22,24 +22,16 @@ def admin_user():
sched = Scheduler()
def disconnect_members_from_residence(admin_user, residence_dn):
current_year = CotisationComputes.current_year()
now = datetime.datetime.now()
members = Member.get_all(admin_user, residence_dn)
for member in members:
machines_tuples = Machine.get_machine_tuples_of_member(admin_user, member.dn)
if machines_tuples != []:
cotisations = Cotisation.cotisations_of_member(admin_user, member.dn, current_year)
months_list, anniversary = CotisationComputes.ldap_items_to_months_list(cotisations)
if not (now.month in months_list
or ((now.month - 1) in months_list and now.day <= (anniversary + 7))):
if not CotisationComputes.is_cotisation_paid(member, admin_user, residence_dn):
dhcps = Machine.get_dhcps(admin_user, member.dn)
machine_membre_tag = "machine_membre" # FIXME move to config
for dhcp_item in dhcps:

View File

@ -1,4 +1,5 @@
from brie.config import ldap_config
from brie.model.ldap import *
import datetime
class Residences:
@ -96,29 +97,116 @@ class CotisationComputes:
#end def
@staticmethod
def ldap_items_to_months_list(ldap_cotisations):
def anniversary_from_ldap_items(ldap_cotisations):
result = []
for cotisation in ldap_cotisations:
anniversary_data = cotisation.get("x-time").first()
anniversary_datetime = datetime.datetime.strptime(anniversary_data,
"%Y-%m-%d %H:%M:%S.%f")
months = []
for month in cotisation.get("x-validMonth").all():
result.append((anniversary_datetime, int(month)))
months.append(int(month))
#end for
result.append((anniversary_datetime, months))
#end for
first_anniversary_day = 0
# tri par ordre d'inscription et pas ordre de mois
anniversary = 0
# tri par ordre d'inscription
result = sorted(result)
if result != []:
# premier anniversaire
first_anniversary_day = result[0][0].day
anniversary_day = result[0][0].day
months = result[0][1]
SORT_ORDER = {9: 0, 10: 1, 11: 2, 12: 3, 1: 4, 2: 5, 3: 6, 4: 7, 5: 8, 6: 9, 7: 10, 8: 11}
months.sort(key=lambda val: SORT_ORDER[val])
anniversary_month = months[-1] + 1
if anniversary_month == 13:
anniversary_month = 1
if anniversary_month > 9:
anniversary_year = result[0][0].year
else :
anniversary_year = result[0][0].year + 1
anniversary = datetime.datetime.strptime(str(anniversary_year) + "-" + str(anniversary_month) + "-1 0:0", "%Y-%m-%d %H:%M") + datetime.timedelta(days=(anniversary_day - 1))
#end if
months_without_anniversary = [item[1] for item in result]
return months_without_anniversary, first_anniversary_day
return anniversary
#end def
@staticmethod
# old = SDF or no cotisation this year
def is_old_member(member, user_session, residence_dn):
current_year = CotisationComputes.current_year()
cotisations = Cotisation.cotisations_of_member(user_session, member.dn, current_year)
return Room.get_by_member_dn(user_session, residence_dn, member.dn) == None or cotisations == []
#end def
@staticmethod
# 7 days grace period
def is_cotisation_paid(member, user_session, residence_dn):
if CotisationComputes.is_old_member(member, user_session, residence_dn):
return False
current_year = CotisationComputes.current_year()
now = datetime.datetime.now()
cotisations = Cotisation.cotisations_of_member(user_session, member.dn, current_year)
anniversary = CotisationComputes.anniversary_from_ldap_items(cotisations)
delta = (now - anniversary)
return delta.days <= 7
#end def
@staticmethod
# less than a month late but more than a week
def is_cotisation_late(member, user_session, residence_dn):
if CotisationComputes.is_old_member(member, user_session, residence_dn):
return False
current_year = CotisationComputes.current_year()
now = datetime.datetime.now()
cotisations = Cotisation.cotisations_of_member(user_session, member.dn, current_year)
anniversary = CotisationComputes.anniversary_from_ldap_items(cotisations)
delta = (now - anniversary)
#print("[DEBUG] cotisation en retard pour l'utilisateur "+ member.dn +" now="+ str(now) +" anniversary="+ str(anniversary) +" delta="+ str(delta))
return delta.days <= 30 and delta.days > 7
#end def
@staticmethod
# more than a month late
def is_no_cotisation(member, user_session, residence_dn):
if CotisationComputes.is_old_member(member, user_session, residence_dn):
return False
current_year = CotisationComputes.current_year()
now = datetime.datetime.now()
cotisations = Cotisation.cotisations_of_member(user_session, member.dn, current_year)
anniversary = CotisationComputes.anniversary_from_ldap_items(cotisations)
delta = (now - anniversary)
return delta.days > 30
#end def
@staticmethod
def members_status_from_residence(user_session, residence_dn):
members = Member.get_all(user_session, residence_dn)
old_members = []
cotisation_paid_members = []
cotisation_late_members = []
no_cotisation_members = []
WTF_members = []
for member in members:
if CotisationComputes.is_old_member(member, user_session, residence_dn):
old_members.append(member)
elif CotisationComputes.is_cotisation_paid(member, user_session, residence_dn):
cotisation_paid_members.append(member)
elif CotisationComputes.is_cotisation_late(member, user_session, residence_dn):
cotisation_late_members.append(member)
#print("[DEBUG] cotisation en retard pour l'utilisateur "+ member.dn)
elif CotisationComputes.is_no_cotisation(member, user_session, residence_dn):
no_cotisation_members.append(member)
else:
WTF_members.append(member)
#end if
#end for
return dict(old_members=old_members, cotisation_paid_members=cotisation_paid_members, cotisation_late_members=cotisation_late_members, no_cotisation_members=no_cotisation_members, WTF_members=WTF_members)
#end def
#end class

View File

@ -6,7 +6,7 @@
<meta name="description" content=""/>
<meta name="author" content=""/>
<!-- Le styles -->
<!-- Le style -->
<xi:include href="common-css-header.html" />
</head>
<body>
@ -37,6 +37,13 @@
<input type="submit" value="Search" class="button"/>
</form>
</div>
<!-- Ne fonctionne pas encore : -->
<div class="section">
<form action="/disconnectall/" method="post" class="inline_block">
<input type="hidden" name="residence" value="${residence}"/>
<input type="submit" value="Déconnexion globale" class="button"/>
</form>
</div>
</div>
</body>
</html>

View File

@ -8,30 +8,37 @@
<xi:include href="common-css-header.html" />
</head>
<body>
<!--
<xi:include href="navbar.html" />
<div class="rooms_legend">
<py:def function="display_stat(name)">
<div class="room_number ${name}">${"%03d" % stats[name]}</div>
<div class="room_number ok_color">${"%03d" % stats[name]}</div>
</py:def>
<div>
${display_stat("ok_color")}
<span>ordinateurs connectés</span>
<div class="room_number ok_color">${"%03d" % len(status['cotisation_paid_members'])}</div>
<span>Cotisations à jour</span>
</div>
<div>
${display_stat("non_certif_color")}
<span>certifs manquants</span>
<div class="room_number non_certif_color">${"%03d" % len(status['cotisation_late_members'])}</div>
<span>Cotisations en retard</span>
</div>
<div>
${display_stat("non_paye_color")}
<span>fins de connexions passées</span>
<div class="room_number non_paye_color">${"%03d" % len(status['no_cotisation_members'])}</div>
<span>Sans cotisations</span>
</div>
<div>
${display_stat("vide_color")}
<span>chambres vides</span>
<div class="room_number vide_color">${"%03d" % len(status['old_members'])}</div>
<span>Anciens membres</span>
</div>
<div>
<div class="room_number vide_color">${"%03d" % len(status['WTF_members'])}</div>
<span>Membres WTF</span>
</div>
<div>
<!--${display_stat("vide_color")}-->
<span>Chambres vides</span>
</div>
</div>
-->
<xi:include href="navbar.html" />
<div class="area section">
<span class="section_name">PREVIEW</span>
<div py:if="defined('preview')">