Browse Source

Implement chart feature.

tags/v0.1^2
Ben Kurtovic 12 years ago
parent
commit
80bc2c527d
2 changed files with 118 additions and 14 deletions
  1. +113
    -14
      earwigbot/tasks/drn_clerkbot.py
  2. +5
    -0
      earwigbot/tasks/schema/drn_clerkbot.sql

+ 113
- 14
earwigbot/tasks/drn_clerkbot.py View File

@@ -24,7 +24,7 @@ from datetime import datetime
from os import expanduser
import re
from threading import RLock
from time import sleep, time
from time import mktime, sleep, time

import oursql

@@ -55,6 +55,7 @@ class DRNClerkBot(Task):
STATUS_REVIEW: ("review",),
STATUS_RESOLVED: ("resolved", "resolve"),
STATUS_CLOSED: ("closed", "close"),
STATUS_ARCHIVE: ("archive",)
}

def setup(self):
@@ -62,9 +63,11 @@ class DRNClerkBot(Task):
cfg = self.config.tasks.get(self.name, {})

# Set some wiki-related attributes:
self.title = cfg.get("title", "Wikipedia:Dispute resolution noticeboard")
self.talk = cfg.get("talk", "Wikipedia talk:Dispute resolution noticeboard")
self.volunteer_title = cfg.get("volunteers", "Wikipedia:Dispute resolution noticeboard/Volunteering")
self.title = cfg.get("title",
"Wikipedia:Dispute resolution noticeboard")
self.chart_title = cfg.get("chartTitle", "Template:DRN case status")
self.volunteer_title = cfg.get("volunteers",
"Wikipedia:Dispute resolution noticeboard/Volunteering")
self.very_old_title = cfg.get("veryOldTitle", "User talk:Szhang (WMF)")
default_summary = "Updating $3 cases for the [[WP:DRN|dispute resolution noticeboard]]."
self.summary = self.make_summary(cfg.get("summary", default_summary))
@@ -77,6 +80,11 @@ class DRNClerkBot(Task):
self.tl_archive_top = templates.get("archiveTop", "DRN archive top")
self.tl_archive_bottom = templates.get("archiveBottom",
"DRN archive bottom")
self.tl_chart_header = templates.get("chartHeader",
"DRN case status/header")
self.tl_chart_row = templates.get("chartRow", "DRN case status/row")
self.tl_chart_footer = templates.get("chartFooter",
"DRN case status/footer")

# Connection data for our SQL database:
kwargs = cfg.get("sql", {})
@@ -106,6 +114,8 @@ class DRNClerkBot(Task):
noticies = self.clerk(conn, cases)
self.save(page, cases, kwargs)
self.send_notices(site, notices)
if action in ["all", "update_chart"]:
self.update_chart(conn)
finally:
self.db_access_lock.release()

@@ -176,11 +186,20 @@ class DRNClerkBot(Task):
except (AttributeError, IndexError):
id_ = self.select_next_id(conn, "case_id", "case")
re_id2 = "(\{\{" + tl_status_esc
re_id2 += "(.*?)\}\})(<!-- Bot Case ID \(please don't modify\): .*? -->)?"
re_id2 += r"(.*?)\}\})(<!-- Bot Case ID \(please don't modify\): .*? -->)?"
repl = ur"\1 <!-- Bot Case ID (please don't modify): {0} -->"
body = re.sub(re_id2, repl.format(id_), body)
case = _Case(id_, title, status, self.STATUS_UNKNOWN, time(),
0, False, False, 0, new=True)
re_f = r"\{\{drn filing editor\|(.*?)\|(\d{2}:\d{2},\s\d{2}\s\w+\s\d{4}\s\(UTC\))\}\}"
match = re.search(re_f, body, re.U)
if match:
f_user = match.group(1).split("/", 1)[0].replace("_", " ")
strp = "%H:%M, %d %B %Y (UTC)"
f_time = datetime.strptime(strp, match.group(2))
else:
f_user, f_time = None, datetime.utcnow()
case = _Case(id_, title, status, self.STATUS_UNKNOWN, f_user,
f_time, "", 0, "", 0, 0, False, False, 0,
new=True)
cases.append(case)
else:
case.status = status
@@ -252,7 +271,7 @@ class DRNClerkBot(Task):
log = u"Unsure of how to deal with case {0} (title: {1})"
self.logger.error(log.format(case.id, case.title))
return notices
self.save_case_updates(conn, case, signatures, storedsigs)
self.save_case_updates(conn, case, volunteers, signatures, storedsigs)
return notices

def clerk_new_case(self, case, volunteers, signatures):
@@ -351,7 +370,7 @@ class DRNClerkBot(Task):
case.parties_notified = True
return notices

def save_case_updates(self, conn, case, signatures, storedsigs):
def save_case_updates(self, conn, case, volunteers, sigs, storedsigs):
if case.status != case.original_status:
case.last_action = case.status
new = self.ALIASES[case.status][0]
@@ -360,6 +379,16 @@ class DRNClerkBot(Task):
repl = "{{" + self.tl_status + "|" + new + "}}"
case.body = re.sub(search, repl, case.body)

newest_ts = max([stamp for (user, stamp) in sigs])
newest_user = [usr for (usr, stamp) in sigs if stamp == newest_ts][0]
case.modify_user = newest_ts
case.modify_time = newest_user

