whos.py 50.8 KB
Newer Older
1
#!/bin/bash /usr/scripts/python.sh
2
# -*- coding: utf-8 -*-
bernat's avatar
bernat committed
3

4
# Copyright (C) Frédéric Pauget
bernat's avatar
bernat committed
5 6
# Licence : GPLv2

7 8
u"""Ce script permet de recherche et d'afficher le détail d'une machine ou
d'un adhérent.
bernat's avatar
bernat committed
9 10 11

Usage: %(prog)s [options] <chaine de recherche>

12 13
La chaine de recherche peut être :
    * soit un terme unique, dans ce cas la recherche sera effectuée sur les
bernat's avatar
bernat committed
14
champs en bleu ci-dessous.
15 16
    * soit du type "champ1=valeur1&champ2!=valeur2 ...", les résultats seront
alors limités aux entrées correspondantes à tous les critères.
bernat's avatar
bernat committed
17

18
Les champs de recherche possibles sont :
bernat's avatar
bernat committed
19
%(champs_rech)s
20 21

Recherche sur prise possible (utiliser uniquement ce champ dans ce cas).
bernat's avatar
bernat committed
22

23 24
Les options de recherches sont :
   * limitations sur l'affichage :
25
        -a ou --adherent : limitation de l'affichage aux adhérents
26 27 28 29
        -m ou --machine : limitation de l'affichage aux machines
        -c ou --club : limitation de l'affichage aux clubs
        -b ou --bornes : limitation de l'affichage aux bornes wifi
        --crans : recherche uniquement les machines du crans
bernat's avatar
bernat committed
30
   * options d'affichage :
31
        -t ou --tech : affichages des infos techniques des machines
32
à la place des infos administratives dans les résumés.
33
        -i ou --ipsec : montre la clef wifi (anciennement ipsec) des machines wifi
34
        -l <num> ou --limit=<num> : limite du nombre de résultats pour utiliser
35
le mode d'affichage condensé au lieu du mode détaillé (défaut %(limit_aff_details)i)
bernat's avatar
bernat committed
36
        -L <num> ou --limit-historique=<num> : limitation du nombre de lignes
37
d'historique affichées (défaut %(limit_aff_historique)i)
38 39
        -d <num> ou --limit-blacklist=<num> : limitation du nombre de lignes
d'historique des déconnexions affichées (défaut %(limit_aff_blacklist)i)
40
        -s ou --ssh : affiche les fingerprint ssh des machines
41 42
        --servers : limite les résultats aux servers
        --adm : limite les résultats aux machines adm
43
        -6 ou --ipv6 : affiche les ipv6 dans l'affichage synthétique
bernat's avatar
bernat committed
44 45
"""

46 47 48 49
import commands
import getopt
import subprocess
import sys
50
from time import strftime, localtime, time
51 52

from affich_tools import *
53
from ldap_crans import is_actif, crans_ldap, AssociationCrans, hostname
54
from ldap_crans import MachineCrans, MachineWifi, BorneWifi
55
from ldap_crans import Adherent
56
import gestion.config as config
57
import ridtools
58 59 60
import user_tests

base = None
bernat's avatar
bernat committed
61 62

limit_aff_details = 1
63
limit_aff_machines = 15
bernat's avatar
bernat committed
64
limit_aff_historique = 4
65
limit_aff_blacklist = 4
bernat's avatar
bernat committed
66
aff_ipsec = 0
67
aff_ssh = 0
68
aff_ipv6 = 0
bernat's avatar
bernat committed
69

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
################################################################################
### Fonctions utiles

def indicatif (num):
    """
    Sépare l'indicatif de nationalité du reste d'un numéro de téléphone
    """
    if num[0:2] != '00':
        return ('', num)
    if num[2] == '1':
        return ('1 '+num[3:6], num[6:])
    if num[2] in ['0','7']:
        return (num[2], num[3:])
    # indicatifs nationaux à 2 chiffres
    ids = [20, 27, 36, 86, 98]
    ids.extend(range(30,35))
    ids.extend(range(39,42))
    ids.extend(range(43,50))
    ids.extend(range(51,59))
    ids.extend(range(60,67))
    ids.extend(range(81,85))
    ids.extend(range(90,96))
    if int(num[2:4]) in ids:
        return (num[2:4], num[4:])
    return (num[2:5], num[5:])

def format_tel (num):
    """
    Formate un numéro de téléphone
        * Remplace un éventuel "00" au début par un "+"
        * Insère des espaces pour que ce soit lisible
    """
    res = ''
    (indic, reste) = indicatif(num)
    if indic != '':
        res += '+' + indic + ' '
    l = len(reste)
    if l%2 == 1:
        res += reste[0] + ' '
    for i in range(l%2,l,2):
        res += reste[i:i+2] + ' '
    return res

################################################################################

bernat's avatar
bernat committed
115 116
def aff(qqch,mtech=0) :
    """ Affichage de qqch.
117
    qqch peut être une liste d'instances des classes adhérent ou machine
118
    (un seul type dans la liste) dans ce cas :
119
        * si la longueur de la liste est inférieure à limit_aff_details
120
    affiche les propriétés détaillées de chaque élément.
121 122
        * sinon résume dans un tabeau des principales propriétés
    si qqch est une instance seul la traité comme une liste à une élément
123
    Si mtech = 1 affiches les infomations techniques des machines plutot
124
    qu'administratives dans le tableau des propriétés
125
    """
bernat's avatar
bernat committed
126
    if type(qqch) != list :
127
        qqch = [ qqch ]
128

bernat's avatar
bernat committed
129
    if len(qqch) > limit_aff_details :
130
        t = qqch[0].idn
131 132
        if t == 'aid':
            cprint(adhers_brief(qqch))
133
        elif t == 'mid':
