Utilisateur:Cæruleum/editcount-local

(en) Description modifier

In case toolserver is not working and you need to check editcount anyway, here is a small script. It will download the contribution files of a given user on a project and classify them like an editcount. All names (months...) are taken from the result pages. The critical part is the regex for month identification. The one included here works for wikipedia en and fr. The one for wikipedia pt is also added but commented (look for the text « update the regex » in the code).

Developed on GNU/linux, should work anywhere else if you care to install perl and wget.

Since it will download quite large amount of bytes (330 ko per page of 500 contributions), one should use it with moderation. The program does not (yet) implement an update or tracking system to avoid developing.

Output can be either in plain text or in <timeline> format.

Usage : launching the program without argument will give a description of command-line arguments, which one can find in the beginning of the code. Typical command-line :

./editcount.pl --user=Bob --project=fr.wikipedia --action=all --size=500  --maxpages=3 --source=contributions --output=text > file.txt

This will download and show the results of last 1500 contributions of user Bob and give output in plain text format. Further tuning can be changing "--output=text" for "--output=timeline" to get a <timeline> output; changing "--source=contributions" to "source=log" to see the journal actions of the user instead of contributions. If one wants to see again the results for files already downloaded, it is possible to change "--action=all" to "--action=show".

At the moment, it only works on fr.wikipedia due to some specific regex. This will change in future.

Please let me bugreports on my talk page or just edit the code on this page.

Sample output modifier

Text format modifier

== janvier 2008 ==
Catégorie : 2 (0.21505376344086 %)
(principal) : 429 (46.1290322580645 %)
Discussion Projet : 2 (0.21505376344086 %)
Discussion Wikipédia : 13 (1.39784946236559 %)
Wikipédia : 409 (43.9784946236559 %)
Discussion Modèle : 1 (0.10752688172043 %)
Modèle : 3 (0.32258064516129 %)
Utilisateur : 3 (0.32258064516129 %)
Projet : 1 (0.10752688172043 %)
Discuter : 26 (2.79569892473118 %)
Discussion Utilisateur : 41 (4.40860215053763 %)

 (dernière) : 189 (20.3225806451613 %)
m : 397 (42.6881720430108 %)
-> 930

== décembre 2007 ==
Catégorie : 1 (0.114025085518814 %)
(principal) : 585 (66.7046750285063 %)
Discussion Projet : 2 (0.228050171037628 %)
Discussion Wikipédia : 5 (0.570125427594071 %)
Wikipédia : 208 (23.7172177879133 %)
Image : 1 (0.114025085518814 %)
Utilisateur : 14 (1.5963511972634 %)
Projet : 6 (0.684150513112885 %)
Portail : 1 (0.114025085518814 %)
Discuter : 23 (2.62257696693273 %)
Discussion Utilisateur : 31 (3.53477765108324 %)

 (dernière) : 127 (14.4811858608894 %)
m : 411 (46.8643101482326 %)
-> 877

Timeline output modifier

The domains are in different colours, taken from mw:Extension:EasyTimeline#Charts examples. The main green area is the Article domainname, the main blue domain is the Wikpedia domainname. The small bar shows the top edits (in red) and the minor edits (in green). If you still have top edit in very old pages, maybe it's a good idea to go check the page again and try to improve it.

 
Sample editcount taken on 3rd February 2008.

Code modifier

#!/usr/bin/perl
# This programs downloads the user's history on a MediaWiki project
# and provides an editcount classified by domainname.
#
#            This program is free software. Permission is granted to
#            copy, distribute and/or modify this document under the terms
#            of either:
#            - the GNU Free Documentation License, Version 1.3 or any
#              later version published by the Free Software Foundation;
#              with no Invariant Sections, no Front-Cover Texts, and no
#              Back-Cover Texts.
#            - the GNU General Public License as published by the Free
#              Software Foundation; either version 3 of the License, or
#              (at your option) any later version.
#            - the Perl Artistic licence.
#            You should have received a copy of these three licences.
#            If not, they are available at:
#            - http://www.gnu.org/licenses/gpl.html
#            - http://www.gnu.org/licenses/fdl.html
#            - http://www.perl.com/pub/a/language/misc/Artistic.html
#
#     Program written by [[:w:fr:Utilisateur:Jborme]] in February 2008.

# Bugs :
# - Les couleurs en mode frise ne sont pas assignées correctement dans le
#   cas de l'analyse des fichiers log.

