Passage aux adhésions glissantes (partie I, sans lc_ldap)

This commit is contained in:
Pierre-Elliott Bécue 2014-08-15 20:26:12 +02:00
parent 169b7000b8
commit 49cef4c095
12 changed files with 485 additions and 231 deletions

View file

@ -40,7 +40,6 @@ import time
db = crans_ldap()
date_debut_ann_scol = time.mktime((ann_scol, 8, 1, 0, 0, 0, 0, 0, 0))
paiement_ann_scol = "paiement+%d" % ann_scol
def datestrtoint(strdate):
u""" Convertit une date en entier. """

View file

@ -18,7 +18,7 @@ def _need_conn(f):
if __name__.endswith('annuaires_pg_test'):
host='localhost'
else:
host='pgsql.adm.crans.org'
host='pgsql.v4.adm.crans.org'
# "connecting …"
try:
if not conn:

View file

@ -13,6 +13,7 @@ import datetime
import time
import re
import lc_ldap.shortcuts
from lc_ldap.crans_utils import fromGeneralizedTimeFormat, toGeneralizedTimeFormat
conn = lc_ldap.shortcuts.lc_ldap_admin()
import mail as mail_module
@ -38,12 +39,11 @@ year = config.ann_scol
delai = config.demenagement_delai
# On récupère ceux qui n'ont pas payé cette année
if config.periode_transitoire:
bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(paiement=%d)(!(paiement=%d)))' % (year-1,year))
else:
bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(paiement=%d))' % year)
now = time.time()
if config.periode_transitoire:
bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(|(&(paiement=%d)(!(paiement=%d)))(finAdhesion<=%s)(finConnexion<=%s)))' % (year-1, year, toGeneralizedTimeFormat(now), toGeneralizedTimeFormat(now)))
else:
bad_boys_e_s = conn.search(u'(&(aid=*)(chbre=????)(|(paiement=%d)(finAdhesion>=%s)(finConnexion>=%s)))' % (year, toGeneralizedTimeFormat(now), toGeneralizedTimeFormat(now)))
to_print = []
to_error = []

View file

