Χρήστης:Lou/Χειρισμός bot/catauto.py

Από Βικιλεξικό
Πήδηση στην πλοήγηση Πήδηση στην αναζήτηση
#-*- coding: utf-8 -*-

#################################################################
# Σκοπός: αλλαγή των κατηγοριών προτύπων του λόγου
# με αυτόματη κατηγοριοποίηση χάρη στα πρότυπα τίτλων τομέα
# στο ελληνικό Βικιλεξικό
#
# Χρησιμοποιούνται ASCII για το ελληνικό και λατινικό αλφάβητο
#
# Το bot χρειάζεται την pywikipedia
# Σημείωση: μερικά κομμάτια του κώδικα προέρχονται 
# από το replace.py της pywikipedia
# (run της class LectureRemplacement και main),
# αλλά έχουν τροποποιηθεί ελαφρά.
#
#---------------------
# Παράμετροι:
#---------------------
# - afficher : εάν 'true', οι τίτλοι παρουσιάζονται
# (αλλά το script γίνεται πιο αργό)
afficher = True
#
commentaire = u"Catégorisation automatique des modèles de titres"
#
################################################################
# Τα scripts  τοποθετούνται σε διαφορετικό ντοσιέ
# από την pywikipedia
# Οι πληροφορίες χωρίζονται σε πολλά ντοσιέ
# 1 ντοσιέ ΒΛ στο ίδιο ντοσιέ με την pywikipedia
# περιέχει τα ντοσιέ data, temp, logs, scripts
from __future__ import generators
import sys, re
sys.path.append('../pywikipedia')
sys.path.append('../data')
import os
import time

# Récupération des données
os.chdir('../data')
from lists import majASCII, asciis, asciisation, ponctascii, ex_lang, exceptions, types, nom_types
# Le fichier lists doit être présent dans le dossier data

# récupération des début et fins
# Un fichier stop
st = open('stop', 'r')
stop = unicode(st.read(), 'utf-8')
stop = stop[:-1]
st.close
# Un fichier start_page
st = open('start_page', 'r')
start_page = unicode(st.read(), 'utf-8')
st.close
os.chdir("..")

# Import des modules pywikipedia
os.chdir('../pywikipedia')
import wikipedia, pagegenerators, catlib, config, watchlist
os.chdir('../wiktio')

# Répertoire de travail
os.chdir('temp')

