Browse Source
* 👥 Update FastAPI People * ✨ Add first version of FastAPI People GitHub action code * 🐳 Add Docker and configs for the FastAPI People GitHub Action * 👷 Add GitHub Action workflow for FastAPI People * 📝 Add FastAPI People to docs * 💄 Add custom CSSs for FastAPI Peoplepull/2196/head
committed by
GitHub
8 changed files with 1062 additions and 5 deletions
@ -0,0 +1,7 @@ |
|||
FROM python:3.7 |
|||
|
|||
RUN pip install httpx PyGithub "pydantic==1.5.1" "pyyaml>=5.3.1,,6.0.0" |
|||
|
|||
COPY ./app /app |
|||
|
|||
CMD ["python", "/app/main.py"] |
@ -0,0 +1,10 @@ |
|||
name: "Generate FastAPI People" |
|||
description: "Generate the data for the FastAPI People page" |
|||
author: "Sebastián Ramírez <[email protected]>" |
|||
inputs: |
|||
token: |
|||
description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}' |
|||
required: true |
|||
runs: |
|||
using: 'docker' |
|||
image: 'Dockerfile' |
@ -0,0 +1,510 @@ |
|||
import logging |
|||
import subprocess |
|||
from collections import Counter |
|||
from datetime import datetime, timedelta, timezone |
|||
from pathlib import Path |
|||
from typing import Container, Dict, List, Optional, Set |
|||
|
|||
import httpx |
|||
from github import Github |
|||
import yaml |
|||
from pydantic import BaseModel, BaseSettings, SecretStr |
|||
|
|||
github_graphql_url = "https://api.github.com/graphql" |
|||
|
|||
issues_query = """ |
|||
query Q($after: String) { |
|||
repository(name: "fastapi", owner: "tiangolo") { |
|||
issues(first: 100, after: $after) { |
|||
edges { |
|||
cursor |
|||
node { |
|||
number |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
title |
|||
createdAt |
|||
state |
|||
comments(first: 100) { |
|||
nodes { |
|||
createdAt |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
""" |
|||
|
|||
prs_query = """ |
|||
query Q($after: String) { |
|||
repository(name: "fastapi", owner: "tiangolo") { |
|||
pullRequests(first: 100, after: $after) { |
|||
edges { |
|||
cursor |
|||
node { |
|||
number |
|||
labels(first: 100) { |
|||
nodes { |
|||
name |
|||
} |
|||
} |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
title |
|||
createdAt |
|||
state |
|||
comments(first: 100) { |
|||
nodes { |
|||
createdAt |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
} |
|||
} |
|||
reviews(first:100) { |
|||
nodes { |
|||
author { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
state |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
""" |
|||
|
|||
sponsors_query = """ |
|||
query Q($after: String) { |
|||
user(login: "tiangolo") { |
|||
sponsorshipsAsMaintainer(first: 100, after: $after) { |
|||
edges { |
|||
cursor |
|||
node { |
|||
sponsorEntity { |
|||
... on Organization { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
... on User { |
|||
login |
|||
avatarUrl |
|||
url |
|||
} |
|||
} |
|||
tier { |
|||
name |
|||
monthlyPriceInDollars |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
""" |
|||
|
|||
|
|||
class Author(BaseModel): |
|||
login: str |
|||
avatarUrl: str |
|||
url: str |
|||
|
|||
|
|||
class CommentsNode(BaseModel): |
|||
createdAt: datetime |
|||
author: Optional[Author] = None |
|||
|
|||
|
|||
class Comments(BaseModel): |
|||
nodes: List[CommentsNode] |
|||
|
|||
|
|||
class IssuesNode(BaseModel): |
|||
number: int |
|||
author: Optional[Author] = None |
|||
title: str |
|||
createdAt: datetime |
|||
state: str |
|||
comments: Comments |
|||
|
|||
|
|||
class IssuesEdge(BaseModel): |
|||
cursor: str |
|||
node: IssuesNode |
|||
|
|||
|
|||
class Issues(BaseModel): |
|||
edges: List[IssuesEdge] |
|||
|
|||
|
|||
class IssuesRepository(BaseModel): |
|||
issues: Issues |
|||
|
|||
|
|||
class IssuesResponseData(BaseModel): |
|||
repository: IssuesRepository |
|||
|
|||
|
|||
class IssuesResponse(BaseModel): |
|||
data: IssuesResponseData |
|||
|
|||
|
|||
class LabelNode(BaseModel): |
|||
name: str |
|||
|
|||
|
|||
class Labels(BaseModel): |
|||
nodes: List[LabelNode] |
|||
|
|||
|
|||
class ReviewNode(BaseModel): |
|||
author: Optional[Author] = None |
|||
state: str |
|||
|
|||
|
|||
class Reviews(BaseModel): |
|||
nodes: List[ReviewNode] |
|||
|
|||
|
|||
class PullRequestNode(BaseModel): |
|||
number: int |
|||
labels: Labels |
|||
author: Optional[Author] = None |
|||
title: str |
|||
createdAt: datetime |
|||
state: str |
|||
comments: Comments |
|||
reviews: Reviews |
|||
|
|||
|
|||
class PullRequestEdge(BaseModel): |
|||
cursor: str |
|||
node: PullRequestNode |
|||
|
|||
|
|||
class PullRequests(BaseModel): |
|||
edges: List[PullRequestEdge] |
|||
|
|||
|
|||
class PRsRepository(BaseModel): |
|||
pullRequests: PullRequests |
|||
|
|||
|
|||
class PRsResponseData(BaseModel): |
|||
repository: PRsRepository |
|||
|
|||
|
|||
class PRsResponse(BaseModel): |
|||
data: PRsResponseData |
|||
|
|||
|
|||
class SponsorEntity(BaseModel): |
|||
login: str |
|||
avatarUrl: str |
|||
url: str |
|||
|
|||
|
|||
class Tier(BaseModel): |
|||
name: str |
|||
monthlyPriceInDollars: float |
|||
|
|||
|
|||
class SponsorshipAsMaintainerNode(BaseModel): |
|||
sponsorEntity: SponsorEntity |
|||
tier: Tier |
|||
|
|||
|
|||
class SponsorshipAsMaintainerEdge(BaseModel): |
|||
cursor: str |
|||
node: SponsorshipAsMaintainerNode |
|||
|
|||
|
|||
class SponsorshipAsMaintainer(BaseModel): |
|||
edges: List[SponsorshipAsMaintainerEdge] |
|||
|
|||
|
|||
class SponsorsUser(BaseModel): |
|||
sponsorshipsAsMaintainer: SponsorshipAsMaintainer |
|||
|
|||
|
|||
class SponsorsResponseData(BaseModel): |
|||
user: SponsorsUser |
|||
|
|||
|
|||
class SponsorsResponse(BaseModel): |
|||
data: SponsorsResponseData |
|||
|
|||
|
|||
class Settings(BaseSettings): |
|||
input_token: SecretStr |
|||
github_repository: str |
|||
|
|||
|
|||
def get_graphql_response( |
|||
*, settings: Settings, query: str, after: Optional[str] = None |
|||
): |
|||
headers = {"Authorization": f"token {settings.input_token.get_secret_value()}"} |
|||
variables = {"after": after} |
|||
response = httpx.post( |
|||
github_graphql_url, |
|||
headers=headers, |
|||
json={"query": query, "variables": variables, "operationName": "Q"}, |
|||
) |
|||
if not response.status_code == 200: |
|||
logging.error(f"Response was not 200, after: {after}") |
|||
logging.error(response.text) |
|||
raise RuntimeError(response.text) |
|||
data = response.json() |
|||
return data |
|||
|
|||
|
|||
def get_graphql_issue_edges(*, settings: Settings, after: Optional[str] = None): |
|||
data = get_graphql_response(settings=settings, query=issues_query, after=after) |
|||
graphql_response = IssuesResponse.parse_obj(data) |
|||
return graphql_response.data.repository.issues.edges |
|||
|
|||
|
|||
def get_graphql_pr_edges(*, settings: Settings, after: Optional[str] = None): |
|||
data = get_graphql_response(settings=settings, query=prs_query, after=after) |
|||
graphql_response = PRsResponse.parse_obj(data) |
|||
return graphql_response.data.repository.pullRequests.edges |
|||
|
|||
|
|||
def get_graphql_sponsor_edges(*, settings: Settings, after: Optional[str] = None): |
|||
data = get_graphql_response(settings=settings, query=sponsors_query, after=after) |
|||
graphql_response = SponsorsResponse.parse_obj(data) |
|||
return graphql_response.data.user.sponsorshipsAsMaintainer.edges |
|||
|
|||
|
|||
def get_experts(settings: Settings): |
|||
issue_nodes: List[IssuesNode] = [] |
|||
issue_edges = get_graphql_issue_edges(settings=settings) |
|||
|
|||
while issue_edges: |
|||
for edge in issue_edges: |
|||
issue_nodes.append(edge.node) |
|||
last_edge = issue_edges[-1] |
|||
issue_edges = get_graphql_issue_edges(settings=settings, after=last_edge.cursor) |
|||
|
|||
commentors = Counter() |
|||
last_month_commentors = Counter() |
|||
authors: Dict[str, Author] = {} |
|||
|
|||
now = datetime.now(tz=timezone.utc) |
|||
one_month_ago = now - timedelta(days=30) |
|||
|
|||
for issue in issue_nodes: |
|||
issue_author_name = None |
|||
if issue.author: |
|||
authors[issue.author.login] = issue.author |
|||
issue_author_name = issue.author.login |
|||
issue_commentors = set() |
|||
for comment in issue.comments.nodes: |
|||
if comment.author: |
|||
authors[comment.author.login] = comment.author |
|||
if comment.author.login == issue_author_name: |
|||
continue |
|||
issue_commentors.add(comment.author.login) |
|||
for author_name in issue_commentors: |
|||
commentors[author_name] += 1 |
|||
if issue.createdAt > one_month_ago: |
|||
last_month_commentors[author_name] += 1 |
|||
return commentors, last_month_commentors, authors |
|||
|
|||
|
|||
def get_contributors(settings: Settings): |
|||
pr_nodes: List[PullRequestNode] = [] |
|||
pr_edges = get_graphql_pr_edges(settings=settings) |
|||
|
|||
while pr_edges: |
|||
for edge in pr_edges: |
|||
pr_nodes.append(edge.node) |
|||
last_edge = pr_edges[-1] |
|||
pr_edges = get_graphql_pr_edges(settings=settings, after=last_edge.cursor) |
|||
|
|||
contributors = Counter() |
|||
commentors = Counter() |
|||
reviewers = Counter() |
|||
authors: Dict[str, Author] = {} |
|||
|
|||
for pr in pr_nodes: |
|||
author_name = None |
|||
if pr.author: |
|||
authors[pr.author.login] = pr.author |
|||
author_name = pr.author.login |
|||
pr_commentors: Set[str] = set() |
|||
pr_reviewers: Set[str] = set() |
|||
for comment in pr.comments.nodes: |
|||
if comment.author: |
|||
authors[comment.author.login] = comment.author |
|||
if comment.author.login == author_name: |
|||
continue |
|||
pr_commentors.add(comment.author.login) |
|||
for author_name in pr_commentors: |
|||
commentors[author_name] += 1 |
|||
for review in pr.reviews.nodes: |
|||
if review.author: |
|||
authors[review.author.login] = review.author |
|||
pr_reviewers.add(review.author.login) |
|||
for reviewer in pr_reviewers: |
|||
reviewers[reviewer] += 1 |
|||
if pr.state == "MERGED" and pr.author: |
|||
contributors[pr.author.login] += 1 |
|||
return contributors, commentors, reviewers, authors |
|||
|
|||
|
|||
def get_sponsors(settings: Settings): |
|||
nodes: List[SponsorshipAsMaintainerNode] = [] |
|||
edges = get_graphql_sponsor_edges(settings=settings) |
|||
|
|||
while edges: |
|||
for edge in edges: |
|||
nodes.append(edge.node) |
|||
last_edge = edges[-1] |
|||
edges = get_graphql_sponsor_edges(settings=settings, after=last_edge.cursor) |
|||
|
|||
entities: Dict[str, SponsorEntity] = {} |
|||
for node in nodes: |
|||
entities[node.sponsorEntity.login] = node.sponsorEntity |
|||
return entities |
|||
|
|||
|
|||
def get_top_users( |
|||
*, |
|||
counter: Counter, |
|||
min_count: int, |
|||
authors: Dict[str, Author], |
|||
skip_users: Container[str], |
|||
): |
|||
users = [] |
|||
for commentor, count in counter.most_common(50): |
|||
if commentor in skip_users: |
|||
continue |
|||
if count >= min_count: |
|||
author = authors[commentor] |
|||
users.append( |
|||
{ |
|||
"login": commentor, |
|||
"count": count, |
|||
"avatarUrl": author.avatarUrl, |
|||
"url": author.url, |
|||
} |
|||
) |
|||
return users |
|||
|
|||
|
|||
if __name__ == "__main__": |
|||
logging.basicConfig(level=logging.INFO) |
|||
settings = Settings() |
|||
logging.info(f"Using config: {settings.json()}") |
|||
g = Github(settings.input_token.get_secret_value()) |
|||
repo = g.get_repo(settings.github_repository) |
|||
issue_commentors, issue_last_month_commentors, issue_authors = get_experts( |
|||
settings=settings |
|||
) |
|||
contributors, pr_commentors, reviewers, pr_authors = get_contributors( |
|||
settings=settings |
|||
) |
|||
authors = {**issue_authors, **pr_authors} |
|||
maintainers_logins = {"tiangolo"} |
|||
bot_names = {"codecov", "github-actions"} |
|||
maintainers = [] |
|||
for login in maintainers_logins: |
|||
user = authors[login] |
|||
maintainers.append( |
|||
{ |
|||
"login": login, |
|||
"answers": issue_commentors[login], |
|||
"prs": contributors[login], |
|||
"avatarUrl": user.avatarUrl, |
|||
"url": user.url, |
|||
} |
|||
) |
|||
|
|||
min_count_expert = 10 |
|||
min_count_last_month = 3 |
|||
min_count_contributor = 4 |
|||
min_count_reviewer = 4 |
|||
skip_users = maintainers_logins | bot_names |
|||
experts = get_top_users( |
|||
counter=issue_commentors, |
|||
min_count=min_count_expert, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
last_month_active = get_top_users( |
|||
counter=issue_last_month_commentors, |
|||
min_count=min_count_last_month, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
top_contributors = get_top_users( |
|||
counter=contributors, |
|||
min_count=min_count_contributor, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
top_reviewers = get_top_users( |
|||
counter=reviewers, |
|||
min_count=min_count_reviewer, |
|||
authors=authors, |
|||
skip_users=skip_users, |
|||
) |
|||
|
|||
sponsors_by_login = get_sponsors(settings=settings) |
|||
sponsors = [] |
|||
for login, sponsor in sponsors_by_login.items(): |
|||
sponsors.append( |
|||
{"login": login, "avatarUrl": sponsor.avatarUrl, "url": sponsor.url} |
|||
) |
|||
|
|||
people = { |
|||
"maintainers": maintainers, |
|||
"experts": experts, |
|||
"last_month_active": last_month_active, |
|||
"top_contributors": top_contributors, |
|||
"top_reviewers": top_reviewers, |
|||
"sponsors": sponsors, |
|||
} |
|||
people_path = Path("./docs/en/data/people.yml") |
|||
people_path.write_text( |
|||
yaml.dump(people, sort_keys=False, width=200, allow_unicode=True), |
|||
encoding="utf-8", |
|||
) |
|||
logging.info("Setting up GitHub Actions git user") |
|||
subprocess.run(["git", "config", "user.name", "github-actions"], check=True) |
|||
subprocess.run(["git", "config", "user.email", "[email protected]"], check=True) |
|||
branch_name = "fastapi-people" |
|||
logging.info(f"Creating a new branch {branch_name}") |
|||
subprocess.run(["git", "checkout", "-b", branch_name], check=True) |
|||
logging.info("Adding updated file") |
|||
subprocess.run(["git", "add", str(people_path)], check=True) |
|||
logging.info("Committing updated file") |
|||
message = "👥 Update FastAPI People" |
|||
result = subprocess.run(["git", "commit", "-m", message], check=True) |
|||
logging.info("Pushing branch") |
|||
subprocess.run(["git", "push", "origin", branch_name], check=True) |
|||
logging.info("Creating PR") |
|||
pr = repo.create_pull(title=message, body=message, base="master", head=branch_name) |
|||
logging.info(f"Created PR: {pr.number}") |
|||
logging.info("Finished") |
@ -0,0 +1,15 @@ |
|||
name: FastAPI People |
|||
|
|||
on: |
|||
schedule: |
|||
- cron: "30 * * * *" |
|||
workflow_dispatch: |
|||
|
|||
jobs: |
|||
deploy: |
|||
runs-on: ubuntu-latest |
|||
steps: |
|||
- uses: actions/checkout@v2 |
|||
- uses: ./.github/actions/people |
|||
with: |
|||
token: ${{ secrets.GITHUB_TOKEN }} |
@ -0,0 +1,340 @@ |
|||
maintainers: |
|||
- login: tiangolo |
|||
answers: 979 |
|||
prs: 177 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/1326112?u=05f95ca7fdead36edd9c86be46b4ef6c3c71f876&v=4 |
|||
url: https://github.com/tiangolo |
|||
experts: |
|||
- login: dmontagu |
|||
count: 262 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4 |
|||
url: https://github.com/dmontagu |
|||
- login: euri10 |
|||
count: 166 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/1104190?u=ffd411da5d3b7ad3aa18261317f7ddc76f763c33&v=4 |
|||
url: https://github.com/euri10 |
|||
- login: phy25 |
|||
count: 129 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/331403?v=4 |
|||
url: https://github.com/phy25 |
|||
- login: Kludex |
|||
count: 93 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 |
|||
url: https://github.com/Kludex |
|||
- login: sm-Fifteen |
|||
count: 39 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/516999?u=437c0c5038558c67e887ccd863c1ba0f846c03da&v=4 |
|||
url: https://github.com/sm-Fifteen |
|||
- login: prostomarkeloff |
|||
count: 33 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/28061158?u=72309cc1f2e04e40fa38b29969cb4e9d3f722e7b&v=4 |
|||
url: https://github.com/prostomarkeloff |
|||
- login: ycd |
|||
count: 33 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/62724709?u=496a800351ea1009678e40b26288a2a6c0dfa8bd&v=4 |
|||
url: https://github.com/ycd |
|||
- login: ArcLightSlavik |
|||
count: 30 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/31127044?u=b81d0c33b056152513fb14749a9fe00f39887a8e&v=4 |
|||
url: https://github.com/ArcLightSlavik |
|||
- login: wshayes |
|||
count: 29 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4 |
|||
url: https://github.com/wshayes |
|||
- login: Mause |
|||
count: 28 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/1405026?v=4 |
|||
url: https://github.com/Mause |
|||
- login: dbanty |
|||
count: 25 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/43723790?u=0cf33e4f40efc2ea206a1189fd63a11344eb88ed&v=4 |
|||
url: https://github.com/dbanty |
|||
- login: nsidnev |
|||
count: 22 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4 |
|||
url: https://github.com/nsidnev |
|||
- login: chris-allnutt |
|||
count: 21 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/565544?v=4 |
|||
url: https://github.com/chris-allnutt |
|||
- login: Dustyposa |
|||
count: 21 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4 |
|||
url: https://github.com/Dustyposa |
|||
- login: acnebs |
|||
count: 19 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/9054108?u=bfd127b3e6200f4d00afd714f0fc95c2512df19b&v=4 |
|||
url: https://github.com/acnebs |
|||
- login: retnikt |
|||
count: 19 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/24581770?v=4 |
|||
url: https://github.com/retnikt |
|||
- login: SirTelemak |
|||
count: 19 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/9435877?u=719327b7d2c4c62212456d771bfa7c6b8dbb9eac&v=4 |
|||
url: https://github.com/SirTelemak |
|||
- login: jorgerpo |
|||
count: 17 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/12537771?u=7444d20019198e34911082780cc7ad73f2b97cb3&v=4 |
|||
url: https://github.com/jorgerpo |
|||
- login: raphaelauv |
|||
count: 17 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4 |
|||
url: https://github.com/raphaelauv |
|||
- login: Slyfoxy |
|||
count: 17 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4 |
|||
url: https://github.com/Slyfoxy |
|||
- login: haizaar |
|||
count: 13 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/58201?u=4f1f9843d69433ca0d380d95146cfe119e5fdac4&v=4 |
|||
url: https://github.com/haizaar |
|||
- login: zamiramir |
|||
count: 11 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/40475662?u=e58ef61034e8d0d6a312cc956fb09b9c3332b449&v=4 |
|||
url: https://github.com/zamiramir |
|||
- login: stefanondisponibile |
|||
count: 10 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/20441825?u=ee1e59446b98f8ec2363caeda4c17164d0d9cc7d&v=4 |
|||
url: https://github.com/stefanondisponibile |
|||
last_month_active: |
|||
- login: Mause |
|||
count: 20 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/1405026?v=4 |
|||
url: https://github.com/Mause |
|||
- login: ycd |
|||
count: 8 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/62724709?u=496a800351ea1009678e40b26288a2a6c0dfa8bd&v=4 |
|||
url: https://github.com/ycd |
|||
- login: Kludex |
|||
count: 7 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 |
|||
url: https://github.com/Kludex |
|||
- login: ArcLightSlavik |
|||
count: 6 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/31127044?u=b81d0c33b056152513fb14749a9fe00f39887a8e&v=4 |
|||
url: https://github.com/ArcLightSlavik |
|||
- login: SebastianLuebke |
|||
count: 4 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/21161532?u=ba033c1bf6851b874cfa05a8a824b9f1ff434c37&v=4 |
|||
url: https://github.com/SebastianLuebke |
|||
- login: includeamin |
|||
count: 3 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/11836741?u=8bd5ef7e62fe6a82055e33c4c0e0a7879ff8cfb6&v=4 |
|||
url: https://github.com/includeamin |
|||
top_contributors: |
|||
- login: dmontagu |
|||
count: 16 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4 |
|||
url: https://github.com/dmontagu |
|||
- login: waynerv |
|||
count: 16 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4 |
|||
url: https://github.com/waynerv |
|||
- login: euri10 |
|||
count: 13 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/1104190?u=ffd411da5d3b7ad3aa18261317f7ddc76f763c33&v=4 |
|||
url: https://github.com/euri10 |
|||
- login: tokusumi |
|||
count: 10 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4 |
|||
url: https://github.com/tokusumi |
|||
- login: mariacamilagl |
|||
count: 7 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4 |
|||
url: https://github.com/mariacamilagl |
|||
- login: Serrones |
|||
count: 6 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4 |
|||
url: https://github.com/Serrones |
|||
- login: wshayes |
|||
count: 5 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4 |
|||
url: https://github.com/wshayes |
|||
- login: jekirl |
|||
count: 4 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/2546697?v=4 |
|||
url: https://github.com/jekirl |
|||
top_reviewers: |
|||
- login: Kludex |
|||
count: 47 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/7353520?u=cf8455cb899806b774a3a71073f88583adec99f6&v=4 |
|||
url: https://github.com/Kludex |
|||
- login: tokusumi |
|||
count: 37 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4 |
|||
url: https://github.com/tokusumi |
|||
- login: dmontagu |
|||
count: 23 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/35119617?u=58ed2a45798a4339700e2f62b2e12e6e54bf0396&v=4 |
|||
url: https://github.com/dmontagu |
|||
- login: cassiobotaro |
|||
count: 14 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/3127847?u=b0a652331da17efeb85cd6e3a4969182e5004804&v=4 |
|||
url: https://github.com/cassiobotaro |
|||
- login: AdrianDeAnda |
|||
count: 13 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/1024932?u=bb7f8a0d6c9de4e9d0320a9f271210206e202250&v=4 |
|||
url: https://github.com/AdrianDeAnda |
|||
- login: Laineyzhang55 |
|||
count: 12 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/59285379?v=4 |
|||
url: https://github.com/Laineyzhang55 |
|||
- login: yanever |
|||
count: 11 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/21978760?v=4 |
|||
url: https://github.com/yanever |
|||
- login: SwftAlpc |
|||
count: 11 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/52768429?u=6a3aa15277406520ad37f6236e89466ed44bc5b8&v=4 |
|||
url: https://github.com/SwftAlpc |
|||
- login: ycd |
|||
count: 11 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/62724709?u=496a800351ea1009678e40b26288a2a6c0dfa8bd&v=4 |
|||
url: https://github.com/ycd |
|||
- login: waynerv |
|||
count: 10 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4 |
|||
url: https://github.com/waynerv |
|||
- login: mariacamilagl |
|||
count: 10 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4 |
|||
url: https://github.com/mariacamilagl |
|||
- login: Attsun1031 |
|||
count: 10 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/1175560?v=4 |
|||
url: https://github.com/Attsun1031 |
|||
- login: RunningIkkyu |
|||
count: 9 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/31848542?u=706e1ee3f248245f2d68b976d149d06fd5a2010d&v=4 |
|||
url: https://github.com/RunningIkkyu |
|||
- login: komtaki |
|||
count: 9 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/39375566?v=4 |
|||
url: https://github.com/komtaki |
|||
- login: Serrones |
|||
count: 7 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/22691749?u=4795b880e13ca33a73e52fc0ef7dc9c60c8fce47&v=4 |
|||
url: https://github.com/Serrones |
|||
- login: ryuckel |
|||
count: 7 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/36391432?u=094eec0cfddd5013f76f31e55e56147d78b19553&v=4 |
|||
url: https://github.com/ryuckel |
|||
- login: MashhadiNima |
|||
count: 5 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/49960770?u=e39b11d47188744ee07b2a1c7ce1a1bdf3c80760&v=4 |
|||
url: https://github.com/MashhadiNima |
|||
- login: euri10 |
|||
count: 4 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/1104190?u=ffd411da5d3b7ad3aa18261317f7ddc76f763c33&v=4 |
|||
url: https://github.com/euri10 |
|||
- login: rkbeatss |
|||
count: 4 |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/23391143?u=56ab6bff50be950fa8cae5cf736f2ae66e319ff3&v=4 |
|||
url: https://github.com/rkbeatss |
|||
sponsors: |
|||
- login: samuelcolvin |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/4039449?u=807390ba9cfe23906c3bf8a0d56aaca3cf2bfa0d&v=4 |
|||
url: https://github.com/samuelcolvin |
|||
- login: mkeen |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/38221?u=03e076e08a10a4de0d48a348f1aab0223c5cf24a&v=4 |
|||
url: https://github.com/mkeen |
|||
- login: wshayes |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4 |
|||
url: https://github.com/wshayes |
|||
- login: ltieman |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/1084689?u=c9bf77f5e57f98b49694870219b9bd9d1cc862e7&v=4 |
|||
url: https://github.com/ltieman |
|||
- login: mrmattwright |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/1277725?v=4 |
|||
url: https://github.com/mrmattwright |
|||
- login: timdrijvers |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/1694939?v=4 |
|||
url: https://github.com/timdrijvers |
|||
- login: abdelhai |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/1752577?u=8f8f2bce75f3ab68188cea2b5da37c784197acd8&v=4 |
|||
url: https://github.com/abdelhai |
|||
- login: ddahan |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/1933516?u=4068dc3c5db5d3605116c4f5df6deb9fee324c33&v=4 |
|||
url: https://github.com/ddahan |
|||
- login: cbonoz |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/2351087?u=fd3e8030b2cc9fbfbb54a65e9890c548a016f58b&v=4 |
|||
url: https://github.com/cbonoz |
|||
- login: mrgnw |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/2504532?u=7ec43837a6d0afa80f96f0788744ea6341b89f97&v=4 |
|||
url: https://github.com/mrgnw |
|||
- login: paul121 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/3116995?u=6e2d8691cc345e63ee02e4eb4d7cef82b1fcbedc&v=4 |
|||
url: https://github.com/paul121 |
|||
- login: andre1sk |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/3148093?v=4 |
|||
url: https://github.com/andre1sk |
|||
- login: igorcorrea |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/3438238?u=c57605077c31a8f7b2341fc4912507f91b4a5621&v=4 |
|||
url: https://github.com/igorcorrea |
|||
- login: pawamoy |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4 |
|||
url: https://github.com/pawamoy |
|||
- login: p141592 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/5256328?u=7f9fdf3329bf90017cff00c8a78781bd7a2b48aa&v=4 |
|||
url: https://github.com/p141592 |
|||
- login: fabboe |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/7251331?v=4 |
|||
url: https://github.com/fabboe |
|||
- login: macleodmac |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/8996312?u=e39c68c3e0b1d264dcba4850134a291680f46355&v=4 |
|||
url: https://github.com/macleodmac |
|||
- login: cristeaadrian |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/9112724?u=76099d546d6ee44b3ad7269773ecb916590c6a36&v=4 |
|||
url: https://github.com/cristeaadrian |
|||
- login: iambobmae |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/12390270?u=c9a35c2ee5092a9b4135ebb1f91b7f521c467031&v=4 |
|||
url: https://github.com/iambobmae |
|||
- login: Cozmo25 |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/12619962?u=679dcd6785121e14f6254e9dd0961baf3b1fef5d&v=4 |
|||
url: https://github.com/Cozmo25 |
|||
- login: augustogoulart |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/13952931?u=9326220a94c303c21dc0da56f1f2ff3c10ed591f&v=4 |
|||
url: https://github.com/augustogoulart |
|||
- login: la-mar |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/16618300?u=7755c0521d2bb0d704f35a51464b15c1e2e6c4da&v=4 |
|||
url: https://github.com/la-mar |
|||
- login: robintully |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/17059673?u=862b9bb01513f5acd30df97433cb97a24dbfb772&v=4 |
|||
url: https://github.com/robintully |
|||
- login: wedwardbeck |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4 |
|||
url: https://github.com/wedwardbeck |
|||
- login: linusg |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/19366641?u=125e390abef8fff3b3b0d370c369cba5d7fd4c67&v=4 |
|||
url: https://github.com/linusg |
|||
- login: SebastianLuebke |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/21161532?u=ba033c1bf6851b874cfa05a8a824b9f1ff434c37&v=4 |
|||
url: https://github.com/SebastianLuebke |
|||
- login: raminsj13 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/24259406?u=d51f2a526312ebba150a06936ed187ca0727d329&v=4 |
|||
url: https://github.com/raminsj13 |
|||
- login: mertguvencli |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/29762151?u=16a906d90df96c8cff9ea131a575c4bc171b1523&v=4 |
|||
url: https://github.com/mertguvencli |
|||
- login: orihomie |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/29889683?u=6bc2135a52fcb3a49e69e7d50190796618185fda&v=4 |
|||
url: https://github.com/orihomie |
|||
- login: dcooper01 |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/32238294?u=2a83c78b7f2a5f97beeede0b604bbe44cd21b46b&v=4 |
|||
url: https://github.com/dcooper01 |
|||
- login: d3vzer0 |
|||
avatarUrl: https://avatars3.githubusercontent.com/u/34250156?u=c50c9df0e34f411f7e5f050a72e8d89696284eba&v=4 |
|||
url: https://github.com/d3vzer0 |
|||
- login: dbanty |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/43723790?u=0cf33e4f40efc2ea206a1189fd63a11344eb88ed&v=4 |
|||
url: https://github.com/dbanty |
|||
- login: Brontomerus |
|||
avatarUrl: https://avatars0.githubusercontent.com/u/61284158?u=c00d807195815014d0b6597b3801ee9c494802dd&v=4 |
|||
url: https://github.com/Brontomerus |
|||
- login: primer-api |
|||
avatarUrl: https://avatars2.githubusercontent.com/u/62152773?u=4549d79b0ad1d30ecfbef6c6933593e90e819c75&v=4 |
|||
url: https://github.com/primer-api |
|||
- login: daverin |
|||
avatarUrl: https://avatars1.githubusercontent.com/u/70378377?u=6d1814195c0de7162820eaad95a25b423a3869c0&v=4 |
|||
url: https://github.com/daverin |
@ -1,18 +1,57 @@ |
|||
a.external-link::after { |
|||
/* \00A0 is a non-breaking space |
|||
/* \00A0 is a non-breaking space |
|||
to make the mark be on the same line as the link |
|||
*/ |
|||
content: "\00A0[↪]"; |
|||
content: "\00A0[↪]"; |
|||
} |
|||
|
|||
a.internal-link::after { |
|||
/* \00A0 is a non-breaking space |
|||
/* \00A0 is a non-breaking space |
|||
to make the mark be on the same line as the link |
|||
*/ |
|||
content: "\00A0↪"; |
|||
content: "\00A0↪"; |
|||
} |
|||
|
|||
/* Give space to lower icons so Gitter chat doesn't get on top of them */ |
|||
.md-footer-meta { |
|||
padding-bottom: 2em; |
|||
padding-bottom: 2em; |
|||
} |
|||
|
|||
.user-list { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.user-list-center { |
|||
justify-content: space-evenly; |
|||
} |
|||
|
|||
.user { |
|||
margin: 1em; |
|||
min-width: 7em; |
|||
} |
|||
|
|||
.user .avatar-wrapper { |
|||
width: 80px; |
|||
height: 80px; |
|||
margin: 10px auto; |
|||
overflow: hidden; |
|||
border-radius: 50%; |
|||
position: relative; |
|||
} |
|||
|
|||
.user .avatar-wrapper img { |
|||
position: absolute; |
|||
top: 50%; |
|||
left: 50%; |
|||
transform: translate(-50%, -50%); |
|||
} |
|||
|
|||
.user .title { |
|||
text-align: center; |
|||
} |
|||
|
|||
.user .count { |
|||
font-size: 80%; |
|||
text-align: center; |
|||
} |
|||
|
@ -0,0 +1,135 @@ |
|||
# FastAPI People |
|||
|
|||
FastAPI has an amazing community that welcomes people from all backgrounds. |
|||
|
|||
## Creator - Maintainer |
|||
|
|||
Hey! 👋 |
|||
|
|||
This is me: |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.maintainers %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Answers: {{ user.answers }}</div><div class="count">Pull Requests: {{ user.prs }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
I'm the creator and maintainer of **FastAPI**. You can read more about that in [Help FastAPI - Get Help - Connect with the author](help-fastapi.md#connect-with-the-author){.internal-link target=_blank}. |
|||
|
|||
...But here I want to show you the community. |
|||
|
|||
--- |
|||
|
|||
**FastAPI** receives a lot of support from the community. And I want to highlight their contributions. |
|||
|
|||
These are the people that: |
|||
|
|||
* [Help others with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank}. |
|||
* [Create Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank}. |
|||
* Review Pull Requests, [especially important for translations](contributing.md#translations){.internal-link target=_blank}. |
|||
|
|||
A round of applause to them. 👏 🙇 |
|||
|
|||
## Most active users last month |
|||
|
|||
These are the users that have been [helping others the most with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} during the last month. ☕ |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.last_month_active %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Issues replied: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Experts |
|||
|
|||
Here are the **FastAPI Experts**. 🤓 |
|||
|
|||
These are the users that have [helped others the most with issues (questions) in GitHub](help-fastapi.md#help-others-with-issues-in-github){.internal-link target=_blank} through *all time*. |
|||
|
|||
They have proven to be experts by helping many others. ✨ |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.experts %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Issues replied: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Top Contributors |
|||
|
|||
Here are the **Top Contributors**. 👷 |
|||
|
|||
These users have [created the most Pull Requests](help-fastapi.md#create-a-pull-request){.internal-link target=_blank} that have been *merged*. |
|||
|
|||
They have contributed source code, documentation, translations, etc. 📦 |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.top_contributors %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Pull Requests: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
There are many other contributors (more than a hundred), you can see them all in the <a href="https://github.com/tiangolo/fastapi/graphs/contributors" class="external-link" target="_blank">FastAPI GitHub Contributors page</a>. 👷 |
|||
|
|||
## Top Reviewers |
|||
|
|||
These users are the **Top Reviewers**. 🕵️ |
|||
|
|||
### Reviews for Translations |
|||
|
|||
I only speak a few languages (and not very well 😅). So, the reviewers are the ones that have the [**power to approve translations**](contributing.md#translations){.internal-link target=_blank} of the documentation. Without them, there wouldn't be documentation in several other languages. |
|||
|
|||
--- |
|||
|
|||
The **Top Reviewers** 🕵️ have reviewed the most Pull Requests from others, ensuring the quality of the code, documentation, and especially, the **translations**. |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.top_reviewers %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a> <div class="count">Reviews: {{ user.count }}</div></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## Sponsors |
|||
|
|||
These are the **Sponsors**. 😎 |
|||
|
|||
They are supporting my work with **FastAPI** (and others) through <a href="https://github.com/sponsors/tiangolo" class="external-link" target="_blank">GitHub Sponsors</a>. |
|||
|
|||
{% if people %} |
|||
<div class="user-list user-list-center"> |
|||
{% for user in people.sponsors %} |
|||
|
|||
<div class="user"><a href="{{ user.url }}" target="_blank"><div class="avatar-wrapper"><img src="{{ user.avatarUrl }}"/></div><div class="title">@{{ user.login }}</div></a></div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
{% endif %} |
|||
|
|||
## About the data - technical details |
|||
|
|||
The intention of this page is to highlight the effort of the community to help others. |
|||
|
|||
Especially including efforts that are normally less visible, and in many cases more arduous, like helping others with issues and reviewing Pull Requests with translations. |
|||
|
|||
The data is calculated each month, you can read the <a href="https://github.com/tiangolo/fastapi/blob/master/.github/actions/people/app/main.py" class="external-link" target="_blank">source code here</a>. |
|||
|
|||
I also reserve the right to update the algorithm, sections, thresholds, etc (just in case 🤷). |
Loading…
Reference in new issue