@ -30,7 +30,8 @@ else:
ann_scol = dat[0]
periode_transitoire = False
duree_periode_transitoire = 45*86400
debut_periode_transitoire = time.mktime(time.strptime("%s/08/16 00:00:00" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
fin_periode_transitoire = time.mktime(time.strptime("%s/09/30 23:59:59" % (ann_scol,), "%Y/%m/%d %H:%M:%S"))
## Bloquage si carte d'étudiants manquante pour l'année en cours
# /!\ Par sécurité, ces valeurs sont considérées comme False si

View file

@ -21,19 +21,25 @@ Retournent None si pas d'objet trouvé.
# Destinataires, si vide n'envoi rien
To = ['root@crans.org']
import string, os, sys
import string
import os
import sys
import dialog
from whos import aff
import signal, getopt
from time import strftime, strptime, localtime, mktime
import signal
import getopt
from time import strftime, strptime, localtime, mktime, time
import re
import affich_tools, config
import affich_tools
import config
import config.cotisation as cotisation
from affich_tools import cprint, to_encoding, to_unicode
from lock import make_lock, remove_lock
from ldap_crans import crans_ldap, blacklist_items, ann_scol, droits_possibles, droits_critiques, smtpserv, script_utilisateur
from ldap_crans import Adherent, AssociationCrans, Club, Facture
from ldap_crans import Machine, MachineFixe, MachineWifi, MachineCrans, BorneWifi
from ldap_crans import tz, generalizedTimeFormat, fromGeneralizedTimeFormat
import user_tests
isadm = user_tests.isadm()
@ -61,7 +67,7 @@ else:
def dialog(arg):
return affich_tools.dialog(u'Gestion des adhérents et machines du Crans', arg, dialog_theme)
in_facture = None
#########################################################################
## Fonctions de remplissage ou modification des paramètres d'un adhérent
@ -717,7 +723,6 @@ def del_adher(adher):
arg += u'--msgbox "Machines détruites\n\n\n" 0 0'
dialog(arg)
return
del(machines)
while 1:
arg = u'--title "Destruction adhérent %s " --colors ' % adher.Nom()
@ -1125,9 +1130,12 @@ def set_vente(proprio):
def confirm(clas):
u""" Demande confirmation avant enregistrement"""
global in_facture
# On va faire en texte, les couleurs ne passent pas en curses
os.system('clear')
aff(clas)
if in_facture is not None:
cprint("Une facture d'un montant total de %s € sera confirmée." % (in_facture.total()), "rouge")
while 1:
r = affich_tools.prompt(u"Valider et enregister ? [O/N]")
if r == 'O' or r == 'o':
@ -1136,11 +1144,15 @@ def confirm(clas):
return 1
try:
res = clas.save()
except (EnvironmentError, RuntimeError) as c:
if in_facture is not None:
in_facture.recuPaiement(strftime("%Y-%m-%d %H:%M:%S"))
in_facture.save()
except Exception as c:
arg = u'--title "Enregistrement" '
arg += u'--msgbox "%s\n\n\n" 0 0' % to_unicode(c.args[0])
dialog(arg)
return 1
in_facture = None
cprint(res)
affich_tools.prompt(u"Appuyez sur ENTREE pour continuer")
@ -1288,11 +1300,7 @@ def set_admin(proprio):
has_card = proprio.idn != 'cid'
# Initialisation des différentes checkbox
carte = on_off(ann_scol in proprio.carteEtudiant())
prev_carte = on_off(ann_scol - 1 in proprio.carteEtudiant())
paiement = on_off(ann_scol in proprio.paiement())
prev_paiement = on_off(ann_scol - 1 in proprio.paiement())
paiement_ok = on_off('p' in proprio.controle())
carte = on_off(proprio.carteEtudiant())
carte_ok = on_off('c' in proprio.controle())
if has_card: charte_MA = on_off(proprio.charteMA())
@ -1302,35 +1310,17 @@ def set_admin(proprio):
if has_card:
if carte_ok == 'off' or iscontroleur:
checklist.append(u'"1" "Carte d\'étudiant %d/%d fournie" "%s"' %
(ann_scol, ann_scol+1, carte))
checklist.append(u'"1" "Carte d\'étudiant fournie" "%s"' %
(carte,))
else:
texte.append(u'Carte vérifiée')
if isinstance(proprio, Club) or proprio.adherentPayant():
if paiement_ok == 'off' or iscontroleur:
checklist.append(u'"2" "Cotisation %d/%d réglée et charte signée" "%s"' %
(ann_scol, ann_scol+1, paiement))
else:
texte.append(u'Cotisation/charte vérifiées')
else:
texte.append(u'Adhérent non payant')
if iscontroleur:
if has_card:
checklist.append(u'"4" "Carte d\'étudiant vérifiée" "%s"' % carte_ok)
checklist.append(u'"5" "Cotisation/charte vérifiées" "%s"' % paiement_ok)
checklist.append(u'"2" "Carte d\'étudiant vérifiée" "%s"' % carte_ok)
# Carte et paiement de l'année précédente
if has_card:
checklist.append(u'"6" "Carte d\'étudiant %d/%d fournie" "%s"' %
(ann_scol - 1, ann_scol, prev_carte))
if isinstance(proprio, Club) or proprio.adherentPayant():
checklist.append(u'"7" "Cotisation %d/%d réglée et charte signée" "%s"' %
(ann_scol - 1, ann_scol, prev_paiement))
if (isbureau or isadm) and has_card:
checklist.append(u'"8" "Charte des MA signee" "%s"' % charte_MA)
checklist.append(u'"3" "Charte des MA signee" "%s"' % charte_MA)
if not checklist:
# Il n'y a rien de modifiable
@ -1351,71 +1341,189 @@ def set_admin(proprio):
# Traitement
if has_card:
if '1\n' in result:
proprio.carteEtudiant(ann_scol)
proprio.carteEtudiant(True)
elif iscontroleur or carte_ok == 'off':
proprio.carteEtudiant(-ann_scol)
if iscontroleur:
if '6\n' in result:
proprio.carteEtudiant(ann_scol - 1)
else:
proprio.carteEtudiant(-(ann_scol - 1))
if '4\n' in result:
proprio.carteEtudiant(False)
if '2\n' in result:
proprio.controle('+c')
else:
proprio.controle('-c')
if '2\n' in result and ann_scol not in proprio.paiement():
# On est en train de renouveller l'adhésion
# Combien a-t-il de machines ?
if proprio.idn != 'cid' and len(proprio.machines_fixes()) > 1 and not proprio.droits():
# Il a plus d'une machine fixe et n'est pas membre actif
arg = u'--title "Trop de machines fixes" '
arg += u'--menu " Cet adhérent a trop de machines fixes.\n\n'
arg += u'Seuls les membres actifs peuvent posséder plusieurs\n'
arg += u'machines fixes. L\'adhérent actuel n\'est pas membre\n'
arg += u'actif, il n\'est donc pas possible de lui laisser autant\n'
arg += u'de machines fixes..." 0 0 0 '
arg += u'"OK" "OK, je vais lui virer des machines" '
annul, result = dialog(arg)
if not annul and result[0] == "Si":
proprio.paiement(ann_scol)
else:
set_admin(proprio)
return
else:
proprio.paiement(ann_scol)
elif '2\n' not in result and (paiement_ok == 'off' or iscontroleur):
proprio.paiement(-ann_scol)
if iscontroleur:
if '7\n' in result:
proprio.paiement(ann_scol - 1)
else:
proprio.paiement(-(ann_scol - 1))
if '3\n' in result:
proprio.paiement(ann_scol+1)
elif paiement_ok == 'off' or iscontroleur:
proprio.paiement(-ann_scol-1)
if '5\n' in result:
proprio.controle('+p')
elif iscontroleur:
proprio.controle('-p')
if has_card:
if '8\n' in result:
if '3\n' in result:
proprio.charteMA(True)
elif isadm or isbureau:
proprio.charteMA(False)
if 'C\n' in result:
proprio.controle('+k')
if not iscontroleur:
proprio.controle('-p')
elif iscontroleur:
proprio.controle('-k')
def set_adhesion(proprio):
"""Maj de la période d'accès de l'adhérent"""
global in_facture
end = proprio.adhesion()
annul = 0
args = u'--title "Adhésion de %s" ' % proprio.Nom()
if end and end - cotisation.delai_readh > time():
t_end = strftime('%d/%m/%Y %H:%M:%S', localtime(end))
args += u'--msgbox "Actuellement adhérent jusqu\'au %s.\nMerci de revenir lorsqu\'il restera moins de %s jours avant la fin." 0 0 ' % (t_end, cotisation.delai_readh_jour,)
dialog(args)
return 1
elif end and end - cotisation.delai_readh <= time():
t_end = strftime('%d/%m/%Y %H:%M:%S', localtime(end))
args += u'--yesno "Adhésion jusqu\'au %s. Réadhérer ?" 0 0 ' %(t_end,)
annul, res = dialog(args)
else:
args += u'--yesno "Adhésion pour un an, continuer ?" 0 0 '
annul, res = dialog(args)
if annul:
return 1
if in_facture is not None:
f = in_facture
else:
f = None
facture = proprio.adhesion(True, f)
if float(facture.total()) == 0.0:
facture.modePaiement('liquide')
while True and not facture.modePaiement():
arg = u'--title "Mode de paiement pour l\'adhésion de %s" ' % (proprio.Nom(),)
arg += u'--menu "Comment %s souhaite-t-il payer ?" 0 0 0 ' % (proprio.Nom(), )
arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." '
arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
if proprio.solde() - facture.total() > 0:
arg += u'"Solde" "Par solde : il a assez d\'argent pour ça." '
annul, res = dialog(arg)
if annul:
facture._set('finAdhesion', [])
facture._set('debutAdhesion', [])
facture.supprime(pop=True)
return 1
res = res[0]
if res == "Liquide" or res == "Cheque":
arg = u'--title "Avertissement" '
arg += u'--msgbox "Une facture sera créée, après validation par le trésorier, l\'adhérent\npourra y accéder via l\'intranet ou la demander." 0 0'
dialog(arg)
facture.modePaiement(res.lower())
break
else:
facture.modePaiement(res.lower())
break
in_facture = facture
def set_connexion(proprio):
"""Maj de la période d'accès de l'adhérent"""
global in_facture
# Si l'adhérent ne l'est plus, on commence par le faire adhérer, sauf s'il a une facture adhésion.
adhEnd = proprio.adhesion()
if in_facture is not None:
adhEnd = max(adhEnd, fromGeneralizedTimeFormat(in_facture._data.get('finAdhesion', ["19700101000000Z"])[0]))
if adhEnd < time():
stat = set_adhesion(proprio)
if stat == 1:
return 1
end = proprio.connexion()
args = u'--title "Connexion de %s" ' % proprio.Nom()
if end > adhEnd:
args += u'--msgbox "La fin de l\'adhésion arrivera avant le début de cette connexion.\nIl faudra d\'abord réadhérer." 0 0 ' % (t_end, cotisation.delai_readh_jour,)
dialog(args)
return 1
while True:
while True:
args = u'--title "Connexion de %s" ' % proprio.Nom()
if proprio.connexion() > time():
args += u'--menu "Connexion jusqu\'au %s, choisir une durée de prolongation. : " 0 0 0 ' % (strftime("%d/%m/%Y %H:%M:%S"),)
else:
args += u'--menu "Connexion actuellement inactive, choisir une durée. : " 0 0 0 '
args += u'"An" "Prolonger d\'un an." '
args += u'"Select" "Prolonger de plusieurs mois." '
args += u'"Mois" "Prolonger d\'un mois." '
annul, res = dialog(args)
if annul:
in_facture.supprime(pop=True)
return 1
res = res[0]
if res == "An":
nb_mois = 12
break
elif res == "Select":
back = False
while True:
arg = u'--title "Nombre de mois de connexion ? (entre 2 et 12)" '
arg += u'--inputbox "" 0 0 "1" '
annul, res = dialog(arg)
if annul:
back = True
break
else:
try:
res = int(res[0])
except:
arg = u'--title "Opération impossible" '
arg += u'--msgbox "On a dit un entier…" 0 0'
dialog(arg)
continue
if res < 2 or res > 12:
arg = u'--title "Opération impossible" '
arg += u'--msgbox "On a dit un entier entre 2 et 12." 0 0'
dialog(arg)
continue
else:
nb_mois = res
break
if back:
continue
break
else:
nb_mois = 1
break
if in_facture is not None:
f = in_facture
else:
f = None
facture = proprio.connexion(nb_mois, f)
newEnd = fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0])
if newEnd > adhEnd and newEnd - adhEnd >= (cotisation.duree_conn_max - cotisation.duree_conn_plafond)*30*86400:
arg = u'--title "Avertissement" '
arg += u'--yesno "La fin de la connexion de l\'adhérent (%s) tombera après la fin de son adhésion (%s).\nS\'il veut en profiter, il lui faudra réadhérer. Continuer ?" 0 0' %(strftime('%d/%m/%Y %H:%M:%S', localtime(newEnd)), strftime('%d/%m/%Y %H:%M:%S', localtime(adhEnd)), )
no, res = dialog(arg)
if no:
facture._set('finConnexion', [])
facture._set('debutConnexion', [])
facture.supprime(pop=True)
continue
if not f.modePaiement():
arg = u'--title "Mode de paiement pour la connexion de %s" ' % (proprio.Nom(),)
arg += u'--menu "Comment %s souhaite-t-il payer ?" 0 0 0 ' % (proprio.Nom(), )
arg += u'"Liquide" "En espèces : penser à mettre l\'argent dans une enveloppe." '
arg += u'"Cheque" "Par chèque : ne pas oublier de vérifier signature, date, ordre et montant." '
if proprio.solde() - facture.total() > 0:
arg += u'"Solde" "Par solde : il a assez d\'argent pour ça." '
annul, res = dialog(arg)
if annul:
facture._set('finConnexion', [])
facture._set('debutConnexion', [])
facture.supprime(pop=True)
continue
res = res[0]
if res == "Liquide" or res == "Cheque":
arg = u'--title "Avertissement" '
arg += u'--msgbox "Une facture sera créée, après validation par le trésorier, l\'adhérent\npourra y accéder via l\'intranet ou la demander." 0 0'
dialog(arg)
facture.modePaiement(res.lower())
break
else:
facture.modePaiement(res.lower())
break
in_facture = facture
###############################################################
## Fonctions de remplissage ou modification des paramètres club
@ -1441,7 +1549,7 @@ def new_club(club):
if step == 4:
# Administratif
if set_admin(club): step -= 1
if set_adhesion(club): step -= 1
else: step += 1
if step == 5:
@ -1459,12 +1567,15 @@ def modif_club(club):
Modification du club fourni (instance de club)
Retourne 1 si annulation.
"""
global in_facture
arg = u'--title "Modification du club %s" ' % club.Nom()
arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
arg += u'"NomClub" "Modifier le nom du club" '
arg += u'"Responsable" "Changer le responsable du club %s" ' % club.responsable().Nom()
arg += u'"Imprimeurs" "Changer la liste des imprimeurs" '
arg += u'"Administratif" "Précâblage" '
arg += u'"Administratif" "Données administratives" '
arg += u'"Adhesion" "Pour les réadhésions" '
arg += u'"Local" "Modifier le local du club" '
arg += u'"Compte" "Créer un compte crans." '
if club.compte():
@ -1485,8 +1596,8 @@ def modif_club(club):
set_responsable(club)
elif res[0] == 'Imprimeurs':
set_imprimeurs(club)
elif res[0] == 'Administratif':
set_admin(club)
elif res[0] == 'Adhesion':
set_adhesion(club)
elif res[0] == 'Compte':
set_club_compte(club)
elif res[0] == 'Remarque':
@ -1811,7 +1922,8 @@ def new_adher(adher):
if set_bases(adher): return 1
steps = [set_etudes,
#set_type_de_connexion, # Plus de connexion gratuite. Les adhérents sont payant par défaut
set_adhesion,
set_connexion,
set_admin,
set_mail,
set_rque]
@ -1828,6 +1940,8 @@ def modif_adher(adher):
Modification de l'adhérent fourni (instance de adhérent)
Retourne 1 si annulation.
"""
global in_facture
# Préliminaire : si la chambre est inconnue on force la question
if adher.chbre() == '????':
res= ['Chambre']
@ -1845,8 +1959,8 @@ def modif_adher(adher):
arg = u'--title "Modification de %s" ' % adher.Nom()
arg += u'--menu "Que souhaitez vous modifier ?" 0 0 0 '
arg += u'"Administratif" "Précâblage, carte d\'étudiant, études" '
if not payant:
arg += u'"Connexion" "Changer de type de connexion(gratuit->payant)" '
arg += u'"Adhesion" "Pour toute réadhésion" '
arg += u'"Connexion" "Mise à jour de l\'accès Internet" '
arg += u'"Etat-civil" "Nom, prénom" '
if adher.chbre() == 'EXT':
arg += u'"Adresse" "Déménagement" '
@ -1881,8 +1995,7 @@ def modif_adher(adher):
elif res[0] == 'Etat-civil':
set_etat_civil(adher)
elif res[0] == 'Administratif':
if not set_admin(adher):
set_etudes(adher)
set_admin(adher)
elif res[0] == 'Mail':
set_mail(adher)
elif res[0] == 'Remarque':
@ -1891,14 +2004,10 @@ def modif_adher(adher):
set_droits(adher)
elif res[0] == 'Blackliste':
set_blackliste(adher)
elif res[0] == 'Charte des MA' :
set_charte_MA(adher)
elif res[0] == 'Adhesion':
set_adhesion(adher)
elif res[0] == 'Connexion':
if dlg.yesno(u"Passer à un compte payant ?") == 0:
# On attribue une ip à toute les machines
for m in adher.machines():
m.ip("<automatique>")
adher.adherentPayant(True)
set_connexion(adher)
elif res[0] == 'Adresse' or res[0] == 'Chambre':
arg = u'--title "Déménagement de %s" ' % adher.Nom()
arg += u'--menu "Question :" 0 0 0 '
@ -2003,7 +2112,7 @@ def modif_adher(adher):
elif res[0] == 'Vente':
set_vente(adher)
if adher.modifs:
if adher.modifs or in_facture is not None:
return confirm(adher)
def modif_machine(machine):
@ -2344,6 +2453,7 @@ def menu_principal():
if not proprio: continue
if del_adher(proprio): continue
del(proprio) ; proprio= None
del(becane) ; becane= None
elif choix == 'dM':
# Destruction machine
@ -2533,7 +2643,7 @@ if __name__ == '__main__':
sys.stderr = sys.__stderr__
traceback = s.getvalue()
try:
if To:
if not debug and To:
# Paramètres pour le mail
From = script_utilisateur + '@crans.org'

