User:AfdlBot/Source

#!/usr/bin/python
# -*- coding: utf-8  -*-
#
# AfdlBot - Bot to maintain Wikipedia Afdl pages, see http://en.wikipedia.org/wiki/User:AfdlBot
# Written by an Anon E. Mouse Wikipedia editor, http://en.wikipedia.org/wiki/User:AnonEMouse
# June 2007
#
# Intended potential users of this bot:
# Wikipedia:WikiProject Macintosh/Deletion,
# Wikipedia:WikiProject Georgia Tech/Deletion
# Wikipedia:WikiProject Pornography/Deletion
# Wikipedia:WikiProject Video games/Deletion
# Wikipedia:WikiProject Warcraft/Deletion
# Wikipedia:WikiProject Webcomics/Deletion

# TODO: Templates, (and tfdl), mfdl?
# TODO: actually read categories for discussion, rather than just maintaining the already listed ones
# TODO: Deletion review as for categories
# TODO: follow down config-input categories, get all subcategories
# TODO: WikiProject Deletion Sorting and subst:ed Afds
# TODO: mark Afd when listing

import sys,re,wikipedia,codecs,time,config
from datetime import date, timedelta

site = wikipedia.getSite()
logfile = open("afd.log", 'w')
afdlBotPage = 'User:AfdlBot/Configuration'
afdlProjects = []

def followRedirects(title):
    """ Gets the page content, and as long as it's a redirect, follows those redirects. """
    page = wikipedia.Page(site, title);
    try:
        page.get()
        return page
    except wikipedia.NoPage:
        return page
    except wikipedia.SectionError:
        return page
    except wikipedia.IsRedirectPage, arg:
        return followRedirects(arg.args[0])

def parseDate(string):
    """ Accept YYYY-MM-DD or 17:55, June 8, 2007 formats, return a date object"""
    if string == None: return None
    # print 'parsing "' + string + '"'
    try:
        return date(*(time.strptime(string, '%Y-%m-%d')[0:3]))
    except ValueError:
        try:
            return date(*(time.strptime(string, '%H:%M, %B %d, %Y')[0:3]))
        except ValueError:
            try:
                return date(*(time.strptime(string, '%H:%M, %d %B %Y')[0:3]))
            except ValueError:
                return None

# Retular sub-expression to match 4 kinds of wikilinked dates
# [[2007]]-[[03-15]]
# [[2007-03-15]]
# [[July 3]], [[2007]]
# [[3 July]], [[2007]]
# Note {len(May)=3, len(September)=9}
datexpr = r'(\[\[\d{4}\]\]-\[\[\d\d\-\d\d\]\])|' + \
          r'(\[\[\d{4}-\d\d-\d\d\]\])|' + \
          r'(\[\[[A-Za-z]{3,9} \d\d\]\],?\s*\[\[\d{4}\]\])|' + \
          r'(\[\[\d\d [A-Za-z]{3,9}\]\],?\s*\[\[\d{4}\]\])'
def parseWikidate(string):
    """ Parses the above 4 formats into a date object. """
    if string == None: return None
    string = re.sub('[\]\[,]', '', string)
    # print 'newstring=', string
    # print 'parsing "' + string + '"'
    try:
        return date(*(time.strptime(string, '%Y-%m-%d')[0:3]))
    except ValueError:
        try:
            return date(*(time.strptime(string, '%B %d %Y')[0:3]))
        except ValueError:
            try:
                return date(*(time.strptime(string, '%d %B %Y')[0:3]))
            except ValueError:
                return None
    

def dateToPageName(d):
    """Need to produce single digits for days < 10. For example, 2007 June 9."""
    return d.strftime('%Y %B ') + str(d.day)


# The result was '''Speedy delete per G3'''. [[User talk:PeaceNT|'''''Peacent''''']] 02:29, 9 June 2007 (UTC)
Rresult = re.compile(r".*\<!--Template:Afd top.*The result was[^']*'''(?P<result>[^']*)'''.*" +
                     r"(?P<date>[0-9][0-9]:[0-9][0-9], [0-9][0-9] [A-Za-z]+ [0-9]{4}) \(UTC\)", re.DOTALL)


