|
|
(111 versions intermédiaires par 8 utilisateurs non affichées) |
Ligne 1 : |
Ligne 1 : |
| <!-- PUB METAINFOS : ID = &beginID&8&endID& --> | | <!-- PUB METAINFOS : ID = &beginID&1632&endID& --> |
| == Résumé des fonctionnalité ==
| | = Résumé des fonctionnalités = |
| [[PageUpdaterBot]] est un robot qui s'occupe de compléter les différentes pages sur Wikipast à partir d'hyperliens. [[PageUpdaterBot]] vérifie que sur chaque page, les hyperliens mènent vers une page existante et que les entrées associées soient placées aux bons endroits. Si la page associée à l'hyperlien n'existe pas, [[PageUpdaterBot]] crée la page et place l'entrée associée dans cette page. | | [[PageUpdaterBot]] est un robot qui s'occupe de compléter les différentes pages sur Wikipast à partir d'hyperliens. [[PageUpdaterBot]] vérifie que sur chaque page, tous les hyperliens (excepté les dates) mènent vers une page existante et que les entrées correspondantes existent et soient placées aux bons endroits. Si la page associée à l'hyperlien n'existe pas, [[PageUpdaterBot]] crée la page et place l'entrée associée dans cette page. |
|
| |
|
| == Description technique et discussion ==
| | = Description technique = |
| [[PageUpdaterBot]] peut charger les pages à traiter de deux manières différentes, soit selon les dernières modifications, soit depuis le début, assurant de ce fait le traitement de toutes les pages. [[PageUpdaterBot]] commence par trier la page originale, si elle ne l'était pas encore. Puis [[PageUpdaterBot]] va analyser le contenu de la page entrée par entrée. Pour chaque entrée, le bot extrait les hyperliens qu'elle contient, en excluant la date et celui de la page en cours d'analyse. [[PageUpdaterBot]] va ensuite vérifier que chaque hyperliens redirige vers une page existante, que l'entrée existe bien dans cette page et qu'elle est placée au bon endroit. Afin de faciliter le traitement, un ID est associé à chaque entrée sous la forme d'un commentaire <code><nowiki><!-- PUB METAINFOS : entryID = &beginID&69&endID& --></nowiki></code> placé à la fin de la ligne. | | La principale fonction de [[PageUpdaterBot]] est de répercuter les modifications faites sur une page particulière sur toutes les autres pages concernée par l'entrée. [[PageUpdaterBot]] commence par analyser le contenu de chaque page entrée par entrée. Pour chacune d'entre elles, le bot extrait les hyperliens qu'elle contient,à l'exception de la date et de celui de la page en cours d'analyse. |
|
| |
|
| Dans le cas ou deux entrées sont présentes sous des formes différentes sur deux pages, la plus récente sera gardée. Les pages sont sélectionnées de la plus récente à la plus ancienne pour assurer que la modification la plus récente prime. Pour vérifier que les entrées sont les mêmes, leurs ''PUB_id'' sont comparés. Si une entrée de la page mère n'a pas de ''PUB_id'', l'algorithme lui en donnera un, si une entrée de la page fille ne possède pas de ''PUB_id'', la liste d'hyperliens et de références qu'elles contiennent est comparée, si une correspondance est trouvée, leurs identifiants seront mis à jour. Ce traitement peut être source d'erreur si un utilisateur maladroit modifie accidentellement le ''PUB_id'' à la fin de chaque entrée. Ce qui rend ce bot un peu vulnérable au mauvais comportement des utilisateurs. Un autre cas peut poser problème : dans le cas ou une section contient des entrées sous forme de liste, puis quelques paragraphes de textes, et enfin à nouveau une liste d'entrée. Le texte entre les deux listes sera effacé par le bot. Ce qui ne devrait pas poser trop de problèmes si les utilisateurs respectent la convention adoptée.
| | [[PageUpdaterBot]] va exécuter une action dans trois différents cas: si la page référencée dans l'hyperlien n'existe pas, si l'entrée en question n'est pas présente dans la page référencée ou si une entrée a été modifiée dans l'une des deux pages. Dans le premier cas, [[PageUpdaterBot]] crée la page et y ajoute l'entrée. Dans le deuxième cas, il ajoute l'entrée à la liste présente dans la page référencée puis trie toutes les entrées par ordre chronologique. Dans le dernier cas, l'entrée la plus récente remplace l'entrée la plus ancienne. |
| | |
| | Afin de faciliter le traitement, un commentaire est associé à chaque entrée. Le commentaire possède la forme suivante : <code><nowiki> <!-- PUB METAINFOS : entryID = &beginID&42&endID& entryHash = &beginHASH&6936b65604c6f91fab6552c2b2bdad9a&endHASH& --> </nowiki></code>. Il est placé à la suite de l'entrée dans le code source de la page. Pour chaque entrée, [[PageUpdaterBot]] vérifie tout d'abord qu'elle possède un id et un hash. Si tel n'est pas le cas, il lui ajoute un nouvel id et un nouvel hash, qui n'est rien d'autre que le md5 du texte de l'entrée. Le dernier id utilisé sur wikipast est écrit dans le commentaire au somment de la présente page. Si l'entrée possède un commentaire, dans le cas où [[PageUpdaterBot]] est déjà passé une fois, [[PageUpdaterBot]] va aller sur les pages référencées par les hyperliens, appelées pages filles par la suite. Plusieurs cas de figure sont considérés. Si l'entrée est absente de la page fille (l'id ne s'y trouve pas), c'est qu'elle a été supprimée manuellement. L'entrée est supprimée de la page mère. Si l'entrée est encore présente sur la page fille (une entrée avec le même id s'y trouve ou une entrée sans commentaire mais contenant les mêmes hyperliens et références), le bot compare l'entrée sur les deux pages avec leur hash respectif. Si le hash de l'entrée ne correspond plus au hash écrit dans le commentaire, c'est que l'entrée a été modifiée depuis le dernier passage de [[PageUpdaterBot]]. L'entrée avec le hash différent est gardée sur les deux pages. Le hash est mis à jour. |
|
| |
|
| == Exemples ==
| | [[PageUpdaterBot]] possède aussi un mode de nettoyage, qui supprime les ''PUB_id'' dans toutes les pages de Wikipast. Pour cela il faut l'appeler avec le paramètre ''clean''. |
|
| |
|
| | = Discussion = |
|
| |
|
| ==Code== | | == Performances == |
| Version du 07.05.17 à 17:17 (commit : Regex correction)
| | L'algorithme possède une complexité en O(n^2) par page. En effet, [[PageUpdaterBot]] compare toutes les entrées de la page mère avec toutes celles de la page fille. Le bot a des performances raisonnables compte tenu du faible nombre de pages contenu sur [http://wikipast.epfl.ch WikiPast]. |
| <pre>
| |
| # coding: utf-8
| |
| import urllib
| |
| import requests
| |
| import json
| |
| import re
| |
| from bs4 import BeautifulSoup
| |
|
| |
|
| passw = 'hqk-NGF-S6z-qqF'
| | Sur le Wikipast du 16.05.17, le script a trié toute la base de donnée en 2 minutes 10 secondes. Comme le script s'arrête quand il y a du texte entre deux entrées, [[PageUpdaterBot]] n'a considéré que un quart des entrées au premier passage du script. |
| baseurl = 'http://wikipast.epfl.ch/wikipast/'
| |
| summary = 'Wikipastbot update'
| |
|
| |
|
| user = 'PageUpdaterBot' #nom du bot
| | Sur le Wikipast du 16.05.17, le script a nettoyé toute la base de donnée en 1 minute 1 seconde. |
| HUBPage = baseurl + 'index.php/PageUpdaterBot' #Page contenant les méta information de PUB, notamment son compteur d'IDs.
| |
| beginID = '&beginID&'
| |
| endID = '&endID& -->'
| |
| metaInfo = '<!-- PUB METAINFOS : ID = ' #synthaxe des métainfos présentes sur le HUB du bot
| |
| entryMetaInfo = '<!-- PUB METAINFOS : entryID = ' #synthaxe des métainfos présentes sur les **entrées des pages**
| |
|
| |
|
| # Login request
| | Sur le Wikipast du 16.05.17, le script a trié toute la base de donnée en 5 minutes 14 secondes. |
| payload = {'action':'query','format':'json','utf8':'','meta':'tokens','type':'login'}
| |
| r1 = requests.post(baseurl + 'api.php', data = payload)
| |
|
| |
|
| #login confirm
| | == Fréquence d'utilisation / Scheduling du bot == |
| login_token = r1.json()['query']['tokens']['logintoken']
| |
| 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
| | Ce bot devrait être appliqué une fois par jour au wiki. |
| 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()
| | == Limites du bot == |
| edit_cookie.update(r3.cookies)
| | Le traitement peut être source d'erreur si un utilisateur maladroit modifie accidentellement le ''PUB_id'' à la fin de chaque entrée, ce qui rend ce bot vulnérable au mauvais comportement des utilisateurs. Il serait envisageable d'ajouter une sorte de "checksum" pour contrôler si le commentaire a été affecté par un utilisateur et dans ce cas recommencer la construction des IDs à partir de zéro. De plus, dans le cas où une section contiendrait des entrées sous forme de liste, puis quelques paragraphes de textes, et enfin à nouveau une liste d'entrée, seul la première liste d'entrée sera prise en charge. |
|
| |
|
| | Dans le cas ou deux entrées ayant le même ''PUB_id'' ont été modifiées de façon différente, la modification ne sera pas répercutées sur toutes les pages au premier passage du bot. Il faudra un nouveau passage pour unifier toutes les entrées. |
|
| |
|
| | Si une entrée est mal conçue et ne contient pas l'hyperlien de sa propre page, le bot ajoutera l'entrée sur les pages référencées mais une modification sur un de ces dernières n'entrainera pas la modification sur la page de base. (Voir [http://wikipast.epfl.ch/wikipast/index.php/PageUpdaterBot#Exemple_3 exemple 3]) |
|
| |
|
| # Définis à part du code du main, chaque fonction
| | = Exemples = |
| # devrait idéalement être testée, si possible ici.
| | ===Exemple 1=== |
| # mais après on fait comme on veut.
| | Un utilisateur met à jour la page [[Henri Dunant]] et entre l'information suivante : |
| def test():
| |
| #TODO
| |
| pass
| |
|
| |
|
| #--------- main code-------------------
| | *[[1864.08.22]] / [[Genève]]. [[Création]] par [[Henri Dunant]] de la [[Croix rouge]]. [http://letemps.archives.world/page/JDG_1897_12_31/1/%22Henri%20Dunant%22] |
| def main():
| |
| # Récupération de l'Id de base pour les PUB_id
| |
| PUBId = fetchPUBmetaInfo(HUBPage)
| |
|
| |
|
| # Récupération de la liste de pages à parcourir.
| | En admettant que l'entrée n'est pas encore présente dans [[Genève]], [[Création]] et que la page [[Croix rouge]] n'existe pas, le bot commence par associer un ''PUB_id'' à la nouvelle entrée et copie la ligne dans la page [[Genève]] ainsi que [[Création]]. Le bot trie chronologiquement les entrées dans chacune des 2 pages. L'entrée se retrouve donc à la bonne place. Comme la page [[Croix rouge]] n'existe pas, le bot crée la page et recopie la ligne dans cette nouvelle page. Finalement, les entrées de la page [[Henri Dunant]] sont ordonnées chronologiquement. |
| pagesToMod = []
| |
| if PUBId == 0:
| |
| pagesToMod = getPageList(True)
| |
| else:
| |
| pagesToMod = getPageList(False)
| |
|
| |
|
| ## boucle d'action principale du code.
| | De plus, un hash est associé à chacune des entrées afin de détecter une quelconque modification et ainsi mettre à jour les pages qui y sont référencées. |
| for u in pagesToMod:
| |
| contenu = fetchPageData(u)
| |
| pageTitle = u
| |
| allEntries = parseEntries(contenu)
| |
|
| |
|
| for entry in allEntries:
| | ===Exemple 2=== |
| if getPUBId(entry) == None:
| |
| PUBIdInt = int(PUBId) + 1
| |
| PUBId = str(PUBIdInt)
| |
| setPUBId(entry, PUBId)
| |
| #Important, à partir de ce moment la getPUBId(entry) devrait plus pouvoir retourner None !
| |
|
| |
|
| pagesConcerned=getHyperLinks(entry, pageTitle)
| | Un utilisateur met à jour la page [[Création]] et supprime l'information suivante : |
| for name in pagesConcerned:
| |
| urlFetched = getWikiPastUrl(name)
| |
| if urlFetched == None:
| |
| #ilavec n'y a pas d'Url qui correspond à notre hypermot, donc on doit créer cette page
| |
| urlFetched = createNewPage(name)
| |
|
| |
|
| fillePageContenu = fetchPageData(urlFetched)
| | *[[1864.08.22]] / [[Genève]]. [[Création]] par [[Henri Dunant]] de la [[Croix rouge]]. [http://letemps.archives.world/page/JDG_1897_12_31/1/%22Henri%20Dunant%22] |
| fillePageEntries = parseEntries(fillePageContenu)
| |
|
| |
|
| #ensuite on créé un index des différentes entrées selon leur PUBId
| | Le bot supprime l'entrée dans les pages [[Croix rouge]], [[Henri Dunant]] et [[Genève]] en détectant qu'un id est associé à une entrée dans ces 3 pages mais pas dans la page [[Création]]. |
| #IdAndEntry est une liste de tuples de la forme (PUBId: Int, Entries : String)
| |
| IdAndEntry = map(lambda e: (getPUBId(e), e), fillePageEntries)
| |
| found = False
| |
| currPUBId = getPUBId(entry)
| |
| #Le coeur de PUB on update les entrées selon l'entrée qu'on dispose nous.
| |
| for t1, t2 in IdAndEntry:
| |
| #On regarde d'abord si on a le même ID:
| |
| if t1 != None:
| |
| if t1 == currPUBId:
| |
| #on a trouvé Un Id qui match, on overwrite l'entrée par celle de la page courante.
| |
| t2 = entry
| |
| found=True
| |
| else:
| |
| #On un entrée indexée par "None", donc il faut regarder si les deux entrées sont similaires pour l'updater correctement.
| |
| if areEntrySimilar(entry, t2):
| |
| t2 = entry
| |
| found=True
| |
|
| |
|
| if not found:
| | ===Exemple 3=== |
| #Puisqu'aucune entrée matche, soit avec le PUBId soit avec leur similarité, on doit ajouter cette entrée comme une nouvelle entrée.
| |
| IdAndEntry.append((currPUBId, entry))
| |
|
| |
|
| newEntries = map(lambda t1, t2: t2, IdAndEntry)
| | Un utilisateur met à jour la page [[Henri Dunant]] et écrit une entrée problématique : |
| sortedEntries = sortEntries(newEntries)
| |
|
| |
|
| #A présent qu'on a updaté tout comme il fallait, on peut mettre en ligne les modifications sur la page.
| | *[[1864.08.22]] / [[Genève]]. Il crée la [[Croix rouge]]. [http://letemps.archives.world/page/JDG_1897_12_31/1/%22Henri%20Dunant%22] |
| contentToUp = unParseEntries(sortEntries)
| |
| uploadModifications(contentTuUp, urlFetched)
| |
|
| |
|
| #le bot a finit ses modifications, il va à présent mettre à jour le PUBId de sa page avec le dernier PUBId attribué.
| | Le bot modifie l'entrée sur la page [[Croix rouge]]. Puis un autre utilisateur supprime l'entrée sur la page [[Croix rouge]]. |
| updatePUBmetaInfo(HUBPage, PUBId)
| | Le bot ne supprimera pas l'entrée sur la page [[Henri Dunant]] car aucun hyperlien la référençant n'est présent. |
|
| |
|
| | == Groupe == |
| | {| class="wikitable" |
| | ! scope="col" | Nom et Prénom |
| | |- |
| | | Fournier Romain |
| | |- |
| | | Giorla Matteo |
| | |- |
| | | Pannatier Arnaud |
| | |- |
| | | Viaccoz Cédric |
| | |- |
| | |} |
|
| |
|
| | == Code == |
| | Le code est disponible sur le [https://github.com/cedricviaccoz/PageUpdaterBot repository GitHub] |
|
| |
|
| '''
| | [[PageUpdaterBot BioPathBot]] |
| Récupère le PUB_id
| |
| dans le contenu passé en argument.
| |
| (Un PUB_id a la forme suivante :
| |
| "<!-- PUB_id = &beginID&69&endID& -->")
| |
| S'il n'y en a pas, retourne None,
| |
| sinon retourne le PUBId (en string).
| |
| S'il y en a plusieurs, il retourne le dernier.
| |
| | |
| @param content : String
| |
| le contenu dans lequel trouver l'id.
| |
| '''
| |
| def getPUBId(content):
| |
| return re.search(beginID + '(.*)' + endID, content).group(1)
| |
| | |
| | |
| '''
| |
| Récupère tous les PUB_id
| |
| dans le contenu passé en argument.
| |
| (Un PUB_id a la forme suivante :
| |
| "<!-- PUB_id = &beginID&69&endID& -->")
| |
| S'il n'y en a pas, retourne un tableau vide.
| |
| | |
| @param content : String
| |
| le contenu dans lequel trouver les ids.
| |
| '''
| |
| def getAllPUBIds(content):
| |
| return re.findall(beginID + '(.*)' + endID, content)
| |
| | |
| | |
| '''
| |
| Cette fonction va s'occuper d'aller sur la page
| |
| indiquée en argument et regarder si elle trouve le bloc
| |
| de méta information de PUB (bloc commençant par
| |
| "<!-- PUB METAINFOS :").
| |
| Si ce bloc de méta info
| |
| est présent, elle va retourner l'Id qui y
| |
| est contenu (représentant le dernier Id attribué
| |
| à une entrée de page).
| |
| Si ce bloc de méta info n'est pas présent,
| |
| cette fonction va s'occuper de le créer,
| |
| y mettre comme valuer d'ID 0, puis retourner
| |
| 0.
| |
| Si une quelquonque erreur s'est produite,
| |
| la fonction retournera un chiffre négatif
| |
| ou bien enverra une exception (comme vous le sentez)
| |
| '''
| |
| def fetchPUBmetaInfo(initialPass):
| |
| if initialPass:
| |
| currentID = '0'
| |
| #écrire metainfo dans le HUB
| |
| newMetaInfo = metaInfo + beginID + currentID + endID
| |
| newContent = newMetaInfo + '\n'
| |
| payload={'action':'edit','assert':'user','format':'json','utf8':'','prependtext':newContent,'summary':summary,'title':user,'token':edit_token}
| |
| r4=requests.post(baseurl+'api.php',data=payload,cookies=edit_cookie)
| |
| else:
| |
| HUBcontent = fetchPageData(user)
| |
| result = getPUBId(HUBcontent)
| |
| if result == None:
| |
| #la balise a été effacée/endommagée
| |
| currentID = '-1'
| |
| else:
| |
| currentID = result
| |
| return currentID
| |
| | |
| | |
| '''Cette fonction doit être appelée après que
| |
| PUB ait fait toute sa traversée, elle doit
| |
| actualiser l'ID contenu de méta_info.
| |
| | |
| @param newId : Int
| |
| Le nouvel ID a inscrire dans les metaInfo.
| |
| '''
| |
| def updatePUBmetaInfo(newId):
| |
| result=requests.post(baseurl+'api.php?action=query&titles='+user+'&export&exportnowrap')
| |
| soup=BeautifulSoup(result.text,'html.parser')
| |
| content=''
| |
| for primitive in soup.findAll("text"):
| |
| content+=primitive.string
| |
| currentID = fetchPUBmetaInfo()
| |
| content=content.replace(metaInfo + beginID + currentID + endID, metaInfo + beginID + str(newId) + endID)
| |
| payload={'action':'edit','assert':'user','format':'json','utf8':'','text':content,'summary':summary,'title':user,'token':edit_token}
| |
| r4=requests.post(baseurl+'api.php',data=payload,cookies=edit_cookie)
| |
| | |
| | |
| '''
| |
| En fonction de la variable fromScratch,
| |
| cette fonction va récupérer la liste des pages qui
| |
| ont été modifiées récemment, ou bien
| |
| toutes les pages sur lequel on veut établir
| |
| un indexage par PUBId.
| |
| Elle retournera la liste des pages sous
| |
| la forme d'une liste d'url wikipast.
| |
| | |
| @para fromScratch : Boolean
| |
| vaut true si on veut faire un indexage depuis 0
| |
| ou false si on veut juste récupérer les pages
| |
| récemment modifiées.
| |
| '''
| |
| def getPageList(fromScratch):
| |
| protected_logins=["Frederickaplan","Maud","Vbuntinx","Testbot","IB","SourceBot","PageUpdaterBot","Orthobot","BioPathBot","ChronoBOT","Amonbaro","AntoineL","AntoniasBanderos","Arnau","Arnaudpannatier","Aureliver","Brunowicht","Burgerpop","Cedricviaccoz","Christophe","Claudioloureiro","Ghislain","Gregoire3245","Hirtg","Houssm","Icebaker","JenniCin","JiggyQ","JulienB","Kl","Kperrard","Leandro Kieliger","Marcus","Martin","MatteoGiorla","Mireille","Mj2905","Musluoglucem","Nacho","Nameless","Nawel","O'showa","PA","Qantik","QuentinB","Raphael.barman","Roblan11","Romain Fournier","Sbaaa","Snus","Sonia","Tboyer","Thierry","Titi","Vlaedr","Wanda"]
| |
| if fromScratch:
| |
| depuis_date = '2017-02-02T16:00:00Z'
| |
| else:
| |
| depuis_date = '2017-05-02T16:00:00Z'
| |
| | |
| liste_pages=[]
| |
| for user in protected_logins:
| |
| result=requests.post(baseurl+'api.php?action=query&list=usercontribs&ucuser='+user+'&format=xml&ucend='+depuis_date)
| |
| soup=BeautifulSoup(result.content,'lxml')
| |
| for primitive in soup.usercontribs.findAll('item'):
| |
| liste_pages.append(primitive['title'])
| |
| | |
| return list(set(liste_pages))
| |
| | |
| | |
| '''
| |
| À l'aide du titre de la page donné en argument,
| |
| récupère les données de cette page,
| |
| sous la forme d'une string
| |
| | |
| @param pageName : String
| |
| le titre de la page wikipast où aller chercher les données.
| |
| '''
| |
| def fetchPageData(pageName):
| |
| result=requests.post(baseurl+'api.php?action=query&titles='+pageName+'&export&exportnowrap')
| |
| soup=BeautifulSoup(result.text, "lxml")
| |
| pageData=''
| |
| for primitive in soup.findAll("text"):
| |
| pageData+=primitive.string
| |
| return pageData
| |
| | |
| | |
| '''
| |
| Vérifie que l'entrée donnée en argument soit bien une
| |
| entrée biographie (c'est à dire une entrée à puce commencant par une date)
| |
| @param entre : String
| |
| l'entrée à vérifier.
| |
| '''
| |
| def isValidEntry(entry):
| |
| if entry[0:3] == '*[[' and (entry[3:7]+entry[8:10]+entry[11:13]).isdigit() and entry[7]+entry[10] == '..' and entry[13:15] == ']]':
| |
| return True
| |
| else:
| |
| return False
| |
| | |
| | |
| '''
| |
| Va s'occuper de trier le contenu
| |
| de la page donné en argument
| |
| pour en ressortir que les entrées biographiques
| |
| sourcées (le texte ajouté autre part est ignoré).
| |
| Une entrée est normalement écrite sous la forme (dans le code):
| |
| *[[YYYY.MM.JJ]] / [[<lieu>]]. [[<évènement>]] entre [[<Mr. X>]] et [[<Mr.Y>]]. [<référence>]
| |
| | |
| !! remarque importante que je n'avais pas pensé avant :
| |
| Où met-on le PUB_id ? (il sera de la forme "<!-- PUB_id=69-->") ?
| |
| Il faut penser qu'on doit le mettre dans un endroit stratégique,
| |
| car il faudrait que si d'autres bots reprennent ou modifient des entrées,
| |
| ils conservent l'intégrité du PUB_id.
| |
| | |
| Cette fonction retournera une liste de String qui constituent
| |
| les entrées retournées sous la forme montrée plus haut.
| |
| | |
| @param content : String
| |
| le contenu de la page
| |
| '''
| |
| def parseEntries(content):
| |
| lines = content.split('\n')
| |
| newLines = []
| |
| for line in lines:
| |
| if not line.isValidEntry():
| |
| newLines.append(line)
| |
| #if a subtitle is found, abort
| |
| if line.startswith('='):
| |
| return newLines
| |
| return newLines
| |
| | |
| | |
| '''
| |
| Va mettre à jour le PUBId de l'entrée
| |
| passée en argument si Un PUBId est présent,
| |
| Sinon va ajouter ce PUBId à l'entrée
| |
| | |
| @param entry : String
| |
| l'entrée biographie.
| |
| @param PUBId : Int
| |
| l'Id à mettre à jour sur cette page.
| |
| '''
| |
| def setPUBId(entry, PUBId):
| |
| return entry+' '+entryMetaInfo+beginID+PUBId+endID
| |
| | |
| | |
| '''
| |
| retourne une liste d'hyperLinks contenu
| |
| dans cette entrée sous une forme de liste
| |
| de String, mais en excluant de cette liste l'argument
| |
| toExclude.
| |
| '''
| |
| def getHyperLinks(entry, toExclude):
| |
| hyperLinks = re.findall('\[\[(.*?)\]\]', entry)
| |
| hyperLinks = set(hyperLinks)
| |
| if toExclude in hyperLinks: hyperLinks.remove(toExclude)
| |
| return list(hyperLinks)
| |
| | |
| | |
| '''
| |
| retourne une liste d'hyperLinks contenu
| |
| dans cette entrée sous une forme de liste
| |
| de String, mais en excluant de cette liste l'argument
| |
| toExclude.
| |
| '''
| |
| def getReferences(entry):
| |
| return re.findall('\[(.*?)\]', entry)
| |
| | |
| | |
| '''
| |
| Va créer une nouvelle page wikipast nommée selon
| |
| l'argument donné. Si cette page existe déjà, va
| |
| simplement retourner l'URL de la page. Autrement retourne
| |
| l'url de cette pas nouvelle créée.
| |
| | |
| @oaram name : String
| |
| Le nom de la page à créer.
| |
| '''
| |
| def createNewPage(name):
| |
| subtitleToAdd = ''
| |
| payload={'action':'edit','assert':'user','format':'json','utf8':'','prependtext':subtitleToAdd,'summary':summary,'title':name,'token':edit_token}
| |
| r4=requests.post(baseurl+'api.php',data=payload,cookies=edit_cookie)
| |
| | |
| | |
| '''
| |
| Détermine si deux entrées sont identiques.
| |
| Pour ce faire on teste que
| |
| les dates, les lieux et la liste des hypermots
| |
| sont identiques.
| |
| (Pas de comparaison entre les PUBId!)
| |
| Si toutes les conditions énumérées ci dessus
| |
| sont satisfaites, alors on renvoit True,
| |
| autrement Talse.
| |
| | |
| @param entry1 : String
| |
| La première entrée à comparer
| |
| @param entry2 : String
| |
| La seconde entrée avec laquelle on compare la première
| |
| '''
| |
| def areEntrySimilar(entry1, entry2):
| |
| #la liste des hypermots inclus également la date
| |
| listOfHyperLinks1 = getHyperLinks(entry1)
| |
| listOfHyperLinks2 = getHyperLinks(entry2)
| |
| listOfReferences1 = getReferences(entry1)
| |
| listOfReferences2 = getReferences(entry2)
| |
|
| |
| return (listOfHyperLinks1 == listOfHyperLinks2) and (listOfReferences1 == listOfReferences2)
| |
| | |
| | |
| '''
| |
| Tri une liste d'entrée par ordre chronologique
| |
| @param listOfEntries : List(String)
| |
| La liste des entrées à trier
| |
| '''
| |
| def sortEntries(listOfEntries):
| |
| return sorted(listOfEntries)
| |
| | |
| | |
| '''
| |
| Va transformer une liste d'entrée
| |
| en un format que la page wikipédia en une seule
| |
| sting et retourner donc ces entrées
| |
| formatée en un seul bloc
| |
| | |
| @param entries : List(String)
| |
| Les entrées nouvellement modifiées
| |
| '''
| |
| def unParseEntries(entries):
| |
| return '\n'.join(entries)
| |
| | |
| | |
| '''
| |
| Va uploader le contenu nouvellement modifié
| |
| sur la page indiqué par url
| |
| | |
| Note importante :
| |
| il faudrait peut être ajouter à "content" le reste
| |
| de la page (pas que la partie des entrées biographiques)
| |
| selon comment il faut uploader des modifications sur wikipast
| |
| (soit juste la partie qui change, soit toute la page)
| |
| | |
| @param content : ???? (à voir)
| |
| le contenu à uploader
| |
| @param url : String
| |
| L'url de la page ou mettre ces modifications.
| |
| | |
| '''
| |
| def uploadModifications(content, url):
| |
| #TODO
| |
| pass
| |
| | |
| </pre >
| |
Résumé des fonctionnalités
PageUpdaterBot est un robot qui s'occupe de compléter les différentes pages sur Wikipast à partir d'hyperliens. PageUpdaterBot vérifie que sur chaque page, tous les hyperliens (excepté les dates) mènent vers une page existante et que les entrées correspondantes existent et soient placées aux bons endroits. Si la page associée à l'hyperlien n'existe pas, PageUpdaterBot crée la page et place l'entrée associée dans cette page.
Description technique
La principale fonction de PageUpdaterBot est de répercuter les modifications faites sur une page particulière sur toutes les autres pages concernée par l'entrée. PageUpdaterBot commence par analyser le contenu de chaque page entrée par entrée. Pour chacune d'entre elles, le bot extrait les hyperliens qu'elle contient,à l'exception de la date et de celui de la page en cours d'analyse.
PageUpdaterBot va exécuter une action dans trois différents cas: si la page référencée dans l'hyperlien n'existe pas, si l'entrée en question n'est pas présente dans la page référencée ou si une entrée a été modifiée dans l'une des deux pages. Dans le premier cas, PageUpdaterBot crée la page et y ajoute l'entrée. Dans le deuxième cas, il ajoute l'entrée à la liste présente dans la page référencée puis trie toutes les entrées par ordre chronologique. Dans le dernier cas, l'entrée la plus récente remplace l'entrée la plus ancienne.
Afin de faciliter le traitement, un commentaire est associé à chaque entrée. Le commentaire possède la forme suivante : <!-- PUB METAINFOS : entryID = &beginID&42&endID& entryHash = &beginHASH&6936b65604c6f91fab6552c2b2bdad9a&endHASH& -->
. Il est placé à la suite de l'entrée dans le code source de la page. Pour chaque entrée, PageUpdaterBot vérifie tout d'abord qu'elle possède un id et un hash. Si tel n'est pas le cas, il lui ajoute un nouvel id et un nouvel hash, qui n'est rien d'autre que le md5 du texte de l'entrée. Le dernier id utilisé sur wikipast est écrit dans le commentaire au somment de la présente page. Si l'entrée possède un commentaire, dans le cas où PageUpdaterBot est déjà passé une fois, PageUpdaterBot va aller sur les pages référencées par les hyperliens, appelées pages filles par la suite. Plusieurs cas de figure sont considérés. Si l'entrée est absente de la page fille (l'id ne s'y trouve pas), c'est qu'elle a été supprimée manuellement. L'entrée est supprimée de la page mère. Si l'entrée est encore présente sur la page fille (une entrée avec le même id s'y trouve ou une entrée sans commentaire mais contenant les mêmes hyperliens et références), le bot compare l'entrée sur les deux pages avec leur hash respectif. Si le hash de l'entrée ne correspond plus au hash écrit dans le commentaire, c'est que l'entrée a été modifiée depuis le dernier passage de PageUpdaterBot. L'entrée avec le hash différent est gardée sur les deux pages. Le hash est mis à jour.
PageUpdaterBot possède aussi un mode de nettoyage, qui supprime les PUB_id dans toutes les pages de Wikipast. Pour cela il faut l'appeler avec le paramètre clean.
Discussion
Performances
L'algorithme possède une complexité en O(n^2) par page. En effet, PageUpdaterBot compare toutes les entrées de la page mère avec toutes celles de la page fille. Le bot a des performances raisonnables compte tenu du faible nombre de pages contenu sur WikiPast.
Sur le Wikipast du 16.05.17, le script a trié toute la base de donnée en 2 minutes 10 secondes. Comme le script s'arrête quand il y a du texte entre deux entrées, PageUpdaterBot n'a considéré que un quart des entrées au premier passage du script.
Sur le Wikipast du 16.05.17, le script a nettoyé toute la base de donnée en 1 minute 1 seconde.
Sur le Wikipast du 16.05.17, le script a trié toute la base de donnée en 5 minutes 14 secondes.
Fréquence d'utilisation / Scheduling du bot
Ce bot devrait être appliqué une fois par jour au wiki.
Limites du bot
Le traitement peut être source d'erreur si un utilisateur maladroit modifie accidentellement le PUB_id à la fin de chaque entrée, ce qui rend ce bot vulnérable au mauvais comportement des utilisateurs. Il serait envisageable d'ajouter une sorte de "checksum" pour contrôler si le commentaire a été affecté par un utilisateur et dans ce cas recommencer la construction des IDs à partir de zéro. De plus, dans le cas où une section contiendrait des entrées sous forme de liste, puis quelques paragraphes de textes, et enfin à nouveau une liste d'entrée, seul la première liste d'entrée sera prise en charge.
Dans le cas ou deux entrées ayant le même PUB_id ont été modifiées de façon différente, la modification ne sera pas répercutées sur toutes les pages au premier passage du bot. Il faudra un nouveau passage pour unifier toutes les entrées.
Si une entrée est mal conçue et ne contient pas l'hyperlien de sa propre page, le bot ajoutera l'entrée sur les pages référencées mais une modification sur un de ces dernières n'entrainera pas la modification sur la page de base. (Voir exemple 3)
Exemples
Exemple 1
Un utilisateur met à jour la page Henri Dunant et entre l'information suivante :
En admettant que l'entrée n'est pas encore présente dans Genève, Création et que la page Croix rouge n'existe pas, le bot commence par associer un PUB_id à la nouvelle entrée et copie la ligne dans la page Genève ainsi que Création. Le bot trie chronologiquement les entrées dans chacune des 2 pages. L'entrée se retrouve donc à la bonne place. Comme la page Croix rouge n'existe pas, le bot crée la page et recopie la ligne dans cette nouvelle page. Finalement, les entrées de la page Henri Dunant sont ordonnées chronologiquement.
De plus, un hash est associé à chacune des entrées afin de détecter une quelconque modification et ainsi mettre à jour les pages qui y sont référencées.
Exemple 2
Un utilisateur met à jour la page Création et supprime l'information suivante :
Le bot supprime l'entrée dans les pages Croix rouge, Henri Dunant et Genève en détectant qu'un id est associé à une entrée dans ces 3 pages mais pas dans la page Création.
Exemple 3
Un utilisateur met à jour la page Henri Dunant et écrit une entrée problématique :
Le bot modifie l'entrée sur la page Croix rouge. Puis un autre utilisateur supprime l'entrée sur la page Croix rouge.
Le bot ne supprimera pas l'entrée sur la page Henri Dunant car aucun hyperlien la référençant n'est présent.
Groupe
Nom et Prénom
|
Fournier Romain
|
Giorla Matteo
|
Pannatier Arnaud
|
Viaccoz Cédric
|
Code
Le code est disponible sur le repository GitHub
PageUpdaterBot BioPathBot