# TÀF :
# (temps requis)   Description
# - mineur    faire une frise de référence pour les couleurs ;
# - mineur    dé-dupliquer le code de la frise (j'ai une copie de code indiquée par ###### ;
# - mineur    renommer les couleurs.
# - moyen     Améliorer la détection du mois. Actuellement, on doit passer
#             manuellement de l'un à l'autre dans le code, et les logs ne marchent
#             que pour fr:. Récupérer les mois dans <option> n'est pas suffisant
#             car certains projets (es.wikipedia) n'utilisent ensuite pas la même
#             convention dans les lignes d'historique. J'imagine qu'il faudra
#             écrire des regex par $projet.
# - majeur    Faire un fichier préparsé avec les commentaires, d'importation facile
#             dans perl ; générer les stats seulement après, en fusionnant les deux.

use strict ;

###########################
# Paramétrage utilisateur #
###########################
# my $utilisateur = "DocteurCosmos" ;
# my $projet = "fr.wikipedia" ;
#
# my $format_frise = 0 ; # mettre à 1 pour un format de frise chronologique, à 0 pour une sortie texte basique.
# my $taille = 500 ;
# my $mode_hors_ligne = 1 ;  # Mettre à 1 pour demander un nouvel affichage des données déjà calculées.
# # Si le mode hors_ligne vaut 1, il est nécessaire de préciser le nombre_max de pages.
# my $nombre_max_pages = 9 ; # Non pris en compte si inférieur ou égal à 0

##########################################
# Paramètres d'utilisation moins commune #
##########################################
my $fake_internet = 0 ; # Vaut 0 pour l'utilisation normale, 1 (ou autre) pour empêcher
                        # le téléchargement. Le but est uniquement le déboggage du programme.
my $options_wget = "" ;
# Sur certains site (pas Wikipédia, mais bon...), les requêtes sont filtrées sur la base du user-agent et wget peut être rejeté
# Dans ce cas, il suffit de passer à wget le nom d'un navigateur accepté.
#$options_wget = "--user-agent =\"Mozilla/5.0 (compatible; Konqueror/3.5; Linux; fr, en_US) KHTML/3.5.8 (like Gecko)\"" ;

######################################################
# Reconnaissance des paramètres de ligne de commande #
######################################################
my $i ;
my $utilisateur ;
my $projet ;
my $format_frise ;
my $taille ;
my $action ;
my $sources ;

#my $mode_hors_ligne ;
my $nombre_max_pages ;

