Skip to content

Commit 449de77

Browse files
authored
refactor: begin deprecating qualifiedID for cards (#858)
error prone manual string manipulation. Broke after cards given `c-{id}` prefix. replacing in-place now w/ structured represnetation (object) of same data
2 parents c4cdb00 + 6ba415b commit 449de77

File tree

8 files changed

+88
-47
lines changed

8 files changed

+88
-47
lines changed

packages/db/src/core/interfaces/contentSource.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,12 @@ export function isReview(item: StudySessionItem): item is StudySessionReviewItem
3131

3232
export interface StudySessionItem {
3333
status: 'new' | 'review' | 'failed-new' | 'failed-review';
34-
qualifiedID: `${string}-${string}` | `${string}-${string}-${number}`;
35-
cardID: string;
3634
contentSourceType: 'course' | 'classroom';
3735
contentSourceID: string;
36+
// qualifiedID: `${string}-${string}` | `${string}-${string}-${number}`;
37+
cardID: string;
3838
courseID: string;
39+
elo?: number;
3940
// reviewID?: string;
4041
}
4142

packages/db/src/core/interfaces/courseDB.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,16 @@ export interface CourseDBInterface extends NavigationStrategyManager {
5353
/**
5454
* Get cards sorted by ELO rating
5555
*/
56-
getCardsByELO(elo: number, limit?: number): Promise<string[]>;
56+
getCardsByELO(
57+
elo: number,
58+
limit?: number
59+
): Promise<
60+
{
61+
courseID: string;
62+
cardID: string;
63+
elo?: number;
64+
}[]
65+
>;
5766

5867
/**
5968
* Get ELO data for specific cards
@@ -145,7 +154,7 @@ export interface CourseDBInterface extends NavigationStrategyManager {
145154
searchCards(query: string): Promise<any[]>;
146155

147156
/**
148-
* Find documents using PouchDB query syntax
157+
* Find documents using PouchDB query syntax
149158
* @param request PouchDB find request
150159
* @returns Query response
151160
*/

packages/db/src/impl/couch/classroomDB.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,7 @@ import { ENV } from '@db/factory';
88
import { logger } from '@db/util/logger';
99
import moment from 'moment';
1010
import pouch from './pouchdb-setup';
11-
import {
12-
getCourseDB,
13-
getStartAndEndKeys,
14-
createPouchDBConfig,
15-
REVIEW_TIME_FORMAT,
16-
} from '.';
11+
import { getCourseDB, getStartAndEndKeys, createPouchDBConfig, REVIEW_TIME_FORMAT } from '.';
1712
import { CourseDB, getTag } from './courseDB';
1813

1914
import { UserDBInterface } from '@db/core';
@@ -181,10 +176,13 @@ export class StudentClassroomDB
181176
}
182177
}
183178

184-
logger.info(`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => c.qualifiedID)}`);
179+
logger.info(
180+
`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => `${c.courseID}-${c.cardID}`)}`
181+
);
185182

