Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"build": "npm run clean && tsc && NODE_ENV=production rollup --config rollup.config.js",
"clean": "rimraf dist out-tsc",
"deploy": "npm run build && NODE_ENV=production firebase deploy",
"experimental:sessionize:import": "ts-node-script ./scripts/experimental-sessionize-import",
"firestore:copy": "ts-node-script ./scripts/firestore-copy",
"firestore:init": "firebase functions:config:set schedule.enabled=true && firebase deploy --except hosting && ts-node-script ./scripts/firestore-init",
"fix": "concurrently npm:fix:*",
Expand Down Expand Up @@ -99,6 +100,7 @@
"jest-runner-prettier": "^0.3.6",
"jest-runner-stylelint": "^2.3.7",
"jest-runner-tsc": "^1.6.0",
"moment": "^2.29.0",
"nunjucks": "^3.2.2",
"prettier": "2.1.2",
"prettier-plugin-package": "1.1.0",
Expand Down
19 changes: 19 additions & 0 deletions scripts/experimental-sessionize-import/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { importSchedule } from './schedule';
import { importSessions } from './sessions';
import { importSpeakers } from './speakers';

// TODO: accept sessionize file as a param
// TODO: support not having a schedule
// TODO: support multiple tracks on multiple days

importSpeakers()
.then(() => importSessions())
.then(() => importSchedule())
.then(() => {
console.log('Finished');
process.exit();
})
.catch((err: Error) => {
console.log(err);
process.exit();
});
90 changes: 90 additions & 0 deletions scripts/experimental-sessionize-import/schedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import moment from 'moment';
import data from '../../sessionize.json';
import { Schedule } from '../../src/models/schedule';
import { Timeslot } from '../../src/models/timeslot';
import { Track } from '../../src/models/track';
import { firestore } from '../firebase-config';
import { SessionizeSession } from './types';

export const importSchedule = async () => {
const schedule: Schedule = await convertSchedule();
const { length } = await save(schedule);
console.log(`Imported data for ${length} day(s)`);
};

const convertTracks = (): Track[] => {
return data.rooms.map(({ name, id }) => ({ title: name, sessionizeId: id }));
};

const getSessionids = async (): Promise<{ [id: string]: string }> => {
const { docs } = await firestore.collection('sessions').get();
const ids: { [id: string]: string } = {};

docs.forEach((doc) => {
const { sessionizeId } = doc.data() as SessionizeSession;
ids[sessionizeId] = doc.id;
});

return ids;
};

const convertSchedule = async (): Promise<Schedule> => {
const sessionIds = await getSessionids();
const schedule: Schedule = {};

for (const sessionData of data.sessions) {
console.log(`Importing session ${sessionData.title} at ${sessionData.startsAt}`);
const date = moment(sessionData.startsAt).format('YYYY-MM-DD');
if (!schedule[date]) {
schedule[date] = {
date,
dateReadable: moment(sessionData.startsAt).format('MMMM D'),
timeslots: [],
tracks: convertTracks(),
};
}
const startTime = time(sessionData.startsAt);
const existingTimeslot = schedule[date].timeslots.find((timeslot) => {
return timeslot.startTime === startTime;
});

if (existingTimeslot) {
existingTimeslot.sessions.push({
items: [sessionIds[sessionData.id]],
});
} else {
const timeslot: Timeslot = {
endTime: time(sessionData.endsAt),
startTime,
sessions: [
{
items: [sessionIds[sessionData.id]],
},
],
};
schedule[date].timeslots.push(timeslot);
}
}

return schedule;
};

const time = (date: string): string => {
return moment(date).format('HH:mm');
};

const save = (schedule: Schedule) => {
const { length } = Object.keys(schedule);
if (length === 0) {
throw new Error('No schedule days found!');
}
console.log(`Importing ${length} day(s)...`);

const batch = firestore.batch();

Object.keys(schedule).forEach(async (date) => {
batch.set(firestore.collection('schedule').doc(date), schedule[date]);
});

return batch.commit();
};
111 changes: 111 additions & 0 deletions scripts/experimental-sessionize-import/sessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import data from '../../sessionize.json';
import { firestore } from '../firebase-config';
import { Answer, SessionizeSession } from './types';
import { categoryItem, nameToId, questionAnswer } from './utils';

export const importSessions = async () => {
const sessions: SessionizeSession[] = convertSessions();
const { length } = await save(sessions);
console.log(`Imported data for ${length} sessions`);
};

const cleanTags = (text: string): string[] => {
return text
.split(',')
.map((dirtyTag) => dirtyTag.trim())
.filter(Boolean);
};

const cleanComplexity = (text: string): string => {
return text === 'Introductory and overview' ? 'Beginner' : text;
};

const convertSpeakerIds = (ids: string[]): string[] => {
return ids.map((sessionizeId) => {
const speaker = data.speakers.find(({ id }) => id === sessionizeId);
if (speaker) {
return nameToId(speaker.fullName);
} else {
throw new Error(`Unable to find speaker with id ${sessionizeId}`);
}
});
};

const matchIcon = ({
title,
isServiceSession,
}: {
title: string;
isServiceSession: boolean;
}): string => {
if (isServiceSession) {
switch (true) {
case title.toLowerCase().includes('break'):
return 'coffee-break';
case title.toLowerCase().includes('closing'):
case title.toLowerCase().includes('welcome'):
return 'opening';
case title.toLowerCase().includes('lunch'):
return 'lunch';
case title.toLowerCase().includes('party'):
return 'party';
default:
return '';
}
}
return '';
};