View file

@ -17,6 +17,7 @@ import os
import random
import string
import time
import datetime
import sys
import pwd
import errno
@ -25,15 +26,17 @@ import ldap.modlist
import ldap_passwd
import netaddr
import time
import annuaires_pg as annuaires
import config
import config.impression
import config.cotisation as cotisation
import iptools
import ip6tools
import cPickle
import config_mail
from chgpass import change_password
from calendar import monthrange
from affich_tools import coul, prompt, cprint
from email_tools import send_email
from syslog import openlog, closelog, syslog
@ -151,6 +154,27 @@ def decode(s):
else:
return s.decode('utf-8', 'ignore') # On ignore les erreurs
def tz(thetz):
abstz = 100*abs(thetz)
if thetz == 0:
return "Z"
else:
return "%s%04d" % ("+"*(thetz < 0) + "-"*(thetz > 0), abstz)
def generalizedTimeFormat(stamp):
"""Converts a timestamp (local) in a generalized time format
for LDAP
"""
return "%s%s" % (time.strftime("%Y%m%d%H%M%S", time.localtime(stamp)), tz(time.altzone/3600))
def fromGeneralizedTimeFormat(gtf):
"""Converts a GTF stamp to unix timestamp
"""
return time.mktime(time.strptime(gtf.split("-", 1)[0].split("+", 1)[0].split('Z', 1)[0], "%Y%m%d%H%M%S"))
def strip_accents(a, sois_un_porc_avec_les_espaces = True):
""" Supression des accents de la chaîne fournie """
res = normalize('NFKD', decode(a)).encode('ASCII', 'ignore')
@ -392,7 +416,8 @@ class CransLdap:
'historique', 'blacklist', 'droits', 'uidNumber', 'info',
'solde', 'controle', 'contourneGreylist', 'rewriteMailHeaders',
'ablacklist', 'homepageAlias', 'charteMA',
'adherentPayant', 'gpgFingerprint'], \
'adherentPayant', 'gpgFingerprint', 'debutConnexion', 'finConnexion',
'debutAdhesion', 'finAdhesion'], \
'club': \
['cid', 'responsable', 'paiement', 'historique', 'blacklist',
'mailAlias', 'info', 'controle', 'ablacklist', 'imprimeurClub'], \
@ -401,7 +426,7 @@ class CransLdap:
'borneWifi': non_auto_search_machines_champs + \
['prise', 'puissance', 'canal', 'hotspot', 'positionBorne', 'nvram'],
'machineWifi': non_auto_search_machines_champs + ['ipsec'],
'facture': ['fid']}
'facture': ['fid', 'debutConnexion', 'finConnexion', 'debutAdhesion', 'finAdhesion']}
# tous les champs de recherche
search_champs = {}
@ -691,7 +716,6 @@ class CransLdap:
except:
raise ValueError(u"Impossible de créer l'objet %s" % nom_classe)
def search(self, expression, mode=''):
"""
Recherche dans la base LDAP, expression est une chaîne :
@ -757,13 +781,13 @@ class CransLdap:
# définifif (cf config.py).
if config.periode_transitoire:
# Pour la période transitoire année précédente ok
el = "(|(paiement=%d)(paiement=%d))" % (ann_scol, ann_scol-1)
el = "(|(paiement=%d)(paiement=%d)(finAdhesion>=%s))" % (ann_scol, ann_scol-1, generalizedTimeFormat(time.time()))
else:
el = "(paiement=%s)" % ann_scol
el = "(|(paiement=%s)(finAdhesion>=%s))" % (ann_scol, generalizedTimeFormat(time.time()))
# Doit-on bloquer en cas de manque de la carte d'etudiant ?
# (si période transitoire on ne bloque dans aucun cas)
if config.bl_carte_et_definitif:
el = "(&(|(carteEtudiant=%d)(objectClass=club))%s)" % (ann_scol, el)
el = "(&(|(carteEtudiant=%d)(objectClass=club)(carteEtudiant=TRUE))%s)" % (ann_scol, el)
elif champ[1:] == 'blacklist':
el = '(blacklist=%s)' % expr
else:
@ -1029,9 +1053,9 @@ class BaseClasseCrans(CransLdap):
# Il faut aussi regarder la blackliste du propriétaire
p = self.proprietaire()
bl_liste += p.blacklist()
elif isinstance(self, Adherent) and (config.ann_scol in self.paiement()):
elif isinstance(self, Adherent) and (config.ann_scol in self.paiement() or (self.adhesion() > time.time() and self.connexion() > time.time())):
# blacklistes virtuelle si on est un adhérent pour carte étudiant et chambre invalides
if not config.periode_transitoire and config.bl_carte_et_actif and not (config.ann_scol in self.carteEtudiant()) and not self.sursis_carte():
if not config.periode_transitoire and config.bl_carte_et_actif and not bool(self.carteEtudiant()) and not self.sursis_carte():
actifs['carte_etudiant']=('-','-')
if self.chbre() == '????':
actifs['chambre_invalide']=('-','-')
@ -1144,9 +1168,7 @@ class BaseClasseCrans(CransLdap):
[ index de la remarque à modifier, nouvelle remarque ]
l'index est celui obtenu dans la liste retournée par info()
"""
if not self._data.has_key('info'):
self._data['info'] = []
liste = list(self._data['info'])
liste = list(self._data.get('info', []))
if new == None: return map(decode, liste)
if type(new) == list:
@ -1180,7 +1202,11 @@ class BaseClasseCrans(CransLdap):
return []
if not self.dn:
# Enregistrement à placer en tête de base
# Enregistrement à placer en tête de base sauf si
# facture.
if isinstance(self, Facture):
self.dn = self.proprietaire().dn
else:
self.dn = self.base_dn
# Construction de l'historique
@ -1209,7 +1235,8 @@ class BaseClasseCrans(CransLdap):
'puissance', 'canal', 'prise', 'responsable',
'macAddress', 'ipHostNumber', 'ip6HostNumber',
'host', 'positionBorne', 'derniereConnexion',
'hotspot', 'dnsIpv6', 'machineAlias']:
'hotspot', 'dnsIpv6', 'machineAlias', 'finAdhesion',
'finConnexion', 'debutConnexion', 'debutAdhesion']:
if champ in self.modifs:
if champ not in self._init_data.keys():
valeur_initiale = 'N/A'
@ -1227,7 +1254,9 @@ class BaseClasseCrans(CransLdap):
for champ in ['droits', 'controle', 'paiement', 'carteEtudiant',
'mailAlias', 'hostAlias', 'exempt', 'nvram',
'portTCPin', 'portTCPout', 'portUDPin', 'portUDPout',
'homepageAlias', 'imprimeurClub', 'gpgFingerprint']:
'homepageAlias', 'imprimeurClub', 'gpgFingerprint',
'debutConnexion', 'finConnexion', 'debutAdhesion',
'finAdhesion']:
if champ in self.modifs:
if champ == 'controle':
# Ce n'est pas pareil que self._init_data.get('controle', [''])
@ -1461,6 +1490,46 @@ class BaseProprietaire(BaseClasseCrans):
self._init_data = {}
self._modifiable = 'w'
def adhesion(self, update=False, f=None):
"""
Gestion de l'adhésion d'un adhérent
La durée d'adhésion ne peut être choisie
* Si update vaut True, on ajoute un an
* f est une facture passée en référence.
"""
thetime = time.time()
# On récupère sur les factures l'ensemble de celles comportant une adhésion.
adh_factures = self.factures_adh()
finAdh = max([0.0] + [fromGeneralizedTimeFormat(facture._data.get('finAdhesion', ["19700101000000Z"])[0]) for facture in adh_factures if facture.controle() != "FALSE"])
if update == False:
return finAdh
else:
ftime = datetime.datetime.fromtimestamp(max(finAdh, thetime))
if not finAdh - thetime < cotisation.delai_readh:
raise EnvironmentError, u"On ne peut réadhérer que 15 jours avant l'expiration de l'adhésion précédente."
# Calcul de la nouvelle date de fin d'adhésion.
# le +86400 est une souplesse pour permettre au câblage de se passer sans warning
# quand le mec se fait câbler pour un an.
newFinAdh = time.mktime(ftime.replace(year=ftime.year + cotisation.duree_adh_an).timetuple()) + 86400
# Si aucune facture n'est passée en référence, on en crée une nouvelle.
if f is None:
f = Facture(self)
if isinstance(self, Adherent):
f.ajoute(cotisation.dico_adh)
elif isinstance(self, Club):
f.ajoute(cotisation.dico_adh_club)
f._set("finAdhesion", [generalizedTimeFormat(newFinAdh)])
f._set("debutAdhesion", [generalizedTimeFormat(thetime)])
self._set("finAdhesion", self._data.get("finAdhesion", []) + [generalizedTimeFormat(newFinAdh)])
self._set("debutAdhesion", self._data.get("debutAdhesion", []) + [generalizedTimeFormat(thetime)])
return f
def droits(self, droits=None, light=False):
""" Renvoie les droits courants. Non modifiable (sauf si surchargée dans classe enfant)"""
if droits <> None:
@ -1543,6 +1612,7 @@ class BaseProprietaire(BaseClasseCrans):
if not isadm() and isadm(self.compte()):
raise EnvironmentError(u'Il faut être administrateur pour effectuer cette opération.')
else:
if type(new) == list:
# Modif
@ -1651,18 +1721,30 @@ class BaseProprietaire(BaseClasseCrans):
else:
return []
def factures(self):
def factures(self, filtre=None):
""" Retourne les factures (instances) appartenant à la classe """
# Le champ id n'est pas initialisé lorsque le proprio est en cours
# de création
if filtre is None:
filtre = Facture.filtre_idn
else:
filtre = "(&%s%s)" % (filtre, Facture.filtre_idn)
if self.id():
res = []
for r in self.conn.search_s('%s=%s,%s' % (self.idn, self.id(), self.base_dn), 1, Facture.filtre_idn):
for r in self.conn.search_s('%s=%s,%s' % (self.idn, self.id(), self.base_dn), 1, filtre):
res.append(self.make(r, self._modifiable))
return res
else:
return []
def factures_adh(self):
""" Retourne les factures pour adhésion """
return self.factures("(debutAdhesion=*)")
def factures_conn(self):
""" Retourne les factures pour connexion """
return self.factures("(debutConnexion=*)")
def solde(self, operation=None, comment=None):
""" Retourne ou modifie le solde d'un propriétaire
operation doit être un nombre positif ou négatif
@ -1825,8 +1907,12 @@ class BaseProprietaire(BaseClasseCrans):
return False
def paiement_ok(self):
if config.ann_scol in self.paiement() or (config.periode_transitoire and (config.ann_scol-1) in self.paiement()):
if config.periode_transitoire or not isinstance(self, Adherent) or not config.bl_carte_et_definitif or config.ann_scol in self.carteEtudiant():
if isinstance(self, Adherent):
m_paiement = min(self.adhesion(), self.connexion())
else:
m_paiement = self.adhesion()
if config.ann_scol in self.paiement() or (config.periode_transitoire and (config.ann_scol-1) in self.paiement()) or (m_paiement > time.time()) or (config.periode_transitoire and config.debut_periode_transitoire <= m_paiement <= config.fin_periode_transitoire):
if config.periode_transitoire or not isinstance(self, Adherent) or not config.bl_carte_et_definitif or bool(self.carteEtudiant()):
return True
else:
return self.sursis_carte()
@ -2281,6 +2367,47 @@ class Adherent(BaseProprietaire):
# renvoie la valeur trouvée dans la base
return bool(self._data.get('charteMA', []))
def connexion(self, mois=None, f=None):
"""
Gestion de la connexion d'un adhérent
* valeur est un entier définissant un nombre de mois
* f est une facture
"""
thetime = time.time()
# On récupère sur les factures l'ensemble de celles comportant une connexion.
conn_factures = self.factures_conn()
finConn = max([0.0] + [fromGeneralizedTimeFormat(facture._data.get('finConnexion', ["19700101000000Z"])[0]) for facture in conn_factures if facture.controle() != "FALSE"])
if mois is None:
return finConn
elif not isinstance(mois, int):
raise ValueError, u"Le nombre de mois doit être un entier"
else:
ftime = max(finConn, thetime)
# Calcul de la nouvelle date de fin d'adhésion.
curyear = datetime.datetime.now().year
curmonth = datetime.datetime.now().month
nbJours = 0
for i in xrange(1, mois+1):
nbJours += monthrange((curmonth + i - 1)/12 + curyear, (curmonth + i - 1)%12 + 12 * ((curmonth + i - 1) % 12 == 0))[1]
# On ajoute 3600 secondes sur suggestion de Raphaël Bonaque (<bonaque@crans.org>), pour tenir compte des malheureux qui
# pourraient subir le changement d'heure.
newFinConn = ftime + 86400 * nbJours + 3600
# Si aucune facture n'est passée en référence, on en crée une nouvelle.
if f is None:
f = Facture(self)
f.ajoute(cotisation.dico_cotis(mois))
f._set("finConnexion", [generalizedTimeFormat(newFinConn)])
f._set("debutConnexion", [generalizedTimeFormat(thetime)])
f._set("finConnexion", self._data.get("finConnexion", []) + [generalizedTimeFormat(newFinConn)])
f._set("debutConnexion", self._data.get("debutConnexion", []) + [generalizedTimeFormat(thetime)])
return f
def adherentPayant(self, valeur = None):
"""
L'adhérent paie sa cotisation (a droit au WiFi, à un compte Crans, ... True par défaut
@ -2354,7 +2481,11 @@ class Adherent(BaseProprietaire):
si positif ajoute l'année à la liste
si négatif le supprime
"""
return self._an('carteEtudiant', action)
if action == True:
self._set('carteEtudiant', ['TRUE'])
elif action == False:
self._set('carteEtudiant', [])
return bool(self._data.get('carteEtudiant', []))
def checkPassword(self, password):
"""Vérifie le mot de passe de l'adhérent"""
@ -3803,7 +3934,7 @@ class Facture(BaseClasseCrans):
self._set('modePaiement', [new])
return decode(self._data.get('modePaiement', [None])[0])
return decode(self._data.get('modePaiement', [''])[0])
def recuPaiement(self, new=None):
"""
@ -3833,6 +3964,19 @@ class Facture(BaseClasseCrans):
# renvoie la valeur trouvée dans la base
return self._data.get("recuPaiement", [None])[0]
def controle(self, action=None):
if action is None:
return self._data.get("controle", [''])[0]
else:
if action == True:
self._set("controle", ["TRUE"])
elif action == False:
self._set("controle", ["FALSE"])
elif action == "":
self._set("controle", [])
else:
raise ValueError("Mauvaise valeur pour l'attribut controle : %r" % (repr(action),))
def _del_recu_paiement(self):
""" Pour test """
self._set("recuPaiement", [])
@ -3940,7 +4084,7 @@ class Facture(BaseClasseCrans):
# enregistre la nouvelle liste
self._articles(arts)
def supprime(self, supprime):
def supprime(self, supprime=None, pop=None):
"""Supprime un/des article(s) à la facture
arts est un article ou une liste d'articles
"""
@ -3951,6 +4095,9 @@ class Facture(BaseClasseCrans):
# charge la liste des articles
arts = self._articles()
if pop is not None:
_ = arts.pop()
# on supprime les anciens articles
if type(supprime)==dict:
supprime = [supprime]
@ -4062,6 +4209,10 @@ class _FakeProprio(CransLdap):
for r in res:
m.append(self.make(r))
return m
def adhesion(self):
return time.time() + 86400
def connexion(self):
return time.time() + 86400
class AssociationCrans(_FakeProprio):
""" Classe définissant l'assoce (pour affichage de ses machines) """

