@@ -42,78 +42,154 @@ export const isComplete = (question: any, session: any, audioComplete = false) =
4242export const outcome = ( question : any , session : any , env : any ) =>
4343 new Promise ( ( resolve ) => {
4444 if ( ! session || isEmpty ( session ) ) {
45- resolve ( { score : 0 , empty : true } ) ;
45+ resolve ( {
46+ score : 0 ,
47+ empty : true ,
48+ traceLog : [ 'Student did not select any answers. Score is 0.' ] ,
49+ } ) ;
4650 return ;
4751 }
4852
4953 session = normalizeSession ( session ) ;
54+ const correctness = getCorrectness ( question , session ) ;
5055
51- if ( env . mode !== 'evaluate' ) {
52- resolve ( { score : undefined , completed : undefined } ) ;
53- } else {
54- const correctness = getCorrectness ( question , session ) ;
55- if ( correctness === 'unanswered' ) {
56- resolve ( { score : 0 , empty : true } ) ;
57- return ;
58- }
59- const score = correctness === 'correct' ? 1 : 0 ;
60- resolve ( { score, empty : false } ) ;
56+ if ( correctness === 'unanswered' ) {
57+ resolve ( {
58+ score : 0 ,
59+ empty : true ,
60+ traceLog : [ 'Student did not select any answers. Score is 0.' ] ,
61+ } ) ;
62+ return ;
6163 }
64+
65+ const score = correctness === 'correct' ? 1 : 0 ;
66+ const traceLog = [
67+ `Mode: ${ env ?. mode || 'unknown' } .` ,
68+ `Student selected choice: ${ session . choiceId } .` ,
69+ `Correct choice: ${ question ?. correctChoiceId || 'none' } .` ,
70+ `Final score: ${ score } .` ,
71+ ] ;
72+ resolve ( { score, empty : false , traceLog } ) ;
6273 } ) ;
6374
6475export const createDefaultModel = ( model : any = { } ) => ( { ...defaults . model , ...model } ) ;
6576
6677export const normalizeSession = ( s : any ) => ( { ...s } ) ;
6778
68- export const model = ( question : any , session : any , env : any ) => {
69- return new Promise ( ( resolve ) => {
70- session = session || { } ;
71- const normalizedQuestion = createDefaultModel ( question ) ;
72-
73- const out : any = {
74- prompt : normalizedQuestion . promptEnabled ? normalizedQuestion . prompt : null ,
75- interactionMode : normalizedQuestion . interactionMode || 'populate_blank' ,
76- layoutProfile : normalizedQuestion . layoutProfile || '' ,
77- choiceLayout : normalizedQuestion . choiceLayout || '' ,
78- sentenceHtml : normalizedQuestion . sentenceHtml || null ,
79- template : normalizedQuestion . template ,
80- choiceMode : normalizedQuestion . choiceMode ,
81- choices : normalizedQuestion . choices ,
82- correctChoiceId : normalizedQuestion . correctChoiceId ,
83- hasAudio : normalizedQuestion . hasAudio ,
84- autoplayAudioEnabled : ! ! normalizedQuestion . autoplayAudioEnabled ,
85- completeAudioEnabled : ! ! normalizedQuestion . completeAudioEnabled ,
86- audioUrl : normalizedQuestion . hasAudio ? normalizedQuestion . audioUrl : null ,
87- audioTranscript : normalizedQuestion . hasAudio ? normalizedQuestion . audioTranscript : null ,
88- showVisibleTranscript : ! ! normalizedQuestion . showVisibleTranscript ,
89- locale : normalizedQuestion . locale || '' ,
90- disabled : env . mode !== 'gather' ,
91- view : env . mode === 'view' ,
92- env,
93- } ;
94-
95- if ( env . mode === 'evaluate' ) {
96- const correctness = getCorrectness ( normalizedQuestion , session ) ;
97- out . correctness = correctness ;
98- }
79+ const shouldShuffleChoices = ( question : any ) => ! ! question ?. shuffle ;
9980
100- if ( env . role === 'instructor' && ( env . mode === 'view' || env . mode === 'evaluate' ) ) {
101- out . teacherInstructions = normalizedQuestion . teacherInstructionsEnabled
102- ? normalizedQuestion . teacherInstructions
103- : null ;
104- } else {
105- out . teacherInstructions = null ;
81+ const shouldLockChoices = ( question : any , env : any ) => {
82+ if ( question ?. lockChoiceOrder ) return true ;
83+ if ( env ?. [ '@pie-element' ] ?. lockChoiceOrder ) return true ;
84+ return env ?. role === 'instructor' ;
85+ } ;
86+
87+ const shuffleArray = < T > ( items : T [ ] ) : T [ ] => {
88+ const out = [ ...items ] ;
89+ for ( let i = out . length - 1 ; i > 0 ; i -- ) {
90+ const j = Math . floor ( Math . random ( ) * ( i + 1 ) ) ;
91+ [ out [ i ] , out [ j ] ] = [ out [ j ] , out [ i ] ] ;
92+ }
93+ return out ;
94+ } ;
95+
96+ const getStoredShuffle = ( session : any ) : string [ ] =>
97+ Array . isArray ( session ?. data ?. shuffledValues )
98+ ? session . data . shuffledValues
99+ : Array . isArray ( session ?. shuffledValues )
100+ ? session . shuffledValues
101+ : [ ] ;
102+
103+ const applyShuffledValues = ( choices : any [ ] , shuffledValues : string [ ] , choiceKey : string ) => {
104+ const orderedChoices = shuffledValues
105+ . map ( ( value ) => choices . find ( ( choice ) => choice ?. [ choiceKey ] === value ) )
106+ . filter ( Boolean ) ;
107+
108+ if ( orderedChoices . length === choices . length ) {
109+ return orderedChoices ;
110+ }
111+
112+ const orderedValues = new Set ( orderedChoices . map ( ( choice : any ) => choice [ choiceKey ] ) ) ;
113+ const leftovers = choices . filter ( ( choice ) => ! orderedValues . has ( choice ?. [ choiceKey ] ) ) ;
114+ return [ ...orderedChoices , ...leftovers ] ;
115+ } ;
116+
117+ const getOrderedChoices = async ( question : any , session : any , env : any , updateSession ?: any ) => {
118+ const choices = Array . isArray ( question ?. choices ) ? [ ...question . choices ] : [ ] ;
119+ if ( ! choices . length || ! shouldShuffleChoices ( question ) ) {
120+ return choices ;
121+ }
122+
123+ if ( shouldLockChoices ( question , env || { } ) ) {
124+ return choices ;
125+ }
126+
127+ const shuffledValues = getStoredShuffle ( session ) ;
128+ if ( shuffledValues . length ) {
129+ return applyShuffledValues ( choices , shuffledValues , 'id' ) ;
130+ }
131+
132+ const shuffledChoices = shuffleArray ( choices ) ;
133+
134+ if ( updateSession && typeof updateSession === 'function' && session ?. id && session ?. element ) {
135+ const shuffledIds = shuffledChoices . map ( ( choice ) => choice ?. id ) . filter ( Boolean ) ;
136+ if ( shuffledIds . length ) {
137+ await updateSession ( session . id , session . element , { shuffledValues : shuffledIds } ) ;
106138 }
139+ }
107140
108- resolve ( out ) ;
109- } ) ;
141+ return shuffledChoices ;
142+ } ;
143+
144+ export const model = async ( question : any , session : any , env : any , updateSession ?: any ) => {
145+ session = session || { } ;
146+ const normalizedQuestion = createDefaultModel ( question ) ;
147+ const choices = await getOrderedChoices ( normalizedQuestion , session , env , updateSession ) ;
148+
149+ const out : any = {
150+ prompt : normalizedQuestion . promptEnabled ? normalizedQuestion . prompt : null ,
151+ interactionMode : normalizedQuestion . interactionMode || 'populate_blank' ,
152+ layoutProfile : normalizedQuestion . layoutProfile || '' ,
153+ choiceLayout : normalizedQuestion . choiceLayout || '' ,
154+ sentenceHtml : normalizedQuestion . sentenceHtml || null ,
155+ template : normalizedQuestion . template ,
156+ choiceMode : normalizedQuestion . choiceMode ,
157+ choices,
158+ hasAudio : normalizedQuestion . hasAudio ,
159+ autoplayAudioEnabled : ! ! normalizedQuestion . autoplayAudioEnabled ,
160+ completeAudioEnabled : ! ! normalizedQuestion . completeAudioEnabled ,
161+ audioUrl : normalizedQuestion . hasAudio ? normalizedQuestion . audioUrl : null ,
162+ audioTranscript : normalizedQuestion . hasAudio ? normalizedQuestion . audioTranscript : null ,
163+ showVisibleTranscript : ! ! normalizedQuestion . showVisibleTranscript ,
164+ locale : normalizedQuestion . locale || '' ,
165+ disabled : env . mode !== 'gather' ,
166+ view : env . mode === 'view' ,
167+ mode : env . mode ,
168+ } ;
169+
170+ if ( env . mode === 'evaluate' ) {
171+ const correctness = getCorrectness ( normalizedQuestion , session ) ;
172+ out . correctness = correctness ;
173+ out . responseCorrect = correctness === 'correct' ;
174+ out . correctChoiceId = normalizedQuestion . correctChoiceId ;
175+ }
176+
177+ if ( env . role === 'instructor' && ( env . mode === 'view' || env . mode === 'evaluate' ) ) {
178+ out . teacherInstructions = normalizedQuestion . teacherInstructionsEnabled
179+ ? normalizedQuestion . teacherInstructions
180+ : null ;
181+ } else {
182+ out . teacherInstructions = null ;
183+ }
184+
185+ return out ;
110186} ;
111187
112188export const createCorrectResponseSession = ( question : any , env : any ) => {
113189 return new Promise ( ( resolve ) => {
114190 if ( env . mode !== 'evaluate' && env . role === 'instructor' ) {
115191 resolve ( {
116- id : question ?. id || '1' ,
192+ id : '1' ,
117193 element : 'mc-populated-blank' ,
118194 choiceId : question ?. correctChoiceId || '' ,
119195 } ) ;
0 commit comments