team: Include users without an associated GitHub profile.

Including anon=1 in API requests will retrieve all contributors
of the repo. If there is no asscoiated GitHub account present for
the commits then the email and name of the author mentioned in
commit messages is returned.
This commit is contained in:
Vishnu KS
2020-07-24 11:45:27 +00:00
committed by Tim Abbott
parent ecd0530578
commit 3ec64b6092
3 changed files with 72 additions and 22 deletions

View File

@@ -24,29 +24,48 @@ function calculate_total_commits(contributor) {
return commits;
}
function get_profile_url(contributor, tab_name) {
const commit_email_linked_to_github = "github_username" in contributor;
if (commit_email_linked_to_github) {
return "https://github.com/" + contributor.github_username;
}
const email = contributor.email;
if (tab_name) {
return `https://github.com/zulip/${tab_name}/commits?author=${email}`;
}
for (const repo_name in repo_name_to_tab_name) {
if (repo_name in contributor) {
return `https://github.com/zulip/${repo_name}/commits?author=${email}`;
}
}
}
function get_display_name(contributor) {
if (contributor.github_username) {
return "@" + contributor.github_username;
}
return contributor.name;
}
// TODO (for v2 of /team contributors):
// - Make tab header responsive.
// - Display full name instead of github username.
export default function render_tabs() {
const template = _.template($("#contributors-template").html());
// The GitHub API limits the number of contributors per repo to somwhere in the 300s.
// Since zulip/zulip repo has the highest number of contributors by far, we only show
// contributors who have atleast the same number of contributions than the last contributor
// returned by the API for zulip/zulip repo.
const least_server_commits = _.chain(contributors_list)
.filter("zulip")
.sortBy("zulip")
.value()[0].zulip;
const total_tab_html = _.chain(contributors_list)
.map((c) => ({
name: c.name,
name: get_display_name(c),
github_username: c.github_username,
avatar: c.avatar,
profile_url: get_profile_url(c),
commits: calculate_total_commits(c),
}))
.sortBy("commits")
.reverse()
.filter((c) => c.commits >= least_server_commits)
.map((c) => template(c))
.value()
.join("");
@@ -69,8 +88,10 @@ export default function render_tabs() {
.reverse()
.map((c) =>
template({
name: c.name,
name: get_display_name(c),
github_username: c.github_username,
avatar: c.avatar,
profile_url: get_profile_url(c),
commits: c[repo_name],
}),
)

View File

@@ -123,12 +123,12 @@
<!-- Compiled using underscore -->
<script type="text/template" id="contributors-template">
<div class="person">
<a href="https://github.com/<%= name %>" target="_blank" rel="noopener noreferrer" class="no-underline">
<a href="<%= profile_url %>" target="_blank" rel="noopener noreferrer" class="no-underline">
<div class="avatar">
<img class="avatar_img" src="<%= avatar %>" alt="{{ _('Avatar') }}" />
</div>
<div class='info'>
<b>@<%= name %></b><br>
<b><%= name %></b><br>
<%= commits %> <%= commits === 1 ? 'commit' : 'commits' %>
</div>
</a>

View File

@@ -16,7 +16,7 @@ import logging
from datetime import date
from random import randrange
from time import sleep
from typing import Dict, List, Union
from typing import Dict, List, Optional, Union
from typing_extensions import TypedDict
@@ -30,6 +30,9 @@ import json
import requests
from django.conf import settings
from zerver.lib.avatar_hash import gravatar_hash
from zproject.config import get_secret
duplicate_commits_file = os.path.join(os.path.dirname(__file__), 'duplicate_commits.json')
parser = argparse.ArgumentParser()
@@ -42,9 +45,11 @@ class ContributorsJSON(TypedDict):
contributors: List[Dict[str, Union[int, str]]]
class Contributor(TypedDict):
avatar_url: str
avatar_url: Optional[str]
contributions: int
login: str
login: Optional[str]
email: Optional[str]
name: Optional[str]
logger = logging.getLogger('zulip.fetch_contributors_json')
@@ -53,10 +58,16 @@ def fetch_contributors(repo_name: str, max_retries: int) -> List[Contributor]:
retry_attempts = 0
page_index = 1
api_link = f"https://api.github.com/repos/zulip/{repo_name}/contributors"
api_link = f"https://api.github.com/repos/zulip/{repo_name}/contributors?anon=1"
certificates = os.environ.get('CUSTOM_CA_CERTIFICATES')
headers: Dict[str, str] = {}
personal_access_token = get_secret('github_personal_access_token')
if personal_access_token is not None:
headers = {"Authorization": f"token {personal_access_token}"}
while True:
response: requests.Response = requests.get(f"{api_link}?page={page_index}", verify=os.environ.get('CUSTOM_CA_CERTIFICATES'))
response: requests.Response = requests.get(f"{api_link}&page={page_index}", verify=certificates, headers=headers)
if response.status_code == 200:
data = response.json()
if len(data) == 0:
@@ -91,16 +102,34 @@ def update_contributor_data_file() -> None:
for repo_name in repo_names:
contributors = fetch_contributors(repo_name, args.max_retries)
for contributor in contributors:
username = contributor['login']
username = contributor.get('login') or contributor.get('email')
assert(username is not None)
if username in contributor_username_to_data:
contributor_username_to_data[username][repo_name] = contributor['contributions']
else:
contributor_username_to_data[username] = {
'avatar': contributor['avatar_url'],
'name': username,
repo_name: contributor['contributions']
}
avatar_url = contributor.get('avatar_url')
if avatar_url is not None:
contributor_username_to_data[username]['avatar'] = avatar_url
email = contributor.get('email')
if email is not None:
contributor_username_to_data[username]["email"] = email
hash_key = gravatar_hash(email)
gravatar_url = f"https://secure.gravatar.com/avatar/{hash_key}?d=identicon"
contributor_username_to_data[username]['avatar'] = gravatar_url
login = contributor.get('login')
if login is not None:
contributor_username_to_data[username]["github_username"] = login
name = contributor.get('name')
if name is not None:
contributor_username_to_data[username]["name"] = name
# remove duplicate contributions count
# find commits at the time of split and subtract from zulip-server
with open(duplicate_commits_file) as f: