Files
seer/.github/scripts/generate_stargazer_map.py
T
Ernie Pasveer eb5d2ab150 Renamed file.
2026-03-03 11:42:38 -06:00

129 lines
3.8 KiB
Python

import os
import requests
import pandas as pd
import plotly.express as px
from collections import Counter
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
REPO = os.environ["REPO"]
HEADERS = {
"Authorization": f"Bearer {GITHUB_TOKEN}",
"Accept": "application/vnd.github+json",
}
def get_all_stargazers():
"""Fetch all stargazers (paginated)."""
stargazers = []
page = 1
while True:
url = f"https://api.github.com/repos/{REPO}/stargazers?per_page=100&page={page}"
resp = requests.get(url, headers=HEADERS)
resp.raise_for_status()
data = resp.json()
if not data:
break
stargazers.extend(data)
page += 1
print(f"Total stargazers: {len(stargazers)}")
return stargazers
def get_user_location(username):
"""Fetch location field from a user's profile."""
url = f"https://api.github.com/users/{username}"
resp = requests.get(url, headers=HEADERS)
if resp.status_code == 200:
return resp.json().get("location", None)
return None
def geocode_location(location, session, cache):
"""Use Nominatim (OSM) to geocode a location string."""
if not location or location in cache:
return cache.get(location)
try:
url = "https://nominatim.openstreetmap.org/search"
params = {"q": location, "format": "json", "limit": 1}
resp = session.get(url, params=params, timeout=5,
headers={"User-Agent": f"StargazerMap/{REPO}"})
results = resp.json()
if results:
lat = float(results[0]["lat"])
lon = float(results[0]["lon"])
cache[location] = (lat, lon)
return (lat, lon)
except Exception:
pass
cache[location] = None
return None
def generate_map(points):
"""Generate an interactive Plotly world map."""
df = pd.DataFrame(points, columns=["lat", "lon", "location"])
country_counts = Counter(df["location"])
df["count"] = df["location"].map(country_counts)
fig = px.scatter_geo(
df,
lat="lat",
lon="lon",
hover_name="location",
size="count",
size_max=30,
color="count",
color_continuous_scale="Plasma",
projection="natural earth",
title=f"⭐ Stargazers of {REPO}",
template="plotly_dark",
)
fig.update_layout(
title_font_size=22,
geo=dict(
showframe=False,
showcoastlines=True,
coastlinecolor="rgba(255,255,255,0.2)",
showland=True,
landcolor="rgb(30, 30, 50)",
showocean=True,
oceancolor="rgb(15, 15, 35)",
showcountries=True,
countrycolor="rgba(255,255,255,0.1)",
),
paper_bgcolor="rgb(10, 10, 25)",
font_color="white",
coloraxis_showscale=False,
margin=dict(l=0, r=0, t=50, b=0),
)
os.makedirs("images", exist_ok=True)
fig.write_html("images/stargazer-map.html", include_plotlyjs="cdn")
fig.write_image("images/stargazer-map.svg", width=1200, height=600)
print("Map saved to images/stargazer-map.html and images/stargazer-map.svg")
def main():
import time
stargazers = get_all_stargazers()
geocache = {}
points = []
session = requests.Session()
for i, user in enumerate(stargazers):
username = user["login"]
location = get_user_location(username)
if location:
coords = geocode_location(location, session, geocache)
if coords:
points.append((coords[0], coords[1], location))
# Respect Nominatim rate limit: 1 req/sec
if i % 5 == 0:
time.sleep(1)
print(f"Mapped {len(points)} stargazers with locations.")
if points:
generate_map(points)
else:
print("No geocodable locations found.")
if __name__ == "__main__":
main()