diff py/stats2.py @ 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 3ecbd4fa2a08
children 5cf88bd4e556
line wrap: on
line diff
--- 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()