134 135 136 137 138 139
            if mtech:
                cprint(list_machines(qqch))
            else:
                cprint(machines_brief(qqch))
        elif t == 'cid':
            cprint(clubs_brief(qqch))
140 141
        elif t == 'fid':
            cprint(factures_brief(qqch))
bernat's avatar
bernat committed
142
    else :
143 144 145
        i = 0
        for c in qqch :
            t = c.idn
146
            if i: cprint(u'='*80, 'cyan')
147
            i = 1
148 149 150 151 152 153
            if t == 'aid':
                cprint(adher_details(c).strip())
            elif t == 'mid':
                cprint(machine_details(c).strip())
            elif t == 'cid':
                cprint(club_details(c).strip())
154 155
            elif t == 'fid':
                cprint(facture_details(c).strip())
156

157
    # affiche le nombre de résultats
bernat's avatar
bernat committed
158
    if len(qqch) > 1:
159
        cprint(u"Total: %d" % len(qqch))
160

bernat's avatar
bernat committed
161 162
def adhers_brief(adhers) :
    """
163
    Formatage sous forme de tableau des infos sur la liste d'adhérent fournie :
164
        * aid
165
        * prénom nom
166 167
        * chambre
        * machines
bernat's avatar
bernat committed
168
    """
169
    data = []
glondu's avatar
glondu committed
170

171
    # Copie locale triée par (nom, prenom)
glondu's avatar
glondu committed
172 173
    adhers = adhers[:]
    adhers.sort(lambda x, y: cmp((x.nom(), x.prenom()), (y.nom(), y.prenom())))
174

glondu's avatar
glondu committed
175
    for a in adhers:
176
        ## État administratif
177
        ok = u'\x1b[1;32mo\x1b[1;0m'
178
        ook = u'\x1b[1;32mO\x1b[1;0m'
179 180
        nok = u'\x1b[1;31mn\x1b[1;0m'
        # Paiement
181
        if a.adhesion() > time() or a.paiement():
182 183
            if 'p' in a.controle(): paid = ook
            else: paid = ok
184
        else: paid = nok
185

186
        # Carte d'étudiant
187
        if a.carteEtudiant():
188 189
            if 'c' in a.controle(): carte = ook
            else: carte = ok
190
        else : carte = nok
191

192
        machines = ''
193
        # Récupération des machines
194 195 196 197
        if len(adhers) <= limit_aff_machines:
            for machine in a.machines() :
                nom = machine.nom().split('.')[0]
                if machine.blacklist_actif() : k = 'rouge'
198
                elif isinstance(machine, MachineWifi): k = 'cyan'
199 200 201 202 203
                else : k= ''
                if machines : machines += ', ' + coul(nom,k)
                else : machines = coul(nom,k)
        else:
            machines = None
204
        # Données
205 206 207 208
        if len(adhers) <= limit_aff_machines:
            data.append([a.id(), a.Nom(), a.chbre(), paid, carte, machines])
        else:
            data.append([a.id(), a.Nom(), a.chbre(), paid, carte])
209

210 211
    if len(adhers) <= limit_aff_machines:
        return u"Machines en rouge = machines avec limitation de services\n" + \
212 213
               u"P : paiement année en cours, le fond vert indique le précâblage (G bleu = inscription gratuite)\n" + \
               u"C : carte d'étudiant année en cours\n" + \
214
               tableau(data,
215
                       titre = [u'aid', u'Prénom Nom', u'Chbre', u'P', u'C', u'Machines'],
216 217 218 219
                       largeur = [5, 30, 5, 1, 1, '*'],
                       alignement = ['d', 'c', 'g', 'c', 'c', 'c'])
    else:
        return u"Machines en rouge = machines avec limitation de services\n" + \
220 221
               u"P : paiement année en cours, le fond vert indique le précâblage (G bleu = inscription gratuite)\n" + \
               u"C : carte d'étudiant année en cours\n" + \
222
               tableau(data,
223
                       titre = [u'aid', u'Prénom Nom', u'Chbre', u'P', u'C'],
224 225
                       largeur = [5, '*', 5, 1, 1],
                       alignement = ['d', 'c', 'g', 'c', 'c'])
226

bernat's avatar
bernat committed
227 228
def machines_brief(machines) :
    """
229
    Formatage sous forme d'un tableau des propriétés de la liste de machine :
230
        * mid
231 232
        * rid
        * type (serveur, adm, fil, wifi, adm, borne)
233 234 235
        * nom
        * adresse IP
        * adresse MAC
236
        * si blacklistée
bernat's avatar
bernat committed
237
    """
238
    data = []
glondu's avatar
glondu committed
239

240
    # Copie locale triée par nom
glondu's avatar
glondu committed
241 242
    machines = machines[:]
    machines.sort(lambda x, y: cmp(x.nom(), y.nom()))
243

bernat's avatar
bernat committed
244
    for m in machines :
245
        t, bl = __bases_machines(m)
246

247
        # Propriétaire
248 249
        a = m.proprietaire()
        p = a.Nom()
250

251
        # A jour administrativement
252
        if not a.paiement_ok():
253
            p = coul(p,'rouge')
254

255
        # Données
256
        data.append([m.id(), m.rid() , t, m.nom().split('.')[0], p, a.chbre(), bl])
257

258
    return u"Le propriétaire en rouge signale un problème administratif, en bleu une inscription gratuite\n" + \
259
            tableau(data,
260 261 262
                    titre = [u'mid', u'rid', u'Type', u'Nom de machine', u'Propriétaire', u'Chbre', u'Limitation'],
                    largeur = [5, 5, 13, 18, '*', 5, 10],
                    alignement = ['d', 'd', 'c', 'c', 'c', 'g', 'c'])
bernat's avatar
bernat committed
263

