« MasterBot » : différence entre les versions

De Wikipast
Aller à la navigation Aller à la recherche
Aucun résumé des modifications
Aucun résumé des modifications
 
(16 versions intermédiaires par le même utilisateur non affichées)
Ligne 8 : Ligne 8 :
Le [[MasterBot]] se charge de gérer et d'orchestrer les [[bots]] de wikipast. D'une part, il permet de lancer les bots de manière individuelle, en entrant des paramètres depuis un tableau de bord. D'autre part, il permet personaliser des séquences de lancement de bots, en spécifiant les paramètres (i.e. la fréquence le lancement, l'ordre, etc) de chaque bot.  
Le [[MasterBot]] se charge de gérer et d'orchestrer les [[bots]] de wikipast. D'une part, il permet de lancer les bots de manière individuelle, en entrant des paramètres depuis un tableau de bord. D'autre part, il permet personaliser des séquences de lancement de bots, en spécifiant les paramètres (i.e. la fréquence le lancement, l'ordre, etc) de chaque bot.  


==Lancer les bots individuellement==
==Implémentation et fonctionnalités==


Dans un premier temps, l'idée est d'avoir le code de chaque bot sur une page (ou section d'une page) spécifique. Cela permet de lancer les bots à partir du code disponible sur la page de chacun (ou même depuis Github). Ensuite, il suffit d'avoir un script pour lancer chacun des bots (aves les paramètres nécessaires, par ex. la page à traduire pour le translatorBot). Il faudrait pouvoir faire tourner tout cela sur une page "tableau de bord" sur wikipast ou à l'aide d'une simple application web.
L'idée est d'avoir le code de chaque bot sur une page (ou section d'une page) spécifique. Cela permet de lancer les bots à partir du code disponible sur la page de chacun (ou même depuis Github). Une simple API créée avec flask  se charge de gérer les requêtes reçues. Tous les bots et leurs informations pertinentes (nom, adresse du code, ..description) sont stockés dans un dictionnaire. L'API est dotée d'une interface web graphique qui facilite l'interaction avec le bot.  


===Les scripts===
===Dictionnaire des bots===  


Chaque bot a son propre script qui se charge des tâches suivantes:
Le dictionnaire des bots contient le nom du bot, l'adresse où son code se trouve, le nom d'une fonction main à appeler (si nécessaire), et une brève description du bot.
# Parser les paramètres de la ligne de commande
# Récupérer le code du bot (de wikipast ou de Github) (TODO)
# Lancer le bot avec les paramètres donnés


===L'interface graphique===
===Lancer un bot===


J'ai utilisé l'interface graphique Wooey [https://wooey.readthedocs.io/en/latest/index.html] qui permet de lancer des scripts Pythons. J'ai testé le tout avec [[TranslatorBot]] sur les pages de [[Lausanne]] et de [[David Bowie]]. [[File:Wooey home.png|center|450px]] [[File:Wooey done.png|center|450px]]
Pour lancer un bot, il suffit d'effectuer une requête GET. Il faut passer en paramètre le nom du bot, et éventuellement, les paramètres requis. Par exemple, pour lancer le bot [[SummarizingBot]] sur les pages David Bowie et Mario Botta il suffit de faire:
http://localhost:5000/api/v1/bots/run?name=SummarizingBot&params=David+Bowie+,+Mario+Botta


==Code==
Ceci va appeler la fonction run_bot_request(), qui va parser et formatter les paramètres de la requête (dans l'exemple, le nom et les deux pages), et appeler la méthode auxiliaire run_bot(). Cette dernière va récupérer le code du bot à l'aide de la fonction retrieve_bot() puis executer le code correspondant. Dans certains cas, il faut appeler une certaine fonction et lui donner des paramètres. Un message de succès s'affiche lors de la fin d'execution du bot, et tous les messages print du bot sont écrits dans un fichier (changement du stdout vers un fichier out.txt). Si le bot est introuvable, la fonction run_bot() retourne -1 et la fonction plus générale retourne un message d'erreur. L'affichage est formaté à l'aide d'un simple template html. 


<nowiki>
==== Récupérer le code d'un bot ====
import requests
import re
from bs4 import BeautifulSoup
from googletrans import Translator


# the function takes a table of strings as argument containing the names of the pages to translate
La fonction vérifie si l'adresse où se trouve le code (dans le dictionnaire des bots) contient le mot clé "GitHub" ou "wikipast". Si le code se trouve sur wikipast, la fonction assume qu'il est dans une seule section "Code" et entouré des tags "nowiki". Si le code se trouve sur GitHub, la fonction assume que le code est disponible dans un seul et unique fichier (et donc ne dépend pas de fichier externes). Après la réorganisation des bots, le code de chacun devrait se trouver sur une page wikipast au format Code_[nom du bot].
def translate(*names):
    user='testbot'
    passw='dhbot2017'
    baseurl='http://wikipast.epfl.ch/wikipast/'
    summary='Wikipastbot update'
    translator = Translator()


    # this parameter is the target language in which we want to translate
===Obtenir de l'information sur les différents bots===
    target_lang = 'en'
    target_language = 'English'


    # login request
Pour obtenir une brève description du bot il suffit d'effectuer la requête suivante (en passant le nom du bot en paramètre)
    payload={'action':'query','format':'json','utf8':'','meta':'tokens','type':'login'}
http://localhost:5000/api/v1/bots/info?name=SummarizingBot
    r1=requests.post(baseurl + 'api.php', data=payload)


    # login confirm
Pour avoir une vue d'ensemble sur tous les bots disponibles:
    login_token=r1.json()['query']['tokens']['logintoken']
http://localhost:5000/api/v1/bots/all
    payload={'action':'login','format':'json','utf8':'','lgname':user,'lgpassword':passw,'lgtoken':login_token}
    r2=requests.post(baseurl + 'api.php', data=payload, cookies=r1.cookies)


    # get edit token2
===Séquence de lancement des bots===
    params3='?format=json&action=query&meta=tokens&continue='
    r3=requests.get(baseurl + 'api.php' + params3, cookies=r2.cookies)
    edit_token=r3.json()['query']['tokens']['csrftoken']


    edit_cookie=r2.cookies.copy()
Pour pouvoir lancer une séquence de bots, il faut pouvoir uploader un fichier texte contenant un nom de bot par ligne. La fonction upload_sequence() se charge de vérifier la validité du fichier (extension valide .txt et fichier existant), de le renommer en "sequence.txt", et de le sauvegarder. Le formulaire d'upload "upload_form.html" envoie donc une requête POST à l'adresse
    edit_cookie.update(r3.cookies)
http://localhost:5000/api/v1/config/sequence
lors de la soumission d'un fichier.


    # we fetch the text we want to translate
Ensuite, il faut pouvoir lire et parser les lignes de ce fichier, et pour chaque ligne, lancer le bot correspondant avec comme paramètre toutes les pages a traduire périodiquement (i.e. biographies ou les pages traduites récemment). Lors de la réception de la requête GET
    for name in names:
http://localhost:5000/api/v1/bots/sequence
        result=requests.post(baseurl+'api.php?action=query&titles='+name+'&export&exportnowrap')
la fonction run_sequence() se charge de trouver le fichier, l'ouvrir, et lancer chaque bot dans l'ordre précisé à l'aide de la fonction run_bot(). Lors du lancement de la séquence, un registre documente toutes les actions effectuées (ouverture du fichier séquence, lancement d'un bot, son succès ou son échec...). Les bots n'étant pas tous parfaits, leur exécution sur chaque paramètre se fait dans un bloc try, pour ne pas empêcher l'exécution du reste de la séquence lors d'une erreur.  
        soup=BeautifulSoup(result.text, "lxml")
        code=''
        for primitive in soup.findAll("text"):
            code += primitive.string


        # create names with english prefix
==Limitations==
        en_name = "(" + target_lang + ")_" + translator.translate(name, src='fr', dest=target_lang).text


        # add a table in the french page if it still not exists
Le code des bots est soit sur gitub, soit directement sur la page wiki du bot. Cependant, la structure des bots n'est pas homogène. Certains bots sont programmés pour agir sur des pages spécifiques, ou sur toutes les pages des biographies: il n'y a aucun moyen de spécifier une page précise. D'autre part, le code de certains bots est éparpillé sur plusieurs fichiers python: cela n'est plus en accord avec les assomptions de la fonction qui récupère le code des bots.
        if(code != '' and code[0] != '{'and code[0] != '|') :
Pour remédier à cela, une page Wikipast a été crée pour stocker le code de chaque bot. Cela facilitera la lecture du code par le MasterBot, et permettra de définir une "convention" pour les futurs bots.
          code2 = '''{| class="wikitable"\n|Langue \n|''' + "'''Français'''\n|[[" + en_name + "|" + target_language + "]]\n|}\n" + code
Aussi, la structure des bots a été harmonisée de façon à ce que chaque bot ait une fonction main prenant en paramètre un nombre indéfini de noms de pages.  
          payload2={'action':'edit','assert':'user','format':'json','utf8':'','text':code2,'summary':summary,'title':name,'token':edit_token}
          r5=requests.post(baseurl+'api.php',data=payload2,cookies=edit_cookie)


        # save the links of sources that we won't translate
Du point de vue performances, le temps d'execution d'une séquence du MasterBot depend vraiment des performances individuelles de chaque bot. Vu que chaque job est exécuté séquentiellement et que l'ordre d'exécution est important, il est difficile d'optimiser les performances du MasterBot. Il faut attendre qu'un premier bot s'execute pour pouvoir lancer le suivant.
        sources = []
        i=0
        while i< len(code):
            if (code[i-1] != '[' and code[i] == '[' and code[i+1] != '[') :
                j = i+2
                while(code[j] != ']') :
                    j += 1
                sources.append(code[i:j+1])       
                code = code.replace(code[i:j+1], "&&&", 1)
                i = j+1
            else:
                i += 1


        # translate the whole text by chunk of approx. 5000 characters.
Ci-dessous, un tableau qui résume les bots posants problème pour l'intégration au MasterBot
        length = len(code)
        chaine =''
        punto = '.'
        k = 0
        diminution = 1
        last = k+5000
        while last < length:       
            if code[last] == punto:
                chaine += translator.translate(code[k:last], src = 'fr', dest= target_lang).text
                k = last
            else:
                while code[k+5000-diminution] != punto:
                    diminution += 1
                chaine += translator.translate(code[k:k+5000-diminution],src = 'fr', dest= target_lang).text
                k = k+5000-diminution+1
                diminution = 1
            last += 5000       
        last -= 5000
        chaine += translator.translate(code[last:length],src='fr',dest= target_lang).text


        translated_text = chaine
{| class="wikitable"
! style="text-align: center;" | Name
! style="text-align: center;" | Code Location
! style="text-align: center;" | Problem
! style="text-align: center;" | Function
|-
| style="text-align: center;" | BioPathBot
| style="text-align: center;" | [[Code_BioPathBot]]
| style="text-align: center;" | Problem installing some dependency
| style="text-align: center;" | main()
|-
| style="text-align: center;" | ChronoBot
| style="text-align: center;" |
| style="text-align: center;" | Source code scattered on multiple files
| style="text-align: center;" |
|-
| style="text-align: center;" | CreatoBot
| style="text-align: center;" | [[Code_Creatobot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | EntryMakerBot
| style="text-align: center;" |
| style="text-align: center;" | Source code scattered on multiple files
| style="text-align: center;" |
|-
| style="text-align: center;" | FormatBot
| style="text-align: center;" | [[Code_FormatBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | GallicaSpqarqlBot
| style="text-align: center;" |
| style="text-align: center;" |
| style="text-align: center;" |
|-
| style="text-align: center;" | GraderBot
| style="text-align: center;" | [[Code_GraderBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | ImageBot
| style="text-align: center;" | [[Code_ImageBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | LinkBot
| style="text-align: center;" | [[Code_LinkBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | MinerRegExBot
| style="text-align: center;" |
| style="text-align: center;" |
| style="text-align: center;" |
|-
| style="text-align: center;" | OrthoBot
| style="text-align: center;" | [[Code_OrthoBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | PageUpdaterBot
| style="text-align: center;" | [[Code_PageUpdaterBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | SimilarBot
| style="text-align: center;" | [[Code_SimilarBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | SliderBot
| style="text-align: center;" |
| style="text-align: center;" | Uses a graphical user interface
| style="text-align: center;" |
|-
| style="text-align: center;" | SourceBot
| style="text-align: center;" | [[Code_SourceBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | SparqlBot
| style="text-align: center;" |
| style="text-align: center;" | Source code scattered on multiple files
| style="text-align: center;" |
|-
| style="text-align: center;" | SummarizingBot
| style="text-align: center;" | [[Code_SummarizingBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | TangoBot
| style="text-align: center;" |
| style="text-align: center;" | Problem installing some dependency
| style="text-align: center;" |
|-
| style="text-align: center;" | TranslatorBot
| style="text-align: center;" | [[Code_TranslatorBot]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | Triplificator
| style="text-align: center;" | [[Code_Triplificator]]
| style="text-align: center;" | -
| style="text-align: center;" | main()
|-
| style="text-align: center;" | VandalBot
| style="text-align: center;" |
| style="text-align: center;" | Source code scattered on multiple files
| style="text-align: center;" |
|-
| style="text-align: center;" | Wikidataficator
| style="text-align: center;" |
| style="text-align: center;" |
| style="text-align: center;" |
|}


        # make the hyperlinks point to the correct page while hiding the (en)
==Exemples==  
        for i in range(len(translated_text)):
            if (translated_text[i] == '[' and translated_text[i+1] == '[' and translated_text[i+2].isalpha()) :
                j = i
                while(translated_text[j] != ']') :
                    j += 1
                m = translated_text[i+2:j]
                linkM = "[[" + m + "]]"
                translated_text = translated_text.replace(linkM, "[[(" + target_lang + ")_" + m + '|' + m + "]]")


        # replace the translates sources by the original ones
Ce bot ne produit pas de résultats particulièrement visibles. En guise de test, j'ai lancé une séquence "ImageBot, TranslatorBot" sur les 1000 pages modifiées récemment. L'activité récente du bot est visible sur cette [http://wikipast.epfl.ch/wikipast/index.php/Spécial:Contributions/MasterBot page]. Le lancement est satisfaisant, mais les résultats ne sont pas forcément bons. La traduction n'est pas tout le temps complète, et elle répète parfois quelque mots. Les images ne correspondent pas toutes au sujet de la page. Ceci est hors de la portée du MasterBot.
        M_final_text = translated_text.split("&&&")
        final_text =""
        for i in range(len(sources)):
            final_text += M_final_text[i]
            final_text += sources[i]
        translated_text = final_text


        # avoid problems due to the comments of other bots
==Code==
        translated_text = translated_text.replace('->', '-->').replace('<! -', '<!--').replace('</ ', '</')
 
        # add the table
        translated_text = '''{| class="wikitable"\n|Language \n|[[''' + name + "|Français]]\n|'''" + target_language + "'''\n|}\n" + translated_text


    # write on the page
Le code source est disponible sur ce [https://github.com/karimassi/masterbot repo GitHub]
    payload={'action':'edit','assert':'user','format':'json','utf8':'','text':translated_text,'summary':summary,'title':en_name,'token':edit_token}
    r4=requests.post(baseurl+'api.php',data=payload,cookies=edit_cookie)
</nowiki>

Dernière version du 8 mai 2019 à 19:50

Langue Français English

Description

Le MasterBot se charge de gérer et d'orchestrer les bots de wikipast. D'une part, il permet de lancer les bots de manière individuelle, en entrant des paramètres depuis un tableau de bord. D'autre part, il permet personaliser des séquences de lancement de bots, en spécifiant les paramètres (i.e. la fréquence le lancement, l'ordre, etc) de chaque bot.

Implémentation et fonctionnalités

L'idée est d'avoir le code de chaque bot sur une page (ou section d'une page) spécifique. Cela permet de lancer les bots à partir du code disponible sur la page de chacun (ou même depuis Github). Une simple API créée avec flask se charge de gérer les requêtes reçues. Tous les bots et leurs informations pertinentes (nom, adresse du code, ..description) sont stockés dans un dictionnaire. L'API est dotée d'une interface web graphique qui facilite l'interaction avec le bot.

Dictionnaire des bots

Le dictionnaire des bots contient le nom du bot, l'adresse où son code se trouve, le nom d'une fonction main à appeler (si nécessaire), et une brève description du bot.

Lancer un bot

Pour lancer un bot, il suffit d'effectuer une requête GET. Il faut passer en paramètre le nom du bot, et éventuellement, les paramètres requis. Par exemple, pour lancer le bot SummarizingBot sur les pages David Bowie et Mario Botta il suffit de faire:

http://localhost:5000/api/v1/bots/run?name=SummarizingBot&params=David+Bowie+,+Mario+Botta

Ceci va appeler la fonction run_bot_request(), qui va parser et formatter les paramètres de la requête (dans l'exemple, le nom et les deux pages), et appeler la méthode auxiliaire run_bot(). Cette dernière va récupérer le code du bot à l'aide de la fonction retrieve_bot() puis executer le code correspondant. Dans certains cas, il faut appeler une certaine fonction et lui donner des paramètres. Un message de succès s'affiche lors de la fin d'execution du bot, et tous les messages print du bot sont écrits dans un fichier (changement du stdout vers un fichier out.txt). Si le bot est introuvable, la fonction run_bot() retourne -1 et la fonction plus générale retourne un message d'erreur. L'affichage est formaté à l'aide d'un simple template html.

Récupérer le code d'un bot

La fonction vérifie si l'adresse où se trouve le code (dans le dictionnaire des bots) contient le mot clé "GitHub" ou "wikipast". Si le code se trouve sur wikipast, la fonction assume qu'il est dans une seule section "Code" et entouré des tags "nowiki". Si le code se trouve sur GitHub, la fonction assume que le code est disponible dans un seul et unique fichier (et donc ne dépend pas de fichier externes). Après la réorganisation des bots, le code de chacun devrait se trouver sur une page wikipast au format Code_[nom du bot].

Obtenir de l'information sur les différents bots

Pour obtenir une brève description du bot il suffit d'effectuer la requête suivante (en passant le nom du bot en paramètre)

http://localhost:5000/api/v1/bots/info?name=SummarizingBot

Pour avoir une vue d'ensemble sur tous les bots disponibles:

http://localhost:5000/api/v1/bots/all

Séquence de lancement des bots

Pour pouvoir lancer une séquence de bots, il faut pouvoir uploader un fichier texte contenant un nom de bot par ligne. La fonction upload_sequence() se charge de vérifier la validité du fichier (extension valide .txt et fichier existant), de le renommer en "sequence.txt", et de le sauvegarder. Le formulaire d'upload "upload_form.html" envoie donc une requête POST à l'adresse

http://localhost:5000/api/v1/config/sequence

lors de la soumission d'un fichier.

Ensuite, il faut pouvoir lire et parser les lignes de ce fichier, et pour chaque ligne, lancer le bot correspondant avec comme paramètre toutes les pages a traduire périodiquement (i.e. biographies ou les pages traduites récemment). Lors de la réception de la requête GET

http://localhost:5000/api/v1/bots/sequence

la fonction run_sequence() se charge de trouver le fichier, l'ouvrir, et lancer chaque bot dans l'ordre précisé à l'aide de la fonction run_bot(). Lors du lancement de la séquence, un registre documente toutes les actions effectuées (ouverture du fichier séquence, lancement d'un bot, son succès ou son échec...). Les bots n'étant pas tous parfaits, leur exécution sur chaque paramètre se fait dans un bloc try, pour ne pas empêcher l'exécution du reste de la séquence lors d'une erreur.

Limitations

Le code des bots est soit sur gitub, soit directement sur la page wiki du bot. Cependant, la structure des bots n'est pas homogène. Certains bots sont programmés pour agir sur des pages spécifiques, ou sur toutes les pages des biographies: il n'y a aucun moyen de spécifier une page précise. D'autre part, le code de certains bots est éparpillé sur plusieurs fichiers python: cela n'est plus en accord avec les assomptions de la fonction qui récupère le code des bots. Pour remédier à cela, une page Wikipast a été crée pour stocker le code de chaque bot. Cela facilitera la lecture du code par le MasterBot, et permettra de définir une "convention" pour les futurs bots. Aussi, la structure des bots a été harmonisée de façon à ce que chaque bot ait une fonction main prenant en paramètre un nombre indéfini de noms de pages.

Du point de vue performances, le temps d'execution d'une séquence du MasterBot depend vraiment des performances individuelles de chaque bot. Vu que chaque job est exécuté séquentiellement et que l'ordre d'exécution est important, il est difficile d'optimiser les performances du MasterBot. Il faut attendre qu'un premier bot s'execute pour pouvoir lancer le suivant.

Ci-dessous, un tableau qui résume les bots posants problème pour l'intégration au MasterBot

Name Code Location Problem Function
BioPathBot Code_BioPathBot Problem installing some dependency main()
ChronoBot Source code scattered on multiple files
CreatoBot Code_Creatobot - main()
EntryMakerBot Source code scattered on multiple files
FormatBot Code_FormatBot - main()
GallicaSpqarqlBot
GraderBot Code_GraderBot - main()
ImageBot Code_ImageBot - main()
LinkBot Code_LinkBot - main()
MinerRegExBot
OrthoBot Code_OrthoBot - main()
PageUpdaterBot Code_PageUpdaterBot - main()
SimilarBot Code_SimilarBot - main()
SliderBot Uses a graphical user interface
SourceBot Code_SourceBot - main()
SparqlBot Source code scattered on multiple files
SummarizingBot Code_SummarizingBot - main()
TangoBot Problem installing some dependency
TranslatorBot Code_TranslatorBot - main()
Triplificator Code_Triplificator - main()
VandalBot Source code scattered on multiple files
Wikidataficator

Exemples

Ce bot ne produit pas de résultats particulièrement visibles. En guise de test, j'ai lancé une séquence "ImageBot, TranslatorBot" sur les 1000 pages modifiées récemment. L'activité récente du bot est visible sur cette page. Le lancement est satisfaisant, mais les résultats ne sont pas forcément bons. La traduction n'est pas tout le temps complète, et elle répète parfois quelque mots. Les images ne correspondent pas toutes au sujet de la page. Ceci est hors de la portée du MasterBot.

Code

Le code source est disponible sur ce repo GitHub