@@ -30,6 +30,48 @@ Sentry.init()
3030
3131const redisExec = getRedis ( )
3232
33+ function updateLeaderboards ( {
34+ redisMulti,
35+ participantId,
36+ participantRole,
37+ sessionKey,
38+ sessionBlockId,
39+ pointsAwarded,
40+ xpAwarded,
41+ } : {
42+ redisMulti : ChainableCommander
43+ participantId : string
44+ participantRole : string
45+ sessionKey : string
46+ sessionBlockId : string
47+ pointsAwarded : number
48+ xpAwarded : number
49+ } ) {
50+ // depending on the participant account type (permanent student account or
51+ // temporary pseudonym), set the correct points / experience points
52+ if ( participantRole === 'PARTICIPANT' ) {
53+ redisMulti . hincrby (
54+ `${ sessionKey } :b:${ sessionBlockId } :lb` ,
55+ participantId ,
56+ pointsAwarded
57+ )
58+ redisMulti . hincrby ( `${ sessionKey } :lb` , participantId , pointsAwarded )
59+ redisMulti . hincrby ( `${ sessionKey } :xp` , participantId , xpAwarded )
60+ } else if ( participantRole === 'TEMPORARY_PARTICIPANT' ) {
61+ // temporary participants are only granted points, xp cannot be collected
62+ redisMulti . hincrby (
63+ `${ sessionKey } :b:${ sessionBlockId } :lbTemporary` ,
64+ participantId ,
65+ pointsAwarded
66+ )
67+ redisMulti . hincrby (
68+ `${ sessionKey } :lbTemporary` ,
69+ participantId ,
70+ pointsAwarded
71+ )
72+ }
73+ }
74+
3375interface Message {
3476 messageId : string
3577 sessionId : string
@@ -96,7 +138,12 @@ const serviceBusTrigger = async function (
96138 process . env . APP_SECRET
97139 ) as { sub : string ; role : string }
98140
99- if ( participantData . role !== 'PARTICIPANT' ) {
141+ if (
142+ ! (
143+ participantData . role === 'PARTICIPANT' ||
144+ participantData . role === 'TEMPORARY_PARTICIPANT'
145+ )
146+ ) {
100147 participantData = null
101148 } else {
102149 context . log ( "Participant's JWT verified" , participantData )
@@ -106,12 +153,15 @@ const serviceBusTrigger = async function (
106153 context . error ( 'JWT verification failed' , e , queueItem . cookie )
107154 Sentry . captureException ( e )
108155 }
156+
109157 // if the participant has already responded to the question instance, return instantly
110158 if (
111159 participantData &&
112160 ( await redisExec . hexists (
113161 `${ instanceKey } :responses` ,
114- participantData . sub
162+ participantData . role === 'TEMPORARY_PARTICIPANT'
163+ ? `temporary-${ participantData . sub } `
164+ : participantData . sub
115165 ) )
116166 ) {
117167 context . log (
@@ -120,13 +170,13 @@ const serviceBusTrigger = async function (
120170 return { status : 200 }
121171 }
122172 }
173+
123174 const instanceInfo = await redisExec . hgetall ( `${ instanceKey } :info` )
124175 // if the instance metadata is not available, it has been closed and purged already
125176 if ( ! instanceInfo ) {
126177 context . log ( 'Question instance metadata not found' , queueItem )
127178 return { status : 400 }
128179 }
129-
130180 context . log ( 'Instance info' , instanceInfo )
131181
132182 const {
@@ -156,12 +206,15 @@ const serviceBusTrigger = async function (
156206 case 'SC' :
157207 case 'MC' :
158208 case 'KPRIM' : {
209+ // add the vote to the aggregated results
159210 response . choices . forEach ( ( choiceIndex : number ) => {
160211 redisMulti . hincrby ( `${ instanceKey } :results` , String ( choiceIndex ) , 1 )
161212 } )
162213 redisMulti . hincrby ( `${ instanceKey } :results` , 'participants' , 1 )
214+
215+ // if the participant was logged in, award points (and xp if regular student acount was used)
163216 if ( participantData ) {
164- let pointsPercentage
217+ let pointsPercentage : number | null
165218 if ( type === 'SC' ) {
166219 pointsPercentage = gradeQuestionSC ( {
167220 responseCount : Number ( choiceCount ) ,
@@ -219,27 +272,31 @@ const serviceBusTrigger = async function (
219272 responseTimestamp
220273 )
221274 }
275+
222276 redisMulti . hset (
223277 `${ instanceKey } :responses` ,
224- participantData . sub ,
278+ participantData . role === 'TEMPORARY_PARTICIPANT'
279+ ? `temporary-${ participantData . sub } `
280+ : participantData . sub ,
225281 `[${ String ( response . choices ) } ]`
226282 )
227- redisMulti . hincrby (
228- ` ${ sessionKey } :b: ${ sessionBlockId } :lb` ,
229- participantData . sub ,
230- pointsAwarded
231- )
232- redisMulti . hincrby (
233- ` ${ sessionKey } :lb` ,
234- participantData . sub ,
235- pointsAwarded
236- )
237- redisMulti . hincrby ( ` ${ sessionKey } :xp` , participantData . sub , xpAwarded )
283+
284+ // update both the regular and temporary live quiz leaderboards
285+ updateLeaderboards ( {
286+ redisMulti ,
287+ participantId : participantData . sub ,
288+ participantRole : participantData . role ,
289+ sessionKey,
290+ sessionBlockId ,
291+ pointsAwarded,
292+ xpAwarded ,
293+ } )
238294 }
239295 break
240296 }
241297 // TODO: points based on distance to correct range?
242298 case 'NUMERICAL' : {
299+ // add the response to the aggregated results
243300 const MD5 = createHash ( 'md5' )
244301 MD5 . update ( response . value )
245302 const responseHash = MD5 . digest ( 'hex' )
@@ -251,19 +308,20 @@ const serviceBusTrigger = async function (
251308 )
252309 redisMulti . hincrby ( `${ instanceKey } :results` , 'participants' , 1 )
253310
254- const exactSolutionsDefined =
255- typeof parsedSolutions !== 'undefined' &&
256- parsedSolutions . length > 0 &&
257- ( typeof parsedSolutions [ 0 ] === 'number' ||
258- typeof parsedSolutions [ 0 ] === 'string' )
259-
260- const answerCorrect = gradeQuestionNumerical ( {
261- response : response . value ,
262- solutionRanges : exactSolutionsDefined ? undefined : parsedSolutions ,
263- exactSolutions : exactSolutionsDefined ? parsedSolutions : undefined ,
264- } )
265-
311+ // if the participant was logged in, award points (and xp if regular student acount was used)
266312 if ( participantData ) {
313+ const exactSolutionsDefined =
314+ typeof parsedSolutions !== 'undefined' &&
315+ parsedSolutions . length > 0 &&
316+ ( typeof parsedSolutions [ 0 ] === 'number' ||
317+ typeof parsedSolutions [ 0 ] === 'string' )
318+
319+ const answerCorrect = gradeQuestionNumerical ( {
320+ response : response . value ,
321+ solutionRanges : exactSolutionsDefined ? undefined : parsedSolutions ,
322+ exactSolutions : exactSolutionsDefined ? parsedSolutions : undefined ,
323+ } )
324+
267325 pointsAwarded = computeAwardedPoints ( {
268326 firstResponseReceivedAt,
269327 responseTimestamp,
@@ -298,29 +356,32 @@ const serviceBusTrigger = async function (
298356 responseTimestamp
299357 )
300358 }
359+
301360 redisMulti . hset (
302361 `${ instanceKey } :responses` ,
303- participantData . sub ,
362+ participantData . role === 'TEMPORARY_PARTICIPANT'
363+ ? `temporary-${ participantData . sub } `
364+ : participantData . sub ,
304365 String ( response . value )
305366 )
306- redisMulti . hincrby (
307- ` ${ sessionKey } :b: ${ sessionBlockId } :lb` ,
308- participantData . sub ,
309- pointsAwarded
310- )
311- redisMulti . hincrby (
312- ` ${ sessionKey } :lb` ,
313- participantData . sub ,
314- pointsAwarded
315- )
316- redisMulti . hincrby ( ` ${ sessionKey } :xp` , participantData . sub , xpAwarded )
367+
368+ // update both the regular and temporary live quiz leaderboards
369+ updateLeaderboards ( {
370+ redisMulti ,
371+ participantId : participantData . sub ,
372+ participantRole : participantData . role ,
373+ sessionKey,
374+ sessionBlockId ,
375+ pointsAwarded,
376+ xpAwarded ,
377+ } )
317378 }
318379 break
319380 }
320381 // TODO: future -> distance in embedding space?
321382 case 'FREE_TEXT' : {
383+ // add the response to the aggregated results
322384 const cleanResponseValue = toLowerCase ( response . value . trim ( ) )
323-
324385 const MD5 = createHash ( 'md5' )
325386 MD5 . update ( cleanResponseValue )
326387 const responseHash = MD5 . digest ( 'hex' )
@@ -331,6 +392,8 @@ const serviceBusTrigger = async function (
331392 cleanResponseValue
332393 )
333394 redisMulti . hincrby ( `${ instanceKey } :results` , 'participants' , 1 )
395+
396+ // if the participant was logged in, award points (and xp if regular student acount was used)
334397 if ( participantData ) {
335398 const answerCorrect = gradeQuestionFreeText ( {
336399 response : cleanResponseValue ,
@@ -371,26 +434,30 @@ const serviceBusTrigger = async function (
371434 responseTimestamp
372435 )
373436 }
437+
374438 redisMulti . hset (
375439 `${ instanceKey } :responses` ,
376- participantData . sub ,
440+ participantData . role === 'TEMPORARY_PARTICIPANT'
441+ ? `temporary-${ participantData . sub } `
442+ : participantData . sub ,
377443 cleanResponseValue
378444 )
379- redisMulti . hincrby (
380- ` ${ sessionKey } :b: ${ sessionBlockId } :lb` ,
381- participantData . sub ,
382- pointsAwarded
383- )
384- redisMulti . hincrby (
385- ` ${ sessionKey } :lb` ,
386- participantData . sub ,
387- pointsAwarded
388- )
389- redisMulti . hincrby ( ` ${ sessionKey } :xp` , participantData . sub , xpAwarded )
445+
446+ // update both the regular and temporary live quiz leaderboards
447+ updateLeaderboards ( {
448+ redisMulti ,
449+ participantId : participantData . sub ,
450+ participantRole : participantData . role ,
451+ sessionKey,
452+ sessionBlockId ,
453+ pointsAwarded,
454+ xpAwarded ,
455+ } )
390456 }
391457 break
392458 }
393459 case 'SELECTION' : {
460+ // add the response to the aggregated results
394461 response . selection . forEach ( ( answerId : number ) => {
395462 // skipped input fields should not be considered
396463 if ( answerId === - 1 ) {
@@ -401,6 +468,7 @@ const serviceBusTrigger = async function (
401468 } )
402469 redisMulti . hincrby ( `${ instanceKey } :results` , 'participants' , 1 )
403470
471+ // if the participant was logged in, award points (and xp if regular student acount was used)
404472 if ( participantData ) {
405473 const pointsPercentage = gradeQuestionSelection ( {
406474 numberOfInputs : parseInt ( instanceInfo . numberOfInputs ) ,
@@ -450,24 +518,27 @@ const serviceBusTrigger = async function (
450518
451519 redisMulti . hset (
452520 `${ instanceKey } :responses` ,
453- participantData . sub ,
521+ participantData . role === 'TEMPORARY_PARTICIPANT'
522+ ? `temporary-${ participantData . sub } `
523+ : participantData . sub ,
454524 `[${ String ( response . selection . filter ( ( r : number ) => r !== - 1 ) ) } ]` // filter out skipped response fields
455525 )
456- redisMulti . hincrby (
457- ` ${ sessionKey } :b: ${ sessionBlockId } :lb` ,
458- participantData . sub ,
459- pointsAwarded
460- )
461- redisMulti . hincrby (
462- ` ${ sessionKey } :lb` ,
463- participantData . sub ,
464- pointsAwarded
465- )
466- redisMulti . hincrby ( ` ${ sessionKey } :xp` , participantData . sub , xpAwarded )
526+
527+ // update both the regular and temporary live quiz leaderboards
528+ updateLeaderboards ( {
529+ redisMulti ,
530+ participantId : participantData . sub ,
531+ participantRole : participantData . role ,
532+ sessionKey,
533+ sessionBlockId ,
534+ pointsAwarded,
535+ xpAwarded ,
536+ } )
467537 }
468538 break
469539 }
470540 case 'CASE_STUDY' : {
541+ // add the response to the aggregated results
471542 Object . entries ( response . assessment ) . forEach ( ( [ caseId , caseData ] ) => {
472543 Object . entries ( caseData ) . forEach ( ( [ itemId , itemData ] ) => {
473544 Object . entries ( itemData ) . forEach (
@@ -500,6 +571,7 @@ const serviceBusTrigger = async function (
500571 // increment participant count
501572 redisMulti . hincrby ( `${ instanceKey } :results` , 'participants' , 1 )
502573
574+ // if the participant was logged in, award points (and xp if regular student acount was used)
503575 if ( participantData ) {
504576 const pointsPercentage = gradeQuestionCaseStudy ( {
505577 response : response . assessment ,
@@ -546,20 +618,22 @@ const serviceBusTrigger = async function (
546618 }
547619 redisMulti . hset (
548620 `${ instanceKey } :responses` ,
549- participantData . sub ,
621+ participantData . role === 'TEMPORARY_PARTICIPANT'
622+ ? `temporary-${ participantData . sub } `
623+ : participantData . sub ,
550624 JSON . stringify ( response . assessment )
551625 )
552- redisMulti . hincrby (
553- ` ${ sessionKey } :b: ${ sessionBlockId } :lb` ,
554- participantData . sub ,
555- pointsAwarded
556- )
557- redisMulti . hincrby (
558- ` ${ sessionKey } :lb` ,
559- participantData . sub ,
560- pointsAwarded
561- )
562- redisMulti . hincrby ( ` ${ sessionKey } :xp` , participantData . sub , xpAwarded )
626+
627+ // update both the regular and temporary live quiz leaderboards
628+ updateLeaderboards ( {
629+ redisMulti ,
630+ participantId : participantData . sub ,
631+ participantRole : participantData . role ,
632+ sessionKey,
633+ sessionBlockId ,
634+ pointsAwarded,
635+ xpAwarded ,
636+ } )
563637 }
564638
565639 break
0 commit comments