diff --git a/attributs.py b/attributs.py index 420310b..fe38fea 100644 --- a/attributs.py +++ b/attributs.py @@ -48,6 +48,7 @@ import random import string from unicodedata import normalize from crans_utils import format_tel, format_mac, mailexist, validate_name, ip4_of_rid, ip6_of_mac, fetch_cert_info +from crans_utils import toGeneralizedTimeFormat, fromGeneralizedTimeFormat, extractTz import itertools sys.path.append("/usr/scripts") @@ -557,12 +558,86 @@ class derniereConnexion(intAttr): can_modify = [nounou, cableur, soi] # il faut bien pouvoir le modifier pour le mettre à jour ldap_name = "derniereConnexion" +class generalizedTimeFormat(Attr): + """Classe définissant un ensemble de données pour manipuler + une donnée de temps suivant la RFC 4517 + + """ + default = "19700101000000Z" + + def __float__(self): + return self._stamp + + def __int__(self): + return int(self._stamp) + + def __eq__(self, othertime): + if isinstance(self, othertime): + return self._stamp == othertime._stamp + else: + resource = generalizedTimeFormat(othertime, conn=None, Parent=None) + return self._stamp == resource._stamp + + def __neq__(self, othertime): + return not (self == othertime) + + def __lt__(self, othertime): + if isinstance(othertime, generalizedTimeFormat): + return self._stamp < othertime._stamp + else: + resource = generalizedTimeFormat(othertime, conn=None, Parent=None) + return self._stamp < resource._stamp + + def __gt__(self, othertime): + return not (self < othertime) and not (self == othertime) + + def parse_value(self, gtf): + if isinstance(gtf, str) or isinstance(gtf, unicode): + if not ('Z' in gtf or '+' in gtf or '-' in gtf): + self._stamp = gtf + self.value = toGeneralizedTimeFormat(float(gtf)) + else: + self._stamp = fromGeneralizedTimeFormat(gtf) + self.value = gtf + elif isinstance(gtf, float): + self._stamp = gtf + self.value = toGeneralizedTimeFormat(gtf) + +@crans_attribute +class debutAdhesion(generalizedTimeFormat): + legend = u"Date de début de l'adhésion" + category = 'Adh' + can_modify = [cableur, nounou] + ldap_name = 'debutAdhesion' + + +@crans_attribute +class finAdhesion(generalizedTimeFormat): + legend = u"Date de fin de l'adhésion" + category = 'Adh' + can_modify = [cableur, nounou] + ldap_name = 'finAdhesion' + +@crans_attribute +class debutConnexion(generalizedTimeFormat): + legend = u"Date de début de la connexion" + category = 'Adh' + can_modify = [cableur, nounou] + ldap_name = 'debutConnexion' + +@crans_attribute +class finConnexion(generalizedTimeFormat): + legend = u"Date de fin de la connexion" + category = 'Adh' + can_modify = [cableur, nounou] + ldap_name = 'finConnexion' + @crans_attribute class mail(Attr): singlevalue = False optional = False unique = True - legend = "Adresse mail de l'adhérent" + legend = u"Adresse mail de l'adhérent" can_modify = [soi, nounou, cableur] category = 'mail' ldap_name = "mail" @@ -1446,7 +1521,7 @@ class controle(Attr): ldap_name = "controle" def parse_value(self, ctrl): - if ctrl not in [u"", u"c", u"p", u"cp", u"pc"]: + if ctrl not in [u"", u"c", u"p", u"cp", u"pc", u"TRUE", u"FALSE"]: raise ValueError("Contrôle peut prendre les valeurs [c][p]") self.value = ctrl diff --git a/crans_utils.py b/crans_utils.py index 515acec..1ac5aa2 100644 --- a/crans_utils.py +++ b/crans_utils.py @@ -306,3 +306,29 @@ def ip4_addresses(): ip_list.append(link['addr']) return ip_list +def extractTz(thetz): + abstz = 100*abs(thetz) + if thetz == 0: + return "Z" + else: + return "%s%04d" % ("+"*(thetz < 0) + "-"*(thetz > 0), abstz) + +def toGeneralizedTimeFormat(stamp): + """Converts a timestamp (local) in a generalized time format + for LDAP. + + * stamp : float value + * output : a string without the dot second + + """ + + return "%s%s" % (time.strftime("%Y%m%d%H%M%S", time.localtime(stamp)), extractTz(time.altzone/3600)) + +def fromGeneralizedTimeFormat(gtf): + """Converts a GTF stamp to unix timestamp + + * gtf : a generalized time format resource without dotsecond + * output : a float value + + """ + return time.mktime(time.strptime(gtf.split("-", 1)[0].split("+", 1)[0].split('Z', 1)[0], "%Y%m%d%H%M%S")) diff --git a/objets.py b/objets.py index 99f1730..7b9d120 100644 --- a/objets.py +++ b/objets.py @@ -713,10 +713,13 @@ class proprio(CransLdapObject): u""" Un propriétaire de machine (adhérent, club…) """ can_be_by = { variables.created: [attributs.nounou, attributs.bureau, attributs.cableur], variables.modified: [attributs.nounou, attributs.bureau, attributs.soi, attributs.cableur], - variables.deleted: [attributs.nounou, attributs.bureau], + variables.deleted: [attributs.nounou, attributs.bureau,], } - attribs = [attributs.nom, attributs.chbre, attributs.paiement, attributs.info, attributs.blacklist, attributs.controle, attributs.historique] + attribs = [attributs.nom, attributs.chbre, attributs.paiement, attributs.info, + attributs.blacklist, attributs.controle, attributs.historique, + attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion, + attributs.finConnexion] def __repr__(self): return str(self.__class__) + " : nom=" + str(self['nom'][0]) @@ -857,6 +860,14 @@ class proprio(CransLdapObject): u"""Renvoie si le propriétaire a payé et donné sa carte pour l'année en cours""" return self.paiement_ok() and self.carte_ok() + def fin_adhesion(self): + """Retourne la date de fin d'adhésion""" + return max([float(facture.get('finAdhesion', [crans_utils.fromGeneralizedTimeFormat(attributs.finAdhesion.default)])[0]) for facture in self.factures() if facture.get('controle', [''])[0] != u"FALSE"] + [0.0]) + + def fin_connexion(self): + """Retourne la date de fin de connexion""" + return max([float(facture.get('finConnexion', [crans_utils.fromGeneralizedTimeFormat(attributs.finConnexion.default)])[0]) for facture in self.factures() if facture.get('controle', [''])[0] != u"FALSE"] + [0.0]) + def paiement_ok(self, no_bl=False): u""" Renvoie si le propriétaire a payé pour l'année en cours, en prenant en compte les périodes de transition et les blacklistes. @@ -868,7 +879,13 @@ class proprio(CransLdapObject): for bl in self.blacklist_actif(): if bl['type'] == 'paiement': return False - return config.ann_scol in self['paiement'] or (config.periode_transitoire and (config.ann_scol - 1) in self['paiement']) + old_style_paiement = config.ann_scol in self['paiement'] or (config.periode_transitoire and (config.ann_scol - 1) in self['paiement']) + if isinstance(self, adherent): + fin_paiement = min(self.fin_adhesion(), self.fin_connexion()) + else: + fin_paiement = self.fin_adhesion() + new_style_paiement = time.time() < fin_paiement or (config.periode_transitoire and config.debut_periode_transitoire <= fin_paiement <= config.fin_periode_transitoire) + return (old_style_paiement or new_style_paiement) def carte_ok(self): u"""Renvoie si le propriétaire a donné sa carte pour l'année en cours, en prenant en compte les periode transitoires et le sursis carte""" @@ -1251,7 +1268,9 @@ class facture(CransLdapObject): variables.deleted: [attributs.nounou, attributs.bureau, attributs.cableur], } attribs = [attributs.fid, attributs.modePaiement, attributs.recuPaiement, - attributs.historique, attributs.article, attributs.info] + attributs.historique, attributs.article, attributs.info, + attributs.debutAdhesion, attributs.finAdhesion, attributs.debutConnexion, + attributs.finConnexion, attributs.controle ] ldap_name = "facture" _proprio = None diff --git a/printing/templates.py b/printing/templates.py index 9c69018..3e4cff8 100644 --- a/printing/templates.py +++ b/printing/templates.py @@ -1,10 +1,11 @@ #!/bin/bash /usr/scripts/python.sh # -*- coding: utf-8 -*- -from gestion.affich_tools import coul, tableau +from gestion.affichage import style, tableau import importlib import time import ldap import sys +import gestion.config as config # Import inutile, mais on en a besoin pour que le # script continue à fonctionner. @@ -35,9 +36,9 @@ def prise_etat(chbre): # fonction prise_etat propre à lc_ldap, pour # ne plus en avoir besoin ci-après. gestion.whos = try_import(u"gestion.whos") - gestion.whos.coul = coul + gestion.whos.style = style if chbre=="????": - return coul("Chambre invalide", "violet") + return style("Chambre invalide", "violet") return gestion.whos.prise_etat(chbre)[0] def timeformat(t, format): @@ -50,13 +51,13 @@ def blacklists(l): fin=b['fin'] if b['fin'] == '-' else time.strftime("%d/%m/%Y %H:%M", time.localtime(b['fin'])) couleur='rouge' if b['actif'] else None if debut != '-' and fin !='-': - bl.append(coul(u"du %s au %s : %s [%s]" % (debut, fin, b['type'], b['comm']), couleur)) + bl.append(style(u"du %s au %s : %s [%s]" % (debut, fin, b['type'], b['comm']), couleur)) elif debut != '-': - bl.append(coul(u"À partir du %s : %s [%s]" % (debut, b['type'], b['comm']), couleur)) + bl.append(style(u"À partir du %s : %s [%s]" % (debut, b['type'], b['comm']), couleur)) elif fin != '-': - bl.append(coul(u"Jusqu'au %s : %s [%s]" % (fin, b['type'], b['comm']), couleur)) + bl.append(style(u"Jusqu'au %s : %s [%s]" % (fin, b['type'], b['comm']), couleur)) else: - bl.append(coul(u"%s [%s]" % (b['type'], b['comm']), couleur)) + bl.append(style(u"%s [%s]" % (b['type'], b['comm']), couleur)) return bl def split(str, *arg): @@ -90,19 +91,19 @@ def const_of_mac(mac): templateEnv=None def template(dialog=False): - global templateEnv, coul, tableau + global templateEnv, style, tableau if not templateEnv: # un import paresseux, comme ça, pas la peine d'installer jinja2 sur les machines où il n'y en a pas besoin import jinja2 - oldcoul = coul + oldstyle = style oldtableau = tableau tableau = lambda *args,**kwargs: oldtableau(*args,dialog=dialog,**kwargs) - coul = lambda *args,**kwargs:oldcoul(*args,dialog=dialog,**kwargs) + style = lambda *args,**kwargs:oldstyle(*args,dialog=dialog,**kwargs) template_path = '/usr/scripts/lc_ldap/printing/templates/' templateLoader = jinja2.FileSystemLoader( searchpath=["/", template_path] ) templateEnv = jinja2.Environment( loader=templateLoader, trim_blocks=True ) templateEnv.add_extension('jinja2.ext.do') - templateEnv.filters['coul'] = coul + templateEnv.filters['coul'] = style templateEnv.filters['blacklists'] = blacklists templateEnv.filters['prise_etat'] = prise_etat templateEnv.filters['timeformat'] = timeformat @@ -130,7 +131,7 @@ def list_machines(machines, width=None): def list_factures(factures, width=None): return tableau([ [f['fid'][0], f['modePaiement'][0], - coul("OK", "vert") if f.get('recuPaiement', []) else coul("NON", "rouge"), + style("OK", "vert") if f.get('recuPaiement', []) else style("NON", "rouge"), ' '.join(attr['code'] for attr in f.get('article',[])), u"%s€" % sum([float(a['pu'])*int(a['nombre']) for a in f.get('article',[])]) ] for f in factures], @@ -143,8 +144,8 @@ def list_adherents(adherents, width=None): return tableau([ [a['aid'][0], u' '.join(unicode(i) for i in a['prenom'] + a['nom']), - a['chbre'][0], coul('o', 'vert') if a.paiement_ok() else coul('n', 'rouge'), - coul('o', 'vert') if a.carte_ok() else coul('n', 'rouge'), + a['chbre'][0], style('o', 'vert') if a.paiement_ok() else style('n', 'rouge'), + style('o', 'vert') if a.carte_ok() else style('n', 'rouge'), u', '.join(unicode(m['host'][0]).split('.',1)[0] for m in a.machines()) ] for a in adherents ], titre = [u'aid', u'Prénom Nom', u'Chbre', u'P', u'C', u'Machines'], @@ -156,7 +157,7 @@ def list_clubs(clubs, width=None): return tableau([ [a['cid'][0], u' '.join(unicode(i) for i in a['nom']), - a['chbre'][0], coul('o', 'vert') if a.paiement_ok() else coul('n', 'rouge'), + a['chbre'][0], style('o', 'vert') if a.paiement_ok() else style('n', 'rouge'), u', '.join(unicode(m['host'][0]).split('.',1)[0] for m in a.machines()) ] for a in clubs ], titre = [u'cid', u'Nom', u'Chbre', u'P', u'Machines'], @@ -168,14 +169,39 @@ def proprio(proprio, params): params['o']=proprio etat_administratif=[] if proprio.paiement_ok() and proprio.carte_ok(): - etat_administratif.append(coul(u"à jour", "vert")) + etat_administratif.append(style(u"à jour", "vert")) if not proprio.carte_ok(): - etat_administratif.append(coul(u"manque carte d'étudiant", "violet")) + etat_administratif.append(style(u"manque carte d'étudiant", "violet")) if not proprio.paiement_ok(): - etat_administratif.append(coul(u"cotisation non réglée", "violet")) + etat_administratif.append(style(u"cotisation non réglée", "violet")) + if proprio.fin_adhesion() >= time.time(): + adh = style(u"Adhésion jusqu'au %s" % (time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(proprio.fin_adhesion())),), "vert") + elif config.ann_scol in proprio['paiement']: + adh = style(u"Adhésion pour la période %s-%s ok." % (config.ann_scol, config.ann_scol+1), "vert") + elif proprio.paiement_ok(): + adh = style(u"Adhésion terminée, mais il y a un sursis.", 'orange') + else: + adh = style(u"Pas adhérent actuellement.", 'rouge') + params["adh"] = adh + if proprio.fin_connexion() >= time.time(): + conn = style(u"Connexion jusqu'au %s" % (time.strftime("%d/%m/%Y %H:%M:%S", time.localtime(proprio.fin_connexion())),), "vert") + elif config.ann_scol in proprio['paiement']: + conn = style(u"Connexion pour la période %s-%s ok." % (config.ann_scol, config.ann_scol+1), "vert") + elif proprio.paiement_ok(): + conn = style(u"Connexion terminée, mais il y a un sursis.", 'orange') + else: + conn = style(u"Pas connecté actuellement.", 'rouge') + params["conn"] = conn + if proprio.get('carteEtudiant', []): + cetud = style(u"Carte d'étudiant fournie.", 'vert') + elif proprio.sursis_carte() or proprio.carte_ok(): + cetud = style(u"Carte d'étudiant non fournie, mais en sursis.", 'orange') + else: + cetud = style(u"Pas de carte d'étudiant") + params["cetud"] = cetud params['etat_administratif']=etat_administratif if proprio["chbre"][0].value not in ["????", "EXT"]: - params['brassage'] = coul("Cr@ns", "bleu") if gestion.annuaires_pg.is_crans(proprio["chbre"][0].value[0], proprio["chbre"][0].value[1:]) else coul("CROUS", "jaune") + params['brassage'] = style("Cr@ns", "bleu") if gestion.annuaires_pg.is_crans(proprio["chbre"][0].value[0], proprio["chbre"][0].value[1:]) else style("CROUS", "jaune") try: if proprio.machines(): diff --git a/printing/templates/adherent b/printing/templates/adherent index 5e17f95..2c2f90c 100644 --- a/printing/templates/adherent +++ b/printing/templates/adherent @@ -11,6 +11,10 @@ {{"Chambre : "|coul('gras')}}{{o.chbre.0}} ({{o.chbre.0|string|prise_etat}}) {% endif %} {{"Études : "|coul('gras')}}{{o.etudes|join(' ')}} -{{"Cotisation payée pour les années : "|coul('gras')}}{{o.paiement|join(' ')}} {% if o.get('controle', []) and 'p' in o.controle.0.value %}{{"(OK)"|coul('vert')}}{% endif %} -{{"Carte d'étudiant fournie pour les années : "|coul('gras')}}{{o.carteEtudiant|join(' ')}} {% if o.get('controle', []) and 'c' in o.controle.0.value %}{{"(OK)"|coul('vert')}}{% endif %} +{{adh}} {% if o.get('controle', []) and 'p' in o.controle.0.value %}{{"(OK)"|coul('vert')}}{% endif %} + +{{conn}} {% if o.get('controle', []) and 'p' in o.controle.0.value %}{{"(OK)"|coul('vert')}}{% endif %} + +{{cetud}} {% if o.get('controle', []) and 'c' in o.controle.0.value %}{{"(OK)"|coul('vert')}}{% endif %} + {% endblock%} diff --git a/printing/templates/club b/printing/templates/club index 16a17e2..e08b933 100644 --- a/printing/templates/club +++ b/printing/templates/club @@ -7,5 +7,5 @@ {{"Imprimeurs : "|coul('gras')}}{% for i in o.imprimeurClub %}{{i.value.prenom|join(' ')}} {{i.value.nom|join(' ')}} ({{i.value.aid|join(' ')}}) {% endfor%} {{"Local : "|coul('gras')}}{{o.chbre.0}} ({{o.chbre.0|string|prise_etat}}) -{{"Charte signée pour les années scolaires : "|coul('gras')}}{{o.paiement|join(' ')}} +{{adh}} {% endblock%} diff --git a/services.py b/services.py index 61ee752..b07e7af 100644 --- a/services.py +++ b/services.py @@ -16,7 +16,7 @@ import gestion.config as config # liste des attributs dont dépend un service services_to_attrs = {} -services_to_attrs['macip'] = [ attributs.ipHostNumber, attributs.ip6HostNumber, attributs.macAddress, attributs.paiement, attributs.carteEtudiant ] +services_to_attrs['macip'] = [ attributs.ipHostNumber, attributs.ip6HostNumber, attributs.macAddress, attributs.paiement, attributs.carteEtudiant, attributs.finConnexion ] services_to_attrs['dns'] = [ attributs.ipHostNumber, attributs.ip6HostNumber, attributs.sshFingerprint, attributs.host, attributs.hostAlias, attributs.dnsIpv6 , attributs.hostCert, attributs.portTCPin, attributs.portUDPin ] services_to_attrs['blacklist'] = [ attributs.blacklist, attributs.chbre, attributs.mailInvalide ] + services_to_attrs['macip'] services_to_attrs['ports'] = [ attributs.portUDPout, attributs.portUDPin, attributs.portTCPout, attributs.portTCPin ] @@ -319,7 +319,7 @@ def services_to_restart(conn, old_attrs={}, new_attrs={}, created_object=[], del if 'cransAccount' in added_objectClass: arg = services_to_args['home'](added_objectClass[0]) service_to_restart(conn, "home", list(arg), 0) - + if 'cransAccount' in deleted_objectClass: service_to_restart(conn, "del_user", ["%s,%s" % (old_attrs['uid'][0], old_attrs['homeDirectory'][0])], 0)