@@ -28,7 +28,7 @@ See README.md for a basic overview, or the docs/ directory for details. | |||
""" | |||
__author__ = "Ben Kurtovic" | |||
__copyright__ = "Copyright (C) 2009, 2010, 2011 by Ben Kurtovic" | |||
__copyright__ = "Copyright (C) 2009, 2010, 2011, 2012 by Ben Kurtovic" | |||
__license__ = "MIT License" | |||
__version__ = "0.1.dev" | |||
__email__ = "ben.kurtovic@verizon.net" | |||
@@ -30,7 +30,7 @@ class Command(BaseCommand): | |||
name = "report" | |||
def process(self, data): | |||
self.site = wiki.get_site() | |||
self.site = self.bot.wiki.get_site() | |||
self.site._maxlag = None | |||
self.data = data | |||
@@ -22,7 +22,6 @@ | |||
import re | |||
from earwigbot import wiki | |||
from earwigbot.commands import BaseCommand | |||
class Command(BaseCommand): | |||
@@ -45,7 +44,7 @@ class Command(BaseCommand): | |||
return False | |||
def process(self, data): | |||
self.site = wiki.get_site() | |||
self.site = self.bot.wiki.get_site() | |||
self.site._maxlag = None | |||
if data.line[1] == "JOIN": | |||
@@ -41,7 +41,7 @@ class Command(BaseCommand): | |||
else: | |||
name = ' '.join(data.args) | |||
site = wiki.get_site() | |||
site = self.bot.wiki.get_site() | |||
site._maxlag = None | |||
user = site.get_user(name) | |||
@@ -41,7 +41,7 @@ class Command(BaseCommand): | |||
else: | |||
name = ' '.join(data.args) | |||
site = wiki.get_site() | |||
site = self.bot.wiki.get_site() | |||
site._maxlag = None | |||
user = site.get_user(name) | |||
@@ -39,7 +39,7 @@ class Command(BaseCommand): | |||
else: | |||
name = ' '.join(data.args) | |||
site = wiki.get_site() | |||
site = self.bot.wiki.get_site() | |||
site._maxlag = None | |||
user = site.get_user(name) | |||
@@ -29,6 +29,8 @@ import yaml | |||
from earwigbot import blowfish | |||
__all__ = ["BotConfig"] | |||
class BotConfig(object): | |||
""" | |||
EarwigBot's YAML Config File Manager | |||
@@ -110,10 +110,10 @@ class BaseTask(object): | |||
try: | |||
site = self.site | |||
except AttributeError: | |||
site = wiki.get_site() | |||
site = self.bot.wiki.get_site() | |||
try: | |||
cfg = self.bot.config.wiki["shutoff"] | |||
cfg = self.config.wiki["shutoff"] | |||
except KeyError: | |||
return False | |||
title = cfg.get("page", "User:$1/Shutoff/Task $2") | |||
@@ -22,6 +22,8 @@ | |||
from earwigbot.tasks import BaseTask | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to delink mainspace categories in declined [[WP:AFC]] | |||
submissions.""" | |||
@@ -26,9 +26,10 @@ from threading import Lock | |||
import oursql | |||
from earwigbot import wiki | |||
from earwigbot.tasks import BaseTask | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to check newly-edited [[WP:AFC]] submissions for copyright | |||
violations.""" | |||
@@ -62,7 +63,7 @@ class Task(BaseTask): | |||
if self.shutoff_enabled(): | |||
return | |||
title = kwargs["page"] | |||
page = wiki.get_site().get_page(title) | |||
page = self.bot.wiki.get_site().get_page(title) | |||
with self.db_access_lock: | |||
self.conn = oursql.connect(**self.conn_data) | |||
self.process(page) | |||
@@ -22,6 +22,8 @@ | |||
from earwigbot.tasks import BaseTask | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
""" A task to create daily categories for [[WP:AFC]].""" | |||
name = "afc_dailycats" | |||
@@ -34,11 +34,7 @@ import oursql | |||
from earwigbot import wiki | |||
from earwigbot.tasks import BaseTask | |||
# Valid submission statuses: | |||
STATUS_NONE = 0 | |||
STATUS_PEND = 1 | |||
STATUS_DECLINE = 2 | |||
STATUS_ACCEPT = 3 | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to generate charts about AfC submissions over time. | |||
@@ -56,6 +52,12 @@ class Task(BaseTask): | |||
""" | |||
name = "afc_history" | |||
# Valid submission statuses: | |||
STATUS_NONE = 0 | |||
STATUS_PEND = 1 | |||
STATUS_DECLINE = 2 | |||
STATUS_ACCEPT = 3 | |||
def setup(self): | |||
cfg = self.config.tasks.get(self.name, {}) | |||
self.num_days = cfg.get("days", 90) | |||
@@ -72,7 +74,7 @@ class Task(BaseTask): | |||
self.db_access_lock = Lock() | |||
def run(self, **kwargs): | |||
self.site = wiki.get_site() | |||
self.site = self.bot.wiki.get_site() | |||
with self.db_access_lock: | |||
self.conn = oursql.connect(**self.conn_data) | |||
@@ -136,7 +138,7 @@ class Task(BaseTask): | |||
stored = cursor.fetchall() | |||
status = self.get_status(title, pageid) | |||
if status == STATUS_NONE: | |||
if status == self.STATUS_NONE: | |||
if stored: | |||
cursor.execute(q_delete, (pageid,)) | |||
continue | |||
@@ -154,14 +156,14 @@ class Task(BaseTask): | |||
ns = page.namespace() | |||
if ns == wiki.NS_FILE_TALK: # Ignore accepted FFU requests | |||
return STATUS_NONE | |||
return self.STATUS_NONE | |||
if ns == wiki.NS_TALK: | |||
new_page = page.toggle_talk() | |||
sleep(2) | |||
if new_page.is_redirect(): | |||
return STATUS_NONE # Ignore accepted AFC/R requests | |||
return STATUS_ACCEPT | |||
return self.STATUS_NONE # Ignore accepted AFC/R requests | |||
return self.STATUS_ACCEPT | |||
cats = self.categories | |||
sq = self.site.sql_query | |||
@@ -169,16 +171,16 @@ class Task(BaseTask): | |||
match = lambda cat: list(sq(query, (cat.replace(" ", "_"), pageid))) | |||
if match(cats["pending"]): | |||
return STATUS_PEND | |||
return self.STATUS_PEND | |||
elif match(cats["unsubmitted"]): | |||
return STATUS_NONE | |||
return self.STATUS_NONE | |||
elif match(cats["declined"]): | |||
return STATUS_DECLINE | |||
return STATUS_NONE | |||
return self.STATUS_DECLINE | |||
return self.STATUS_NONE | |||
def get_date_counts(self, date): | |||
query = "SELECT COUNT(*) FROM page WHERE page_date = ? AND page_status = ?" | |||
statuses = [STATUS_PEND, STATUS_DECLINE, STATUS_ACCEPT] | |||
statuses = [self.STATUS_PEND, self.STATUS_DECLINE, self.STATUS_ACCEPT] | |||
counts = {} | |||
with self.conn.cursor() as cursor: | |||
for status in statuses: | |||
@@ -192,9 +194,9 @@ class Task(BaseTask): | |||
plt.xlabel(self.graph.get("xaxis", "Date")) | |||
plt.ylabel(self.graph.get("yaxis", "Submissions")) | |||
pends = [d[STATUS_PEND] for d in data.itervalues()] | |||
declines = [d[STATUS_DECLINE] for d in data.itervalues()] | |||
accepts = [d[STATUS_ACCEPT] for d in data.itervalues()] | |||
pends = [d[self.STATUS_PEND] for d in data.itervalues()] | |||
declines = [d[self.STATUS_DECLINE] for d in data.itervalues()] | |||
accepts = [d[self.STATUS_ACCEPT] for d in data.itervalues()] | |||
pends_declines = [p + d for p, d in zip(pends, declines)] | |||
ind = arange(len(data)) | |||
xsize = self.graph.get("xsize", 1200) | |||
@@ -32,14 +32,7 @@ import oursql | |||
from earwigbot import wiki | |||
from earwigbot.tasks import BaseTask | |||
# Chart status number constants: | |||
CHART_NONE = 0 | |||
CHART_PEND = 1 | |||
CHART_DRAFT = 2 | |||
CHART_REVIEW = 3 | |||
CHART_ACCEPT = 4 | |||
CHART_DECLINE = 5 | |||
CHART_MISPLACE = 6 | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to generate statistics for WikiProject Articles for Creation. | |||
@@ -52,6 +45,15 @@ class Task(BaseTask): | |||
name = "afc_statistics" | |||
number = 2 | |||
# Chart status number constants: | |||
CHART_NONE = 0 | |||
CHART_PEND = 1 | |||
CHART_DRAFT = 2 | |||
CHART_REVIEW = 3 | |||
CHART_ACCEPT = 4 | |||
CHART_DECLINE = 5 | |||
CHART_MISPLACE = 6 | |||
def setup(self): | |||
self.cfg = cfg = self.config.tasks.get(self.name, {}) | |||
@@ -82,7 +84,7 @@ class Task(BaseTask): | |||
(self.save()). We will additionally create an SQL connection with our | |||
local database. | |||
""" | |||
self.site = wiki.get_site() | |||
self.site = self.bot.wiki.get_site() | |||
with self.db_access_lock: | |||
self.conn = oursql.connect(**self.conn_data) | |||
@@ -285,7 +287,7 @@ class Task(BaseTask): | |||
query = """DELETE FROM page, row USING page JOIN row | |||
ON page_id = row_id WHERE row_chart IN (?, ?) | |||
AND ADDTIME(page_special_time, '36:00:00') < NOW()""" | |||
cursor.execute(query, (CHART_ACCEPT, CHART_DECLINE)) | |||
cursor.execute(query, (self.CHART_ACCEPT, self.CHART_DECLINE)) | |||
def update(self, **kwargs): | |||
"""Update a page by name, regardless of whether anything has changed. | |||
@@ -332,7 +334,7 @@ class Task(BaseTask): | |||
namespace = self.site.get_page(title).namespace() | |||
status, chart = self.get_status_and_chart(content, namespace) | |||
if chart == CHART_NONE: | |||
if chart == self.CHART_NONE: | |||
msg = "Could not find a status for [[{0}]]".format(title) | |||
self.logger.warn(msg) | |||
return | |||
@@ -366,7 +368,7 @@ class Task(BaseTask): | |||
namespace = self.site.get_page(title).namespace() | |||
status, chart = self.get_status_and_chart(content, namespace) | |||
if chart == CHART_NONE: | |||
if chart == self.CHART_NONE: | |||
self.untrack_page(cursor, pageid) | |||
return | |||
@@ -498,23 +500,23 @@ class Task(BaseTask): | |||
statuses = self.get_statuses(content) | |||
if "R" in statuses: | |||
status, chart = "r", CHART_REVIEW | |||
status, chart = "r", self.CHART_REVIEW | |||
elif "H" in statuses: | |||
status, chart = "p", CHART_DRAFT | |||
status, chart = "p", self.CHART_DRAFT | |||
elif "P" in statuses: | |||
status, chart = "p", CHART_PEND | |||
status, chart = "p", self.CHART_PEND | |||
elif "T" in statuses: | |||
status, chart = None, CHART_NONE | |||
status, chart = None, self.CHART_NONE | |||
elif "D" in statuses: | |||
status, chart = "d", CHART_DECLINE | |||
status, chart = "d", self.CHART_DECLINE | |||
else: | |||
status, chart = None, CHART_NONE | |||
status, chart = None, self.CHART_NONE | |||
if namespace == wiki.NS_MAIN: | |||
if not statuses: | |||
status, chart = "a", CHART_ACCEPT | |||
status, chart = "a", self.CHART_ACCEPT | |||
else: | |||
status, chart = None, CHART_MISPLACE | |||
status, chart = None, self.CHART_MISPLACE | |||
return status, chart | |||
@@ -613,23 +615,23 @@ class Task(BaseTask): | |||
returned if we cannot determine when the page was "special"-ed, or if | |||
it was "special"-ed more than 250 edits ago. | |||
""" | |||
if chart ==CHART_NONE: | |||
if chart ==self.CHART_NONE: | |||
return None, None, None | |||
elif chart == CHART_MISPLACE: | |||
elif chart == self.CHART_MISPLACE: | |||
return self.get_create(pageid) | |||
elif chart == CHART_ACCEPT: | |||
elif chart == self.CHART_ACCEPT: | |||
search_for = None | |||
search_not = ["R", "H", "P", "T", "D"] | |||
elif chart == CHART_DRAFT: | |||
elif chart == self.CHART_DRAFT: | |||
search_for = "H" | |||
search_not = [] | |||
elif chart == CHART_PEND: | |||
elif chart == self.CHART_PEND: | |||
search_for = "P" | |||
search_not = [] | |||
elif chart == CHART_REVIEW: | |||
elif chart == self.CHART_REVIEW: | |||
search_for = "R" | |||
search_not = [] | |||
elif chart == CHART_DECLINE: | |||
elif chart == self.CHART_DECLINE: | |||
search_for = "D" | |||
search_not = ["R", "H", "P", "T"] | |||
@@ -683,12 +685,12 @@ class Task(BaseTask): | |||
""" | |||
notes = "" | |||
ignored_charts = [CHART_NONE, CHART_ACCEPT, CHART_DECLINE] | |||
ignored_charts = [self.CHART_NONE, self.CHART_ACCEPT, self.CHART_DECLINE] | |||
if chart in ignored_charts: | |||
return notes | |||
statuses = self.get_statuses(content) | |||
if "D" in statuses and chart != CHART_MISPLACE: | |||
if "D" in statuses and chart != self.CHART_MISPLACE: | |||
notes += "|nr=1" # Submission was resubmitted | |||
if len(content) < 500: | |||
@@ -705,7 +707,7 @@ class Task(BaseTask): | |||
if time_since_modify > max_time: | |||
notes += "|no=1" # Submission hasn't been touched in over 4 days | |||
if chart in [CHART_PEND, CHART_DRAFT]: | |||
if chart in [self.CHART_PEND, self.CHART_DRAFT]: | |||
submitter = self.site.get_user(s_user) | |||
try: | |||
if submitter.blockinfo(): | |||
@@ -22,6 +22,8 @@ | |||
from earwigbot.tasks import BaseTask | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to clear [[Category:Undated AfC submissions]].""" | |||
name = "afc_undated" | |||
@@ -22,6 +22,8 @@ | |||
from earwigbot.tasks import BaseTask | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to add |blp=yes to {{WPB}} or {{WPBS}} when it is used along with | |||
{{WP Biography}}.""" | |||
@@ -22,6 +22,8 @@ | |||
from earwigbot.tasks import BaseTask | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to create daily categories for [[WP:FEED]].""" | |||
name = "feed_dailycats" | |||
@@ -0,0 +1,35 @@ | |||
# -*- coding: utf-8 -*- | |||
# | |||
# Copyright (C) 2009-2012 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||
# | |||
# Permission is hereby granted, free of charge, to any person obtaining a copy | |||
# of this software and associated documentation files (the "Software"), to deal | |||
# in the Software without restriction, including without limitation the rights | |||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
# copies of the Software, and to permit persons to whom the Software is | |||
# furnished to do so, subject to the following conditions: | |||
# | |||
# The above copyright notice and this permission notice shall be included in | |||
# all copies or substantial portions of the Software. | |||
# | |||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
# SOFTWARE. | |||
from earwigbot.tasks import BaseTask | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to tag talk pages with WikiProject Banners.""" | |||
name = "wikiproject_tagger" | |||
def setup(self): | |||
pass | |||
def run(self, **kwargs): | |||
pass |
@@ -22,6 +22,8 @@ | |||
from earwigbot.tasks import BaseTask | |||
__all__ = ["Task"] | |||
class Task(BaseTask): | |||
"""A task to tag files whose extensions do not agree with their MIME | |||
type.""" | |||
@@ -27,6 +27,8 @@ from os import path | |||
from earwigbot import __version__ | |||
from earwigbot.bot import Bot | |||
__all__ = ["BotUtility", "main"] | |||
class BotUtility(object): | |||
""" | |||
DOCSTRING NEEDED | |||
@@ -28,21 +28,21 @@ Wikipedia and other wiki sites. No connection whatsoever to python-wikitools | |||
written by Mr.Z-man, other than a similar purpose. We share no code. | |||
Import the toolset directly with `from earwigbot import wiki`. If using the | |||
built-in integration with the rest of the bot, that's usually not necessary: | |||
Bot() objects contain a `wiki` attribute containing a SitesDBManager object | |||
tied to the sites.db file located in the same directory as config.yml. That | |||
object has the principal methods get_site, add_site, and remove_site. | |||
built-in integration with the rest of the bot, Bot() objects contain a `wiki` | |||
attribute, which is a SitesDBManager object tied to the sites.db file located | |||
in the same directory as config.yml. That object has the principal methods | |||
get_site, add_site, and remove_site that should handle all of your Site (and | |||
thus, Page, Category, and User) needs. | |||
""" | |||
import logging as _log | |||
logger = _log.getLogger("earwigbot.wiki") | |||
logger.addHandler(_log.NullHandler()) | |||
from earwigbot.wiki.category import * | |||
from earwigbot.wiki.constants import * | |||
from earwigbot.wiki.exceptions import * | |||
from earwigbot.wiki.category import Category | |||
from earwigbot.wiki.page import Page | |||
from earwigbot.wiki.site import Site | |||
from earwigbot.wiki.sitesdb import SitesDBManager | |||
from earwigbot.wiki.user import User | |||
from earwigbot.wiki.page import * | |||
from earwigbot.wiki.site import * | |||
from earwigbot.wiki.sitesdb import * | |||
from earwigbot.wiki.user import * |
@@ -22,6 +22,8 @@ | |||
from earwigbot.wiki.page import Page | |||
__all__ = ["Category"] | |||
class Category(Page): | |||
""" | |||
EarwigBot's Wiki Toolset: Category Class | |||
@@ -27,13 +27,16 @@ This module defines some useful constants: | |||
* USER_AGENT - our default User Agent when making API queries | |||
* NS_* - default namespace IDs for easy lookup | |||
Import with `from earwigbot.wiki import constants` or `from earwigbot.wiki.constants import *`. | |||
Import directly with `from earwigbot.wiki import constants` or | |||
`from earwigbot.wiki.constants import *`. These are also available from | |||
earwigbot.wiki (e.g. `earwigbot.wiki.USER_AGENT`). | |||
""" | |||
# Default User Agent when making API queries: | |||
from earwigbot import __version__ as _v | |||
from platform import python_version as _p | |||
USER_AGENT = "EarwigBot/{0} (Python/{1}; https://github.com/earwig/earwigbot)".format(_v, _p()) | |||
del _v, _p | |||
# Default namespace IDs: | |||
NS_MAIN = 0 | |||
@@ -28,6 +28,8 @@ from urllib import quote | |||
from earwigbot.wiki.copyright import CopyrightMixin | |||
from earwigbot.wiki.exceptions import * | |||
__all__ = ["Page"] | |||
class Page(CopyrightMixin): | |||
""" | |||
EarwigBot's Wiki Toolset: Page Class | |||
@@ -43,6 +43,8 @@ from earwigbot.wiki.exceptions import * | |||
from earwigbot.wiki.page import Page | |||
from earwigbot.wiki.user import User | |||
__all__ = ["Site"] | |||
class Site(object): | |||
""" | |||
EarwigBot's Wiki Toolset: Site Class | |||
@@ -26,6 +26,8 @@ from earwigbot.wiki.constants import * | |||
from earwigbot.wiki.exceptions import UserNotFoundError | |||
from earwigbot.wiki.page import Page | |||
__all__ = ["User"] | |||
class User(object): | |||
""" | |||
EarwigBot's Wiki Toolset: User Class | |||
@@ -42,7 +42,7 @@ setup( | |||
url = "https://github.com/earwig/earwigbot", | |||
description = "EarwigBot is a Python robot that edits Wikipedia and interacts with people over IRC.", | |||
long_description = long_docs, | |||
download_url = "https://github.com/earwig/earwigbot/tarball/{0}".format(__version__), | |||
download_url = "https://github.com/earwig/earwigbot/tarball/v{0}".format(__version__), | |||
keywords = "earwig earwigbot irc wikipedia wiki mediawiki", | |||
license = "MIT License", | |||
classifiers = [ | |||