~edwargix/git.sr.ht

e95fbad612e6041dbde5ff1232bc2752f2de4b62 — Ivan Habunek 7 years ago b498476
Add git contributors view
3 files changed, 136 insertions(+), 1 deletions(-)

M gitsrht/blueprints/repo.py
A gitsrht/templates/contributors.html
M gitsrht/templates/repo.html
M gitsrht/blueprints/repo.py => gitsrht/blueprints/repo.py +73 -1
@@ 4,11 4,13 @@ import pygit2
import pygments
import sys
import subprocess
from datetime import datetime, timedelta
from collections import defaultdict
from datetime import date, datetime, timedelta
from jinja2 import Markup
from flask import Blueprint, render_template, abort, send_file, request
from flask import Response, redirect, url_for
from flask_login import current_user
from functools import lru_cache
from gitsrht.access import get_repo, has_access, UserAccess
from gitsrht.editorconfig import EditorConfig
from gitsrht.redis import redis


@@ 469,3 471,73 @@ 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)

A gitsrht/templates/contributors.html => gitsrht/templates/contributors.html +58 -0
@@ 0,0 1,58 @@
{% extends "repo.html" %}

{% block title %}
<title>
  {{repo.owner.canonical_name}}/{{repo.name}} contributors
  - {{cfg("sr.ht", "site-name")}} git
</title>
{% endblock %}

{% block head %}
  <style type="text/css">
    svg { background-color: WhiteSmoke }
    rect { fill: #007bff }
    rect:hover { fill: #dc3545 }
  </style>
{% endblock %}

{% block content %}
<div class="container">
  <p>Contributions in the last 52 weeks</p>

  <div class="row">
  {% for email, 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 %}
          {{ email }}
        {% endif %}
      </h3>

      <svg
        version="1.1"
        baseProfile="full"
        viewBox="0 0 520 100"
        xmlns="http://www.w3.org/2000/svg"
      >
        {% for bar in bars %}
          <rect
            x="{{ bar.ordinal * 10 }}"
            y="{{ 100 - bar.height }}%"
            width="10"
            height="{{ bar.height }}%"
          >
            <title>{{ bar.commits }} commits in week {{ bar.week }}</title>
          </rect>
        {% endfor %}
      </svg>

      <p>Commits: {{ commits }}</p>
    </div>
  {% endfor %}
  </div>
</div>
{% endblock %}

M gitsrht/templates/repo.html => gitsrht/templates/repo.html +5 -0
@@ 35,6 35,11 @@
            owner=repo.owner.canonical_name,
            repo=repo.name), "refs")}}
        </li>
        <li class="nav-item">
          {{link(url_for("repo.contributors",
            owner=repo.owner.canonical_name,
            repo=repo.name), "contributors")}}
        </li>
        {% if current_user.id == repo.owner_id %}
        <li class="nav-item">
          {{link(url_for("manage.settings_info",