11import { BreakTimeslotWithPotentiallyUnknownIcon , FullEvent } from "../../models/Event" ;
22import { z } from "zod" ;
3- import { EVENT_DESCRIPTOR_PARSER , EVENT_FEATURES_CONFIG_PARSER , RATINGS_CONFIG_PARSER } from "../crawler-parsers" ;
4- import { CrawlerKind , TALK_TRACK_FALLBACK_COLORS } from "../crawl" ;
3+ import {
4+ BREAK_PARSER ,
5+ BREAK_TIME_SLOT_PARSER ,
6+ EVENT_DESCRIPTOR_PARSER ,
7+ EVENT_FEATURES_CONFIG_PARSER ,
8+ RATINGS_CONFIG_PARSER , ROOM_PARSER ,
9+ SPEAKER_PARSER ,
10+ TALK_PARSER ,
11+ TALKS_TIME_SLOT_PARSER , THEMABLE_LANGUAGE_PARSER ,
12+ THEMABLE_TALK_FORMAT_PARSER ,
13+ THEMABLE_TALK_TRACK_PARSER
14+ } from "../crawler-parsers" ;
15+ import { CrawlerKind , TALK_FORMAT_FALLBACK_COLORS , TALK_TRACK_FALLBACK_COLORS } from "../crawl" ;
516import { ISO_DATETIME_PARSER } from "../../utils/zod-parsers" ;
617import {
718 DailySchedule ,
@@ -11,14 +22,43 @@ import {
1122 Speaker ,
1223 Talk ,
1324 TalkFormat ,
14- TalksTimeSlot ,
25+ TalksTimeSlot , ThemedTalkFormat ,
1526 ThemedTrack ,
1627} from "../../../../../shared/daily-schedule.firestore" ;
1728import { match , P } from "ts-pattern" ;
1829import { Temporal } from "@js-temporal/polyfill" ;
1930import { ISODatetime } from "../../../../../shared/type-utils" ;
2031import { fillUnknownBreakIcons } from "../utils" ;
2132
33+ const OPENPLANNER_SESSION_PARSER = z . object ( {
34+ speakers : z . array ( z . string ( ) ) ,
35+ tags : z . array ( z . string ( ) ) ,
36+ title : z . string ( ) ,
37+ id : z . string ( ) ,
38+ categoryName : z . string ( ) . nullish ( ) ,
39+ categoryId : z . string ( ) . nullish ( ) , // talkTrack
40+ formatId : z . string ( ) . nullish ( ) , // talkFormat
41+ abstract : z . string ( ) . nullish ( ) ,
42+ trackId : z . string ( ) , // room
43+ trackTitle : z . string ( ) ,
44+ startTime : ISO_DATETIME_PARSER . optional ( ) ,
45+ endTime : ISO_DATETIME_PARSER . optional ( ) ,
46+ type : z . literal ( 'undefined' ) . optional ( ) . default ( 'undefined' ) ,
47+ } ) ;
48+
49+ const OPENPLANNER_SPEAKER_PARSER = z . object ( {
50+ id : z . string ( ) ,
51+ name : z . string ( ) ,
52+ photoUrl : z . string ( ) . optional ( ) ,
53+ socials : z . array ( z . object ( {
54+ icon : z . union ( [ z . literal ( "" ) , z . literal ( "site" ) , z . literal ( "twitter" ) , z . literal ( "linkedin" ) , z . literal ( "github" ) ] ) ,
55+ link : z . string ( ) ,
56+ name : z . string ( ) . optional ( ) ,
57+ } ) ) ,
58+ bio : z . string ( ) . nullish ( ) ,
59+ company : z . string ( ) . nullish ( )
60+ } )
61+
2262export const OPENPLANNER_DESCRIPTOR_PARSER = EVENT_DESCRIPTOR_PARSER . omit ( {
2363 id : true ,
2464 days : true ,
@@ -37,6 +77,16 @@ export const OPENPLANNER_DESCRIPTOR_PARSER = EVENT_DESCRIPTOR_PARSER.omit({
3777 openPlannerGeneratedJson : z . string ( ) ,
3878 language : z . string ( ) ,
3979 ratings : RATINGS_CONFIG_PARSER ,
80+ additionalBreakTimeslots : z . array ( BREAK_TIME_SLOT_PARSER . omit ( { type : true , id : true } ) ) . optional ( ) . default ( [ ] ) ,
81+ additionalSessions : z . record ( z . string ( ) , OPENPLANNER_SESSION_PARSER
82+ . omit ( { type : true } )
83+ . extend ( {
84+ type : z . literal ( 'talk' ) . optional ( ) . default ( 'talk' )
85+ } ) ) . optional ( ) . default ( { } ) ,
86+ additionalSpeakers : z . record ( z . string ( ) , OPENPLANNER_SPEAKER_PARSER ) . optional ( ) . default ( { } ) ,
87+ additionalTalkFormats : z . array ( THEMABLE_TALK_FORMAT_PARSER ) . optional ( ) . default ( [ ] ) ,
88+ additionalTalkTracks : z . array ( THEMABLE_TALK_TRACK_PARSER ) . optional ( ) . default ( [ ] ) ,
89+ additionalRooms : z . array ( ROOM_PARSER ) . optional ( ) . default ( [ ] ) ,
4090} )
4191
4292export const OPENPLANNER_GENERATED_SCHEDULE_PARSER = EVENT_DESCRIPTOR_PARSER . omit ( {
@@ -50,32 +100,8 @@ export const OPENPLANNER_GENERATED_SCHEDULE_PARSER = EVENT_DESCRIPTOR_PARSER.omi
50100} ) . extend ( {
51101 features : EVENT_FEATURES_CONFIG_PARSER . omit ( { ratings : true } ) . extend ( {
52102 } ) ,
53- sessions : z . record ( z . string ( ) , z . object ( {
54- speakers : z . array ( z . string ( ) ) ,
55- tags : z . array ( z . string ( ) ) ,
56- title : z . string ( ) ,
57- id : z . string ( ) ,
58- categoryName : z . string ( ) . nullish ( ) ,
59- categoryId : z . string ( ) . nullish ( ) , // talkTrack
60- formatId : z . string ( ) . nullish ( ) , // talkFormat
61- abstract : z . string ( ) . nullish ( ) ,
62- trackId : z . string ( ) , // room
63- trackTitle : z . string ( ) ,
64- startTime : ISO_DATETIME_PARSER . optional ( ) ,
65- endTime : ISO_DATETIME_PARSER . optional ( ) ,
66- } ) ) ,
67- speakers : z . record ( z . string ( ) , z . object ( {
68- id : z . string ( ) ,
69- name : z . string ( ) ,
70- photoUrl : z . string ( ) . optional ( ) ,
71- socials : z . array ( z . object ( {
72- icon : z . union ( [ z . literal ( "" ) , z . literal ( "site" ) , z . literal ( "twitter" ) , z . literal ( "linkedin" ) , z . literal ( "github" ) ] ) ,
73- link : z . string ( ) ,
74- name : z . string ( ) . optional ( ) ,
75- } ) ) ,
76- bio : z . string ( ) . nullish ( ) ,
77- company : z . string ( ) . nullish ( )
78- } ) )
103+ sessions : z . record ( z . string ( ) , OPENPLANNER_SESSION_PARSER ) ,
104+ speakers : z . record ( z . string ( ) , OPENPLANNER_SPEAKER_PARSER ) ,
79105} )
80106
81107export const OPENPLANNER_CRAWLER : CrawlerKind < typeof OPENPLANNER_DESCRIPTOR_PARSER > = {
@@ -88,12 +114,12 @@ export const OPENPLANNER_CRAWLER: CrawlerKind<typeof OPENPLANNER_DESCRIPTOR_PARS
88114 . then ( data => OPENPLANNER_GENERATED_SCHEDULE_PARSER . parse ( data ) ) ;
89115
90116 const talks : DetailedTalk [ ] = [ ] ,
91- tracks : ThemedTrack [ ] = openPlannerSchedule . talkTracks ,
92- formats : TalkFormat [ ] = openPlannerSchedule . talkFormats ,
93- rooms : Room [ ] = openPlannerSchedule . rooms ,
117+ tracks : ThemedTrack [ ] = openPlannerSchedule . talkTracks . concat ( descriptor . additionalTalkTracks ) ,
118+ formats : TalkFormat [ ] = openPlannerSchedule . talkFormats . concat ( descriptor . additionalTalkFormats ) ,
119+ rooms : Room [ ] = openPlannerSchedule . rooms . concat ( descriptor . additionalRooms ) ,
94120 timezone = openPlannerSchedule . timezone ;
95121
96- const speakers = Object . values ( openPlannerSchedule . speakers ) . map ( openPlannerSpeaker => {
122+ const speakers = Object . values ( { ... openPlannerSchedule . speakers , ... descriptor . additionalSpeakers } ) . map ( openPlannerSpeaker => {
97123 const speaker : Speaker = {
98124 id : openPlannerSpeaker . id ,
99125 fullName : openPlannerSpeaker . name ,
@@ -117,7 +143,7 @@ export const OPENPLANNER_CRAWLER: CrawlerKind<typeof OPENPLANNER_DESCRIPTOR_PARS
117143 return speaker ;
118144 } )
119145
120- let trackFallbackColors = 0
146+ let trackFallbackColors = 0 , formatFallbackColors = 0 ;
121147 const UNKNOWN_TRACK_ID = 'unknown'
122148 // Auto-appending an 'unknown' track as openplanner's category might be nullable (ex: on sunnytech, for keynotes)
123149 // => in that case, we'll use this special 'unknown' track name
@@ -128,7 +154,7 @@ export const OPENPLANNER_CRAWLER: CrawlerKind<typeof OPENPLANNER_DESCRIPTOR_PARS
128154 } )
129155
130156 const dailySchedules = openPlannerSchedule . days . map ( ( day , dayIndex ) => {
131- const dailyRawSessions = Object . values ( openPlannerSchedule . sessions )
157+ const dailyRawSessions = Object . values ( { ... openPlannerSchedule . sessions , ... descriptor . additionalSessions } )
132158 . filter ( session => session . startTime && session . endTime && session . startTime . startsWith ( day . localDate ) )
133159
134160 const { talkTimeslots, breakTimeslots : breakTimeslotsWithPotentiallyUnknownIcons } = dailyRawSessions . reduce ( ( { talkTimeslots, breakTimeslots} , session ) => {
@@ -140,7 +166,7 @@ export const OPENPLANNER_CRAWLER: CrawlerKind<typeof OPENPLANNER_DESCRIPTOR_PARS
140166
141167 const timeslotId = `${ start } --${ end } ` as const
142168
143- const room = match ( rooms . find ( r => r . id === session . trackId ) )
169+ const room = match ( rooms . find ( r => r . id === session . trackId ) ) // track is room in openplanner
144170 . with ( P . nullish , ( ) => {
145171 const newRoom : Room = {
146172 id : session . trackId ,
@@ -151,7 +177,7 @@ export const OPENPLANNER_CRAWLER: CrawlerKind<typeof OPENPLANNER_DESCRIPTOR_PARS
151177 return newRoom ;
152178 } ) . otherwise ( room => room )
153179
154- if ( ! session . formatId ) {
180+ if ( ! session . formatId && session . type !== 'talk' ) {
155181 const breakSlot : BreakTimeslotWithPotentiallyUnknownIcon = {
156182 type : 'break' ,
157183 start, end,
@@ -242,7 +268,12 @@ export const OPENPLANNER_CRAWLER: CrawlerKind<typeof OPENPLANNER_DESCRIPTOR_PARS
242268
243269 const breakTimeslots = fillUnknownBreakIcons (
244270 { isFirst : dayIndex === 0 , isLast : dayIndex === openPlannerSchedule . days . length - 1 } ,
245- timezone , breakTimeslotsWithPotentiallyUnknownIcons , talkTimeslots ) ;
271+ timezone , breakTimeslotsWithPotentiallyUnknownIcons , talkTimeslots )
272+ . concat ( ( descriptor . additionalBreakTimeslots || [ ] ) . map ( partialTimeslot => ( {
273+ ...partialTimeslot ,
274+ id : `${ partialTimeslot . start as ISODatetime } --${ partialTimeslot . end as ISODatetime } ` ,
275+ type : 'break'
276+ } ) ) )
246277
247278 const dailySchedule : DailySchedule = {
248279 day : day . id ,
0 commit comments