Ce script réalise les actions suivantes :
{{de-tab-cas}}
par {{de-nom}}
, si le format des paramètres de {{de-tab-cas}}
est celui attendu{{de-nom}}
par un de ses dérivés{{pron}}
sur la ligne de forme{{pron}}
à la ligne de forme lorsqu’absent (pour les lignes de forme simples du type '''xxx''' {{genre}}
)# -*- coding: utf-8 -*-
'''
Ce script remplace le modèle {{de-tab-cas}} par
{{de-nom}} ou ses dérivés
'''
import re
import codecs
from wikipedia import *
import pagegenerators
import webbrowser
from pywikibot import i18n
Test = False # Pour tester le script
edit_fileerror = True # Pour mettre à jour le fichier d'erreur qd une page n'est pas modifiée
editcounter = 0 # Nombres de pages traitées
pages_traitees = # Liste des pages traitées (modifiées ou listées comme erreur)
pages_modifiees = # Liste des pages modifiées
# Fréquence de mise à jour des listes de pages à traiter et traitées
frqMajListes = 100
# Fichier contenant la liste des pages à modifier
fichier_entree = u'_Entrées\de-tab-cas.txt'
# Pour lister les pages non modifiées à cause d'une erreur
fichier_erreurs = u'_Sorties\Erreurs de-tab-cas.txt'
# Pour lister les pages modifiées
fichier_sortie = u'_Sorties\Modifiées de-tab-cas.txt'
def main(page):
'''
Fonction éditant une page avec de-tab-cas pour le remplacer par de-nom
'''
summary = u'Bot: Remplacement : {{de-tab-cas}} → {{de-nom}}'
titre = page.title()
if Test:
output('\n' + titre)
try:
contenu = page.get()
except wikipedia.NoPage:
non_modifiee(titre, msg=u'Pas de telle page')
return
except wikipedia.LockedPage:
non_modifiee(titre, msg=u'Page bloquée en écriture')
return
except wikipedia.IsRedirectPage:
non_modifiee(titre, msg=u'Page de redirection')
return
except wikipedia.ServerError:
non_modifiee(titre, msg=u'ServerError pour .get()')
return
except wikipedia.BadTitle:
non_modifiee(titre, msg=u'BadTitle pour .get()')
return
except wikipedia.EditConflict:
non_modifiee(titre, msg=u'EditConflict pour .get()')
return
# Fonction de remplacement de {{de-nom}} en un modèle plus spécifique
def deNomRepl(match):
# Nom du modèle de remplacement
modele = ''
genre = match.group(1)
cas = {
# strip() pour éradiquer d'éventuelles tabulations avant le terme
'ns': match.group(2).strip(),
'np': match.group(3).strip(),
'as': match.group(4).strip(),
'ap': match.group(5).strip(),
'gs': match.group(6).strip(),
'gp': match.group(7).strip(),
'ds': match.group(8).strip(),
'dp': match.group(9).strip()
}
def plurEndWithSuffix(suffix):
'''
Est-ce que toutes les formes du pluriel ont la même graphie que le
nominatif singulier suffixé par "suffix" ?
'''
if cas == cas and cas == cas and cas == cas and cas == cas + suffix:
return True
else:
return False
def singEndWithSuffix(suffix):
''' Idem, mais pour les 3 singuliers (excepté 'ns') '''
if cas == cas and cas == cas and cas == cas + suffix:
return True
else:
return False
def endWithSuffix(list, suffix):
'''
Est-ce que toutes les formes du tableau liste ont la même graphie que le
nominatif singulier suffixé par "suffix" ?
@type list: Liste de chaines de caractères ('ns', 'ap',...)
'''
for elt in list:
if cas == cas + suffix:
continue
else:
return False
return True
def equalToNs(list):
'''
Toutes les formes de la liste donnée sont-elles identique au nominatif singulier ?
'''
for elt in list:
if cas == cas:
continue
else:
return False
return True
if genre == 'f':
if plurEndWithSuffix('nen') and singEndWithSuffix('') and cas == 'in':
modele = 'de-nom-f-in'
elif plurEndWithSuffix('en') and singEndWithSuffix(''):
modele = 'de-nom-f-en'
elif plurEndWithSuffix('n') and singEndWithSuffix(''):
modele = 'de-nom-f-n'
if genre == 'm':
if plurEndWithSuffix('en') and singEndWithSuffix('en'):
modele = 'de-nom-m-en'
elif plurEndWithSuffix('n') and singEndWithSuffix('n'):
modele = 'de-nom-m-n'
elif equalToNs() and cas == cas + 's' and \
cas == cas + 'n' and cas == 'er':
modele = 'de-nom-m-er'
elif equalToNs() and cas == cas + 's' and \
endWithSuffix(, 'e') and cas == cas + 'en':
modele = 'de-nom-m-s-e'
elif plurEndWithSuffix('en') and equalToNs() and cas == cas + 's':
modele = 'de-nom-m-s-en'
elif plurEndWithSuffix('s') and equalToNs() and cas == cas + 's':
modele = 'de-nom-m-s-s'
if genre == 'n':
if equalToNs() and cas == cas + 's' and \
endWithSuffix(, 'e') and cas == cas + 'en':
modele = 'de-nom-n-s-e'
elif equalToNs() and cas == cas + 's' and plurEndWithSuffix('') and cas == 'chen':
modele = 'de-nom-n-chen'
elif equalToNs() and cas == cas + 'ses' and \
endWithSuffix(, 'se') and cas == cas + 'sen' and cas == 'is':
modele = 'de-nom-n-is'
elif equalToNs() and cas == cas + 's' and plurEndWithSuffix('') and cas == 'en':
modele = 'de-nom-n-en'
elif equalToNs() and cas == cas + 's' and plurEndWithSuffix('en'):
modele = 'de-nom-n-s-en'
if modele != '':
if cas == titre:
return '{{' + modele + '}}'
else:
return '{{' + modele + '|ns=' + cas + '}}'
else:
return match.group(0)
# Si un paramètre contient deux formes séparées par une virgule, on ne traite pas
if re.search(ur'\{\{de-tab-cas\s*\|+=*?,+?\}\}', contenu):
non_modifiee(titre, msg=u'Deux formes dans un paramètre')
return
if Test:
ldf = re.search(ur'(\'\'\'.*?\'\'\'.*?)\n\{\{de-tab-cas', contenu)
if ldf:
output('Ligne de forme : %s' % ldf.group(1))
# Le pluriel existe-t-il ? Si non, alors on pourra supprimer le pluriel de la ligne
# de forme lorsuqe redondant avec le tableau de flexions
# Cas 1 : absent car égal à -
no_plur = re.search(ur'\{\{de-tab-cas\s*\|+?\|\s*np *=\s*(?:\\]\]|+)\s*\|', contenu)
# Cas 2 : absent car vide et (nominatif) singulier présent
if re.search(ur'\{\{de-tab-cas\s*\|\s*ns *=\s*(?:das|der|die) ', contenu) and \
re.search(ur'\{\{de-tab-cas\s*\|+?\|\s*np *=\s*\|', contenu):
no_plur = True
# de-tab-cas → de-nom
# contenu = re.sub(ur'\{\{de-tab-cas\s*\|', u'{{de-nom|', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *das (?:\'\'\'|\+?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|as= *das (?:\\])?\n?\|ap= *(?:die )?(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\|gs= *des (?:\\])?\n?\|gp= *(?:der)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\|ds= *dem (?:\\])?\n?\|dp= *(?:den)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=n\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns= *die (?:\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\|as= *die (?:\\])?\n?\|ap= *(?:die)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\|gs= *der (?:\\])?\n?\|gp= *(?:der)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\|ds= *der (?:\\])?\n?\|dp= *(?:den)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=f\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns= *der (?:\'\'\'|\+?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\|as= *den (?:\\])?\n?\|ap= *(?:die)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\|gs= *des (?:\\])?\n?\|gp= *(?:der)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\|ds= *dem (?:\\])?\n?\|dp= *(?:den)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\s*\}\}',
ur'{{de-nom|genre=m\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
# Idem, mais lorsque les paramètres pour le singulier sont tous placés au début
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *das (?:\'\'\'|\+?)(?:\'\'\'|\]\])?\n?\|as= *das (?:\\])?\n?\|gs= *des (?:\\])?\n?\|ds= *dem (?:\\])?\n?\|np= *(?:die)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=n\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *die (?:\'\'\'|\+?)(?:\'\'\'|\]\])?\n?\|as= *die (?:\\])?\n?\|gs= *der (?:\\])?\n?\|ds= *der (?:\\])?\n?\|np= *(?:die)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=f\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *der (?:\'\'\'|\+?)(?:\'\'\'|\]\])?\n?\|as= *den (?:\\])?\n?\|gs= *des (?:\\])?\n?\|ds= *dem (?:\\])?\n?\|np= *(?:die)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\?\]\]|\'\'\'|\*?)(?:\'\'\'|\]\])?\}\}',
ur'{{de-nom|genre=m\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
# On enlève les tirets signalant l'absence de paramètre
R = re.compile(ur'(\{\{de-nom\|*?(?:np|ap|dp|gp|ns|as|ds|gs)) *= *+(\}|\n|\|)')
oldContenu = contenu
contenu = R.sub(ur'\1=\2', contenu)
while oldContenu != contenu:
oldContenu = contenu
contenu = R.sub(ur'\1=\2', contenu)
contenu = re.sub(ur'\|(ns|as|gs|ds|np|ap|gp|dp)=\|', ur'|\1= |', contenu)
if re.search('\{\{de-nom\|*?(?:der|den|die|das) ', contenu):
non_modifiee(titre, msg=u'Article défini incorrect dans le tableau de flexions')
return
# de-nom → de-nom-xx
R = re.compile(ur'\{\{de-nom\|genre=(m|f|n)\n\|ns= (*?) \|np= (*?)\n\|as= (*?) \|ap= (*?)\n\|gs= (*?) \|gp= (*?)\n\|ds= (*?) \|dp= (*?)\}\}')
m = R.search(contenu)
# On extrait le nominatif pluriel dont on aura besoin plus base
nom_p = None
if m:
nom_p = m.group(3)
contenu = R.sub(deNomRepl, contenu)
# Placement du modèle avant la ligne du forme
contenu = re.sub(ur'\n(\'\'\'.*?\'\'\'.*?)\n(\{\{de-nom+?\}\})\n', ur'\n\2\n\1\n', contenu)
# Retrait du pluriel entre parenthèses sur la ligne de forme
# lorsque la pron est absente (ex : {{After]] : ' ({{p}}: '''die After''' /…/)')
if nom_p:
contenu = re.sub(ur'(\n\{\{de-nom+?\}\})\n(\'\'\'.*?\'\'\'.*?) *\(\{\{p\}\} *:? \'\'\'(?:die )?(?:\\])?\'\'\'(?: /?/| \{\{pron\|\|de\}\})? *\)\n' % nom_p,
ur'\1\n\2\n', contenu)
if no_plur:
contenu = re.sub(ur'(\n\{\{de-nom+?\}\})\n(\'\'\'.*?\'\'\'.*?) *\(\{\{p\}\} *:? \'\'\'+\'\'\'(?: /?/| \{\{pron\|\|de\}\})? *\)\n',
ur'\1\n\2\n', contenu)
# Remplacement de // par {{pron||de}}
contenu = re.sub(ur'(\n\{\{de-nom+?\}\}\n\'\'\'*?\'\'\') /?/', ur'\1 {{pron||de}}', contenu)
# Ajout de {{pron}} sur la ligne de forme lorsque manquant
contenu = re.sub(ur'(\n\{\{de-nom+?\}\})\n(\'\'\'*?\'\'\') (\{\{{1,2}\}\})\n', ur'\1\n\2 {{pron||de}} \3\n', contenu)
sauvegarde(page, contenu, summary)
def non_modifiee(titre, fichier=fichier_erreurs, msg=None):
'''
Ajout une page à un fichier d'erreurs avec un éventuel message d'erreur
'''
global editcounter
editcounter += 1
output(u'\n%s : %s\r\n' % (titre, msg))
pages_traitees.append(titre)
if edit_fileerror:
with codecs.open(fichier, 'a', config.textfile_encoding) as f:
if msg is None:
f.write(u'# ]\r\n' % titre)
else:
f.write(u'# ] : %s\r\n' % (titre, msg))
# adapté d'un script de JackPotte
def sauvegarde(PageCourante, Contenu, summary, log=True):
global pages_traitees
global pages_modifiees
global editcounter
titre = PageCourante.title()
modif = "o"
chgmt = Contenu != PageCourante.get() # Y a-t-il eu des changements ?
if not chgmt:
non_modifiee(titre, msg=u'Aucun changement')
return
while 1:
if Test:
showDiff(PageCourante.get(), Contenu)
modif = inputChoice(u"Sauvegarder ?",
,
, default="n")
if modif == "o":
if not Test:
ArretDUrgence()
try:
PageCourante.put(Contenu, summary)
except wikipedia.NoPage:
non_modifiee(titre, msg=u"NoPage en sauvegarde",)
return
except wikipedia.IsRedirectPage:
non_modifiee(titre, msg=u"IsRedirectPage en sauvegarde")
return
except wikipedia.LockedPage:
non_modifiee(titre, msg=u"LockedPage en sauvegarde")
return
except pywikibot.EditConflict:
non_modifiee(titre, msg=u"EditConflict en sauvegarde")
return
except wikipedia.ServerError:
non_modifiee(titre, msg=u"ServerError en sauvegarde")
return
except wikipedia.BadTitle:
non_modifiee(titre, msg=u"BadTitle en sauvegarde")
return
except AttributeError:
non_modifiee(titre, msg=u"AttributeError en sauvegarde")
return
else:
editcounter += 1
if log:
# Enregistrement de la liste des pages modifiées dans un fichier texte dédié
pages_traitees.append(titre)
pages_modifiees.append(titre)
if editcounter % frqMajListes == 0:
output(u'\03{lightgreen}%s pages modifiée(s)\03{default}' % editcounter)
with codecs.open(fichier_sortie, 'a', config.textfile_encoding) as f:
for page in pages_modifiees:
f.write(u"%s\r\n" % page)
retireFromList(pages_traitees)
pages_traitees =
pages_modifiees =
return
non_modifiee(titre, msg=u"Exception levée lors de la sauvegarde")
return
elif modif == 'd':
webbrowser.open("http://%s%s" % (
PageCourante.site.hostname(),
PageCourante.site.nice_get_address(titre)
))
i18n.input('pywikibot-enter-finished-browser')
continue
elif modif == 'q':
if len(pages_traitees) > 0:
with codecs.open(fichier_sortie, 'a', config.textfile_encoding) as f:
for page in pages_traitees:
f.write(u"%s\r\n" % page)
retireFromList(pages_traitees)
sys.exit()
else:
output(u"Non modifié (clic de l'utilisateur)")
return
# adapté d'un script de JackPotte
def ArretDUrgence():
page = Page(getSite(), u"User talk:Botomatik")
if page.exists():
try:
lastContributor = page.userName()
except wikipedia.NoPage: return
except wikipedia.IsRedirectPage: return
except wikipedia.LockedPage: return
except wikipedia.ServerError: return
except wikipedia.BadTitle: return
except wikipedia.EditConflict: return
if lastContributor != u'Automatik':
output(u"\n*** \03{lightyellow}Arrêt d'urgence demandé\03{default} ***")
sys.exit(0)
else:
output(u'La page de discussion du robot n\'existe pas')
def TraiteFichier(fichier=fichier_entree, fonction=main, *args, **kwargs):
"""
Applique une fonction à toutes les pages d'un fichier,
en lui passant la page pour premier paramètre
"""
gen = pagegenerators.TextfilePageGenerator(fichier)
for page in gen:
fonction(page, *args, **kwargs)
def retireFromList(pages):
"""Retire des pages du fichier d'entrée
Les pages doivent être sous la forme # ]
@param pages: liste de pages
"""
with codecs.open(fichier_entree, 'r', config.textfile_encoding) as f:
contenu = f.read()
for page in pages:
contenu = re.sub(ur"# \\]\r\n" % page, u"", contenu)
with codecs.open(fichier_entree, 'w', config.textfile_encoding) as f:
f.write(contenu)
if __name__ == '__main__':
try:
# main(Page(getSite(), 'Adamsapfel'))
TraiteFichier()
finally:
stopme()