Skip to content

Commit 1d7f561

Browse files
authored
all re-displays of cards (2 per session)... (#947)
This count refers to 'reshuffling' a failed card into the session Qs for later display
2 parents 837c1ba + b03fa1a commit 1d7f561

File tree

9 files changed

+428
-34
lines changed

9 files changed

+428
-34
lines changed
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
<template>
2+
<v-card v-if="sessionController" class="session-debug ma-2" elevation="2">
3+
<v-card-title class="text-caption bg-grey-darken-3">
4+
Session Controller Debug
5+
<v-spacer></v-spacer>
6+
<v-icon size="small">mdi-bug</v-icon>
7+
</v-card-title>
8+
9+
<v-card-text class="pa-2">
10+
<v-row dense>
11+
<!-- Review Queue -->
12+
<v-col cols="12" md="4">
13+
<div class="debug-section">
14+
<div class="debug-header">
15+
<v-icon size="x-small" class="mr-1">mdi-calendar-check</v-icon>
16+
<strong>Review Queue</strong>
17+
</div>
18+
<div class="debug-stats">
19+
<span class="text-caption">Length: {{ debugInfo.reviewQueue.length }}</span>
20+
<span class="text-caption ml-2">Dequeued: {{ debugInfo.reviewQueue.dequeueCount }}</span>
21+
</div>
22+
<div v-if="debugInfo.reviewQueue.items.length > 0" class="debug-items">
23+
<div
24+
v-for="(item, idx) in debugInfo.reviewQueue.items.slice(0, 5)"
25+
:key="idx"
26+
class="debug-item"
27+
>
28+
<span class="text-caption">{{ idx }}: {{ item.courseID }}::{{ item.cardID }}</span>
29+
<span class="text-caption text-grey ml-1">({{ item.status }})</span>
30+
</div>
31+
<div v-if="debugInfo.reviewQueue.items.length > 5" class="text-caption text-grey">
32+
... +{{ debugInfo.reviewQueue.items.length - 5 }} more
33+
</div>
34+
</div>
35+
<div v-else class="text-caption text-grey">
36+
(empty)
37+
</div>
38+
</div>
39+
</v-col>
40+
41+
<!-- New Cards Queue -->
42+
<v-col cols="12" md="4">
43+
<div class="debug-section">
44+
<div class="debug-header">
45+
<v-icon size="x-small" class="mr-1">mdi-file-document-plus</v-icon>
46+
<strong>New Cards Queue</strong>
47+
</div>
48+
<div class="debug-stats">
49+
<span class="text-caption">Length: {{ debugInfo.newQueue.length }}</span>
50+
<span class="text-caption ml-2">Dequeued: {{ debugInfo.newQueue.dequeueCount }}</span>
51+
</div>
52+
<div v-if="debugInfo.newQueue.items.length > 0" class="debug-items">
53+
<div
54+
v-for="(item, idx) in debugInfo.newQueue.items.slice(0, 5)"
55+
:key="idx"
56+
class="debug-item"
57+
>
58+
<span class="text-caption">{{ idx }}: {{ item.courseID }}::{{ item.cardID }}</span>
59+
<span class="text-caption text-grey ml-1">({{ item.status }})</span>
60+
</div>
61+
<div v-if="debugInfo.newQueue.items.length > 5" class="text-caption text-grey">
62+
... +{{ debugInfo.newQueue.items.length - 5 }} more
63+
</div>
64+
</div>
65+
<div v-else class="text-caption text-grey">
66+
(empty)
67+
</div>
68+
</div>
69+
</v-col>
70+
71+
<!-- Failed Cards Queue -->
72+
<v-col cols="12" md="4">
73+
<div class="debug-section">
74+
<div class="debug-header">
75+
<v-icon size="x-small" class="mr-1">mdi-alert-circle</v-icon>
76+
<strong>Failed Cards Queue</strong>
77+
</div>
78+
<div class="debug-stats">
79+
<span class="text-caption">Length: {{ debugInfo.failedQueue.length }}</span>
80+
<span class="text-caption ml-2">Dequeued: {{ debugInfo.failedQueue.dequeueCount }}</span>
81+
</div>
82+
<div v-if="debugInfo.failedQueue.items.length > 0" class="debug-items">
83+
<div
84+
v-for="(item, idx) in debugInfo.failedQueue.items.slice(0, 5)"
85+
:key="idx"
86+
class="debug-item"
87+
>
88+
<span class="text-caption">{{ idx }}: {{ item.courseID }}::{{ item.cardID }}</span>
89+
<span class="text-caption text-grey ml-1">({{ item.status }})</span>
90+
</div>
91+
<div v-if="debugInfo.failedQueue.items.length > 5" class="text-caption text-grey">
92+
... +{{ debugInfo.failedQueue.items.length - 5 }} more
93+
</div>
94+
</div>
95+
<div v-else class="text-caption text-grey">
96+
(empty)
97+
</div>
98+
</div>
99+
</v-col>
100+
</v-row>
101+
102+
<!-- Hydrated Cards Cache -->
103+
<v-row dense>
104+
<v-col cols="12">
105+
<v-divider class="my-2"></v-divider>
106+
<div class="debug-section">
107+
<div class="debug-header">
108+
<v-icon size="x-small" class="mr-1">mdi-database</v-icon>
109+
<strong>Hydrated Cards Cache</strong>
110+
</div>
111+
<div class="debug-stats">
112+
<span class="text-caption">Cached: {{ debugInfo.hydratedCache.count }}</span>
113+
<span class="text-caption ml-2">Failed Cache: {{ debugInfo.hydratedCache.failedCacheSize }}</span>
114+
</div>
115+
<div v-if="debugInfo.hydratedCache.items.length > 0" class="debug-items">
116+
<div
117+
v-for="(item, idx) in debugInfo.hydratedCache.items.slice(0, 8)"
118+
:key="idx"
119+
class="debug-item d-inline-block mr-3"
120+
>
121+
<span class="text-caption">{{ item.courseID }}::{{ item.cardID }}</span>
122+
</div>
123+
<div v-if="debugInfo.hydratedCache.items.length > 8" class="text-caption text-grey">
124+
... +{{ debugInfo.hydratedCache.items.length - 8 }} more
125+
</div>
126+
</div>
127+
<div v-else class="text-caption text-grey">
128+
(empty)
129+
</div>
130+
</div>
131+
</v-col>
132+
</v-row>
133+
</v-card-text>
134+
</v-card>
135+
</template>
136+
137+
<script lang="ts">
138+
import { defineComponent, PropType, computed, ref, onMounted, onUnmounted } from 'vue';
139+
import { SessionController } from '@vue-skuilder/db';
140+
141+
interface QueueDebugInfo {
142+
length: number;
143+
dequeueCount: number;
144+
items: Array<{ courseID: string; cardID: string; status: string }>;
145+
}
146+
147+
interface HydratedCacheInfo {
148+
count: number;
149+
failedCacheSize: number;
150+
items: Array<{ courseID: string; cardID: string }>;
151+
}
152+
153+
export interface SessionDebugInfo {
154+
reviewQueue: QueueDebugInfo;
155+
newQueue: QueueDebugInfo;
156+
failedQueue: QueueDebugInfo;
157+
hydratedCache: HydratedCacheInfo;
158+
}
159+
160+
export default defineComponent({
161+
name: 'SessionControllerDebug',
162+
163+
props: {
164+
sessionController: {
165+
type: Object as PropType<SessionController<any> | null>,
166+
required: true,
167+
},
168+
},
169+
170+
setup(props) {
171+
const refreshTrigger = ref(0);
172+
let pollInterval: NodeJS.Timeout | null = null;
173+
174+
onMounted(() => {
175+
// Poll every 500ms to update debug display
176+
pollInterval = setInterval(() => {
177+
refreshTrigger.value++;
178+
}, 500);
179+
});
180+
181+
onUnmounted(() => {
182+
if (pollInterval) {
183+
clearInterval(pollInterval);
184+
}
185+
});
186+
187+
const debugInfo = computed((): SessionDebugInfo => {
188+
// Create dependency on refreshTrigger to force updates
189+
refreshTrigger.value;
190+
191+
if (!props.sessionController) {
192+
return {
193+
reviewQueue: { length: 0, dequeueCount: 0, items: [] },
194+
newQueue: { length: 0, dequeueCount: 0, items: [] },
195+
failedQueue: { length: 0, dequeueCount: 0, items: [] },
196+
hydratedCache: { count: 0, failedCacheSize: 0, items: [] },
197+
};
198+
}
199+
200+
return props.sessionController.getDebugInfo();
201+
});
202+
203+
return {
204+
debugInfo,
205+
};
206+
},
207+
});
208+
</script>
209+
210+
<style scoped>
211+
.session-debug {
212+
font-family: 'Courier New', monospace;
213+
font-size: 0.75rem;
214+
max-height: 400px;
215+
overflow-y: auto;
216+
}
217+
218+
.debug-section {
219+
padding: 8px;
220+
background-color: rgba(255, 255, 255, 0.05);
221+
border-radius: 4px;
222+
min-height: 120px;
223+
}
224+
225+
.debug-header {
226+
display: flex;
227+
align-items: center;
228+
margin-bottom: 4px;
229+
padding-bottom: 4px;
230+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
231+
}
232+
233+
.debug-stats {
234+
margin-bottom: 8px;
235+
display: flex;
236+
gap: 8px;
237+
}
238+
239+
.debug-items {
240+
margin-top: 4px;
241+
padding-left: 8px;
242+
}
243+
244+
.debug-item {
245+
padding: 2px 0;
246+
border-left: 2px solid rgba(255, 255, 255, 0.2);
247+
padding-left: 6px;
248+
margin-bottom: 2px;
249+
}
250+
</style>

packages/common-ui/src/components/StudySession.vue

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
<v-progress-circular v-if="loading" color="primary" indeterminate size="32" width="4" />
77
</v-row>
88

9+
<!-- Debug Panel (only visible if window.debugMode is true) -->
10+
<session-controller-debug v-if="debugMode" :session-controller="sessionController" />
11+
912
<br />
1013

1114
<div v-if="sessionFinished" class="text-h4">
@@ -76,6 +79,7 @@ import SkMouseTrap from './SkMouseTrap.vue';
7679
import { alertUser } from './SnackbarService';
7780
import StudySessionTimer from './StudySessionTimer.vue';
7881
import CardViewer from './cardRendering/CardViewer.vue';
82+
import SessionControllerDebug from './SessionControllerDebug.vue';
7983
8084
import { CourseElo, Status, toCourseElo, ViewData } from '@vue-skuilder/common';
8185
import {
@@ -119,6 +123,7 @@ export default defineComponent({
119123
StudySessionTimer,
120124
SkMouseTrap,
121125
HeatMap,
126+
SessionControllerDebug,
122127
},
123128
124129
props: {
@@ -180,6 +185,7 @@ export default defineComponent({
180185
timeRemaining: 300, // 5 minutes * 60 seconds
181186
intervalHandler: null as NodeJS.Timeout | null,
182187
cardType: '',
188+
debugMode: (window as any).debugMode === true,
183189
};
184190
},
185191
@@ -376,7 +382,13 @@ export default defineComponent({
376382
console.log(`[StudySession] StudySession.processResponse is running...`);
377383
// DEBUG: Added logging to track hanging issue - can be removed if issue resolved
378384
// console.log(`[StudySession] About to call logCardRecord...`);
379-
const cardHistory = this.logCardRecord(r);
385+
386+
let cardHistory;
387+
try {
388+
cardHistory = this.logCardRecord(r);
389+
} catch (e: unknown) {
390+
console.log(`Caught ${e} during putCardHistory...`)
391+
}
380392
// console.log(`[StudySession] logCardRecord called, cardHistory promise created...`);
381393
382394
// Get view constraints for response processing
@@ -473,10 +485,9 @@ export default defineComponent({
473485
},
474486
475487
async logCardRecord(r: CardRecord): Promise<CardHistory<CardRecord>> {
476-
// DEBUG: Added logging to track hanging issue - can be removed if issue resolved
477-
// console.log(`[StudySession] About to call user.putCardRecord...`);
488+
console.log(`[StudySession] About to call user.putCardRecord...`);
478489
const result = await this.user!.putCardRecord(r);
479-
// console.log(`[StudySession] user.putCardRecord completed`);
490+
console.log(`[StudySession] user.putCardRecord completed`);
480491
return result;
481492
},
482493

packages/common-ui/src/composables/CompositionViewable.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export function useQuestionView<Q extends Question>(
8787
const priorAttempts = ref(0);
8888
const priorAnswers = ref<[Answer, string][]>([]);
8989
const maxAttemptsPerView = ref(3);
90-
const maxSessionViews = ref(1);
90+
const maxSessionViews = ref(2);
9191
const question = ref<Q>();
9292

9393
const submitAnswer = (answer: Answer, submittingClass?: string): QuestionRecord => {

packages/common-ui/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export * from './composables/useEntitlements';
3535

3636
export { default as StudySession } from './components/StudySession.vue';
3737
export { default as StudySessionTimer } from './components/StudySessionTimer.vue';
38+
export { default as SessionControllerDebug } from './components/SessionControllerDebug.vue';
3839
export type { StudySessionConfig } from './components/StudySession.types';
3940

4041
/*

packages/db/src/impl/common/BaseUserDB.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -722,12 +722,22 @@ Currently logged-in as ${this._username}.`
722722

723723
/**
724724
* Logs a record of the user's interaction with the card and returns the card's
725-
* up-to-date history
725+
* up-to-date history.
726+
*
727+
* **Automatic Initialization:**
728+
* If this is the user's first interaction with the card (CardHistory doesn't exist),
729+
* this method automatically creates the CardHistory document with initial values
730+
* (lapses: 0, streak: 0, bestInterval: 0).
731+
*
732+
* **Error Handling:**
733+
* - Handles 404 errors by creating initial CardHistory document
734+
* - Re-throws all other errors from UpdateQueue
726735
*
727736
* // [ ] #db-refactor extract to a smaller scope - eg, UserStudySession
728737
*
729-
* @param record the recent recorded interaction between user and card
738+
* @param record - The recent recorded interaction between user and card
730739
* @returns The updated state of the card's CardHistory data
740+
* @throws Error if document creation fails or non-404 database error occurs
731741
*/
732742

733743
public async putCardRecord<T extends CardRecord>(

0 commit comments

Comments
 (0)