#-----------------------------------------------------------
class LectureRemplacement:
        """
        Un robot qui fait les remplacements
        """

        def __init__(self, generator, acceptall = False):
                """
                Arguments :
                        * generator - Générateur qui apporte les pages
                        * acceptall  - Si vrai, pas de demande de confirmation à chaque modification
                """
                
                self.generator = generator
                self.acceptall = acceptall

        def ChangeArticles(self, text, titre):
                """
                1) Ajoute la langue aux titres de types
                2) Ajoute l'ASCII aux titres de types
                3) Enlève les catégories gérées automatiquement par les titres
                """
                cat = False # Présence de catégories ?

                # Recherche et isolation des catégories si présentes
                try:
                        text = repl(text, u"[[catégorie:",u"[[Catégorie:") # harmonise
                        categorie_start = re.search(u"\[\[Catégorie:([^\]]*)\]\]", text).start()
                        text = text[:categorie_start]+ u"{{=:cat:=}}\n" + text[categorie_start:]
                        cat = True # catégories présentes
                except:
                        #Affiche(u"Pas de catégories dans cette page")
                        0
                
                # Séparation des sections de langue
                parties = text.split("{{=")
                selection = u""
                langues = {}                                                            # liste des sections de langues
                text_final = ""                                                        # contriendra l'article final
                valeur = {}                                                              # type de la section
                nombre_titre = 0                                                        # compte des titres de section
                n = 0                                                                      # compte des catégories
                difference = 0                                                    # différence entre les deux comptes
                titrascii = ''   # traduction ASCII du titre
                askiwi = ''
                error = ''
                titrascii_commun = ''
                askiwi_commun = ''
                error_commun = ''
                
                # sections de langues dans l'article
                for i, p in enumerate(parties):
                        p = replex(p, u"^[^\{](.+?=)\|.*?(\}\})", r"\1\2")
                        trouve = re.search("=}}", p)
                        if trouve:
                                fin = trouve.start()
                                nom = p[:fin]
                                langues[nom] = "{{=" + p
                                valeur[n] = nom
                        else:
                                langues["0"] = p
                                valeur[n] = "0"
                        n+=1
                # types dans chaque section de langue
                for z, i in enumerate(langues):
                        if i == "conv" or i == "ine":
                                continue
                        if i != ":cat:" and i!="0":
                                # Cas particuliers ?
                                if i in ex_lang:
                                        titrascii, askiwi, error = self.NormASCII(titre, i)
                                elif not titrascii_commun:
                                        titrascii_commun, askiwi_commun, error_commun = self.NormASCII(titre, 0)
                                        titrascii = titrascii_commun
                                        askiwi = askiwi_commun
                                        error = error_commun
                                else:
                                        titrascii = titrascii_commun
                                        askiwi = askiwi_commun
                                        error = error_commun
                        
                                typesec = langues[i].split("{{-")
                                for m, sec in enumerate(typesec):
                                        if m!=0:
                                                sec = "{{-" + sec
                                        trouve = re.search("-.*?\}\}", sec)
                                        tit = ""
                                        if trouve:
                                                tit = sec[:trouve.end()]
                                        # D'une part s'il y a besoin d'ajouter l'ASCII
                                                if askiwi:
                                                        for section in types:
                                                                # section avec langue avec ASCII (ASCII remplacés)
                                                                tit = replex(tit, "(\{\{-" + section +"-\|)"+i+"\|[^}\|=]+(\}\}|num=[0-9]\}\})", r"\1"+i+"|"+titrascii+r"\2")
                                                                # section avec langue sans ASCII (ASCII ajouté)
                                                                tit = replex(tit, "(\{\{-" + section +"-\|)"+i+"(\}\}|num=[0-9]\}\})", r"\1"+i+"|"+titrascii+r"\2")
                                                                # section sans langue avec ASCII (langue ajoutée et ASCII remplacé)
                                                                tit = replex(tit, "(\{\{-" + section +"-\|)\|[^}\|=]+(\}\}|num=[0-9]\}\})", r"\1"+i+"|"+titrascii+r"\2")
                                                                # section sans langue ni ASCII (langue et ASCII ajoutés)
                                                                tit = replex(tit, "(\{\{-" + section +"-)(\}\}|\|num=[0-9]\}\})", r"\1|"+i+"|"+titrascii+r"\2")

                                        # D'autre part, s'il n'y a pas besoin d'ASCII
                                                else:
                                                        for section in types:
                                                                # section avec langue avec ASCII (ASCII retiré)
                                                                tit = replex(tit, "(\{\{-" + section +"-\|)"+i+"\|[^}\|=]+(\}\}|\|num=[0-9]\}\})", r"\1"+i+r"\2")
                                                                # section avec langue sans ASCII (langue ajouté)
                                                                tit = replex(tit, "(\{\{-" + section +"-\|)"+i+"(\}\}|\|num=[0-9]\}\})", r"\1"+i+r"\2")
                                                                # section sans langue avec ASCII (langue ajoutée et ASCII retiré)
                                                                tit = replex(tit, "(\{\{-" + section +"-\|)\|[^}\|=]+(\}\}|\|num=[0-9]\}\})", r"\1"+i+r"\2")
                                                                # section sans langue ni ASCII (langue ajoutée)
                                                                tit = replex(tit, "(\{\{-" + section +"-)(\}\}|\|num=[0-9]\}\})", r"\1|"+i+r"\2")
                                                typesec[m] = tit + sec[trouve.end():]
                                        else:
                                                typesec[m] = sec
                                                                
                                        langues[i] = '' ;
                                        for n in range(len(typesec)):
                                                langues[i] += typesec[n]
                                                
                        if i == ":cat:": # s'il y a des catégories
                                langues[i] = replex(langues[i], '^.+?:cat:=\}\}', '')
                                langues[i] = replex(langues[i], '\n\n+', '')
                
                # Rassemblement des sections de langue
                for i in valeur:
                        text_final += langues[valeur[i]]

                # Suppression des catégories grammaticales désormais gérées automatiquement
                for ty in nom_types:
                        text_final = replex(text_final, u"(\r\n+)?\[\[Catégorie:" + ty + " en .+?\]\]\n?", "")

                # Suppression des catégories langues seules (=seul rôle ASCII), prises en compte dans les modèles
        # Attention ! N'est valable que sur les articles normaux avec au moins un titre normal
        # Note : toutes les catégories contenant une minuscule sont considérées comme des langues
                text_final = replex(text_final, u"(\r\n+)?\[\[Catégorie:[a-zéèà].+?\|(.+?)\]\]\n?", "")

        # Catégories inutiles mais parasites
                text_final = replex(text_final, u"(\r\n+)?\[\[Catégorie:[a-zéèà].+?\]\]\n?", "")

                # Nettoyage des espaces en trop
                text_final = replex(text_final, u"\r?\n\r?\n\r?\n\r?\n?\r?\n?", "\r\n\r\n\r\n")
                return text_final, titrascii, error

        def NormASCII(self, expression, langue):
                """
                Transcrit le terme entré (un nom d'article) en ASCII
                pour une catégorisation correcte
                """
                expressionA = expression
                
        # Ponctuation
                for s, t in ponctascii:
                        expressionA = replex(expressionA, s, t)

        # Transcription en ASCII
                if langue:
                        for s, t in ex_lang[langue]:
                                expressionA = replex(expressionA, s, t)
                else:
                        for s, t in asciisation:
                                expressionA = replex(expressionA, s, t)
                
                expressionA = self.minusASCII(expressionA)
                expressionB = self.minusASCII(expression)
                        
                error = False
                askiwi = False
                # Vérifie si le titre est déjà en ASCII (pas besoin alors de remplacer)
                if expressionA == expressionB:
                        askiwi = False
                        error = False
                        print "Titre déjà en ascii :", expressionA
                # Vérifie si le nouveau titre est bien tout en ascii
                elif not self.isASCII(expressionA) and not langue:
                        askiwi = False
                        error = True
                        print "Titre changé non ASCII : ", expressionA
                else:
                        askiwi = True
                        error = False
                return expressionA, askiwi, error

        def minusASCII(self, terme):
                """
                Transforme en minuscules les ASCII uniquement
                """
                for s, t in majASCII:
                        terme = repl(terme, s, t)
                return terme
                
        def isASCII(self, text):
                """
                Vérifie que le texte contient les caractères ascii autorisés
                """
                return all(a in asciis for a in text)

        def run(self):
                """
                Fait fonctionner le truc
                """
                Affiche(u"Au boulot !!")
                ancien= ""

                for page in self.generator:
                        if page.title() == stop:
                                Affiche(u""+stop+u" atteint, arrêt du bot.")
                                break
                        st = open('../data/start_page', 'w')
                        st.write(page.title().encode('utf-8'))
                        st.close
                        try:
                                ancien = page.get()
                                if not page.canBeEdited():
                                        wikipedia.output(u'Saute la page protégée %s' % page.title())
                        except wikipedia.NoPage:
                                wikipedia.output(u'Page %s introuvable' % page.title())
                        except wikipedia.IsRedirectPage:
                                wikipedia.output(u'La page %s est un redirect' % page.title())
                                continue

                        titre = page.title()

                        continuer = True
                        for sauf in exceptions:
                                try:
                                        if re.search(sauf, titre):
                                                continuer = False
                                                break
                                except:
                                        Affiche("Erreur de " + titre + " avec " + sauf)
                                        wikipedia.output("Erreur de " + titre + " avec " + sauf)
                                        continue
                                        
                        nouveau, ascii, error = self.ChangeArticles(ancien, titre)
                        if error:
                                wikipedia.output(u"La page contient des caractères non reconnus : %s | %s" % (titre, ascii))
                                #conterror = wikipedia.input(u'Continuer ? (o/n)')
                                #if conterror == 'n' or conterror == 'N':
                                #       break
                        if replex(nouveau, "\r|\n", "") == replex(ancien, "\r|\n", "") or not continuer:
                                Affiche(u"Pas de changements nécessaires dans %s" % titre)
                        else:
                                wikipedia.output('>>> %s <<<' % (titre))
                                Affiche('%s -> %s' % (titre, ascii))
                                wikipedia.output(unicode(time.strftime("%Hh%M | %D"), 'utf8'))
                                wikipedia.showDiff(ancien, nouveau)
