changeset 45:0f4163dbbafc

SVG charts: separate database-querying and chart-printing code. Create Game methods in rlgall.py to get histogram data for score, xl, maxdepth. In stats2.py, make the chart-printing functions use this data and avoid running hard-coded queries.
author John "Elwin" Edwards
date Thu, 16 Jan 2014 16:25:09 -0800
parents 7c789e87ee5d
children afb58e16bca8
files py/rlgall.py py/stats2.py
diffstat 2 files changed, 109 insertions(+), 63 deletions(-) [+]
line wrap: on
line diff
--- a/py/rlgall.py	Thu Jan 16 11:04:45 2014 -0800
+++ b/py/rlgall.py	Thu Jan 16 16:25:09 2014 -0800
@@ -296,6 +296,73 @@
     for d in data:
       d["game"] = self
     return data
+  def getXLCounts(self, nmax=15):
+    "Returns a list of (xlevel, gamecount) pairs."
+    lquery = "SELECT count(*) FROM {0} WHERE xl = %s;".format(self.uname)
+    mquery = "SELECT count(*) FROM {0} WHERE xl >= %s;".format(self.uname)
+    try:
+      nmax = int(nmax)
+    except (ValueError, TypeError):
+      return []
+    if nmax <= 0:
+      return []
+    xlevels = range(1, nmax)
+    results = []
+    conn = psycopg2.connect("dbname=rlg")
+    cur = conn.cursor()
+    for xl in xlevels:
+      cur.execute(lquery, [xl])
+      results.append((xl, cur.fetchone()[0]))
+    cur.execute(mquery, [nmax])
+    results.append((nmax, cur.fetchone()[0]))
+    cur.close()
+    conn.close()
+    return results
+  def getDepthCounts(self, nmax=30):
+    "Returns a list of (maxdepth, gamecount) pairs."
+    dqry = "SELECT count(*) FROM {0} WHERE maxdepth = %s;".format(self.uname)
+    mqry = "SELECT count(*) FROM {0} WHERE maxdepth >= %s;".format(self.uname)
+    try:
+      nmax = int(nmax)
+    except (ValueError, TypeError):
+      return []
+    if nmax <= 0:
+      return []
+    depths = range(1, nmax)
+    results = []
+    conn = psycopg2.connect("dbname=rlg")
+    cur = conn.cursor()
+    for lev in depths:
+      cur.execute(dqry, [lev])
+      results.append((lev, cur.fetchone()[0]))
+    cur.execute(mqry, [nmax])
+    results.append((nmax, cur.fetchone()[0]))
+    cur.close()
+    conn.close()
+    return results
+  def getScoreCounts(self, blocks=10, size=1000):
+    "Returns a list of (minscore, gamecount) pairs."
+    sqry = "SELECT count(*) FROM {0} WHERE score >= %s AND score < %s;".format(self.uname)
+    mqry = "SELECT count(*) FROM {0} WHERE score >= %s;".format(self.uname)
+    try:
+      blocks = int(blocks)
+      size = int(size)
+    except (ValueError, TypeError):
+      return []
+    if blocks <= 0 or size <= 0:
+      return []
+    marks = range(blocks - 1)
+    results = []
+    conn = psycopg2.connect("dbname=rlg")
+    cur = conn.cursor()
+    for m in marks:
+      cur.execute(sqry, [m * size, (m + 1) * size])
+      results.append((m * size, cur.fetchone()[0]))
+    cur.execute(mqry, [(blocks - 1) * size])
+    results.append(((blocks - 1) * size, cur.fetchone()[0]))
+    cur.close()
+    conn.close()
+    return results
 # End Game class definition
 
 class RogueGame(Game):
