Skip to content

Commit 6d1ac21

Browse files
committed
add script to sync speakers from schedule
Signed-off-by: Nabarun Pal <[email protected]>
1 parent f0c8d0c commit 6d1ac21

File tree

1 file changed

+195
-0
lines changed

1 file changed

+195
-0
lines changed

sync-speakers.js

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
const crypto = require('crypto');
6+
const https = require('https');
7+
8+
const scheduleFilePath = 'src/_data/schedule.json';
9+
const speakersFilePath = 'src/_data/speakers.json';
10+
11+
function getAnswerByQuestion(answers, questionId) {
12+
if (!answers || !Array.isArray(answers)) return null;
13+
14+
const answer = answers.find(a => a.question === questionId);
15+
return answer ? answer.answer : null;
16+
}
17+
18+
function extractSpeakers(scheduleData) {
19+
const speakers = new Map();
20+
21+
function processEvents(events) {
22+
events.forEach(event => {
23+
if (event.persons && event.persons.length > 0) {
24+
event.persons.forEach(person => {
25+
if (!speakers.has(person.guid)) {
26+
const linkedinUrl = getAnswerByQuestion(person.answers, 19);
27+
let xUrl = getAnswerByQuestion(person.answers, 20);
28+
let instagramUrl = getAnswerByQuestion(person.answers, 21);
29+
let gravatarUrl = getAnswerByQuestion(person.answers, 17);
30+
let title = getAnswerByQuestion(person.answers, 16);
31+
32+
let speaker = {
33+
code: person.code,
34+
name: person.public_name,
35+
socials: {},
36+
}
37+
38+
if (title !== null) {
39+
speaker.title = title;
40+
}
41+
42+
if (linkedinUrl !== null) {
43+
speaker.socials.linkedin = linkedinUrl;
44+
}
45+
46+
if (xUrl !== null) {
47+
speaker.socials.x = xUrl;
48+
}
49+
50+
if (instagramUrl !== null) {
51+
speaker.socials.instagram = instagramUrl;
52+
}
53+
54+
if (gravatarUrl !== null) {
55+
speaker.socials.gravatarUrl = gravatarUrl;
56+
}
57+
58+
speakers.set(person.code, speaker);
59+
}
60+
});
61+
}
62+
});
63+
}
64+
65+
if (scheduleData && scheduleData.schedule && scheduleData.schedule.conference && scheduleData.schedule.conference.days) {
66+
scheduleData.schedule.conference.days.forEach(day => {
67+
if (day.rooms) {
68+
Object.values(day.rooms).forEach(roomEvents => {
69+
processEvents(roomEvents);
70+
});
71+
}
72+
});
73+
} else {
74+
console.error('Invalid schedule data structure');
75+
return [];
76+
}
77+
78+
return Array.from(speakers.values());
79+
}
80+
81+
async function fetchGravatarImageUrl(gravatarUrl) {
82+
try {
83+
// Extract username from gravatar URL (e.g., "vivekkeshore" from "https://gravatar.com/vivekkeshore")
84+
const username = gravatarUrl.split('/').pop();
85+
86+
// Fetch profile data from Gravatar API
87+
const apiUrl = `https://api.gravatar.com/v3/profiles/${username}`;
88+
89+
return new Promise((resolve, reject) => {
90+
https.get(apiUrl, (res) => {
91+
let data = '';
92+
93+
res.on('data', (chunk) => {
94+
data += chunk;
95+
});
96+
97+
res.on('end', () => {
98+
try {
99+
const profile = JSON.parse(data);
100+
101+
if (profile.hash) {
102+
// Generate the actual image URL
103+
const imageUrl = `https://gravatar.com/avatar/${profile.hash}.png?size=2048&default=initials`;
104+
resolve(imageUrl);
105+
} else {
106+
resolve(null);
107+
}
108+
} catch (error) {
109+
console.log(`Failed to parse profile data for ${username}:`, error.message);
110+
resolve(null);
111+
}
112+
});
113+
}).on('error', (error) => {
114+
console.log(`Failed to fetch profile for ${username}:`, error.message);
115+
resolve(null);
116+
});
117+
});
118+
} catch (error) {
119+
console.log(`Invalid gravatar URL: ${gravatarUrl}`);
120+
return null;
121+
}
122+
}
123+
124+
async function generateGravatarImageUrls(speakers) {
125+
console.log('Fetching gravatar image URLs...');
126+
127+
for (const speaker of speakers) {
128+
if (speaker.socials && speaker.socials.gravatarUrl && speaker.socials.gravatarUrl.startsWith('https://gravatar.com/')) {
129+
console.log(`Fetching image URL for ${speaker.name}...`);
130+
const imageUrl = await fetchGravatarImageUrl(speaker.socials.gravatarUrl);
131+
if (imageUrl || imageUrl !== null) {
132+
speaker.imageLink = imageUrl;
133+
}
134+
}
135+
}
136+
137+
return speakers;
138+
}
139+
140+
async function main() {
141+
try {
142+
if (!fs.existsSync(scheduleFilePath)) {
143+
console.error(`Schedule file not found: ${scheduleFilePath}`);
144+
process.exit(1);
145+
}
146+
147+
console.log('Loading schedule data...');
148+
const scheduleData = JSON.parse(fs.readFileSync(scheduleFilePath, 'utf8'));
149+
150+
console.log('Extracting speakers...');
151+
let speakers = extractSpeakers(scheduleData);
152+
153+
console.log(`Found ${speakers.length} unique speakers`);
154+
155+
// Fetch gravatar image URLs
156+
speakers = await generateGravatarImageUrls(speakers);
157+
158+
speakers.sort((a, b) => a.name.localeCompare(b.name));
159+
160+
fs.writeFileSync(speakersFilePath, JSON.stringify(speakers, null, 2));
161+
console.log(`Speakers data written to: ${speakersFilePath}`);
162+
163+
const speakersWithLinkedIn = speakers.filter(s => s.socials.linkedin).length;
164+
const speakersWithX = speakers.filter(s => s.socials.x).length;
165+
const speakersWithInstagram = speakers.filter(s => s.socials.instagram).length;
166+
const speakersWithGravatar = speakers.filter(s => s.socials.gravatarUrl).length;
167+
const speakersWithImageLink = speakers.filter(s => s.socials.imageLink).length;
168+
const speakersWithTitle = speakers.filter(s => s.title).length;
169+
170+
console.log('\nSummary:');
171+
console.log(`- Total speakers: ${speakers.length}`);
172+
console.log(`- Speakers with Title: ${speakersWithTitle}`);
173+
console.log(`- Speakers with LinkedIn profiles: ${speakersWithLinkedIn}`);
174+
console.log(`- Speakers with X profiles: ${speakersWithX}`);
175+
console.log(`- Speakers with Instagram profiles: ${speakersWithInstagram}`);
176+
console.log(`- Speakers with gravatar URLs: ${speakersWithGravatar}`);
177+
console.log(`- Speakers with image links: ${speakersWithImageLink}`);
178+
} catch (error) {
179+
console.error('Error processing schedule:', error.message);
180+
process.exit(1);
181+
}
182+
}
183+
184+
if (require.main === module) {
185+
main().catch(error => {
186+
console.error('Error:', error.message);
187+
process.exit(1);
188+
});
189+
}
190+
191+
module.exports = {
192+
extractSpeakers,
193+
generateGravatarImageUrls,
194+
getAnswerByQuestion
195+
};

0 commit comments

Comments
 (0)