Aller au contenu

MediaWiki:Gadget-translation editor.js/Statistiques/code/partie 1

Définition, traduction, prononciation, anagramme et synonyme sur le dictionnaire libre Wiktionnaire.
#include <iostream>
#include <fstream>
#include <string>
#include <map>

using namespace std;

// Dump à télécharger à : https://dumps.wikimedia.org/frwiktionary/latest/ :
// frwiktionary-latest-pages-meta-history.xml.7z , puis à décompresser
string input_folder = "C:\\Users\\automatik\\Documents\\Wiktionnaire\\Stats translation editor\\";
string input_file_history = input_folder + "frwiktionary-latest-pages-meta-history.xml";

// Chemins des fichiers contenant les résultats
string output_folder = "C:\\Users\\automatik\\Documents\\Wiktionnaire\\Stats translation editor\\";
string output_file_res = output_folder + "stats-summary.txt"; // Chiffres-clés
string output_file_langs = output_folder + "stats-langs.txt"; // Stats par langue
string output_file_dates = output_folder + "stats-months.txt"; // Stats par mois
string output_file_contribs = output_folder + "stats-trads-contributors.txt"; // Stats par contributeur
string output_file_ips = output_folder + "stats-trads-ips.txt"; // Stats par IP
string output_file_diffs = output_folder + "stats-trads-diffs-to-check.txt"; // Diffs à analyser en Partie 2

string from_date = "2014-07"; // Mois à partir duquel on dénombre les ajouts (mois inclus)
string to_date = "2024-01"; // Mois jusqu’auquel on dénombre les ajouts (mois exclus)

/**** STATS ****/
std::map<string, int> stats_langs = {}; // exemple (en début d’analyse) : { "italien": 3, "allemand" : 8 }
std::map<string, int> stats_dates = {}; // ex : { "2014-08": 6169, "2014-09" : 6203 }
std::map<string, int> stats_contribs = {}; // ex : { "Un utilisateur": 213, "Un deuxième utilisateur": 65 }
std::map<string, int> stats_ips = {}; // ex : { "184.252.23.16": 11, "174.145.64.87": 75 }
// Maps pour stocker temporairement des résultats (pour être sûr que les trads analysées n'ont pas été révoquées)
// Ces maps sont transférées vers les "map" finaux ci-dessus une fois qu'on est sûr que les traductions concernées n'ont pas été révoquées
// Note : pour s'assurer que les ajouts ne sont pas révoqués, on lit tous les résumés d'édition qui succèdent à un ajout de traduction
// jusqu'à ce qu'on tombe sur une révocation ou autre chose qu'un ajout de traduction (via l'outil). Si l'ajout correspond à la dernière version
// de l'article, on le considère comme non révoqué
std::map<string, int> stats_langs_tmp = {};
std::map<string, int> stats_dates_tmp = {};
std::map<string, int> stats_contribs_tmp = {};
std::map<string, int> stats_ips_tmp = {};

unsigned int counter_pages = 0; // Nombre de pages parcourues dans le dump (simplement pour afficher la progression de la tâche)
unsigned int counter_additions = 0; // Nombre d'ajouts avec l'outil de traductions (ajouts révoqués ou non)
unsigned int counter_pages_with_trans = 0; // Nombre de pages avec au moins un ajout de traduction non révoqué
unsigned int counter_trans = 0; // Nombre de traductions ajoutées et non révoquées
unsigned int total_count_contribs = 0, total_count_ips = 0; // Nombre de traductions non révoquées ajoutées par des contributeurs enregistrés / non enregistrés
unsigned int total_count_contribs_tmp = 0, total_count_ips_tmp = 0; // Dénombrements temporaires transférés si traductions pas révoquées

unsigned int counter_additions_contributors = 0; // Nombre d’ajouts de traduction(s) par des utilisateurs… inscrits
unsigned int counter_additions_ips = 0;          //         "           "           "           "        … non inscrits
unsigned int revoked_contributor_additions = 0; // Nombre de traductions révoquées… de contributeurs inscrits
unsigned int revoked_ip_additions = 0;      //      "          "        "     … de contributeurs non inscrits

string remember; // diffs à mettre de côté car résumé d'édition tronqué
string remember_tmp; // Variable temporaire (transférée si traductions correspondantes non révoquées)
int cnt_rem = 0; // Nombre de diffs à analyser
int cnt_rem_tmp = 0;
/**************/

