[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
# 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/")
from ldap_crans import CransLdap
from affich_tools import cprint
from config import ann_scol
db = CransLdap()
@ -42,7 +46,8 @@ def parse_ligne_histo(ligne):
return date, champ
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() ]
def dec_date(date):
@ -54,12 +59,21 @@ def dec_date(date):
class StatsCableursBase:
u"""Classe prototype pour afficher des stats de câbleurs."""
colonnes = [('score', u'Son score')]
colonnes = []
nbmois = 4
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:
# si on demande depuis 09 (septembre) , on remonte
# jusqu'à septembre
@ -69,50 +83,87 @@ class StatsCableursBase:
# 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.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):
u"""Génération dummy de statistiques"""
self.stats = {}
u"""Fonction censée récupérer les stats des différents câbleurs,
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
définies dans self.colonnes."""
stats = self.stats
cableurs = stats.keys()
### 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)
# On génère la liste des noms sous lesquels on va afficher nos câbleurs
noms = {}
for cableur in cableurs:
try:
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
ligne = "%-*s" % (long_max, u'Câbleur')
for typ, nom in self.colonnes:
ligne += " | %s" % nom
cprint(ligne)
ligne += " | Score"
for col in self.colonnes:
ligne += " | %s" % col
print(ligne)
# Ligne pour délimiter
ligne = ''.center(long_max, '-')
for typ, nom in self.colonnes:
ligne += "-+-%s" % ''.center(len(nom), '-')
cprint(ligne)
ligne = '-' * long_max
ligne += "-+-%s" % ('-' * len ("score"))
for col in self.colonnes:
ligne += "-+-%s" % ('-' * len(col))
print(ligne)
# Statiqtiques par câbleur
for cableur in cableurs:
acts = stats[cableur]
ligne = "%-*s" % (long_max, cableur)
for typ, nom in self.colonnes:
ligne += " | %*d" % (len(nom), acts[typ])
cprint(ligne)
acts = self.stats[cableur]
ligne = "%-*s" % (long_max, noms[cableur])
ligne += " | %*d" % (len ("score"), self.score[cableur])
for i in range (len (self.colonnes)):
ligne += " | %*d" % (len(self.colonnes[i]), acts[i])
print(ligne)
def sort_events(self, events):
u"""Split une liste d'évènements selon les mois"""
@ -129,12 +180,25 @@ class StatsCableursBase:
sorted_events.append(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):
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"""
kwargs = { 'fqn' : '%(login)s' , 'top' : 0 }
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."""
@ -152,6 +216,18 @@ class StatsCableursMachines(StatsCableursBase):
except KeyError:
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):
u"""Récupère les statistiques des différents câbleurs sur les
différentes machines"""
@ -178,151 +254,139 @@ class StatsCableursMachines(StatsCableursBase):
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]) )
def calc_score(self):
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__":
StatsCableursMachines()
usage = "usage: %prog [options]\n %prog [options] [--efficiency -e]"
parser = OptionParser(usage=usage)
## 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)
# options pour décider le nombre de câbleurs à afficher
parser.add_option('-a', '--all', help= u"Affiche tous les câbleurs",
action='store_const', const=0, dest='top')
parser.add_option('-t', '--top', metavar= "NB", help= u"N'affiche que les NB meilleurs câbleurs",
type= 'int', dest='top')
# options pour le format d'affichage des câbleurs
parser.add_option('-d', '--droits', help= u"Affiche les droits du câbleur",
action='callback', callback= update_fqn, callback_kwargs= { 'fmt': ' (%(droits)s)'})
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'})
parser.add_option('-l', '--login', help= u"Affiche le login des câbleurs",
action='callback', callback= update_fqn, callback_kwargs= { 'fmt': '%(login)s'})
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")
# options de verbosité
parser.add_option('-D', '--debug', help= u"Affiche des informations de debuggage",
action='append_const', const='d', dest= 'flags')
parser.add_option('-k', '--kikoolol', help="Affiche des trucs kikoolol",
action='callback', callback= notimplerr)
parser.add_option('-v', '--verbose', help= u"Augmente la verbosité",
action='append_const', const= 'd', dest= 'flags')
# options sur la durée étudiée
parser.add_option('-f', '--for', metavar= 'N', help= u"Affiche les statistiqes depuis N mois",
type= 'int', dest='nbmois')
parser.add_option('-s', '--since', metavar= 'MM', help= u'Affiche les stats depuis MOIS',
type= 'int', dest='mois')
# Devrait plutôt être un argument qu'une option
parser.add_option('-e', '--efficiency', help= u"Compte les actions effectuées sur les machines",
action= 'store_const', const= StatsCableursMachines, dest= 'default_stats',
default= StatsCableursAdherents)
# on parse, enfin, on laisse optparse le faire pour nous
(options, args) = parser.parse_args()
# Parce que les clefs ont par défaut la valeur None
kwargs = {}
for lbl, field in options.__dict__.items():
if field != None and lbl != 'default_stats':
kwargs[lbl] = field
# On apelle les stats que l'on veut calculer
options.default_stats(**kwargs)