@@ 1,30 1,15 @@
import pygit2
-from collections import defaultdict
-from datetime import date, datetime, timedelta
+from datetime import datetime, timedelta
from flask import Blueprint, render_template
-from functools import lru_cache
from gitsrht.git import Repository as GitRepository
from gitsrht.types import User
from scmsrht.access import get_repo_or_redir
+from scmsrht.stats import RepoContributions, get_contrib_chart_data
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)
- })
+ contributions = RepoContributions(User)
since_ts = since.timestamp()
for commit in git_repo.walk(tip.id, pygit2.GIT_SORT_TIME):
@@ 32,46 17,12 @@ def get_contributions(git_repo, tip, since):
if timestamp < since_ts:
break
- try:
- week = _week(datetime.fromtimestamp(timestamp))
- except:
- continue
- user = _user(commit.author.email, commit.author.name)
- contributions[user]['commits'] += 1
- contributions[user]['weekly'][week] += 1
+ user = contributions.get_or_create_user(
+ commit.author.email, commit.author.name)
+ user.add_commit(timestamp)
return contributions
-def get_contrib_chart_data(contributions):
- # Max number of commits by a contributor in a single week
- try:
- max_commits = max(
- max(commits for _, commits in data['weekly'].items())
- for _, data in contributions.items())
- except:
- max_commits = 0
-
- 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)
@@ 8,53 8,9 @@
{% endblock %}
{% block head %}
- <style type="text/css">
- svg.contributors { background-color: WhiteSmoke }
- rect { fill: #116bdd }
- rect:hover { fill: #dc3545 }
- </style>
+{% include "partials/scmcontrib-head.html" %}
{% endblock %}
{% block content %}
-<div class="container">
- <p>
- Contributions to {{repo.owner.canonical_name}}/{{repo.name}}
- in the last 52 weeks:
- </p>
- <div class="row">
- {% for email, full_name, username, commits, bars in chart_data %}
- <div class="col-md-6">
- <h3>
- {% if username %}
- <a href="{{ url_for('public.user_index', username=username) }}">
- ~{{ username }}
- </a>
- {% else %}
- {{ full_name }}
- {% endif %}
- <small>{{ commits }} commits</small>
- </h3>
-
- <svg
- version="1.1"
- baseProfile="full"
- viewBox="0 0 520 100"
- xmlns="http://www.w3.org/2000/svg"
- class="contributors"
- >
- {% for bar in bars %}
- <rect
- x="{{ bar.ordinal * 10 }}"
- y="{{ 100 - bar.height }}%"
- width="11"
- height="{{ bar.height }}%"
- >
- <title>{{ bar.commits }} commits in the week of {{ bar.week }}</title>
- </rect>
- {% endfor %}
- </svg>
- </div>
- {% endfor %}
- </div>
-</div>
+{% include "partials/scmcontrib-body.html" %}
{% endblock %}