[stats_cableurs] Rcriture du script

Ignore-this: 61894b618f182f8d08129c2ba96a5ee1
 * ne marche pas en unicode
 * ne marche pas sous python 2.4

darcs-hash:20090329213507-bd074-27b2cad7615c89c4dfbd4ecf6d487d63aada99d0.gz
This commit is contained in:
Antoine Durand-Gasselin 2009-03-29 23:35:07 +02:00
parent f1cb76f449
commit 8036930aa5

View file

@ -23,10 +23,14 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. # Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
import sys, re, datetime, time import sys, re, datetime
from optparse import OptionParser
sys.path.append("/usr/scripts/gestion/") sys.path.append("/usr/scripts/gestion/")
from ldap_crans import CransLdap from ldap_crans import CransLdap
from affich_tools import cprint from config import ann_scol
db = CransLdap() db = CransLdap()
@ -42,7 +46,8 @@ def parse_ligne_histo(ligne):
return date, champ return date, champ
def parse_historique(truc): def parse_historique(truc):
u"""Parse l'historique""" u"""Parse l'historique et renvoie la liste des actions sous la forme
(['dd', 'mm', 'yyyy'], ['date', 'heure', 'who', 'what'])"""
return [ parse_ligne_histo(ligne) for ligne in truc.historique() ] return [ parse_ligne_histo(ligne) for ligne in truc.historique() ]
def dec_date(date): def dec_date(date):
@ -54,12 +59,21 @@ def dec_date(date):
class StatsCableursBase: class StatsCableursBase:
u"""Classe prototype pour afficher des stats de câbleurs.""" u"""Classe prototype pour afficher des stats de câbleurs."""
colonnes = [('score', u'Son score')] colonnes = []
nbmois = 4 nbmois = 4
stats = {} stats = {}
score = {}
kwargs = {}
def __init__(self, mois = -4): def __init__(self, **kwargs):
self.kwargs.update(kwargs)
self.affiche (self.kwargs, 'd')
# On regarde s'il y a un mois à partir duquel on veuille partir.
mois = kwargs.get("mois", -4)
# On compte ensuite combien de mois ça fait
if mois > 0: if mois > 0:
# si on demande depuis 09 (septembre) , on remonte # si on demande depuis 09 (septembre) , on remonte
# jusqu'à septembre # jusqu'à septembre
@ -69,50 +83,87 @@ class StatsCableursBase:
# mois # mois
self.nbmois = - mois self.nbmois = - mois
# On récupère les statistiques # On regarde si on avait pas déjà précisé sur combien de mois on
# voulait partir.
self.nbmois = kwargs.get("nbmois", self.nbmois)
# bout de code à mon avis explicite
self.def_columns()
self.affiche("Génération des stats")
self.get_stats() self.get_stats()
self.calc_score()
cableurs = self.sort_cableurs()
self.print_stats(cableurs)
# On les affiche
self.print_stats()
def def_columns(self):
u"""Fonction qui sera appelée pour éventuellement mettre à jour
self.colonnes"""
pass
def get_stats(self): def get_stats(self):
u"""Génération dummy de statistiques""" u"""Fonction censée récupérer les stats des différents câbleurs,
self.stats = {} et les fouttre dans self.stats"""
raise NotImplementedError
def print_stats(self): def calc_score(self):
u"""Fonction censée calculer le score en fonction des stats, et
le mettre dans self.score"""
raise NotImplementedError
def sort_cableurs(self):
u"""Renvoie la liste des câbleurs triés, en fonction de leur score"""
cableurs = self.stats.keys()
cableurs.sort(lambda x, y : cmp(self.score[x], self.score[y]), reverse=True)
if self.kwargs.get('top', 10) != 0:
cableurs = cableurs[:self.kwargs.get('top', 10)]
return cableurs
def print_stats(self, cableurs):
u"""Affiche pour chaque cableur les valeurs de ses différentes colonnes u"""Affiche pour chaque cableur les valeurs de ses différentes colonnes
définies dans self.colonnes.""" définies dans self.colonnes."""
stats = self.stats
cableurs = stats.keys()
### Si quelqu'un trouve une manière plus élégante de faire… # On génère la liste des noms sous lesquels on va afficher nos câbleurs
try: noms = {}
cableurs.sort(lambda x, y : cmp(stats[x]['score'], stats[y]['score']), reverse=True) for cableur in cableurs:
except TypeError: try:
cableurs.sort(lambda x, y : cmp(stats[x][0], stats[y][0]), reverse=True) ldap_cableur = db.search('login=%s' % cableur)['adherent'][0]
ids = { 'nom' : ldap_cableur.nom(),
'prenom' : ldap_cableur.prenom(),
'droits' : reduce(unicode.__add__,
[d[0] for d in ldap_cableur.droits()], u''),
'login' : cableur }
noms[cableur] = self.kwargs.get("fqn", "%(prenom)s %(nom)s") % ids
except IndexError:
self.affiche ("Unknown cableur %s!" % cableur, 'd')
noms[cableur] = cableur+'*'
long_max = reduce(lambda m, c : max(m, len(c)), cableurs, len('cableur')) # On récupère la liste des câbleurs, triés en fonction de leur score
long_max = reduce(lambda m, c : max(m, len(c)), noms.values(), len('cableur'))
# Titres des colonnes # Titres des colonnes
ligne = "%-*s" % (long_max, u'Câbleur') ligne = "%-*s" % (long_max, u'Câbleur')
for typ, nom in self.colonnes: ligne += " | Score"
ligne += " | %s" % nom for col in self.colonnes:
cprint(ligne) ligne += " | %s" % col
print(ligne)
# Ligne pour délimiter # Ligne pour délimiter
ligne = ''.center(long_max, '-') ligne = '-' * long_max
for typ, nom in self.colonnes: ligne += "-+-%s" % ('-' * len ("score"))
ligne += "-+-%s" % ''.center(len(nom), '-') for col in self.colonnes:
cprint(ligne) ligne += "-+-%s" % ('-' * len(col))
print(ligne)
# Statiqtiques par câbleur # Statiqtiques par câbleur
for cableur in cableurs: for cableur in cableurs:
acts = stats[cableur] acts = self.stats[cableur]
ligne = "%-*s" % (long_max, cableur) ligne = "%-*s" % (long_max, noms[cableur])
for typ, nom in self.colonnes: ligne += " | %*d" % (len ("score"), self.score[cableur])
ligne += " | %*d" % (len(nom), acts[typ]) for i in range (len (self.colonnes)):
cprint(ligne) ligne += " | %*d" % (len(self.colonnes[i]), acts[i])
print(ligne)
def sort_events(self, events): def sort_events(self, events):
u"""Split une liste d'évènements selon les mois""" u"""Split une liste d'évènements selon les mois"""
@ -129,12 +180,25 @@ class StatsCableursBase:
sorted_events.append(events) sorted_events.append(events)
return sorted_events return sorted_events
def affiche(self, msg, reqs= 'v'):
u"""Fonction de print qui n'affiche que si on est dans un mode
kikoolol, debug ou verbose suffisamment avancé."""
try:
pool = list (self.kwargs.get('flags', ''))
for i in reqs:
pool.remove(i)
print msg
except ValueError:
return
class StatsCableursMachines(StatsCableursBase): class StatsCableursMachines(StatsCableursBase):
u"""Cette classe s'occupe de recenser l'activité des différents 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âbleurs sur les machines, ce qui représente le travail typique du
câbleur en milieu d'année""" câbleur en milieu d'année"""
kwargs = { 'fqn' : '%(login)s' , 'top' : 0 }
def ajoute_actions(self, machine, hist): def ajoute_actions(self, machine, hist):
u"""Ajoute dans hist la liste des actions effectués par u"""Ajoute dans hist la liste des actions effectués par
chacuns des câbleurs sur la machine pendant les mois.""" chacuns des câbleurs sur la machine pendant les mois."""
@ -152,6 +216,18 @@ class StatsCableursMachines(StatsCableursBase):
except KeyError: except KeyError:
hist[champ[2]] = [ (date, champ[3]) ] hist[champ[2]] = [ (date, champ[3]) ]
def def_columns(self):
u"""Simplement une par mois, plus une pour toutes les actions
d'avant."""
month = datetime.date.replace(datetime.date.today(), day=1)
for i in range(self.nbmois):
self.colonnes.append("%d/%02d" % (month.month, month.year - 2000))
month = dec_date(month)
self.colonnes.append("< " + self.colonnes[-1])
def get_stats(self): def get_stats(self):
u"""Récupère les statistiques des différents câbleurs sur les u"""Récupère les statistiques des différents câbleurs sur les
différentes machines""" différentes machines"""
@ -178,151 +254,139 @@ class StatsCableursMachines(StatsCableursBase):
for cableur in hists.keys(): for cableur in hists.keys():
split_hist[cableur] = self.sort_events(hists[cableur]) split_hist[cableur] = self.sort_events(hists[cableur])
self.stats[cableur] = [ len(i) for i in split_hist[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) def calc_score(self):
self.colonnes.append( (self.nbmois +1, "< " + self.colonnes[-1][1]) ) u"""Le calcul se fait simplement comme la somme des actions
(indépendamment de leur type pour l'instant) sur les mois
concernés."""
for cableur in self.stats.keys():
self.score[cableur] = sum (self.stats[cableur][:-1])
class StatsCableursAdherents(StatsCableursBase):
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 = [ u'Inscriptions', u'Réinscriptions' ]
debut_ann_scol = datetime.date(ann_scol, 8, 1)
paiement_ann_scol = "paiement+%d" % ann_scol
def donne_cableur(self, adherent):
u""" Cherche le cableur qui a inscrit ou réinscrit un adhérent. """
# Est-ce qu'on recherche une inscription ou une reinscription?
date_inscr = datetime.date.fromtimestamp(adherent.dateInscription())
if date_inscr < self.debut_ann_scol:
action = 1 # 'reinscription'
action_filtre = self.paiement_ann_scol
else:
action = 0 # 'inscription'
action_filtre = 'inscription'
for date, champ in parse_historique(adherent):
if date >= self.debut_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 get_stats(self):
u"""Calcule le nombre d'inscriptions et réinscription pour tous
les cableurs ayant inscrit ou réinscrit quelqu'un pour l'année
en cours."""
liste = db.search("paiement=%s" % ann_scol)['adherent']
for adherent in liste:
action, cableur = self.donne_cableur(adherent)
if cableur:
if not self.stats.has_key(cableur):
self.stats[cableur] = [0, 0]
self.stats[cableur][action] += 1
def calc_score(self):
u"""Calcule le score d'un câbleur, en fonction du nombre
d'adhésions qu'il a effectué"""
# On calcule le score total pour chaque cableur
for cableur in self.stats.keys():
self.score[cableur] = 2 * self.stats[cableur][0] + self.stats[cableur][1]
def update_fqn(option, opt_str, value, this, fmt):
u"""Utilisée dans le parser, cette fonction permet de mettre à jour
correctement la chaîne de format"""
try:
if opt_str not in ['-d', '--droits']:
if re.match ('^ \(%\(droits\)s\)', this.values.fqn):
this.values.fqn = fmt + this.values.fqn
else:
this.values.fqn = this.values.fqn + (' (%s)' % fmt)
else:
this.values.fqn = this.values.fqn + fmt
except TypeError:
this.values.fqn = fmt
def notimplerr(option, opt_str, value, parseur):
u"""Un callback pour le parser pour renvoyer un NotImplementedError"""
raise NotImplementedError
if __name__ == "__main__": if __name__ == "__main__":
StatsCableursMachines() usage = "usage: %prog [options]\n %prog [options] [--efficiency -e]"
parser = OptionParser(usage=usage)
## class StatsCableursAdherents: # options pour décider le nombre de câbleurs à afficher
## u"""Cette classe s'occupe de recenser les actions que les câbleurs parser.add_option('-a', '--all', help= u"Affiche tous les câbleurs",
## font sur les adhérents (en fait les {,re}adhésions).""" action='store_const', const=0, dest='top')
## parser.add_option('-t', '--top', metavar= "NB", help= u"N'affiche que les NB meilleurs câbleurs",
## colonnes = [('inscription', u'inscription'), type= 'int', dest='top')
## ('reinscription', u'réinscription'),
## ('total', u'score total')] # options pour le format d'affichage des câbleurs
## parser.add_option('-d', '--droits', help= u"Affiche les droits du câbleur",
## date_debut_ann_scol = time.mktime((ann_scol, 8, 1, 0, 0, 0, 0, 0, 0)) action='callback', callback= update_fqn, callback_kwargs= { 'fmt': ' (%(droits)s)'})
## paiement_ann_scol = "paiement+%d" % ann_scol parser.add_option('-F', '--full-name', help=u"Affiche Prenom Nom des câbleurs",
## action='callback', callback= update_fqn, callback_kwargs= { 'fmt': '%(prenom)s %(nom)s'})
## def donne_cableur(mois, adherent): parser.add_option('-l', '--login', help= u"Affiche le login des câbleurs",
## u""" Cherche le cableur qui a inscrit ou réinscrit un adhérent. """ action='callback', callback= update_fqn, callback_kwargs= { 'fmt': '%(login)s'})
## cableur = None parser.add_option('--fqn', dest='fqn',
## help= u"Définit le format d'affichage du nom du câbleur, les champs possibles sont %(droits)s, %(nom)s, %(prenom)s, %(login)s")
## # Est-ce qu'on recherche une inscription ou une reinscription?
## if adherent.dateInscription() < self.date_debut_ann_scol: # options de verbosité
## action = 'reinscription' parser.add_option('-D', '--debug', help= u"Affiche des informations de debuggage",
## action_filtre = self.paiement_ann_scol action='append_const', const='d', dest= 'flags')
## else: parser.add_option('-k', '--kikoolol', help="Affiche des trucs kikoolol",
## action = 'inscription' action='callback', callback= notimplerr)
## action_filtre = 'inscription' parser.add_option('-v', '--verbose', help= u"Augmente la verbosité",
## action='append_const', const= 'd', dest= 'flags')
## for hist in adherent.historique():
## # On decoupe pour avoir un truc utilisable # options sur la durée étudiée
## champ = hist.replace(',', '').replace(': ', '').split(' ') parser.add_option('-f', '--for', metavar= 'N', help= u"Affiche les statistiqes depuis N mois",
## # On inspecte la date type= 'int', dest='nbmois')
## date = champ[0].split('/') parser.add_option('-s', '--since', metavar= 'MM', help= u'Affiche les stats depuis MOIS',
## if int(date[1]) in mois and int(date[2]) == ann_scol: type= 'int', dest='mois')
## # Maintenant on regarde si l'action recherchee est ici
## if action_filtre in champ[3:]: # Devrait plutôt être un argument qu'une option
## return action, champ[2] parser.add_option('-e', '--efficiency', help= u"Compte les actions effectuées sur les machines",
## action= 'store_const', const= StatsCableursMachines, dest= 'default_stats',
## return action, None default= StatsCableursAdherents)
##
## def calcul_score(mois): # on parse, enfin, on laisse optparse le faire pour nous
## u""" Calcul le score de tous les cableurs ayant inscrit ou (options, args) = parser.parse_args()
## réinscrit quelqu'un pour l'année en cours. """
## # Parce que les clefs ont par défaut la valeur None
## liste = db.search("paiement=2008")['adherent'] kwargs = {}
## for lbl, field in options.__dict__.items():
## score = {} if field != None and lbl != 'default_stats':
## for adherent in liste: kwargs[lbl] = field
## action, cableur = donne_cableur(mois, adherent)
## if cableur: # On apelle les stats que l'on veut calculer
## if not score.has_key(cableur): options.default_stats(**kwargs)
## 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)