diff --git a/stats_cableurs.py b/stats_cableurs.py index a6f398c9..6b5b66ec 100644 --- a/stats_cableurs.py +++ b/stats_cableurs.py @@ -1,8 +1,6 @@ #!/usr/bin/python # -*- mode: python; coding: utf-8 -*- # -# $Id: stats_cableurs.py,v 1.2 2007-09-29 17:50:09 dimino Exp $ -# # stats_cableurs.py # ----------------- # @@ -25,228 +23,306 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. -import sys, re +import sys, re, datetime, time sys.path.append("/usr/scripts/gestion/") -from ldap_crans import crans_ldap -from config import ann_scol -from affich_tools import cprint, anim -import time +from ldap_crans import CransLdap +from affich_tools import cprint -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 +db = CransLdap() + +__DAYS_IN_PREVIOUS_MONTH = [datetime.timedelta(days) for days in + [ 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 ] ] + +def parse_ligne_histo(ligne): + u"""Parse une ligne d'historique et renvoie (['dd', 'mm', 'yyyy'], + ['date', 'heure', 'who', 'what'])""" + champ = ligne.replace(',', '').replace(': ', '').split(' ') + sdate = champ[0].split('/') + date = datetime.date(int(sdate[2]), int(sdate[1]), int(sdate[0])) + return date, champ + +def parse_historique(truc): + u"""Parse l'historique""" + return [ parse_ligne_histo(ligne) for ligne in truc.historique() ] + +def dec_date(date): + u"""retranche un mois à la date.""" + return (date - __DAYS_IN_PREVIOUS_MONTH[date.month -1]) -class ActionsCableurs: - u"""Cette classe s'occupe de recenser l'activité des différents - câbleurs.""" +class StatsCableursBase: + u"""Classe prototype pour afficher des stats de câbleurs.""" + + colonnes = [('score', u'Son score')] + nbmois = 4 stats = {} - sablier = None - def __init__(self, mois): + def __init__(self, mois = -4): - self.sablier = anim(u"Récupération de la liste des câbleurs") - ldap_cableurs = db.search("droits=cableur")['adherent'] - for i in ldap_cableurs: - self.stats[i.nom()] = [] - self.sablier.reinit() - cprint('OK', 'vert') + if mois > 0: + # si on demande depuis 09 (septembre) , on remonte + # jusqu'à septembre + self.nbmois = (datetime.date.today().month + 12 - mois) % 12 + else: + # si on demande les stats sur 4 mois, on remonte sur 4 + # mois + self.nbmois = - mois - self.get_stats(mois) - self.print_results() + # On récupère les statistiques + self.get_stats() - def ajoute_actions(self, mois, machine): - u"""Ajoute dans les stats la liste des actions effectués par - chacuns des câbleurs sur la machine pendant les mois.""" + # On les affiche + self.print_stats() - for hist in machine.historique(): - # On decoupe pour avoir un truc utilisable - champ = hist.replace(',', '').replace(': ', '').split(' ') - date = champ[0].split('/') - # On inspecte la date - if (int (date[1]), int(date[2])) in mois: - if self.stats.has_key(champ[2]): - self.stats[champ[2]].append(champ[3]) - else: - self.stats[champ[2]] = [champ[3]] - def get_stats(self, mois): - u"""Récupère les statistiques des différents câbleurs sur les - différentes machines""" + def get_stats(self): + u"""Génération dummy de statistiques""" + self.stats = {} - self.sablier = anim(u"Récupération de la liste des machines") - all_machines = db.search('host=*') - self.sablier.cycle() - machines = all_machines['machineFixe'] + all_machines['machineWifi'] - self.sablier.reinit() - cprint('OK', 'vert') + def print_stats(self): + u"""Affiche pour chaque cableur les valeurs de ses différentes colonnes + définies dans self.colonnes.""" + stats = self.stats + cableurs = stats.keys() - self.sablier = anim(u"Récupération des actions sur les machines", iter = len (machines)) - for becane in machines: - self.ajoute_actions(mois, becane) - self.sablier.cycle() - self.sablier.reinit() - cprint('OK', 'vert') + ### Si quelqu'un trouve une manière plus élégante de faire… + try: + cableurs.sort(lambda x, y : cmp(stats[x]['score'], stats[y]['score']), reverse=True) + except TypeError: + cableurs.sort(lambda x, y : cmp(stats[x][0], stats[y][0]), reverse=True) - def print_results(self, nombre= 0): - u"""Affiche les statistiques d'activité des différents câbleurs""" - - cableurs = self.stats.keys() - cableurs.sort(lambda x, y : cmp(len (self.stats[x]), len (self.stats[y])), reverse=True) long_max = reduce(lambda m, c : max(m, len(c)), cableurs, len('cableur')) - colonnes = [('actions', u'Actions'), - ('ins', u'Inscription'), - ('mac', u'Change MAC'), - ('host', u'Change DNS'), - ('ip', u'Change IP')] - # Titres des colonnes ligne = "%-*s" % (long_max, u'Câbleur') - for typ, nom in colonnes: + for typ, nom in self.colonnes: ligne += " | %s" % nom cprint(ligne) # Ligne pour délimiter ligne = ''.center(long_max, '-') - for typ, nom in colonnes: + for typ, nom in self.colonnes: ligne += "-+-%s" % ''.center(len(nom), '-') cprint(ligne) # Statiqtiques par câbleur for cableur in cableurs: - acts = self.stats[cableur] - score_cableur = {} - score_cableur['actions'] = len (acts) - for key, chaine in colonnes[1:]: - score_cableur[key] = len ([i for i in acts if re.search(key, i)]) + acts = stats[cableur] ligne = "%-*s" % (long_max, cableur) - for typ, nom in colonnes: - ligne += " | %*d" % (len(nom), score_cableur[typ]) + for typ, nom in self.colonnes: + ligne += " | %*d" % (len(nom), acts[typ]) cprint(ligne) + def sort_events(self, events): + u"""Split une liste d'évènements selon les mois""" + sorted_events = [] + since = datetime.date.replace(datetime.date.today(), day = 1) + backwards = self.nbmois -def donne_cableur(mois, adherent): - u""" Cherche le cableur qui a inscrit ou réinscrit un adhérent. """ - cableur = None - # Est-ce qu'on recherche une inscription ou une reinscription? - if adherent.dateInscription() < date_debut_ann_scol: - action = 'reinscription' - action_filtre = paiement_ann_scol - else: - action = 'inscription' - action_filtre = 'inscription' + while backwards > 0: + sorted_events.append([ ev for ev in events if ev[0] >= since ]) + events = [ ev for ev in events if ev[0] < since ] + backwards -= 1 + since = dec_date(since) - for hist in adherent.historique(): - # On decoupe pour avoir un truc utilisable - champ = hist.replace(',', '').replace(': ', '').split(' ') - # On inspecte la date - date = champ[0].split('/') - if int(date[1]) in mois and int(date[2]) == ann_scol: - # Maintenant on regarde si l'action recherchee est ici - if action_filtre in champ[3:]: - return action, champ[2] + sorted_events.append(events) + return sorted_events - return action, None -def calcul_score(mois): - u""" Calcul le score de tous les cableurs ayant inscrit ou - réinscrit quelqu'un pour l'année en cours. """ +class StatsCableursMachines(StatsCableursBase): + u"""Cette classe s'occupe de recenser l'activité des différents + câbleurs sur les machines, ce qui représente le travail typique du + câbleur en milieu d'année""" - liste = db.search("paiement=2008")['adherent'] + def ajoute_actions(self, machine, hist): + u"""Ajoute dans hist la liste des actions effectués par + chacuns des câbleurs sur la machine pendant les mois.""" - score = {} - for adherent in liste: - action, cableur = donne_cableur(mois, adherent) - if cableur: - if not score.has_key(cableur): - score[cableur] = { 'inscription': 0, - 'reinscription': 0 } - score[cableur][action] += 1 + for date, champ in parse_historique(machine): - # On calcul le score total pour chaque cableur - for s in score.values(): - s['total'] = 2 * s['inscription'] + s['reinscription'] + # on ne va pas non plus compter les actions qu'on fait sur + # ses propres machines. + if machine.proprietaire().compte() != champ[2]: - return score + # Il se peut qu'une personne sans les droits câbleur ait + # effectué une action. + try: + hist[champ[2]].append( (date, champ[3]) ) + except KeyError: + hist[champ[2]] = [ (date, champ[3]) ] -def classe(score={}): - u""" Retourne la liste des câbleurs classé par score total décroisant. """ - cableurs = score.keys() - cableurs.sort(lambda x, y : cmp(score[x]['total'], score[y]['total']), reverse=True) - return cableurs + def get_stats(self): + u"""Récupère les statistiques des différents câbleurs sur les + différentes machines""" + + # La liste des câbleurs en tant qu'entité ldap, histoire de bien + # faire apparaître ceux qui n'ont rien fait + ldap_cableurs = db.search("droits=cableur")['adherent'] + + # On récupère la liste des machines + all_machines = db.search('mid=*') + machines = all_machines['machineFixe'] + all_machines['machineWifi'] + + # hists permet de répartir toutes les actions sur les différents + # câbleurs. + hists = {} + for i in ldap_cableurs: + hists[i.compte()] = [] + + # on récupère l'historique des machines que l'on met dans hists + for becane in machines: + self.ajoute_actions(becane, hists) + + split_hist = {} + for cableur in hists.keys(): + split_hist[cableur] = self.sort_events(hists[cableur]) + self.stats[cableur] = [ len(i) for i in split_hist[cableur] ] + score = sum (self.stats[cableur][:-1]) + self.stats[cableur].insert(0, score) + + month = datetime.date.today() + for i in range(self.nbmois): + self.colonnes.append( (i + 1, "%d/%02d" % (month.month, month.year - 2000))) + month = dec_date(month) + + self.colonnes[0] = (0, "%d last" % self.nbmois) + self.colonnes.append( (self.nbmois +1, "< " + self.colonnes[-1][1]) ) if __name__ == "__main__": - nb_affiche = 10 - mois = range(8, 11) - if len(sys.argv) > 1: - if sys.argv[1] == '-e': - mois = range(8, 13) - if len(sys.argv) > 2: - try: - nb_affiche = int(sys.argv[2]) - except ValueError: - nb_affiche = 10 - else: - try: - nb_affiche = int(sys.argv[1]) - except ValueError: - nb_affiche = 10 + StatsCableursMachines() - if sys.argv[1] == '--efficiency': - ActionsCableurs([(1, 2009), (2, 2009), (3, 2009)]) - sys.exit(0) - - score = calcul_score(mois) - classement = classe(score) - - if nb_affiche > 0: - classement = classement[0:nb_affiche] - - # On cherche les noms des câbleurs parceque c'est quand même mieux - nom_reel = {} - for cableur in classement: - nom_reel[cableur] = db.search('uid=%s' % cableur)['adherent'][0].Nom() - - # Calcul des statistiques - total_inscription = 0 - total_reinscription = 0 - total = 0 - for s in score.values(): - total_inscription += s['inscription'] - total_reinscription += s['reinscription'] - total += s['total'] - cprint(u"""Statistiques globales: -- inscriptions: %(inscription)d -- réinscription: %(reinscription)d -- total: %(total)d - -""" % { 'inscription': total_inscription, - 'reinscription': total_reinscription, - 'total': total }, newline=False) - - # Calcul la longueur du nom le plus long - long_max = reduce(lambda m, c : max(m, len(c)), nom_reel.values(), len('cableur')) - - colonnes = [('inscription', u'inscription'), - ('reinscription', u'réinscription'), - ('total', u'score total')] - - # Titres des colonnes - ligne = "%-*s" % (long_max, u'câbleur') - for typ, nom in colonnes: - ligne += " | %s" % nom - cprint(ligne) - # Ligne pour délimiter - ligne = ''.center(long_max, '-') - for typ, nom in colonnes: - ligne += "-+-%s" % ''.center(len(nom), '-') - cprint(ligne) - # Statiqtiques par câbleur - for cableur in classement: - score_cableur = score[cableur] - ligne = "%-*s" % (long_max, nom_reel[cableur]) - for typ, nom in colonnes: - ligne += " | %*d" % (len(nom), score_cableur[typ]) - cprint(ligne) +## class StatsCableursAdherents: +## u"""Cette classe s'occupe de recenser les actions que les câbleurs +## font sur les adhérents (en fait les {,re}adhésions).""" +## +## colonnes = [('inscription', u'inscription'), +## ('reinscription', u'réinscription'), +## ('total', u'score total')] +## +## date_debut_ann_scol = time.mktime((ann_scol, 8, 1, 0, 0, 0, 0, 0, 0)) +## paiement_ann_scol = "paiement+%d" % ann_scol +## +## def donne_cableur(mois, adherent): +## u""" Cherche le cableur qui a inscrit ou réinscrit un adhérent. """ +## cableur = None +## +## # Est-ce qu'on recherche une inscription ou une reinscription? +## if adherent.dateInscription() < self.date_debut_ann_scol: +## action = 'reinscription' +## action_filtre = self.paiement_ann_scol +## else: +## action = 'inscription' +## action_filtre = 'inscription' +## +## for hist in adherent.historique(): +## # On decoupe pour avoir un truc utilisable +## champ = hist.replace(',', '').replace(': ', '').split(' ') +## # On inspecte la date +## date = champ[0].split('/') +## if int(date[1]) in mois and int(date[2]) == ann_scol: +## # Maintenant on regarde si l'action recherchee est ici +## if action_filtre in champ[3:]: +## return action, champ[2] +## +## return action, None +## +## def calcul_score(mois): +## u""" Calcul le score de tous les cableurs ayant inscrit ou +## réinscrit quelqu'un pour l'année en cours. """ +## +## liste = db.search("paiement=2008")['adherent'] +## +## score = {} +## for adherent in liste: +## action, cableur = donne_cableur(mois, adherent) +## if cableur: +## if not score.has_key(cableur): +## score[cableur] = { 'inscription': 0, +## 'reinscription': 0 } +## score[cableur][action] += 1 +## +## # On calcul le score total pour chaque cableur +## for s in score.values(): +## s['total'] = 2 * s['inscription'] + s['reinscription'] +## +## return score +## +## def classement(score={}): +## u""" Retourne la liste des câbleurs classé par score total décroisant. """ +## cableurs = score.keys() +## cableurs.sort(lambda x, y : cmp(score[x]['total'], score[y]['total']), reverse=True) +## return cableurs +## +## +## +## if __name__ == "__main__": +## nb_affiche = 10 +## mois = range(8, 11) +## if len(sys.argv) > 1: +## if sys.argv[1] == '-e': +## mois = range(8, 13) +## if len(sys.argv) > 2: +## try: +## nb_affiche = int(sys.argv[2]) +## except ValueError: +## nb_affiche = 10 +## else: +## try: +## nb_affiche = int(sys.argv[1]) +## except ValueError: +## nb_affiche = 10 +## +## if sys.argv[1] == '--efficiency': +## ActionsCableurs([(1, 2009), (2, 2009), (3, 2009)]) +## sys.exit(0) +## +## score = calcul_score(mois) +## classement = classe(score) +## +## if nb_affiche > 0: +## classement = classement[0:nb_affiche] +## +## # On cherche les noms des câbleurs parceque c'est quand même mieux +## nom_reel = {} +## for cableur in classement: +## nom_reel[cableur] = db.search('uid=%s' % cableur)['adherent'][0].Nom() +## +## # Calcul des statistiques +## total_inscription = 0 +## total_reinscription = 0 +## total = 0 +## for s in score.values(): +## total_inscription += s['inscription'] +## total_reinscription += s['reinscription'] +## total += s['total'] +## cprint(u"""Statistiques globales: +## - inscriptions: %(inscription)d +## - réinscription: %(reinscription)d +## - total: %(total)d +## +## """ % { 'inscription': total_inscription, +## 'reinscription': total_reinscription, +## 'total': total }, newline=False) +## +## # Calcul la longueur du nom le plus long +## long_max = reduce(lambda m, c : max(m, len(c)), nom_reel.values(), len('cableur')) +## +## +## # Titres des colonnes +## ligne = "%-*s" % (long_max, u'câbleur') +## for typ, nom in colonnes: +## ligne += " | %s" % nom +## cprint(ligne) +## # Ligne pour délimiter +## ligne = ''.center(long_max, '-') +## for typ, nom in colonnes: +## ligne += "-+-%s" % ''.center(len(nom), '-') +## cprint(ligne) +## # Statiqtiques par câbleur +## for cableur in classement: +## score_cableur = score[cableur] +## ligne = "%-*s" % (long_max, nom_reel[cableur]) +## for typ, nom in colonnes: +## ligne += " | %*d" % (len(nom), score_cableur[typ]) +## cprint(ligne)