// Transfert d’une map (temporaire) à une autre (dénombrant les résultats finaux)
void transfer_between_maps(map<string, int> &map_source, map<string, int> &map_dest) {
    for(auto const &item : map_source) {
        if (map_dest[item.first]) {
            map_dest[item.first] += item.second;
        } else {
            map_dest[item.first] = item.second;
        }
    }
}
// Vide les variables temporaires après un transfert
void empty_all_tmp_vars() {
    stats_langs_tmp = {};
    stats_dates_tmp = {};
    stats_contribs_tmp = {};
    stats_ips_tmp = {};
    total_count_contribs_tmp = 0;
    total_count_ips_tmp = 0;
    cnt_rem_tmp = 0;
    remember_tmp = "";
}
// Transfert de toutes les variables temporaires vers leurs versions persistantes
void transfer_all_tmp_vars() {
    transfer_between_maps(stats_langs_tmp, stats_langs);
    transfer_between_maps(stats_dates_tmp, stats_dates);
    transfer_between_maps(stats_contribs_tmp, stats_contribs);
    transfer_between_maps(stats_ips_tmp, stats_ips);
    total_count_contribs += total_count_contribs_tmp;
    total_count_ips += total_count_ips_tmp;
    counter_trans += (total_count_contribs_tmp + total_count_ips_tmp);
    remember += remember_tmp;
    cnt_rem += cnt_rem_tmp;
    empty_all_tmp_vars();
}