264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
def factures_brief(factures) :
    """
    Formatage sous forme d'un tableau des propriétés de la liste de machine :
        * fid
        * priorio
        * articles
        * modePaiement
        * recuPaiement
        * controle
        * total
    """
    data = []

    # Copie locale triée par nom
    factures = [] + factures
    factures.sort(lambda x, y: cmp(int(x.id()), int(y.id())))

    for facture in factures:
        # Propriétaire
        a = facture.proprietaire()
        p = a.Nom()

        # A jour administrativement
        if not a.paiement_ok():
            p = coul(p,'rouge')

        # Contrôle
        controle = facture.controle()
        if controle == "TRUE":
293
            controle = coul(u"Validée", "vert")
294
        elif controle == "FALSE":
295
            controle = coul(u"Invalide", "rouge")
296
        else:
297
            controle = u"N/A"
298 299 300 301 302 303 304

        # Données
        data.append([
            facture.id(),
            p,
            ', '.join(article['code'] for article in facture.articles()),
            facture.modePaiement(),
305
            coul(facture.recuPaiement(), "vert") if facture.recuPaiement() else coul("NON", "rouge"),
306 307 308 309 310 311 312
            controle,
            unicode(facture.total()) + u" €",
            ])

    return u"Le propriétaire en rouge signale un problème administratif.\n" + \
        tableau(data,
            titre = [u'fid', u'Propriétaire', u'Articles', u'Mode de paiement', u'Payé', u"Contrôle", u"Total"],
313
            largeur = [5, 18, '*', 8, 19, 8, 8],
314 315 316
            alignement = ['d', 'g', 'g', 'c', 'c', 'g', 'd']
        )

bernat's avatar
bernat committed
317 318 319
def clubs_brief(clubs) :
    """
    Formatage sous forme de tableau des infos sur la liste de clubs fournie :
320 321 322 323
        * cid
        * nom
        * local
        * machines
bernat's avatar
bernat committed
324
    """
325
    data = []
glondu's avatar
glondu committed
326

327
    # Copie locale triée par Nom
glondu's avatar
glondu committed
328 329
    clubs = clubs[:]
    clubs.sort(lambda x, y: cmp(x.Nom(), y.Nom()))
330

bernat's avatar
bernat committed
331
    for c in clubs :
332
        ## État administratif
333
        ok = u'\x1b[1;32mo\x1b[1;0m'
334
        ook = u'\x1b[1;32mO\x1b[1;0m'
335 336
        nok = u'\x1b[1;31mn\x1b[1;0m'
        # Paiement
337 338 339 340 341
        if c.paiement_ok():
            # TODO
            #if 'p' in c.controle(): paid = ook
            #else: paid = ok
            paid = ok
342
        else : paid = nok
343

344
        # Précablage
345
        # TODO
346

347
        machines = ''
348
        # Récupération des machines
349 350 351 352 353 354
        for machine in c.machines() :
            nom = machine.nom().split('.')[0]
            if machine.blacklist_actif() : k = 'rouge'
            else : k= ''
            if machines : machines += ', ' + coul(nom,k)
            else : machines = coul(nom,k)
355

356
        # Responsable
357 358 359 360
        try:
            resp = c.responsable().Nom()
        except ValueError, e:
            resp = e
361

362
        # Données
363
        data.append([c.id() , c.Nom(), c.local(), paid, resp, machines])
364

bernat's avatar
bernat committed
365
    return u"Machines en rouge = machines avec limitation de services\n" + \
366
           u"P : signature charte année en cours, le fond vert indique le précâblage\n" + \
367 368 369 370
           tableau(data,
                   titre = [u'cid', u'Nom ', u'Local', u'P', u'Responsable', u'Machines'],
                   largeur = [5, '*', 6, 1, 21, 15],
                   alignement = ['d', 'c', 'g', 'c', 'c', 'c'])
bernat's avatar
bernat committed
371

372

bernat's avatar
bernat committed
373 374
def list_machines(machines) :
    """
375
    Formatage sous forme d'un tableau des propriétés de la liste de machine :
376 377 378 379 380
        * mid
        * type (fixe ou wifi)
        * nom
        * adresse IP
        * adresse MAC
381
        * si blacklistée
bernat's avatar
bernat committed
382
    """
383
    data = []
bernat's avatar
bernat committed
384

385
    # Copie locale triée par nom
glondu's avatar
glondu committed
386 387 388
    machines = machines[:]
    machines.sort(lambda x, y: cmp(x.nom(), y.nom()))

bernat's avatar
bernat committed
389
    for m in machines :
390
        t, bl = __bases_machines(m)
391

392
        # Données
393 394 395 396 397 398
        if not aff_ipv6:
            data.append([m.id(), m.rid(), t, m.nom().split('.')[0], m.ip(), m.mac(), bl])
            larg = 15
        else:
            larg = 22
            data.append([m.id(), m.rid(), t, m.nom().split('.')[0], m.ipv6(), m.mac(), bl])
399

400
    return tableau(data,
401
                titre = [u'mid', u'rid', u'Type', u'Nom de machine', u'Adresse IP', u'Adresse MAC', u'Limitation'],
402
                largeur = [5, 5, 9, '*', larg, 17, 10],
403
                alignement = ['d', 'd', 'c', 'c', 'c', 'c', 'c'])
bernat's avatar
bernat committed
404

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
def list_factures(factures) :
    """
    Formatage sous forme d'un tableau des propriétés de la liste de facture :
        * fid
        * mode de paiement
        * payé
        * codes article
        * total
    """
    data = []

    # Copie locale triée par fid
    factures = factures[:]
    factures.sort(lambda x, y: cmp(x.id(), y.id()))

    for f in factures :
421 422
        controle = f.controle()
        if controle == "TRUE":
