Utilisateur:Daahbot/scripts/catauto.py

Définition, traduction, prononciation, anagramme et synonyme sur le dictionnaire libre Wiktionnaire.
#-*- coding: utf-8 -*-

#################################################################
# But : remplacer les catégories de types de mot par la
# catégorisation automatique par les modèles de titres de section
# Sur le Wiktionnaire fr
#
# ASCII pris en compte : alphabets latin et grec
#
# Bot nécessitant pywikipedia
# Note : Certaines parties de code proviennent du replace.py de pywikipedia
# (run de la class LectureRemplacement et main). Ils ont été plus ou moins modifiés.
#
#---------------------
# Paramètres :
#---------------------
# - afficher : mettre oui pour voir les titres défiler (ralenti le processus)
afficher = True
#
commentaire = u"Catégorisation automatique des modèles de titres"
#
################################################################
# Scripts  placés dans un dossier différent de pywikipedia
# Séparation des informations dans plusieurs dossiers
# 1 dossier wiktio dans le même répertoire que pywikipedia
# contenant les dossiers 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
		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[i] = nom
			else:
				langues["0"] = p
				valeur[n] = "0"
		# types dans chaque section de langue
		for i in 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] = ''.join(typesec)
						
			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
		"""
		isasc = True
		for x in text:
			ascok = False
			for a in asciis:
				if a == x:
					ascok = True
					break
			if ascok == False:
				isasc = False
				break
		return isasc

	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
	"""
	avant = re.compile(avant, re.UNICODE)
	texte = avant.sub(apres, texte)
	return 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
	for n in range(len(texte)-len(motif)):
		mot = u""
		for x in range(len(motif)):
			mot += texte[n+x]
		if mot == motif:
			occurence +=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()