#                               if difference > 0:
#                                       if difference == 1:
#                                               Affiche(u">> Attention : 1 catégorie semble avoir été effacée sans contrepartie apparente !\n")
#                                       else:
#                                               Affiche(u">>Attention : %s catégories semblent avoir été effacées sans contrepartie apparente !\n" % difference)
#                               if difference < 0:
#                                       difference = -difference
#                                       if difference == 1:
#                                               Affiche(u">> Note : il semble qu'il manquait 1 catégorie.\n")
#                                       else:
#                                               Affiche(u">>Note : il semble qu'il manquait %s catégories.\n" % difference)
                                if not self.acceptall:
                                        choice = wikipedia.inputChoice(u'Accepter les changements ?',  ['Yes', 'No', 'All'], ['y', 'N', 'a'], 'N')
                                        if choice in ['a', 'A']:
                                                self.acceptall = True
                                if self.acceptall or choice in ['y', 'Y']:
                                        page.put(nouveau, comment = commentaire, minorEdit = True)
                                        Affiche(u"Bien joué !\n")
                Affiche(u"Déjà fini ?")

        
def repl(original_text, old, new):
        """
        Outils de remplacement de texte simple (sans expressions régulières)
        """
        try:
                new_text = old.sub(new, original_text)
        except:
                new_text = original_text.replace(old, new)
        return new_text