423
            controle = coul(u"Validée", "vert")
424
        elif controle == "FALSE":
425
            controle = coul(u"Invalide", "rouge")
426
        else:
427
            controle = u"N/A"
428 429 430 431
        data.append([
           f.id(),
           f.modePaiement(),
           coul("OK", "vert") if f.recuPaiement() else coul("NON", "rouge"),
432
           controle,
433 434 435 436 437
           ', '.join(a['code'] for a in f.articles()),
           u"%s€" % sum([float(a['pu'])*int(a['nombre']) for a in f.articles()])
        ])

    return tableau(data,
438 439 440
                titre = [u'fid', u'Mode de paiement', u'Payé', u"Contrôle", u'Articles', u"Total"],
                largeur = [5, 16, 6, 10, '*', 8],
                alignement = ['d', 'g', 'c', 'c', 'g', 'd'])
441

442
def list_spec(machines) :
bernat's avatar
bernat committed
443
    """
444
    Formatage sous forme d'un tableau des propriétés de la liste de machines spéciales :
445
        * mid
446
        * rid
447
        * nom
448
        * adresse IPv4/IPv6
449
        * adresse MAC
450

451 452 453 454 455 456 457
        Pour les bornes :
            * État
            * puissance
            * canal
            * lieu (la première remarque en fait)
        Pour le reste :
            * ??
bernat's avatar
bernat committed
458
    """
459
    data = []
bernat's avatar
bernat committed
460

461
    # Copie locale triée par nom
462 463
    machines = machines[:]
    machines.sort(lambda x, y: cmp(x.nom(), y.nom()))
glondu's avatar
glondu committed
464

465
    for b in machines:
466
        t, bl = __bases_machines(b)
467

468
        # Données
469 470 471 472 473 474 475
        if t == 'borne':
            try :
                l = [x for x in b.info() if not x[0]=='<'][0]
                if len(l) > 11 :
                    l = l[0:11]
            except :
                l = u'????'
476

477 478 479 480 481 482 483 484 485 486 487 488 489
            if '-' in b.puissance() :
                puiss = coul(b.puissance(),'rouge')
            else :
                puiss = b.puissance()

            if aff_ipv6:
                data.append([b.id(), b.rid(), b.nom().split('.')[0], b.ipv6(), b.mac(), b.canal(), puiss, b.prise(), l])
            else:
                data.append([b.id(), b.rid(), b.nom().split('.')[0], b.ip(), b.mac(), b.canal(), puiss, b.prise(), l])

            titre = [u'mid', u'rid', u'Nom', u'Adresse IP', u'Adresse MAC', u'Can', u'P', u'Pris', u'Lieu']
            largeur = [5, 5, 16, 22, 17, 1, 5, 3, 4, '*']
            alignement = ['d', 'd', 'c', 'c', 'c', 'c', 'c', 'c', 'g', 'c']
490

491
        else:
492 493 494 495
            if aff_ipv6:
                data.append([b.id(), b.rid(), t, b.nom().split('.')[0], b.ipv6(), b.mac()])
            else:
                data.append([b.id(), b.rid(), t, b.nom().split('.')[0], b.ip(), b.mac()])
496

497 498 499 500 501 502 503
            titre = [u'mid', u'rid', u'Type', u'Nom', u'Adresse IP', u'Adresse MAC']
            largeur = [5, 5, 10, 20, '*', 17]
            alignement = ['d', 'd', 'd', 'c', 'c', 'c']

    output = tableau(data, titre=titre, largeur=largeur, alignement=alignement)

    return output
bernat's avatar
bernat committed
504 505 506

def adher_details(adher) :
    """
507
    Affichage du détail des propriétés d'un adhérent
bernat's avatar
bernat committed
508
    """
509
    f=u''
bernat's avatar
bernat committed
510 511 512 513
    # Aid
    f+= coul(u'aid=%s   ' % adher.id() ,'bleu')
    # Nom, prenom
    f += coul(u'Nom : ','gras') + "%s\n" % adher.Nom()
514

bernat's avatar
bernat committed
515
    # Mail
chove's avatar
chove committed
516
    GL = RMH = u''
517
    if adher.mail() == '' or adher.compte() == '':
518
        f += coul(u'Adresse mail : ','gras')
519 520 521
        if adher.mail() == '':
            f += coul('INCONNUE','rouge')
        elif adher.mail_invalide():
salles's avatar
salles committed
522
            f += coul(adher.mail(),'rouge')
523 524 525
        else:
            f += adher.mail()
        f += "\n"
bernat's avatar
bernat committed
526
    else :
527 528
        f += coul(u'Login : ','gras')
        if adher.mail_invalide():
529
            f += coul(adher.compte(),'rouge')
530
        else:
531
            f += adher.compte()
532 533
        if not adher.active():
            f += " " + coul(u"(Compte désactivé)", "rouge")
534
        f += "\t"
535 536 537
        # controurneGreylisting
        if not adher.contourneGreylist():
            GL = u' (%s)'%coul(u'GreyList','gris')
chove's avatar
chove committed
538
        if adher.rewriteMailHeaders():
539
            RMH = u' (%s)'%coul(u'réécriture en-têtes mail','gris')
540

541
        alias = u', '.join([adher.canonical_alias()] + adher.alias())
542
        if alias:
543
            if alias[0] == u',':
544
                # Canonical étéait vide
545 546 547 548 549
                alias = alias[2:]
            f += coul(u'Alias : ','gras') + alias
        f += GL
        f += RMH
        f += u'\n'
550 551
        if len(adher.gpgFingerprint()) > 0:
            f += u"\n".join([coul(u'Fingerprint GPG : ', 'gras') + u"%s" % (i) for i in adher.gpgFingerprint()])+"\n"
552
        try:
