mirror of
https://github.com/zulip/zulip.git
synced 2025-11-03 05:23:35 +00:00
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:
@@ -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],
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user