Enhace CSS. Add activity chart for pygit.

portnov [2008-06-10 12:44:04]
Enhace CSS. Add activity chart for pygit.
Filename
media/css/main.css
mgmt/models.py
pygit/chart.py
pygit/pygit.py
pygit/views.py
settings.py
templates/all_bugs.html
templates/base.html
templates/bug_body.html
templates/comments.html
templates/document.html
templates/git_blame.html
templates/git_diff.html
templates/project_body.html
templates/repo.html
urls.py
diff --git a/media/css/main.css b/media/css/main.css
index 255728c..02daebf 100644
--- a/media/css/main.css
+++ b/media/css/main.css
@@ -1,6 +1,21 @@

 body {
   font-family: Helvetica,Arial,sans-serif;
+  padding: 0;
+  margin: 0;
+}
+
+a {
+  text-decoration: none;
+  color: #091;
+}
+
+a:visited {
+  color: #060;
+}
+
+a:hover {
+  color: #0b0;
 }

 td {
@@ -13,33 +28,71 @@ tr:hover {
   background: #eee;
 }

+pre {
+  border: 1px #888 solid;
+  background: #fafafa;
+  overflow: hidden;
+  padding: 0.5ex;
+}
+
+h1 {
+  margin: 0;
+  padding: 0.5ex;
+}
+
 #body {
-  padding: 1ex;
+  padding: 0;
 }

 #header {
-  padding: 1ex;
   border-bottom: 1px #888 solid;
 }

+#login-message {
+  padding: 1ex;
+}
+
 #main {
   margin-left: 13em;