# Don't keep afdls more than a month old
archivedate = date.today() + timedelta(days=-31)

class AfdlProject(object):
    """A project (or other party) maintaining a list of Afds with Afdls."""

    # Regular expression matching an Afdl template
    Rafdl = re.compile(r'\s*(\*\s*)?{{\s*Afdl(\|(?P<params>.+?))?}}\s*', re.DOTALL | re.IGNORECASE)

    def parseAfdls(self, text):
        """ Parses Afdls from text, returns unparsed text."""
        last = 0
        rest = ''
        for m in AfdlProject.Rafdl.finditer(text):
            paramString = m.group('params')
            params = []
            if paramString:
                params = paramString.split('|')
            aa = AfdArticleFromAfdlParams(params)
            if aa:
                if m.start() > last:
                    rest += text[last:m.start()] + '\n'
                last = m.end()
                if aa.result:
                    if aa.closedate > archivedate:
                        self.closedAfds.append(aa)
                    else:
                        self.newArchived += 1
                else:
                    self.openAfds.append(aa)
        if last < len(text):
            rest += text[last:] + '\n'
        return rest

    # Matching a categories for deletion line is tough since it's not a template, but free form text.
    # * [[:Category:Anime and manga inspired webcomics]] to [[:Category:Anime and manga webcomics]] at [[Wikipedia:Categories for deletion/Log/2006 July 11#Category:Anime and manga inspired webcomics to Category:Anime and manga webcomics]] '''Kept''' ''([[July 10]] [[2006]] – [[July 20]] [[2006]])''
    # *[[:Category:Keentoons]] at [[Wikipedia:Categories for discussion/Log/2007 April 30#Category:Keentoons]]
    # * [[:Category:WikiProject Webcomics members]] at [[Wikipedia:Categories for deletion/Log/2006 July 20#WikiProject participants]] ''[[July 20]] [[2006]] – [[July 31]] [[2006]]''
    # * [[:Category:Big-bust models and performers]] ([[2007]]-[[03-15]] – [[2007]]-[[03-21]]) '''No consensus'''
    # *[[:Category:Naturally busty porn stars]] at [[Wikipedia:Categories for discussion/Log/2007 March 8#Category:Naturally busty porn stars]] ([[2007]]-[[03-08]] - ([[2007]]-[[03-14]]) '''Delete'''
    # * [[:Category:Adult video games]] at [[Wikipedia:Categories for deletion/Log/2006 May 12#Category:Adult video games to Category:Erotic computer and video games]] '''renamed to''' [[:Category:Erotic computer and video games]]
    # * [[:Category:Artifex]] at [[Wikipedia:Categories for discussion/Log/2007 April 17#Category:Artifex]] '''Speedy Delete'''
    Rcfd = re.compile(r'\s*(\*\s*)?\[\[:(?P<category>Category:[^\]]+)\]\](?P<optional>.*?)'
                      + r'(\s+at\s+\[\[(?P<cfd>Wikipedia:Categories[ _]for[ _]d[a-z]+/Log/[^\]]+)\]\])?\s*'
                      + r"((?P<optional2>.*?)'''(?P<result>[^'\n]+)''')?(?P<rest>.*)", re.IGNORECASE)
    
    # Similarly for deletion review.
    # * [[Air Force Amy]] at [[Wikipedia:Deletion_review/Log/2007 May 5#Air Force Amy]]  ([[2007-04-05]]—[[2007-04-06]]) '''Keep rewritten article.'''
    #* [[List of male performers in gay porn films]] at [[Wikipedia:Deletion review/Log/2007 April 18#List of male performers in gay porn films]] ([[2007-04-18]]—[[23 April]] [[2007]]) '''Deletion overturned'''
    Rdrv = re.compile(r'\s*(\*\s*)?\[\[:?(?P<target>[^\]]+)\]\](?P<optional>.*?)\s+at\s+'
                      + r'\[\[(?P<cfd>Wikipedia:Deletion[ _]review[ _]deletion/Log/[^\]]+)\]\]\s*'
                      + r"((?P<optional2>.*?)'''(?P<result>[^'\n]+)''')?(?P<rest>.*)", re.IGNORECASE)

    Rdatespan = re.compile(r'\s*\(\s*(?P<fromdate>' +datexpr+ r')\s*[^\[\)]*\s*(?P<todate>' +datexpr+ r')\s*\)\s*')
    # Rdatespan = re.compile(r'\s*\(\s*(?P<fromdate>' +datexpr+ u')\s*-|(–)\s*(?P<todate>' +datexpr+ r')\s*\)\s*')
    # Rdatespan = re.compile(r'\s*\(\s*(?P<fromdate>\[\[\S+\]\])\s*(?P<dash>-|(–))\s*(?P<todate>\[\[\S+\]\])\)\s*')
    # Rdatespan = re.compile(r'\s*\(\s*(?P<fromdate>' +datexpr+ ')\s*(-|(—)|(–))\s*(?P<todate>' +datexpr+ ')\s*\)\s*')
    
    def parseCfds(self, text):
        """ Parses Cfd listings from text, returns unparsed text."""
        last = 0
        rest = ''
        for m in AfdlProject.Rcfd.finditer(text):
            # print 'match=', m.group()
            # print 'category=', m.group('category')
            # print 'cfd=', m.group('cfd')
            # print 'optional=', m.group('optional')
            # print 'optional2=', m.group('optional2')
            # print 'result=', m.group('result')
            # print 'rest=', m.group('rest')
            cfdname = m.group('cfd')
            if cfdname:
                cfd = wikipedia.Page(site, m.group('cfd'))
            else:
                cfd = None
            category = wikipedia.Page(site, m.group('category'))
            cfdrest = ''
            if m.group('optional'):
                cfdrest += ' ' + m.group('optional')
                cfdrest = cfdrest.strip()
            if m.group('optional2'):
                cfdrest += ' ' + m.group('optional2')
                cfdrest = cfdrest.strip()
            if m.group('rest'):
                cfdrest += ' ' + m.group('rest')
                cfdrest = cfdrest.strip()
            datespan = AfdlProject.Rdatespan.search(cfdrest)
            fromdate = None
            todate = None
            if datespan:
                # print 'datespan=', datespan.group()
                # print 'fromdate=', datespan.group('fromdate')
                # print 'dash=', datespan.group('dash')
                # print 'todate=', datespan.group('todate')
                cfdrest = AfdlProject.Rdatespan.sub('', cfdrest)
                fromdate=parseWikidate(datespan.group('fromdate'))
                todate=parseWikidate(datespan.group('todate'))
                if fromdate and not cfd :
                    cfd = wikipedia.Page(site, 'Wikipedia:Categories for discussion/Log/' + dateToPageName(fromdate))
                    # Todo: check if cfd page links to category?
            c = CfdCategory(cfd, category, fromdate, todate, m.group('result'), cfdrest)
            if c.startdate: # in other words, if it's legitimate
                if m.start() > last:
                    rest += text[last:m.start()] + '\n'
                last = m.end()
                if c.result:
                    if not c.closedate or c.closedate > archivedate:
                        self.closedAfds.append(aa)
                    else:
                        self.newArchived += 1
                else:
                    self.openAfds.append(c)
        if last < len(text):
            rest += text[last:] + '\n'
        print 'rest after cfds=', rest
        return rest

    # Regular expression that matches an Afdl list page
    RafdlPage = re.compile(r'(?P<header>.*)' +
                       r'==\s*Open\s*==\s*(?P<open>.*)' +
                       r'==\s*Closed\s*==\s*(?P<closed>.*)', re.DOTALL) #Todo: separate footer?

    def __init__(self, listpage, articleCategories, articleTemplates, talkCategories, talkTemplates):
        # print listpage, articleTemplates, articleCategories, talkTemplates, talkCategories
        self.listpage = listpage
        self.articleTemplates = articleTemplates
        self.articleCategories = articleCategories
        self.talkTemplates = talkTemplates
        self.talkCategories = talkCategories
        self.openAfds = []
        self.closedAfds = []
        # Count the number of useful changes that would be made to list page when writing
        #- if none, don't write anything
        self.newOpen = 0
        self.newClosed = 0
        self.newArchived = 0
        # Todo: self.archivedAfds = []
        
        match = AfdlProject.RafdlPage.match(listpage.get())
        if not match:
            print 'Could not parse', listpage, '!!'
            logfile.write('Could not parse ' + str(listpage) + '!!\n')
            return
        self.header = match.group('header')

        openmatch = match.group('open')
        openmatch = AfdlProject.Rdateheader.sub('', openmatch)
        closedmatch = match.group('closed')
        closedmatch = AfdlProject.Rdateheader.sub('', closedmatch)
        
        self.opentext = self.parseAfdls(openmatch)
        self.opentext = self.parseCfds(self.opentext)
        # Some of the formerly open Afds will have just been closed, count them
        self.newClosed = len(self.closedAfds)
        self.closedtext = self.parseAfdls(closedmatch)
        self.closedtext = self.parseCfds(self.closedtext)
                        
    def __str__(self):
        """A console representation of the AfdlProject"""
        return str(self.listpage)

    def logAfd(self, page, afd, reason, spec):
        """ Add an article and its afd to the project lists. Log this in a file for fun."""
        # print self.listpage, page.title(), afd.title(), reason, spec
        aa = AfdArticle(afd, page)
        # print aa
        # Consider if article has been deleted or redirected
        if aa.result:
            # Todo: should we check archivedate? Or should we put it on the page at least once?
            self.closedAfds.append(aa)
            self.newClosed += 1
        else:
            self.openAfds.append(aa)
            self.newOpen += 1
        logfile.write(self.listpage.title() + '\t' + page.title() + '\t' + afd.title() + '\t' + reason + ':' + spec + '\n')
        logfile.flush()

    def checkAfdArticle(self, afd, article, talkpage):
        """ Check if an Afd for an article qualifies to be added to the project lists.
        Returns True if qualifies (and has been added), False if not. """

        # check for articles already in Afd list, those don't even need to be "gotten"
        for open in self.openAfds:
            if open.afd == afd and open.article == article:
                # print afd, 'matches', open
                if Rresult.match(afd.get()): # afd has a result, in other words, was closed
                    self.openAfds.remove(aa)
                    self.logAfd(article, afd, 'listed as open on', sortPageName)
                return True
        for closed in self.closedAfds:
            if closed.afd == afd and closed.article == article:
                return True

        if len(self.articleCategories)>0:
            for cat in article.categories():
                if cat.title().capitalize() in self.articleCategories:
                    self.logAfd(article, afd, 'article category', cat.title())
                    return True
        if len(self.articleTemplates)>0:
            for template in article.templates():
                if template.title().capitalize() in self.articleTemplates:
                    self.logAfd(article, afd, 'article template', template)
                    return True
        # Do we need to check talk page?
        if len(self.talkCategories) + len(self.talkTemplates) <= 0:
            return False
        if not talkpage.exists():
            return False
        if len(self.talkCategories) > 0:
            for cat in talkpage.categories():
                if cat.capitalize() in self.talkCategories:
                    self.logAfd(article, afd, 'talk category', cat.title())
                    return True
        if len(self.talkTemplates) > 0:
            for template in talkpage.templates():
                if template.capitalize() in self.talkTemplates:
                    self.logAfd(article, afd, 'talk template', template)
                    return True
        return False

    # Regular expression that matches the date header generated below
    Rdateheader = re.compile(r"^\s*'''\d\d? [A-Za-z]{3,9}'''\s+\(\[\[.*\|AfD*\]\].*\)[ \t]*\n", re.MULTILINE)

    def afdsByTime(self, list):
        list.sort()
        lastdate = None
        result = ''
        for afd in list:
            if afd.startdate != lastdate:
                # print 'changing lastdate', lastdate, 'to', afd.startdate
                lastdate = afd.startdate
                # '''19 June''' ([[Wikipedia:Articles for deletion/Log/2007 June 19|AfD]],
                #                [[Wikipedia:Categories for discussion/Log/2007 June 19|CfD]])
                datename = dateToPageName(afd.startdate)
                result += afd.startdate.strftime("'''%d %B'''") \
                          + ' ([[Wikipedia:Articles for deletion/Log/' + datename + '|AfD]],' \
                          + ' [[Wikipedia:Categories for discussion/Log/' + datename + '|CfD]])' + '\n'
            result += '* ' + str(afd) + '\n'
            logfile.write('* ' + str(afd).encode(config.console_encoding, 'replace') + '\n')
        return result

    def afdlsText(self):
        """Returns the AfdArticle lists in this project, also to logfile."""
        logfile.write(str(self) + '\n')
        text = self.header + '== Open ==\n' + self.opentext
        text += self.afdsByTime(self.openAfds)
        text += '== Closed ==\n' + self.closedtext
        text += self.afdsByTime(self.closedAfds)
        # Todo: archived Afds by alphabetical order?
        logfile.write(text.encode(config.console_encoding, 'replace'))
        return text

