"""Script to help a human promote new Featured Pictures.
by Stephen Lawson (en:User:Veledan)
released under the GPL.
version 1.3beta 13-May-06
"""
import re, time
import wikipedia
from v_MyPage import MyPage as Page
try:
set
except NameError:
from future import set
mySite = wikipedia.Site('en')
wikipedia.setAction('FP promotion script') #should always be overridden
# This is a purely interactive script with limited edits. Set the delay low.
wikipedia.put_throttle.setDelay(5)
#relevant pages on en wikipedia
fpPages = { 'fp' : 'Wikipedia:Featured pictures',
'fpt' : 'Wikipedia:Featured pictures thumbs',
'fpv' : 'Wikipedia:Featured pictures visible',
'fpc' : 'Wikipedia:Featured picture candidates',
'nfp' : 'Template:Announcements/New featured pages',
'go' : 'Wikipedia:Goings-on',
'arch': 'Wikipedia:Featured pictures candidates/'} #archives
#local exceptions
class ModificationFailed(wikipedia.Error):
"""Modification failed"""
class Error(Exception):
"""Promobot error"""
class DropOutError(Error):
"""Fatal error. Abandon script"""
#classes to represent the main FP pages.
#Catch wikipedia exceptions from these in the calling code
class FpPage(Page):
"""Wikipedia:Featured pictures"""
def __init__(self):
Page.__init__(self, mySite, fpPages['fp'])
def addNewFp(self, newFp):
"""Add a new FP to WP:FP. Takes a NewFP object"""
oldtext=self.get()
headerRegex = newFp.fpSection.replace(' ','[ ]')
sectionsearch = re.compile(r"""
={3,5}[ ]?""" + headerRegex + """[ ]?={3,5} #match the header
.*? #plus the section contents
(?= #and look ahead but don't match
==|<!--[ ]end[ ]of[ ]list[ ]--> #the next header or end of list
)""", re.DOTALL|re.VERBOSE)
#Section headings are frequently duplicated. Work out which one to use
sections = []
for s in sectionsearch.finditer(oldtext):
sections.append(s)
if len(sections)==0:
raise ModificationFailed('Unable to match section ' + newFp.fpSection)
if len(sections)==2 and not newFp.createdByWikipedian:
section = sections[1]
else:
section = sections[0]
#make the new entry
creator = newFp.creator
if newFp.createdByWikipedian:
creator="[[" + creator + "|]]"
newEntry = "\n* '''[[:" + newFp.title() + "|" + newFp.pipedName + \
"]]''' at [[" + newFp.mainArticle + "]], by " + creator + "\n"
#put it together
sectiontext = section.group().rstrip()
sectiontext += newEntry
newtext = oldtext[:section.start()] + sectiontext + oldtext[section.end():]
#update the FP counter
numberofFps=len(self.imagelinks()) + 1
countersearch=r"are currently '''\d\d\d\d?''' featured pictures"
countertext=r"are currently '''" + str(numberofFps) + r"''' featured pictures"
counter=re.search(countersearch, newtext)
if counter:
newtext=re.sub(countersearch,countertext,newtext,1)
editsum = 'Adding ' + newFp.title()
self.put(newtext,editsum,minorEdit=False)
if not counter:
raise ModificationFailed('Image added to WP:FP ok but failed to match FP counter.')
def headerlinks(self, createdByWikipedian):
"""Return a list of headers from WP:FP.
Avoid ambiguity from the duplicate section names by specifying up
front whether 'created by' or 'found by' wikipedian is appropriate.
"""
oldtext=self.get()
s = r'== Images created by Wikipedians ==(?P<createdby>.*?)== Images ' \
'found by Wikipedians ==(?P<foundby>.*?)<!-- end of list -->'
sections=re.search(s, oldtext, re.DOTALL)
if not sections:
raise DropOutError('ERROR: Unable to read headings from WP:FP')
if createdByWikipedian:
return self._listheaders(sections.group('createdby'))
else:
return self._listheaders(sections.group('foundby'))
def _listheaders(self, text):
"""Return list of ===, ====, ===== level headers. Show level by -(s) in front"""
headersearch=r'===.+===|====.+====|=====.+====='
headers=[]
for header in re.finditer(headersearch,text):
fullheader=header.group()
if '=====' in fullheader:
headers.append('--' + fullheader.strip(' ='))
elif '====' in fullheader:
headers.append('-' + fullheader.strip(' ='))
elif '===' in fullheader:
headers.append(fullheader.strip(' ='))
else:
raise Error('debug: header regex not working')
return headers
class FptPage(Page):
"""Wikipedia:Featured pictures thumbs"""
def __init__(self):
fptRedirPage = Page(mySite, fpPages['fpt'])
Page.__init__(self, mySite, fptRedirPage.getRedirectTarget())
fptRedirPage = None
def addNewFp(self, newFp):
"""Add a new FP to WP:FPT. Takes a NewFP object"""
oldtext=self.get(force=True)
gallerystart = re.search(r'<gallery>\s*(?=Image:)',oldtext)
if not gallerystart:
raise ModificationFailed('Unable to locate start of gallery')
newentry = newFp.title() + '|' + newFp.pipedName + '\n'
newtext=oldtext[:gallerystart.end()] + newentry + oldtext[gallerystart.end():]
editsum = "Adding " + newFp.title()
self.put(newtext,editsum,minorEdit=False)
class FpvPage(Page):
"""Wikipedia:Featured pictures visible"""
def __init__(self):
Page.__init__(self, mySite, fpPages['fpv'])
def addNewFp(self, newFp):
"""Add a new FP to WP:FPV. Takes a NewFP object"""
oldtext=self.get()
headerRegex = newFp.fpSection.replace(' ','[ ]')
sectionsearch = re.compile(r"""
={3,5}[ ]?""" + headerRegex + """[ ]?={3,5} #match the header
.*? #plus the section contents
(?= #and look ahead but don't match
==|<!--[ ]end[ ]of[ ]list[ ]--> #the next header or end of list
)""", re.DOTALL|re.VERBOSE)
#Section headings are frequently duplicated. Work out which one to use
sections = []
for s in sectionsearch.finditer(oldtext):
sections.append(s)
if len(sections)==0:
raise ModificationFailed('Unable to match section ' + newFp.fpSection)
if len(sections)==2 and not newFp.createdByWikipedian:
section = sections[1]
else:
section = sections[0]
#make the new entry
creator = newFp.creator
if newFp.createdByWikipedian:
creator="[[" + creator + "|]]"
if newFp.fpvSize:
newEntry = "\n:[[" + newFp.title() + "|thumb|none|" + newFp.fpvSize \
+ "|" + newFp.pipedName + r"<br />at [[" + newFp.mainArticle \
+ "]] by " + creator + "]]\n"
else:
newEntry = "\n:[[" + newFp.title() + "|frame|none|" \
+ newFp.pipedName + r"<br />at [[" + newFp.mainArticle \
+ "]] by " + creator + "]]\n"
#finally put it together and save
sectiontext = section.group().rstrip()
sectiontext += newEntry
newtext = oldtext[:section.start()] + sectiontext + oldtext[section.end():]
editsum = 'Adding ' + newFp.title()
self.put(newtext,editsum,minorEdit=False)
def headerlinks(self, createdByWikipedian):
"""Return a list of headers from WP:FP.
Avoid ambiguity from the duplicate section names by specifying up
front whether 'created by' or 'found by' wikipedian is appropriate.
"""
oldtext=self.get()
s = r'== Images created by Wikipedians ==(?P<createdby>.*?)== Images ' \
'found by Wikipedians ==(?P<foundby>.*?)<!-- end of list -->'
sections=re.search(s, oldtext, re.DOTALL)
if not sections:
raise DropOutError('ERROR: Unable to read headings from WP:FP')
if createdByWikipedian:
return self._listheaders(sections.group('createdby'))
else:
return self._listheaders(sections.group('foundby'))
def _listheaders(self, text):
"""Return list of ===, ====, ===== level headers. Show level by -(s) in front"""
headersearch=r'===[^=\n]+===|====[^=\n]+====|=====[^=\n]+====='
headers=[]
for header in re.finditer(headersearch,text):
fullheader=header.group()
if '=====' in fullheader:
headers.append('--' + fullheader.strip(' ='))
elif '====' in fullheader:
headers.append('-' + fullheader.strip(' ='))
elif '===' in fullheader:
headers.append(fullheader.strip(' ='))
else:
raise Error('debug: header regex not working')
return headers
class FpcPage(Page):
"""Wikipedia:Featured picture candidates"""
def __init__(self):
Page.__init__(self, mySite, fpPages['fpc'])
def removeNomination(self, nom):
"""Remove one nomination from WP:FPC. Takes nomination Page as arg."""
oldtext = self.get()
editsum = 'Closing %s' % nom.title()
#build regex to match nomination subst page
searchexp = nom.title()
searchexp=searchexp.replace(' ','[ _]')
searchexp=r'{{ ?' + searchexp + r' ?}}\n|\[\[ ?' + searchexp + r' ?\]\]\n'
if not re.search(searchexp, oldtext):
raise Error('%s isn\'t listed at WP:FPC' % nom.title())
newtext=re.sub(searchexp, '', oldtext)
self.put(newtext,editsum,minorEdit=False)
class AnnouncePage(Page):
"""Template:Announcements/New featured pages"""
def __init__(self):
Page.__init__(self, mySite, fpPages['nfp'])
def addNewFp(self, newFp):
"""Add a new FP to WP:FPV. Takes a NewFP object"""
#TODO: add in auto-limiting of number of new FPs listed at any one time
#find the current list
oldtext = self.get(force=True)
fpSectionPattern = """'''\[\[Wikipedia:Featured pictures\|Pictures\]\] recently awarded "featured" status'''.*(?='''\[\[Wikipedia:Featured portals\|Portals\]\] recently)"""
fpSection = re.search(fpSectionPattern, oldtext, re.DOTALL)
if not fpSection:
raise ModificationFailed('Failed to match FP section')
#make new entry
newentry = '* [[:' + newFp.title() + '|' + newFp.pipedName + ']] '
newentry += '(' + unicode(time.strftime('%B %d',time.gmtime())) + ')'
#insert and save
sectionText = fpSection.group().rstrip()
sectionText += '\n' + newentry + '\n\n'
newtext = oldtext[:fpSection.start()] + sectionText + oldtext[fpSection.end():]
editsum = 'Adding ' + newFp.title()
self.put(newtext, editsum, minorEdit=False)
class GoPage(Page):
"""Wikipedia:Goings-on"""
def __init__(self):
Page.__init__(self, mySite, fpPages['go'])
def addNewFp(self, newFp):
"""Add a new FP to WP:FPV. Takes a NewFP object"""
#TODO: add in auto-limiting of number of new FPs listed at any one time
#find the current list
oldtext = self.get(force=True)
fpSectionPattern = """'''\[\[Wikipedia:Featured pictures\|Pictures\]\] that gained "featured" status'''.*(?='''\[\[Wikipedia:Featured portals\|Portals\]\] that)"""
fpSection = re.search(fpSectionPattern, oldtext, re.DOTALL)
if not fpSection:
raise ModificationFailed('Failed to match FP section')
#make new entry
newentry = '* [[:' + newFp.title() + '|' + newFp.pipedName + ']] '
newentry += '(' + time.strftime('%B %d',time.gmtime()) + ')'
#insert and save
sectionText = fpSection.group().rstrip()
sectionText += '\n' + newentry + '\n\n'
newtext = oldtext[:fpSection.start()] + sectionText + oldtext[fpSection.end():]
editsum = 'Adding ' + newFp.title()
self.put(newtext, editsum, minorEdit=False)
class ArchivePage(Page):
"""Wikipedia:FPC Archives"""
def __init__(self):
currentMonth = time.strftime('%B-%Y',time.gmtime())
currentArchive = fpPages['arch'] + currentMonth
Page.__init__(self, mySite, currentArchive)
def addNomination(self, nom):
"""Insert one nomination from WP:FPC. Takes nomination Page as arg.
Create new archive if necessary."""
try:
oldtext = self.get(force=True)
except NoPage:
oldtext='{{FPCArchiveBar}}\n\n'
editsum = 'Adding %s' % nom.title()
newtext = oldtext + '\n{{%s}}\n' % nom.title()
#save page
self.put(newtext,editsum,minorEdit=False)
class NominationPage(Page):
"""WP:FPC/subpage for nomination."""
def verdict(self, isPromoted, image=None, promotionMessage=''):
"""Add verdict to the nomination page"""
#make verdict string & edit summary
if isPromoted:
verdict = '{{FPCresult|Promoted|%s}} %s ~~~~' % (image.title(), promotionMessage)
editsum = 'Promoted'
else:
verdict = '{{FPCresult|Not promoted| }} %s ~~~~' % promotionMessage
editsum = 'Not promoted'
#insert verdict in place of reserve note
oldtext=self.get(force=True)
reservemarker=r'FP Promotion bot — closure.*<!-- end note -->'
marker = re.search(reservemarker, oldtext)
if not marker:
raise DropOutError('ERROR: Reservation marker has gone missing. Please complete manually')
newtext=oldtext[:marker.start()] + verdict + oldtext[marker.end():]
#save page
self.put(newtext, editsum, minorEdit=False)
def firstName(self):
"""Return the first wikilinked username on page"""
username=re.search(r'\[\[(User:[^\]\|]+)',self.get())
if username:
return username.group(1)
def reserve(self):
"""Add a line to the nomination page effectively 'locking' it"""
reservetext = r'FP Promotion bot — closure in progress. Please do ' \
'not amend this page while this note is showing'
es = 'Adding reserve marker prior to closure'
#First check not already reserved by someone else using the bot
oldtext = self.get() #we've already checked this exists
reservemarker=reservetext + r'[^\[]*\[\[(?P<sig>[^\|\]]+)'
alreadyreserved = re.search(reservemarker, oldtext)
if alreadyreserved:
raise DropOutError('The page has already been reserved by ' + alreadyreserved.group('sig'))
#find where to insert our line. Any of three end markers are matched, or if not, end of str$
endmarker=re.search(r'<!-- additional votes go above this line -->|\{\{-\}\}|\{\{breakafterimages\}\}|$', oldtext)
#insert reserve marker
reservetext = '\n' + reservetext + '. ~~~~ <!-- end note -->\n\n'
newtext = oldtext[:endmarker.start()] + reservetext + oldtext[endmarker.start():]
#save page
self.put(newtext, es)
#class for the image due for promotion
class NewFP(wikipedia.ImagePage):
"""An image to be promoted.
Call gatherInfo() after construction.
"""
def __init__(self, site, imageTitle, otherVersions, nominationPage):
wikipedia.ImagePage.__init__(self, site, imageTitle)
self.otherVersions = otherVersions # list of pages
self.nominationPage = nominationPage
self.nominator = None
self.creator = None
self.createdByWikipedian = None #boolean
self.fpSection = None #string
self.fpvSection = None #string
self.isPanorama = None
self.isAnimated = None
self.fpvSize = None #string, '300px'
self.pipedName = None
self.mainArticle = None #str title
self.promotionMessage = None #string
self.talkPageMessage = None #string
self.mainVersionToReplace = None #Page
def gatherInfo(self):
#gather all required info
self.pipedName = self._getPipedName()
self.mainArticle = self._getArticle()
self.mainVersionToReplace = self._getMainVersion()
self.nominator=self._getNominator()
self.creator, self.createdByWikipedian = self._getCreator()
self.isPanorama, self.isAnimated, self.fpvSize = self._getSize()
self.fpSection = self._getFpSection()
self.fpvSection = self._getFpvSection()
self.promotionMessage=self._getPromotionMessage()
self.talkPageMessage=self._getTalkPageMessage()
def supplantOriginalVersion(self):
#Replace original image with self (promoted version) in articles
editsum='Updating image after promotion of new version at %s' % self.nominationPage.title()
for article in self.mainVersionToReplace.usingPages():
if article.namespace()==0:
article.put(article.get().replace(self.mainVersionToReplace.title(),self.title()),editsum)
def addFpTemplate(self):
oldtext=''
if self.exists():
oldtext=self.get()
if '{{FPC}}' in oldtext:
newtext = oldtext.replace('{{FPC}}','{{FeaturedPicture}}')
else:
newtext = '{{FeaturedPicture}}\n' + oldtext
editsum = '{{FeaturedPicture}}'
self.put(newtext,editsum)
def _getPromotionMessage(self):
return fpInput('Would you like to add any extra message to the Promotion ' \
'template on the nomination page? Leave this blank if not.' \
'\n\nIf you enter a message it will appear between the verdict ' \
'and your signature on the nomination page')
def _getTalkPageMessage(self):
return fpInput('Would you like to add any extra message to the Promotion ' \
'template on the nominator\'s and/or creator\'s talk page?' \
'Leave this blank if not.\n\nIf you enter a message it ' \
'will appear between the promotion template and your ' \
'signature on the nomination page')
def _getArticle(self):
while True:
choice = fpInput('What is the title of this image\'s main article?')
choice = unicode(choice)
page = Page(mySite, choice)
if page.exists():
return page.title()
output('That page doesn\'t exist. Try again')
def _getMainVersion(self):
replace = letteredquestion('Is the promoted image an edit that should replace '
'the original version in mainspace articles?')
if replace=='[n]o':
return None
oldversion=numberedquestion('Which was the original version?\n\nThe image you '
'select will be replaced in mainspace articles '
'with ' + self.title(), [i.title() for i in self.otherVersions])
return wikipedia.ImagePage(mySite,oldversion)
def _getPipedName(self):
return fpInput('What wording should be used in piping text links to the '
'image? (e.g. Image:blue_bird02_edit.jpg might be "Blue bird")')
def _getSize(self):
choice=letteredquestion('Is this a wide panorama? (This affects the size '
'it will be shown at in Wikipedia:Featured '
'pictures visible)')
isPanorama = (choice=='[y]es')
choice=letteredquestion('Is this an animated gif? (These are not resized)')
isAnimated = (choice=='[y]es')
if isPanorama:
size='600px'
elif isAnimated:
size=''
else:
size='300px'
return isPanorama, isAnimated, size
def _getFpSection(self):
question = 'What section should this be listed under at Wikipedia:Featured pictures?'
return numberedquestion(question, fp.headerlinks(self.createdByWikipedian)).strip(' -')
def _getFpvSection(self):
question = 'What section should this be listed under at Wikipedia:Featured pictures visible?'
if self.isAnimated or self.isPanorama:
question += '\n\nYour previous answers indicate this is a panorama ' \
'or animation. Please choose the matching section as the ' \
'image will be sized differently from normal.'
return numberedquestion(question,fpv.headerlinks(self.createdByWikipedian)).strip(' -')
def _getNominator(self):
probableNominator = self.nominationPage.firstName()
while True:
question='Which wikipedian nominated the image? (enter username, ' \
'not nickname)'
if probableNominator:
question+='\n\nYou can leave this blank if it was ' + probableNominator
choice = fpInput(question)
if choice.strip()=='':
choice=probableNominator
if ':' not in choice:
choice='User:' + choice
userpage = Page(mySite, choice)
try:
userpage.get()
except wikipedia.Error:
choice=letteredquestion('Page %s doesn\'t exist. Please confirm ' \
'the username was correct (in which case the bot will ' \
'create the talk page if necessary when leaving a message) ' \
'or choose No to re-enter the name:' % userpage.title())
else:
choice='[y]es'
if choice=='[y]es':
break
return userpage.title()
def _getCreator(self):
choice = letteredquestion('Was %s also the creator of the image?' % self.nominator)
if choice=='[y]es':
return self.nominator, True
choice = letteredquestion('Is the creator of the image a wikipedian?')
createdByWikipedian = (choice=='[y]es')
if createdByWikipedian:
question="Please enter the username of the wikipedian who created" \
"the image (with or without the \'User:\')"
else:
question="Please give a name or other suitable attribution for " \
"the creator of the image (e.g. 'John Smith' or 'NASA')"
while True:
choice = fpInput(question)
if not createdByWikipedian:
creator = choice
break #no validation in that case
if ':' not in choice:
choice='User:' + choice
userpage = Page(mySite, choice)
try:
userpage.get()
except wikipedia.Error:
choice=letteredquestion('Page %s doesn\'t exist. Please confirm ' \
'the username was correct (in which case the bot will ' \
'create the talk page if necessary when leaving a message) ' \
'or choose No to re-enter the name:' % userpage.title())
else:
choice='[y]es'
if choice=='[y]es':
creator = userpage.title()
break
return creator, createdByWikipedian
#main control class
class FpPromoter(object):
def __init__(self):
#initialize page objects if necessary
try:
fp
except NameError:
createGlobalPageObjects()
#instance variables
self.nomPage=None #Page
self.newFp=None #Page
self.promoted=None #boolean
self.report=['== FP Promotion Tool started =='] #List of outcomes
def run(self):
"""Main control procedure."""
output('== FP Promotion Tool started ==')
#Ask which nomination to close
self.nomPage = self._getNominatedPage()
self._report('%s selected' % self.nomPage.title())
#Reserve the page to stop edit conflicts
output('Attempting to reserve nomination page...')
try:
self.nomPage.reserve()
except wikipedia.Error, error:
raise DropOutError(error)
self._report('Reserve marker added to page')
#Ask whether the image is promoted or not
self.promoted = self._getResult()
#promote or not promote
if self.promoted:
self.promote()
else:
self.dontpromote()
def promote(self):
"""Perform the steps to close a nomination and promote an image"""
#find out which image is to be promoted
images=self.nomPage.imagelinks()
imagelist = [i.title() for i in images]
promotedimage = numberedquestion('Which image to promote?',imagelist)
#make list of imagepages not being promoted
otherimages = set([i for i in images if i.title() != promotedimage])
#create newFp object
self.newFp = NewFP(mySite, promotedimage, otherimages, self.nomPage)
#new FP will ask the rest of the questions
self.newFp.gatherInfo()
#PROMOTION STEPS
#1. Add verdict
output('Adding verdict to nomination page...')
try:
self.nomPage.verdict(True, self.newFp, self.newFp.promotionMessage)
except wikipedia.Error, error:
raise DropOutError('ERROR: Failed to add verdict: ' + str(error))
else:
self._report('Promoted. Verdict added to nomination page')
#2. Add to archive
output('Adding entry to %s archive...' % time.strftime('%B',time.gmtime()))
try:
arc.addNomination(self.nomPage)
except wikipedia.Error, error:
self._report('Failed to add entry to archive: ' + str(error))
else:
self._report('Added entry to archive OK')
output('Removing entry from WP:FPC...')
try:
fpc.removeNomination(self.nomPage)
except wikipedia.Error, error:
self._report('Failed to remove entry from WP:FPC: ' + str(error))
except Error, error:
self._report(str(error))
else:
self._report('Removed from WP:FPC OK')
#3. Add to New Featured content template
output('Adding to Template:Announcements/New featured pages...')
try:
nfp.addNewFp(self.newFp)
except wikipedia.Error, error:
self._report('Failed to add entry to Template:Announcements/New featured pages: ' + str(error))
else:
self._report('Added to Template:Announcements/New featured pages OK')
#4. Add to Goings-on
output('Adding to Wikipedia:Goings-on...')
try:
goo.addNewFp(self.newFp)
except wikipedia.Error, error:
self._report('Failed to add entry to Wikipedia.Goings-on: ' + str(error))
else:
self._report('Added to Wikipedia:Goings-on OK')
#5. Add to Featured Pictures
output('Adding to WP:FP...')
try:
fp.addNewFp(self.newFp)
except wikipedia.Error, error:
self._report('Failed to add entry to Wikipedia:Featured pictures: ' + str(error))
else:
self._report('Added to Wikipedia:Featured pictures OK')
#6. Add to WP:FPV
output('Adding to WP:FPV...')
try:
fpv.addNewFp(self.newFp)
except wikipedia.Error, error:
self._report('Failed to add entry to Wikipedia:Featured pictures visible: ' + str(error))
else:
self._report('Added to Wikipedia:Featured pictures visible OK')
#7. Add to WP:FPT
output('Adding to WP:FPT...')
try:
fpt.addNewFp(self.newFp)
except wikipedia.Error, error:
self._report('Failed to add entry to Wikipedia:Featured pictures thumbs: ' + str(error))
else:
self._report('Added to Wikipedia:Featured pictures thumbs OK')
#8. Update {{FPC}} tags
output('Adding {{FeaturedPicture}}...')
try:
self.newFp.addFpTemplate()
except wikipedia.Error, error:
self._report('Failed to add {{FeaturedPicture}}: ' + str(error))
else:
self._report('Added {{FeaturedPicture}} OK')
self._report('Removing {{FPC}} from all other images in Nom page...')
for image in self.newFp.otherVersions:
newtext=''
if not image.exists(): #no need to remove from empty page
continue
oldtext=image.get()
if '{{FPC}}' in oldtext:
newtext=oldtext.replace('{{FPC}}','')
try:
image.put(newtext,'Removing {{FPC}}')
except wikipedia.Error:
self._report('Failed to remove {{FPC}} from ' + image.title())
continue
self._report('{{FPC}} removed from ' + image.title())
#9. Notify nominator
output('Notifying nominator...')
editsum='New featured pic'
try:
nominatorsTalkPage=Page(mySite,self.newFp.nominator).switchTalkPage()
newmessage='\n==Featured picture promotion==\n{{subst:PromotedFPC|%s}}<br />%s ~~~~' % (self.newFp.title(), self.newFp.talkPageMessage)
nominatorsTalkPage.put(nominatorsTalkPage.get()+newmessage,editsum,minorEdit=False)
except wikipedia.Error, error:
self._report('Failed to leave message for nominator: ' + str(error))
else:
self._report('Notified nominator OK')
#10. Notify creator
if self.newFp.nominator != self.newFp.creator and self.newFp.createdByWikipedian:
output('Notifying creator...')
editsum='New featured pic'
try:
creatorsTalkPage=Page(mySite,self.newFp.creator).switchTalkPage()
newmessage='\n==Featured picture promotion==\n{{subst:UploadedFP|%s}}<br />%s ~~~~' % (self.newFp.title(), self.newFp.talkPageMessage)
creatorsTalkPage.put(creatorsTalkPage.get()+newmessage,editsum,minorEdit=False)
except wikipedia.Error, error:
self._report('Failed to leave message for nominator: ' + str(error))
else:
self._report('Creator nominator OK')
self._report('Promotion completed')
#11. Replace original image with edited version if necessary
if self.newFp.mainVersionToReplace:
output('Replacing image in articles...')
self.newFp.supplantOriginalVersion()
def dontpromote(self):
"""Perform the steps to close a nom with no promotion"""
#add verdict
message = fpInput('Would you like to add any extra message to the non-Promotion ' \
'template on the nomination page? Leave this blank if not.' \
'\n\nIf you enter a message it will appear between the verdict ' \
'and your signature on the nomination page')
output('Adding verdict to nomination page...')
try:
self.nomPage.verdict(False, None, message)
except wikipedia.Error, error:
raise DropOutError('ERROR: Failed to add verdict:' + str(error))
else:
self._report('Not promoted. Verdict added to nomination page')
#remove {{FPC}} from versions
self._report('Removing {{FPC}} from all images in Nom page...')
images = set(self.nomPage.imagelinks())
for image in images:
newtext=''
if not image.exists(): #no need to remove from empty page
continue
oldtext=image.get()
if '{{FPC}}' in oldtext:
newtext=oldtext.replace('{{FPC}}','')
try:
image.put(newtext,'Removing {{FPC}}')
except wikipedia.Error:
self._report('Failed to remove {{FPC}} from ' + image.title())
continue
self._report('{{FPC}} removed from ' + image.title())
#Remove from Wp:FPC
output('Removing entry from WP:FPC...')
try:
fpc.removeNomination(self.nomPage)
except wikipedia.Error, error:
self._report('Failed to remove entry from WP:FPC: ' + str(error))
except Error, error:
self._report(str(error))
else:
self._report('Removed from WP:FPC OK')
#Add to archive
output('Adding entry to %s archive' % time.strftime('%B',time.gmtime()))
try:
arc.addNomination(self.nomPage)
except wikipedia.Error, error:
self._report('Failed to add entry to archive: ' + str(error))
else:
self._report('Added entry to archive OK')
self._report('Non-promotion completed')
def _report(self, message, echo=True):
self.report.append(message)
if echo:
output(message)
def _getResult(self):
return (letteredquestion('Has %s resulted in a promotion?' % self.nomPage.title())=='[y]es')
def _getNominatedPage(self):
while True:
choice = fpInput("What page to work on? Please enter the full title " \
"pasted from the subpage, e.g. 'Wikipedia:Featured picture " \
"candidates/Street Trinidad Cuba'")
nomPage=NominationPage(mySite, choice)
if nomPage.exists():
break
else:
output('Error: %s doesn\'t exist. Please try again...' % choice)
return nomPage
#functions for use by all classes
def fpInput(message):
output('\n' + message)
answer=raw_input('--> ')
answer = unicode(answer.strip(),'utf-8')
output(answer,logonly=True)
output('\n\n')
return answer
def output(message, logonly=False):
if not logonly:
wikipedia.output(unicode('\n' + message))
#also write to log if there is one
try:
promobotlogfile
except NameError: pass
else:
promobotlogfile.write(time.strftime('[%d%b%y %H:%M:%S] ',time.gmtime()) + message + '\n')
def numberedquestion(message, answers):
"""Show a numbered list of 'answers' and return the selected item"""
if message[:-1] != '\n':
message+='\n'
for item in answers:
message+=('%u. ' % (answers.index(item) + 1)) + item + '\n'
while True:
choice = fpInput(message)
try:
choice=int(choice)
except ValueError:
choice=0
if choice > len(answers) or choice < 1:
output('ERROR: You have to choose a number from the menu. Try again.\n')
else:
return answers[choice-1]
def letteredquestion(message, answers=['[y]es','[n]o']):
"""Show a list of options with [s]ignicant letters and return selected item"""
if message[:-1] != '\n':
message+='\n'
answerlist=''
for item in answers:
message+=(' - ') + item + '\n'
keyletter=re.search(r'\[([^\[\]])\]', item).group(1).lower()
if keyletter=='':
raise DropOutError('Bad list of possible answers - no [] in %s' % item)
answerlist+=keyletter
while True:
choice=fpInput(message).lower()[:1]
if not choice or choice not in answerlist:
output('ERROR: You have to choose a letter from the menu. Try again.\n')
else:
return answers[answerlist.index(choice)]
def createGlobalPageObjects():
global fp, fpc, fpv, fpt, nfp, goo, arc
fp = FpPage()
fpv = FpvPage()
fpc = FpcPage()
fpt = FptPage()
nfp = AnnouncePage()
goo = GoPage()
arc = ArchivePage()
def createLogFile():
global promobotlogfile
promobotlogfile = file('promobotlog.txt','a')
def closeLogFile():
promobotlogfile.close()
if __name__=='__main__':
try:
createLogFile()
wikipedia.Site.forceLogin(mySite)
#make global FP page objects
fp = FpPage()
fpv = FpvPage()
fpc = FpcPage()
fpt = FptPage()
nfp = AnnouncePage()
goo = GoPage()
arc = ArchivePage()
#go
try:
promobot = FpPromoter()
promobot.run()
except DropOutError, error:
output('\nThe script was terminated early. Reason::')
output(str(error) + ' -- Please complete manually')
finally:
wikipedia.stopme()
#if the report exists, output it
try:
promobot.report
except:
pass
else:
output('=================================')
output('Progress report::')
output('\n'.join(promobot.report))
closeLogFile()