Rewrite charts api, make activity chart more smart.

portnov [2008-06-12 12:28:32]
Rewrite charts api, make activity chart more smart.
Filename
pygit/chart.py
pygit/chart_test.py
pygit/views.py
diff --git a/pygit/chart.py b/pygit/chart.py
index 8abff8a..022900a 100644
--- a/pygit/chart.py
+++ b/pygit/chart.py
@@ -4,57 +4,90 @@ import cairo

 from django.conf import settings

-width,height = 900,150
 pl,pr,py = 10,30,17

 def chart_path(data):
-  return "/media/charts/chart_%s.png" % hash(data)
-
-def make_one_chart(y,color,cx,step_x,scale_y):
-  cx.set_source_rgb(*color)
-  cx.new_path()
-  cx.move_to(pl,int(height-y[0]*scale_y-py))
-  cur_x = pl
-  for d in y:
-    cx.line_to(int(cur_x),int(height-d*scale_y-py))
-    cur_x += step_x
-  cx.stroke()
-
-def make_chart(labels,dataset,colors,out_path):
-  n = len(dataset[0])-1
-  step_x = float(width-pl-pr)/n
-  surf = cairo.ImageSurface(cairo.FORMAT_ARGB32,width,height)
-  cx = cairo.Context(surf)
-  M = max(map(max,dataset))
-
-  for i in range(len(dataset)):
-    data = dataset[i]
-    color = colors[i]
-    scale_y = float(height-2*py)/M
-    make_one_chart(data,color,cx,step_x,scale_y)
-
-  cx.set_source_rgb(0.9,0.9,0.9)
-  cx.set_line_width(1)
-  cur_x = pl
-  for i in range(len(data)):
-    cx.move_to(cur_x,height-py)
-    cx.line_to(cur_x,py)
-    cx.stroke()
-    cur_x += step_x
-  cx.set_source_rgb(0,0,0)
-  cur_x = pl
-  for i in range(len(dataset[0])):
-    label = labels[i]
-    cx.move_to(cur_x,int(height-py/2))
-    cx.show_text(label)
-    cur_x += step_x
-  cx.move_to(pl,height-py)
-  cx.line_to(width-pr,height-py)
-  cx.stroke()
-  surf.write_to_png(out_path)
-
-def chart(labels,dataset):
-  path = chart_path((tuple(labels),tuple(map(tuple,dataset))))
-  if not exists(settings.ROOT+path):
-    make_chart(labels,dataset,settings.COLORS,settings.ROOT+path)
-  return "<img src='%s'/>" % path
+  return
+
+class Place(object):
+  def __init__(self,cx,x0,y0,width,height):
+    self.cx = cx
+    self.x0 = x0
+    self.y0 = y0
+    self.width = width
+    self.height = height
+    self.x_ticks = None
+
+  def set_scale(self,x_ticks,y_max):
+    self.x_ticks = x_ticks
+    self.y_max = y_max
+    self.step_x = float(self.width)/x_ticks
+    self.scale_y = float(self.height)/float(self.y_max)
+
+  def translate(self,x,y):
+    return int(self.x0+x*self.step_x), int(self.y0-y*self.scale_y)
+
+  def move_to(self,x,y):
+    self.cx.move_to(*self.translate(x,y))
+
+  def line_to(self,x,y):
+    self.cx.line_to(*self.translate(x,y))
+
+class Chart(object):
+  def __init__(self,width,height,n=1):
+    self.n_places = n
+    self.width = width
+    self.height = height
+    self.surf = cairo.ImageSurface(cairo.FORMAT_ARGB32,width,height)
+    self.cx = cairo.Context(self.surf)
+    self.hash = 0
+    place_height = float(self.height)/n
+    self.places = []
+    self.cx.set_source_rgb(1,1,1)
+    self.cx.rectangle(0,0,width,height)
+    self.cx.fill()
+    for i in range(n):
+      place = Place(self.cx,pl,height-place_height*i-py, width-pl-pr, place_height-2*py)
+      self.places.append(place)
+
+  def draw(self,lst,color=(0.9,0.2,0.2),place=0):
+    pc = self.places[place]
+    pc.set_scale(len(lst)-1,max(lst))
+    pc.cx.new_path()
+    pc.cx.set_source_rgb(*color)
+    pc.move_to(0,lst[0])
+    for i in range(len(lst)):
+      pc.line_to(i,lst[i])
+    pc.cx.stroke()
+    self.hash += hash(tuple(lst))
+
+  def labels(self,lst):
+    step_x = (self.width-pl-pr)/(len(lst)-1)
+    self.cx.set_source_rgb(0,0,0)
+    for pc in self.places:
+      pc.move_to(0,0)
+      pc.line_to(len(lst)-1,0)
+      pc.cx.stroke()
+    cur_x = pl
+    self.cx.set_source_rgb(0.9,0.9,0.9)
+    for i in range(len(lst)):
+      self.cx.move_to(cur_x, self.height-py)
+      self.cx.line_to(cur_x, py)
+      self.cx.stroke()
+      cur_x += step_x
+    cur_x = pl
+    self.cx.set_source_rgb(0,0,0)
+    for i in range(len(lst)):
+      label = lst[i]
+      self.cx.move_to(cur_x, int(self.height-py/2))
+      self.cx.show_text(label)
+      cur_x += step_x
+    self.hash += hash(tuple(lst))
+
+  def finish(self):
+    path = "/media/charts/chart_%s.png" % self.hash
+    root = "/home/portnov/www/projects"
+    if not exists(root+path):
+      self.surf.write_to_png(root+path)
+    print root+path
+    return "<img src='%s'/>" % path
diff --git a/pygit/chart_test.py b/pygit/chart_test.py
new file mode 100755
index 0000000..62d55a3
--- /dev/null
+++ b/pygit/chart_test.py
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+
+from chart import Chart
+c = Chart(900,150,n=2)
+c.draw([1,5,3,2,7],place=0)
+c.draw([2,4,3,5,6],place=0,color=(0.2,0.9,0.2))
+c.draw([1,3,7,2,1],place=1,color=(0.2,0.2,0.9))
+c.labels(['1','2','3','4','5'])
+c.finish()
+
diff --git a/pygit/views.py b/pygit/views.py
index 00dcf3b..0c2dfa5 100644
--- a/pygit/views.py
+++ b/pygit/views.py
@@ -7,7 +7,7 @@ from django.http import HttpResponseRedirect
 from django.core.urlresolvers import reverse

 import pygit
-from chart import chart
+from chart import Chart

 sys.path.append(dirname(dirname(__file__)))
 from mgmt.views import render_it
@@ -35,14 +35,19 @@ def one_repo(request,rid,branch='master'):
   cs  = pygit.commits(rid,branch)
   dirs,files = pygit.root_tree(rid,branch)
   dates,n_commits,files_s,lines = pygit.commits_stats(rid,branch)
-  cht = chart(dates,[n_commits,files_s,lines])
+  cht = Chart(900,200,n=2)
+  cht.draw(n_commits,place=0,color=(0.9,0.9,0.2))
+  cht.draw(files_s,place=1,color=(0.2,0.2,0.9))
+  cht.draw(lines,place=1,color=(0.2,0.9,0.2))
+  cht.labels(dates)
+  chart = cht.finish()
   return render_it('repo.html',
       {'description': dsc,
        'rid': rid,
        'current_branch': branch,
        'branches': brs,
        'tags': tags,
-       'activity_chart': cht,
+       'activity_chart': chart,
        'commits': cs,
        'dirs': dirs,
        'files': files},
ViewGit