// Fonction effectuant le traitement de la Partie 1
void count_trads_added_with_te()
{
    /**** FICHIERS D'ENTREE ET DE SORTIE ****/
    ifstream infile(input_file_history, ifstream::in);
    if (!infile) {
        cout << "Le dump n'est pas situe a " << input_file_history << endl;
        return;
    }
    ofstream out_res(output_file_res, ofstream::out);
    if (!out_res) {
        cout << "Probleme avec le fichier de sortie global " << endl;
        return;
    }
    ofstream out_langs(output_file_langs, ofstream::out);
    if (!out_langs) {
        cout << "Probleme avec le fichier de sortie sur les langues" << endl;
        return;
    }
    ofstream out_dates(output_file_dates, ofstream::out);
    if (!out_dates) {
        cout << "Probleme avec le fichier de sortie sur les dates" << endl;
        return;
    }
    ofstream out_contribs(output_file_contribs, ofstream::out);
    if (!out_contribs) {
        cout << "Probleme avec le fichier de sortie sur les contributeurs" << endl;
        return;
    }
    ofstream out_ips(output_file_ips, ofstream::out);
    if (!out_ips) {
        cout << "Probleme avec le fichier de sortie sur les ips" << endl;
        return;
    }
    ofstream out_diff(output_file_diffs, ofstream::out);
    if (!out_diff) {
        cout << "Probleme avec le fichier de sortie de diffs" << endl;
        return;
    }
    /***************************************/

    /*** PARAMETRES ***/
    // Pour s’assurer que la révision en cours est bien dans la période d’analyse désirée (et définie plus haut)
    int from_year = stoi(from_date.substr(0, 4));
    int from_month = stoi(from_date.substr(5, 2));
    int to_year = stoi(to_date.substr(0, 4));
    int to_month = stoi(to_date.substr(5, 2));
    /******************/

    /**** REVISION EN COURS ****/
    bool pending_rev = false;
    bool additions_to_count = false; // Ajouts à décompter ? Variable vidée après s'être assurée que les ajouts décomptés n'ont pas été annulés
    unsigned int ip_additions_to_count = 0;      // Nombre d'ajouts d'IPs à décompter comme révoqués si tel est le cas
    unsigned int contrib_additions_to_count = 0; //      "      "   de contributeurs    "      "         "      "
    bool id_read = false;
    string title, rev_id, prev_rev_id; // Informations utiles pour les ajouts dont on veut analyser les diffs
    int ns = -1;
    string tstamp; // Timestamp de la révision en cours d'analyse
    int year, month;
    string contributor; // Nom du contributeur ou adresse IP
    bool is_ip = false; // Si le contributeur est enregistré ou non
    string comment = ""; // résumé d’édition en cours d’analyse
    string lang; // langue de la traduction en cours d’analyse (langues des traductions mentionnées dans le résumé d'édition)

    string line; // Ligne du dump actuellement en cours d’analyse
    size_t pos1, pos2, pos3;

    bool entry_with_trans = false;
    /***************************/

    while (getline(infile, line)) {
        if (line.find("<title>") != string::npos) {
            pos1 = line.find("<title>");
            pos2 = line.find("</title>");
            title= line.substr(pos1+7, pos2-pos1-7);
            counter_pages++;
        }
        // Si pas main, on passe
        if (line.find("<ns>") != string::npos) {
            pos1 = line.find("<ns>");
            pos2 = line.find("</ns>");
            ns = stoi(line.substr(pos1+4, pos2-pos1-4));
        }
        if (ns != 0) {
            continue;
        }
        if (line.find("<revision>") != string::npos) {
            pending_rev = true;
            id_read = false;
        }
        if (pending_rev) {
            if (!id_read && line.find("<id>") != string::npos) {
                pos1 = line.find("<id>");
                pos2 = line.find("</id>");
                prev_rev_id = rev_id;
                rev_id = line.substr(pos1+4, pos2-pos1-4);
                id_read = true;
            }
            // On verifie que l'édition est faite dans la période choisie
            if (line.find("<timestamp>") != string::npos) {
                pos1 = line.find("<timestamp>");
                pos2 = line.find("</timestamp>");
                tstamp = line.substr(pos1+11, 7);
                year = stoi( tstamp.substr(0, 4) );
                month = stoi( tstamp.substr(5, 2) );
                if (year < from_year || (year == from_year && month < from_month) ||
                    year > to_year || (year == to_year && month >= to_month)
                    ) {
                    pending_rev = false;
                    if (additions_to_count) {
                        transfer_all_tmp_vars();
                        additions_to_count = false;
                    }
                    continue;
                }
            }
            // Pour un utilisateur on aura dans le fichier XML <username></username>, tandis que pour une IP on aura <ip></ip>
            if (line.find("<username>") != string::npos) {
                pos1 = line.find("<username>");
                pos2 = line.find("</username>");
                contributor = line.substr(pos1+10, pos2-pos1-10);
                is_ip = false;
            }
            if (line.find("<ip>") != string::npos) {
                pos1 = line.find("<ip>");
                pos2 = line.find("</ip>");
                contributor = line.substr(pos1+4, pos2-pos1-4);
                is_ip = true;
            }

            // On analyse tous les commentaires des pages de l’espace principal à la recherche de
            // résumés d’édition correspondant à des ajouts traductions avec Translation editor
            if (line.find("<comment>") != string::npos) {
                pos1 = line.find("<comment>");
                pos2 = line.find("</comment>");
                comment = line.substr(pos1+9, pos2-pos1-9);
                if (comment.substr(0, 15) == "Traductions : +") { // cas 1 : traductions ajoutées avec l'outil : on met de côté
                                                                // avant de s’assurer qu'elles n'ont pas été révoquées
                    additions_to_count = true;
                    counter_additions++;
                    if (is_ip) {
                        ip_additions_to_count++;
                        counter_additions_ips++;
                    } else {
                        contrib_additions_to_count++;
                        counter_additions_contributors++;
                    }
                    // Si le résumé d’édition est tronqué, on garde les infos de côté pour analyser + tard les diffs
                    if (comment.find("(assist") == string::npos) {
                        remember_tmp += "title=" + title + ";prev_rev_id=" + prev_rev_id + ";rev_id=" + rev_id +
                                    ";contrib=" + contributor + ";is_ip=" + (is_ip ? "true" : "false") +
                                    ";date=" + tstamp + "\n";
                        cnt_rem_tmp++;
                        continue;
                    }
                    entry_with_trans = true;
                    pos3 = 11;
                    do {
                        pos3 += 4;
                        lang = comment.substr(pos3, comment.find(" :", pos3) - pos3);
                        if (lang == "en-tête") continue; // On ignore les modifications d’en-tête
                        if (stats_langs_tmp.count(lang)) {
                            stats_langs_tmp[lang]++;
                        } else {
                            stats_langs_tmp[lang] = 1;
                        }
                        if (stats_dates_tmp.count(tstamp)) {
                            stats_dates_tmp[tstamp]++;
                        } else {
                            stats_dates_tmp[tstamp] = 1;
                        }
                        if (!is_ip) {
                            if (stats_contribs_tmp.count(contributor)) {
                                stats_contribs_tmp[contributor]++;
                            } else {
                                stats_contribs_tmp[contributor] = 1;
                            }
                            total_count_contribs_tmp++;
                        } else {
                            if (stats_ips_tmp.count(contributor)) {
                                stats_ips_tmp[contributor]++;
                            } else {
                                stats_ips_tmp[contributor] = 1;
                            }
                            total_count_ips_tmp++;
                        }
                        pos3 = comment.find(" ; +", pos3);
                    } while (pos3 != string::npos); // On répète la boucle pour toutes les traductions présentes dans le résumé d’édition
                } else if ((comment.substr(0, 15) == "R\u00E9vocation des"
                            || comment.substr(0, 14) == "Annulation des")
                            && additions_to_count
                           ) { // cas 2 : révocation d'ajout(s) de traduction : on ignore
                    revoked_contributor_additions += contrib_additions_to_count;
                    revoked_ip_additions += ip_additions_to_count;
                    empty_all_tmp_vars();
                    additions_to_count = false;
                    ip_additions_to_count = 0;
                    contrib_additions_to_count = 0;
                    entry_with_trans = false;
                } else if (additions_to_count) { // cas 3 : non-révocation après ajout de traductions :
                                                 // on compte les trads précédemment analysées
                    transfer_all_tmp_vars();
                    additions_to_count = false;
                    ip_additions_to_count = 0;
                    contrib_additions_to_count = 0;
                }
            }
            if (line.find("</revision>") != string::npos) {
                pending_rev = false;
            }
        }
        if (line.find("</title>") != string::npos) {
            if (additions_to_count) { // cas ou la derniere modif d'une page est un ajout de traduction (non révoqué) :
                                    // on prend alors en compte les ajouts de trads
                transfer_all_tmp_vars();
                additions_to_count = false;
                ip_additions_to_count = 0;
                contrib_additions_to_count = 0;
            }
            if (entry_with_trans) {
                counter_pages_with_trans++;
                // Tous les 100 pages modifiées avec Translation editor, on affiche des stats récapitulatives
                if (counter_pages_with_trans % 100 == 0) {
                    cout << counter_pages_with_trans << " pages modifiees sur " <<
                        counter_pages << " scannees - " << counter_trans << " trads ajoutees avec TE (et "
                                        << revoked_contributor_additions + revoked_ip_additions << "/"
                                        << counter_additions << " ajouts revoques) contribs: "
                                        << counter_additions_contributors << " ; IPs: " << counter_additions_ips << endl;
                }
                // Tous les 10 000 pages modifiées avec Translation editor, on affiche les stats accumulés jusqu’alors
                // (pour le suivi et le repérage anticipé d’aberrations dans les résultats)
                if (counter_pages_with_trans % 10000 == 0) {
                    for (const auto &p : stats_langs) {
                        std::cout << p.first << " : " << p.second << '\n';
                    }
                    for (const auto &p : stats_dates) {
                        std::cout << p.first << " : " << p.second << '\n';
                    }
                    for (const auto &p : stats_contribs) {
                        std::cout << p.first << " : " << p.second << '\n';
                    }
                    std::cout << "Total contributeurs : " << total_count_contribs << '\n';
                    std::cout << "Total IPs : " << total_count_ips << '\n';
                    cout << cnt_rem << " diffs a analyser en +" << endl;
                }
            }
            entry_with_trans = false;
        }
    }

    cout << cnt_rem << " diffs a analyser en +" << endl;
    cout << counter_pages_with_trans << " pages modifiees avec translation_editor (" << counter_trans << " traductions)" << endl;

    infile.close();

    out_res  << "* Traductions ajoutées : " << counter_trans << endl;
    out_res << "* Ajouts avec l'outil : " << counter_additions << " (contributeurs : " << counter_additions_contributors
            << " ; IPs : " << counter_additions_ips << ")" << endl;
    out_res << "* Pages modifiées avec translation_editor : " << counter_pages_with_trans << endl;
    out_res << "* Traductions ajoutées par des utilisateurs inscrits : " << total_count_contribs << endl;
    out_res << "* Traductions ajoutées par des utilisateurs non inscrits : " << total_count_ips << endl;
    out_res << "* Ajouts de contributeurs révoqués : " << revoked_contributor_additions << endl;
    out_res << "* Ajouts d'IPs révoqués : " << revoked_ip_additions << endl;

    // Ecriture des resultats dans le fichier de sortie
    for (const auto &p : stats_langs) {
        out_langs << p.first << " : " << p.second << endl;
    }
    for (const auto &p : stats_dates) {
        out_dates << p.first << " : " << p.second << endl;
    }
    for (const auto &p : stats_contribs) {
        out_contribs << p.first << " : " << p.second << endl;
    }
    for (const auto &p : stats_ips) {
        out_ips << p.first << " : " << p.second << endl;
    }

    out_res.close();
    out_langs.close();
    out_dates.close();
    out_contribs.close();
    out_ips.close();

    // Entrees restantes à traiter en analysant les diffs => dans un fichier a part
    out_diff << remember;
    out_diff.close();
}

int main()
{
    count_trads_added_with_te();

    cout << "End of processing!" << endl;
    return 0;
}
// Traductions décomptées sur le dump du 1/12/2023 : 494138

Ce script a pris ~30 minutes à s’exécuter (Windows 10, processeur quadricore (2.3GHz), 8 Go de RAM).