Skip to content

Commit 04f6687

Browse files
committed
Changes
1 parent 4276314 commit 04f6687

File tree

4 files changed

+237
-35
lines changed

4 files changed

+237
-35
lines changed

scripts/sync-sched/schedule-2025.json

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,93 @@
1-
[]
1+
[
2+
{
3+
"event_key": "1",
4+
"active": "Y",
5+
"pinned": "N",
6+
"name": "Keynote Session Example",
7+
"event_start": "2025-09-08 09:00",
8+
"event_end": "2025-09-08 09:15",
9+
"event_type": "Keynote Sessions",
10+
"goers": "0",
11+
"seats": "0",
12+
"invite_only": "N",
13+
"venue": "Keynote Room",
14+
"audience": "Any",
15+
"id": "aa6a893426a3d92bcbc4b140abeb56c6",
16+
"venue_id": "2111098",
17+
"speakers": [
18+
{
19+
"username": "lee_byron.25jvpjmb",
20+
"id": "18743534",
21+
"name": "Lee Byron",
22+
"company": "GraphQL Foundation",
23+
"custom_order": 0
24+
}
25+
],
26+
"event_start_year": "2025",
27+
"event_start_month": "September",
28+
"event_start_month_short": "Sep",
29+
"event_start_day": "8",
30+
"event_start_weekday": "Monday",
31+
"event_start_weekday_short": "Mon",
32+
"event_start_time": "09:00",
33+
"event_end_year": "2025",
34+
"event_end_month": "September",
35+
"event_end_month_short": "Sep",
36+
"event_end_day": "8",
37+
"event_end_weekday": "Monday",
38+
"event_end_weekday_short": "Mon",
39+
"event_end_time": "09:15",
40+
"start_date": "2025-09-08",
41+
"start_time": "09:00:00",
42+
"start_time_ts": 1757318400,
43+
"end_date": "2025-09-08",
44+
"end_time": "09:15:00",
45+
"event_type_sort": "2",
46+
"description": ""
47+
},
48+
{
49+
"event_key": "2",
50+
"active": "Y",
51+
"pinned": "N",
52+
"name": "Breakout session example",
53+
"event_start": "2025-09-10 10:30",
54+
"event_end": "2025-09-10 10:45",
55+
"event_type": "Breakout Session",
56+
"description": "This is an example description.",
57+
"goers": "0",
58+
"seats": "0",
59+
"invite_only": "N",
60+
"venue": "Breakout Session Room",
61+
"id": "c0205884be221948a39bc4af9939c675",
62+
"venue_id": "2111100",
63+
"speakers": [
64+
{
65+
"username": "benjie3",
66+
"id": "18743846",
67+
"name": "Benjie Gillam",
68+
"company": "Graphile",
69+
"custom_order": 0
70+
}
71+
],
72+
"event_start_year": "2025",
73+
"event_start_month": "September",
74+
"event_start_month_short": "Sep",
75+
"event_start_day": "10",
76+
"event_start_weekday": "Wednesday",
77+
"event_start_weekday_short": "Wed",
78+
"event_start_time": "10:30",
79+
"event_end_year": "2025",
80+
"event_end_month": "September",
81+
"event_end_month_short": "Sep",
82+
"event_end_day": "10",
83+
"event_end_weekday": "Wednesday",
84+
"event_end_weekday_short": "Wed",
85+
"event_end_time": "10:45",
86+
"start_date": "2025-09-10",
87+
"start_time": "10:30:00",
88+
"start_time_ts": 1757496600,
89+
"end_date": "2025-09-10",
90+
"end_time": "10:45:00",
91+
"event_type_sort": "1"
92+
}
93+
]

scripts/sync-sched/speakers.json

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,44 @@
1-
[]
1+
[
2+
{
3+
"username": "benjie3",
4+
"name": "Benjie Gillam",
5+
"email": "[email protected]",
6+
"about": "A self-described \"community-funded open source maintainer,\" Benjie spends much of his time on OSS, enabled by appreciative and forward-thinking individuals and organizations who sponsor his continued efforts. Through his 5 years attending the GraphQL Working Group, Benjie has become one of the key members – both in terms of his own contributions, and in terms of helping others to advance their submissions. As a member of the GraphQL TSC, Benjie is proud to help guide GraphQL into the future.",
7+
"url": "https://graphile.org/",
8+
"avatar": "//avatars.sched.co/b/99/18743846/avatar.jpg.320x320px.jpg?b57",
9+
"role": "speaker",
10+
"tags": "",
11+
"tickets": [],
12+
"plusones": 0,
13+
"~syncedDetailsAt": 1749489924661
14+
},
15+
{
16+
"username": "lee_byron.25jvpjmb",
17+
"name": "Lee Byron",
18+
"email": "[email protected]",
19+
"about": "",
20+
"url": "",
21+
"avatar": "http://avatars.sched.co/5/24/18743534/avatar.jpg.320x320px.jpg?480",
22+
"role": "speaker",
23+
"tags": "",
24+
"tickets": [],
25+
"plusones": 0,
26+
"company": "GraphQL Foundation",
27+
"position": "Co-creator of GraphQL and Director",
28+
"location": "",
29+
"socialurls": []
30+
},
31+
{
32+
"username": "saihaj",
33+
"name": "Saihajpreet Singh",
34+
"email": "[email protected]",
35+
"about": "",
36+
"url": "",
37+
"avatar": "",
38+
"role": "speaker",
39+
"tags": "",
40+
"tickets": [],
41+
"plusones": 0,
42+
"~syncedDetailsAt": 1749489924661
43+
}
44+
]