186183
return ret.filter((c) => {
187-
if (activeCards.some((ac) => c.qualifiedID.includes(ac))) {
184+
if (activeCards.some((ac) => c.cardID.includes(ac))) {
185+
// [ ] almost certainly broken after removing qualifiedID from StudySessionItem
188186
return false;
189187
} else {
190188
return true;

packages/db/src/impl/couch/courseDB.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,13 @@ export class CourseDB implements StudyContentSource, CourseDBInterface {
277277
return s;
278278
}
279279
})
280-
.map((c) => `${this.id}-${c.id}-${c.key}`);
280+
.map((c) => {
281+
return {
282+
courseID: this.id,
283+
cardID: c.id,
284+
elo: c.key,
285+
};
286+
});
281287

282288
const str = `below:\n${below.rows.map((r) => `\t${r.id}-${r.key}\n`)}
283289
@@ -565,7 +571,7 @@ above:\n${above.rows.map((r) => `\t${r.id}-${r.key}\n`)}`;
565571
targetElo = options.elo;
566572
}
567573

568-
let cards: string[] = [];
574+
let cards: { courseID: string; cardID: string; elo?: number }[] = [];
569575
let mult: number = 4;
570576
let previousCount: number = -1;
571577
let newCount: number = 0;
@@ -578,14 +584,30 @@ above:\n${above.rows.map((r) => `\t${r.id}-${r.key}\n`)}`;
578584
logger.debug(`Found ${cards.length} elo neighbor cards...`);
579585

580586
if (filter) {
581-
cards = cards.filter(filter);
587+
const filtered = cards
588+
.map((card) => {
589+
if (card.elo) {
590+
return `${card.courseID}-${card.cardID}-${card.elo}`;
591+
} else {
592+
return `${card.courseID}-${card.cardID}`;
593+
}
594+
})
595+
.filter(filter);
582596
logger.debug(`Filtered to ${cards.length} cards...`);
597+
cards = filtered.map((card) => {
598+
const [courseID, cardID, elo] = card.split('-');
599+
return { courseID, cardID, elo: elo ? parseInt(elo) : undefined };
600+
});
583601
}
584602

585603
mult *= 2;
586604
}
587605

588-
const selectedCards: string[] = [];
606+
const selectedCards: {
607+
courseID: string;
608+
cardID: string;
609+
elo?: number;
610+
}[] = [];
589611

590612
while (selectedCards.length < options.limit && cards.length > 0) {
591613
const index = randIntWeightedTowardZero(cards.length);
@@ -594,13 +616,12 @@ above:\n${above.rows.map((r) => `\t${r.id}-${r.key}\n`)}`;
594616
}
595617

596618
return selectedCards.map((c) => {
597-
const split = c.split('-');
598619
return {
599620
courseID: this.id,
600-
cardID: split[1],
601-
qualifiedID: `${split[0]}-${split[1]}`,
621+
cardID: c.cardID,
602622
contentSourceType: 'course',
603623
contentSourceID: this.id,
624+
elo: c.elo,
604625
status: 'new',
605626
};
606627
});

packages/db/src/impl/static/courseDB.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,20 @@ export class StaticCourseDB implements CourseDBInterface {
9191
};
9292
}
9393