# end class AfdlProject

class AfdArticle(object):
    """An article for deletion, with its article (usually but not always 1-1)."""
    def __init__(self, afd, article, startdate=None, closedate=None, result=None):
        # print afd, article, startdate, closedate, result
        self.article = article
        self.afd = afd
        if startdate:
            self.startdate = startdate
        else:
            # An approximation - assuming first edit created
            # print 'getting version history'
            edits = afd.getVersionHistory(reverseOrder = True, getAll = True)
            if not edits: return # an AfD must have a startdate
            self.startdate = parseDate(edits[0][1])
        if result and closedate:
            self.result = result
            self.closedate = closedate
        else:
            # print 'getting afd'
            afdtext = afd.get()
            match = Rresult.match(afdtext)
            if match:
                if result and len(result) > 0:
                    self.result = result
                else:
                    self.result = match.group('result')
                    # print self.result
                if closedate:
                    self.closedate = closedate
                else:
                    self.closedate = parseDate(match.group('date'))
            else:
                self.result = self.closedate = None
        # print self

    def __str__(self):
        """A console representation of the AfdArticle"""
        return self.afdl()

    def __cmp__(self, other):
        """Allows sorting AfdArticles. Descending order by startdate. """
        return cmp(other.startdate, self.startdate)

    def afdl(self):
        """{{afdl|Rebecca Cummings|Rebecca Cummings (2nd nomination)|2007-05-30|2007-06-04|Delete}}"""
        retval = '{{afdl|' + self.article.title() + '|'
        if self.afd.title() != 'Wikipedia:Articles for deletion/' + self.article.title():
            retval += self.afd.title()
        retval += '|' + self.startdate.strftime('%Y-%m-%d') + '|'
        if self.result:
            # 2007-03-16
            retval += self.closedate.strftime('%Y-%m-%d') + '|' + self.result
        else:
            retval += '|'
        retval += '}}'
        return retval

