This commit is contained in:
Alexandre
2025-06-08 16:26:04 +02:00
committed by GitHub
parent 309ca649cc
commit 2fe9bb7b3b

View File

@@ -8,17 +8,18 @@ up once (unless the country entry is blank).
import csv import csv
import os import os
import time
import sys import sys
import requests import time
from collections import Counter from collections import Counter
from pathlib import Path from pathlib import Path
import plotly.express as px import plotly.express as px
import pycountry import pycountry
import requests
from geopy.geocoders import Nominatim from geopy.geocoders import Nominatim
# ---- Configuration ---------------------------------------------------------- # ---- Configuration ----------------------------------------------------------
REPO = os.getenv("REPO") # expected "owner/repo" REPO = os.getenv("REPO") # expected "owner/repo"
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") # provided by workflow GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") # provided by workflow
CSV_PATH = Path(".github/stargazer_countries.csv") CSV_PATH = Path(".github/stargazer_countries.csv")
@@ -30,8 +31,10 @@ HEADERS = {
} }
GEOL = Nominatim(user_agent="gh-stargazer-map") GEOL = Nominatim(user_agent="gh-stargazer-map")
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
def github_paginated(url): def github_paginated(url):
page = 1 page = 1
while True: while True:
@@ -43,16 +46,19 @@ def github_paginated(url):
yield from data yield from data
page += 1 page += 1
def fetch_stargazer_usernames(): def fetch_stargazer_usernames():
url = f"https://api.github.com/repos/{REPO}/stargazers" url = f"https://api.github.com/repos/{REPO}/stargazers"
return [s["login"] for s in github_paginated(url)] return [s["login"] for s in github_paginated(url)]
def load_cache(): def load_cache():
if not CSV_PATH.exists(): if not CSV_PATH.exists():
return {} return {}
with CSV_PATH.open(newline="", encoding="utf-8") as f: with CSV_PATH.open(newline="", encoding="utf-8") as f:
return {row["username"]: row["country"] for row in csv.DictReader(f)} return {row["username"]: row["country"] for row in csv.DictReader(f)}
def save_cache(cache): def save_cache(cache):
CSV_PATH.parent.mkdir(parents=True, exist_ok=True) CSV_PATH.parent.mkdir(parents=True, exist_ok=True)
with CSV_PATH.open("w", newline="", encoding="utf-8") as f: with CSV_PATH.open("w", newline="", encoding="utf-8") as f:
@@ -61,6 +67,7 @@ def save_cache(cache):
for user, country in sorted(cache.items()): for user, country in sorted(cache.items()):
w.writerow([user, country or ""]) w.writerow([user, country or ""])
def username_to_country(login): def username_to_country(login):
"""Return readable country name or '' if unknown.""" """Return readable country name or '' if unknown."""
resp = requests.get(f"https://api.github.com/users/{login}", headers=HEADERS) resp = requests.get(f"https://api.github.com/users/{login}", headers=HEADERS)
@@ -84,6 +91,7 @@ def username_to_country(login):
pass pass
return "" return ""
def build_choropleth(percent_by_iso): def build_choropleth(percent_by_iso):
# build dataframe-like structure for plotly # build dataframe-like structure for plotly
iso, vals = zip(*percent_by_iso.items()) iso, vals = zip(*percent_by_iso.items())
@@ -96,11 +104,12 @@ def build_choropleth(percent_by_iso):
) )
fig.update_layout( fig.update_layout(
coloraxis_colorbar={"title": "% stargazers"}, coloraxis_colorbar={"title": "% stargazers"},
margin=dict(l=0, r=0, t=0, b=0) margin=dict(l=0, r=0, t=0, b=0),
) )
PNG_PATH.parent.mkdir(parents=True, exist_ok=True) PNG_PATH.parent.mkdir(parents=True, exist_ok=True)
fig.write_image(str(PNG_PATH), scale=2) fig.write_image(str(PNG_PATH), scale=2)
def main(): def main():
if not REPO or not GITHUB_TOKEN: if not REPO or not GITHUB_TOKEN:
sys.exit("REPO and GITHUB_TOKEN env vars are required") sys.exit("REPO and GITHUB_TOKEN env vars are required")
@@ -139,15 +148,19 @@ def main():
for c, pct in pct_by_country.items(): for c, pct in pct_by_country.items():
try: try:
iso = pycountry.countries.lookup(c).alpha_3 iso = pycountry.countries.lookup(c).alpha_3
pct_by_iso[iso] = pct*100 # plotly wants numeric pct_by_iso[iso] = pct * 100 # plotly wants numeric
except LookupError: except LookupError:
print("Skip unknown country:", c) print("Skip unknown country:", c)
print("Rendering PNG map…") print("Rendering PNG map…")
build_choropleth(pct_by_iso) build_choropleth(pct_by_iso)
print("Done files saved:", print(
CSV_PATH.relative_to('.'), "Done files saved:",
PNG_PATH.relative_to('.'), sep="\n") CSV_PATH.relative_to("."),
PNG_PATH.relative_to("."),
sep="\n",
)
if __name__ == "__main__": if __name__ == "__main__":
main() main()