94-
async getCardsByELO(elo: number, limit?: number): Promise<string[]> {
95-
return this.unpacker.queryByElo(elo, limit || 25);
94+
async getCardsByELO(
95+
elo: number,
96+
limit?: number
97+
): Promise<
98+
{
99+
courseID: string;
100+
cardID: string;
101+
elo?: number;
102+
}[]
103+
> {
104+
return (await this.unpacker.queryByElo(elo, limit || 25)).map((card) => {
105+
const [courseID, cardID, elo] = card.split('-');
106+
return { courseID, cardID, elo: elo ? parseInt(elo) : undefined };
107+
});
96108
}
97109

98110
async getCardEloData(cardIds: string[]): Promise<CourseElo[]> {
@@ -403,7 +415,7 @@ export class StaticCourseDB implements CourseDBInterface {
403415
// Could be implemented with local search if needed
404416
return {
405417
docs: [],
406-
warning: 'Find operations not supported in static mode'
418+
warning: 'Find operations not supported in static mode',
407419
} as any;
408420
}
409421
}

packages/db/src/study/SessionController.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ class ItemQueue<T extends StudySessionItem> {
6262

6363
public get toString(): string {
6464
return (
65-
`${typeof this.q[0]}:\n` + this.q.map((i) => `\t${i.qualifiedID}: ${i.status}`).join('\n')
65+
`${typeof this.q[0]}:\n` +
66+
this.q.map((i) => `\t${i.courseID}+${i.cardID}: ${i.status}`).join('\n')
6667
);
6768
}
6869
}
@@ -224,7 +225,7 @@ export class SessionController extends Loggable {
224225
for (let i = 0; i < dueCards.length; i++) {
225226
const card = dueCards[i];
226227
this.reviewQ.add(card);
227-
report += `\t${card.qualifiedID}}\n`;
228+
report += `\t${card.courseID}-${card.cardID}\n`;
228229
}
229230
this.log(report);
230231
}
@@ -244,7 +245,7 @@ export class SessionController extends Loggable {
244245
for (let i = 0; i < newContent.length; i++) {
245246
if (newContent[i].length > 0) {
246247
const item = newContent[i].splice(0, 1)[0];
247-
this.log(`Adding new card: ${item.qualifiedID}`);
248+
this.log(`Adding new card: ${item.courseID}-${item.cardID}`); // revealed bug here w/ new prefixes. osbserved log "Adding new card: 5e627b7f630998243834152aa00920f5-c"
248249
this.newQ.add(item);
249250
n--;
250251
}
@@ -264,6 +265,7 @@ export class SessionController extends Loggable {
264265
}
265266

266267
public nextCard(
268+
// [ ] this is often slow. Why?
267269
action:
268270
| 'dismiss-success'
269271
| 'dismiss-failed'
@@ -373,7 +375,6 @@ export class SessionController extends Loggable {
373375
failedItem = {
374376
cardID: this._currentCard.cardID,
375377
courseID: this._currentCard.courseID,
376-
qualifiedID: this._currentCard.qualifiedID,
377378
contentSourceID: this._currentCard.contentSourceID,
378379
contentSourceType: this._currentCard.contentSourceType,
379380
status: 'failed-review',
@@ -383,7 +384,6 @@ export class SessionController extends Loggable {
383384
failedItem = {
384385
cardID: this._currentCard.cardID,
385386
courseID: this._currentCard.courseID,
386-
qualifiedID: this._currentCard.qualifiedID,
387387
contentSourceID: this._currentCard.contentSourceID,
388388
contentSourceType: this._currentCard.contentSourceType,
389389
status: 'failed-new',

packages/mcp/src/resources/cards.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ export async function handleCardsAllResource(
4242
const courseInfo = await courseDB.getCourseInfo();
4343

4444
// Get cards using ELO-based query (this gives us all cards sorted by ELO)
45-
const cardIds = await courseDB.getCardsByELO(1500, limit + offset);
45+
const eloCenteredCards = await courseDB.getCardsByELO(1500, limit + offset);
4646

4747
// Skip offset cards and take limit
48-
const targetCardIds = cardIds.slice(offset, offset + limit);
48+
const targetCards = eloCenteredCards.slice(offset, offset + limit);
4949

50-
if (targetCardIds.length === 0) {
50+
if (targetCards.length === 0) {
5151
return {
5252
cards: [],
5353
total: courseInfo.cardCount,
@@ -58,11 +58,11 @@ export async function handleCardsAllResource(
5858
}
5959

6060
// Get card documents
61-
const cardDocs = await courseDB.getCourseDocs(targetCardIds);
61+
const cardDocs = await courseDB.getCourseDocs(targetCards.map(card => card.cardID));
6262

6363
// Get ELO data for these cards
64-
const eloData = await courseDB.getCardEloData(targetCardIds);
65-
const eloMap = new Map(eloData.map((elo, index) => [targetCardIds[index], elo.global?.score || 1500]));
64+
const eloData = await courseDB.getCardEloData(targetCards.map(card => card.cardID));
65+
const eloMap = new Map(eloData.map((elo, index) => [targetCards[index], elo.global?.score || 1500]));
6666

6767
// Transform to CardResourceData format
6868
const cards: CardResourceData[] = [];
@@ -162,7 +162,7 @@ export async function handleCardsShapeResource(
162162
}
163163

164164
// Get card documents to check their shapes
165-
const cardDocs = await courseDB.getCourseDocs(allCardIds);
165+
const cardDocs = await courseDB.getCourseDocs(allCardIds.map(c => c.cardID));
166166

167167
// Filter by shape and collect card IDs
168168
const filteredCardIds: string[] = [];
@@ -240,9 +240,9 @@ export async function handleCardsEloResource(
240240

241241
// Get cards around the middle of the ELO range
242242
const targetElo = Math.floor((parsedRange.min + parsedRange.max) / 2);
243-
const cardIds = await courseDB.getCardsByELO(targetElo, 1000); // Get more to filter from
243+
const cards = await courseDB.getCardsByELO(targetElo, 1000); // Get more to filter from
244244

245-
if (cardIds.length === 0) {
245+
if (cards.length === 0) {
246246
return {
247247
cards: [],
248248
total: 0,
@@ -253,21 +253,21 @@ export async function handleCardsEloResource(
253253
}
254254

255255
// Get ELO data for all cards
256-
const eloData = await courseDB.getCardEloData(cardIds);
256+
const eloData = await courseDB.getCardEloData(cards.map(c => c.cardID));
257257

258258
// Filter by ELO range
259259
const filteredEloData = eloData
260-
.map((elo, index) => ({ elo, cardId: cardIds[index] }))
260+
.map((elo, index) => ({ elo, cardId: cards[index] }))
261261
.filter(({ elo }) => {
262262
const score = elo.global?.score || 1500;
263263
return score >= parsedRange.min && score <= parsedRange.max;
264264
});
265265

266266
// Apply pagination
267267
const paginatedEloData = filteredEloData.slice(offset, offset + limit);
268-
const paginatedCardIds = paginatedEloData.map(({ cardId }) => cardId);
268+
const paginatedCards = paginatedEloData.map(({ cardId }) => cardId);
269269

270-
if (paginatedCardIds.length === 0) {
270+
if (paginatedCards.length === 0) {
271271
return {
272272
cards: [],
273273
total: filteredEloData.length,
@@ -278,15 +278,15 @@ export async function handleCardsEloResource(
278278
}
279279

280280
// Get card documents
281-
const cardDocs = await courseDB.getCourseDocs(paginatedCardIds);
281+
const cardDocs = await courseDB.getCourseDocs(paginatedCards.map(c => c.cardID));
282282
const eloMap = new Map(paginatedEloData.map(({ elo, cardId }) => [cardId, elo.global?.score || 1500]));
283283

284284
// Transform to CardResourceData format
285-
const cards: CardResourceData[] = [];
285+
const cardsData: CardResourceData[] = [];
286286
for (const row of cardDocs.rows) {
287287
if (isSuccessRow(row)) {
288288
const doc = row.doc;
289-
cards.push({
289+
cardsData.push({
290290
cardId: doc._id,
291291
datashape: (doc as any).shape?.name || 'unknown',
292292
data: (doc as any).data || {},
@@ -299,7 +299,7 @@ export async function handleCardsEloResource(
299299
}
300300

301301
return {
302-
cards,
302+
cards: cardsData,
303303
total: filteredEloData.length,
304304
page: Math.floor(offset / limit) + 1,
305305
limit,

packages/mcp/src/resources/shapes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ export async function handleShapeSpecificResource(
8282
let examples: any[] = [];
8383
try {
8484
// Get a few cards that use this shape to provide examples
85-
const cardIds = await courseDB.getCardsByELO(1500, 10); // Get some sample cards
86-
if (cardIds.length > 0) {
87-
const cardDocs = await courseDB.getCourseDocs(cardIds.slice(0, 5)); // Limit to 5 examples
85+
const cards = await courseDB.getCardsByELO(1500, 10); // Get some sample cards
86+
if (cards.length > 0) {
87+
const cardDocs = await courseDB.getCourseDocs(cards.map(c=>c.cardID).slice(0, 5)); // Limit to 5 examples
8888
examples = [];
8989
for (const row of cardDocs.rows) {
9090
if (isSuccessRow(row) && (row.doc as any).shape?.name === shapeName) {

0 commit comments

Comments
 (0)