# end class AfdArticle

def AfdArticleFromAfdlParams(afdlp):
        """Reads an AfdArticle from ['article', 'AfD name', 'open YYYY-MM-DD', 'close YYYY-MM-DD', 'result'].
        Last 3 params optional. """
        # print afdlp
        if not afdlp or len(afdlp) < 1: return None
        if len(afdlp) > 1 and len(afdlp[1]) > 0:
            afdname = afdlp[1]
        else:
            afdname = 'Wikipedia:Articles for deletion/' + afdlp[0]
        afd = wikipedia.Page(site, afdname)
        # if not afd.exists(): return
        article = wikipedia.Page(site, afdlp[0])
        # Any missing params will be read from the afd
        if len(afdlp) > 4:
            aa = AfdArticle(afd, article, parseDate(afdlp[2]), parseDate(afdlp[3]), afdlp[4])
        elif len(afdlp) > 2:
            aa = AfdArticle(afd, article, parseDate(afdlp[2]), None, None)
        else:
            aa = AfdArticle(afd, article, None, None, None)
        # No AFD
        if not hasattr(aa, 'startdate'): return None
        return aa


class CfdCategory(AfdArticle):
    """Some special treatment for Categories for discussion/deletion debates."""

    # Parse date and subsection out of a cfd link
    Rcfdlink = re.compile(r'Wikipedia:Categories for d[a-z]+/Log/(?P<date>[A-Za-z0-9_ ]+)(#.*)?')
    
    def __init__(self, cfd, category, startdate, closedate, result, rest):
        # print cfd, category, startdate, closedate, result, rest
        self.article = category
        self.afd = cfd
        self.startdate = startdate
        self.closedate = closedate
        self.result = result
        self.rest = rest # any unparsed stuff
        if not startdate:
            match = CfdCategory.Rcfdlink.match(cfd.title())
            if match: #If not, should throw error
                self.startdate = parseDate(match.group('date'))
            else:
                # Throw error?
                return
        # Todo: parse result and closedate from cfd?
        # if result and not closedate:
            # self.closedate = self.startdate + timedelta(10) # Nasty hack
        # print self

    def __str__(self):
        """A console representation of the CfdCategory"""
        # *[[:Category:Naturally busty porn stars]] at [[Wikipedia:Categories for discussion/Log/2007 March 8#Category:Naturally busty porn stars]] ([[2007]]-[[03-08]] - ([[2007]]-[[03-14]]) '''Delete'''
        result = '[[:' + self.article.title() + ']] at [[' + self.afd.title() + ']] ([[' + str(self.startdate) + ']] -'
        if self.closedate:
            result += ' [[' + str(self.closedate) + ']]'
        result += ')'
        if self.result:
            result += " '''" + self.result + "'''"
        result += self.rest
        return result