scripts/sync-sched/sync.ts

Lines changed: 99 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import assert from "node:assert"
44
import { parseArgs } from "node:util"
55
import { join } from "node:path"
6+
import { readFile, writeFile } from "node:fs/promises"
67

78
import {
89
getSchedule,
@@ -11,7 +12,6 @@ import {
1112
RequestContext,
1213
} from "@/app/conf/_api/sched-client"
1314
import type { SchedSpeaker } from "@/app/conf/_api/sched-types"
14-
import { readFile, writeFile } from "node:fs/promises"
1515

1616
/**
1717
* Sched API rate limit is 30 requests per minute per token.
@@ -20,7 +20,7 @@ import { readFile, writeFile } from "node:fs/promises"
2020
* - one request for the list of speakers with partial details
2121
* - and N requests for the full details of each speaker
2222
*/
23-
const SPEAKER_DETAILS_REQUEST_QUOTA = 10
23+
const SPEAKER_DETAILS_REQUEST_QUOTA = 2
2424

2525
const options = {
2626
year: {
@@ -35,7 +35,7 @@ const options = {
3535

3636
const unsafeKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>
3737

38-
async function main() {
38+
;(async function main() {
3939
try {
4040
const { values } = parseArgs({ options })
4141

@@ -60,11 +60,7 @@ async function main() {
6060
}
6161
throw error
6262
}
63-
}
64-
65-
if (require.main === module) {
66-
void main()
67-
}
63+
})()
6864

6965
async function sync(year: number, token: string) {
7066
const apiUrl = {
@@ -77,9 +73,8 @@ async function sync(year: number, token: string) {
7773

7874
const ctx: RequestContext = { apiUrl, token }
7975

80-
const scriptDir = __dirname
81-
const speakersFilePath = join(scriptDir, "speakers.json")
82-
const scheduleFilePath = join(scriptDir, `schedule-${year}.json`)
76+
const speakersFilePath = join(import.meta.dirname, "speakers.json")
77+
const scheduleFilePath = join(import.meta.dirname, `schedule-${year}.json`)
8378

8479
console.log("Getting schedule and speakers list...")
8580

@@ -172,7 +167,13 @@ async function updateSpeakerDetails(
172167
const location = locations.get(speaker.username)
173168
if (location) {
174169
const [key, index] = location
175-
comparison[key][index] = speaker
170+
if (key === "changed") {
171+
comparison[key][index].new = speaker
172+
comparison[key][index].new["~syncedDetailsAt"] = Date.now()
173+
} else {
174+
comparison[key][index] = speaker
175+
comparison[key][index]["~syncedDetailsAt"] = Date.now()
176+
}
176177
}
177178
}
178179
}
@@ -208,7 +209,7 @@ function compare<T extends object>(
208209
for (const newItem of news) {
209210
const oldItem = oldMap.get(newItem[key])
210211
if (oldItem) {
211-
if (JSON.stringify(oldItem) === JSON.stringify(newItem)) {
212+
if (deepStrictEqualWithoutInternals(oldItem, newItem)) {
212213
unchanged.push(oldItem)
213214
} else {
214215
changed.push({
@@ -235,42 +236,48 @@ function printComparison<T extends object>(
235236
name: string,
236237
key: keyof T,
237238
) {
238-
console.log(`Removed: ${comparison.removed.length}`)
239-
console.log(`Changed: ${comparison.changed.length}`)
240-
console.log(`Unchanged: ${comparison.unchanged.length}`)
241-
242-
console.log(`Added ${comparison.added.length} ${name}`)
243-
for (const item of comparison.added) {
244-
console.log(`+ ${item}`)
239+
if (comparison.added.length > 0) {
240+
console.log(`Added ${comparison.added.length} ${name}`)
241+
for (const item of comparison.added) {
242+
console.log(green(`+ ${JSON.stringify(item)}`))
243+
}
245244
}
246245

247-
console.log(`Removed ${comparison.removed.length} ${name}`)
248-
for (const item of comparison.removed) {
249-
console.log(`- ${item}`)
246+
if (comparison.removed.length > 0) {
247+
console.log(`Removed ${comparison.removed.length} ${name}`)
248+
for (const item of comparison.removed) {
249+
console.log(red(`- ${JSON.stringify(item)}`))
250+
}
250251
}
251252

252-
console.log(`Unchanged ${comparison.unchanged.length} ${name}`)
253-
for (const item of comparison.unchanged) {
254-
console.log(item)
253+
if (comparison.unchanged.length > 0) {
254+
console.log(`Unchanged ${comparison.unchanged.length} ${name}`)
255+
for (const item of comparison.unchanged) {
256+
console.log(yellow(`{ ${String(key)}: ${item[key]}, ... }`))
257+
}
255258
}
256259

257-
console.log(`Changed ${comparison.changed.length} ${name}`)
258-
for (const change of comparison.changed) {
259-
console.log(change.new[key], objectDiff(change))
260+
if (comparison.changed.length > 0) {
261+
console.log(`Changed ${comparison.changed.length} ${name}`)
262+
for (const change of comparison.changed) {
263+
console.log(change.new[key] + "\n", objectDiff(change))
264+
}
260265
}
261266
}
262267

263268
function objectDiff<T extends object>(change: Change<T>): string {
264269
const allKeys = [
265270
...new Set([...unsafeKeys(change.old), ...unsafeKeys(change.new)]),
266-
].sort()
271+
]
272+
.sort()
273+
.filter(key => !String(key).startsWith("~"))
267274

268275
const diff = allKeys
269276
.map(key => {
270277
const oldValue = change.old[key]
271278
const newValue = change.new[key]
272279

273-
if (oldValue === newValue) {
280+
if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
274281
return null
275282
}
276283

@@ -280,9 +287,69 @@ function objectDiff<T extends object>(change: Change<T>): string {
280287

281288
return diff
282289
.map(diff => {
283-
return `\x1b[33m${String(diff.key)}\x1b[0m: ${diff.oldValue} -> ${diff.newValue}`
290+
return `${yellow(String(diff.key))}:\n ${red("-" + JSON.stringify(diff.oldValue))}\n ${green("+" + JSON.stringify(diff.newValue))}`
284291
})
285292
.join("\n")
286293
}
287294

295+
function green(text: string) {
296+
return `\x1b[32m${text}\x1b[0m`
297+
}
298+
299+
function red(text: string) {
300+
return `\x1b[31m${text}\x1b[0m`
301+
}
302+
303+
function yellow(text: string) {
304+
return `\x1b[33m${text}\x1b[0m`
305+
}
306+
307+
function deepStrictEqualWithoutInternals(a: unknown, b: unknown): boolean {
308+
if (a === b) return true
309+
310+
if (a === null || b === null || a === undefined || b === undefined) {
311+
return a === b
312+
}
313+
314+
if (typeof a !== typeof b) return false
315+
316+
if (typeof a !== "object") return false
317+
318+
if (Array.isArray(a) !== Array.isArray(b)) return false
319+
320+
if (Array.isArray(a) && Array.isArray(b)) {
321+
if (a.length !== b.length) return false
322+
323+
for (let i = 0; i < a.length; i++) {
324+
if (!deepStrictEqualWithoutInternals(a[i], b[i])) {
325+
return false
326+
}
327+
}
328+
return true
329+
}
330+
331+
const aObj = a as Record<string, unknown>
332+
const bObj = b as Record<string, unknown>
333+
334+
const aKeys = Object.keys(aObj).filter(key => !key.startsWith("~"))
335+
const bKeys = Object.keys(bObj).filter(key => !key.startsWith("~"))
336+
337+
if (aKeys.length !== bKeys.length) return false
338+
339+
aKeys.sort()
340+
bKeys.sort()
341+
342+
for (let i = 0; i < aKeys.length; i++) {
343+
if (aKeys[i] !== bKeys[i]) return false
344+
}
345+
346+
for (const key of aKeys) {
347+
if (!deepStrictEqualWithoutInternals(aObj[key], bObj[key])) {
348+
return false
349+
}
350+
}
351+
352+
return true
353+
}
354+
288355
// #endregion utility

src/app/conf/_api/sched-client.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export async function fetchSchedData(
2727
path: string,
2828
searchParams: Record<string, string> = {},
2929
): Promise<object> {
30-
const url = new URL(path, ctx.apiUrl)
30+
const url = new URL(ctx.apiUrl + path)
3131
const search = new URLSearchParams(searchParams)
3232
search.set("api_key", ctx.token)
3333
search.set("format", "json")

0 commit comments

Comments
 (0)