newest_vts = max([stamp for (usr, stamp) in sigs if usr in volunteers])
newest_vuser = [usr for (usr, stamp) in sigs if stamp == newest_vts][0]
case.volunteer_user = newest_vol
case.volunteer_time = volsigs[1]

if case.new:
self.save_new_case(conn, case)
else:
@@ -368,8 +397,8 @@ class DRNClerkBot(Task):
with conn.cursor() as cursor:
query1 = "DELETE FROM signature WHERE signature_case = ? AND signature_username = ? AND signature_timestamp = ?"
query2 = "INSERT INTO signature VALUES (?, ?, ?, ?)"
removals = set(storedsigs) - set(signatures)
additions = set(signatures) - set(storedsigs)
removals = set(storedsigs) - set(sigs)
additions = set(sigs) - set(storedsigs)
if removals:
args = [(case.id, name, stamp) for (name, stamp) in removals]
cursor.execute(query1, args)
@@ -466,17 +495,87 @@ class DRNClerkBot(Task):

self.logger.info("Done sending notices")

def update_chart(self, conn, site):
page = site.get_page(self.chart_title)
self.logger.info(u"Updating case status at [[{0}]]".format(page.title))
statuses = self.compile_chart(conn)
text = page.get()
newtext = re.sub(u"<!-- status begin -->(.*?)<!-- status end -->",
"<!-- status begin -->\n" + statuses + "\n<!-- status end -->",
text, flags=re.DOTALL)
if newtext == text:
self.logger.info("Chart unchanged; not saving")
return

newtext = re.sub("<!-- sig begin -->(.*?)<!-- sig end -->",
"<!-- sig begin -->~~~ at ~~~~~<!-- sig end -->",
newtext)
page.edit(newtext, summary, minor=True, bot=True)
self.logger.info(u"Chart saved to [[{0}]]".format(page.title))

def compile_chart(self, conn):
chart = "{{" + self.tl_chart_header + "}}\n"
query = "SELECT case_title, case_status, case_file_time FROM case"
with conn.cursor(oursql.DictCursor) as cursor:
cursor.execute(query)
for case in query:
chart += self.compile_row(case)
chart += "{{" + self.tl_chart_footer + "}}"
return chart

def compile_row(self, case):
data = "|t={case_title}|s={status}"
data += "|cu={case_file_user}|cs={file_sortkey}|ct={file_time}"
if case["volunteer_user"]:
data += "|vu={case_volunteer_user}|vs={volunteer_sortkey}|vt={volunteer_time}"
case["volunteer_time"] = self.format_time(case["case_volunteer_time"])
case["volunteer_sortkey"] = int(mktime(case["case_volunteer_time"].timetuple()))
data += "|mu={case_modify_user}|ms={modify_sortkey}|mt={modify_time}"

case["case_title"] = case["case_title"].replace("|", "&#124;")
case["status"] = self.translate_status(case["case_status"])
case["file_time"] = self.format_time(case["case_file_time"])
case["file_sortkey"] = int(mktime(case["case_file_time"].timetuple()))
case["modify_time"] = self.format_time(case["case_modify_time"])
case["modify_sortkey"] = int(mktime(case["case_modify_time"].timetuple()))
row = "{{" + self.tl_chart_row + data.format(**case) + "}}\n"
return row

def translate_status(self, num):
translations = {
STATUS_UNKNOWN: "u",
STATUS_NEW: "n",
STATUS_OPEN: "o",
STATUS_STALE: "s",
STATUS_NEEDASSIST: "e",
STATUS_REVIEW: "r",
STATUS_RESOLVED: "d",
STATUS_CLOSED: "c",
STATUS_ARCHIVE: "a"
}
return translations[num]

def format_time(self, dt):
"""Format a datetime into the standard MediaWiki timestamp format."""
return dt.strftime("%H:%M, %d %b %Y")


class _Case(object):
"""A object representing a dispute resolution case."""
def __init__(self, id_, title, status, last_action, file_time, close_time,
parties_notified, very_old_notified, last_volunteer_size,
new=False):
def __init__(self, id_, title, status, last_action, file_user, file_time,
modify_user, modify_time, volunteer_user, volunteer_time,
close_time, parties_notified, very_old_notified,
last_volunteer_size, new=False):
self.id = id_
self.title = title
self.status = status
self.last_action = last_action
self.file_user = file_user
self.file_time = file_time
self.modify_user = modify_user
self.modify_time = modify_time
self.volunteer_user = volunteer_user
self.volunteer_time = volunteer_time
self.close_time = close_time
self.parties_notified = parties_notified
self.very_old_notified = very_old_notified


+ 5
- 0
earwigbot/tasks/schema/drn_clerkbot.sql View File

@@ -18,7 +18,12 @@ CREATE TABLE `case` (
`case_title` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL,
`case_status` int(2) unsigned DEFAULT NULL,
`case_last_action` int(2) unsigned DEFAULT NULL,
`case_file_user` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL,
`case_file_time` int(10) unsigned DEFAULT NULL,
`case_modify_user` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL,
`case_modify_time` int(10) unsigned DEFAULT NULL,
`case_volunteer_user` varchar(512) COLLATE utf8_unicode_ci DEFAULT NULL,
`case_volunteer_time` int(10) unsigned DEFAULT NULL,
`case_close_time` int(10) unsigned DEFAULT NULL,
`case_parties_notified` tinyint(1) unsigned DEFAULT NULL,
`case_very_old_notified` tinyint(1) unsigned DEFAULT NULL,


Loading…
Cancel
Save