def replex(texte, avant, apres):
        """
        Outils de remplacement de texte avec expressions régulières
        """
        return re.sub('(?u)'+avant, apres, texte)

def Affiche(texte):
        """
        N'affiche les commentaires que si "Commentaires" est activé
        """
        if afficher:
                try:
                        print unicode(texte, 'utf-8')
                except:
                        print texte
#               wikipedia.output(texte)
        return

def compte(texte, motif):
        """
        Compte le nombre de motif dans le texte
        """
        occurence = 0
        n=0
        while n<len(texte)-len(motif):
                x=0
                mot = u""
                while x<len(motif):
                        mot += texte[n+x]
                        x += 1
                if mot == motif:
                        occurence +=1
                n +=1

        return occurence


def main():
        """
        arguments possibles :
        -start
        -page
        -ref
        -cat
        -from
        et :
        -all
        """
        acceptall = False
        namespaces=["0"]
        gen = None
        PageTitles = []
        
        for arg in sys.argv[1:]:
                arg = wikipedia.argHandler(arg, 'catauto')
                if arg:
                        if arg.startswith('-start'):
                                if len(arg) == 6:
                                        firstPageTitle = wikipedia.input(u'Par quelle page commencer ?')
                                else:
                                        firstPageTitle = arg[7:]
                                namespace = wikipedia.Page(wikipedia.getSite(), firstPageTitle).namespace()
                                gen = pagegenerators.AllpagesPageGenerator(firstPageTitle, namespace)
                        elif arg.startswith('-from'):
                                firstPageTitle = start_page
                                namespace = wikipedia.Page(wikipedia.getSite(), firstPageTitle).namespace()
                                gen = pagegenerators.AllpagesPageGenerator(firstPageTitle, namespace)
                        elif arg.startswith('-page'):
                                if len(arg) == 5:
                                        PageTitles.append(wikipedia.input(u'Quelle page changer ?'))
                                else:
                                        PageTitles.append(arg[6:])
                                pages = [wikipedia.Page(wikipedia.getSite(), PageTitle) for PageTitle in PageTitles]
                                gen = iter(pages)
                        elif arg.startswith('-ref'):
                                if len(arg) == 4:
                                        referredPageTitle = wikipedia.input(u'Quelle page sert-elle de référence ?')
                                else:
                                        referredPageTitle = arg[5:]
                                referredPage = wikipedia.Page(wikipedia.getSite(), referredPageTitle)
                                gen = pagegenerators.ReferringPageGenerator(referredPage)
                        elif arg.startswith('-cat'):
                                if len(arg) == 4:
                                        categoryname = wikipedia.input(u'Nom de la catégorie : ')
                                else:
                                        categoryname = arg[5:]
                                cat = catlib.Category(wikipedia.getSite(), 'Category:%s' % categoryname)
                                gen = pagegenerators.CategorizedPageGenerator(cat)
                        if arg.startswith('-all'):
                                acceptall = True

        if not gen:
                firstPageTitle = start_page
                namespace = wikipedia.Page(wikipedia.getSite(), firstPageTitle).namespace()
                gen = pagegenerators.AllpagesPageGenerator(firstPageTitle, namespace)
                        
                # syntax error, show help text from the top of this file
                #wikipedia.output(__doc__, 'utf-8')
                #wikipedia.stopme()
                #sys.exit()

        preloadingGen = pagegenerators.PreloadingGenerator(gen, pageNumber = 10)
        bot = LectureRemplacement(preloadingGen, acceptall)
        bot.run()

if __name__ == "__main__":
        try:
                main()
        finally:
                wikipedia.output(u"\nFin du processus.\n")
                wikipedia.stopme()