Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/sessionize

src/project.ts

280 lines · 7.7 KB · TypeScript
import { normalizeId } from './client.ts'
import type {
	EventSchedule,
	EventSummary,
	ScheduleSlot,
	SessionizeAllResult,
	SessionizeAllSession,
	SessionizeAllSpeaker,
	SessionizeListedSession,
	SessionizeListedSpeaker,
	SessionizeScheduleDay,
	SessionizeSessionGroup,
	SessionizeSpeakerRef,
	SlimSession,
	SlimSpeaker,
} from './types.ts'

function roomNameById(
	rooms: Array<{ id: number; name: string }>,
): Map<number, string> {
	return new Map(rooms.map((room) => [room.id, room.name]))
}

function categoryValues(
	session: SessionizeAllSession,
	categories: SessionizeAllResult['categories'],
): Array<{ name: string; values: Array<string> }> {
	const selected = new Set(session.categoryItems)
	return categories
		.map((category) => ({
			name: category.title,
			values: category.items
				.filter((item) => selected.has(item.id))
				.map((item) => item.name),
		}))
		.filter((category) => category.values.length > 0)
}

function listedCategoryValues(
	session: SessionizeListedSession,
): Array<{ name: string; values: Array<string> }> {
	return session.categories
		.map((category) => ({
			name: category.name,
			values: category.categoryItems.map((item) => item.name),
		}))
		.filter((category) => category.values.length > 0)
}

export function slimSessionFromAll(
	session: SessionizeAllSession,
	speakersById: Map<string, SessionizeAllSpeaker>,
	rooms: Array<{ id: number; name: string }>,
	categories: SessionizeAllResult['categories'],
): SlimSession {
	const roomsById = roomNameById(rooms)
	return {
		id: normalizeId(session.id),
		title: session.title,
		startsAt: session.startsAt,
		endsAt: session.endsAt,
		roomId: session.roomId,
		room: session.roomId == null ? null : (roomsById.get(session.roomId) ?? null),
		speakers: session.speakers.map((speakerId) => {
			const speaker = speakersById.get(normalizeId(speakerId))
			return {
				id: normalizeId(speakerId),
				name: speaker?.fullName ?? normalizeId(speakerId),
			}
		}),
		categories: categoryValues(session, categories),
		isServiceSession: session.isServiceSession,
		isPlenumSession: session.isPlenumSession,
		status: session.status,
	}
}

export function slimSessionFromListed(
	session: SessionizeListedSession,
): SlimSession {
	return {
		id: normalizeId(session.id),
		title: session.title,
		startsAt: session.startsAt,
		endsAt: session.endsAt,
		roomId: session.roomId,
		room: session.room,
		speakers: session.speakers.map((speaker) => ({
			id: normalizeId(speaker.id),
			name: speaker.name,
		})),
		categories: listedCategoryValues(session),
		isServiceSession: session.isServiceSession,
		isPlenumSession: session.isPlenumSession,
		status: session.status,
	}
}

export function flattenSessionGroups(
	groups: Array<SessionizeSessionGroup>,
): Array<SlimSession> {
	const byId = new Map<string, SlimSession>()
	for (const group of groups) {
		for (const session of group.sessions) {
			const slim = slimSessionFromListed(session)
			const existing = byId.get(slim.id)
			if (existing) {
				const alreadyHasGroup = existing.categories.some(
					(category) =>
						category.values.includes(group.groupName) ||
						category.name === group.groupName,
				)
				if (!alreadyHasGroup && group.groupName) {
					existing.categories = [
						...existing.categories,
						{ name: 'Group', values: [group.groupName] },
					]
				}
				continue
			}
			byId.set(slim.id, slim)
		}
	}
	return [...byId.values()]
}

export function slimSpeakerFromAll(
	speaker: SessionizeAllSpeaker,
	sessionsById: Map<string, SessionizeAllSession>,
): SlimSpeaker {
	const sessionIds = speaker.sessions.map((id) => normalizeId(id))
	return {
		id: normalizeId(speaker.id),
		fullName: speaker.fullName,
		firstName: speaker.firstName,
		lastName: speaker.lastName,
		tagLine: speaker.tagLine,
		isTopSpeaker: speaker.isTopSpeaker,
		profilePicture: speaker.profilePicture,
		links: speaker.links,
		sessionIds,
		sessionTitles: sessionIds
			.map((id) => sessionsById.get(id)?.title)
			.filter((title): title is string => Boolean(title)),
	}
}