sub usage {
print "  This script will dowload a user's contributions from a MediaWiki
  project, parse the files report the editcount for, classified by
  month and namespace.

  Compulsory argument
   --user=username                     -- no default
        The username to be used. If needed, can be placed between
        single ou double quotes. Non-pairing initial quote will be
        considered part of the name.
        e.g. --username=\"John Doe\"

   --project=projectname               -- no default
        The project to be used. This is the central part of the URL,
        e.g. --project=commons.wikimedia

  Optional arguments
   --action=[download|show|all*] -- default is all *** TODO : parse ***
        The actions to be taken by the program. They are
        mutually exclusive, except all which does all the others
        in order. It is necessary to execute the preceding actions
        to execute a given one. If this is not done, empty results
        will appear.
        e.g. --action=download

   --maxpages=number                   -- default is -1
        If specified, this gives the maximum number of pages
        to be treated by actions download and parse. Each page
        downloaded will have a number of useful lines given by
        --size. Because all events are processed by reverse
        chronological order, this option limits the results to
        the most recent contributions. If the number passed to
        --maxpages is non zero negative, there is no limitation.
        In case several sources are fetched as of --source
        parameter, the limit applies individually to each source.
        If no number is specified and a dowload is part of the
        action, the number of downloaded files will be used.
        The analysis will stop at the encounters a missing
        file.
        e.g. --maxpages=5

   --size=[20|50|100|250|500*]         -- default is 500
        The contributions pages are provided by MediaWiki by
        packets. As of the day of writing, MediaWiki would
        accept 20, 50, 100, 250 or 500. A small value can be used
        for tests, a larger value for actual editcount calculation.
        e.g. --size=250

   --source=[contributions|log]   -- default is contributions *** TODO : all ***
        The data to be fetched, parsed or showed can be the
        contributions to the pages and the logs. The latter
        include the administrative actions.

   --output=[(text|0)*|(timeline|1)]   -- default is text
        Two output formats are provided, a plain text format and
        a format compatible with the EasyTimeline extension for
        MediaWiki. Default is text (0).
        e.g. --output=timeline
             --output=1
        Both examples do the same.

     Example use :
       ./editcount.pl --user=John_Doe --project=fr.wikpedia
                      --action=download --size=50  --maxpages=5
                      --source=log
        will download the last 5 pages of 50 actions in the log files
        of user John_Doe on fr.wikipedia project.
" ; }

if ($#ARGV < 1) { # 2 arguments
   usage ;
   exit (0) ;
}

# Les paramètres peuvent être passés plusieurs fois. Seule la dernière occurrence sera retenue.
foreach $i (@ARGV) {
   # Le nom de l'utilisateur peut être entouré de guillemets, ou pas. S'il est entouré de
   # guillemets, ce derniers vont par paire. Si les guillemets ne sont pas appariés et
   # que l'interpréteur les renvoie quand même, alors is font partie du nom.
   # Le traitement est séquentiel. Une fois la bonne chaine découverte, on doit
   # utiliser $1 avant toute autre utilisation de m//.
   # Si les autres détections échouent, c'est que l'on a commencé par
   # un guillemet non apparié qui fait partie du nom d'utilisateur.
   if    ($i =~ m/^--user=([^\"\'][^ ]*)/) { $utilisateur = $1 ; }
   elsif ($i =~ m/^--user=\"([^ ]*)\"/)    { $utilisateur = $1 ; }
   elsif ($i =~ m/^--user=\'([^ ]*)\'/)    { $utilisateur = $1 ; }
   elsif ($i =~ m/^--user=([^ ]*)/)        { $utilisateur = $1 ; }

   # Pour les autres arguments, on ne prend jamais les guillemets initiaux et
   # finaux, s'ils existent.
   # print "voilà $i\n" ;
   if ($i =~ m/--project=[\"\']?([^ ]*)[\"\']?/)  { $projet = $1 ; }
   if ($i =~ m/--timeline=[\"\']?([^ ]*)[\"\']?/) { $format_frise = $1 ; }
   if (($i =~ m/--output=[\"\']?text[\"\']?/)     or
       ($i =~ m/--output=[\"\']?0[\"\']?/))       { $format_frise = 0 ; }
   if (($i =~ m/--output=[\"\']?timeline[\"\']?/) or
       ($i =~ m/--output=[\"\']?1[\"\']?/))       { $format_frise = 1 ; }
   if ($i =~ m/--size=[\"\']?([^ ]*)[\"\']?/)     { $taille = $1 ; }
   if    ($i =~ m/--action=[\"\']?download[\"\']?/)   { $action = 1 ; }
   elsif ($i =~ m/--action=[\"\']?parse[\"\']?/)      { $action = 2 ; }
   elsif ($i =~ m/--action=[\"\']?show[\"\']?/)       { $action = 4 ; }
   elsif ($i =~ m/--action=[\"\']?all[\"\']?/)        { $action = 7 ; }
   if    ($i =~ m/--source=[\"\']?[Cc]ontributions[\"\']?/) { $sources = 1 ; }
   elsif ($i =~ m/--source=[\"\']?[Ll]og[\"\']?/)           { $sources = 2 ; }
   elsif ($i =~ m/--source=[\"\']?all[\"\']?/)              { $sources = 3 ; }
   if ($i =~ m/--maxpages=[\"\']?([^ ]*)[\"\']?/) { $nombre_max_pages = $1 ; }
}

die "Please specify --user=your_username\n" unless (defined $utilisateur) ;
die "Please specify --project=target_project (e.g. en.wikipedia)\n" unless (defined $projet) ;
$format_frise = 0      unless (defined $format_frise) ;
$taille = 500          unless (defined $taille) ;
$nombre_max_pages = -1 unless (defined $nombre_max_pages) ;
$action = 7            unless (defined $action) ;

##########################
# Récupération des pages #
##########################
my $ligne ;
my $base_nom ;
my $pages_recuperees = 0 ;

if ($action % 2 == 1) { # 1 -> inclure le téléchargement
#unless ($mode_hors_ligne) {
my ($requete, $decalage, $pos, $chaine, $continue_boucle) ;
my $requete_partielle ;
my (@decalages) ;
my $page_suivante ;

sub recupere_pages {
   my ($requete_partielle, $base_nom) = @_ ;

   if ($nombre_max_pages != -1) { $page_suivante = 1 ; }
   $i = 1 ;
   $requete = "wget \"${requete_partielle}\" ${options_wget} --output-document=\"${projet}-${utilisateur}-${base_nom}-${i}.html\"" ;
   while ($page_suivante) {
      $continue_boucle = 1 ;
      unless ($fake_internet) { system $requete ; }
      $pages_recuperees++ ;

      # $nombre_max_pages == -1 sert à noter l'absence de limite (+inf)
      if (($nombre_max_pages >= 0) and ($pages_recuperees >= $nombre_max_pages)) {
         $page_suivante = 0 ;
      }

      open FIC1, "${projet}-${utilisateur}-${base_nom}-${i}.html" ;
      while (($ligne = <FIC1>) and ($continue_boucle)) {
         # On passe leslignes jusqu'à trouver le lien « suivant »
         # S'il n'y a pas d'occurrence sur la page, le contributeur a moins de $taille modifications.
         next unless ($ligne =~ m/offset=/) ;
         $continue_boucle = 0 ;
         if ($i eq 1) {
            # 1re page et > $taille modifications. Il y a exactement 1 occurrence de « offset= »
            $ligne =~ m/^.*offset=([0-9]*)\&/ ;
            $decalage = $1 ;
            push @decalages, $decalage ;
            $i++ ;
            $requete = "wget \"${requete_partielle}&offset=${decalage}\" ${options_wget} --output-document=\"${projet}-${utilisateur}-${base_nom}-${i}.html\"" ;
         } else {
            # Lorsqu'on n'est pas sur la première page, il y a toujours au moins deux occurrences
            # de « offset = »
            # On choisit la deuxième occurrence de « offset = »
            # TIMTIWTD
            $pos = index ($ligne, "offset=") ; # position de la première occurrence
            $chaine = substr ($ligne, 1+$pos, length ($ligne)) ; # le reste de la ligne
            $pos = index ($chaine, "offset=") ; # position de la deuxième occurrence
            $chaine = substr ($chaine, $pos, length ($chaine)) ; # le reste de la ligne
            $chaine =~ m/offset=([0-9]*)[^0-9]/ ;
            $decalage = $1 ;
            # print "Nous avons trouvé un offset de $decalage\n" ;
            # Cette deuxième occurrence peut être soit « page suivante », soit « page précédente »,
            # auquel cas on a fini la recherche. On reconnait que c'est « page précédente » parce
            # qu'on l'a déjà enregistrée dans le tablea @decalages.
            if (grep (/${decalage}/, @decalages)) {
               $page_suivante = 0 ;
            } else {
               push @decalages, $decalage ;
               $i++ ;
               $requete = "wget \"${requete_partielle}&offset=${decalage}\" ${options_wget} --output-document=\"${projet}-${utilisateur}-${base_nom}-${i}.html\"" ;
            }
         }
      }
      close FIC1 ;
      if ($continue_boucle eq 1) {
         $page_suivante = 0 ;
      }
   } # while ($page_suivante) { ... }
}# du sub recupere_page

if ($sources % 2 == 1) {
   $base_nom = "Contributions" ;
   $requete_partielle = "http://${projet}.org/w/index.php?title=Special:${base_nom}&limit=${taille}&contribs=user&target=${utilisateur}" ;
   recupere_pages $requete_partielle, $base_nom ;
}

if ($sources % 4 >= 2) {
   $base_nom = "Log" ;
   $requete_partielle = "http://${projet}.org/w/index.php?title=Special:${base_nom}&limit=${taille}&type=&page=&pattern=&user=${utilisateur}" ;
   recupere_pages $requete_partielle, $base_nom ;
}

} # if ($action

if (($action % 4) >= 2) { # 2 -> inclure le traitement
# Retraitement, pas encore codé
}

if ($action % 8 >= 4) { # 4 -> affichage
# Affichage, à recoder, mais pour l'instant, ça passera comme ça, parce
# qu'on n'a pas encore activé « --sources=all »,
# sources vaut donc soit 1, soit 2, mais pas 3.
if ($sources % 2 == 1) { # sources == 1
   $base_nom = "Contributions" ;
}

if ($sources % 4 >= 2) { # sources == 2 ou 3.
   $base_nom = "Log" ;
}

####################################
# Récupération du nom des domaines #
####################################
my $debut = 1 ;
my $fin = 0 ;
my %couleurs ;
my ($domaine, $numero_domaine, $domaine_principal, @liste_domaines) ;
my @couleurs_mediawiki = qw /neogene paleogene cretaceous jurassic triassic permian carboniferous devonian silurian ordovician cambrian neoproterozoic mesoproterozoic paleoproterozoic eoarchean paleoarchean mesoarchean neoarchean cryogenian tonian stratherian mesozoic/ ; # mesozoic parce que calymmian utilisait une couleur déjà utilisée.

$i = 1 ;

open FIC2, "${projet}-${utilisateur}-${base_nom}-1.html" ;

while (($ligne = <FIC2>) and (! $fin)) {
   if ($debut) {
      #next unless (($ligne =~ m@<select id=\"namespace\"@) or # pour la page des constributions
      #             ($ligne =~ m@<select name=\'type\'@)) ;    # pour la page des journaux
      next unless ($ligne =~ m@<select @) ;
      $debut = 0 ;
      next ;
   } else {
      $ligne =~ m@^<option value=\"([0-9]*)\">(.*)</option>@ ;
      $numero_domaine = $1 ;
      $domaine = $2 ;
      if ($numero_domaine eq 0) {
         $domaine_principal = $domaine ;
      }
      $couleurs {$domaine} = $couleurs_mediawiki [$i] ;
      $i ++ ;
      if ($ligne =~ m@</select>@) {
         $fin = 1 ;
      }
   }
}
close FIC2 ;
@liste_domaines = keys %couleurs ;

################################
# Récupération du nom des mois #
################################
# (marche sur fr:, en:, pas directement sur pt: et pas du tout sur es:)

################################
# Classement des contributions #
################################

# my $max_i ;
# if ($nombre_max_pages < 0) {
# }
# if ($action % 2 == 1) {
# #if ($mode_hors_ligne) {
#    $max_i = $nombre_max_pages ;
# } else {
#    $max_i = $i ;
# }

my $j ;
my ($espace_nom, $mineure, $deniere) ;
my $mois_courant = "" ;
my $mois_ligne ;
my $temp ;
my $mois_defini = 0 ;
my %stats_noms ;
my %stats_noms_total ;
my $stats_mineure = 0 ;
my $stats_derniere = 0 ;
my $stats_mineure_total = 0 ;
my $stats_derniere_total = 0 ;
my $stats_total = 0 ;
my $stats_total_total = 0 ;
my $premier_mois = 1 ;
my @clefs ;
my $max_total_mois ;
my $nom_derniere ;
my $nom_mineure ;
my $total_courant ;
my $ancien_total_courant ;
my $valeur_courante ;
my $couleur ;
my $nombre_mois = 1 ;
my $frise ;
my $mois_imprimable ;

sub mise_a_jour_stats_contributions {
   if ($ligne =~ m@title=\"([^:]*):[^:]*\">diff</a>\)@) {
      $espace_nom = $1 ;
      # Si l'espace de noms n'est pas dans la liste, il vient du titre d'article.
      unless (grep /$espace_nom/, @liste_domaines) {
         $espace_nom = $domaine_principal ;
      } ;
   } else {
      $espace_nom = $domaine_principal ;
   }
   $stats_noms {$espace_nom} ++ ;
   if ($ligne =~ m@<span class="minor">([^<]*)</span>@) {
      $stats_mineure ++ ;
      if (defined ($nom_mineure)) { ### corr.
         $nom_mineure = $1 ;
      }
   }
   if ($ligne =~ m@\<strong>([^<]*)</strong></li>@) {
      $stats_derniere ++ ;
      if (defined ($nom_derniere)) { ### corr.
         $nom_derniere = $1 ;
      }
   }
   $stats_total ++ ;
}

sub mise_a_jour_stats_logs {
   #      print "on a $ligne on a trouvé $espace_nom\n" ;
   if ($ligne =~ m@\) a ([^ ]*) @) { # [«<]
      $espace_nom = $1 ;
      # print "on a $espace_nom\n" ;
   } else {
      $espace_nom = $domaine_principal ;
       print "$ligne" ;
   }
   $stats_noms {$espace_nom} ++ ;
   $stats_total ++ ;
}

sub affiche_stats {
   # On a fini le mois ; afficher les stats
   if ($format_frise) {
      if ($premier_mois) {
      # La frise ne peut pas être imprimée à la volée parce que
      # le paramètre Période, que je ne peux connaitre qu'à la fin,
      # doit être ajouté au début. Les balises du tout début seront ajoutées à la fin
         $frise = "DateFormat = yyyy\n" ;
         $frise = "${frise}TimeAxis   = orientation:horizontal\n" ;
         $frise = "${frise}ScaleMajor = unit:year increment:100 start:0\n" ;
         # couleurs : [[:mw:Extension:EasyTimeline#Charts examples]]
         $frise = "${frise}Colors =\n  id:neogene   value:rgb(0.99215,0.8,0.54)\n  id:paleogene value:rgb(1,0.7019,0)\n  id:cretaceous   value:rgb(0.5,0.764,0.1098)\n  id:jurassic      value:rgb(0.302,0.706,0.5)\n  id:triassic    value:rgb(0.403,0.765,0.716)\n  id:permian   value:rgb(0.404,0.776,0.867)\n  id:carboniferous     value:rgb(0.6,0.741,0.855)\n  id:devonian  value:rgb(0.6,0.6,0.788)\n  id:silurian  value:rgb(0.694,0.447,0.714)\n  id:ordovician      value:rgb(0.976,0.506,0.651)\n  id:cambrian  value:rgb(0.984,0.5,0.373)\n  id:neoproterozoic    value:rgb(0.792,0.647,0.583)\n  id:mesoproterozoic    value:rgb(0.867,0.761,0.533)\n  id:paleoproterozoic    value:rgb(0.702,0.698,0.369)\n  id:eoarchean    value:rgb(0.5,0.565,0.565)\n  id:paleoarchean    value:rgb(0.6,0.592,0.569)\n  id:mesoarchean    value:rgb(0.698,0.65,0.6)\n  id:neoarchean    value:rgb(0.796,0.804,0.784)\n  id:cryogenian    value:rgb(0.863,0.671,0.667)\n  id:tonian        value:rgb(0.796,0.643,0.424)\n  id:stratherian   value:rgb(1,1,0.8)\n  id:mesozoic   value:rgb(0.5,0.6784,0.3176)\n" ;
         $frise = "${frise}PlotData=\n" ;
      }
      $mois_imprimable = $mois_courant ;
      $mois_imprimable =~ s/ /_/g ;
      $frise = "${frise}  bar:${mois_imprimable} color:red width:30 mark:(line,white) align:left fontsize:XS\n" ;
# Le code avec undef @clefs s'assurait
# que les domaines sont classés dans l'ordre de Wikipédia
# Cela n'est pas compatible (pour l'instant) avec l'utilisation
# des fichiers log. Je commente et passe en classement alphabétique.
#       undef @clefs ;
#       foreach $j (keys (%couleurs)) {
#          if (defined $stats_noms {$j}) {
#             push @clefs, $j ;
#          }
#       }
      @clefs = sort keys %stats_noms ;
      $total_courant = 0 ;
      $ancien_total_courant = 0 ;
      foreach $j (@clefs) {
         $couleur = $couleurs {$j} ;
         $valeur_courante = $stats_noms {$j} ;
         $total_courant += $valeur_courante ;
         $frise = "${frise}  color:${couleur} from:${ancien_total_courant} till:${total_courant} shift:(-5,0) fontsize:S text:${valeur_courante}\n" ;
         $ancien_total_courant = $total_courant ;
      }
      if ((defined ($nom_derniere)) and ($stats_derniere != 0)) {
         $frise = "${frise}  bar:${mois_imprimable}_ color:red  width:5  mark:(line,white) align:left fontsize:XS\n" ;
         $frise = "${frise}  bar:${mois_imprimable}_ color:red  from:0 till:${stats_derniere} shift:(0,0) fontsize:XS text:${stats_derniere}\n" ;
      }
      my $total = $stats_derniere + $stats_mineure ;
      if ((defined ($nom_mineure)) and ($stats_derniere != 0)) {
         $frise = "${frise}  bar:${mois_imprimable}_ color:green  width:5  mark:(line,white) align:left fontsize:XS\n" ;
         $frise = "${frise}  bar:${mois_imprimable}_ from:${stats_derniere} till:${total} shift:(0,0) fontsize:XS text:${stats_mineure}\n" ;
      }
   } else {
      print "== ${mois_courant} ==\n" ;
#       undef @clefs ;
#       foreach $j (keys (%couleurs)) {
#          if (defined $stats_noms {$j}) {
#             push @clefs, $j ;
#          }
#       }
      @clefs = sort keys %stats_noms ;
      foreach $j (@clefs) {
         $temp = $stats_noms {$j} / $stats_total * 100 ;
         print "${j} : ", $stats_noms {$j}, " (${temp} %)\n" ;
      }
      print "\n" ;
      if ((defined ($nom_derniere)) and ($stats_derniere != 0)) {
         $temp = $stats_derniere / $stats_total * 100 ;
         print "${nom_derniere} : $stats_derniere ($temp %)\n" ;
      }
      if ((defined ($nom_mineure)) and ($stats_derniere != 0)) {
         $temp = $stats_mineure / $stats_total * 100 ;
         print "${nom_mineure} : $stats_mineure ($temp %)\n" ;
      }
      print "-> $stats_total\n\n" ;
   }
   if ($stats_total > $max_total_mois) {
      $max_total_mois = $stats_total ;
   }
   # Mise à jour des statistiques globales
   $stats_mineure_total += $stats_mineure ;
   $stats_derniere_total += $stats_derniere ;
   $stats_total_total += $stats_total ;
   foreach $j (keys (%stats_noms)) {
      $stats_noms_total {$j} += $stats_noms {$j} ;
   }

   # Remise à zéro du tableau de stats du mois
   undef %stats_noms ;
   $stats_mineure = 0 ;
   $stats_derniere = 0 ;
   $stats_total = 0 ;
   $premier_mois = 0 ;

   $nombre_mois++ ;

   $mois_courant = $mois_ligne ;
   # On est passé ici parce que la ligne courante avait changé de mois ;
   # on met à jour cette ligne courante dans son nouveau mois.
         # à réécrire, genre, à 100 %.
         if ($sources % 2 == 1) {
            $base_nom = "Contributions" ;
            mise_a_jour_stats_contributions ;
         }
         if ($sources % 4 >= 2) {
            $base_nom = "Log" ;
            mise_a_jour_stats_logs ;
         }
}

if ($nombre_max_pages == 0) {
   $fin = 1 ;
} else {
   $fin = 0 ;
}

$i = 0 ;
# Le nombre maximum de pages à traiter, $max_i,
# suit plusieurs contraintes.
my $max_i ;

if (($nombre_max_pages <= 0) and ($action % 2 == 1)) {
   $max_i = $pages_recuperees ;
} elsif ($nombre_max_pages >= 0) {
   $max_i = $nombre_max_pages ;
} else {
   # cas $nom_max_pages = -1 et $action ne contient pas download :
   # on n'a aucun moyen de savoir combien de pages il faut traiter.
   # On le déterminera à partir du glob.
   $max_i = -1 ;
}

# Liste des fichiers précédemment téléchargés. On le récupère de toute
# façon, même si ce n'est obligatoire que dans certains cas (voir juste
# au-dessus), c'est une sécurité complémentaire qui ne mange pas de pain.
my @liste_fichiers = glob "${projet}-${utilisateur}-${base_nom}-*.html" ;
my $nom_fichier ;
while (! $fin) {
   $i += 1 ; # nombre de fichiers ouverts
   if (($max_i >= 0) and ($i >= $max_i)) {
      # Au cas où l'utilisateur a spécifié un nombre maximum de pages et
      # qu'on atteint ce nombre, c'est la dernière page que l'on va traiter.
      $fin = 1 ; 
   }
#foreach $i (1..$max_i) {
   # Nous vérifions que nous disposons bien du fichier en question avant
   # de l'ouvrir.
   $nom_fichier = "${projet}-${utilisateur}-${base_nom}-${i}.html" ;
   if (grep /$nom_fichier/, @liste_fichiers) {
      open FIC3, $nom_fichier ;
      while ($ligne = <FIC3>) {
         next unless ($ligne =~ m/^<li>/) ;
         # Update the regex for your project here and uncomment the appropriate line.
         $ligne =~ m/^<li>[0-9]* ([^ ]* [0-9]*)[^0-9]/ ; # marche sur fr:
         # $ligne =~ m/^<li>.* [0-9]*([^0-9]* de 200[0-9]) \(<a/ ; # marche sur pt
         #$ligne =~ m/^<li>.* [0-9]*([^0-9]* 200[0-9]) \(<a/ ; # marche sur fr: et en:
         $mois_ligne = $1 ;
         #print "salut : $mois_ligne\n" ;
         if (! $mois_defini) { # Premier passage
            $mois_courant = $mois_ligne ;
            $mois_defini = 1 ;
            # Les traitements seront faits à l'instruction conditionnelle suivante.
         }
         if ($mois_ligne eq $mois_courant) {
            # à réécrire, genre, à 100 %.
            if ($sources % 2 == 1) {
               $base_nom = "Contributions" ;
               mise_a_jour_stats_contributions ;
            }
            if ($sources % 4 >= 2) {
               $base_nom = "Log" ;
               mise_a_jour_stats_logs ;
            }
         } else {
            affiche_stats ;
         }
      }
      close FIC3 ;
   } else {
      # Nous ne disposons pas de ce fichier.
      $fin = 1 ;
   }
}
affiche_stats ; # Le mois en cours
my $taille = 35*$nombre_mois ;
if ($taille < 100) {
   $taille = 100 ;
}
my $taille_inf = $taille - 30 ;
if ($format_frise) {
   print "<timeline>\n" ;
   print "Period = from:0 till:${max_total_mois}\n" ;
   print "ImageSize  = width:600  height:${taille} \n" ;
   print "PlotArea   = width:500  height:${taille_inf}  left:100  bottom:25\n" ;
   print $frise ;
   print "</timeline>\n" ;

   # On en commence une seconde, pour la synthèse.
   # Attention, code dupliqué, à simplifier à l'avenir (avec un _total)
   ###########################################################
   print "<timeline>\n" ;
   print "Period = from:0 till:${stats_total_total}\n" ;
   print "ImageSize  = width:600  height:100 \n" ;
   print "PlotArea   = width:500  height:80  left:100  bottom:20\n" ;
   print "DateFormat = yyyy\n" ;
   print "TimeAxis   = orientation:horizontal\n" ;
   print "ScaleMajor = unit:year increment:1000 start:0\n" ;
   # couleurs : [[:mw:Extension:EasyTimeline#Charts examples]]
   print "Colors =\n  id:neogene   value:rgb(0.99215,0.8,0.54)\n  id:paleogene value:rgb(1,0.7019,0)\n  id:cretaceous   value:rgb(0.5,0.764,0.1098)\n  id:jurassic      value:rgb(0.302,0.706,0.5)\n  id:triassic    value:rgb(0.403,0.765,0.716)\n  id:permian   value:rgb(0.404,0.776,0.867)\n  id:carboniferous     value:rgb(0.6,0.741,0.855)\n  id:devonian  value:rgb(0.6,0.6,0.788)\n  id:silurian  value:rgb(0.694,0.447,0.714)\n  id:ordovician      value:rgb(0.976,0.506,0.651)\n  id:cambrian  value:rgb(0.984,0.5,0.373)\n  id:neoproterozoic    value:rgb(0.792,0.647,0.583)\n  id:mesoproterozoic    value:rgb(0.867,0.761,0.533)\n  id:paleoproterozoic    value:rgb(0.702,0.698,0.369)\n  id:eoarchean    value:rgb(0.5,0.565,0.565)\n  id:paleoarchean    value:rgb(0.6,0.592,0.569)\n  id:mesoarchean    value:rgb(0.698,0.65,0.6)\n  id:neoarchean    value:rgb(0.796,0.804,0.784)\n  id:cryogenian    value:rgb(0.863,0.671,0.667)\n  id:tonian        value:rgb(0.796,0.643,0.424)\n  id:stratherian   value:rgb(1,1,0.8)\n  id:mesozoic   value:rgb(0.5,0.6784,0.3176)\n" ;
   print "PlotData=\n" ;

   print "  bar:___ color:red width:30 mark:(line,white) align:left fontsize:S\n" ;
#    undef @clefs ;
#    foreach $j (keys (%couleurs)) {
#       if (defined $stats_noms_total {$j}) {
#          push @clefs, $j ;
#       }
#    }
   @clefs = sort keys %stats_noms ;
   $total_courant = 0 ;
   $ancien_total_courant = 0 ;
   foreach $j (@clefs) {
      $couleur = $couleurs {$j} ;
      $valeur_courante = $stats_noms_total {$j} ;
      $total_courant += $valeur_courante ;
      print "  color:${couleur} from:${ancien_total_courant} till:${total_courant} shift:(0,0) fontsize:XS text:${valeur_courante}\n" ;
      $ancien_total_courant = $total_courant ;
   }
   if ((defined ($nom_derniere)) and ($stats_derniere != 0)) {
      print "  bar:${mois_imprimable}_ color:red  width:5  mark:(line,white) align:left fontsize:XS\n" ;
      print "  bar:${mois_imprimable}_ color:red  from:0 till:${stats_derniere} shift:(0,0) fontsize:XS text:${stats_derniere}\n" ;
   }
   if ((defined ($nom_mineure)) and ($stats_derniere != 0)) {
      print "  bar:${mois_imprimable}_ color:green  width:5  mark:(line,white) align:left fontsize:XS\n" ;
      print "  bar:${mois_imprimable}_ from:0 till:${stats_mineure} shift:(0,0) fontsize:XS text:${stats_mineure}\n" ;
   }
   ###########################################################

    print "</timeline>\n" ;
} else {
   print "== +++ ===\n" ;
#    undef @clefs ;
#    foreach $j (keys (%couleurs)) {
#       if (defined $stats_noms_total {$j}) {
#          push @clefs, $j ;
#       }
#    }
   @clefs = sort keys %stats_noms_total ;
   foreach $j (@clefs) {
      $temp = $stats_noms_total {$j} / $stats_total_total * 100 ;
      print "${j} : ", $stats_noms_total {$j}, " (${temp} %)\n" ;
   }
   print "\n" ;
   if ((defined ($nom_derniere)) and ($stats_derniere_total != 0)) {
      $temp = $stats_derniere_total / $stats_total_total * 100 ;
      print "${nom_derniere} : $stats_derniere_total ($temp %)\n" ;
   }
   if ((defined ($nom_mineure)) and ($stats_derniere_total != 0)) {
      $temp = $stats_mineure_total / $stats_total_total * 100 ;
      print "${nom_mineure} : $stats_mineure_total ($temp %)\n" ;
   }
   print "-> $stats_total_total\n\n" ;
}

} # if ($action % 8 >= 4) { # 4 -> affichage


__END__