553
            forward = file(os.path.join(adher.home(), ".forward")).readlines()
554
            if len(forward) > 1:
555
                forward = forward[0].strip() + u" ...\n"
556
            elif len(forward) == 1:
557
                forward = forward[0].strip() + u"\n"
558 559 560
            if forward:
                f += coul(u'Redirection : ', 'gras') + forward
        except IOError, e:
561
            # Pas de .forward, ou .forward privé... on laisse tomber
562
            pass
563
        f += coul(u'Dernière connexion : ', 'gras')
564 565 566 567 568 569
        timestamp = adher.derniereConnexion()
        if timestamp == 0:
            f += coul(u'Jamais', 'rouge')
        else:
            f += coul(strftime('%d/%m/%Y %H:%M', localtime(timestamp)),
                      time()-timestamp > 32*24*3600 and 'rouge' or '')
570
        f += u"\n"
bernat's avatar
bernat committed
571

572
    # État administratif
573
    f += coul(u"Date d'inscription : ", "gras")
574
    f += strftime('%d/%m/%Y %H:%M:%S', localtime(adher.dateInscription()))
575
    f += coul(u'\nÉtat administratif : ','gras')
576 577
    jour = True
    if not adher.carteEtudiant() :
578
        f += coul(u"manque carte d'étudiant",'violet')
579
        jour = False
580
    if config.ann_scol not in adher.paiement() and (adher.adhesion() <= time()):
581
        if not jour: f += ' et '
582 583 584 585
        f += coul(u"non adhérent actuellement",'violet')
        jour = False
    if not adher.paiement_ok():
        if not jour: f += ' et '
586
        f += coul(u"pas d'accès Internet ", "violet")
587

588
    if jour:
589
        f += coul(u"à jour",'vert')
590
    f += "\n"
591

bernat's avatar
bernat committed
592 593
    # Telephone
    tel = adher.tel()
594
    if tel != 'inconnu' :
595
        f += coul(u'Numéro de téléphone : ','gras') + "%s\n" % format_tel(tel).ljust(12)
596

bernat's avatar
bernat committed
597 598 599
    # Adresse
    chbre = adher.chbre()
    if chbre == 'EXT' :
600
        # Adhérent extérieur
601
        addr = adher.adresse()
602
        if addr[0] :
603 604 605 606
            f += coul(u'Adresse : ','gras')
            f += addr[0] + u'\n'
            if addr[1] != ' ' : f += u'          ' + addr[1] + u'\n'
            f+= u'          ' + addr[2] + u' ' + addr[3] + '\n'
607 608
    elif chbre == '????' :
        f += coul(u'Chambre invalide\n','violet')
bernat's avatar
bernat committed
609
    else :
610
        # Chambre + prise (d'après annuaire)
611
        etat, vlans, cablage = prise_etat(adher.chbre())
612
        f += coul(u'Chambre : ','gras') + u"%s " % chbre
613
        f += u'(%s)' % etat
614
        f += u'\n'
615 616 617
        f += coul(u'Brassage : ','gras')
        f += cablage
        f += u'\n'
618 619 620 621
        # VLAN
        if vlans :
            f += coul(u'VLAN : ','gras') + u'%s' % vlans
            f += u'\n'
622

623
    # Études
bernat's avatar
bernat committed
624
    if adher.etudes(1).isdigit() :
625
        f += coul(u'Études : ','gras')+ "%s %s%s\n" % \
626
        ( adher.etudes(0), adher.etudes(1), adher.etudes(2) )
627
    elif adher.etudes(0) :
628
        f += coul(u'Études : ','gras')+ "%s %s %s\n" % \
629
        ( adher.etudes(0), adher.etudes(1), adher.etudes(2) )
630

631 632 633
    # Solde
    solde = adher.solde()
    if solde :
634 635 636 637 638 639
        f += coul(u'Solde : ','gras')
        if solde < 0 :
            f+= coul(str(solde).replace('.',','),'rouge')
        else :
            f += str(solde).replace('.',',')
        f += u" Euros\n"
640

bernat's avatar
bernat committed
641
    # Role dans l'assoce
642 643
    d = adher.droits()
    if d :
644
        f += coul(u"Droits sur les serveurs : ",'gras') + ', '.join(d)
bos's avatar
bos committed
645
        if adher.droitsGeles():
646
            f +=  coul(u" (droits gelés car pas cotisé cette année)",'bleu')
647
        f += u'\n'
648

649 650 651 652
    # 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"
653 654
    elif config.ann_scol in adher.paiement():
        f += coul(u"Paiement pour %s/%s ok (connexion incluse)" % (config.ann_scol, config.ann_scol+1), "vert")
655
        f += u"\n"
656 657
    elif config.periode_transitoire and ((config.ann_scol - 1) in adher.paiement() or config.debut_periode_transitoire <= min(adher.adhesion(), adher.connexion()) <= config.fin_periode_transitoire):
        f += coul(u"Fin d'adhésion, mais en sursis jusqu'au %s" % (strftime("%d/%m/%Y %H:%M:%S", localtime(config.fin_periode_transitoire)),), "rouge")
658
        f += u"\n"
659

bernat's avatar
bernat committed
660
    # Paiement
661
    if adher.connexion() > time():
662
        f += coul(u"Connexion jusqu'au %s" % strftime("%d/%m/%Y %H:%M:%S", localtime(min(adher.connexion(), adher.adhesion()))), "vert")
663 664
        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")
665
        f += u'\n'
bernat's avatar
bernat committed
666

667 668 669 670
    # Carte d'étudiant fournie
    if adher.carteEtudiant():
        f += coul(u"Carte d'étudiant fournie.",'gras')
        if 'c' in adher.controle(): f += coul(u' (Contrôle OK)', 'vert')
671
        f += u'\n'
