Browse Source

Graphing with matplotlib.

tags/v0.1^2
Ben Kurtovic 12 years ago
parent
commit
1701016a7d
2 changed files with 49 additions and 9 deletions
  1. +5
    -2
      README.md
  2. +44
    -7
      bot/tasks/afc_history.py

+ 5
- 2
README.md View File

@@ -26,5 +26,8 @@ integration between bot parts, and easier maintenance.

EarwigBot uses the MySQL library
[oursql](http://packages.python.org/oursql/) (>= 0.9.2) for communicating with
MediaWiki databases, and some tasks use their own tables for storage. It is not
required.
MediaWiki databases, and some tasks use their own tables for storage.
Additionally, the afc_history task uses
[matplotlib](http://matplotlib.sourceforge.net/) and
[numpy](http://numpy.scipy.org/) for graphing AfC statistics. Neither of these
modules are required for the main bot itself.

+ 44
- 7
bot/tasks/afc_history.py View File

@@ -1,10 +1,14 @@
# -*- coding: utf-8 -*-

from collections import OrderedDict
from datetime import datetime, timedelta
from itertools import count
from os.path import expanduser
from threading import Lock
from time import sleep

from matplotlib import pyplot as plt
from numpy import arange
import oursql

from classes import BaseTask
@@ -26,16 +30,22 @@ class Task(BaseTask):
submissions every day.

This information is saved to a MySQL database ("u_earwig_afc_history") and
used to generate attractive graphs showing the number of AfC submissions
over time.
used to generate a graph showing the number of AfC submissions by date
with matplotlib and numpy. The chart is saved as a PNG to
config.tasks["afc_history"]["graph"]["dest"], which defaults to
"afc_history.png".
"""
name = "afc_history"

def __init__(self):
cfg = config.tasks.get(self.name, {})
self.destination = cfg.get("destination", "afc_history.png")
self.num_days = cfg.get("days", 90)
self.categories = cfg.get("categories", {})

# Graph stuff:
self.graph = cfg.get("graph", {})
self.dest = self.graph.get("dest", "afc_history.png")

# Connection data for our SQL database:
kwargs = cfg.get("sql", {})
kwargs["read_default_file"] = expanduser("~/.my.cnf")
@@ -49,7 +59,7 @@ class Task(BaseTask):
action = kwargs.get("action")
try:
num_days = int(kwargs.get("days", 90))
num_days = int(kwargs.get("days", self.num_days))
if action == "update":
self.update(num_days)
elif action == "generate":
@@ -69,7 +79,7 @@ class Task(BaseTask):

def generate(self, num_days):
self.logger.info("Generating chart for past {0} days".format(num_days))
data = {}
data = OrderedDict()
generator = self.backwards_cat_iterator()
for d in xrange(num_days):
category = generator.next()
@@ -77,8 +87,8 @@ class Task(BaseTask):
data[date] = self.get_date_counts(date)

dest = expanduser(self.destination)
with open(dest, "wb") as fp:
fp.write(str(data))
self.generate_chart(reversed(data))
plt.savefig(dest)
self.logger.info("Chart saved to {0}".format(dest))

def backwards_cat_iterator(self):
@@ -155,3 +165,30 @@ class Task(BaseTask):
count = cursor.fetchall()[0][0]
counts[status] = count
return counts

def generate_chart(self, data):
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()]
ind = arange(len(data))
width = self.graph.get("width", 0.75)
xstep = self.graph.get("xAxisStep", 6)
xticks = arange(xstep-1, ind.size+xstep-1, xstep) + width/2.0
xlabels = [d for c, d in zip(count(1), data.keys()) if not c % xstep]
pcolor = self.graph.get("pendingColor", "y")
dcolor = self.graph.get("declinedColor", "r")
acolor = self.graph.get("acceptedColor", "g")

p1 = plt.bar(ind, pends, width, color=pcolor)
p2 = plt.bar(ind, declines, width, color=dcolor, bottom=pends)
p3 = plt.bar(ind, accepts, width, color=acolor, bottom=declines)

plt.title("AfC submissions per date")
plt.ylabel("Submissions")
plt.xlabel("Date")
plt.xticks(xticks, xlabels)
plt.legend((p1[0], p2[0], p3[0]), ("Pending", "Declined", "Accepted"))

fig = plt.gcf()
fig.set_size_inches(12, 9) # 1200, 900
fig.autofmt_xdate()

Loading…
Cancel
Save