const getTags = (answers: Answer[], items: number[]): string[] => {
const isKeynote = categoryItem('Session format', items).toLowerCase() === 'keynote';
const isLive = categoryItem('Delivery', items).toLowerCase() === 'live';
const isPrerecorded = categoryItem('Delivery', items).toLowerCase() === 'pre-recorded';
const tags = [
questionAnswer('Tags', answers),
isKeynote ? 'keynote' : '',
isLive ? 'live' : '',
isPrerecorded ? 'prerecorded' : '',
].join(',');
return cleanTags(tags);
};

const convertSessions = (): SessionizeSession[] => {
const sessions: SessionizeSession[] = [];
for (const sessionData of data.sessions) {
console.log(`Importing session ${sessionData.title}`);
sessions.push({
sessionizeId: sessionData.id,
complexity: cleanComplexity(categoryItem('Level', sessionData.categoryItems)),
description: sessionData.description || '',
icon: matchIcon(sessionData),
image: '',
language: 'en',
speakers: convertSpeakerIds(sessionData.speakers),
tags: getTags(sessionData.questionAnswers, sessionData.categoryItems),
title: sessionData.title,
});
}
return sessions;
};

const save = async (sessions: SessionizeSession[]) => {
if (sessions.length === 0) {
throw new Error('No sessions found!');
}
console.log(`Importing ${sessions.length} sessions...`);

const collectionRef = firestore.collection('sessions');
const { docs } = await collectionRef.get();
const batch = firestore.batch();

sessions.forEach(async (session) => {
const existingDoc = docs.find((doc) => doc.data().sessionizeId === session.sessionizeId);
if (existingDoc) {
batch.set(existingDoc.ref, session);
} else {
batch.set(collectionRef.doc(), session);
}
});

return batch.commit();
};
94 changes: 94 additions & 0 deletions scripts/experimental-sessionize-import/speakers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import data from '../../sessionize.json';
import { Badge } from '../../src/models/badge';
import { Social } from '../../src/models/social';
import { firestore } from '../firebase-config';
import { Link, SessionizeSpeaker } from './types';
import { nameToId, questionAnswer } from './utils';

export const importSpeakers = async () => {
const speakers: SessionizeSpeaker[] = convertSpeakers();
await save(speakers);
};

const selectBadges = (company: string): Badge[] => {
return [
{
description: 'Google',
link: 'https://www.google.com',
name: 'google',
},
].filter(({ name }) => name.toLowerCase() === company.toLowerCase());
};

const selectSocials = (links: Link[]): Social[] => {
const approvedSources = [
'facebook',
'github',
'instagram',
'linkedin',
'twitter',
'website',
'youtube',
];
const approvedLinks = links.filter(({ linkType }) =>
approvedSources.includes(linkType.toLowerCase())
);

return approvedLinks.map((link) => {
return {
icon: link.linkType.toLowerCase(),
link: link.url,
name: link.title,
};
});
};

const convertSpeakers = (): SessionizeSpeaker[] => {
const speakers: SessionizeSpeaker[] = [];
for (const speakerData of data.speakers) {
console.log(`Importing speaker ${speakerData.fullName}`);
speakers.push({
sessionizeId: speakerData.id,
badges: selectBadges(questionAnswer('Company', speakerData.questionAnswers)),
bio: speakerData.bio,
company: questionAnswer('Company', speakerData.questionAnswers),
companyLogo: '',
companyLogoUrl: '',
country: questionAnswer('Location', speakerData.questionAnswers),
featured: speakerData.isTopSpeaker,
name: speakerData.fullName,
order: 0,
photo: '',
photoUrl: speakerData.profilePicture,
pronouns: questionAnswer('Pronouns', speakerData.questionAnswers),
shortBio: '',
socials: selectSocials(speakerData.links),
title: speakerData.tagLine,
});
}
return speakers;
};

const save = async (speakers: SessionizeSpeaker[]) => {
if (speakers.length === 0) {
throw new Error('No speakers found!');
}
console.log(`Importing ${speakers.length} speakers...`);

const collectionRef = firestore.collection('speakers');
const { docs } = await collectionRef.get();
const batch = firestore.batch();

speakers.forEach(async (speaker) => {
const existingDoc = docs.find((doc) => doc.data().sessionizeId === speaker.sessionizeId);
if (existingDoc) {
batch.set(existingDoc.ref, speaker);
} else {
const id = nameToId(speaker.name);
batch.set(collectionRef.doc(id), speaker);
}
});

const { length } = await batch.commit();
console.log(`Imported data for ${length} speakers`);
};
28 changes: 28 additions & 0 deletions scripts/experimental-sessionize-import/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Session } from '../../src/models/session';
import { Speaker } from '../../src/models/speaker';

export interface Question {
id: number;
question: string;
questionType: string;
sort: number;
}

export interface Answer {
questionId: number;
answerValue: string;
}

export interface Link {
title: string;
url: string;
linkType: string;
}

export type SessionizeSpeaker = Speaker & {
sessionizeId: string;
};

export type SessionizeSession = Session & {
sessionizeId: string;
};
Loading