bernat's avatar
bernat committed
672 673 674 675

    f += _blacklist(adher)
    f += _info(adher)
    f += _hist(adher)
676

bernat's avatar
bernat committed
677 678 679 680
    # Formatage des machines aussi
    f += coul(u'Machine(s) : ','gras')
    m = adher.machines()
    if m :
681
        f += u'\n' + list_machines(m)
bernat's avatar
bernat committed
682
    else :
683
        f += u'aucune'
bernat's avatar
bernat committed
684

685 686 687 688 689 690 691 692
    f += u"\n"
    f += coul(u'Facture(s) : ','gras')
    m = adher.factures()
    if m :
        f += u'\n' + list_factures(m)
    else :
        f += u'aucune'

bernat's avatar
bernat committed
693
    return f
bernat's avatar
bernat committed
694

695 696 697 698 699 700 701
def facture_details(facture) :
    """
    Affichage du détail des propriétés d'une facture
    """

    controle = facture.controle()
    if controle == "TRUE":
702
        controle = coul(u"Validée", "vert")
703
    elif controle == "FALSE":
704
        controle = coul(u"Invalide", "rouge")
705
    else:
706
        controle = u"N/A"
707 708 709 710 711 712 713 714 715 716 717

    f=u''
    # Fid
    f+= coul(u'fid=%s   ' % facture.id() ,'bleu')
    # Proprio
    f += coul(u'Proprio : ','gras') + "%s\n" % facture.proprietaire().Nom()

    # Articles
    f += coul(u"Articles :", "gras") + u"\n"
    f += list_articles(facture)
    f += u"\n"
718
    f += coul(u"Total : ", "gras") + u"%s €\n" % facture.total()
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740

    # Mode de paiement
    f += coul(u"Mode de paiement : ", "gras")
    f += facture.modePaiement()
    f += u"        "
    f += coul(u"Paiement reçu : ", "gras")
    f += coul(facture.recuPaiement(), "vert") if facture.recuPaiement() else coul(u"Non", "rouge")
    f += u"\n"
    f += coul(u"Contrôle : ", "gras")
    f += controle

    return f

def list_articles(facture):
    """Liste en détail des articles d'une facture

    """

    data = [[article['code'], article['designation'], article['nombre'], article['pu']] for article in facture.articles()]

    return tableau(data,
                titre = [u'Code', u'Désignation', u'Nombre', u"Prix unitaire"],
741
                largeur = [10, '*', 10, 13],
742 743 744
                alignement = ['c', 'g', 'c', 'c'])


bernat's avatar
bernat committed
745 746
clients_ipsec = None
def ipsec_ok(machine) :
747
    """Indique si une machine est correctement authentifiée"""
748
    return False
749

bernat's avatar
bernat committed
750
def machine_details(machine) :
751
    """
752
    Formatage du détail des propriétés d'une machine
bernat's avatar
bernat committed
753 754 755
    """
    f = ''
    f+= coul(u'mid=%s   ' % machine.id(),'bleu')
756

bernat's avatar
bernat committed
757
    # Type de machine
glondu's avatar
glondu committed
758
    if isinstance(machine, MachineWifi): a = 'Machine wifi'
glondu's avatar
glondu committed
759 760 761
    elif isinstance(machine, BorneWifi): a = 'Borne wifi'
    else: a = 'Machine fixe'
    f += coul(a + ' : ', 'gras')
762

763 764 765
    f+= "%s   " % machine.nom()

    if machine.rid() != '':
766
        f+= coul(u'rid=%s    '% machine.rid(), 'bleu')
767 768

    f+= "\n"
769

bernat's avatar
bernat committed
770 771
    # Alias ?
    alias = machine.alias()
772
    if alias :
773 774
        f += coul(u'Alias : ' ,'gras') + ', '.join(alias)
        f+= '\n'
775

bernat's avatar
bernat committed
776 777
    f+= coul(u'IP : ','gras') + "%s\t\t" %machine.ip()
    f+= coul(u'MAC : ','gras') + "%s\n" %machine.mac()
778 779
    if machine.ipv6() != None:
        f+= coul(u'IPv6 : ','gras') + "%s\n" %machine.ipv6()
780

781 782 783
    if len(machine.sshFingerprint()) > 0 and aff_ssh:
        f += u"\n".join([coul(u'Fingerprint SSH : ', 'gras') + u"%s" % (i) for i in machine.sshFingerprint()])+"\n"

784 785
    # Propriétaire
    f+= coul(u'Propriétaire : ','gras')
786
    try :
787
        f += machine.proprio + coul(' (adhérent détruit)', 'jaune')
glondu's avatar
glondu committed
788
        a = AssociationCrans()
789
    except :
790 791
        a = machine.proprietaire()
        f += "%s" % a.Nom()
792
        if a.chbre() in ['EXT', '????']:
793
            f += ' (%s = %s)' % (a.idn, a.id())
794
        elif a.chbre() != 'CRA':
795 796 797
            f += " (%s)" % a.chbre()
        else :
            f += coul(u'\t\tPrise : ','gras') + machine.prise()
bernat's avatar
bernat committed
798
    f+= '\n'
799

800 801 802 803 804
    if isinstance(machine, MachineCrans):
        n = machine.nombrePrises()
        if n >= 0:
            f += coul(u'Nombre de prises : ', 'gras')
            f += "%d\n" % n
bernat's avatar
bernat committed
805

806
    # Adhérent blacklisté ?
807
    bl = a.blacklist_actif()
bernat's avatar
bernat committed
808
    if bl :
809
        f += coul(u'Restrictions sur adhérent : ','gras')
810 811
        f +=  coul(u', '.join(bl),'rouge')
        f += '\n'
812

bernat's avatar
bernat committed
813
    # Borne wifi