# end class CfdCategory(AfdArticle)

    
def readAfdlProjects(projpagename):
    """ Reads specifications of all AfdlProjects on input page. """
    projPage = followRedirects(projpagename)
    # Afd List:, article templates:, article categories:, talk templates:
    # The Afd List one is mandatory, the rest are optional
    Rspec = re.compile(r'==[^=]*'
                       + r'^\*\s*Afd List:[^\[]*\[\[(?P<listpage>[^\]]+)\]\][^\*=$]*$'
                       +'[^=]*',
                       re.IGNORECASE | re.MULTILINE)
    # Note that the category includes the word 'Category:' but the template doesn't include the
    # word 'Template:'. This is to match the results of the Page methods.
    Rtemplate = re.compile(r'\[\[Template:(?P<template>[^\]]+)\]\]', re.IGNORECASE)
    Rcategory = re.compile(r'\[\[:(?P<category>Category:[^\]]+)\]\]', re.IGNORECASE)
    RartCat = re.compile(r'(^\*\s*Article categories:[^\*$]*$)', re.IGNORECASE)
    RartTem = re.compile(r'(^\*\s*Article templates:[^\*$]*$)', re.IGNORECASE)
    RtalkCat = re.compile(r'(^\*\s*Talk categories:[^\*$]*$)', re.IGNORECASE)
    RtalkTem = re.compile(r'(^\*\s*Talk templates:[^\*$]*$)', re.IGNORECASE)
    for match in Rspec.finditer(projPage.get()):
        # print match
        listpagename = match.group('listpage')
        listPage = followRedirects(listpagename)
        if not listPage.exists():
            continue
        articleTemplates = []
        articleCategories = []
        talkTemplates = []
        talkCategories = []
        # print 'listpage=', listpage
        for line in match.group().splitlines():
            # print line
            if RartCat.match(line):
                for cat in Rcategory.finditer(line):
                    articleCategories.append(cat.group('category').capitalize())
                # print articleCategories
            if RartTem.match(line):
                for template in Rtemplate.finditer(line):
                    articleTemplates.append(template.group('template').capitalize())
                # print articleTemplates
            if RtalkCat.match(line):
                for cat in Rcategory.finditer(line):
                    talkCategories.append(cat.group('category').capitalize())
                # print talkCategories
            if RtalkTem.match(line):
                for template in Rtemplate.finditer(line):
                    talkTemplates.append(template.group('template').capitalize())
                # print talkTemplates
        afdlProjects.append(AfdlProject(listPage, articleCategories, articleTemplates, talkCategories, talkTemplates))



