Skip to content

Commit ecefd9e

Browse files
committed
Improve sponsors script and add venz url
1 parent dd91f3a commit ecefd9e

5 files changed

Lines changed: 115 additions & 46 deletions

File tree

packages/docs/scripts/get-monthly-sponsorships-github.ts

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { graphql } from '@octokit/graphql';
22

3-
const START_DATE = new Date('2023-11-01');
4-
const RECURRING_ONLY = process.argv.includes('--recurring-only');
3+
const token = process.env.GITHUB_TOKEN;
4+
5+
type Options = {
6+
token?: string;
7+
startDate: Date;
8+
endDate?: Date;
9+
recurringOnly: boolean;
10+
};
511

612
interface SponsorActivity {
713
action: 'NEW_SPONSORSHIP' | 'CANCELLED_SPONSORSHIP';
@@ -23,7 +29,9 @@ interface GraphQLResponse {
2329
};
2430
}
2531

26-
const getMonthlyTotals = async (token: string) => {
32+
const getMonthlyTotals = async (options: Options) => {
33+
const { token, startDate, recurringOnly } = options;
34+
2735
const { viewer } = await graphql<GraphQLResponse>({
2836
query: `
2937
query {
@@ -58,18 +66,18 @@ const getMonthlyTotals = async (token: string) => {
5866
const monthlyTotals = new Map<string, number>();
5967
const now = new Date();
6068

61-
for (let d = new Date(START_DATE); d <= now; d = new Date(d.getFullYear(), d.getMonth() + 1, 1)) {
69+
for (let d = new Date(startDate); d <= now; d = new Date(d.getFullYear(), d.getMonth() + 1, 1)) {
6270
monthlyTotals.set(d.toISOString().substring(0, 7), 0);
6371
}
6472

6573
for (const activity of activities) {
6674
const { action, sponsor, sponsorsTier, timestamp } = activity;
67-
if (RECURRING_ONLY && sponsorsTier?.isOneTime) continue;
75+
if (recurringOnly && sponsorsTier?.isOneTime) continue;
6876
const amount = sponsorsTier?.monthlyPriceInDollars || 0;
6977
const monthYear = new Date(timestamp).toISOString().substring(0, 7);
7078

7179
if (sponsorsTier?.isOneTime) {
72-
if (!RECURRING_ONLY && action === 'NEW_SPONSORSHIP') {
80+
if (!recurringOnly && action === 'NEW_SPONSORSHIP') {
7381
monthlyTotals.set(monthYear, (monthlyTotals.get(monthYear) || 0) + amount);
7482
}
7583
} else {
@@ -85,23 +93,13 @@ const getMonthlyTotals = async (token: string) => {
8593
return monthlyTotals;
8694
};
8795

88-
const main = async () => {
89-
const token = process.env.GITHUB_TOKEN;
90-
if (!token) {
91-
throw new Error('GITHUB_TOKEN environment variable is not set');
92-
}
93-
const monthlyData = await getMonthlyTotals(token);
94-
95-
let grandTotal = 0;
96-
const startMonth = START_DATE.toISOString().substring(0, 7);
97-
const data = [...monthlyData.entries()].filter(([month]) => month >= startMonth);
98-
99-
for (const [month, amount] of data.sort()) {
100-
console.log(`${month} ${amount}`);
101-
grandTotal += amount;
96+
export const getGitHubTotals = async (options: Options) => {
97+
if (!token) throw new Error('GITHUB_TOKEN is not set');
98+
const monthlyData = await getMonthlyTotals({ ...options, token });
99+
const startMonth = options.startDate.toISOString().substring(0, 7);
100+
const endMonth = options.endDate?.toISOString().substring(0, 7);
101+
for (const month of monthlyData.keys()) {
102+
if (month < startMonth || (endMonth && month > endMonth)) monthlyData.delete(month);
102103
}
103-
104-
console.log(`\nGrand total: ${grandTotal} (${data.length} months)`);
104+
return monthlyData;
105105
};
106-
107-
main().catch(console.error);

packages/docs/scripts/get-monthly-sponsorships-opencollective.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { graphql } from '@octokit/graphql';
22

3-
const START_DATE = new Date('2023-11-01');
3+
const token = process.env.OPENCOLLECTIVE_TOKEN;
44
const RATE_EUR_TO_USD = 1.08;
5-
const RECURRING_ONLY = process.argv.includes('--recurring-only');
5+
6+
type Options = {
7+
token?: string;
8+
startDate: Date;
9+
endDate?: Date;
10+
recurringOnly: boolean;
11+
};
612

713
interface Transaction {
814
id: string;
@@ -38,7 +44,9 @@ interface GraphQLResponse {
3844
};
3945
}
4046

41-
const getMonthlyTotals = async (token: string): Promise<Map<string, number>> => {
47+
const getMonthlyTotals = async (options: Options): Promise<Map<string, number>> => {
48+
const { token, startDate, recurringOnly } = options;
49+
4250
const { account, expenses } = await graphql<GraphQLResponse>({
4351
query: `
4452
query {
@@ -81,19 +89,19 @@ const getMonthlyTotals = async (token: string): Promise<Map<string, number>> =>
8189
const monthlyTotals = new Map<string, number>();
8290
const now = new Date();
8391

84-
for (let d = new Date(START_DATE); d <= now; d = new Date(d.getFullYear(), d.getMonth() + 1, 1)) {
92+
for (let d = new Date(startDate); d <= now; d = new Date(d.getFullYear(), d.getMonth() + 1, 1)) {
8593
monthlyTotals.set(d.toISOString().substring(0, 7), 0);
8694
}
8795

8896
for (const transaction of account.transactions.nodes) {
89-
if (RECURRING_ONLY && transaction.kind !== 'CONTRIBUTION') continue;
97+
if (recurringOnly && transaction.kind !== 'CONTRIBUTION') continue;
9098

9199
const month = new Date(transaction.createdAt).toISOString().substring(0, 7);
92100
const amount = Math.round(transaction.amount.value);
93101
monthlyTotals.set(month, (monthlyTotals.get(month) || 0) + amount);
94102
}
95103

96-
if (!RECURRING_ONLY) {
104+
if (!recurringOnly) {
97105
for (const expense of expenses.nodes) {
98106
const month = new Date(expense.createdAt).toISOString().substring(0, 7);
99107
const amount =
@@ -107,18 +115,13 @@ const getMonthlyTotals = async (token: string): Promise<Map<string, number>> =>
107115
return monthlyTotals;
108116
};
109117

110-
const main = async () => {
111-
const token = process.env.OPENCOLLECTIVE_TOKEN;
118+
export const getOpenCollectiveTotals = async (options: Options) => {
112119
if (!token) throw new Error('OPENCOLLECTIVE_TOKEN is not set');
113-
const monthlyData = await getMonthlyTotals(token);
114-
115-
let grandTotal = 0;
116-
for (const [month, amount] of [...monthlyData.entries()].sort()) {
117-
console.log(`${month} ${amount}`);
118-
grandTotal += amount;
120+
const monthlyData = await getMonthlyTotals({ ...options, token });
121+
const startMonth = options.startDate.toISOString().substring(0, 7);
122+
const endMonth = options.endDate?.toISOString().substring(0, 7);
123+
for (const month of monthlyData.keys()) {
124+
if (month < startMonth || (endMonth && month > endMonth)) monthlyData.delete(month);
119125
}
120-
121-
console.log(`\nGrand total: ${grandTotal} (${monthlyData.size} months)`);
126+
return monthlyData;
122127
};
123-
124-
main().catch(console.error);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Table } from '../../knip/src/util/table.ts';
2+
import { getGitHubTotals } from './get-monthly-sponsorships-github.ts';
3+
import { getOpenCollectiveTotals } from './get-monthly-sponsorships-opencollective.ts';
4+
5+
const START_DATE = new Date('2023-11-01');
6+
const END_DATE = new Date();
7+
END_DATE.setDate(1);
8+
END_DATE.setHours(-1);
9+
10+
const main = async () => {
11+
const [monthlyGHS, monthlyGHSRO, monthlyOC, monthlyOCRO] = await Promise.all([
12+
getGitHubTotals({ startDate: START_DATE, endDate: END_DATE, recurringOnly: false }),
13+
getGitHubTotals({ startDate: START_DATE, endDate: END_DATE, recurringOnly: true }),
14+
getOpenCollectiveTotals({ startDate: START_DATE, endDate: END_DATE, recurringOnly: false }),
15+
getOpenCollectiveTotals({ startDate: START_DATE, endDate: END_DATE, recurringOnly: true }),
16+
]);
17+
18+
const monthly = new Map<string, number[]>();
19+
for (const [month, amount] of monthlyGHS) monthly.set(month, [...(monthly.get(month) || []), amount]);
20+
for (const [month, amount] of monthlyOC) monthly.set(month, [...(monthly.get(month) || []), amount]);
21+
22+
const monthlyRO = new Map<string, number[]>();
23+
for (const [month, amount] of monthlyGHSRO) monthlyRO.set(month, [...(monthlyRO.get(month) || []), amount]);
24+
for (const [month, amount] of monthlyOCRO) monthlyRO.set(month, [...(monthlyRO.get(month) || []), amount]);
25+
26+
const table = new Table();
27+
const digits = (n: number) => (v: any) => (typeof v === 'number' ? v.toFixed(n) : v);
28+
const sum = Array.from(monthly.values()).reduce((a, c) => a + c.reduce((a, b) => a + b, 0), 0);
29+
const sumGHS = Array.from(monthlyGHS.values()).reduce((a, c) => a + c, 0);
30+
const sumOC = Array.from(monthlyOC.values()).reduce((a, c) => a + c, 0);
31+
const sumRO = Array.from(monthlyRO.values()).reduce((a, c) => a + c.reduce((a, b) => a + b, 0), 0);
32+
for (const [key, value] of [
33+
['months', monthly.size],
34+
['github', sumGHS],
35+
['OC', sumOC],
36+
['sum', sum],
37+
['avg', sum / monthly.size],
38+
['recurring sum', sumRO],
39+
['recurring avg', sumRO / monthlyRO.size],
40+
]) {
41+
table.row();
42+
table.cell('key', key);
43+
table.cell('value', value, digits(0));
44+
}
45+
46+
console.log(table.toString());
47+
48+
const url = new URL('/', 'https://try.venz.dev');
49+
url.searchParams.set('type', 'pivot');
50+
url.searchParams.set('lp', 'tr');
51+
url.searchParams.set('br', '0');
52+
url.searchParams.set('labelX', 'month');
53+
url.searchParams.set('labelY', 'amount ($)');
54+
url.searchParams.append('l', 'GitHub Sponsors');
55+
url.searchParams.append('color', '#fbfbfb');
56+
url.searchParams.append('l', 'Open Collective');
57+
url.searchParams.append('color', '#2487ff');
58+
for (const [month, amount] of monthly) {
59+
url.searchParams.append('label', month);
60+
url.searchParams.append('data', amount.join(','));
61+
}
62+
63+
console.log('\nVenz link');
64+
const text = `${url.origin}?${decodeURIComponent(url.searchParams.toString()).replaceAll('#', '%23')}`;
65+
console.log(text);
66+
};
67+
68+
main().catch(console.error);

packages/docs/src/assets/venz-chart.svg

Lines changed: 2 additions & 2 deletions
Loading

packages/docs/src/content/docs/sponsors.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ development started one year before that.
5252

5353
<SponsorsChart />
5454

55-
- The monthly aggregated average over the charted period is $559.
56-
- The monthly recurring average without one-time payments is $316.
55+
- The monthly aggregated average over the charted period is $528.
56+
- The monthly recurring average without one-time payments is $298.
5757
- GitHub Sponsors is for my GitHub account, which has more repositories, but in
5858
practice targets mostly Knip (and perhaps some [release-it][1]).
5959

0 commit comments

Comments
 (0)