User:Veledan/FP Promoter/Source

Main module
"""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 wikipedia.put_throttle.setDelay(5)
 * 1) This is a purely interactive script with limited edits. Set the delay low.

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
 * 1) relevant pages on en wikipedia

class ModificationFailed(wikipedia.Error): """Modification failed""" class Error(Exception): """Promobot error""" class DropOutError(Error): """Fatal error. Abandon script"""
 * 1) local exceptions

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        ==|             #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.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 .*?)== Images ' \ 'found by Wikipedians ==(?P .*?)' 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' \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        ==|             #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:thumb|none|" + newFp.fpvSize \                + "|" + newFp.pipedName + r" at [[" + newFp.mainArticle \                 + " by " + creator + "]]\n" else: newEntry = "\n:frame|none|" \                    + newFp.pipedName + r" 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 .*?)== Images ' \ 'found by Wikipedians ==(?P .*?)' 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'\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.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.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)
 * 1) classes to represent the main FP pages.
 * 2) Catch wikipedia exceptions from these in the calling code

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='\n\n' editsum = 'Adding %s' % nom.title newtext = oldtext + '\n\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 = ' %s ~' % (image.title, promotionMessage) editsum = 'Promoted' else: verdict = ' %s ~' % promotionMessage editsum = 'Not promoted' #insert verdict in place of reserve note oldtext=self.get(force=True) reservemarker=r'FP Promotion bot &mdash; closure.*' 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 &mdash; 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 [^\|\]]+)' 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'|\{\{-\}\}|\{\{breakafterimages\}\}|$', oldtext) #insert reserve marker reservetext = '\n' + reservetext + '. ~ \n\n' newtext = oldtext[:endmarker.start] + reservetext + oldtext[endmarker.start:] #save page self.put(newtext, es) 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 '' in oldtext: newtext = oldtext.replace(,) else: newtext = '\n' + oldtext editsum = '' 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
 * 1) class for the image due for promotion

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 tags output('Adding ...') try: self.newFp.addFpTemplate except wikipedia.Error, error: self._report('Failed to add : ' + str(error)) else: self._report('Added OK') self._report('Removing 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 '' in oldtext: newtext=oldtext.replace(,) try: image.put(newtext,'Removing ') except wikipedia.Error: self._report('Failed to remove from ' + image.title) continue self._report(' 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%s %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%s %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 from versions self._report('Removing 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 '' in oldtext: newtext=oldtext.replace(,) try: image.put(newtext,'Removing ') except wikipedia.Error: self._report('Failed to remove from ' + image.title) continue self._report(' 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
 * 1) main control class

def fpInput(message): output('\n' + message) answer=raw_input('--> ') answer = unicode(answer.strip,'utf-8') output(answer,logonly=True) output('\n\n') return answer
 * 1) functions for use by all classes

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

v_MyPage.py
"""Class wikipedia.Page is overridden because the regular expression in the original fails to match image links that have external links embedded in their captions, and mismatches image links with wikilinks in their captions """ import wikipedia, re

class MyPage(wikipedia.Page): #def __init__(self, site, title): #   """temp, for testing fp promotor macro""" #   if 'Wikipedia:Sandbox/Veledan/' not in title: #       title='Wikipedia:Sandbox/Veledan/' + title #   wikipedia.Page.__init__(self, site, title) def linkedPages(self): """Gives the normal (not-interwiki, non-category) pages the page          links to, as a list of Page objects           Amended regex to spot links to images with an external link in            their caption, hence overwritten        """ result = [] try: thistxt = wikipedia.removeLanguageLinks(self.get) except NoPage: return [] except IsRedirectPage: raise thistxt = wikipedia.removeCategoryLinks(thistxt, self.site) Rlink = re.compile( \           r'''\[\[		#opening wikilink brackets            (?P [^\]\|]*)	#title=everything up to first ] or |            (			#then an optional, repeatable group [^\]\[]		#containing at least one non-bracket char OR           | \^\[\*\]	#an embedded external link with both brackets [...] OR           | \[\^\[\*\]\]	#an embedded complete wikilink ... )*			#all that zero or more times           \]\]		#until ]] is reached. This will not be matched by above expression            ''', re.VERBOSE) for match in Rlink.finditer(thistxt): title = match.group('title') if not self.site.isInterwikiLink(title): page = MyPage(self.site, title) result.append(page) return result