# Regular expression that matches a "subst"ed Afd debate
# {{Wikipedia:Articles for deletion/Dr. John E. Douglas}}
Rafd = re.compile(r'{{\s*(?P<afd>Wikipedia:Articles for deletion/(?P<title>[^}]*))}}')

def processAfdList(afdListName):
    """ Searches input page name for Afds of pages matching project categories and templates. """
    print 'Processing', afdListName
    listpage = followRedirects(afdListName)
    if not listpage.exists(): return
    listtext = listpage.get()
    # print listtext
    for match in Rafd.finditer(listtext):
        # print 'match=', match.group()
        afdname = match.group('afd')
        # print afdname
        afdtitle = match.group('title')
        afd = followRedirects(afdname)
        # print afd.linkedPages()
        # need to follow every link, to deal with multiple nominations in one AFD
        checked = [] # only follow a link once per Afd
        for article in afd.linkedPages():
            # print 'article', article, 'section', article.section()
            if article.section() != None: continue
            if ':' in article.title(): continue # mainspace pages only
            if article in checked: continue
            checked.append(article)
            article = followRedirects(article.title())
            if not article.exists(): continue
            # print 'considering ', article
            # print article.templatesWithParams()
            for (template, params) in article.templatesWithParams():
                if template == 'AfDM' and 'page='+afdtitle in params:
                    talkpage = wikipedia.Page(site, 'Talk:' + article.title())
                    for proj in afdlProjects:
                        # check them all: even if listed in one, may be listed in many
                        proj.checkAfdArticle(afd, article, talkpage)
                    break # assume only one AfDM template per article

