Published
Edited
Sep 21, 2021
17 forks
Importers
13 stars
Insert cell
Insert cell
repo = "observablehq/plot"
// repo = "observablehq/runtime"
// repo = "fivethirtyeight/russian-troll-tweets"
// repo = "sveltejs/kit"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data = {
// Start with an empty array of results; we'll yield it repeatedly as more results come in
const S = [];
yield S;

// Get total number of pages from response headers
let pages = await fetchGithubResponse(`/repos/${repo}/stargazers`, {per_page: 100}, fetchOptions)
.then(d => [...d.headers].find(d => d[0] === "link")?.[1].split(/,/).pop().match(/&page=(\d+)/)?.[1] || 1);

let l = Math.min(maxPages, pages);
mutable pages_remaining = l;

// Fetch the first page, last page, and then remaining pages shuffled
for (const page of new Set([1, +pages].concat(d3.shuffle(d3.range(2, +pages))))) {
const s = await fetchGithub(`/repos/${repo}/stargazers`, {per_page: 100, page}, fetchOptions);

// If there's an error, return
if (!s) return;
if (s.message) return mutable error_message = s.message;

// Push a transformed version of the stargazer response, sort it, and yield it
s.forEach((u, i) => {
S.push({
login: u.user.login,
id: u.user.id,
date: new Date(u.starred_at),
count: 100 * (page - 1) + i + 1,
repo
});
});
S.sort((a, b) => a.count - b.count);
yield S;

// Decrement log of remaining pages
mutable pages_remaining = --l;
if (l === 0) break;

// Wait before next request
await Promises.delay(150);
}

// Final results
yield S;
}
Insert cell
Insert cell
maxPages = hasApiKey ? 50 : 10
Insert cell
Insert cell
mutable pages_remaining = null
Insert cell
mutable error_message = {}
Insert cell
Insert cell
tags = {
const tags = await fetchGithub(`/repos/${repo}/tags`);
for (const tag of tags) {
const commit = await fetchGithub(`/repos/${repo}/commits/${tag.commit.sha}`);
tag.commit = commit;
tag.date = new Date(commit.commit.author.date);
}
return tags;
}
Insert cell
Insert cell
dates = {
const D = [];
const max = data[data.length - 1].count;
let i = 0;
for (let count = 1; count <= max; count++) {
while (data[i].count < count) i++;
const date =
data[i].count === count
? data[i].date
: d3.interpolate(
data[i - 1].date,
data[i].date
)((count - data[i - 1].count) / (data[i].count - data[i - 1].count));
D.push(date);
}
return D;
}
Insert cell
Insert cell
topDays = d3.groups(data, d => d3.utcDay.floor(d.date)).sort((a, b) => d3.descending(a[1].length, b[1].length))
Insert cell
milestones = d3
.range(Math.ceil(Math.log10(data[data.length - 1].count)))
.map((n) => 10 ** n)
.map((n) => [n, data.find((d) => d.count === n)])
Insert cell
Insert cell
fetchOptions = ({
headers: {
accept: "application/vnd.github.v3.star+json" // necessary to get the starred_at field
}
})
Insert cell
import {
fetchGithub,
fetchGithubResponse,
hasApiKey,
apiKeyStatus,
rateLimitSummary
} from "@observablehq/github-actions-workflows-api"
Insert cell
// For a notebook to access Secrets, it must statically reference one; this cell grants Secret access to the notebook from which we import the GitHub API helpers. `undefined &&` prevents the Secret from appearing in plaintext in the Inspector.
undefined && Secret("GITHUB_ACCESS_TOKEN")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more