-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9d3d191
commit 5ac1b52
Showing
6 changed files
with
422 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { RoomKind } from "../../models/room_kinds"; | ||
import { IAuditorium, IConference, IInterestRoom, IPerson, ITalk, Role } from "../../models/schedule"; | ||
import { FOSDEMSpecificJSONSchedule, FOSDEMPerson, FOSDEMTrack, FOSDEMTalk } from "./jsontypes/FosdemJsonSchedule.schema"; | ||
import * as moment from "moment"; | ||
import { AuditoriumId, InterestId, TalkId } from "../IScheduleBackend"; | ||
import { IConfig } from "../../config"; | ||
|
||
/** | ||
* Loader and holder for FOSDEM-specific JSON schedules, acquired from the | ||
* custom `/p/matrix` endpoint on the Pretalx instance. | ||
*/ | ||
export class FosdemJsonScheduleLoader { | ||
public readonly conference: IConference; | ||
public readonly auditoriums: Map<AuditoriumId, IAuditorium>; | ||
public readonly talks: Map<TalkId, ITalk>; | ||
public readonly interestRooms: Map<InterestId, IInterestRoom>; | ||
public readonly conferenceId: string; | ||
|
||
constructor(jsonDesc: object, globalConfig: IConfig) { | ||
// TODO: Validate and give errors. Assuming it's correct is a bit cheeky. | ||
const jsonSchedule = jsonDesc as FOSDEMSpecificJSONSchedule; | ||
|
||
this.auditoriums = new Map(); | ||
|
||
for (let rawTrack of jsonSchedule.tracks) { | ||
// Tracks are now (since 2025) mapped 1:1 to auditoria | ||
const auditorium = this.convertAuditorium(rawTrack); | ||
if (this.auditoriums.has(auditorium.id)) { | ||
throw `Conflict in auditorium ID «${auditorium.id}»!`; | ||
} | ||
this.auditoriums.set(auditorium.id, auditorium); | ||
} | ||
|
||
this.talks = new Map(); | ||
|
||
for (let rawTalk of jsonSchedule.talks) { | ||
const talk = this.convertTalk(rawTalk); | ||
if (this.talks.has(talk.id)) { | ||
const conflictingTalk = this.talks.get(talk.id)!; | ||
throw `Talk ID ${talk.id} is not unique — occupied by both «${talk.title}» and «${conflictingTalk.title}»!`; | ||
} | ||
const auditorium = this.auditoriums.get(talk.auditoriumId); | ||
if (!auditorium) { | ||
throw `Talk ID ${talk.id} relies on non-existent auditorium ${talk.auditoriumId}`; | ||
} | ||
auditorium.talks.set(talk.id, talk); | ||
this.talks.set(talk.id, talk); | ||
} | ||
|
||
// TODO: Interest rooms are currently not supported by the JSON schedule backend. | ||
this.interestRooms = new Map(); | ||
|
||
this.conference = { | ||
title: globalConfig.conference.name, | ||
auditoriums: Array.from(this.auditoriums.values()), | ||
interestRooms: Array.from(this.interestRooms.values()) | ||
}; | ||
} | ||
|
||
private convertPerson(person: FOSDEMPerson): IPerson { | ||
if (! Object.values<string>(Role).includes(person.event_role)) { | ||
throw new Error("unknown role: " + person.event_role); | ||
} | ||
return { | ||
id: person.person_id.toString(), | ||
name: person.name, | ||
matrix_id: person.matrix_id, | ||
email: person.email, | ||
// safety: checked above | ||
role: person.event_role as Role, | ||
}; | ||
} | ||
|
||
private convertTalk(talk: FOSDEMTalk): ITalk { | ||
const auditoriumId = talk.track.id.toString(); | ||
const startMoment = moment.utc(talk.start_datetime, moment.ISO_8601, true); | ||
const endMoment = startMoment.add(talk.duration, "minutes"); | ||
|
||
return { | ||
id: talk.event_id.toString(), | ||
title: talk.title, | ||
|
||
// Pretalx does not support this concept. FOSDEM 2024 ran with empty strings. From 2025 we hardcode this as empty for now. | ||
subtitle: "", | ||
|
||
auditoriumId, | ||
|
||
// Hardcoded: all talks are now live from FOSDEM 2025 | ||
prerecorded: false, | ||
|
||
// This is sketchy, but the QA start-time is not applicable except to prerecorded talks. | ||
// Even then, it's not clear why it would be different from the end of the talk? | ||
// This is overall a messy concept, but the only thing that matters now is whether this is | ||
// null (Q&A disabled) or non-null (Q&A enabled, with reminder 5 minutes before the end of the talk slot). | ||
// TODO overhaul replace with a boolean instead...? | ||
qa_startTime: 0, | ||
|
||
// Since the talks are not pre-recorded, the livestream is considered ended when the event ends. | ||
livestream_endTime: endMoment.valueOf(), | ||
|
||
speakers: talk.persons.map(person => this.convertPerson(person)), | ||
|
||
// Must .clone() here because .startOf() mutates the moment(!) | ||
dateTs: startMoment.clone().startOf("day").valueOf(), | ||
startTime: startMoment.valueOf(), | ||
endTime: endMoment.valueOf(), | ||
}; | ||
} | ||
|
||
private convertAuditorium(track: FOSDEMTrack): IAuditorium { | ||
return { | ||
id: track.id.toString(), | ||
slug: track.slug, | ||
name: track.name, | ||
kind: RoomKind.Auditorium, | ||
// This will be populated afterwards | ||
talks: new Map(), | ||
// Hardcoded: FOSDEM is always physical now. | ||
isPhysical: true, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
src/backends/json/jsonschemas/FosdemJsonSchedule.schema.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
{ | ||
"$schema": "https://json-schema.org/draft/2020-12/schema", | ||
"$id": "https://matrix.org/conference-bot/FosdemJsonSchedule.schema.json", | ||
"title": "FOSDEM-Specific JSON Schedule", | ||
"description": "A simple FOSDEM-specific JSON format to describe the schedule for a FOSDEM conference driven by conference-bot.", | ||
|
||
"type": "object", | ||
|
||
"properties": { | ||
"talks": { | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/definitions/talk" | ||
} | ||
}, | ||
"tracks": { | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/definitions/track" | ||
} | ||
} | ||
}, | ||
|
||
"required": [ "talks", "tracks" ], | ||
|
||
"definitions": { | ||
|
||
"track": { | ||
"title": "FOSDEM Track", | ||
"description": "Information about a sequence of talks", | ||
"type": "object", | ||
"properties": { | ||
"id": { | ||
"type": "number", | ||
"description": "Stable ID for the track" | ||
}, | ||
"slug": { | ||
"type": "string", | ||
"description": "Stable semi-human-readable slug for the track" | ||
}, | ||
"name": { | ||
"type": "string", | ||
"description": "Human-readable name of the track" | ||
}, | ||
"type": { | ||
"type": "string", | ||
"description": "'devroom' or 'maintrack' (TODO encode this in schema)" | ||
}, | ||
"managers": { | ||
"type": "array", | ||
"description": "List of staff (co-ordinators right now) that apply to the entire track.", | ||
"items": { | ||
"$ref": "#/definitions/person" | ||
} | ||
} | ||
}, | ||
"required": [ "id", "slug", "name", "type", "managers" ] | ||
}, | ||
|
||
|
||
"talk": { | ||
"title": "FOSDEM Talk", | ||
"description": "Information about a scheduled talk", | ||
"type": "object", | ||
"properties": { | ||
"event_id": { | ||
"description": "Unique ID for the talk", | ||
"type": "integer", | ||
"minimum": 0 | ||
}, | ||
"title": { | ||
"type": "string", | ||
"description": "Human-readable name for the talk" | ||
}, | ||
"start_datetime": { | ||
"type": "string", | ||
"description": "Date and time, in RFC3339 format with Z timezone offset, of the start of the talk", | ||
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$" | ||
}, | ||
"duration": { | ||
"type": "number", | ||
"description": "Duration of the talk, in minutes" | ||
}, | ||
"persons": { | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/definitions/person" | ||
} | ||
}, | ||
"track": { | ||
"description": "Information about what track the talk is in. N.B. In practice more fields are contained here but only ID is used.", | ||
"type": "object", | ||
"properties": { | ||
"id": { | ||
"type": "number", | ||
"description": "The Track ID of the track that the talk is in" | ||
} | ||
}, | ||
"required": ["id"] | ||
}, | ||
"conference_room": { | ||
"type": "string", | ||
"description": "Name of the physical (in real life) room that the talk is held in." | ||
} | ||
}, | ||
"required": [ "event_id", "title", "start_datetime", "duration", "persons", "track", "conference_room" ] | ||
}, | ||
|
||
|
||
"person": { | ||
"title": "FOSDEM Person", | ||
"description": "Information about someone who is giving a talk or is assisting with co-ordination", | ||
"type": "object", | ||
"properties": { | ||
"person_id": { | ||
"type": "number", | ||
"description": "ID of the person" | ||
}, | ||
"event_role": { | ||
"type": "string", | ||
"description": "What kind of role the person has for this talk (speaker/coordinator)" | ||
}, | ||
"name": { | ||
"type": "string", | ||
"description": "The name of the person" | ||
}, | ||
"email": { | ||
"type": "string", | ||
"description": "The e-mail address of the person. May be an empty string if not available." | ||
}, | ||
"matrix_id": { | ||
"type": "string", | ||
"description": "The Matrix User ID of the person. May be an empty string if not available. Has historically not been validated thoroughly." | ||
} | ||
}, | ||
"required": [ "person_id", "event_role", "name", "email", "matrix_id" ] | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.