-  padding-left: 1em;
+  padding: 1em;
+  border-left: 1px #888 solid;
+}
+
+#blocks {
+  width: 12em;
+  float: left;
+/*   border-right: 1px #888 solid;*/
+  border-bottom: 1px #888 solid;
+  padding: 1ex;
+  margin-right: 1ex;
 }

 ul.menu {
   float: right;
   text-align: right;
   display: inline;
+  margin: 0;
+  border-top: 1px #888 solid;
+  padding: 0.5ex 1em;
 }

 ul.menu li {
   list-style: none;
   display: inline;
+  padding-left: 1em;
+}
+
+ul.menu a {
 }

 .links {
+  float: right;
   text-align: right;
+  font-size: small;
+}
+
+.links a {
+  padding-left: 1em;
 }

 .project-link {
@@ -47,18 +100,13 @@ ul.menu li {
   text-align: right;
 }

-.project {
-  border-bottom: 1px #888 solid;
-  margin-bottom: 2ex;
+.project-link a {
 }

-#blocks {
-  width: 12em;
-  float: left;
-  border-right: 1px #888 solid;
+.project {
   border-bottom: 1px #888 solid;
-  padding: 1ex;
-  margin-right: 1ex;
+  margin-bottom: 2ex;
+  overflow: hidden;
 }

 .block-last_comments ul {
@@ -108,6 +156,12 @@ p.date {
   background: #ccc;
 }

+.git-tags {
+  border-left: 1px #888 solid;
+  float: right;
+  padding: 1em;
+}
+
 .diff-add {
   color: blue;
 }
diff --git a/mgmt/models.py b/mgmt/models.py
index 9dbc094..2f0425c 100644
--- a/mgmt/models.py
+++ b/mgmt/models.py
@@ -1,5 +1,8 @@
+from os.path import dirname
+
 from django.db import models
 from django.contrib.auth.models import User
+from django.utils.translation import gettext as _

 class Object(models.Model):
   name = models.CharField(max_length=64)
@@ -17,6 +20,8 @@ class Project(Object):
   def __unicode__(self):
     return self.name
   class Meta:
+    verbose_name = _("Project")
+    verbose_name_plural = _("Projects")
     ordering = ("-created",)
   class Admin:
     pass
@@ -25,6 +30,9 @@ class BugState(models.Model):
   name = models.CharField(max_length=64)
   def __unicode__(self):
     return self.name
+  class Meta:
+    verbose_name = _("Bug state")
+    verbose_name_plural = _("Bug states")
   class Admin:
     pass

@@ -36,6 +44,8 @@ class Bug(Object):
   responsible = models.ForeignKey(User,related_name='resp_bugs',null=True)
   status = models.ForeignKey(BugState)
   class Meta:
+    verbose_name = _("Bug")
+    verbose_name_plural = _("Bugs")
     ordering = ("-created",)
   class Admin:
     pass
@@ -44,6 +54,8 @@ class Document(Object):
   project = models.ForeignKey(Project)
   author = models.ForeignKey(User)
   class Meta:
+    verbose_name = _("Document")
+    verbose_name_plural = _("Documents")
     ordering = ("-created",)
   class Admin:
     pass
@@ -60,4 +72,6 @@ class Comment(models.Model):
   def __unicode__(self):
     return self.title
   class Meta:
+    verbose_name = _("Comment")
+    verbose_name_plural = _("Comments")
     ordering = ("created",)
diff --git a/pygit/chart.py b/pygit/chart.py
new file mode 100644
index 0000000..df1e648
--- /dev/null
+++ b/pygit/chart.py
@@ -0,0 +1,51 @@
+
+from os.path import exists
+import cairo
+
+width,height = 600,100
+pl,pr,py = 10,50,17
+root = "/home/portnov/www/projects/"
+
+def chart_path(data):
+  return "/media/charts/chart_%s.png" % hash(tuple(data))
+
+def make_chart(data,out_path):
+  surf = cairo.ImageSurface(cairo.FORMAT_ARGB32,width,height)
+  cx = cairo.Context(surf)
+  y = [d[1] for d in data]
+  M = max(y)
+  n = len(data)-1
+  scale_y = float(height-2*py)/M
+  step_x = float(width-pl-pr)/n
+  cx.set_source_rgb(0.9,0.9,0.5)
+  cx.new_path()
+  cx.move_to(pl,height-py)
+  cur_x = pl
+  for d in y:
+    cx.line_to(int(cur_x),int(height-d*scale_y))
+    cur_x += step_x
+  cx.line_to(width-pr,height-py)
+  cx.close_path()
+  cx.fill()
+  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(data)):
+    label = data[i][0]
+    cx.move_to(cur_x,int(height-py/2))
+    cx.show_text(label)
+    cur_x += step_x
+  surf.write_to_png(out_path)
+
+def chart(data):
+  path = chart_path(data)
+  if not exists(root+path):
+    make_chart(data,root+path)
+  return "<img src='%s'/>" % path
diff --git a/pygit/pygit.py b/pygit/pygit.py
index 4dc8097..c1db846 100644
--- a/pygit/pygit.py
+++ b/pygit/pygit.py
@@ -35,6 +35,10 @@ def branches(rid):
   r = open_repo(rid)
   return [b.name for b in r.branches]

+def tags(rid):
+  r = open_repo(rid)
+  return [t.name for t in r.tags]
+
 def date(tp):
   y,m,d,h,M,s,_,_,_ = tp
   return "%d.%d.%d %d:%d:%d" % (d,m,y,h,M,s)
@@ -87,3 +91,25 @@ def diff_path(rid,path,cid1,cid2=None):
   ds = Commit.diff(r,c2,c1)
   return [(d.a_path,d.diff) for d in ds if d.a_path==path]

+def uniq(lst):
+  r = []
+  count = 0
+  lst.sort()
+  prev = lst[0]
+  for item in lst:
+    if item == prev:
+      count += 1
+    else:
+      r.append((prev,count))
+      count = 1
+    prev = item
+  r.append((prev,count))
+  return r
+
+def commits_stats(rid):
+  def date(tp):
+    y,m,d = tp
+    return "%d.%d" % (d,m)
+  r = open_repo(rid)
+  d = [c.authored_date[:3] for c in r.commits()][:30]
+  return [(date(t[0]),t[1]) for t in uniq(d)]
diff --git a/pygit/views.py b/pygit/views.py
index 28c58dd..561e8e4 100644
--- a/pygit/views.py
+++ b/pygit/views.py
@@ -6,6 +6,7 @@ from os.path import dirname,join
 # from django.template.defaultfilters import escape

 import pygit
+from chart import chart

 sys.path.append(dirname(dirname(__file__)))
 from mgmt.views import render_it
@@ -25,15 +26,21 @@ def all_repos(request):
       request)

 def one_repo(request,rid,branch='master'):
+  if not branch:
+    branch = 'master'
   dsc = pygit.description(rid)
   brs = pygit.branches(rid)
+  tags = pygit.tags(rid)
   cs  = pygit.commits(rid,branch)
   dirs,files = pygit.root_tree(rid,branch)
+  cht = chart(pygit.commits_stats(rid))
   return render_it('repo.html',
       {'description': dsc,
        'rid': rid,
        'current_branch': branch,
        'branches': brs,
+       'tags': tags,
+       'activity_chart': cht,
        'commits': cs,
        'dirs': dirs,
        'files': files},
@@ -55,6 +62,7 @@ def blame(request,rid,branch,path):
   bl = pygit.blame(rid,path,branch)
   return render_it('git_blame.html',
       {'repo': repo,
+       'branch': branch,
        'path': path,
        'blame': bl},
       request)
@@ -63,7 +71,7 @@ def escape(s):
   s1 = s.replace('<','&lt;')
   return s1.replace('>','&gt;')

-def diff(request,rid,cid,path=None):
+def diff(request,rid,branch,cid,path=None):
   def format_diff(diff):
     data = diff[1]
     def format_line(line):
@@ -91,6 +99,7 @@ def diff(request,rid,cid,path=None):
   ds = map(format_diff,ds)
   return render_it('git_diff.html',
     {'repo': repo,
+     'branch': branch,
      'path': path,
      'cid': cid,
      'diffs': ds},
diff --git a/settings.py b/settings.py
index 7d388a4..b5e7bff 100644
--- a/settings.py
+++ b/settings.py
@@ -2,6 +2,7 @@

 DEBUG = True
 TEMPLATE_DEBUG = DEBUG
+USE_I18N = True

 ADMINS = (
     # ('Your Name', 'your_email@domain.com'),
@@ -23,7 +24,8 @@ TIME_ZONE = 'America/Chicago'
 # Language code for this installation. All choices can be found here:
 # http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
 # http://blogs.law.harvard.edu/tech/stories/storyReader$15
-LANGUAGE_CODE = 'ru-ru'
+LANGUAGE_CODE = 'ru'
+LANGUAGES = ('ru','Russian')

 SITE_ID = 1

diff --git a/templates/all_bugs.html b/templates/all_bugs.html
index 9b07be2..f2885be 100644
--- a/templates/all_bugs.html
+++ b/templates/all_bugs.html
@@ -10,8 +10,11 @@
 <div class='bugs'>
 {% for bug in bugs %}
     <div class='bug bug-state-{{bug.status.id}}'>
-      <p><strong>{{bug.name}}</strong></p>
+      <h3><a href='/bugs/{{bug.id}}/'>{{bug.name}}</a></h3>
       <div class='project-link'>{{bug.project|link|safe}}</div>
+        {% if edit_link %}
+          <div class='links'><a href='{{edit_link}}'>Изменить</a></div>
+        {% endif %}
       <p>{{bug.text}}</p>
     </div>
 {% endfor %}
diff --git a/templates/base.html b/templates/base.html
index 40408c8..44c5d6e 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -15,7 +15,7 @@
   <div id='body'>
     <div id='header'>
       {%block header%}
-      {{login_message}}
+      <h1>Управление проектами</h1>
       {% if menu %}
         <ul class='menu'>
         {% for item in menu %}
@@ -23,6 +23,9 @@
         {% endfor %}
         </ul>
       {% endif %}
+      <div id='login-message'>
+        {{login_message}}
+      </div>

       {%endblock%}
     </div>
diff --git a/templates/bug_body.html b/templates/bug_body.html
index ce312f6..5c8fc89 100644
--- a/templates/bug_body.html
+++ b/templates/bug_body.html
@@ -1,9 +1,9 @@
     {% load prj_filters %}
       <div class='bug bug-state-{{bug.status.id}}'>
+        <div class='project-link'>{{project|link|safe}}</div>
         <h3><a href='/bugs/{{bug.id}}/'>Баг #{{bug.id}}: {{bug.name}}</a></h3>
         {% if edit_link %}
           <div class='links'><a href='{{edit_link}}'>Изменить</a></div>
         {% endif %}
-        <div class='project-link'>{{project|link|safe}}</div>
         <p>{{bug.text}}</p>
       </div>
diff --git a/templates/comments.html b/templates/comments.html
index 3408519..d9297fb 100644
--- a/templates/comments.html
+++ b/templates/comments.html
@@ -3,8 +3,9 @@
   <div class='comments'>
     {% for comment in comments %}
       <div class='comment'>
-        <a name='comment-{{comment.id}}' />
-        <p class='comment-title'>{{ comment.title }} от {{ comment.author }}, {{ comment.created }}</p>
+        <p class='comment-title'>
+          <a name='comment-{{comment.id}}'>{{ comment.title }} от {{ comment.author }}, {{ comment.created }}</a>
+        </p>
         <p>{{ comment.text }}</p>
       </div>
     {% endfor %}
diff --git a/templates/document.html b/templates/document.html
index 9fdd49b..7f39bf6 100644
--- a/templates/document.html
+++ b/templates/document.html
@@ -4,12 +4,12 @@

 {% block main %}

-<h2>{{document.name}}</h2>
 {% if edit_link %}
   <div class='links'>
     <a href='{{edit_link}}'>Изменить</a>
   </div>
 {% endif %}
+<h2>{{document.name}}</h2>

 <p>Проект: <a href='/projects/{{document.project.id}}/'>{{document.project.name}}</a></p>
 <div class='document'>
diff --git a/templates/git_blame.html b/templates/git_blame.html
index f48dbaf..b434271 100644
--- a/templates/git_blame.html
+++ b/templates/git_blame.html
@@ -8,7 +8,7 @@
 <table>
   {% for cid,author,dt,message,line in blame %}
   <tr>
-    <td><a href='/git/diff/{{repo.rid}}/{{cid}}/{{path}}/'>{{cid}}</a></td>
+    <td><a href='/git/diff/{{repo.rid}}/{{branch}}/{{cid}}/{{path}}/'>{{cid}}</a></td>
     <td>{{author}}</td><td>{{dt}}</td><td>{{message}}</td><td>{{forloop.counter}}</td><td>{{line}}</td>
   </tr>
   {% endfor %}
diff --git a/templates/git_diff.html b/templates/git_diff.html
index 55c1c5a..71ff04e 100644
--- a/templates/git_diff.html
+++ b/templates/git_diff.html
@@ -7,7 +7,7 @@
 <h2>Diff: #{{cid}}{% if path %} {{path}}{% endif %}</h2>
 {% autoescape off %}
 {% for file,diff in diffs %}
-  <h3>{{file}}</h3>
+  <h3><a href='/git/blame/{{repo.rid}}/{{branch}}/{{file}}/'>{{file}}</a></h3>
   <code><pre>
   {{diff}}
   </pre></code>
diff --git a/templates/project_body.html b/templates/project_body.html
index 392b640..65cf4a2 100644
--- a/templates/project_body.html
+++ b/templates/project_body.html
@@ -3,7 +3,6 @@
       <p><strong><a href='/projects/{{ project.id }}'>{{ project.name }}</a></strong></p>
       <p><strong>Репозиторий: </strong> {{ project.repo }}</p>
       <p>{{ project.text }}</p>
-      <p class='date'>{{ project.created }}</p>
       <p class='links'>
         <a href='/projects/{{project.id}}/docs/'>Документация</a>
         <a href='/projects/{{project.id}}/bugs/'>Баги</a>
diff --git a/templates/repo.html b/templates/repo.html
index 5825391..7fbb919 100644
--- a/templates/repo.html
+++ b/templates/repo.html
@@ -13,16 +13,30 @@
   {% endfor %}
 </div>

+<h3>Активность:</h3>
+{{activity_chart|safe}}
+
 <h3>Последние коммиты:</h3>

 <table>
   {% for id,dt,author,message in commits %}
     <tr>
-      <td><a href='/git/diff/{{rid}}/{{id}}/'>{{id}}</a></td><td>{{dt}}</td><td>{{author}}</td><td>{{message}}</td>
+      <td><a href='/git/diff/{{rid}}/{{current_branch}}/{{id}}/'>{{id}}</a></td><td>{{dt}}</td><td>{{author}}</td><td>{{message}}</td>
     </tr>
   {% endfor %}
 </table>

+{% if tags %}
+<div class='git-tags'>
+  <h3>Теги:</h3>
+  <ul>
+  {% for tag in tags %}
+    <li><a href='/git/diff/{{rid}}/{{current_branch}}/{{tag}}/'>{{tag}}</a></li>
+  {% endfor %}
+  </ul>
+</div>
+{% endif %}
+
 <h3>Файлы:</h3>

 <ul>
diff --git a/urls.py b/urls.py
index 360867a..b914f8f 100644
--- a/urls.py
+++ b/urls.py
@@ -17,8 +17,8 @@ urlpatterns = patterns('',
     (r'^git/repo/(\d+)/(\w+)/$', 'pygit.views.one_repo'),
     (r'^git/repo/(\d+)/(\w+)/tree/(\w+)/(.*)/$', 'pygit.views.tree'),
     (r'^git/blame/(\d+)/(\w+)/(.*)/$','pygit.views.blame'),
-    (r'^git/diff/(\d+)/(\w+)/$', 'pygit.views.diff'),
-    (r'^git/diff/(\d+)/(\w+)/(.*)/$', 'pygit.views.diff'),
+    (r'^git/diff/(\d+)/(\w+)/(\w+)/$', 'pygit.views.diff'),
+    (r'^git/diff/(\d+)/(\w+)/(\w+)/(.*)/$', 'pygit.views.diff'),

     (r'^login/', login),
     (r'^logout/', logout),
ViewGit