From 015eb44a89297dd77b84624f02b487fa8a527bc7 Mon Sep 17 00:00:00 2001 From: Ben Kurtovic Date: Sat, 28 Jul 2012 00:40:07 -0400 Subject: [PATCH] A bunch of updates. --- earwigbot/tasks/drn_clerkbot.py | 94 ++++++++++++++++++++++++++++++++--------- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/earwigbot/tasks/drn_clerkbot.py b/earwigbot/tasks/drn_clerkbot.py index 6ae7229..c9ebc43 100644 --- a/earwigbot/tasks/drn_clerkbot.py +++ b/earwigbot/tasks/drn_clerkbot.py @@ -22,6 +22,8 @@ from os import expanduser import re +from threading import RLock +from time import sleep, time import oursql @@ -46,9 +48,13 @@ class DRNClerkBot(Task): def setup(self): """Hook called immediately after the task is loaded.""" cfg = self.config.tasks.get(self.name, {}) + + # Set some wiki-related attributes: self.title = cfg.get("page", "Wikipedia:Dispute resolution noticeboard") + default_summary = "Updating $3 cases for the [[WP:DRN|dispute resolution noticeboard]]." + self.summary = self.make_summary(cfg.get("summary", default_summary)) - # Templates used in chart generation: + # Templates used: templates = cfg.get("templates", {}) self.tl_status = templates.get("status", "DR case status") self.tl_notify_party = templates.get("notifyParty", "DRN-notice") @@ -61,16 +67,47 @@ class DRNClerkBot(Task): kwargs = cfg.get("sql", {}) kwargs["read_default_file"] = expanduser("~/.my.cnf") self.conn_data = kwargs - self.db_access_lock = Lock() + self.db_access_lock = RLock() def run(self, **kwargs): """Entry point for a task event.""" + if not self.db_access_lock.acquire(False): # Non-blocking + self.logger.info("A job is already ongoing; aborting") + return + with self.db_access_lock: + self.logger.info(u"Starting update to [[{0}]]".format(self.title)) + start = time() conn = oursql.connect(**self.conn_data) cases = read_database(conn) page = self.bot.wiki.get_site().get_page(self.title) text = page.get() - current = read_page(cases, text) + read_page(conn, cases, text) + + # Work! + # Send messages! + + self.save(page, cases) + + def save(self, page, cases): + newtext = text = page.get() + counter = 0 + for case in cases: + if case.old != case.body: + newtext = newtext.replace(case.old, case.body) + counter += 1 + + worktime = time() - start + if worktime < 60: + sleep(60 - worktime) + page.reload() + if page.get() != text: + log = "Someone has edited the page while we were working; restarting" + self.logger.warn(log) + return self.run() + summary = self.summary.replace("$3", str(counter)) + page.edit(text, summary, minor=False, bot=True) + self.logger.info(u"Saved page [[{0}]]".format(page.title)) def read_database(self, conn): """Return a list of _Cases from the database.""" @@ -82,24 +119,42 @@ class DRNClerkBot(Task): cases.append(_Case(id_, title, status)) return cases - def read_page(self, cases, text): + def read_page(self, conn, cases, text): """Read the noticeboard content and update the list of _Cases.""" + tl_status_esc = re.escape(self.tl_status) split = re.split("(^==\s*[^=]+?\s*==$)", text, flags=re.M|re.U) - cases = [] - case = None - for item in split: - if item.startswith("=="): - if case: - cases.append(case) - case = _Case() - case.title = item[2:-2].strip() - else: - templ = re.escape(self.tl_status) - if case and re.match("\s*\{\{" + templ, item, re.U): - case.body = case.old_body = item - case.status = self.read_status(body) - if case: - cases.append(case) + for i in xrange(len(split)): + if i + 1 == len(split): + break + if not split[i].startswith("=="): + continue + title = split[i][2:-2].strip() + body = old = split[i + 1] + if not re.search("\s*\{\{" + tl_status_esc, body, re.U): + continue + status = self.read_status(body) + re_id = "" + try: + id_ = re.search(re_id, body).group(1) + case = [case for case in cases if case.id == id_][0] + except (AttributeError, IndexError): + id_ = self.select_next_id(conn) + re_id2 = "(\{\{" + tl_status_esc + "(.*?)\}\})()?" + repl = ur"\1 " + body = re.sub(re_id2, repl.format(id_), body) + case = _Case(id_, title, status) + cases.append(case) + case.body, case.old = body, old + + def select_next_id(self, conn): + """Return the next incremental ID for a case.""" + query = "SELECT MAX(case_id) FROM case" + with conn.cursor() as cursor: + cursor.execute(query) + current = cursor.fetchone()[0] + if current: + return current + 1 + return 1 def read_status(self, body): """Parse the current status from a case body.""" @@ -130,3 +185,4 @@ class _Case(object): self.status = status self.body = None + self.old = None