export function slimSpeakerFromListed(
	speaker: SessionizeListedSpeaker,
): SlimSpeaker {
	return {
		id: normalizeId(speaker.id),
		fullName: speaker.fullName,
		firstName: speaker.firstName,
		lastName: speaker.lastName,
		tagLine: speaker.tagLine,
		isTopSpeaker: speaker.isTopSpeaker,
		profilePicture: speaker.profilePicture,
		links: speaker.links,
		sessionIds: speaker.sessions.map((session) => normalizeId(session.id)),
		sessionTitles: speaker.sessions.map((session) => session.name),
	}
}

export function projectEvent(
	endpointId: string,
	data: SessionizeAllResult,
): EventSummary {
	const speakersById = new Map(
		data.speakers.map((speaker) => [normalizeId(speaker.id), speaker]),
	)
	const sessionsById = new Map(
		data.sessions.map((session) => [normalizeId(session.id), session]),
	)
	return {
		endpointId,
		sessionCount: data.sessions.length,
		speakerCount: data.speakers.length,
		roomCount: data.rooms.length,
		categoryCount: data.categories.length,
		rooms: data.rooms.map((room) => ({ id: room.id, name: room.name })),
		categories: data.categories.map((category) => ({
			id: category.id,
			title: category.title,
			type: category.type,
			items: category.items.map((item) => ({ id: item.id, name: item.name })),
		})),
		sessions: data.sessions.map((session) =>
			slimSessionFromAll(session, speakersById, data.rooms, data.categories),
		),
		speakers: data.speakers.map((speaker) =>
			slimSpeakerFromAll(speaker, sessionsById),
		),
	}
}

function scheduleSessionToSlot(
	session: SessionizeScheduleDay['rooms'][number]['sessions'][number],
	room: { id: number; name: string },
): ScheduleSlot {
	const speakers: Array<SessionizeSpeakerRef> = session.speakers.map(
		(speaker) => ({
			id: normalizeId(speaker.id),
			name: speaker.name,
		}),
	)
	return {
		startsAt: session.startsAt,
		endsAt: session.endsAt,
		roomId: session.roomId ?? room.id,
		room: session.room ?? room.name,
		session: {
			id: normalizeId(session.id),
			title: session.title,
			isServiceSession: session.isServiceSession,
			isPlenumSession: session.isPlenumSession,
			speakers,
		},
	}
}

export function projectSchedule(
	endpointId: string,
	days: Array<SessionizeScheduleDay>,
): EventSchedule {
	return {
		endpointId,
		days: days.map((day) => {
			const slotsByKey = new Map<string, ScheduleSlot>()
			for (const room of day.rooms) {
				for (const session of room.sessions ?? []) {
					const slot = scheduleSessionToSlot(session, room)
					slotsByKey.set(`${slot.session.id}:${slot.roomId}:${slot.startsAt}`, slot)
				}
			}
			const slots = [...slotsByKey.values()].sort((left, right) => {
				const start = (left.startsAt ?? '').localeCompare(right.startsAt ?? '')
				if (start !== 0) return start
				return (left.room ?? '').localeCompare(right.room ?? '')
			})
			return {
				date: day.date,
				rooms: day.rooms.map((room) => ({ id: room.id, name: room.name })),
				slots,
			}
		}),
	}
}

export function matchSession(
	session: SlimSession,
	query: string,
): boolean {
	const q = query.trim()
	if (!q) return false
	if (normalizeId(session.id) === normalizeId(q)) return true
	if (session.title.toLocaleLowerCase() === q.toLocaleLowerCase()) return true
	return (
		session.title.toLocaleLowerCase().includes(q.toLocaleLowerCase()) ||
		session.speakers.some((speaker) =>
			speaker.name.toLocaleLowerCase().includes(q.toLocaleLowerCase()),
		)
	)
}

export function matchSpeaker(speaker: SlimSpeaker, query: string): boolean {
	const q = query.trim()
	if (!q) return false
	if (normalizeId(speaker.id) === normalizeId(q)) return true
	const haystacks = [
		speaker.fullName,
		speaker.firstName,
		speaker.lastName,
		speaker.tagLine ?? '',
		...speaker.sessionTitles,
	]
	return haystacks.some((value) =>
		value.toLocaleLowerCase().includes(q.toLocaleLowerCase()),
	)
}