diff options
author | Drew DeVault <sir@cmpwn.com> | 2018-11-24 14:23:43 -0500 |
---|---|---|
committer | Drew DeVault <sir@cmpwn.com> | 2018-11-24 14:23:43 -0500 |
commit | 31c9eff0540cbd7e0ee727bfcb4118e9ffff0afa (patch) | |
tree | c5c78919ba7a3aebb859d7f6d1e7abf05027b339 | |
parent | e95fbad612e6041dbde5ff1232bc2752f2de4b62 (diff) |
Tweaks to contributors page
-rw-r--r-- | gitsrht/app.py | 2 | ||||
-rw-r--r-- | gitsrht/blueprints/repo.py | 90 | ||||
-rw-r--r-- | gitsrht/blueprints/stats.py | 81 | ||||
-rw-r--r-- | gitsrht/repos.py | 21 | ||||
-rw-r--r-- | gitsrht/templates/contributors.html | 21 | ||||
-rw-r--r-- | gitsrht/templates/repo.html | 2 | ||||
-rwxr-xr-x | setup.py | 1 |
7 files changed, 118 insertions, 100 deletions
diff --git a/gitsrht/app.py b/gitsrht/app.py index 491801b..9c6a3a9 100644 --- a/gitsrht/app.py +++ b/gitsrht/app.py @@ -29,11 +29,13 @@ class GitApp(SrhtFlask): from gitsrht.blueprints.api import api from gitsrht.blueprints.public import public from gitsrht.blueprints.repo import repo + from gitsrht.blueprints.stats import stats from gitsrht.blueprints.manage import manage self.register_blueprint(api) self.register_blueprint(public) self.register_blueprint(repo) + self.register_blueprint(stats) self.register_blueprint(manage) meta_client_id = cfg("git.sr.ht", "oauth-client-id") diff --git a/gitsrht/blueprints/repo.py b/gitsrht/blueprints/repo.py index 98e044c..2648b2b 100644 --- a/gitsrht/blueprints/repo.py +++ b/gitsrht/blueprints/repo.py @@ -16,8 +16,9 @@ from gitsrht.editorconfig import EditorConfig from gitsrht.redis import redis from gitsrht.git import Repository as GitRepository, commit_time, annotate_tree from gitsrht.git import diffstat -from gitsrht.types import User, Repository, Redirect +from gitsrht.repos import get_repo_or_redir from gitsrht.rss import generate_feed +from gitsrht.types import User, Repository, Redirect from io import BytesIO from pygments import highlight from pygments.lexers import guess_lexer, guess_lexer_for_filename, TextLexer @@ -94,23 +95,6 @@ def _highlight_file(name, data, blob_id): redis.setex(key, html, timedelta(days=7)) return Markup(html) -def get_repo_or_redir(owner, repo): - owner, repo = get_repo(owner, repo) - if not repo: - abort(404) - if not has_access(repo, UserAccess.read): - abort(401) - if isinstance(repo, Redirect): - view_args = request.view_args - if not "repo" in view_args or not "owner" in view_args: - return redirect(url_for(".summary", - owner=repo.new_repo.owner.canonical_name, - repo=repo.new_repo.name)) - view_args["owner"] = repo.new_repo.owner.canonical_name - view_args["repo"] = repo.new_repo.name - abort(redirect(url_for(request.endpoint, **view_args))) - return owner, repo - def get_last_3_commits(commit): commits = [commit] for parent in commit.parents: @@ -471,73 +455,3 @@ def ref(owner, repo, ref): abort(404) return render_template("ref.html", view="refs", owner=owner, repo=repo, git_repo=git_repo, tag=tag) - -def _week(time): - """Used to group contributions by week""" - return time.strftime('%Y/%W') - -@lru_cache(maxsize=256) -def _user(email): - """Used to grouped contributions by either username or email.""" - email = email.lower() - user = User.query.filter_by(email=email).one_or_none() - return (None, user.username) if user else (email, None) - -def get_contributions(git_repo, tip, since): - contributions = defaultdict(lambda: { - "commits": 0, - "weekly": defaultdict(lambda: 0) - }) - - since_ts = since.timestamp() - for commit in git_repo.walk(tip.id, pygit2.GIT_SORT_TIME): - timestamp = commit.commit_time + commit.commit_time_offset - if timestamp < since_ts: - break - - week = _week(datetime.fromtimestamp(timestamp)) - user = _user(commit.author.email) - contributions[user]['commits'] += 1 - contributions[user]['weekly'][week] += 1 - - return contributions - -def get_contrib_chart_data(contributions): - # Max number of commits by a contributor in a single week - max_commits = max( - max(commits for _, commits in data['weekly'].items()) - for _, data in contributions.items()) - - all_weeks = [_week(date.today() - timedelta(weeks=51 - n)) - for n in range(52)] - - def bars(contributions): - bars = list() - for ordinal, week in enumerate(all_weeks): - if week in contributions: - week_commits = contributions[week] - bars.append({ - "ordinal": ordinal, - "week": week, - "commits": week_commits, - "height": 100 * week_commits // max_commits - }) - return bars - - chart_data = [(email, username, data['commits'], bars(data['weekly'])) - for (email, username), data in contributions.items()] - return sorted(chart_data, key=lambda x: x[2], reverse=True) - -@repo.route("/<owner>/<repo>/contributors") -def contributors(owner, repo): - owner, repo = get_repo_or_redir(owner, repo) - since = datetime.now() - timedelta(weeks=52) - - with GitRepository(repo.path) as git_repo: - default_branch = git_repo.default_branch() - tip = git_repo.get(default_branch.target) - contributions = get_contributions(git_repo, tip, since) - chart_data = get_contrib_chart_data(contributions) - - return render_template("contributors.html", view="contributors", - owner=owner, repo=repo, chart_data=chart_data) diff --git a/gitsrht/blueprints/stats.py b/gitsrht/blueprints/stats.py new file mode 100644 index 0000000..e9315f5 --- /dev/null +++ b/gitsrht/blueprints/stats.py @@ -0,0 +1,81 @@ +import pygit2 +from collections import defaultdict +from datetime import date, datetime, timedelta +from flask import Blueprint, render_template +from functools import lru_cache +from gitsrht.git import Repository as GitRepository +from gitsrht.repos import get_repo_or_redir +from gitsrht.types import User + +stats = Blueprint('stats', __name__) + +def _week(time): + """Used to group contributions by week""" + return time.strftime('%Y/%W') + +@lru_cache(maxsize=256) +def _user(email, name): + """Used to grouped contributions by either username or email.""" + email = email.lower() + user = User.query.filter_by(email=email).one_or_none() + return (None, name, user.username) if user else (email, name, None) + +def get_contributions(git_repo, tip, since): + contributions = defaultdict(lambda: { + "commits": 0, + "weekly": defaultdict(lambda: 0) + }) + + since_ts = since.timestamp() + for commit in git_repo.walk(tip.id, pygit2.GIT_SORT_TIME): + timestamp = commit.commit_time + commit.commit_time_offset + if timestamp < since_ts: + break + + week = _week(datetime.fromtimestamp(timestamp)) + user = _user(commit.author.email, commit.author.name) + contributions[user]['commits'] += 1 + contributions[user]['weekly'][week] += 1 + + return contributions + +def get_contrib_chart_data(contributions): + # Max number of commits by a contributor in a single week + max_commits = max( + max(commits for _, commits in data['weekly'].items()) + for _, data in contributions.items()) + + all_weeks = [_week(date.today() - timedelta(weeks=51 - n)) + for n in range(52)] + + def bars(contributions): + bars = list() + for ordinal, week in enumerate(all_weeks): + if week in contributions: + week_commits = contributions[week] + bars.append({ + "ordinal": ordinal, + "week": week, + "commits": week_commits, + "height": 100 * week_commits // max_commits + }) + return bars + + chart_data = [ + (email, full_name, username, data['commits'], bars(data['weekly'])) + for (email, full_name, username), data in contributions.items()] + return sorted(chart_data, key=lambda x: x[3], reverse=True) + +@stats.route("/<owner>/<repo>/contributors") +def contributors(owner, repo): + owner, repo = get_repo_or_redir(owner, repo) + since = datetime.now() - timedelta(weeks=52) + + with GitRepository(repo.path) as git_repo: + default_branch = git_repo.default_branch() + tip = git_repo.get(default_branch.target) + contributions = get_contributions(git_repo, tip, since) + chart_data = get_contrib_chart_data(contributions) + + return render_template("contributors.html", view="contributors", + owner=owner, repo=repo, chart_data=chart_data) diff --git a/gitsrht/repos.py b/gitsrht/repos.py index 8e617e2..2749e10 100644 --- a/gitsrht/repos.py +++ b/gitsrht/repos.py @@ -1,7 +1,9 @@ import subprocess +from flask import redirect, abort, url_for +from gitsrht.access import get_repo, has_access, UserAccess +from gitsrht.types import User, Repository, RepoVisibility, Redirect from srht.database import db from srht.config import cfg -from gitsrht.types import Repository, RepoVisibility, Redirect import shutil import re import os @@ -94,3 +96,20 @@ def delete_repo(repo): pass db.session.delete(repo) db.session.commit() + +def get_repo_or_redir(owner, repo): + owner, repo = get_repo(owner, repo) + if not repo: + abort(404) + if not has_access(repo, UserAccess.read): + abort(401) + if isinstance(repo, Redirect): + view_args = request.view_args + if not "repo" in view_args or not "owner" in view_args: + return redirect(url_for(".summary", + owner=repo.new_repo.owner.canonical_name, + repo=repo.new_repo.name)) + view_args["owner"] = repo.new_repo.owner.canonical_name + view_args["repo"] = repo.new_repo.name + abort(redirect(url_for(request.endpoint, **view_args))) + return owner, repo diff --git a/gitsrht/templates/contributors.html b/gitsrht/templates/contributors.html index 4d0d43d..be71d93 100644 --- a/gitsrht/templates/contributors.html +++ b/gitsrht/templates/contributors.html @@ -10,17 +10,19 @@ {% block head %} <style type="text/css"> svg { background-color: WhiteSmoke } - rect { fill: #007bff } + rect { fill: #116bdd } rect:hover { fill: #dc3545 } </style> {% endblock %} {% block content %} <div class="container"> - <p>Contributions in the last 52 weeks</p> - + <p> + Contributions to {{repo.owner.canonical_name}}/{{repo.name}} + in the last 52 weeks: + </p> <div class="row"> - {% for email, username, commits, bars in chart_data %} + {% for email, full_name, username, commits, bars in chart_data %} <div class="col-md-6"> <h3> {% if username %} @@ -28,8 +30,9 @@ ~{{ username }} </a> {% else %} - {{ email }} + {{ full_name }} {% endif %} + <small>{{ commits }} commits</small> </h3> <svg @@ -42,15 +45,15 @@ <rect x="{{ bar.ordinal * 10 }}" y="{{ 100 - bar.height }}%" - width="10" + width="11" height="{{ bar.height }}%" > - <title>{{ bar.commits }} commits in week {{ bar.week }}</title> + <title> + {{ bar.commits }} commits in the week of {{ bar.week }} + </title> </rect> {% endfor %} </svg> - - <p>Commits: {{ commits }}</p> </div> {% endfor %} </div> diff --git a/gitsrht/templates/repo.html b/gitsrht/templates/repo.html index c0cba9d..1d1fbde 100644 --- a/gitsrht/templates/repo.html +++ b/gitsrht/templates/repo.html @@ -36,7 +36,7 @@ repo=repo.name), "refs")}} </li> <li class="nav-item"> - {{link(url_for("repo.contributors", + {{link(url_for("stats.contributors", owner=repo.owner.canonical_name, repo=repo.name), "contributors")}} </li> @@ -1,7 +1,6 @@ #!/usr/bin/env python3 from setuptools import setup import subprocess -import glob import os import site import sys |