--- a/py/stats2.py	Thu Jan 16 11:04:45 2014 -0800
+++ b/py/stats2.py	Thu Jan 16 16:25:09 2014 -0800
@@ -43,6 +43,18 @@
 </g>
 """
 
+class SVGChart():
+  def __init__(self, filename):
+    self.of = open(filename, "w", encoding="utf-8")
+    self.of.write(dochead)
+  def style(self, barcolor):
+    self.of.write(stylesheet.format(barcolor))
+  def write(self, obj):
+    self.of.write(obj)
+  def close(self):
+    self.of.write('</svg>\n')
+    self.of.close()
+
 def ylimits(x):
   ll = [2, 3, 4, 5, 6, 8, 10, 15]
   m = 1
@@ -71,14 +83,16 @@
       divs = 5
   return divs, lim
 
-def mkxlgraph(game, xls, xlcounts):
-  xlgraph = open("{0}/xl-{1}.svg".format(svgpath, game.uname), "w")
-  xlgraph.write(dochead)
-  xlgraph.write(stylesheet.format("0000ff"))
+def titles(l, c, r):
+  return ltitle.format(l) + ctitle.format(c) + rtitle.format(r) 
+
+def mkxlgraph(game, xldata):
+  xlgraph = SVGChart("{0}/xl-{1}.svg".format(svgpath, game.uname))
+  xlgraph.style("0000ff")
   xlgraph.write(framerect)
-  xldivs, xlmax = ylimits(max(xlcounts))
+  xldivs, xlmax = ylimits(max([ pt[1] for pt in xldata ]))
   scale = 500 / xlmax
-  for xl, count in zip(xls, xlcounts):
+  for xl, count in xldata:
     barx = xl * 50 + 60
     barh = round(scale * count)
     bary = 550 - barh
@@ -90,51 +104,44 @@
     xlgraph.write(ylabel.format(labelh, labeln))
   xlgraph.write(xlabelf.format("Experience level"))
   xlgraph.write(ylabelf.format("# of games"))
-  xlgraph.write(ltitle.format(sitename))
-  xlgraph.write(ctitle.format(game.name))
-  xlgraph.write(rtitle.format(timestr))
-  xlgraph.write('</svg>\n')
+  xlgraph.write(titles(sitename, game.name, timestr))
   xlgraph.close()
   return
 
-def mkscoregraph(game, scoreblocks, scorecounts):
+def mkscoregraph(game, scoredata):
   if isinstance(game, rlgall.ARogueGame):
     scorewidth = 1500
   else:
     scorewidth = 1000
-  scoregraph = open("{0}/score-{1}.svg".format(svgpath, game.uname), "w")
-  scoregraph.write(dochead)
-  scoregraph.write(stylesheet.format("ffff00"))
+  scoregraph = SVGChart("{0}/score-{1}.svg".format(svgpath, game.uname))
+  scoregraph.style("ffff00")
   scoregraph.write(framerect)
-  scoredivs, scoremax = ylimits(max(scorecounts))
+  scoredivs, scoremax = ylimits(max([ pt[1] for pt in scoredata ]))
   scale = 500 / scoremax
-  for block, count in zip(scoreblocks, scorecounts):
-    barx = block * 75 + 100
+  for block, count in scoredata:
+    n = block // scorewidth
+    barx = n * 75 + 100
     barh = round(scale * count)
     bary = 550 - barh
     scoregraph.write(barstr.format(75, barh, barx, bary))
-    scoregraph.write(xllabel.format(barx, block * scorewidth))
+    scoregraph.write(xllabel.format(barx, block))
   for yl in range(scoredivs + 1):
     labeln = int(scoremax * yl / scoredivs)
     labelh = 550 + 8 - 500 * yl / scoredivs
     scoregraph.write(ylabel.format(labelh, labeln))
   scoregraph.write(xlabelf.format("Score"))
   scoregraph.write(ylabelf.format("# of games"))
-  scoregraph.write(ltitle.format(sitename))
-  scoregraph.write(ctitle.format(game.name))
-  scoregraph.write(rtitle.format(timestr))
-  scoregraph.write('</svg>\n')
+  scoregraph.write(titles(sitename, game.name, timestr))
   scoregraph.close()
   return
 
-def mkdeepgraph(game, deeps, deepcounts):
-  deepgraph = open("{0}/deep-{1}.svg".format(svgpath, game.uname), "w")
-  deepgraph.write(dochead)
-  deepgraph.write(stylesheet.format("808000"))
+def mkdeepgraph(game, deepdata):
+  deepgraph = SVGChart("{0}/deep-{1}.svg".format(svgpath, game.uname))
+  deepgraph.style("808000")
   deepgraph.write(framerect)
-  deepdivs, deepmax = ylimits(max(deepcounts))
+  deepdivs, deepmax = ylimits(max([ pt[1] for pt in deepdata ]))
   scale = 500 / deepmax
-  for lev, count in zip(deeps, deepcounts):
+  for lev, count in deepdata:
     barx = lev * 25 + 75
     barh = round(scale * count)
     bary = 550 - barh
@@ -147,48 +154,20 @@
     deepgraph.write(ylabel.format(labelh, labeln))
   deepgraph.write(xlabelf.format("Deepest dungeon level"))
   deepgraph.write(ylabelf.format("# of games"))
-  deepgraph.write(ltitle.format(sitename))
-  deepgraph.write(ctitle.format(game.name))
-  deepgraph.write(rtitle.format(timestr))
-  deepgraph.write('</svg>\n')
+  deepgraph.write(titles(sitename, game.name, timestr))
   deepgraph.close()
   return
 
-con = psycopg2.connect("dbname=rlg")
-cur = con.cursor()
-
 for game in rlgall.gamelist:
-  xlq = "SELECT count(*) FROM {0} WHERE xl = %s;".format(game.uname)
-  scrq = "SELECT count(*) FROM {0} WHERE score >= %s AND score < %s;".format(game.uname)
-  deepq = "SELECT count(*) FROM {0} WHERE maxdepth = %s;".format(game.uname)
-  xls = range(1, 16)
-  scoreblocks = range(10)
-  deeps = range(1, 31)
-  xlcounts = []
-  scorecounts = []
-  deepcounts = []
+  xldata = game.getXLCounts(15)
+  deepdata = game.getDepthCounts(30)
   if isinstance(game, rlgall.ARogueGame):
     scorewidth = 1500
   else:
     scorewidth = 1000
-  for xl in xls:
-    cur.execute(xlq, [xl])
-    xlcounts.append(cur.fetchone()[0])
-  for sn in scoreblocks:
-    lscore = sn * scorewidth
-    if sn == 9:
-      hscore = scorewidth * 32
-    else:
-      hscore = (sn + 1) * scorewidth
-    cur.execute(scrq, (lscore, hscore))
-    scorecounts.append(cur.fetchone()[0])
-  for lev in deeps:
-    cur.execute(deepq, [lev])
-    deepcounts.append(cur.fetchone()[0])
-  mkxlgraph(game, xls, xlcounts)
-  mkscoregraph(game, scoreblocks, scorecounts)
-  mkdeepgraph(game, deeps, deepcounts)
+  scoredata = game.getScoreCounts(10, scorewidth)
+  mkxlgraph(game, xldata)
+  mkscoregraph(game, scoredata)
+  mkdeepgraph(game, deepdata)
 
-cur.close()
-con.close()
 exit()