11import * as functions from "firebase-functions" ;
2- import { extractSingleQueryParam , roundedAverage , sendResponseMessage } from "../utils" ;
2+ import { extractSingleQueryParam , logPerf , roundedAverage , sendResponseMessage } from "../utils" ;
33import {
44 checkEventLastUpdate ,
55 eventTalkStatsFor
@@ -21,114 +21,124 @@ const publicEventStats = functions.https.onRequest(async (request, response) =>
2121 if ( ! eventId ) { return sendResponseMessage ( response , 400 , `Missing [eventId] query parameter !` ) }
2222 if ( ! publicToken ) { return sendResponseMessage ( response , 400 , `Missing [publicToken] query parameter !` ) }
2323
24- const [ eventDescriptor , familyEventsStatsToken ] = await Promise . all ( [
25- getEventDescriptor ( eventId ) ,
26- getFamilyEventsStatsToken ( publicToken ) ,
27- ] ) ;
24+ const [ eventDescriptor , familyEventsStatsToken ] = await logPerf ( "eventDescriptor and familyEventsStatsToken retrieval" , async ( ) => {
25+ return await Promise . all ( [
26+ getEventDescriptor ( eventId ) ,
27+ getFamilyEventsStatsToken ( publicToken ) ,
28+ ] ) ;
29+ } )
2830
2931 if ( ! eventDescriptor . eventFamily || ! familyEventsStatsToken . eventFamilies . includes ( eventDescriptor . eventFamily ) ) {
3032 return sendResponseMessage ( response , 400 , `Provided family events stats token doesn't match with event ${ eventId } family: [${ eventDescriptor . eventFamily } ]` )
3133 }
3234
33- const { cachedHash, updatesDetected } = await checkEventLastUpdate ( eventId , [
34- root => root . favorites ,
35- root => root . allFeedbacks ,
36- root => root . talkListUpdated
37- ] , request , response )
35+ const { cachedHash, updatesDetected } = await logPerf ( "cached hash" , async ( ) => {
36+ return await checkEventLastUpdate ( eventId , [
37+ root => root . favorites ,
38+ root => root . allFeedbacks ,
39+ root => root . talkListUpdated
40+ ] , request , response )
41+ } ) ;
42+
3843 if ( ! updatesDetected ) {
3944 return sendResponseMessage ( response , 304 )
4045 }
4146
4247 try {
43- const [ talkStats , talksDetailsWithRatings ] = await Promise . all ( [
44- eventTalkStatsFor ( eventId ) ,
45- getTalksDetailsWithRatings ( eventId ) ,
46- ] )
47-
48- const perTalkStats = talksDetailsWithRatings . map ( talkDetails => ( {
49- talkId : talkDetails . talk . id ,
50- talkTitle : talkDetails . talk . title ,
51- totalFavoritesCount : talkStats . find ( ts => ts . id === talkDetails . talk . id ) ?. totalFavoritesCount || 0
52- } ) )
53-
54- type DailyTalksAndRatings = {
55- dayId : string ,
56- date : ISOLocalDate ,
57- talks : typeof talksDetailsWithRatings
58- }
59- const dailyTalks = talksDetailsWithRatings . reduce ( ( dailyTalks , talkAndRatings ) => {
60- let talkLocalDate = talkAndRatings . talk . start . substring ( 0 , "yyyy-mm-dd" . length ) as ISOLocalDate ;
61- const day = match ( dailyTalks . find ( dt => dt . date === talkLocalDate ) )
62- . with ( P . nullish , ( _ ) => {
63- const day : DailyTalksAndRatings = {
64- dayId : eventDescriptor . days . find ( d => d . localDate === talkLocalDate ) ! . id ,
65- date : talkLocalDate ,
66- talks : [ ]
67- }
68- dailyTalks . push ( day ) ;
69- return day ;
70- } ) . otherwise ( day => day ) ;
71-
72- day . talks . push ( talkAndRatings )
73-
74- return dailyTalks ;
75- } , [ ] as DailyTalksAndRatings [ ] )
76-
77- const eventTopRatedTalksConfig = eventDescriptor . features . topRatedTalks || {
78- numberOfDailyTopTalksConsidered : 10 ,
79- minimumAverageScoreToBeConsidered : Math . floor ( ( eventDescriptor . features . ratings . scale . labels . length + 1 ) / 2 ) ,
80- minimumNumberOfRatingsToBeConsidered : 10
81- }
82-
83- if ( ! eventDescriptor . features . ratings . scale . enabled ) {
84- eventTopRatedTalksConfig . minimumNumberOfRatingsToBeConsidered = Infinity ;
85- }
86-
87- const dailyTalksStats = sortBy ( dailyTalks , dt => dt . date )
88- . map ( dt => {
89- const talksWithValidAverageRating = dt . talks . map ( talk => {
90- const nonNullishRatings = talk . ratings
91- . map ( r => r [ 'linear-rating' ] )
92- . filter ( v => v !== null && v !== undefined ) as number [ ] ;
48+ const [ talkStats , talksDetailsWithRatings ] = await logPerf ( "eventTalkStats + getTalksDetailsWithRatings" , ( ) => {
49+ return Promise . all ( [
50+ eventTalkStatsFor ( eventId ) ,
51+ getTalksDetailsWithRatings ( eventId ) ,
52+ ] ) ;
53+ } )
54+
55+ const { perTalkStats, dailyTalksStats} = await logPerf ( "Post processing" , async ( ) => {
56+ const perTalkStats = talksDetailsWithRatings . map ( talkDetails => ( {
57+ talkId : talkDetails . talk . id ,
58+ talkTitle : talkDetails . talk . title ,
59+ totalFavoritesCount : talkStats . find ( ts => ts . id === talkDetails . talk . id ) ?. totalFavoritesCount || 0
60+ } ) )
61+
62+ type DailyTalksAndRatings = {
63+ dayId : string ,
64+ date : ISOLocalDate ,
65+ talks : typeof talksDetailsWithRatings
66+ }
67+ const dailyTalks = talksDetailsWithRatings . reduce ( ( dailyTalks , talkAndRatings ) => {
68+ let talkLocalDate = talkAndRatings . talk . start . substring ( 0 , "yyyy-mm-dd" . length ) as ISOLocalDate ;
69+ const day = match ( dailyTalks . find ( dt => dt . date === talkLocalDate ) )
70+ . with ( P . nullish , ( _ ) => {
71+ const day : DailyTalksAndRatings = {
72+ dayId : eventDescriptor . days . find ( d => d . localDate === talkLocalDate ) ! . id ,
73+ date : talkLocalDate ,
74+ talks : [ ]
75+ }
76+ dailyTalks . push ( day ) ;
77+ return day ;
78+ } ) . otherwise ( day => day ) ;
79+
80+ day . talks . push ( talkAndRatings )
81+
82+ return dailyTalks ;
83+ } , [ ] as DailyTalksAndRatings [ ] )
84+
85+ const eventTopRatedTalksConfig = eventDescriptor . features . topRatedTalks || {
86+ numberOfDailyTopTalksConsidered : 10 ,
87+ minimumAverageScoreToBeConsidered : Math . floor ( ( eventDescriptor . features . ratings . scale . labels . length + 1 ) / 2 ) ,
88+ minimumNumberOfRatingsToBeConsidered : 10
89+ }
90+
91+ if ( ! eventDescriptor . features . ratings . scale . enabled ) {
92+ eventTopRatedTalksConfig . minimumNumberOfRatingsToBeConsidered = Infinity ;
93+ }
94+
95+ const dailyTalksStats = sortBy ( dailyTalks , dt => dt . date )
96+ . map ( dt => {
97+ const talksWithValidAverageRating = dt . talks . map ( talk => {
98+ const nonNullishRatings = talk . ratings
99+ . map ( r => r [ 'linear-rating' ] )
100+ . filter ( v => v !== null && v !== undefined ) as number [ ] ;
101+
102+ return {
103+ ...talk . talk ,
104+ averageRating : ( nonNullishRatings . length >= eventTopRatedTalksConfig . minimumNumberOfRatingsToBeConsidered )
105+ ? roundedAverage ( nonNullishRatings )
106+ : undefined ,
107+ numberOfVotes : nonNullishRatings . length
108+ } ;
109+ } ) . filter ( t => t . averageRating !== undefined
110+ && ( eventTopRatedTalksConfig . minimumAverageScoreToBeConsidered === undefined || t . averageRating >= eventTopRatedTalksConfig . minimumAverageScoreToBeConsidered )
111+ ) ;
112+
113+ const topTalks = sortBy ( talksWithValidAverageRating , t => - t . averageRating ! )
114+ . filter ( ( talk , index ) => index < eventTopRatedTalksConfig . numberOfDailyTopTalksConsidered )
115+ . map ( talk => ( {
116+ talkId : talk . id ,
117+ title : talk . title ,
118+ speakers : talk . speakers . map ( s => ( {
119+ id : s . id ,
120+ fullName : s . fullName ,
121+ companyName : s . companyName ,
122+ photoUrl : s . photoUrl
123+ } ) ) ,
124+ start : talk . start , end : talk . end ,
125+ format : talk . format . title ,
126+ language : talk . language ,
127+ room : talk . room . title , track : talk . track . title ,
128+ tags : talk . tags ,
129+ averageRating : talk . averageRating ,
130+ numberOfVotes : talk . numberOfVotes
131+ } ) )
93132
94133 return {
95- ...talk . talk ,
96- averageRating : ( nonNullishRatings . length >= eventTopRatedTalksConfig . minimumNumberOfRatingsToBeConsidered )
97- ? roundedAverage ( nonNullishRatings )
98- : undefined ,
99- numberOfVotes : nonNullishRatings . length
100- } ;
101- } ) . filter ( t => t . averageRating !== undefined
102- && ( eventTopRatedTalksConfig . minimumAverageScoreToBeConsidered === undefined || t . averageRating >= eventTopRatedTalksConfig . minimumAverageScoreToBeConsidered )
103- ) ;
104-
105- const topTalks = sortBy ( talksWithValidAverageRating , t => - t . averageRating ! )
106- . filter ( ( talk , index ) => index < eventTopRatedTalksConfig . numberOfDailyTopTalksConsidered )
107- . map ( talk => ( {
108- talkId : talk . id ,
109- title : talk . title ,
110- speakers : talk . speakers . map ( s => ( {
111- id : s . id ,
112- fullName : s . fullName ,
113- companyName : s . companyName ,
114- photoUrl : s . photoUrl
115- } ) ) ,
116- start : talk . start , end : talk . end ,
117- format : talk . format . title ,
118- language : talk . language ,
119- room : talk . room . title , track : talk . track . title ,
120- tags : talk . tags ,
121- averageRating : talk . averageRating ,
122- numberOfVotes : talk . numberOfVotes
123- } ) )
124-
125- return {
126- date : dt . date ,
127- dayId : dt . dayId ,
128- topTalks
129- }
130- } ) ;
134+ date : dt . date ,
135+ dayId : dt . dayId ,
136+ topTalks
137+ }
138+ } ) ;
131139
140+ return { perTalkStats, dailyTalksStats }
141+ } )
132142
133143 sendResponseMessage ( response , 200 , {
134144 perTalkStats,
0 commit comments