glondu's avatar
glondu committed
814
    if isinstance(machine, BorneWifi):
glondu's avatar
glondu committed
815
        f += coul(u'Hotspot : ', 'gras')
glondu's avatar
glondu committed
816 817 818
        f += machine.hotspot() and 'oui' or 'non'
        position = machine.position()
        if position:
819 820
            f += coul(u'\t\t\tCoordonnées : ', 'gras')
            f += u'%s°N, %s°E\n' % position
glondu's avatar
glondu committed
821
        else:
glondu's avatar
glondu committed
822
            f += '\n'
823 824

        if (machine.prise() != 'N/A'):
825 826 827 828
            try:
                f += coul(u'VLANs : ', 'gras')
                from hptools import sw_prise
                f += ', '.join(sw_prise(machine.prise()).vlans())
829
                f += '\n'
830 831
            except:
                pass
832

833 834
        f += coul(u'Puissance : ','gras') + u"%4.d" % int(machine.puissance())
        f += coul(u'\tCanaux : ', 'gras') + machine.canal()
835
        f += coul(u'\tÉtat : ', 'gras')
836 837
        if borne_etat(machine.nom()):
            f += coul(u'borne active', 'vert')
838
            f += '\n'
839 840 841 842 843
            canal_clients = borne_clients_canal(machine.nom())
            clients = canal_clients["mac-rssi"]
            canal = canal_clients["canal"]
            f += coul(u'\t         Canal courant : ', 'gras') + u"%d" % canal
            f += "\n"
glondu's avatar
glondu committed
844
            # S'il y a des clients, on les liste
845 846 847
            if clients and base:
                f += coul(u'Clients : \n','gras')
                for (client, rssi) in clients:
848
                    # On va chercher le nom correspondant à l'adresse MAC
849
                    res = base.search("mac=%s" % client)['machine']
bernat's avatar
bernat committed
850
                    authentification=""
851 852
                    if not res:
                        client_nom = '????'
dubost's avatar
dubost committed
853
                        client_chbre = '????'
854
                    else:
bernat's avatar
bernat committed
855 856
                        client_nom = ", ".join(["%s [%s]" % (x.nom().split(".")[0],
                                                             x.proprietaire().Nom()) for x in res])
857
                        # On va regarder si le client est authentifié
bernat's avatar
bernat committed
858 859 860
                        auth_ok = ipsec_ok(x)
                        if auth_ok != None:
                            if auth_ok:
bernat's avatar
bernat committed
861
                                authentification = "; IPSec: %s" % OK
bernat's avatar
bernat committed
862
                            else:
bernat's avatar
bernat committed
863
                                authentification = "; IPSec: %s" % ERREUR
864
                        client_chbre = " ".join(["%s " % (x.proprietaire().chbre()) for x in res])
865 866 867 868 869 870 871
                    # On va choisir la bonne couleur pour le RSSI
                    if rssi > -88:
                        coul_rssi = 'vert'
                    elif rssi > -93:
                        coul_rssi = 'jaune'
                    else:
                        coul_rssi = 'rouge'
dubost's avatar
dubost committed
872
                    f += u'  %s (%s Chbre : %s) (RSSI: %s%s)\n' % (client, client_nom, client_chbre,
bernat's avatar
bernat committed
873 874
                                                        coul("%d" % rssi, coul_rssi),
                                                        authentification)
875
        else:
876
            f += coul(u'borne éteinte','rouge')
877
            f += '\n'
glondu's avatar
glondu committed
878 879 880
        if machine.nvram():
            f += coul(u'NVRAM : ', 'gras')
            f += ', '.join(machine.nvram()) + '\n'
881

glondu's avatar
glondu committed
882
    if aff_ipsec and isinstance(machine, MachineWifi):
883
        f += coul(u'Clef WiFi : ','gras') + machine.ipsec()
884
        f += '\n'
885

886
    # Ports spéciaux
887 888 889 890
    if machine.portTCPin():
        f += coul(u'Ports TCP ouvert ext->machine : ','gras') + ' '.join(machine.portTCPin()) + '\n'
    if machine.portTCPout():
        f += coul(u'Ports TCP ouvert machine->ext : ','gras') + ' '.join(machine.portTCPout()) + '\n'
glondu's avatar
glondu committed
891
    if machine.portUDPin():
892 893 894
        f += coul(u'Ports UDP ouvert ext->machine : ','gras') + ' '.join(machine.portUDPin()) + '\n'
    if machine.portUDPout():
        f += coul(u'Ports UDP ouvert machine->ext : ','gras') + ' '.join(machine.portUDPout()) + '\n'
chove's avatar
chove committed
895 896 897

    # Exemption d'upload
    if machine.exempt() :
898
        f += coul(u'Upload exempté vers : ','gras') + ', '.join(machine.exempt()) + '\n'
chove's avatar
chove committed
899

bernat's avatar
bernat committed
900 901 902
    f += _blacklist(machine)
    f += _info(machine)
    f += _hist(machine)
903

bernat's avatar
bernat committed
904 905 906 907
    return f

def club_details(club) :
    """
908
    Affichage du détail des propriétés d'un club
bernat's avatar
bernat committed
909 910 911 912 913 914
    """
    f=''
    # Cid
    f+= coul(u'cid=%s   ' % club.id() ,'bleu')
    # Nom
    f += coul(u'Nom : ','gras') + "%s\n" % club.Nom()
915

916 917
    # responsale
    f += coul(u'Responsable : ','gras') + "%s\n" % club.responsable().Nom()
918

919
    # Imprimeurs
920 921 922 923
    if len(club.imprimeurs()) > 0:
        f += (coul(u'Imprimeurs : ', 'gras') + "%s\n" % ', '.join(map(lambda x:
            club.search("aid=%s" % x)['adherent'][0].Nom(), club.imprimeurs())))

