1
|
#!/usr/bin/python
|
2
|
# -*- coding: utf-8 -*-
|
3
|
|
4
|
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
5
|
# Version 2, December 2004
|
6
|
#
|
7
|
# Copyright (C) 2010 François Poulain <fpoulain@metrodore.fr>
|
8
|
#
|
9
|
# Everyone is permitted to copy and distribute verbatim or modified
|
10
|
# copies of this license document, and changing it is allowed as long
|
11
|
# as the name is changed.
|
12
|
#
|
13
|
# DO WHAT THE FUCK YOU WANT TO PUBLIC LICENCE
|
14
|
# TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
15
|
#
|
16
|
# 0. You just DO WHAT THE FUCK YOU WANT TO.
|
17
|
|
18
|
|
19
|
|
20
|
|
21
|
# Ce script traite les logs IRC des revues hebdo ; pour faciliter la
|
22
|
# digestion. Voir https://redmine.april.org/issues/135
|
23
|
#
|
24
|
# Le programme est conçu pour parser les logs de madix selon leur format
|
25
|
# particulier. Il prends le log sur l'entrée standard et sort sur la sortie
|
26
|
# standard. Si tu veux mettre un chemin de fichier en entrée, ça se passe au
|
27
|
# début du code, il y a un exemple commenté.
|
28
|
#
|
29
|
# Il y a trois modalités de contrôle en ligne :
|
30
|
# * commentaire : si quelqu'un veut écrire un truc qui ne soit pas
|
31
|
# passé en revue, il commence par un %.
|
32
|
# Exemple : <madix> % quand vous avez fini vous le dites
|
33
|
# <liot_> % fini
|
34
|
#
|
35
|
# * directive de revue individuelle : avec un #, tu annonces un
|
36
|
# changement d'état dans la revue. Les messages qui suivent
|
37
|
# sont traités/triés individuellement. Les états sont créés
|
38
|
# individuellement lorsqu'il y a un contenu associé à stocker.
|
39
|
# Exemple : <madix> # 2/ Action passées
|
40
|
#
|
41
|
# * directive de revue globale : avec un ##, tu annonces un
|
42
|
# changement d'état dans la revue. Les messages qui suivent
|
43
|
# sont traités/triés collectivement. Les états sont créés
|
44
|
# uniquement lorsqu'il y a un contenu associé à stocker.
|
45
|
# Exemple : <madix> ## 3/ Point bloquants existants ou levés
|
46
|
# récemment
|
47
|
#
|
48
|
# Les directives ne sont interprétées que pour le conducteur de la
|
49
|
# réunion, défini dans le code. Pour des raisons de fainéantise, elles
|
50
|
# sont triées alphanumériquement ; donc il vaut mieux les numéroter.
|
51
|
#
|
52
|
# L'affichage est factorisé dans 3 méthodes title(), subTitle() et
|
53
|
# subSubTitle() ; pour modifier facilement aux gouts de chacun.
|
54
|
#
|
55
|
# Le code intègre un dictionnaire pseudo -> nom.
|
56
|
|
57
|
|
58
|
|
59
|
import re, sys, textwrap
|
60
|
|
61
|
######### Conducteur de la revue : celui qui envoit les directives ########
|
62
|
conducteur = 'madix'
|
63
|
|
64
|
####################### Dictionnaire peudo / nom ##########################
|
65
|
|
66
|
def pseudo2nom(pseudo):
|
67
|
noms = {
|
68
|
'liot_' : 'Lionel Allorge',
|
69
|
'liot' : 'Lionel Allorge',
|
70
|
'Armony' : 'Armony Altinier',
|
71
|
'kult' : 'Tony Bassette',
|
72
|
'raceme' : 'Christophe Boyanique',
|
73
|
'PetiPandaRou' : 'Julia Buchner',
|
74
|
'PetiPandaRou1' : 'Julia Buchner',
|
75
|
'echarp' : 'Emmanuel Charpentier',
|
76
|
'aurelia' : 'Aurélia Gilardi',
|
77
|
'Luk_' : 'Luc Fievet',
|
78
|
'Remaille' : 'Rémi Boulle',
|
79
|
'teymour' : 'Tangui Morlier',
|
80
|
'coin_p' : 'Marc Chauvet',
|
81
|
'coin_pan' : 'Marc Chauvet',
|
82
|
'coin_pan_' : 'Marc Chauvet',
|
83
|
'lcosty' : 'Laurent Costy',
|
84
|
'Flache-Gore-Donn' : 'Laurent Costy',
|
85
|
'theocrite' : 'theocrite',
|
86
|
'cnestel' : 'Charlie Nestel',
|
87
|
'madix' : 'Frédéric Couchet',
|
88
|
'madix`' : 'Frédéric Couchet',
|
89
|
'dachary' : 'Loïc Dachary',
|
90
|
'mmu_man' : 'François Revol',
|
91
|
'benj' : 'Benjamin Drieu',
|
92
|
'bookynette' : 'Magali Garnero',
|
93
|
'Bookynette' : 'Magali Garnero',
|
94
|
'BookyPaNette' : 'Magali Garnero',
|
95
|
'Siltaar' : 'Simon Descarpentries',
|
96
|
'ave' : 'Eva Mathieu',
|
97
|
'ave1' : 'Eva Mathieu',
|
98
|
'_PoluX_' : 'François Poulain',
|
99
|
'_PoLuX_' : 'François Poulain',
|
100
|
'gibus' : 'Gérald Sédrati-Dinet',
|
101
|
'gibus_at_office' : 'Gérald Sédrati-Dinet',
|
102
|
'leobaillard' : 'Léopold Baillard',
|
103
|
'janchou' : 'Jeanne Tadeusz',
|
104
|
}
|
105
|
if pseudo in set(noms): return noms[pseudo] + ' <' + pseudo + '>'
|
106
|
else: return '<' + pseudo + '>'
|
107
|
|
108
|
###########################################################################
|
109
|
|
110
|
# log_brut = open('/tmp/20100806-log-irc-revueIndividuelle-hebdomadaire.txt').read()
|
111
|
log_brut = sys.stdin.read()
|
112
|
|
113
|
################## Filtrage du log, sortie dans une liste #################
|
114
|
|
115
|
# Nettoyage syntaxe + horodatage + notification
|
116
|
aSupprimer = re.compile(
|
117
|
r'\*\*\*.*?(#april|has quit.*?|is now known as.*?)$'
|
118
|
r'|'
|
119
|
r' {2,}'
|
120
|
r'|'
|
121
|
r'\[\d\d:\d\d\]'
|
122
|
r'|'
|
123
|
r'\[\d\d/\d\d/\d\d \d\d:\d\d\]'
|
124
|
r'|'
|
125
|
r'^\s?\*\s?'
|
126
|
,re.MULTILINE|re.DOTALL)
|
127
|
log = aSupprimer.sub('',log_brut)
|
128
|
|
129
|
aSubstituer = re.compile(r'\n+|\s+')
|
130
|
log = aSubstituer.sub(' ',log)
|
131
|
|
132
|
# Concatenation/découpe des pseudo/propos
|
133
|
pseudo = re.compile(r'(<[^ >]*?>)')
|
134
|
|
135
|
log = pseudo.split(log)
|
136
|
|
137
|
# Supression des éventuelles puces et espaces de début et fin de ligne
|
138
|
puce = re.compile(r'^\s?\*?\s?|\s?$')
|
139
|
for phrase in log:
|
140
|
log[log.index(phrase)] = re.sub(puce, '', phrase)
|
141
|
|
142
|
# Retrait des éventuelles lignes vides
|
143
|
while log.count(''): log.remove('')
|
144
|
|
145
|
################ Parsage du log, stockage en dictionnaire #################
|
146
|
|
147
|
# Regexps pour la reconnaissance des motifs
|
148
|
pseudo = re.compile(r'<([^ >]*?)>')
|
149
|
directive = re.compile(r'^\s?\#\#?\s?')
|
150
|
directiveIndividuelle = re.compile(r'^\s?\#[^#]\s?')
|
151
|
commentaire = re.compile(r'^\s?\%\s?')
|
152
|
|
153
|
# Drapeaux pour le traitement individuel/collectif
|
154
|
individuel = False
|
155
|
|
156
|
# Instantiation des revues
|
157
|
revueIndividuelle = {}
|
158
|
revueCollective = {}
|
159
|
|
160
|
directiveCourante = 'Indéfini'
|
161
|
parleur = 'John Doe' # normalement inutile
|
162
|
|
163
|
# Nombre de colonnes pour l'affichage
|
164
|
text_width = 72
|
165
|
|
166
|
print '\n', ' Commentaires (pour vérification) '.center(text_width, '='), '\n'
|
167
|
|
168
|
for line in log:
|
169
|
# Si on a affaire à un pseudo
|
170
|
if re.match(pseudo,line):
|
171
|
parleur = re.match(pseudo,line).group(1)
|
172
|
|
173
|
# Si on a affaire à une directive proposée par le dictateur
|
174
|
elif re.match(directive, line) and parleur == conducteur:
|
175
|
# Collective ?
|
176
|
individuel = True
|
177
|
if not re.match(directiveIndividuelle, line):
|
178
|
individuel = False
|
179
|
|
180
|
# Individuelle ?
|
181
|
directiveCourante = re.sub(directive, '', line)
|
182
|
|
183
|
# Si on a affaire à un commentaire
|
184
|
elif re.match(commentaire, line):
|
185
|
print parleur, ' : ', line # Affiché pour contrôle visuel
|
186
|
|
187
|
# Sinon il s'agit d'une entrée à stocker
|
188
|
else:
|
189
|
# Traitement individuel ?
|
190
|
if individuel:
|
191
|
# Les nouveaux intervenants sont instanciés
|
192
|
if parleur not in set(revueIndividuelle):
|
193
|
revueIndividuelle[parleur] = {}
|
194
|
|
195
|
# Les nouveaux états sont instanciés
|
196
|
if directiveCourante not in set(revueIndividuelle[parleur]):
|
197
|
revueIndividuelle[parleur][directiveCourante] = []
|
198
|
|
199
|
# Stockage de la ligne dans la revueIndividuelle
|
200
|
revueIndividuelle[parleur][directiveCourante].append(line)
|
201
|
|
202
|
# Sinon : traitement collectif
|
203
|
else:
|
204
|
# Les nouveaux états sont instanciés
|
205
|
if directiveCourante not in set(revueCollective):
|
206
|
revueCollective[directiveCourante] = []
|
207
|
|
208
|
# Stockage de la ligne dans la revueCollective
|
209
|
revueCollective[directiveCourante].append(parleur + ' : ' + line)
|
210
|
|
211
|
############################## Affichage ##############################
|
212
|
|
213
|
# Regexp pour les énumérations
|
214
|
enumeration = re.compile(r'^\s?\d?\/?\s?')
|
215
|
|
216
|
# Procedures d'affichage
|
217
|
|
218
|
def separateur():
|
219
|
print '\n', ''.center(text_width, '=')
|
220
|
|
221
|
def title(texte):
|
222
|
separateur()
|
223
|
print texte.center(text_width)
|
224
|
print ''.center(text_width, '=')
|
225
|
print ''
|
226
|
|
227
|
def subTitle(texte):
|
228
|
separateur()
|
229
|
print ''
|
230
|
print (' ' + texte + ' ').center(text_width,'-')
|
231
|
|
232
|
def subSubTitle(texte):
|
233
|
print '\n=== ', texte, ' ==='
|
234
|
print ''
|
235
|
|
236
|
# Si le conducteur est mal déclaré
|
237
|
if directiveCourante == 'Indéfini':
|
238
|
separateur()
|
239
|
print ''
|
240
|
print textwrap.fill('La réunion n\'a visiblement pas été conduite. Cela peut venir '+
|
241
|
'd\'un problème de formatage de log IRC, ou bien simplement parce '+
|
242
|
'que le conducteur déclaré dans le code est erroné. Êtes vous sur '+
|
243
|
'que le conducteur de la réunion est ' + conducteur + ' ? Vous '+
|
244
|
'pouvez changer ça dans le code revue.py, en éditant la ligne 62.', text_width)
|
245
|
separateur()
|
246
|
exit()
|
247
|
|
248
|
# Affichage des puces
|
249
|
|
250
|
wrapper = textwrap.TextWrapper()
|
251
|
wrapper.initial_indent = "* "
|
252
|
wrapper.subsequent_indent = " "
|
253
|
wrapper.width = text_width
|
254
|
|
255
|
def printPuce(texte):
|
256
|
print wrapper.fill(texte)
|
257
|
|
258
|
title(' Revue de la semaine en cours ')
|
259
|
|
260
|
# Liste des participants
|
261
|
subTitle('Participants')
|
262
|
people = ''
|
263
|
participants = sorted(revueIndividuelle.keys(), reverse = True)
|
264
|
if len(participants) > 0:
|
265
|
while len(participants) > 1:
|
266
|
people += '* ' + pseudo2nom(participants.pop()) + ',\n'
|
267
|
people += '* ' + pseudo2nom(participants.pop()) + '.'
|
268
|
print people
|
269
|
|
270
|
# Revue individuelle
|
271
|
for participant in revueIndividuelle:
|
272
|
subTitle(pseudo2nom(participant))
|
273
|
for directive in sorted(revueIndividuelle[participant]):
|
274
|
subSubTitle(re.sub(enumeration, '', directive))
|
275
|
for puce in revueIndividuelle[participant][directive]:
|
276
|
printPuce(puce)
|
277
|
|
278
|
# Revue collective : passe les états sauf celui indéfini
|
279
|
for directive in sorted(set(revueCollective) - set(['Indéfini'])):
|
280
|
title(re.sub(enumeration, '', directive))
|
281
|
for puce in revueCollective[directive]:
|
282
|
printPuce(puce)
|
283
|
|
284
|
# Passe l'état indéfini
|
285
|
if 'Indéfini' in set(revueCollective):
|
286
|
print '\n', ' Indéfini '.center(text_width, '='), '\n'
|
287
|
for puce in set(revueCollective['Indéfini']):
|
288
|
printPuce(puce)
|
289
|
|
290
|
title('Log IRC brut')
|
291
|
|
292
|
print log_brut
|