View file

@ -179,17 +179,13 @@ def adhers_brief(adhers) :
ook = u'\x1b[1;32mO\x1b[1;0m'
nok = u'\x1b[1;31mn\x1b[1;0m'
# Paiement
if ann_scol in a.paiement():
if a.adhesion() > time() or a.paiement():
if 'p' in a.controle(): paid = ook
else: paid = ok
elif isinstance(a,Adherent) and not a.adherentPayant(): paid = coul('G', 'bleu')
else: paid = nok
# Précablage
if ann_scol+1 in a.paiement() : paid = coul(paid,'f_vert')
# Carte d'étudiant
if ann_scol in a.carteEtudiant():
if a.carteEtudiant():
if 'c' in a.controle(): carte = ook
else: carte = ok
else : carte = nok
@ -254,10 +250,7 @@ def machines_brief(machines) :
p = a.Nom()
# A jour administrativement
if ann_scol not in a.paiement() or ann_scol not in a.carteEtudiant():
if isinstance(a, Adherent) and not a.adherentPayant():
p = coul(p, 'bleu')
else:
if (a.adhesion() > time() and ann_scol not in a.paiement()) or not a.carteEtudiant():
p = coul(p,'rouge')
# Données
@ -515,24 +508,22 @@ def adher_details(adher) :
f += u"\n"
# État administratif
f += coul("Date d'inscription : ", "gras")
f += coul(u"Date d'inscription : ", "gras")
f += strftime('%d/%m/%Y %H:%M:%S', localtime(adher.dateInscription()))
f += coul(u'\nÉtat administratif : ','gras')
jour=1
if ann_scol not in adher.carteEtudiant() :
jour = True
if not adher.carteEtudiant() :
f += coul(u"manque carte d'étudiant",'violet')
jour = 0
if ann_scol not in adher.paiement() :
jour = False
if ann_scol not in adher.paiement() and (adher.adhesion() <= time()):
if not jour: f += ' et '
if isinstance(adher, Adherent) and not adher.adherentPayant():
f += coul(u"inscription gratuite ", 'bleu')
else:
f += coul(u"cotisation %s/%d non réglée"% (ann_scol, ann_scol+1 ),'violet')
jour = 0
f += coul(u"Non adhérent actuellement.",'violet')
jour = False
if jour:
f += coul(u"à jour",'vert')
f += '\n'
f += "\n"
# Telephone
tel = adher.tel()
@ -591,30 +582,25 @@ def adher_details(adher) :
f += coul(u" (droits gelés car pas cotisé cette année)",'bleu')
f += u'\n'
# Adhésion
if adher.adhesion() > time():
f += coul(u"Adhésion jusqu'au %s" % strftime("%d/%m/%Y %H:%M:%S", localtime(adher.adhesion())), "vert")
f += u"\n"
elif adher.paiement():
f += coul(u"Paiement pour %s/%s ok (connexion inclue)" % (ann_scol, ann_scol+1), "vert")
f += u"\n"
# Paiement
if adher.paiement() :
if len(adher.paiement()) == 1 :
f += coul(u'Cotisation payée pour l\'année scolaire :','gras')
else :
f += coul(u'Cotisation payée pour les années scolaires :','gras')
g = u''
for an in adher.paiement() : g += u" %i-%i" % ( an, an+1 )
if len(g) > 35 : f += '\n\t'
f += g
if 'p' in adher.controle(): f += coul(u' (OK)', 'vert')
if adher.connexion() > time():
f += coul(u"Connexion jusqu'au %s" % strftime("%d/%m/%Y %H:%M:%S", localtime(adher.adhesion())), "vert")
if adher.adhesion() < adher.connexion():
f += coul(u"(Théoriquement %s, sous réserve de réadhésion)" % (strftime("%d/%m/%Y %H:%M:%S", localtime(adher.connexion())),), "rouge")
f += u'\n'
# Cartes d'étudiant fournie
# Carte d'étudiant fournie
if adher.carteEtudiant():
if len(adher.carteEtudiant()) == 1 :
f += coul(u"Carte d'étudiant fournie pour l'année scolaire :",'gras')
else :
f += coul(u"Carte d'étudiant fournie pour les années scolaires :",'gras')
g = u''
for an in adher.carteEtudiant() : g += u" %i-%i" % ( an, an+1 )
if len(g) > 25 : f += '\n\t'
f += g
if 'c' in adher.controle(): f += coul(u' (OK)', 'vert')
f += coul(u"Carte d'étudiant fournie.",'gras')
if 'c' in adher.controle(): f += coul(u' (Contrôle OK)', 'vert')
f += u'\n'
f += _blacklist(adher)
@ -820,11 +806,10 @@ def club_details(club) :
# État administratif
f += coul(u'État administratif : ','gras')
jour=1
if ann_scol not in club.paiement() :
if not jour : f += ' et '
f += coul(u"charte %s/%d non signée"% (ann_scol, ann_scol+1 ),'violet')
jour = 0
jour = True
if club.adhesion() < time() and not club.paiement():
jour = False
f += coul(u"Non adhérent." ,'violet')
if jour:
f += coul(u"à jour",'vert')
@ -843,14 +828,12 @@ def club_details(club) :
f += coul(u'VLAN : ','gras') + u'%s' % vlans
f += u'\n'
# Paiement
if club.paiement() :
f += coul(u'Charte signée pour les années scolaires :','gras')
g = ''
for an in club.paiement() : g += " %i-%i" % ( an, an+1 )
if len(g) > 35 : f += '\n\t'
f += g
if 'p' in club.controle(): f += coul(u' (OK)', 'vert')
# Adhésion
if club.adhesion() > time():
f += coul(u"Adhésion jusque %s." % (strftime("%d/%m/%Y %H:%M:%S", localtime(club.adhesion())),), "vert")
f += '\n'
elif ann_scol in club.paiement():
f += coul(u"Adhésion pour l'année en cours", "vert")
f += '\n'
login = club.compte()
@ -883,6 +866,14 @@ def club_details(club) :
else :
f += 'aucune'
f += u"\n"
f += coul(u'Facture(s) : ','gras')
m = club.factures()
if m :
f += u'\n' + list_factures(m)
else :
f += u'aucune'
return f
###########################################
@ -1014,8 +1005,6 @@ def borne_clients_canal(borne) :
wl = subprocess.Popen("ssh -o StrictHostKeyChecking=no -i %s root@%s 'cat /tmp/auth-mac.dump ; echo -n \"CANAL \" ; /usr/sbin/nvram get wl0_channel'" % (clef, borne), shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
wl.stdin.close()
for line in wl.stdout.readlines():
print 'coucou'
print line
# Chaque ligne est de la forme
# 00:11:22:33:44:55 -20
line = line.strip().split()

View file

@ -49,13 +49,17 @@ elif arg == "fichier" :
data = db.search(filtre)
return len(data['adherent']) + len(data['club'])
def count_adh():
data = db.get_adherents()
return len(data['adherent']) + len(data['club'])
total = 0
for bat in 'abcghijmpo' :
nb = count('paiement=ok&chbre=%s*' % bat)
nb = count_adh()
total += nb
file.write("bat%s.value %d\n" % (bat, nb))
nb = count('paiement=ok&chbre=EXT')
nb = count_adh()
total += nb
file.write("wifi.value %d\n" % nb)

View file

@ -16,7 +16,11 @@ ACTIONS POSSIBLES
# Licence : GPLv2
import sys, os, re, time, cPickle
import sys
import os
import re
import time
import cPickle
from time import mktime, time, localtime, strptime, strftime
from socket import gethostname
from smtplib import SMTP
@ -208,7 +212,7 @@ comptes_inactifs.py
mail = nb_mails_non_lus(login)
mail = mail == None and u'?' or mail > 0 and u'X' or u' '
ligne = (a.id(), login, a.Nom(), date, forward, mail)
if ann_scol in a.paiement():
if ann_scol in a.paiement() or a.adhesion() > time():
inscrits.append(ligne)
else:
anciens.append(ligne)

View file

@ -26,14 +26,10 @@ def chap_ok(password, challenge, clear_pass) :
return False
def paiement_ok(adh):
"""Paiment ok ?"""
global ann_scol
paid = max(adh.paiement() + [0])
if periode_transitoire:
# Si periode transitoire, on accepte les personnes n'ayant pas
# réadhéré
ann_scol -= 1
return ann_scol <= paid
"""Vérifie que le paiement est ok
"""
return adh.paiement_ok()
def do_auth(mac, prise):
"""Effectue l'authentification. Renvoie (success, msg,