Revision: 6256
Author: russblau
Date: 2009-01-14 15:17:47 +0000 (Wed, 14 Jan 2009)
Log Message:
-----------
Branch for port to rewrite
Added Paths:
-----------
branches/rewrite/pywikibot/scripts/solve_disambiguation.py
Copied: branches/rewrite/pywikibot/scripts/solve_disambiguation.py (from rev 6255, trunk/pywikipedia/solve_disambiguation.py)
===================================================================
--- branches/rewrite/pywikibot/scripts/solve_disambiguation.py (rev 0)
+++ branches/rewrite/pywikibot/scripts/solve_disambiguation.py 2009-01-14 15:17:47 UTC (rev 6256)
@@ -0,0 +1,1012 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""
+Script to help a human solve disambiguations by presenting a set of options.
+
+Specify the disambiguation page on the command line, or enter it at the
+prompt after starting the program. (If the disambiguation page title starts
+with a '-', you cannot name it on the command line, but you can enter it at
+the prompt.) The program will pick up the page, and look for all
+alternative links, and show them with a number adjacent to them. It will
+then automatically loop over all pages referring to the disambiguation page,
+and show 30 characters of context on each side of the reference to help you
+make the decision between the alternatives. It will ask you to type the
+number of the appropriate replacement, and perform the change.
+
+It is possible to choose to replace only the link (just type the number) or
+replace both link and link-text (type 'r' followed by the number).
+
+Multiple references in one page will be scanned in order, but typing 'n' (next)
+on any one of them will leave the complete page unchanged. To leave only some reference unchanged, use the 's' (skip) option.
+
+Command line options:
+
+ -pos:XXXX adds XXXX as an alternative disambiguation
+
+ -just only use the alternatives given on the command line, do not
+ read the page for other possibilities
+
+ -primary "primary topic" disambiguation (Begriffsklärung nach Modell 2).
+ That's titles where one topic is much more important, the
+ disambiguation page is saved somewhere else, and the important
+ topic gets the nice name.
+
+ -primary:XY like the above, but use XY as the only alternative, instead of
+ searching for alternatives in [[Keyword (disambiguation)]].
+ Note: this is the same as -primary -just -pos:XY
+
+ -file:XYZ reads a list of pages from a text file. XYZ is the name of the
+ file from which the list is taken. If XYZ is not given, the user is asked for a filename.
+ Page titles should be inside [[double brackets]].
+ The -pos parameter won't work if -file is used.
+
+ -always:XY instead of asking the user what to do, always perform the same
+ action. For example, XY can be "r0", "u" or "2". Be careful with
+ this option, and check the changes made by the bot. Note that
+ some choices for XY don't make sense and will result in a loop,
+ e.g. "l" or "m".
+
+ -main only check pages in the main namespace, not in the talk,
+ wikipedia, user, etc. namespaces.
+
+ -start:XY goes through all disambiguation pages in the category on your wiki
+ that is defined (to the bot) as the category containing disambiguation
+ pages, starting at XY. If only '-start' or '-start:' is given, it starts
+ at the beginning.
+
+ -min:XX (XX being a number) only work on disambiguation pages for which
+ at least XX are to be worked on.
+
+To complete a move of a page, one can use:
+
+ python solve_disambiguation.py -just -pos:New_Name Old_Name
+"""
+#
+# (C) Rob W.W. Hooft, 2003
+# (C) Daniel Herding, 2004
+# (C) Andre Engels, 2003-2004
+# (C) WikiWichtel, 2004
+#
+# Distributed under the terms of the MIT license.
+#
+__version__='$Id$'
+#
+# Standard library imports
+import re, sys, codecs
+
+# Application specific imports
+import wikipedia, pagegenerators, editarticle
+
+# Summary message when working on disambiguation pages
+msg = {
+ 'ar': u'توضيح بمساعدة روبوت: %s - غير الوصلة أو الوصلات إلى %s',
+ 'cs': u'Odstranění linku na rozcestník [[%s]] s použitím robota - Změněn(y) odkaz(y) na %s',
+ 'en': u'Robot-assisted disambiguation: %s - Changed link(s) to %s',
+ 'es': u'Bot:Desambiguación asistida: %s - Cambiando enlace(s) para %s',
+ 'da': u'Retter flertydigt link til: %s - Ændrede link(s) til %s',
+ 'de': u'Bot-unterstützte Begriffsklärung: %s - Link(s) ersetzt durch %s',
+ 'fi': u'Täsmennystä botin avulla: %s korvattiin link(e)illä %s',
+ 'fr': u'Homonymie résolue à l\'aide du robot: %s - Modifications du (des) lien(s) pour %s',
+ 'he': u'תיקון קישור לדף פירושונים באמצעות בוט: %s',
+ 'hu': u'Bottal végzett egyértelműsítés: %s –> %s',
+ 'ia': u'Disambiguation assistite per robot: %s - Changed link(s) to %s',
+ 'it': u'Sistemazione automatica della disambigua: %s - Inversione di redirect %s',
+ 'lt': u'Nuorodų į nukrepiamąjį straipsnį keitimas: %s - Pakeistos nuorodos į %s',
+ 'kk': u'Айрықты мағыналарды бот көмегімен шешу: %s - Changed link(s) to %s',
+ 'ko': u'로봇의 도움을 받아 동음이의 처리 : [[%s]] - %s 문서로 링크 걸음',
+ 'nl': u'Robot-geholpen doorverwijzing: [[%s]] - Link(s) veranderd naar %s',
+ 'no': u'bot: Retter lenke til peker: %s - Endret lenke(r) til %s',
+ 'pl': u'Wspomagane przez robota ujednoznacznienie: %s - Zmieniono link(i) %s',
+ 'pt': u'Desambiguação assistida por bot: %s link(s) mudado(s) para %s',
+ 'ru': u'Разрешение значений с помощью бота: %s - Changed link(s) to %s',
+ 'sr': u'Решавање вишезначних одредница помоћу бота: %s - Changed link(s) to %s',
+ 'sv': u'Länkar direkt till rätt artikel för: %s - Bytte länk(ar) till %s',
+ }
+
+# Summary message when working on disambiguation pages and the link is removed
+msg_unlink = {
+ 'ar': u'توضيح بمساعدة روبوت: %s - أزال الوصلة أو الوصلات.',
+ 'cs': u'Odstranění linku na rozcestník [[%s]] s použitím robota - Odstraněn(y) odkaz(y)',
+ 'en': u'Robot-assisted disambiguation: %s - Removed link(s).',
+ 'da': u'Retter flertydigt link til: %s - Fjernede link(s)',
+ 'de': u'Bot-unterstützte Begriffsklärung: %s - Link(s) entfernt',
+ 'fi': u'Täsmennystä botin avulla: %s - poistettiin linkkejä.',
+ 'fr': u'Homonymie résolue à l\'aide du robot: %s - Retrait du (des) lien(s)',
+ 'he': u'הסרת קישור לדף פירושונים באמצעות בוט: %s',
+ 'hu': u'Bottal végzett egyértelműsítés: %s – hivatkozások eltávolítása',
+ 'ia': u'Disambiguation assistite per robot: %s - Removed link(s).',
+ 'it': u'Sistemazione automatica della disambigua: %s - Collegamenti rimossi',
+ 'lt': u'Nuorodų į nukrepiamąjį straipsnį keitimas: %s - Pašalintos nuorodos',
+ 'kk': u'Айрықты мағыналарды бот көмегімен шешу: %s - Removed link(s).',
+ 'ko': u'로봇의 도움을 받아 동음이의 처리: [[%s]] - 링크 제거',
+ 'nl': u'Robot-geholpen doorverwijzing: [[%s]] - Link(s) weggehaald.',
+ 'no': u'bot: Retter lenke til peker: %s - Fjernet lenke(r)',
+ 'pl': u'Wspomagane przez robota ujednoznacznienie: %s - Usunięto link(i)',
+ 'pt': u'Desambiguação assistida por bot: %s link(s) removido(s)',
+ 'ru': u'Разрешение значений с помощью бота: %s - Removed link(s)',
+ 'sr': u'Решавање вишезначних одредница помоћу бота: %s - Removed link(s)',
+ 'sv': u'Länkar direkt till rätt artikel för: %s - Tog bort länk(ar)',
+ }
+
+# Summary message when working on redirects
+msg_redir = {
+ 'ar': u'توضيح بمساعدة روبوت: %s - غير الوصلة أو الوصلات إلى %s',
+ 'cs': u'Robot opravil přesměrování na %s - Změněn(y) odkaz(y) na %s',
+ 'en': u'Robot-assisted disambiguation: %s - Changed link(s) to %s',
+ 'da': u'Retter flertydigt link til: %s - Ændrede link(s) til %s',
+ 'de': u'Bot-unterstützte Redirectauflösung: %s - Link(s) ersetzt durch %s',
+ 'fi': u'Täsmennystä botin avulla: %s korvattiin link(e)illä %s',
+ 'fr': u'Correction de lien vers redirect: %s - Modifications du (des) lien(s) pour %s',
+ 'he': u'תיקון קישור לדף פירושונים באמצעות בוט: %s שונה ל%s',
+ 'hu': u'Bottal végzett egyértelműsítés: %s –> %s',
+ 'ia': u'Resolution de redirectiones assistite per robot: %s - Changed link(s) to %s',
+ 'it': u'Sistemazione automatica del redirect: %s - Inversione di redirect %s',
+ 'lt': u'Nuorodų į peradresavimo straipsnį keitimas: %s - Pakeistos nuorodos į %s',
+ 'kk': u'Айрықты мағыналарды бот көмегімен шешу: %s - Changed link(s) to %s',
+ 'ko': u'로봇의 도움을 받아 동음이의 처리: [[%s]] - %s 문서로 링크 걸음',
+ 'nl': u'Robot-geholpen redirect-oplossing: [[%s]] - Link(s) veranderd naar %s',
+ 'no': u'bot: Endrer omdirigeringslenke: %s - Endret lenke(r) til %s',
+ 'pl': u'Wspomagane przez robota ujednoznacznienie: %s - Zmieniono link(i) %s',
+ 'pt': u'Desambiguação assistida por bot: %s link(s) mudados para %s',
+ 'ru': u'Разрешение значений с помощью бота: %s - Changed link(s) to %s',
+ 'sr': u'Решавање вишезначних одредница помоћу бота: %s - Changed link(s) to %s',
+ 'sv': u'Länkar direkt till rätt artikel för: %s - Bytte länk(ar) till %s',
+ }
+
+# Summary message when working on redirects and the link is removed
+msg_redir_unlink = {
+ 'ar': u'توضيح بمساعدة روبوت: %s - أزال الوصلة أو الوصلات',
+ 'cs': u'Robot opravil přesměrování na %s - Odstraněn(y) odkaz(y)',
+ 'en': u'Robot-assisted disambiguation: %s - Removed link(s)',
+ 'da': u'Retter flertydigt link til: %s - Fjernede link(s)',
+ 'de': u'Bot-unterstützte Redirectauflösung: %s - Link(s) entfernt',
+ 'fr': u'Correction de lien vers redirect: %s - Retrait du (des) lien(s)',
+ 'fi': u'Täsmennystä botin avulla: %s - poistettiin linkkejä',
+ 'he': u'הסרת קישור לדף פירושונים באמצעות בוט: %s',
+ 'hu': u'Bottal támogatott egyértelműsítés: %s – hivatkozások eltávolítása',
+ 'ia': u'Resolution de redirectiones assistite per robot: %s - Removed link(s).',
+ 'it': u'Sistemazione automatica del redirect: %s - Collegamenti rimossi',
+ 'lt': u'Nuorodų į peradresavimo straipsnį keitimas: %s - Pašalintos nuorodos',
+ 'kk': u'Айрықты мағыналарды бот көмегімен шешу: %s - Removed link(s).',
+ 'ko': u'로봇의 도움을 받아 동음이의 처리: [[%s]] - 링크 제거',
+ 'nl': u'Robot-geholpen redirect-oplossing: [[%s]] - Link(s) weggehaald',
+ 'no': u'bot: Endrer omdirigeringslenke: %s - Fjernet lenke(r)',
+ 'pl': u'Wspomagane przez robota ujednoznacznienie: %s - Usunięto link(i)',
+ 'pt': u'Desambiguação assistida por bot: %s link(s) removidos',
+ 'ru': u'Разрешение значений с помощью бота: %s - Removed link(s)',
+ 'sr': u'Решавање вишезначних одредница помоћу бота: %s - Removed link(s)',
+ 'sv': u'Länkar direkt till rätt artikel för: %s - Tog bort länk(ar)',
+ }
+
+# Summary message to (unknown)
+unknown_msg = {
+ 'ar' : u'(غير معروف)',
+ 'en' : u'(unknown)',
+ 'fi' : u'(tuntematon)',
+ 'hu' : u'(ismeretlen)',
+ 'pt' : u'(desconhecido)',
+ }
+
+# disambiguation page name format for "primary topic" disambiguations
+# (Begriffsklärungen nach Modell 2)
+primary_topic_format = {
+ 'ar': u'%s_(توضيح)',
+ 'cs': u'%s_(rozcestník)',
+ 'de': u'%s_(Begriffsklärung)',
+ 'en': u'%s_(disambiguation)',
+ 'fi': u'%s_(täsmennyssivu)',
+ 'hu': u'%s_(egyértelműsítő lap)',
+ 'ia': u'%s_(disambiguation)',
+ 'it': u'%s_(disambigua)',
+ 'lt': u'%s_(reikšmės)',
+ 'kk': u'%s_(айрық)',
+ 'ko': u'%s_(동음이의)',
+ 'nl': u'%s_(doorverwijspagina)',
+ 'no': u'%s_(peker)',
+ 'pl': u'%s_(ujednoznacznienie)',
+ 'pt': u'%s_(desambiguação)',
+ 'he': u'%s_(פירושונים)',
+ 'ru': u'%s_(значения)',
+ 'sr': u'%s_(вишезначна одредница)',
+ 'sv': u'%s_(olika betydelser)',
+ }
+
+# List pages that will be ignored if they got a link to a disambiguation
+# page. An example is a page listing disambiguations articles.
+# Special chars should be encoded with unicode (\x##) and space used
+# instead of _
+
+ignore_title = {
+ 'wikipedia': {
+ 'ar': [
+ u'تصنيف:صفحات توضيح',
+ ],
+ 'cs': [
+ u'Wikipedie:Chybějící interwiki/.+',
+ u'Wikipedie:Rozcestníky',
+ u'Wikipedie diskuse:Rozcestníky',
+ u'Wikipedie:Seznam nejvíce odkazovaných rozcestníků',
+ u'Wikipedie:Seznam rozcestníků/první typ',
+ u'Wikipedie:Seznam rozcestníků/druhý typ',
+ u'Wikipedista:Zirland/okres',
+ ],
+ 'da': [
+ u'Wikipedia:Links til sider med flertydige titler'
+ ],
+ 'de': [
+ u'Benutzer:Katharina/Begriffsklärungen',
+ u'Benutzer:Kirschblut/.+buchstabenkürzel',
+ u'Benutzer:Noisper/Dingliste/[A-Z]',
+ u'Benutzer:SirJective/.+',
+ u'Benutzer:SrbBot/Index/.+',
+ u'Benutzer Diskussion:.+',
+ u'GISLexikon \([A-Z]\)',
+ u'Lehnwort',
+ u'Liste griechischer Wortstämme in deutschen Fremdwörtern',
+ u'Liste von Gräzismen',
+ u'Portal:Abkürzungen/.+',
+ u'Wikipedia:Archiv:.+',
+ u'Wikipedia:Artikelwünsche/Ding-Liste/[A-Z]',
+ u'Wikipedia:Begriffsklärung.*',
+ u'Wikipedia:Dreibuchstabenkürzel von [A-Z][A-Z][A-Z] bis [A-Z][A-Z][A-Z]',
+ u'Wikipedia:Interwiki-Konflikte',
+ u'Wikipedia:Kurze Artikel',
+ u'Wikipedia:Liste aller 2-Buchstaben-Kombinationen',
+ u'Wikipedia:Liste mathematischer Themen/BKS',
+ u'Wikipedia:Liste mathematischer Themen/Redirects',
+ u'Wikipedia:Löschkandidaten/.+',
+ u'Wikipedia:Qualitätsoffensive/UNO', #requested by Benutzer:Addicted
+ u'Wikipedia:WikiProjekt Altertumswissenschaft/.+',
+ u'Wikipedia:WikiProjekt Verwaiste Seiten/Begriffsklärungen',
+ ],
+ 'en': [
+ u'Wikipedia:Links to disambiguating pages',
+ u'Wikipedia:Disambiguation pages with links',
+ u'Wikipedia:Multiple-place names \([A-Z]\)',
+ u'Wikipedia:Non-unique personal name',
+ u"User:Jerzy/Disambiguation Pages i've Editted",
+ u'User:Gareth Owen/inprogress',
+ u'TLAs from [A-Z][A-Z][A-Z] to [A-Z][A-Z][A-Z]',
+ u'List of all two-letter combinations',
+ u'User:Daniel Quinlan/redirects.+',
+ u'User:Oliver Pereira/stuff',
+ u'Wikipedia:French Wikipedia language links',
+ u'Wikipedia:Polish language links',
+ u'Wikipedia:Undisambiguated abbreviations/.+',
+ u'List of acronyms and initialisms',
+ u'Wikipedia:Usemod article histories',
+ u'User:Pizza Puzzle/stuff',
+ u'List of generic names of political parties',
+ u'Talk:List of initialisms/marked',
+ u'Talk:List of initialisms/sorted',
+ u'Talk:Programming language',
+ u'Talk:SAMPA/To do',
+ u"Wikipedia:Outline of Roget's Thesaurus",
+ u'User:Wik/Articles',
+ u'User:Egil/Sandbox',
+ u'Wikipedia talk:Make only links relevant to the context',
+ u'Wikipedia:Common words, searching for which is not possible',
+ ],
+ 'fi': [
+ u'Wikipedia:Luettelo täsmennyssivuista',
+ u'Wikipedia:Luettelo (täsmennyssivuista)',
+ u'Wikipedia:Täsmennyssivu',
+ ],
+ 'fr': [
+ u'Wikipédia:Liens aux pages d\'homonymie',
+ u'Wikipédia:Homonymie',
+ u'Wikipédia:Homonymie/Homonymes dynastiques',
+ u'Wikipédia:Prise de décision, noms des membres de dynasties/liste des dynastiens',
+ u'Liste de toutes les combinaisons de deux lettres',
+ u'Wikipédia:Log d\'upload/.*',
+ u'Sigles de trois lettres de [A-Z]AA à [A-Z]ZZ',
+ u'Wikipédia:Pages sans interwiki,.'
+ ],
+ 'fy': [
+ u'Wikipedy:Fangnet',
+ ],
+ 'ia': [
+ u'Categoria:Disambiguation',
+ u'Wikipedia:.+',
+ u'Usator:.+',
+ u'Discussion Usator:.+',
+ ],
+ 'it': [
+ u'Aiuto:Disambigua/Disorfanamento',
+ u'Discussioni utente:.+',
+ u'Utente:Civvì/disorfanamento',
+ ],
+ 'kk': [
+ u'Санат:Айрықты бет',
+ ],
+ 'ko': [
+ u'위키백과:(동음이의) 문서의 목록',
+ u'위키백과:동음이의어 문서의 목록',
+ ],
+ 'lt': [
+ u'Wikipedia:Rodomi nukreipiamieji straipsniai',
+ ],
+ 'nl': [
+ u"Gebruiker:.*",
+ u"Overleg gebruiker:.+[aA]rchief.*",
+ u"Overleg gebruiker:Pven",
+ u"Portaal:.+[aA]rchief.*",
+ u"Wikipedia:Humor en onzin.*",
+ u"Wikipedia:Links naar doorverwijspagina's/Winkeldochters.*",
+ u"Wikipedia:Project aanmelding bij startpagina's",
+ u"Wikipedia:Wikiproject Roemeense gemeenten/Doorverwijspagina's",
+ u'Categorie:Doorverwijspagina',
+ u'Lijst van Nederlandse namen van pausen',
+ u'Overleg Wikipedia:Discussie spelling 2005',
+ u'Overleg Wikipedia:Doorverwijspagina',
+ u'Overleg Wikipedia:Logboek.*',
+ u'Wikipedia:Logboek.*',
+ u'Overleg gebruiker:Sybren/test.*',
+ u'Overleg gebruiker:[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?',
+ u'Overleg:Lage Landen (staatkunde)',
+ u'Wikipedia:.*[aA]rchief.*',
+ u'Wikipedia:Doorverwijspagina',
+ u'Wikipedia:Lijst van alle tweeletter-combinaties',
+ u'Wikipedia:Onderhoudspagina',
+ u'Wikipedia:Ongelijke redirects',
+ u'Wikipedia:Protection log',
+ u'Wikipedia:Te verwijderen.*',
+ u'Wikipedia:Top 1000 van meest bekeken artikelen',
+ u'Wikipedia:Wikipedianen met een encyclopedisch artikel',
+ u'Wikipedia:Woorden die niet als zoekterm gebruikt kunnen worden',
+ ],
+ 'pl': [
+ u'Wikipedysta:.+',
+ u'Dyskusja.+:.+',
+ ],
+ 'pt': [
+ u'Usuário:.+',
+ u'Usuário Discussão:.+',
+ u'Discussão:.+',
+ u'Lista de combinações de duas letras',
+ u'Wikipedia:Lista de páginas de desambiguação.+',
+ u'Wikipedia:Páginas para eliminar/.+',
+ ],
+ 'ru': [
+ u'Категория:Disambig',
+ u'Википедия:Страницы разрешения неоднозначностей',
+ u'Википедия:Вики-уборка/Статьи без языковых ссылок',
+ u'Википедия:Страницы с пометкой «(значения)»',
+ u'Список общерусских фамилий',
+ ],
+ },
+ 'memoryalpha': {
+ 'en': [
+ u'Memory Alpha:Links to disambiguating pages'
+ ],
+ 'de': [
+ u'Memory Alpha:Liste der Wortklärungsseiten'
+ ],
+ },
+}
+
+def firstcap(string):
+ return string[0].upper()+string[1:]
+
+def correctcap(link, text):
+ # If text links to a page with title link uncapitalized, uncapitalize link, otherwise capitalize it
+ linkupper = link.title()
+ linklower = linkupper[0].lower() + linkupper[1:]
+ if text.find("[[%s]]"%linklower) > -1 or text.find("[[%s|"%linklower) > -1:
+ return linklower
+ else:
+ return linkupper
+
+class ReferringPageGeneratorWithIgnore:
+ def __init__(self, disambPage, primary=False, minimum = 0):
+ self.disambPage = disambPage
+ # if run with the -primary argument, enable the ignore manager
+ self.primaryIgnoreManager = PrimaryIgnoreManager(disambPage,
+ enabled=primary)
+ self.minimum = minimum
+
+ def __iter__(self):
+ # TODO: start yielding before all referring pages have been found
+ refs = [page for page in self.disambPage.getReferences(follow_redirects = False, withTemplateInclusion = False)]
+ wikipedia.output(u"Found %d references." % len(refs))
+ # Remove ignorables
+ if ignore_title.has_key(self.disambPage.site().family.name) and ignore_title[self.disambPage.site().family.name].has_key(self.disambPage.site().lang):
+ for ig in ignore_title[self.disambPage.site().family.name][self.disambPage.site().lang]:
+ for i in range(len(refs)-1, -1, -1):
+ if re.match(ig, refs[i].title()):
+ if wikipedia.verbose:
+ wikipedia.output('Ignoring page %s'
+ % refs[i].title())
+ del refs[i]
+ elif self.primaryIgnoreManager.isIgnored(refs[i]):
+ #wikipedia.output('Ignoring page %s because it was skipped before' % refs[i].title())
+ del refs[i]
+ if len(refs) < self.minimum:
+ wikipedia.output(u"Found only %d pages to work on; skipping." % len(refs))
+ return
+ wikipedia.output(u"Will work on %d pages." % len(refs))
+ for ref in refs:
+ yield ref
+
+class PrimaryIgnoreManager(object):
+ '''
+ If run with the -primary argument, reads from a file which pages should
+ not be worked on; these are the ones where the user pressed n last time.
+ If run without the -primary argument, doesn't ignore any pages.
+ '''
+ def __init__(self, disambPage, enabled = False):
+ self.disambPage = disambPage
+ self.enabled = enabled
+
+ self.ignorelist = []
+ filename = wikipedia.config.datafilepath('disambiguations',
+ self.disambPage.titleForFilename() + '.txt')
+ try:
+ # The file is stored in the disambiguation/ subdir. Create if necessary.
+ f = codecs.open(filename, 'r', 'utf-8')
+ for line in f.readlines():
+ # remove trailing newlines and carriage returns
+ while line[-1] in ['\n', '\r']:
+ line = line[:-1]
+ #skip empty lines
+ if line != '':
+ self.ignorelist.append(line)
+ f.close()
+ except IOError:
+ pass
+
+ def isIgnored(self, refPage):
+ return self.enabled and refPage.urlname() in self.ignorelist
+
+ def ignore(self, refPage):
+ if self.enabled:
+ # Skip this occurence next time.
+ filename = wikipedia.config.datafilepath('disambiguations',
+ self.disambPage.urlname() + '.txt')
+ try:
+ # Open file for appending. If none exists yet, create a new one.
+ # The file is stored in the disambiguation/ subdir. Create if necessary.
+ f = codecs.open(filename, 'a', 'utf-8')
+ f.write(refPage.urlname() + '\n')
+ f.close()
+ except IOError:
+ pass
+
+
+class DisambiguationRobot(object):
+ ignore_contents = {
+ 'de':(u'{{[Ii]nuse}}',
+ u'{{[Ll]öschen}}',
+ ),
+ 'fi':(u'{{[Tt]yöstetään}}',
+ ),
+ 'kk':(u'{{[Ii]nuse}}',
+ u'{{[Pp]rocessing}}',
+ ),
+ 'nl':(u'{{wiu2}}',
+ u'{{nuweg}}',
+ ),
+ 'ru':(u'{{[Ii]nuse}}',
+ u'{{[Pp]rocessing}}',
+ ),
+ }
+
+ primary_redir_template = {
+ # Page.templates() format, first letter uppercase
+ 'hu': u'Egyért-redir',
+ }
+
+ def __init__(self, always, alternatives, getAlternatives, generator, primary, main_only, minimum = 0):
+ self.always = always
+ self.alternatives = alternatives
+ self.getAlternatives = getAlternatives
+ self.generator = generator
+ self.primary = primary
+ self.main_only = main_only
+ self.minimum = minimum
+
+ self.mysite = wikipedia.getSite()
+ self.mylang = self.mysite.language()
+ self.comment = None
+
+ self.setupRegexes()
+
+ def checkContents(self, text):
+ '''
+ For a given text, returns False if none of the regular
+ expressions given in the dictionary at the top of this class
+ matches a substring of the text.
+ Otherwise returns the substring which is matched by one of
+ the regular expressions.
+ '''
+ for ig in self.ignore_contents_regexes:
+ match = ig.search(text)
+ if match:
+ return match.group()
+ return None
+
+ def makeAlternativesUnique(self):
+ # remove duplicate entries
+ result={}
+ for i in self.alternatives:
+ result[i]=None
+ self.alternatives = result.keys()
+
+ def listAlternatives(self):
+ list = u'\n'
+ for i in range(len(self.alternatives)):
+ list += (u"%3i - %s\n" % (i, self.alternatives[i]))
+ wikipedia.output(list)
+
+ def setupRegexes(self):
+ # compile regular expressions
+ self.ignore_contents_regexes = []
+ if self.ignore_contents.has_key(self.mylang):
+ for ig in self.ignore_contents[self.mylang]:
+ self.ignore_contents_regexes.append(re.compile(ig))
+
+ linktrail = self.mysite.linktrail()
+ self.trailR = re.compile(linktrail)
+ # The regular expression which finds links. Results consist of four groups:
+ # group title is the target page title, that is, everything before | or ].
+ # group section is the page section. It'll include the # to make life easier for us.
+ # group label is the alternative link title, that's everything between | and ].
+ # group linktrail is the link trail, that's letters after ]] which are part of the word.
+ # note that the definition of 'letter' varies from language to language.
+ self.linkR = re.compile(r'\[\[(?P<title>[^\]\|#]*)(?P<section>#[^\]\|]*)?(\|(?P<label>[^\]]*))?\]\](?P<linktrail>' + linktrail + ')')
+
+ def treat(self, refPage, disambPage):
+ """
+ Parameters:
+ disambPage - The disambiguation page or redirect we don't want anything
+ to link on
+ refPage - A page linking to disambPage
+ Returns False if the user pressed q to completely quit the program.
+ Otherwise, returns True.
+ """
+ # TODO: break this function up into subroutines!
+
+ include = False
+ unlink = False
+ new_targets = []
+ try:
+ text=refPage.get(throttle=False)
+ ignoreReason = self.checkContents(text)
+ if ignoreReason:
+ wikipedia.output('\n\nSkipping %s because it contains %s.\n\n' % (refPage.title(), ignoreReason))
+ else:
+ include = True
+ except wikipedia.IsRedirectPage:
+ wikipedia.output(u'%s is a redirect to %s' % (refPage.title(), disambPage.title()))
+ if disambPage.isRedirectPage():
+ target = self.alternatives[0]
+ choice = wikipedia.inputChoice(u'Do you want to make redirect %s point to %s?' % (refPage.title(), target), ['yes', 'no'], ['y', 'N'], 'N')
+ if choice == 'y':
+ redir_text = '#%s [[%s]]' % (self.mysite.redirect(default=True), target)
+ try:
+ refPage.put_async(redir_text,comment=self.comment)
+ except wikipedia.PageNotSaved, error:
+ wikipedia.output(u'Page not saved: %s' % error.args)
+ else:
+ choice = wikipedia.inputChoice(u'Do you want to work on pages linking to %s?' % refPage.title(), ['yes', 'no', 'change redirect'], ['y', 'N', 'c'], 'N')
+ if choice == 'y':
+ gen = ReferringPageGeneratorWithIgnore(refPage, self.primary)
+ preloadingGen = pagegenerators.PreloadingGenerator(gen)
+ for refPage2 in preloadingGen:
+ # run until the user selected 'quit'
+ if not self.treat(refPage2, refPage):
+ break
+ elif choice == 'c':
+ text=refPage.get(throttle=False,get_redirect=True)
+ include = "redirect"
+ except wikipedia.NoPage:
+ wikipedia.output(u'Page [[%s]] does not seem to exist?! Skipping.' % refPage.title())
+ include = False
+ if include in (True, "redirect"):
+ # make a backup of the original text so we can show the changes later
+ original_text = text
+ n = 0
+ curpos = 0
+ edited = False
+ # This loop will run until we have finished the current page
+ while True:
+ m = self.linkR.search(text, pos = curpos)
+ if not m:
+ if n == 0:
+ wikipedia.output(u"No changes necessary in %s" % refPage.title())
+ return True
+ else:
+ # stop loop and save page
+ break
+ # Make sure that next time around we will not find this same hit.
+ curpos = m.start() + 1
+ # ignore interwiki links and links to sections of the same page
+ if m.group('title') == '' or self.mysite.isInterwikiLink(m.group('title')):
+ continue
+ else:
+ try:
+ linkPage = wikipedia.Page(disambPage.site(), m.group('title'))
+ # Check whether the link found is to disambPage.
+ except wikipedia.InvalidTitle:
+ continue
+ if linkPage != disambPage:
+ continue
+
+ n += 1
+ # how many bytes should be displayed around the current link
+ context = 60
+ # This loop will run while the user doesn't choose an option
+ # that will actually change the page
+ while True:
+ # Show the title of the page where the link was found.
+ # Highlight the title in purple.
+ wikipedia.output(u"\n\n>>> \03{lightpurple}%s\03{default} <<<" % refPage.title())
+
+ # at the beginning of the link, start red color.
+ # at the end of the link, reset the color to default
+ wikipedia.output(text[max(0, m.start() - context) : m.start()] + '\03{lightred}' + text[m.start() : m.end()] + '\03{default}' + text[m.end() : m.end() + context])
+
+ if not self.always:
+ if edited:
+ choice = wikipedia.input(u"Option (#, r#, s=skip link, e=edit page, n=next page, u=unlink, q=quit\n"
+ " m=more context, l=list, a=add new, x=save in this form):")
+ else:
+ choice = wikipedia.input(u"Option (#, r#, s=skip link, e=edit page, n=next page, u=unlink, q=quit\n"
+ " m=more context, d=show disambiguation page, l=list, a=add new):")
+ else:
+ choice = self.always
+ if choice in ['a', 'A']:
+ newAlternative = wikipedia.input(u'New alternative:')
+ self.alternatives.append(newAlternative)
+ self.listAlternatives()
+ elif choice in ['e', 'E']:
+ editor = editarticle.TextEditor()
+ newText = editor.edit(text, jumpIndex = m.start(), highlight = disambPage.title())
+ # if user didn't press Cancel
+ if newText and newText != text:
+ text = newText
+ break
+ elif choice in ['d', 'D']:
+ editor = editarticle.TextEditor()
+ if disambPage.isRedirectPage():
+ disambredir = disambPage.getRedirectTarget()
+ disambigText = editor.edit(disambredir.get(), jumpIndex = m.start(), highlight = disambredir.title())
+ else:
+ disambigText = editor.edit(disambPage.get(), jumpIndex = m.start(), highlight = disambPage.title())
+ elif choice in ['l', 'L']:
+ self.listAlternatives()
+ elif choice in ['m', 'M']:
+ # show more text around the link we're working on
+ context *= 2
+ else:
+ break
+
+ if choice in ['e', 'E']:
+ # user has edited the page and then pressed 'OK'
+ edited = True
+ curpos = 0
+ continue
+ elif choice in ['n', 'N']:
+ # skip this page
+ if self.primary:
+ # If run with the -primary argument, skip this occurence next time.
+ self.primaryIgnoreManager.ignore(refPage)
+ return True
+ elif choice in ['q', 'Q']:
+ # quit the program
+ return False
+ elif choice in ['s', 'S']:
+ # Next link on this page
+ n -= 1
+ continue
+ elif choice in ['x', 'X'] and edited:
+ # Save the page as is
+ break
+
+ # The link looks like this:
+ # [[page_title|link_text]]trailing_chars
+ page_title = m.group('title')
+ link_text = m.group('label')
+
+ if not link_text:
+ # or like this: [[page_title]]trailing_chars
+ link_text = page_title
+ if m.group('section') == None:
+ section = ''
+ else:
+ section = m.group('section')
+ trailing_chars = m.group('linktrail')
+ if trailing_chars:
+ link_text += trailing_chars
+
+ if choice in ['u', 'U']:
+ # unlink - we remove the section if there's any
+ text = text[:m.start()] + link_text + text[m.end():]
+ unlink = True
+ continue
+ else:
+ if len(choice)>0 and choice[0] == 'r':
+ # we want to throw away the original link text
+ replaceit = True
+ choice = choice[1:]
+ elif include == "redirect":
+ replaceit = True
+ else:
+ replaceit = False
+
+ try:
+ choice=int(choice)
+ except ValueError:
+ wikipedia.output(u"Unknown option")
+ # step back to ask the user again what to do with the current link
+ curpos -= 1
+ continue
+ if choice >= len(self.alternatives) or choice < 0:
+ wikipedia.output(u"Choice out of range. Please select a number between 0 and %i." % (len(self.alternatives) - 1))
+ # show list of possible choices
+ self.listAlternatives()
+ # step back to ask the user again what to do with the current link
+ curpos -= 1
+ continue
+ new_page_title = self.alternatives[choice]
+ repPl = wikipedia.Page(disambPage.site(), new_page_title)
+ if (new_page_title[0].isupper()) or (link_text[0].isupper()):
+ new_page_title = repPl.title()
+ else:
+ new_page_title = repPl.title()
+ new_page_title = new_page_title[0].lower() + new_page_title[1:]
+ if new_page_title not in new_targets:
+ new_targets.append(new_page_title)
+ if replaceit and trailing_chars:
+ newlink = "[[%s%s]]%s" % (new_page_title, section, trailing_chars)
+ elif replaceit or (new_page_title == link_text and not section):
+ newlink = "[[%s]]" % new_page_title
+ # check if we can create a link with trailing characters instead of a pipelink
+ elif len(new_page_title) <= len(link_text) and firstcap(link_text[:len(new_page_title)]) == firstcap(new_page_title) and re.sub(self.trailR, '', link_text[len(new_page_title):]) == '' and not section:
+ newlink = "[[%s]]%s" % (link_text[:len(new_page_title)], link_text[len(new_page_title):])
+ else:
+ newlink = "[[%s%s|%s]]" % (new_page_title, section, link_text)
+ text = text[:m.start()] + newlink + text[m.end():]
+ continue
+
+ wikipedia.output(text[max(0,m.start()-30):m.end()+30])
+ if text == original_text:
+ wikipedia.output(u'\nNo changes have been made:\n')
+ else:
+ wikipedia.output(u'\nThe following changes have been made:\n')
+ wikipedia.showDiff(original_text, text)
+ wikipedia.output(u'')
+ # save the page
+ self.setSummaryMessage(disambPage, new_targets, unlink)
+ try:
+ refPage.put_async(text,comment=self.comment)
+ except wikipedia.LockedPage:
+ wikipedia.output(u'Page not saved: page is locked')
+ except wikipedia.PageNotSaved, error:
+ wikipedia.output(u'Page not saved: %s' % error.args)
+ return True
+
+ def findAlternatives(self, disambPage):
+ if disambPage.isRedirectPage() and not self.primary:
+ if self.primary_redir_template.has_key(disambPage.site().lang) and self.primary_redir_template[disambPage.site().lang] in disambPage.templates(get_redirect = True):
+ baseTerm = disambPage.title()
+ for template in disambPage.templatesWithParams(get_redirect = True):
+ if template[0] == self.primary_redir_template[disambPage.site().lang] and len(template[1]) > 0:
+ baseTerm = template[1][1]
+ disambTitle = primary_topic_format[self.mylang] % baseTerm
+ try:
+ disambPage2 = wikipedia.Page(self.mysite, disambTitle)
+ links = disambPage2.linkedPages()
+ links = [correctcap(l,disambPage2.get()) for l in links]
+ except wikipedia.NoPage:
+ wikipedia.output(u"No page at %s, using redirect target." % disambTitle)
+ links = disambPage.linkedPages()[:1]
+ links = [correctcap(l,disambPage.get(get_redirect = True)) for l in links]
+ self.alternatives += links
+ else:
+ try:
+ target = disambPage.getRedirectTarget().title()
+ self.alternatives.append(target)
+ except wikipedia.NoPage:
+ wikipedia.output(u"The specified page was not found.")
+ user_input = wikipedia.input(u"""\
+Please enter the name of the page where the redirect should have pointed at,
+or press enter to quit:""")
+ if user_input == "":
+ sys.exit(1)
+ else:
+ self.alternatives.append(user_input)
+ except wikipedia.IsNotRedirectPage:
+ wikipedia.output(
+ u"The specified page is not a redirect. Skipping.")
+ return False
+ elif self.getAlternatives:
+ try:
+ if self.primary:
+ try:
+ disambPage2 = wikipedia.Page(self.mysite,
+ primary_topic_format[self.mylang]
+ % disambPage.title()
+ )
+ links = disambPage2.linkedPages()
+ links = [correctcap(l,disambPage2.get()) for l in links]
+ except wikipedia.NoPage:
+ wikipedia.output(u"Page does not exist, using the first link in page %s." % disambPage.title())
+ links = disambPage.linkedPages()[:1]
+ links = [correctcap(l,disambPage.get()) for l in links]
+ else:
+ try:
+ links = disambPage.linkedPages()
+ links = [correctcap(l,disambPage.get()) for l in links]
+ except wikipedia.NoPage:
+ wikipedia.output(u"Page does not exist, skipping.")
+ return False
+ except wikipedia.IsRedirectPage:
+ wikipedia.output(u"Page is a redirect, skipping.")
+ return False
+ self.alternatives += links
+ return True
+
+ def setSummaryMessage(self, disambPage, new_targets = [], unlink = False):
+ # make list of new targets
+ targets = ''
+ for page_title in new_targets:
+ targets += u'[[%s]], ' % page_title
+ # remove last comma
+ targets = targets[:-2]
+
+ if not targets:
+ targets = wikipedia.translate(self.mysite, unknown_msg)
+
+ # first check whether user has customized the edit comment
+ if wikipedia.config.disambiguation_comment.has_key(self.mysite.family.name) and wikipedia.config.disambiguation_comment[self.mysite.family.name].has_key(self.mylang):
+ try:
+ self.comment = wikipedia.translate(self.mysite,
+ wikipedia.config.disambiguation_comment[
+ self.mysite.family.name]
+ ) % (disambPage.title(), targets)
+ #Backwards compatibility, type error probably caused by too many arguments for format string
+ except TypeError:
+ self.comment = wikipedia.translate(self.mysite,
+ wikipedia.config.disambiguation_comment[
+ self.mysite.family.name]
+ ) % disambPage.title()
+ elif disambPage.isRedirectPage():
+ # when working on redirects, there's another summary message
+ if unlink and not new_targets:
+ self.comment = wikipedia.translate(self.mysite, msg_redir_unlink) % disambPage.title()
+ else:
+ self.comment = wikipedia.translate(self.mysite, msg_redir) % (disambPage.title(), targets)
+ else:
+ if unlink and not new_targets:
+ self.comment = wikipedia.translate(self.mysite, msg_unlink) % disambPage.title()
+ else:
+ self.comment = wikipedia.translate(self.mysite, msg) % (disambPage.title(), targets)
+
+ def run(self):
+ if self.main_only:
+ if not ignore_title.has_key(self.mysite.family.name):
+ ignore_title[self.mysite.family.name] = {}
+ if not ignore_title[self.mysite.family.name].has_key(self.mylang):
+ ignore_title[self.mysite.family.name][self.mylang] = []
+ ignore_title[self.mysite.family.name][self.mylang] += [
+ u'%s:' % namespace for namespace in self.mysite.namespaces()]
+
+ for disambPage in self.generator:
+ self.primaryIgnoreManager = PrimaryIgnoreManager(disambPage, enabled=self.primary)
+
+ if not self.findAlternatives(disambPage):
+ continue
+
+ self.makeAlternativesUnique()
+ # sort possible choices
+ if wikipedia.config.sort_ignore_case:
+ self.alternatives.sort(lambda x,y: cmp(x.lower(), y.lower()))
+ else:
+ self.alternatives.sort()
+ self.listAlternatives()
+
+ gen = ReferringPageGeneratorWithIgnore(disambPage, self.primary, minimum = self.minimum)
+ preloadingGen = pagegenerators.PreloadingGenerator(gen)
+ for refPage in preloadingGen:
+ if not self.primaryIgnoreManager.isIgnored(refPage):
+ # run until the user selected 'quit'
+ if not self.treat(refPage, disambPage):
+ break
+
+ # clear alternatives before working on next disambiguation page
+ self.alternatives = []
+
+def main():
+ # the option that's always selected when the bot wonders what to do with
+ # a link. If it's None, the user is prompted (default behaviour).
+ always = None
+ alternatives = []
+ getAlternatives = True
+ # if the -file argument is used, page titles are dumped in this array.
+ # otherwise it will only contain one page.
+ generator = None
+ # This temporary array is used to read the page title if one single
+ # page to work on is specified by the arguments.
+ pageTitle = []
+ primary = False
+ main_only = False
+
+ # For sorting the linked pages, case can be ignored
+ ignoreCase = False
+ minimum = 0
+
+ for arg in wikipedia.handleArgs():
+ if arg.startswith('-primary:'):
+ primary = True
+ getAlternatives = False
+ alternatives.append(arg[9:])
+ elif arg == '-primary':
+ primary = True
+ elif arg.startswith('-always:'):
+ always = arg[8:]
+ elif arg.startswith('-file'):
+ if len(arg) == 5:
+ generator = pagegenerators.TextfilePageGenerator(filename = None)
+ else:
+ generator = pagegenerators.TextfilePageGenerator(filename = arg[6:])
+ elif arg.startswith('-pos:'):
+ if arg[5]!=':':
+ mysite = wikipedia.getSite()
+ page = wikipedia.Page(mysite, arg[5:])
+ if page.exists():
+ alternatives.append(page.title())
+ else:
+ answer = wikipedia.inputChoice(u'Possibility %s does not actually exist. Use it anyway?'
+ % page.title(), ['yes', 'no'], ['y', 'N'], 'N')
+ if answer == 'y':
+ alternatives.append(page.title())
+ else:
+ alternatives.append(arg[5:])
+ elif arg == '-just':
+ getAlternatives = False
+ elif arg == '-main':
+ main_only = True
+ elif arg.startswith('-min:'):
+ minimum = int(arg[5:])
+ elif arg.startswith('-start'):
+ try:
+ if len(arg) <= len('-start:'):
+ generator = pagegenerators.CategorizedPageGenerator(wikipedia.getSite().disambcategory())
+ else:
+ generator = pagegenerators.CategorizedPageGenerator(wikipedia.getSite().disambcategory(), start = arg[7:])
+ generator = pagegenerators.NamespaceFilterPageGenerator(generator, [0])
+ except wikipedia.NoPage:
+ print "Disambiguation category for your wiki is not known."
+ raise
+ elif arg.startswith("-"):
+ print "Unrecognized command line argument: %s" % arg
+ # show help text and exit
+ wikipedia.showHelp()
+ else:
+ pageTitle.append(arg)
+
+ # if the disambiguation page is given as a command line argument,
+ # connect the title's parts with spaces
+ if pageTitle != []:
+ pageTitle = ' '.join(pageTitle)
+ page = wikipedia.Page(wikipedia.getSite(), pageTitle)
+ generator = iter([page])
+
+ # if no disambiguation pages was given as an argument, and none was
+ # read from a file, query the user
+ if not generator:
+ pageTitle = wikipedia.input(u'On which disambiguation page do you want to work?')
+ page = wikipedia.Page(wikipedia.getSite(), pageTitle)
+ generator = iter([page])
+
+ bot = DisambiguationRobot(always, alternatives, getAlternatives, generator, primary, main_only, minimum = minimum)
+ bot.run()
+
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ finally:
+ wikipedia.stopme()