|
@@ -177,6 +177,8 @@ class DRNClerkBot(Task): |
|
|
for row in cursor: |
|
|
for row in cursor: |
|
|
case = _Case(*row) |
|
|
case = _Case(*row) |
|
|
cases.append(case) |
|
|
cases.append(case) |
|
|
|
|
|
log = "Read {0} cases from the database" |
|
|
|
|
|
self.logger.debug(log.format(len(cases))) |
|
|
return cases |
|
|
return cases |
|
|
|
|
|
|
|
|
def read_page(self, conn, cases, text): |
|
|
def read_page(self, conn, cases, text): |
|
@@ -218,17 +220,24 @@ class DRNClerkBot(Task): |
|
|
f_time, "", self.min_ts, "", self.min_ts, |
|
|
f_time, "", self.min_ts, "", self.min_ts, |
|
|
self.min_ts, False, False, 0, new=True) |
|
|
self.min_ts, False, False, 0, new=True) |
|
|
cases.append(case) |
|
|
cases.append(case) |
|
|
|
|
|
log = u"Added new case {0} ('{1}', status={2}, by {3})" |
|
|
|
|
|
self.logger.debug(log.format(id_, title, status, f_user)) |
|
|
else: |
|
|
else: |
|
|
case.status = status |
|
|
case.status = status |
|
|
|
|
|
log = u"Read active case {0} ('{1}')".format(id_, title) |
|
|
|
|
|
self.logger.debug(log) |
|
|
if case.title != title: |
|
|
if case.title != title: |
|
|
self.update_case_title(conn, id_, title) |
|
|
self.update_case_title(conn, id_, title) |
|
|
case.title = title |
|
|
case.title = title |
|
|
case.body, case.old = body, old |
|
|
case.body, case.old = body, old |
|
|
|
|
|
|
|
|
# Ignore cases loaded from the DB that weren't found in the page: |
|
|
|
|
|
for case in cases: |
|
|
for case in cases: |
|
|
if case.body is None: |
|
|
if case.body is None: |
|
|
case.status = self.STATUS_UNKNOWN |
|
|
case.status = self.STATUS_UNKNOWN |
|
|
|
|
|
log = u"Dropped case {0} because it is no longer on the page ('{1}')" |
|
|
|
|
|
self.logger.debug(log.format(case.id, case.title)) |
|
|
|
|
|
|
|
|
|
|
|
self.logger.debug("Done reading cases from the noticeboard page") |
|
|
|
|
|
|
|
|
def select_next_id(self, conn): |
|
|
def select_next_id(self, conn): |
|
|
"""Return the next incremental ID for a case.""" |
|
|
"""Return the next incremental ID for a case.""" |
|
@@ -256,6 +265,8 @@ class DRNClerkBot(Task): |
|
|
query = "UPDATE cases SET case_title = ? WHERE case_id = ?" |
|
|
query = "UPDATE cases SET case_title = ? WHERE case_id = ?" |
|
|
with conn.cursor() as cursor: |
|
|
with conn.cursor() as cursor: |
|
|
cursor.execute(query, (title, id_)) |
|
|
cursor.execute(query, (title, id_)) |
|
|
|
|
|
log = u"Updated title of case {0} to '{1}'".format(id_, title) |
|
|
|
|
|
self.logger.debug(log) |
|
|
|
|
|
|
|
|
def clerk(self, conn, cases): |
|
|
def clerk(self, conn, cases): |
|
|
"""Actually go through cases and modify those to be updated.""" |
|
|
"""Actually go through cases and modify those to be updated.""" |
|
@@ -265,10 +276,13 @@ class DRNClerkBot(Task): |
|
|
volunteers = [name for (name,) in cursor.fetchall()] |
|
|
volunteers = [name for (name,) in cursor.fetchall()] |
|
|
notices = [] |
|
|
notices = [] |
|
|
for case in cases: |
|
|
for case in cases: |
|
|
|
|
|
log = u"Clerking case {0} ('{1}')".format(case.id, case.title) |
|
|
|
|
|
self.logger.debug(log) |
|
|
if case.status == self.STATUS_UNKNOWN: |
|
|
if case.status == self.STATUS_UNKNOWN: |
|
|
self.clerk_unknown_case(conn, case) |
|
|
self.clerk_unknown_case(conn, case) |
|
|
else: |
|
|
else: |
|
|
notices += self.clerk_case(conn, case, volunteers) |
|
|
notices += self.clerk_case(conn, case, volunteers) |
|
|
|
|
|
self.logger.debug("Done clerking cases") |
|
|
return notices |
|
|
return notices |
|
|
|
|
|
|
|
|
def clerk_case(self, conn, case, volunteers): |
|
|
def clerk_case(self, conn, case, volunteers): |
|
@@ -292,47 +306,38 @@ class DRNClerkBot(Task): |
|
|
notices = self.clerk_review_case(case) |
|
|
notices = self.clerk_review_case(case) |
|
|
elif case.status in [self.STATUS_RESOLVED, self.STATUS_CLOSED]: |
|
|
elif case.status in [self.STATUS_RESOLVED, self.STATUS_CLOSED]: |
|
|
self.clerk_closed_case(case, signatures) |
|
|
self.clerk_closed_case(case, signatures) |
|
|
else: |
|
|
|
|
|
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, volunteers, signatures, storedsigs) |
|
|
self.save_case_updates(conn, case, volunteers, signatures, storedsigs) |
|
|
return notices |
|
|
return notices |
|
|
|
|
|
|
|
|
def clerk_new_case(self, case, volunteers, signatures): |
|
|
def clerk_new_case(self, case, volunteers, signatures): |
|
|
notices = self.notify_parties(case) |
|
|
notices = self.notify_parties(case) |
|
|
if any([editor in volunteers for (editor, timestamp) in signatures]): |
|
|
if any([editor in volunteers for (editor, timestamp) in signatures]): |
|
|
if case.last_action != self.STATUS_OPEN: |
|
|
|
|
|
case.status = self.STATUS_OPEN |
|
|
|
|
|
|
|
|
self.update_status(case, self.STATUS_OPEN) |
|
|
return notices |
|
|
return notices |
|
|
|
|
|
|
|
|
def clerk_open_case(self, case, signatures): |
|
|
def clerk_open_case(self, case, signatures): |
|
|
if self.check_for_review(case): |
|
|
if self.check_for_review(case): |
|
|
return [] |
|
|
return [] |
|
|
if len(case.body) - case.last_volunteer_size > 15000: |
|
|
if len(case.body) - case.last_volunteer_size > 15000: |
|
|
if case.last_action != self.STATUS_NEEDASSIST: |
|
|
|
|
|
case.status = self.STATUS_NEEDASSIST |
|
|
|
|
|
|
|
|
self.update_status(case, self.STATUS_NEEDASSIST) |
|
|
timestamps = [timestamp for (editor, timestamp) in signatures] |
|
|
timestamps = [timestamp for (editor, timestamp) in signatures] |
|
|
age = (datetime.utcnow() - max(timestamps)).total_seconds() |
|
|
age = (datetime.utcnow() - max(timestamps)).total_seconds() |
|
|
if age > 60 * 60 * 24 * 2: |
|
|
if age > 60 * 60 * 24 * 2: |
|
|
if case.last_action != self.STATUS_STALE: |
|
|
|
|
|
case.status = self.STATUS_STALE |
|
|
|
|
|
|
|
|
self.update_status(case, self.STATUS_STALE) |
|
|
return [] |
|
|
return [] |
|
|
|
|
|
|
|
|
def clerk_needassist_case(self, case, volunteers, newsigs): |
|
|
def clerk_needassist_case(self, case, volunteers, newsigs): |
|
|
if self.check_for_review(case): |
|
|
if self.check_for_review(case): |
|
|
return [] |
|
|
return [] |
|
|
if any([editor in volunteers for (editor, timestamp) in newsigs]): |
|
|
if any([editor in volunteers for (editor, timestamp) in newsigs]): |
|
|
if case.last_action != self.STATUS_OPEN: |
|
|
|
|
|
case.status = self.STATUS_OPEN |
|
|
|
|
|
|
|
|
self.update_status(case, self.STATUS_OPEN) |
|
|
return [] |
|
|
return [] |
|
|
|
|
|
|
|
|
def clerk_stale_case(self, case, newsigs): |
|
|
def clerk_stale_case(self, case, newsigs): |
|
|
if self.check_for_review(case): |
|
|
if self.check_for_review(case): |
|
|
return [] |
|
|
return [] |
|
|
if newsigs: |
|
|
if newsigs: |
|
|
if case.last_action != self.STATUS_OPEN: |
|
|
|
|
|
case.status = self.STATUS_OPEN |
|
|
|
|
|
|
|
|
self.update_status(case, self.STATUS_OPEN) |
|
|
return [] |
|
|
return [] |
|
|
|
|
|
|
|
|
def clerk_review_case(self, case): |
|
|
def clerk_review_case(self, case): |
|
@@ -343,6 +348,9 @@ class DRNClerkBot(Task): |
|
|
template += case.title.replace("|", "|") + "}}" |
|
|
template += case.title.replace("|", "|") + "}}" |
|
|
notice = _Notice(self.very_old_title, template) |
|
|
notice = _Notice(self.very_old_title, template) |
|
|
case.very_old_notified = True |
|
|
case.very_old_notified = True |
|
|
|
|
|
msg = u" {0}: will notify [[{1}]] with '{2}'" |
|
|
|
|
|
log = msg.format(case.id, self.very_old_title, template) |
|
|
|
|
|
self.logger.debug(log) |
|
|
return [notice] |
|
|
return [notice] |
|
|
return [] |
|
|
return [] |
|
|
|
|
|
|
|
@@ -362,6 +370,7 @@ class DRNClerkBot(Task): |
|
|
if not re.search(arch_bottom + "\s*\}\}\s*\Z", case.body): |
|
|
if not re.search(arch_bottom + "\s*\}\}\s*\Z", case.body): |
|
|
case.body += "\n{{" + arch_bottom + "}}" |
|
|
case.body += "\n{{" + arch_bottom + "}}" |
|
|
case.status = self.STATUS_UNKNOWN |
|
|
case.status = self.STATUS_UNKNOWN |
|
|
|
|
|
self.logger.debug(u" {0}: archived case".format(case.id)) |
|
|
|
|
|
|
|
|
def clerk_unknown_case(self, conn, case): |
|
|
def clerk_unknown_case(self, conn, case): |
|
|
if case.new: |
|
|
if case.new: |
|
@@ -372,9 +381,21 @@ class DRNClerkBot(Task): |
|
|
def check_for_review(self, case): |
|
|
def check_for_review(self, case): |
|
|
age = (datetime.utcnow() - case.file_time).total_seconds() |
|
|
age = (datetime.utcnow() - case.file_time).total_seconds() |
|
|
if age > 60 * 60 * 24 * 4: |
|
|
if age > 60 * 60 * 24 * 4: |
|
|
if case.last_action != self.STATUS_REVIEW: |
|
|
|
|
|
case.status = self.STATUS_REVIEW |
|
|
|
|
|
return True |
|
|
|
|
|
|
|
|
return self.update_status(case, self.STATUS_REVIEW) |
|
|
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
def update_status(self, case, new): |
|
|
|
|
|
old_n = self.ALIASES[case.status][0].upper() |
|
|
|
|
|
new_n = self.ALIASES[new][0].upper() |
|
|
|
|
|
old_n = "NEW" if not old_n else old_n |
|
|
|
|
|
new_n = "NEW" if not new_n else new_n |
|
|
|
|
|
if case.last_action != new: |
|
|
|
|
|
case.status = new |
|
|
|
|
|
log = u" {0}: {1} -> {2}" |
|
|
|
|
|
self.logger.debug(log.format(case.id, old_n, new_n)) |
|
|
|
|
|
return True |
|
|
|
|
|
log = u"Avoiding {0} {1} -> {2} because we already did this ('{3}')" |
|
|
|
|
|
self.logger.info(log.format(case.id, old_n, new_n, case.title)) |
|
|
return False |
|
|
return False |
|
|
|
|
|
|
|
|
def read_signatures(self, text): |
|
|
def read_signatures(self, text): |
|
@@ -418,6 +439,8 @@ class DRNClerkBot(Task): |
|
|
notices.append(notice) |
|
|
notices.append(notice) |
|
|
|
|
|
|
|
|
case.parties_notified = True |
|
|
case.parties_notified = True |
|
|
|
|
|
log = u" {0}: will try to notify {1} parties with '{3}'" |
|
|
|
|
|
self.logger.debug(log.format(case.id, len(notices), template)) |
|
|
return notices |
|
|
return notices |
|
|
|
|
|
|
|
|
def save_case_updates(self, conn, case, volunteers, sigs, storedsigs): |
|
|
def save_case_updates(self, conn, case, volunteers, sigs, storedsigs): |
|
@@ -457,6 +480,9 @@ class DRNClerkBot(Task): |
|
|
for name, stamp in additions: |
|
|
for name, stamp in additions: |
|
|
args.append((case.id, name, stamp)) |
|
|
args.append((case.id, name, stamp)) |
|
|
cursor.executemany(query2, args) |
|
|
cursor.executemany(query2, args) |
|
|
|
|
|
msg = u" {0}: added {1} signatures and removed {2}" |
|
|
|
|
|
log = msg.format(case.id, len(additions), len(removals)) |
|
|
|
|
|
self.logger.debug(log) |
|
|
|
|
|
|
|
|
def save_new_case(self, conn, case): |
|
|
def save_new_case(self, conn, case): |
|
|
args = (case.id, case.title, case.status, case.last_action, |
|
|
args = (case.id, case.title, case.status, case.last_action, |
|
@@ -467,6 +493,8 @@ class DRNClerkBot(Task): |
|
|
with conn.cursor() as cursor: |
|
|
with conn.cursor() as cursor: |
|
|
query = "INSERT INTO cases VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" |
|
|
query = "INSERT INTO cases VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" |
|
|
cursor.execute(query, args) |
|
|
cursor.execute(query, args) |
|
|
|
|
|
log = u" {0}: inserted new case into database".format(case.id) |
|
|
|
|
|
self.logger.debug(log) |
|
|
|
|
|
|
|
|
def save_existing_case(self, conn, case): |
|
|
def save_existing_case(self, conn, case): |
|
|
with conn.cursor(oursql.DictCursor) as cursor: |
|
|
with conn.cursor(oursql.DictCursor) as cursor: |
|
@@ -494,11 +522,17 @@ class DRNClerkBot(Task): |
|
|
if data != stored[column]: |
|
|
if data != stored[column]: |
|
|
changes.append(column + " = ?") |
|
|
changes.append(column + " = ?") |
|
|
args.append(data) |
|
|
args.append(data) |
|
|
|
|
|
msg = u" {0}: will alter {1} ('{2}' -> '{3}')" |
|
|
|
|
|
log = msg.format(case.id, column, stored[column], data) |
|
|
|
|
|
self.logger.debug(log) |
|
|
if changes: |
|
|
if changes: |
|
|
changes = ", ".join(changes) |
|
|
changes = ", ".join(changes) |
|
|
args.append(case.id) |
|
|
args.append(case.id) |
|
|
query = "UPDATE cases SET {0} WHERE case_id = ?".format(changes) |
|
|
query = "UPDATE cases SET {0} WHERE case_id = ?".format(changes) |
|
|
cursor.execute(query, args) |
|
|
cursor.execute(query, args) |
|
|
|
|
|
else: |
|
|
|
|
|
log = u" {0}: no changes to commit".format(case.id) |
|
|
|
|
|
self.logger.debug(log) |
|
|
|
|
|
|
|
|
def save(self, page, cases, kwargs, start): |
|
|
def save(self, page, cases, kwargs, start): |
|
|
"""Save any changes to the noticeboard.""" |
|
|
"""Save any changes to the noticeboard.""" |
|
@@ -514,7 +548,7 @@ class DRNClerkBot(Task): |
|
|
|
|
|
|
|
|
worktime = time() - start |
|
|
worktime = time() - start |
|
|
if worktime < 60: |
|
|
if worktime < 60: |
|
|
log = "Waiting {0} seconds to make sure we don't edit conflict" |
|
|
|
|
|
|
|
|
log = "Waiting {0} seconds to avoid edit conflicts" |
|
|
self.logger.debug(log.format(int(60 - worktime))) |
|
|
self.logger.debug(log.format(int(60 - worktime))) |
|
|
sleep(60 - worktime) |
|
|
sleep(60 - worktime) |
|
|
page.reload() |
|
|
page.reload() |
|
@@ -525,7 +559,8 @@ class DRNClerkBot(Task): |
|
|
summary = self.clerk_summary.replace("$3", str(counter)) |
|
|
summary = self.clerk_summary.replace("$3", str(counter)) |
|
|
summary = summary.replace("$4", "" if counter == 1 else "s") |
|
|
summary = summary.replace("$4", "" if counter == 1 else "s") |
|
|
page.edit(newtext, summary, minor=True, bot=True) |
|
|
page.edit(newtext, summary, minor=True, bot=True) |
|
|
self.logger.info(u"Saved page [[{0}]]".format(page.title)) |
|
|
|
|
|
|
|
|
log = u"Saved page [[{0}]] ({1} updates)" |
|
|
|
|
|
self.logger.info(log.format(page.title, counter)) |
|
|
|
|
|
|
|
|
def send_notices(self, site, notices): |
|
|
def send_notices(self, site, notices): |
|
|
"""Send out any templated notices to users or pages.""" |
|
|
"""Send out any templated notices to users or pages.""" |
|
@@ -534,25 +569,28 @@ class DRNClerkBot(Task): |
|
|
return |
|
|
return |
|
|
for notice in notices: |
|
|
for notice in notices: |
|
|
target, template = notice.target, notice.template |
|
|
target, template = notice.target, notice.template |
|
|
log = u"Notifying [[{0}]] with {1}".format(target, template) |
|
|
|
|
|
self.logger.info(log) |
|
|
|
|
|
|
|
|
log = u"Trying to notify [[{0}]] with '{1}'" |
|
|
|
|
|
self.logger.debug(log.format(target, template)) |
|
|
page = site.get_page(target) |
|
|
page = site.get_page(target) |
|
|
try: |
|
|
try: |
|
|
text = page.get() |
|
|
text = page.get() |
|
|
except exceptions.PageNotFoundError: |
|
|
except exceptions.PageNotFoundError: |
|
|
text = "" |
|
|
text = "" |
|
|
if notice.too_late and notice.too_late in text: |
|
|
if notice.too_late and notice.too_late in text: |
|
|
log = u"Skipping [[{0}]]; was already notified".format(target) |
|
|
|
|
|
self.logger.info(log) |
|
|
|
|
|
|
|
|
log = u"Skipping [[{0}]]; was already notified with '{1}'" |
|
|
|
|
|
self.logger.info(log.format(page.title, template)) |
|
|
text += ("\n" if text else "") + template |
|
|
text += ("\n" if text else "") + template |
|
|
try: |
|
|
try: |
|
|
page.edit(text, self.notify_summary, minor=False, bot=True) |
|
|
page.edit(text, self.notify_summary, minor=False, bot=True) |
|
|
except exceptions.EditError as error: |
|
|
except exceptions.EditError as error: |
|
|
name, msg = type(error).name, error.message |
|
|
name, msg = type(error).name, error.message |
|
|
log = u"Couldn't leave notice on {0} because of {1}: {2}" |
|
|
|
|
|
|
|
|
log = u"Couldn't leave notice on [[{0}]] because of {1}: {2}" |
|
|
self.logger.error(log.format(page.title, name, msg)) |
|
|
self.logger.error(log.format(page.title, name, msg)) |
|
|
|
|
|
else: |
|
|
|
|
|
log = u"Notified [[{0}]] with '{1}'" |
|
|
|
|
|
self.logger.info(log.format(page.title, template)) |
|
|
|
|
|
|
|
|
self.logger.info("Done sending notices") |
|
|
|
|
|
|
|
|
self.logger.debug("Done sending notices") |
|
|
|
|
|
|
|
|
def update_chart(self, conn, site): |
|
|
def update_chart(self, conn, site): |
|
|
page = site.get_page(self.chart_title) |
|
|
page = site.get_page(self.chart_title) |
|
@@ -618,6 +656,8 @@ class DRNClerkBot(Task): |
|
|
return ", ".join(msg) + " ago" if msg else "0 hours ago" |
|
|
return ", ".join(msg) + " ago" if msg else "0 hours ago" |
|
|
|
|
|
|
|
|
def purge_old_data(self, conn): |
|
|
def purge_old_data(self, conn): |
|
|
|
|
|
log = "Purging closed cases older than a month from the database" |
|
|
|
|
|
self.logger.info(log) |
|
|
query = """DELETE cases, signatures |
|
|
query = """DELETE cases, signatures |
|
|
FROM cases JOIN signatures ON case_id = signature_case |
|
|
FROM cases JOIN signatures ON case_id = signature_case |
|
|
WHERE case_status = ? |
|
|
WHERE case_status = ? |
|
|