@vue-skuilder/db 0.1.11-9 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.d.mts +7 -6
- package/dist/core/index.d.ts +7 -6
- package/dist/core/index.js +358 -87
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +358 -87
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-DqtNroSh.d.ts → dataLayerProvider-BiP3kWix.d.mts} +8 -1
- package/dist/{dataLayerProvider-BInqI_RF.d.mts → dataLayerProvider-DSdeyRT3.d.ts} +8 -1
- package/dist/impl/couch/index.d.mts +19 -7
- package/dist/impl/couch/index.d.ts +19 -7
- package/dist/impl/couch/index.js +375 -100
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +374 -99
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.mts +23 -8
- package/dist/impl/static/index.d.ts +23 -8
- package/dist/impl/static/index.js +289 -85
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +289 -85
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/{index-CUNnL38E.d.mts → index-Bmll7Xse.d.mts} +1 -1
- package/dist/{index-CLL31bEy.d.ts → index-CD8BZz2k.d.ts} +1 -1
- package/dist/index.d.mts +123 -20
- package/dist/index.d.ts +123 -20
- package/dist/index.js +1133 -343
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1137 -343
- package/dist/index.mjs.map +1 -1
- package/dist/pouch/index.d.mts +1 -0
- package/dist/pouch/index.d.ts +1 -0
- package/dist/pouch/index.js +49 -0
- package/dist/pouch/index.js.map +1 -0
- package/dist/pouch/index.mjs +16 -0
- package/dist/pouch/index.mjs.map +1 -0
- package/dist/{types-BefDGkKa.d.ts → types-CewsN87z.d.ts} +1 -1
- package/dist/{types-DC-ckZug.d.mts → types-Dbp5DaRR.d.mts} +1 -1
- package/dist/{types-legacy-Birv-Jx6.d.mts → types-legacy-6ettoclI.d.mts} +17 -2
- package/dist/{types-legacy-Birv-Jx6.d.ts → types-legacy-6ettoclI.d.ts} +17 -2
- package/dist/{userDB-DusL7OXe.d.ts → userDB-C4yyAnpp.d.mts} +89 -56
- package/dist/{userDB-C33Hzjgn.d.mts → userDB-CD6s6ZCp.d.ts} +89 -56
- package/dist/util/packer/index.d.mts +3 -3
- package/dist/util/packer/index.d.ts +3 -3
- package/package.json +3 -3
- package/src/core/interfaces/contentSource.ts +3 -2
- package/src/core/interfaces/courseDB.ts +26 -3
- package/src/core/interfaces/dataLayerProvider.ts +9 -1
- package/src/core/interfaces/userDB.ts +80 -64
- package/src/core/navigators/elo.ts +10 -7
- package/src/core/navigators/hardcodedOrder.ts +64 -0
- package/src/core/navigators/index.ts +2 -1
- package/src/core/types/contentNavigationStrategy.ts +2 -1
- package/src/core/types/types-legacy.ts +7 -2
- package/src/impl/common/BaseUserDB.ts +60 -14
- package/src/impl/couch/CouchDBSyncStrategy.ts +2 -2
- package/src/impl/couch/PouchDataLayerProvider.ts +21 -0
- package/src/impl/couch/adminDB.ts +2 -2
- package/src/impl/couch/auth.ts +13 -4
- package/src/impl/couch/classroomDB.ts +10 -12
- package/src/impl/couch/courseAPI.ts +2 -2
- package/src/impl/couch/courseDB.ts +204 -38
- package/src/impl/couch/courseLookupDB.ts +4 -3
- package/src/impl/couch/index.ts +36 -4
- package/src/impl/couch/pouchdb-setup.ts +3 -3
- package/src/impl/couch/updateQueue.ts +59 -36
- package/src/impl/static/StaticDataLayerProvider.ts +68 -17
- package/src/impl/static/courseDB.ts +64 -20
- package/src/impl/static/coursesDB.ts +10 -6
- package/src/pouch/index.ts +2 -0
- package/src/study/ItemQueue.ts +58 -0
- package/src/study/SessionController.ts +182 -111
- package/src/study/SpacedRepetition.ts +1 -1
- package/src/study/services/CardHydrationService.ts +153 -0
- package/src/study/services/EloService.ts +85 -0
- package/src/study/services/ResponseProcessor.ts +224 -0
- package/src/study/services/SrsService.ts +44 -0
- package/tsup.config.ts +1 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CardHistory,
|
|
3
|
+
CardRecord,
|
|
4
|
+
CourseRegistrationDoc,
|
|
5
|
+
isQuestionRecord,
|
|
6
|
+
QuestionRecord,
|
|
7
|
+
StudySessionItem,
|
|
8
|
+
} from '@db/core';
|
|
9
|
+
import { logger } from '@db/util/logger';
|
|
10
|
+
import { ResponseResult, StudySessionRecord } from '../SessionController';
|
|
11
|
+
import { EloService } from './EloService';
|
|
12
|
+
import { SrsService } from './SrsService';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Service responsible for orchestrating the complete response processing workflow.
|
|
16
|
+
* Coordinates SRS scheduling and ELO updates for user card interactions.
|
|
17
|
+
*/
|
|
18
|
+
export class ResponseProcessor {
|
|
19
|
+
private srsService: SrsService;
|
|
20
|
+
private eloService: EloService;
|
|
21
|
+
|
|
22
|
+
constructor(srsService: SrsService, eloService: EloService) {
|
|
23
|
+
this.srsService = srsService;
|
|
24
|
+
this.eloService = eloService;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Processes a user's response to a card, handling SRS scheduling and ELO updates.
|
|
29
|
+
* @param cardRecord User's response record
|
|
30
|
+
* @param cardHistory Promise resolving to the card's history
|
|
31
|
+
* @param studySessionItem Current study session item
|
|
32
|
+
* @param courseRegistrationDoc User's course registration (for ELO updates)
|
|
33
|
+
* @param currentCard Current study session record
|
|
34
|
+
* @param courseId Course identifier
|
|
35
|
+
* @param cardId Card identifier
|
|
36
|
+
* @param maxAttemptsPerView Maximum attempts allowed per view
|
|
37
|
+
* @param maxSessionViews Maximum session views for this card
|
|
38
|
+
* @param sessionViews Current number of session views
|
|
39
|
+
* @returns ResponseResult with navigation and UI instructions
|
|
40
|
+
*/
|
|
41
|
+
public async processResponse(
|
|
42
|
+
cardRecord: CardRecord,
|
|
43
|
+
cardHistory: Promise<CardHistory<CardRecord>>,
|
|
44
|
+
studySessionItem: StudySessionItem,
|
|
45
|
+
courseRegistrationDoc: CourseRegistrationDoc,
|
|
46
|
+
currentCard: StudySessionRecord,
|
|
47
|
+
courseId: string,
|
|
48
|
+
cardId: string,
|
|
49
|
+
maxAttemptsPerView: number,
|
|
50
|
+
maxSessionViews: number,
|
|
51
|
+
sessionViews: number
|
|
52
|
+
): Promise<ResponseResult> {
|
|
53
|
+
// Handle non-question records (simple dismiss)
|
|
54
|
+
if (!isQuestionRecord(cardRecord)) {
|
|
55
|
+
return {
|
|
56
|
+
nextCardAction: 'dismiss-success',
|
|
57
|
+
shouldLoadNextCard: true,
|
|
58
|
+
isCorrect: true, // non-question records are considered "correct"
|
|
59
|
+
shouldClearFeedbackShadow: true,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const history = await cardHistory;
|
|
64
|
+
|
|
65
|
+
// Handle correct responses
|
|
66
|
+
if (cardRecord.isCorrect) {
|
|
67
|
+
return this.processCorrectResponse(
|
|
68
|
+
cardRecord,
|
|
69
|
+
history,
|
|
70
|
+
studySessionItem,
|
|
71
|
+
courseRegistrationDoc,
|
|
72
|
+
currentCard,
|
|
73
|
+
courseId,
|
|
74
|
+
cardId
|
|
75
|
+
);
|
|
76
|
+
} else {
|
|
77
|
+
// Handle incorrect responses
|
|
78
|
+
return this.processIncorrectResponse(
|
|
79
|
+
cardRecord,
|
|
80
|
+
history,
|
|
81
|
+
courseRegistrationDoc,
|
|
82
|
+
currentCard,
|
|
83
|
+
courseId,
|
|
84
|
+
cardId,
|
|
85
|
+
maxAttemptsPerView,
|
|
86
|
+
maxSessionViews,
|
|
87
|
+
sessionViews
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Handles processing for correct responses: SRS scheduling and ELO updates.
|
|
94
|
+
*/
|
|
95
|
+
private processCorrectResponse(
|
|
96
|
+
cardRecord: QuestionRecord,
|
|
97
|
+
history: CardHistory<CardRecord>,
|
|
98
|
+
studySessionItem: StudySessionItem,
|
|
99
|
+
courseRegistrationDoc: CourseRegistrationDoc,
|
|
100
|
+
currentCard: StudySessionRecord,
|
|
101
|
+
courseId: string,
|
|
102
|
+
cardId: string
|
|
103
|
+
): ResponseResult {
|
|
104
|
+
// Only schedule and update ELO for first-time attempts
|
|
105
|
+
if (cardRecord.priorAttemps === 0) {
|
|
106
|
+
// Schedule the card for future review based on performance (async, non-blocking)
|
|
107
|
+
void this.srsService.scheduleReview(history, studySessionItem);
|
|
108
|
+
|
|
109
|
+
// Update ELO ratings
|
|
110
|
+
if (history.records.length === 1) {
|
|
111
|
+
// First interaction with this card - standard ELO update (async, non-blocking)
|
|
112
|
+
const userScore = 0.5 + (cardRecord.performance as number) / 2;
|
|
113
|
+
void this.eloService.updateUserAndCardElo(
|
|
114
|
+
userScore,
|
|
115
|
+
courseId,
|
|
116
|
+
cardId,
|
|
117
|
+
courseRegistrationDoc,
|
|
118
|
+
currentCard
|
|
119
|
+
);
|
|
120
|
+
} else {
|
|
121
|
+
// Multiple interactions - reduce K-factor to limit ELO volatility (async, non-blocking)
|
|
122
|
+
const k = Math.ceil(32 / history.records.length);
|
|
123
|
+
const userScore = 0.5 + (cardRecord.performance as number) / 2;
|
|
124
|
+
void this.eloService.updateUserAndCardElo(
|
|
125
|
+
userScore,
|
|
126
|
+
courseId,
|
|
127
|
+
cardId,
|
|
128
|
+
courseRegistrationDoc,
|
|
129
|
+
currentCard,
|
|
130
|
+
k
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
logger.info(
|
|
135
|
+
'[ResponseProcessor] Processed correct response with SRS scheduling and ELO update'
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
nextCardAction: 'dismiss-success',
|
|
140
|
+
shouldLoadNextCard: true,
|
|
141
|
+
isCorrect: true,
|
|
142
|
+
performanceScore: cardRecord.performance as number,
|
|
143
|
+
shouldClearFeedbackShadow: true,
|
|
144
|
+
};
|
|
145
|
+
} else {
|
|
146
|
+
logger.info(
|
|
147
|
+
'[ResponseProcessor] Processed correct response (retry attempt - no scheduling/ELO)'
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
nextCardAction: 'marked-failed',
|
|
152
|
+
shouldLoadNextCard: true,
|
|
153
|
+
isCorrect: true,
|
|
154
|
+
performanceScore: cardRecord.performance as number,
|
|
155
|
+
shouldClearFeedbackShadow: true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Handles processing for incorrect responses: ELO updates only.
|
|
162
|
+
*/
|
|
163
|
+
private processIncorrectResponse(
|
|
164
|
+
cardRecord: QuestionRecord,
|
|
165
|
+
history: CardHistory<CardRecord>,
|
|
166
|
+
courseRegistrationDoc: CourseRegistrationDoc,
|
|
167
|
+
currentCard: StudySessionRecord,
|
|
168
|
+
courseId: string,
|
|
169
|
+
cardId: string,
|
|
170
|
+
maxAttemptsPerView: number,
|
|
171
|
+
maxSessionViews: number,
|
|
172
|
+
sessionViews: number
|
|
173
|
+
): ResponseResult {
|
|
174
|
+
// Update ELO for first-time failures (not subsequent attempts on same card) (async, non-blocking)
|
|
175
|
+
if (history.records.length !== 1 && cardRecord.priorAttemps === 0) {
|
|
176
|
+
void this.eloService.updateUserAndCardElo(
|
|
177
|
+
0, // Failed response = 0 score
|
|
178
|
+
courseId,
|
|
179
|
+
cardId,
|
|
180
|
+
courseRegistrationDoc,
|
|
181
|
+
currentCard
|
|
182
|
+
);
|
|
183
|
+
logger.info('[ResponseProcessor] Processed incorrect response with ELO update');
|
|
184
|
+
} else {
|
|
185
|
+
logger.info('[ResponseProcessor] Processed incorrect response (no ELO update needed)');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Determine navigation based on attempt limits
|
|
189
|
+
if (currentCard.records.length >= maxAttemptsPerView) {
|
|
190
|
+
if (sessionViews >= maxSessionViews) {
|
|
191
|
+
// Too many session views - dismiss completely with ELO penalty (async, non-blocking)
|
|
192
|
+
void this.eloService.updateUserAndCardElo(
|
|
193
|
+
0,
|
|
194
|
+
courseId,
|
|
195
|
+
cardId,
|
|
196
|
+
courseRegistrationDoc,
|
|
197
|
+
currentCard
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
nextCardAction: 'dismiss-failed',
|
|
201
|
+
shouldLoadNextCard: true,
|
|
202
|
+
isCorrect: false,
|
|
203
|
+
shouldClearFeedbackShadow: true,
|
|
204
|
+
};
|
|
205
|
+
} else {
|
|
206
|
+
// Mark as failed for later retry
|
|
207
|
+
return {
|
|
208
|
+
nextCardAction: 'marked-failed',
|
|
209
|
+
shouldLoadNextCard: true,
|
|
210
|
+
isCorrect: false,
|
|
211
|
+
shouldClearFeedbackShadow: true,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
// Allow more attempts on same card
|
|
216
|
+
return {
|
|
217
|
+
nextCardAction: 'none',
|
|
218
|
+
shouldLoadNextCard: false,
|
|
219
|
+
isCorrect: false,
|
|
220
|
+
shouldClearFeedbackShadow: true,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import moment from 'moment';
|
|
2
|
+
import { CardHistory, CardRecord, UserDBInterface } from '@db/core';
|
|
3
|
+
import { isReview, StudySessionItem } from '@db/impl/couch';
|
|
4
|
+
import { newInterval } from '../SpacedRepetition';
|
|
5
|
+
import { logger } from '@db/util/logger';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Service responsible for Spaced Repetition System (SRS) scheduling logic.
|
|
9
|
+
*/
|
|
10
|
+
export class SrsService {
|
|
11
|
+
private user: UserDBInterface;
|
|
12
|
+
|
|
13
|
+
constructor(user: UserDBInterface) {
|
|
14
|
+
this.user = user;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Calculates the next review time for a card based on its history and
|
|
19
|
+
* schedules it in the user's database.
|
|
20
|
+
* @param history The full history of the card.
|
|
21
|
+
* @param item The study session item, used to determine if a previous review needs to be cleared.
|
|
22
|
+
*/
|
|
23
|
+
public async scheduleReview(
|
|
24
|
+
history: CardHistory<CardRecord>,
|
|
25
|
+
item: StudySessionItem
|
|
26
|
+
): Promise<void> {
|
|
27
|
+
const nextInterval = newInterval(this.user, history);
|
|
28
|
+
const nextReviewTime = moment.utc().add(nextInterval, 'seconds');
|
|
29
|
+
|
|
30
|
+
if (isReview(item)) {
|
|
31
|
+
logger.info(`[SrsService] Removing previously scheduled review for: ${item.cardID}`);
|
|
32
|
+
void this.user.removeScheduledCardReview(item.reviewID);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void this.user.scheduleCardReview({
|
|
36
|
+
user: this.user.getUsername(),
|
|
37
|
+
course_id: history.courseID,
|
|
38
|
+
card_id: history.cardID,
|
|
39
|
+
time: nextReviewTime,
|
|
40
|
+
scheduledFor: item.contentSourceType,
|
|
41
|
+
schedulingAgentId: item.contentSourceID,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|