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('<','<') return s1.replace('>','>') -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),