924 925
    # État administratif
    f += coul(u'État administratif : ','gras')
926
    jour = True
927
    if club.adhesion() < time() and config.ann_scol not in club.paiement():
928 929
        jour = False
        f += coul(u"Non adhérent." ,'violet')
930

931
    if jour:
932
        f += coul(u"à jour",'vert')
bernat's avatar
bernat committed
933
    f += '\n'
934

bernat's avatar
bernat committed
935
    # Chambre + prise
936 937
    etat, vlans, cablage = prise_etat(club.chbre())
    f += coul(u'Local : ','gras') + u"%s " % club.local()
938
    f += u'(%s)' % etat
939
    f += u'\n'
940 941 942
    f += coul(u'Brassage : ','gras')
    f += cablage
    f += u'\n'
943 944 945 946
    # VLAN
    if vlans :
        f += coul(u'VLAN : ','gras') + u'%s' % vlans
        f += u'\n'
947

948 949
    # Adhésion
    if club.adhesion() > time():
950
        f += coul(u"Adhésion jusque %s (connexion incluse)." % (strftime("%d/%m/%Y %H:%M:%S", localtime(club.adhesion())),), "vert")
951
        f += '\n'
952
    elif config.ann_scol in club.paiement():
953
        f += coul(u"Adhésion pour l'année en cours", "vert")
954
        f += '\n'
bernat's avatar
bernat committed
955

956 957
    login = club.compte()
    if login :
958 959
        f += coul(u'Login : ','gras') + login
        alias = club.alias()
960
        if alias :
961 962
            f += coul(u'\tAlias : ','gras') + ', '.join(alias)
        f+= u'\n'
963

964 965 966 967 968 969 970 971 972 973
    # Solde
    solde = club.solde()
    if solde:
        f += coul(u'Solde : ', 'gras')
        if solde < 0:
            f+= coul(str(solde).replace('.', ','), 'rouge')
        else:
            f += str(solde).replace('.', ',')
        f += u" Euros\n"

bernat's avatar
bernat committed
974 975 976 977 978 979 980 981
    f += _blacklist(club)
    f += _info(club)
    f += _hist(club)

    # Formatage des machines aussi
    f += coul(u'Machine(s) : ','gras')
    m = club.machines()
    if m :
982
        f += '\n' + list_machines(m)
bernat's avatar
bernat committed
983
    else :
984
        f += 'aucune'
bernat's avatar
bernat committed
985

986 987 988 989 990 991 992 993
    f += u"\n"
    f += coul(u'Facture(s) : ','gras')
    m = club.factures()
    if m :
        f += u'\n' + list_factures(m)
    else :
        f += u'aucune'

bernat's avatar
bernat committed
994 995 996
    return f

###########################################
997
# Fonctions annexes de formatage de données
bernat's avatar
bernat committed
998

999
def _blacklist(clas):
bernat's avatar
bernat committed
1000
    """ Formatage blackliste de la classe fournie """
1001
    if limit_aff_blacklist == 0: return ''
bernat's avatar
bernat committed
1002
    f = u''
1003 1004 1005 1006 1007 1008
    bl = clas.blacklist()
    bl.reverse()
    nb = 0
    for event in bl:
        nb += 1
        if nb > limit_aff_blacklist: break
1009
        if is_actif(event):
1010 1011 1012 1013
            # Colorisation si sanction en cours
            c = 'rouge'
        else :
            c = 'blanc'
1014 1015 1016
        event = event.split('$')
        dates = strftime('%d/%m/%Y %H:%M', localtime(int(event[0])))
        if event[1] == '-':
1017
            dates = u'à partir du %s' % dates
1018 1019 1020 1021
        else:
            dates = u'du %s au ' % dates
            dates += strftime('%d/%m/%Y %H:%M', localtime(int(event[1])))
        f += u"%s\n\t     " % coul(u'%s : %s [%s]' % (dates, event[2], event[3]), c)
1022

1023 1024 1025 1026
    f = f[:-6] # suppression des espaces superflus

    if len(bl) > limit_aff_blacklist:
        f += '             [...]\n'
bernat's avatar
bernat committed
1027

1028
    if f:
1029
        return coul(u'Blackliste : ', 'gras') + f
1030
    else:
1031
        return ''
1032

bernat's avatar
bernat committed
1033 1034 1035 1036 1037
def _info(clas) :
    """ Formatage des remarques de la classe fournie """
    f= u''
    c = clas.info()
    if c :
1038 1039 1040
        f += coul(u'Remarque :\n   ' ,'gras')
        f += u'\n   '.join(c)
        f += u'\n'
bernat's avatar
bernat committed
1041 1042
    return f

1043
def _hist(clas):
bernat's avatar
bernat committed
1044
    """ Formatage de l'historique de la classe fournie """
1045 1046 1047
    if limit_aff_historique == 0:
        return u''
    f = u''
bernat's avatar
bernat committed
1048
    h = clas.historique()
1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
    if h:
        f += coul(u'Historique : ', 'gras')
        notfirst = False
        lim = - (limit_aff_historique + 1)
        for a in h[:lim:-1]: # les `limit_aff_historique` (au plus) derniers éléments, en partant du dernier
            if notfirst:
                f += u'             '
            else:
                notfirst = True
            try:
                # TODO: clarifier ça, a priori `a` c'est déjà un unicode
                # on force l'encodage ici sinon il est fait au moment de l'impression a
                # l'ecran et il empeche l'affichage de toutes les infos
                f += u'%s\n' % a
            except Exception, e:
                if debug:
                    f += coul(u'*** non affichable [%s] ***\n' % str(e), 'rouge')
                else:
                    f += coul(u'*** non affichable ***\n', 'rouge')
        if