@@ -86,6 +86,15 @@ module.exports = (app, mongo) => {
8686 // clear pending question
8787 clearQuestionQueue ( req , antsy . subject [ 0 ] ) ;
8888
89+ // mark as seen for the current session (prevents in-session repeats)
90+ const _seenSubjectKey = antsy . subject [ 0 ] . toLowerCase ( ) ;
91+ if ( ! req . session . seenQuestions ) req . session . seenQuestions = { } ;
92+ if ( ! req . session . seenQuestions [ _seenSubjectKey ] ) req . session . seenQuestions [ _seenSubjectKey ] = [ ] ;
93+ const _seenIdStr = antsy . _id . toString ( ) ;
94+ if ( ! req . session . seenQuestions [ _seenSubjectKey ] . includes ( _seenIdStr ) ) {
95+ req . session . seenQuestions [ _seenSubjectKey ] . push ( _seenIdStr ) ;
96+ }
97+
8998 // check answer
9099 if ( antsy . answer [ 0 ] == req . body . answerChoice ) {
91100 isRight = true ;
@@ -147,6 +156,15 @@ module.exports = (app, mongo) => {
147156 // clear pending question
148157 clearQuestionQueue ( req , antsy . subject [ 0 ] ) ;
149158
159+ // mark as seen for the current session (prevents in-session repeats)
160+ const _seenSubjectKey = antsy . subject [ 0 ] . toLowerCase ( ) ;
161+ if ( ! req . session . seenQuestions ) req . session . seenQuestions = { } ;
162+ if ( ! req . session . seenQuestions [ _seenSubjectKey ] ) req . session . seenQuestions [ _seenSubjectKey ] = [ ] ;
163+ const _seenIdStr = antsy . _id . toString ( ) ;
164+ if ( ! req . session . seenQuestions [ _seenSubjectKey ] . includes ( _seenIdStr ) ) {
165+ req . session . seenQuestions [ _seenSubjectKey ] . push ( _seenIdStr ) ;
166+ }
167+
150168 // check answer
151169 isRight = arraysEqual ( antsy . answer , req . body . saChoice ) ;
152170
@@ -206,6 +224,15 @@ module.exports = (app, mongo) => {
206224 // clear pending question
207225 clearQuestionQueue ( req , antsy . subject [ 0 ] ) ;
208226
227+ // mark as seen for the current session (prevents in-session repeats)
228+ const _seenSubjectKey = antsy . subject [ 0 ] . toLowerCase ( ) ;
229+ if ( ! req . session . seenQuestions ) req . session . seenQuestions = { } ;
230+ if ( ! req . session . seenQuestions [ _seenSubjectKey ] ) req . session . seenQuestions [ _seenSubjectKey ] = [ ] ;
231+ const _seenIdStr = antsy . _id . toString ( ) ;
232+ if ( ! req . session . seenQuestions [ _seenSubjectKey ] . includes ( _seenIdStr ) ) {
233+ req . session . seenQuestions [ _seenSubjectKey ] . push ( _seenIdStr ) ;
234+ }
235+
209236 // check answer
210237 for ( let j = 0 ; j < antsy . answer . length ; j ++ ) {
211238 if (
@@ -490,13 +517,23 @@ module.exports = (app, mongo) => {
490517 res . setHeader ( 'Expires' , '0' ) ;
491518 // define units and attempt to get queued question
492519 const units = req . query . units . split ( ',' ) ;
520+ const subjectKey = req . params . subject . toLowerCase ( ) ;
521+
522+ // session-scoped seen-question tracker (prevents in-session repeats)
523+ if ( ! req . session . seenQuestions ) req . session . seenQuestions = { } ;
524+ if ( ! req . session . seenQuestions [ subjectKey ] ) req . session . seenQuestions [ subjectKey ] = [ ] ;
525+ const seenIds = req . session . seenQuestions [ subjectKey ] ;
526+
493527 let q = '' ;
494528
495- if ( req . user . stats . toAnswer [ req . params . subject . toLowerCase ( ) ] ) {
496- q = await getQuestion (
497- mongo . Ques ,
498- req . user . stats . toAnswer [ req . params . subject . toLowerCase ( ) ]
499- ) ;
529+ if ( req . user . stats . toAnswer [ subjectKey ] ) {
530+ const queuedId = req . user . stats . toAnswer [ subjectKey ] ;
531+ // drop the queued question silently (no rating penalty) if it was already answered this session
532+ if ( seenIds . includes ( queuedId . toString ( ) ) ) {
533+ clearQuestionQueue ( req , subjectKey ) ;
534+ } else {
535+ q = await getQuestion ( mongo . Ques , queuedId ) ;
536+ }
500537 }
501538
502539 // get experience stats
@@ -522,7 +559,7 @@ module.exports = (app, mongo) => {
522559 skipQuestionUpdates (
523560 mongo . Ques ,
524561 req ,
525- req . params . subject . toLowerCase ( ) ,
562+ subjectKey ,
526563 q . _id
527564 ) ;
528565 }
@@ -533,44 +570,55 @@ module.exports = (app, mongo) => {
533570 }
534571
535572 let ceilingFloor = ratingCeilingFloor (
536- req . user . rating [ req . params . subject . toLowerCase ( ) ]
573+ req . user . rating [ subjectKey ]
537574 ) ;
538575 const floor = ceilingFloor . floor ;
539- const ceiling = ceilingFloor . ceiling ;
576+ let ceiling = ceilingFloor . ceiling ;
540577
541578 //debugging usage
542579 console . log ( floor ) ;
543580 console . log ( ceiling ) ;
544- // get question
545- getQuestions ( mongo . Ques , floor , ceiling , req . params . subject , units ) . then (
546- ( qs ) => {
547- //console.log(qs);
548-
549- // select random question
550- curQ = qs [ Math . floor ( Math . random ( ) * qs . length ) ] ;
551- console . log ( curQ ) ;
552- if ( ! curQ ) {
553- req . flash (
554- 'errorFlash' ,
555- "We couldn't find any questions for your rating in the units you selected."
556- ) ;
557- res . redirect ( '/train/' + req . params . subject + '/chooseUnits' ) ;
558- return ;
559- }
560- // update pending question field
561- updateQuestionQueue ( req , req . params . subject , curQ . _id ) ;
562- // push to frontend
563- res . render ( VIEWS + 'private/train/displayQuestion.ejs' , {
564- units : units ,
565- newQues : curQ ,
566- subject : req . params . subject ,
567- user : req . user ,
568- experienceStats,
569- pageName : 'Classic Trainer' ,
570- referenceSheet,
571- } ) ;
572- }
573- ) ;
581+
582+ // retry-and-widen: push ceiling up (floor stays) until unseen questions appear
583+ const STEP = 200 ;
584+ const MAX_ITERATIONS = 5 ;
585+ let qs = [ ] ;
586+ for ( let i = 0 ; i < MAX_ITERATIONS ; i ++ ) {
587+ qs = await getQuestions ( mongo . Ques , floor , ceiling , req . params . subject , units , seenIds ) ;
588+ if ( qs . length > 0 ) break ;
589+ ceiling += STEP ;
590+ console . log ( 'no unseen questions; expanding ceiling to' , ceiling ) ;
591+ }
592+
593+ // fallback: user has exhausted the unseen pool — allow a repeat rather than erroring
594+ if ( qs . length === 0 ) {
595+ console . log ( 'seen-filtered pool exhausted, falling back to full pool' ) ;
596+ qs = await getQuestions ( mongo . Ques , floor , ceiling , req . params . subject , units ) ;
597+ }
598+
599+ // select random question
600+ curQ = qs [ Math . floor ( Math . random ( ) * qs . length ) ] ;
601+ console . log ( curQ ) ;
602+ if ( ! curQ ) {
603+ req . flash (
604+ 'errorFlash' ,
605+ "We couldn't find any questions for your rating in the units you selected."
606+ ) ;
607+ res . redirect ( '/train/' + req . params . subject + '/chooseUnits' ) ;
608+ return ;
609+ }
610+ // update pending question field
611+ updateQuestionQueue ( req , req . params . subject , curQ . _id ) ;
612+ // push to frontend
613+ res . render ( VIEWS + 'private/train/displayQuestion.ejs' , {
614+ units : units ,
615+ newQues : curQ ,
616+ subject : req . params . subject ,
617+ user : req . user ,
618+ experienceStats,
619+ pageName : 'Classic Trainer' ,
620+ referenceSheet,
621+ } ) ;
574622 }
575623 } ) ;
576624
0 commit comments