def main():
    args = wikipedia.handleArgs() # take out the global params

    readAfdlProjects(afdlBotPage)
    # for proj in afdlProjects:
    #   print proj
    #   print proj.afdlsText()
    # return

    if len(args) > 0:
        for arg in args:
            processAfdList(arg)
    else:
        checkdate = date.today() + timedelta(days=+2)
        lastdate = date.today() + timedelta(days=-12)
        while checkdate > lastdate :
            checkdate = checkdate + timedelta(days=-1)
            # Wikipedia:Articles_for_deletion/Log/2007_June_9
            checkpagename = 'Wikipedia:Articles_for_deletion/Log/' + dateToPageName(checkdate)
            processAfdList(checkpagename)

    for proj in afdlProjects:
        print proj, 'newOpen', proj.newOpen, 'newClosed', proj.newClosed, 'newArchived', proj.newArchived
        if proj.newOpen + proj.newClosed + proj.newArchived > 0:
            comment = ''
            if proj.newOpen > 0:
                comment += '+' + str(proj.newOpen) + ' open'
            if proj.newClosed > 0:
                if len(comment) > 0: comment += ', '
                comment += '+' + str(proj.newClosed) + ' closed'
            if proj.newArchived > 0:
                if len(comment) > 0: comment += ', '
                comment += '-' + str(proj.newArchived) + ' archived'
            comment += ' deletion discussions'
            print comment
            text = proj.afdlsText()
            print text
            proj.listpage.put(text, comment, watchArticle = True, minorEdit = False)

if __name__ == "__main__":
    try:
        main()
    finally:
        wikipedia.stopme()
        logfile.close()

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.