@vue-skuilder/db 0.1.11-7 → 0.1.11
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 +5 -5
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +212 -50
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +212 -50
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-DqtNroSh.d.ts → dataLayerProvider-VieuAAkV.d.mts} +8 -1
- package/dist/{dataLayerProvider-BInqI_RF.d.mts → dataLayerProvider-juuqUHOP.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 +229 -63
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +228 -62
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.mts +13 -6
- package/dist/impl/static/index.d.ts +13 -6
- package/dist/impl/static/index.js +142 -46
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +142 -46
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/{index-CLL31bEy.d.ts → index-CWY6yhkV.d.ts} +1 -1
- package/dist/{index-CUNnL38E.d.mts → index-DZyxHCcf.d.mts} +1 -1
- package/dist/index.d.mts +28 -20
- package/dist/index.d.ts +28 -20
- package/dist/index.js +374 -108
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +378 -108
- 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-DC-ckZug.d.mts → types-Che4wTwA.d.mts} +1 -1
- package/dist/{types-BefDGkKa.d.ts → types-DtoI27Xh.d.ts} +1 -1
- package/dist/{types-legacy-Birv-Jx6.d.mts → types-legacy-B8ahaCbj.d.mts} +5 -1
- package/dist/{types-legacy-Birv-Jx6.d.ts → types-legacy-B8ahaCbj.d.ts} +5 -1
- package/dist/{userDB-C33Hzjgn.d.mts → userDB-B7zTQ123.d.ts} +88 -55
- package/dist/{userDB-DusL7OXe.d.ts → userDB-DJ8HMw83.d.mts} +88 -55
- 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/index.ts +1 -1
- package/src/core/types/types-legacy.ts +5 -0
- package/src/impl/common/BaseUserDB.ts +45 -3
- 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 +130 -11
- 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 +51 -33
- package/src/impl/static/StaticDataLayerProvider.ts +11 -0
- package/src/impl/static/courseDB.ts +47 -8
- package/src/pouch/index.ts +2 -0
- package/src/study/SessionController.ts +168 -51
- package/src/study/SpacedRepetition.ts +1 -1
- package/tsup.config.ts +1 -0
package/dist/index.mjs
CHANGED
|
@@ -165,9 +165,9 @@ var init_pouchdb_setup = __esm({
|
|
|
165
165
|
PouchDB.plugin(PouchDBFind);
|
|
166
166
|
PouchDB.plugin(PouchDBAuth);
|
|
167
167
|
PouchDB.defaults({
|
|
168
|
-
ajax: {
|
|
169
|
-
|
|
170
|
-
}
|
|
168
|
+
// ajax: {
|
|
169
|
+
// timeout: 60000,
|
|
170
|
+
// },
|
|
171
171
|
});
|
|
172
172
|
pouchdb_setup_default = PouchDB;
|
|
173
173
|
}
|
|
@@ -450,37 +450,48 @@ var init_updateQueue = __esm({
|
|
|
450
450
|
} else {
|
|
451
451
|
if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
|
|
452
452
|
this.inprogressUpdates[id] = true;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
453
|
+
const MAX_RETRIES = 5;
|
|
454
|
+
for (let i = 0; i < MAX_RETRIES; i++) {
|
|
455
|
+
try {
|
|
456
|
+
const doc = await this.readDB.get(id);
|
|
457
|
+
logger.debug(`Retrieved doc: ${id}`);
|
|
458
|
+
let updatedDoc = { ...doc };
|
|
459
|
+
const updatesToApply = [...this.pendingUpdates[id]];
|
|
460
|
+
for (const update of updatesToApply) {
|
|
461
|
+
if (typeof update === "function") {
|
|
462
|
+
updatedDoc = { ...updatedDoc, ...update(updatedDoc) };
|
|
463
|
+
} else {
|
|
464
|
+
updatedDoc = {
|
|
465
|
+
...updatedDoc,
|
|
466
|
+
...update
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
await this.writeDB.put(updatedDoc);
|
|
471
|
+
logger.debug(`Put doc: ${id}`);
|
|
472
|
+
this.pendingUpdates[id].splice(0, updatesToApply.length);
|
|
473
|
+
if (this.pendingUpdates[id].length === 0) {
|
|
474
|
+
this.inprogressUpdates[id] = false;
|
|
475
|
+
delete this.inprogressUpdates[id];
|
|
460
476
|
} else {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
477
|
+
return this.applyUpdates(id);
|
|
478
|
+
}
|
|
479
|
+
return updatedDoc;
|
|
480
|
+
} catch (e) {
|
|
481
|
+
if (e.name === "conflict" && i < MAX_RETRIES - 1) {
|
|
482
|
+
logger.warn(`Conflict on update for doc ${id}, retry #${i + 1}`);
|
|
483
|
+
await new Promise((res) => setTimeout(res, 50 * Math.random()));
|
|
484
|
+
} else {
|
|
485
|
+
delete this.inprogressUpdates[id];
|
|
486
|
+
if (this.pendingUpdates[id]) {
|
|
487
|
+
delete this.pendingUpdates[id];
|
|
488
|
+
}
|
|
489
|
+
logger.error(`Error on attemped update (retry ${i}): ${JSON.stringify(e)}`);
|
|
490
|
+
throw e;
|
|
465
491
|
}
|
|
466
492
|
}
|
|
467
|
-
await this.writeDB.put(doc);
|
|
468
|
-
logger.debug(`Put doc: ${id}`);
|
|
469
|
-
if (this.pendingUpdates[id].length === 0) {
|
|
470
|
-
this.inprogressUpdates[id] = false;
|
|
471
|
-
delete this.inprogressUpdates[id];
|
|
472
|
-
} else {
|
|
473
|
-
return this.applyUpdates(id);
|
|
474
|
-
}
|
|
475
|
-
return doc;
|
|
476
|
-
} catch (e) {
|
|
477
|
-
delete this.inprogressUpdates[id];
|
|
478
|
-
if (this.pendingUpdates[id]) {
|
|
479
|
-
delete this.pendingUpdates[id];
|
|
480
|
-
}
|
|
481
|
-
logger.error(`Error on attemped update: ${JSON.stringify(e)}`);
|
|
482
|
-
throw e;
|
|
483
493
|
}
|
|
494
|
+
throw new Error(`UpdateQueue failed for doc ${id} after ${MAX_RETRIES} retries.`);
|
|
484
495
|
} else {
|
|
485
496
|
throw new Error(`Empty Updates Queue Triggered`);
|
|
486
497
|
}
|
|
@@ -735,7 +746,7 @@ function getCourseDB(courseID) {
|
|
|
735
746
|
const dbName = `coursedb-${courseID}`;
|
|
736
747
|
return new pouchdb_setup_default(
|
|
737
748
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
738
|
-
|
|
749
|
+
createPouchDBConfig()
|
|
739
750
|
);
|
|
740
751
|
}
|
|
741
752
|
var AlreadyTaggedErr;
|
|
@@ -857,6 +868,7 @@ var init_courseLookupDB = __esm({
|
|
|
857
868
|
const doc = await _CourseLookup._db.get(courseID);
|
|
858
869
|
return await _CourseLookup._db.remove(doc);
|
|
859
870
|
}
|
|
871
|
+
// [ ] rename to allCourses()
|
|
860
872
|
static async allCourseWare() {
|
|
861
873
|
const resp = await _CourseLookup._db.allDocs({
|
|
862
874
|
include_docs: true
|
|
@@ -927,13 +939,16 @@ var init_elo = __esm({
|
|
|
927
939
|
}
|
|
928
940
|
async getNewCards(limit = 99) {
|
|
929
941
|
const activeCards = await this.user.getActiveCards();
|
|
930
|
-
return (await this.course.getCardsCenteredAtELO(
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
942
|
+
return (await this.course.getCardsCenteredAtELO(
|
|
943
|
+
{ limit, elo: "user" },
|
|
944
|
+
(c) => {
|
|
945
|
+
if (activeCards.some((ac) => c.cardID === ac.cardID)) {
|
|
946
|
+
return false;
|
|
947
|
+
} else {
|
|
948
|
+
return true;
|
|
949
|
+
}
|
|
935
950
|
}
|
|
936
|
-
|
|
951
|
+
)).map((c) => {
|
|
937
952
|
return {
|
|
938
953
|
...c,
|
|
939
954
|
status: "new"
|
|
@@ -981,7 +996,7 @@ var init_navigators = __esm({
|
|
|
981
996
|
static async create(user, course, strategyData) {
|
|
982
997
|
const implementingClass = strategyData.implementingClass;
|
|
983
998
|
let NavigatorImpl;
|
|
984
|
-
const variations = ["", ".js", "
|
|
999
|
+
const variations = [".ts", ".js", ""];
|
|
985
1000
|
for (const ext of variations) {
|
|
986
1001
|
try {
|
|
987
1002
|
const module = await globImport(`./${implementingClass}${ext}`);
|
|
@@ -1290,7 +1305,13 @@ var init_courseDB = __esm({
|
|
|
1290
1305
|
} else {
|
|
1291
1306
|
return s;
|
|
1292
1307
|
}
|
|
1293
|
-
}).map((c) =>
|
|
1308
|
+
}).map((c) => {
|
|
1309
|
+
return {
|
|
1310
|
+
courseID: this.id,
|
|
1311
|
+
cardID: c.id,
|
|
1312
|
+
elo: c.key
|
|
1313
|
+
};
|
|
1314
|
+
});
|
|
1294
1315
|
const str = `below:
|
|
1295
1316
|
${below.rows.map((r) => ` ${r.id}-${r.key}
|
|
1296
1317
|
`)}
|
|
@@ -1345,7 +1366,13 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
1345
1366
|
}
|
|
1346
1367
|
}
|
|
1347
1368
|
async addTagToCard(cardId, tagId, updateELO) {
|
|
1348
|
-
return await addTagToCard(
|
|
1369
|
+
return await addTagToCard(
|
|
1370
|
+
this.id,
|
|
1371
|
+
cardId,
|
|
1372
|
+
tagId,
|
|
1373
|
+
(await this._getCurrentUser()).getUsername(),
|
|
1374
|
+
updateELO
|
|
1375
|
+
);
|
|
1349
1376
|
}
|
|
1350
1377
|
async removeTagFromCard(cardId, tagId) {
|
|
1351
1378
|
return await removeTagFromCard(this.id, cardId, tagId);
|
|
@@ -1538,17 +1565,93 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
1538
1565
|
selectedCards.push(card);
|
|
1539
1566
|
}
|
|
1540
1567
|
return selectedCards.map((c) => {
|
|
1541
|
-
const split = c.split("-");
|
|
1542
1568
|
return {
|
|
1543
1569
|
courseID: this.id,
|
|
1544
|
-
cardID:
|
|
1545
|
-
qualifiedID: `${split[0]}-${split[1]}`,
|
|
1570
|
+
cardID: c.cardID,
|
|
1546
1571
|
contentSourceType: "course",
|
|
1547
1572
|
contentSourceID: this.id,
|
|
1573
|
+
elo: c.elo,
|
|
1548
1574
|
status: "new"
|
|
1549
1575
|
};
|
|
1550
1576
|
});
|
|
1551
1577
|
}
|
|
1578
|
+
// Admin search methods
|
|
1579
|
+
async searchCards(query) {
|
|
1580
|
+
logger.log(`[CourseDB ${this.id}] Searching for: "${query}"`);
|
|
1581
|
+
let displayableData;
|
|
1582
|
+
try {
|
|
1583
|
+
displayableData = await this.db.find({
|
|
1584
|
+
selector: {
|
|
1585
|
+
docType: "DISPLAYABLE_DATA",
|
|
1586
|
+
"data.0.data": { $regex: `.*${query}.*` }
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
logger.log(`[CourseDB ${this.id}] Regex search on data[0].data successful`);
|
|
1590
|
+
} catch (regexError) {
|
|
1591
|
+
logger.log(
|
|
1592
|
+
`[CourseDB ${this.id}] Regex search failed, falling back to manual search:`,
|
|
1593
|
+
regexError
|
|
1594
|
+
);
|
|
1595
|
+
const allDisplayable = await this.db.find({
|
|
1596
|
+
selector: {
|
|
1597
|
+
docType: "DISPLAYABLE_DATA"
|
|
1598
|
+
}
|
|
1599
|
+
});
|
|
1600
|
+
logger.log(
|
|
1601
|
+
`[CourseDB ${this.id}] Retrieved ${allDisplayable.docs.length} documents for manual filtering`
|
|
1602
|
+
);
|
|
1603
|
+
displayableData = {
|
|
1604
|
+
docs: allDisplayable.docs.filter((doc) => {
|
|
1605
|
+
const docString = JSON.stringify(doc).toLowerCase();
|
|
1606
|
+
const match = docString.includes(query.toLowerCase());
|
|
1607
|
+
if (match) {
|
|
1608
|
+
logger.log(`[CourseDB ${this.id}] Manual match found in document: ${doc._id}`);
|
|
1609
|
+
}
|
|
1610
|
+
return match;
|
|
1611
|
+
})
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
logger.log(
|
|
1615
|
+
`[CourseDB ${this.id}] Found ${displayableData.docs.length} displayable data documents`
|
|
1616
|
+
);
|
|
1617
|
+
if (displayableData.docs.length === 0) {
|
|
1618
|
+
const allDisplayableData = await this.db.find({
|
|
1619
|
+
selector: {
|
|
1620
|
+
docType: "DISPLAYABLE_DATA"
|
|
1621
|
+
},
|
|
1622
|
+
limit: 5
|
|
1623
|
+
// Just sample a few
|
|
1624
|
+
});
|
|
1625
|
+
logger.log(
|
|
1626
|
+
`[CourseDB ${this.id}] Sample displayable data:`,
|
|
1627
|
+
allDisplayableData.docs.map((d) => ({
|
|
1628
|
+
id: d._id,
|
|
1629
|
+
docType: d.docType,
|
|
1630
|
+
dataStructure: d.data ? Object.keys(d.data) : "no data field",
|
|
1631
|
+
dataContent: d.data,
|
|
1632
|
+
fullDoc: d
|
|
1633
|
+
}))
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1636
|
+
const allResults = [];
|
|
1637
|
+
for (const dd of displayableData.docs) {
|
|
1638
|
+
const cards = await this.db.find({
|
|
1639
|
+
selector: {
|
|
1640
|
+
docType: "CARD",
|
|
1641
|
+
id_displayable_data: { $in: [dd._id] }
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
logger.log(
|
|
1645
|
+
`[CourseDB ${this.id}] Displayable data ${dd._id} linked to ${cards.docs.length} cards`
|
|
1646
|
+
);
|
|
1647
|
+
allResults.push(...cards.docs);
|
|
1648
|
+
}
|
|
1649
|
+
logger.log(`[CourseDB ${this.id}] Total cards found: ${allResults.length}`);
|
|
1650
|
+
return allResults;
|
|
1651
|
+
}
|
|
1652
|
+
async find(request) {
|
|
1653
|
+
return this.db.find(request);
|
|
1654
|
+
}
|
|
1552
1655
|
};
|
|
1553
1656
|
}
|
|
1554
1657
|
});
|
|
@@ -1614,7 +1717,7 @@ var init_classroomDB2 = __esm({
|
|
|
1614
1717
|
const dbName = `classdb-student-${this._id}`;
|
|
1615
1718
|
this._db = new pouchdb_setup_default(
|
|
1616
1719
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
1617
|
-
|
|
1720
|
+
createPouchDBConfig()
|
|
1618
1721
|
);
|
|
1619
1722
|
try {
|
|
1620
1723
|
const cfg = await this._db.get(CLASSROOM_CONFIG);
|
|
@@ -1683,9 +1786,11 @@ var init_classroomDB2 = __esm({
|
|
|
1683
1786
|
ret.push(await getCourseDB2(content.courseID).get(content.cardID));
|
|
1684
1787
|
}
|
|
1685
1788
|
}
|
|
1686
|
-
logger.info(
|
|
1789
|
+
logger.info(
|
|
1790
|
+
`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => `${c.courseID}-${c.cardID}`)}`
|
|
1791
|
+
);
|
|
1687
1792
|
return ret.filter((c) => {
|
|
1688
|
-
if (activeCards.some((ac) => c.
|
|
1793
|
+
if (activeCards.some((ac) => c.cardID === ac.cardID)) {
|
|
1689
1794
|
return false;
|
|
1690
1795
|
} else {
|
|
1691
1796
|
return true;
|
|
@@ -1704,11 +1809,11 @@ var init_classroomDB2 = __esm({
|
|
|
1704
1809
|
const stuDbName = `classdb-student-${this._id}`;
|
|
1705
1810
|
this._db = new pouchdb_setup_default(
|
|
1706
1811
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
1707
|
-
|
|
1812
|
+
createPouchDBConfig()
|
|
1708
1813
|
);
|
|
1709
1814
|
this._stuDb = new pouchdb_setup_default(
|
|
1710
1815
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + stuDbName,
|
|
1711
|
-
|
|
1816
|
+
createPouchDBConfig()
|
|
1712
1817
|
);
|
|
1713
1818
|
try {
|
|
1714
1819
|
return this._db.get(CLASSROOM_CONFIG).then((cfg) => {
|
|
@@ -1793,7 +1898,7 @@ var init_adminDB2 = __esm({
|
|
|
1793
1898
|
constructor() {
|
|
1794
1899
|
this.usersDB = new pouchdb_setup_default(
|
|
1795
1900
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "_users",
|
|
1796
|
-
|
|
1901
|
+
createPouchDBConfig()
|
|
1797
1902
|
);
|
|
1798
1903
|
}
|
|
1799
1904
|
async getUsers() {
|
|
@@ -1855,9 +1960,10 @@ import fetch2 from "cross-fetch";
|
|
|
1855
1960
|
async function getCurrentSession() {
|
|
1856
1961
|
try {
|
|
1857
1962
|
if (ENV.COUCHDB_SERVER_URL === NOT_SET || ENV.COUCHDB_SERVER_PROTOCOL === NOT_SET) {
|
|
1858
|
-
throw new Error(
|
|
1963
|
+
throw new Error(`CouchDB server configuration not properly initialized. Protocol: "${ENV.COUCHDB_SERVER_PROTOCOL}", URL: "${ENV.COUCHDB_SERVER_URL}"`);
|
|
1859
1964
|
}
|
|
1860
|
-
const
|
|
1965
|
+
const baseUrl = ENV.COUCHDB_SERVER_URL.endsWith("/") ? ENV.COUCHDB_SERVER_URL.slice(0, -1) : ENV.COUCHDB_SERVER_URL;
|
|
1966
|
+
const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${baseUrl}/_session`;
|
|
1861
1967
|
logger.debug(`Attempting session check at: ${url}`);
|
|
1862
1968
|
const response = await fetch2(url, {
|
|
1863
1969
|
method: "GET",
|
|
@@ -1869,8 +1975,10 @@ async function getCurrentSession() {
|
|
|
1869
1975
|
const resp = await response.json();
|
|
1870
1976
|
return resp;
|
|
1871
1977
|
} catch (error) {
|
|
1872
|
-
|
|
1873
|
-
|
|
1978
|
+
const baseUrl = ENV.COUCHDB_SERVER_URL.endsWith("/") ? ENV.COUCHDB_SERVER_URL.slice(0, -1) : ENV.COUCHDB_SERVER_URL;
|
|
1979
|
+
const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${baseUrl}/_session`;
|
|
1980
|
+
logger.error(`Session check error attempting to connect to: ${url} - ${error}`);
|
|
1981
|
+
throw new Error(`Session check failed connecting to ${url}: ${error}`);
|
|
1874
1982
|
}
|
|
1875
1983
|
}
|
|
1876
1984
|
async function getLoggedInUsername() {
|
|
@@ -2063,7 +2171,7 @@ var init_CouchDBSyncStrategy = __esm({
|
|
|
2063
2171
|
log3(`Fetching user database: ${dbName} (${username})`);
|
|
2064
2172
|
const ret = new pouchdb_setup_default(
|
|
2065
2173
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
2066
|
-
|
|
2174
|
+
createPouchDBConfig()
|
|
2067
2175
|
);
|
|
2068
2176
|
if (guestAccount) {
|
|
2069
2177
|
updateGuestAccountExpirationDate(ret);
|
|
@@ -2078,10 +2186,29 @@ var init_CouchDBSyncStrategy = __esm({
|
|
|
2078
2186
|
import fetch3 from "cross-fetch";
|
|
2079
2187
|
import moment4 from "moment";
|
|
2080
2188
|
import process2 from "process";
|
|
2189
|
+
function createPouchDBConfig() {
|
|
2190
|
+
const hasExplicitCredentials = ENV.COUCHDB_USERNAME && ENV.COUCHDB_PASSWORD;
|
|
2191
|
+
const isNodeEnvironment2 = typeof window === "undefined";
|
|
2192
|
+
if (hasExplicitCredentials && isNodeEnvironment2) {
|
|
2193
|
+
return {
|
|
2194
|
+
fetch(url, opts = {}) {
|
|
2195
|
+
const basicAuth = btoa(`${ENV.COUCHDB_USERNAME}:${ENV.COUCHDB_PASSWORD}`);
|
|
2196
|
+
const headers = new Headers(opts.headers || {});
|
|
2197
|
+
headers.set("Authorization", `Basic ${basicAuth}`);
|
|
2198
|
+
const newOpts = {
|
|
2199
|
+
...opts,
|
|
2200
|
+
headers
|
|
2201
|
+
};
|
|
2202
|
+
return pouchdb_setup_default.fetch(url, newOpts);
|
|
2203
|
+
}
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
return pouchDBincludeCredentialsConfig;
|
|
2207
|
+
}
|
|
2081
2208
|
function getCourseDB2(courseID) {
|
|
2082
2209
|
return new pouchdb_setup_default(
|
|
2083
2210
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
|
|
2084
|
-
|
|
2211
|
+
createPouchDBConfig()
|
|
2085
2212
|
);
|
|
2086
2213
|
}
|
|
2087
2214
|
function getCourseDocs(courseID, docIDs, options = {}) {
|
|
@@ -2370,6 +2497,9 @@ Currently logged-in as ${this._username}.`
|
|
|
2370
2497
|
await this.init();
|
|
2371
2498
|
return ret;
|
|
2372
2499
|
}
|
|
2500
|
+
async get(id) {
|
|
2501
|
+
return this.localDB.get(id);
|
|
2502
|
+
}
|
|
2373
2503
|
update(id, update) {
|
|
2374
2504
|
return this.updateQueue.update(id, update);
|
|
2375
2505
|
}
|
|
@@ -2416,7 +2546,12 @@ Currently logged-in as ${this._username}.`
|
|
|
2416
2546
|
endkey: keys.endkey,
|
|
2417
2547
|
include_docs: true
|
|
2418
2548
|
});
|
|
2419
|
-
return reviews.rows.map((r) =>
|
|
2549
|
+
return reviews.rows.map((r) => {
|
|
2550
|
+
return {
|
|
2551
|
+
courseID: r.doc.courseId,
|
|
2552
|
+
cardID: r.doc.cardId
|
|
2553
|
+
};
|
|
2554
|
+
});
|
|
2420
2555
|
}
|
|
2421
2556
|
async getActivityRecords() {
|
|
2422
2557
|
try {
|
|
@@ -2696,8 +2831,18 @@ Currently logged-in as ${this._username}.`
|
|
|
2696
2831
|
}
|
|
2697
2832
|
this.setDBandQ();
|
|
2698
2833
|
this.syncStrategy.startSync(this.localDB, this.remoteDB);
|
|
2699
|
-
|
|
2700
|
-
|
|
2834
|
+
this.applyDesignDocs().catch((error) => {
|
|
2835
|
+
log4(`Error in applyDesignDocs background task: ${error}`);
|
|
2836
|
+
if (error && typeof error === "object") {
|
|
2837
|
+
log4(`Full error details in applyDesignDocs: ${JSON.stringify(error)}`);
|
|
2838
|
+
}
|
|
2839
|
+
});
|
|
2840
|
+
this.deduplicateReviews().catch((error) => {
|
|
2841
|
+
log4(`Error in deduplicateReviews background task: ${error}`);
|
|
2842
|
+
if (error && typeof error === "object") {
|
|
2843
|
+
log4(`Full error details in background task: ${JSON.stringify(error)}`);
|
|
2844
|
+
}
|
|
2845
|
+
});
|
|
2701
2846
|
_BaseUser._initialized = true;
|
|
2702
2847
|
}
|
|
2703
2848
|
static designDocs = [
|
|
@@ -2715,10 +2860,15 @@ Currently logged-in as ${this._username}.`
|
|
|
2715
2860
|
}
|
|
2716
2861
|
];
|
|
2717
2862
|
async applyDesignDocs() {
|
|
2863
|
+
log4(`Starting applyDesignDocs for user: ${this._username}`);
|
|
2864
|
+
log4(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
|
|
2718
2865
|
if (this._username === "admin") {
|
|
2866
|
+
log4("Skipping design docs for admin user");
|
|
2719
2867
|
return;
|
|
2720
2868
|
}
|
|
2869
|
+
log4(`Applying ${_BaseUser.designDocs.length} design docs`);
|
|
2721
2870
|
for (const doc of _BaseUser.designDocs) {
|
|
2871
|
+
log4(`Applying design doc: ${doc._id}`);
|
|
2722
2872
|
try {
|
|
2723
2873
|
try {
|
|
2724
2874
|
const existingDoc = await this.remoteDB.get(doc._id);
|
|
@@ -2817,8 +2967,13 @@ Currently logged-in as ${this._username}.`
|
|
|
2817
2967
|
async deduplicateReviews() {
|
|
2818
2968
|
try {
|
|
2819
2969
|
log4("Starting deduplication of scheduled reviews...");
|
|
2970
|
+
log4(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
|
|
2971
|
+
log4(`Write DB name: ${this.writeDB.name || "unknown"}`);
|
|
2820
2972
|
const reviewsMap = {};
|
|
2821
2973
|
const duplicateDocIds = [];
|
|
2974
|
+
log4(
|
|
2975
|
+
`Attempting to query remoteDB for reviewCards/reviewCards. Database: ${this.remoteDB.name || "unknown"}`
|
|
2976
|
+
);
|
|
2822
2977
|
const scheduledReviews = await this.remoteDB.query("reviewCards/reviewCards");
|
|
2823
2978
|
log4(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
|
|
2824
2979
|
scheduledReviews.rows.forEach((r) => {
|
|
@@ -2853,6 +3008,17 @@ Currently logged-in as ${this._username}.`
|
|
|
2853
3008
|
}
|
|
2854
3009
|
} catch (error) {
|
|
2855
3010
|
log4(`Error during review deduplication: ${error}`);
|
|
3011
|
+
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
3012
|
+
log4(
|
|
3013
|
+
`Database not found (404) during review deduplication. Database: ${this.remoteDB.name || "unknown"}`
|
|
3014
|
+
);
|
|
3015
|
+
log4(
|
|
3016
|
+
`This might indicate the user database doesn't exist or the reviewCards view isn't available`
|
|
3017
|
+
);
|
|
3018
|
+
}
|
|
3019
|
+
if (error && typeof error === "object") {
|
|
3020
|
+
log4(`Full error details: ${JSON.stringify(error)}`);
|
|
3021
|
+
}
|
|
2856
3022
|
}
|
|
2857
3023
|
}
|
|
2858
3024
|
/**
|
|
@@ -3086,6 +3252,16 @@ var init_PouchDataLayerProvider = __esm({
|
|
|
3086
3252
|
getAdminDB() {
|
|
3087
3253
|
return new AdminDB();
|
|
3088
3254
|
}
|
|
3255
|
+
async createUserReaderForUser(targetUsername) {
|
|
3256
|
+
const requestingUsername = await getLoggedInUsername();
|
|
3257
|
+
if (requestingUsername !== "admin") {
|
|
3258
|
+
throw new Error("Unauthorized: Only admin users can access other users' data");
|
|
3259
|
+
}
|
|
3260
|
+
logger.info(`Admin user '${requestingUsername}' requesting UserDBReader for '${targetUsername}'`);
|
|
3261
|
+
const syncStrategy = new CouchDBSyncStrategy();
|
|
3262
|
+
const targetUserDB = await BaseUser.instance(syncStrategy, targetUsername);
|
|
3263
|
+
return targetUserDB;
|
|
3264
|
+
}
|
|
3089
3265
|
isReadOnly() {
|
|
3090
3266
|
return false;
|
|
3091
3267
|
}
|
|
@@ -3538,7 +3714,10 @@ var init_courseDB2 = __esm({
|
|
|
3538
3714
|
};
|
|
3539
3715
|
}
|
|
3540
3716
|
async getCardsByELO(elo, limit) {
|
|
3541
|
-
return this.unpacker.queryByElo(elo, limit || 25)
|
|
3717
|
+
return (await this.unpacker.queryByElo(elo, limit || 25)).map((card) => {
|
|
3718
|
+
const [courseID, cardID, elo2] = card.split("-");
|
|
3719
|
+
return { courseID, cardID, elo: elo2 ? parseInt(elo2) : void 0 };
|
|
3720
|
+
});
|
|
3542
3721
|
}
|
|
3543
3722
|
async getCardEloData(cardIds) {
|
|
3544
3723
|
const results = await Promise.all(
|
|
@@ -3582,14 +3761,19 @@ var init_courseDB2 = __esm({
|
|
|
3582
3761
|
} else if (options.elo === "random") {
|
|
3583
3762
|
targetElo = 800 + Math.random() * 400;
|
|
3584
3763
|
}
|
|
3585
|
-
let cardIds = await this.unpacker.queryByElo(targetElo, options.limit * 2)
|
|
3764
|
+
let cardIds = (await this.unpacker.queryByElo(targetElo, options.limit * 2)).map((c) => {
|
|
3765
|
+
return {
|
|
3766
|
+
cardID: c,
|
|
3767
|
+
courseID: this.courseId
|
|
3768
|
+
};
|
|
3769
|
+
});
|
|
3586
3770
|
if (filter) {
|
|
3587
3771
|
cardIds = cardIds.filter(filter);
|
|
3588
3772
|
}
|
|
3589
|
-
return cardIds.slice(0, options.limit).map((
|
|
3773
|
+
return cardIds.slice(0, options.limit).map((card) => ({
|
|
3590
3774
|
status: "new",
|
|
3591
|
-
qualifiedID: `${this.courseId}-${cardId}`,
|
|
3592
|
-
cardID:
|
|
3775
|
+
// qualifiedID: `${this.courseId}-${cardId}`,
|
|
3776
|
+
cardID: card.cardID,
|
|
3593
3777
|
contentSourceType: "course",
|
|
3594
3778
|
contentSourceID: this.courseId,
|
|
3595
3779
|
courseID: this.courseId
|
|
@@ -3781,6 +3965,16 @@ var init_courseDB2 = __esm({
|
|
|
3781
3965
|
async getAttachmentBlob(docId, attachmentName) {
|
|
3782
3966
|
return this.unpacker.getAttachmentBlob(docId, attachmentName);
|
|
3783
3967
|
}
|
|
3968
|
+
// Admin search methods
|
|
3969
|
+
async searchCards(_query) {
|
|
3970
|
+
return [];
|
|
3971
|
+
}
|
|
3972
|
+
async find(_request) {
|
|
3973
|
+
return {
|
|
3974
|
+
docs: [],
|
|
3975
|
+
warning: "Find operations not supported in static mode"
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3784
3978
|
};
|
|
3785
3979
|
}
|
|
3786
3980
|
});
|
|
@@ -3936,6 +4130,12 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
3936
4130
|
getAdminDB() {
|
|
3937
4131
|
throw new Error("Admin functions not supported in static mode");
|
|
3938
4132
|
}
|
|
4133
|
+
async createUserReaderForUser(targetUsername) {
|
|
4134
|
+
logger.warn(`StaticDataLayerProvider: Multi-user access not supported in static mode`);
|
|
4135
|
+
logger.warn(`Request: trying to access data for ${targetUsername}`);
|
|
4136
|
+
logger.warn(`Returning current user's data instead`);
|
|
4137
|
+
return this.getUserDB();
|
|
4138
|
+
}
|
|
3939
4139
|
isReadOnly() {
|
|
3940
4140
|
return true;
|
|
3941
4141
|
}
|
|
@@ -5655,6 +5855,11 @@ init_dataDirectory();
|
|
|
5655
5855
|
init_tuiLogger();
|
|
5656
5856
|
|
|
5657
5857
|
// src/study/SessionController.ts
|
|
5858
|
+
import {
|
|
5859
|
+
displayableDataToViewData,
|
|
5860
|
+
isCourseElo,
|
|
5861
|
+
toCourseElo as toCourseElo3
|
|
5862
|
+
} from "@vue-skuilder/common";
|
|
5658
5863
|
function randomInt(min, max) {
|
|
5659
5864
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
5660
5865
|
}
|
|
@@ -5665,15 +5870,15 @@ var ItemQueue = class {
|
|
|
5665
5870
|
get dequeueCount() {
|
|
5666
5871
|
return this._dequeueCount;
|
|
5667
5872
|
}
|
|
5668
|
-
add(item) {
|
|
5669
|
-
if (this.seenCardIds.find((d) => d ===
|
|
5873
|
+
add(item, cardId) {
|
|
5874
|
+
if (this.seenCardIds.find((d) => d === cardId)) {
|
|
5670
5875
|
return;
|
|
5671
5876
|
}
|
|
5672
|
-
this.seenCardIds.push(
|
|
5877
|
+
this.seenCardIds.push(cardId);
|
|
5673
5878
|
this.q.push(item);
|
|
5674
5879
|
}
|
|
5675
|
-
addAll(items) {
|
|
5676
|
-
items.forEach((i) => this.add(i));
|
|
5880
|
+
addAll(items, cardIdExtractor) {
|
|
5881
|
+
items.forEach((i) => this.add(i, cardIdExtractor(i)));
|
|
5677
5882
|
}
|
|
5678
5883
|
get length() {
|
|
5679
5884
|
return this.q.length;
|
|
@@ -5691,12 +5896,14 @@ var ItemQueue = class {
|
|
|
5691
5896
|
}
|
|
5692
5897
|
get toString() {
|
|
5693
5898
|
return `${typeof this.q[0]}:
|
|
5694
|
-
` + this.q.map((i) => ` ${i.
|
|
5899
|
+
` + this.q.map((i) => ` ${i.courseID}+${i.cardID}: ${i.status}`).join("\n");
|
|
5695
5900
|
}
|
|
5696
5901
|
};
|
|
5697
5902
|
var SessionController = class extends Loggable {
|
|
5698
5903
|
_className = "SessionController";
|
|
5699
5904
|
sources;
|
|
5905
|
+
dataLayer;
|
|
5906
|
+
getViewComponent;
|
|
5700
5907
|
_sessionRecord = [];
|
|
5701
5908
|
set sessionRecord(r) {
|
|
5702
5909
|
this._sessionRecord = r;
|
|
@@ -5704,12 +5911,9 @@ var SessionController = class extends Loggable {
|
|
|
5704
5911
|
reviewQ = new ItemQueue();
|
|
5705
5912
|
newQ = new ItemQueue();
|
|
5706
5913
|
failedQ = new ItemQueue();
|
|
5914
|
+
hydratedQ = new ItemQueue();
|
|
5707
5915
|
_currentCard = null;
|
|
5708
|
-
|
|
5709
|
-
* Indicates whether the session has been initialized - eg, the
|
|
5710
|
-
* queues have been populated.
|
|
5711
|
-
*/
|
|
5712
|
-
_isInitialized = false;
|
|
5916
|
+
hydration_in_progress = false;
|
|
5713
5917
|
startTime;
|
|
5714
5918
|
endTime;
|
|
5715
5919
|
_secondsRemaining;
|
|
@@ -5727,12 +5931,14 @@ var SessionController = class extends Loggable {
|
|
|
5727
5931
|
/**
|
|
5728
5932
|
*
|
|
5729
5933
|
*/
|
|
5730
|
-
constructor(sources, time) {
|
|
5934
|
+
constructor(sources, time, dataLayer, getViewComponent) {
|
|
5731
5935
|
super();
|
|
5732
5936
|
this.sources = sources;
|
|
5733
5937
|
this.startTime = /* @__PURE__ */ new Date();
|
|
5734
5938
|
this._secondsRemaining = time;
|
|
5735
5939
|
this.endTime = new Date(this.startTime.valueOf() + 1e3 * this._secondsRemaining);
|
|
5940
|
+
this.dataLayer = dataLayer;
|
|
5941
|
+
this.getViewComponent = getViewComponent;
|
|
5736
5942
|
this.log(`Session constructed:
|
|
5737
5943
|
startTime: ${this.startTime}
|
|
5738
5944
|
endTime: ${this.endTime}`);
|
|
@@ -5783,7 +5989,7 @@ var SessionController = class extends Loggable {
|
|
|
5783
5989
|
} catch (e) {
|
|
5784
5990
|
this.error("Error preparing study session:", e);
|
|
5785
5991
|
}
|
|
5786
|
-
this.
|
|
5992
|
+
await this._fillHydratedQueue();
|
|
5787
5993
|
this._intervalHandle = setInterval(() => {
|
|
5788
5994
|
this.tick();
|
|
5789
5995
|
}, 1e3);
|
|
@@ -5821,12 +6027,8 @@ var SessionController = class extends Loggable {
|
|
|
5821
6027
|
}
|
|
5822
6028
|
}
|
|
5823
6029
|
let report = "Review session created with:\n";
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
this.reviewQ.add(card);
|
|
5827
|
-
report += ` ${card.qualifiedID}}
|
|
5828
|
-
`;
|
|
5829
|
-
}
|
|
6030
|
+
this.reviewQ.addAll(dueCards, (c) => c.cardID);
|
|
6031
|
+
report += dueCards.map((card) => `Card ${card.courseID}::${card.cardID} `).join("\n");
|
|
5830
6032
|
this.log(report);
|
|
5831
6033
|
}
|
|
5832
6034
|
async getNewCards(n = 10) {
|
|
@@ -5841,36 +6043,32 @@ var SessionController = class extends Loggable {
|
|
|
5841
6043
|
for (let i = 0; i < newContent.length; i++) {
|
|
5842
6044
|
if (newContent[i].length > 0) {
|
|
5843
6045
|
const item = newContent[i].splice(0, 1)[0];
|
|
5844
|
-
this.log(`Adding new card: ${item.
|
|
5845
|
-
this.newQ.add(item);
|
|
6046
|
+
this.log(`Adding new card: ${item.courseID}::${item.cardID}`);
|
|
6047
|
+
this.newQ.add(item, item.cardID);
|
|
5846
6048
|
n--;
|
|
5847
6049
|
}
|
|
5848
6050
|
}
|
|
5849
6051
|
}
|
|
5850
6052
|
}
|
|
5851
|
-
|
|
5852
|
-
const item = this.newQ.dequeue();
|
|
5853
|
-
if (this._isInitialized && this.newQ.length < 5) {
|
|
5854
|
-
void this.getNewCards();
|
|
5855
|
-
}
|
|
5856
|
-
return item;
|
|
5857
|
-
}
|
|
5858
|
-
nextCard(action = "dismiss-success") {
|
|
5859
|
-
this.dismissCurrentCard(action);
|
|
6053
|
+
_selectNextItemToHydrate(action = "dismiss-success") {
|
|
5860
6054
|
const choice = Math.random();
|
|
5861
6055
|
let newBound = 0.1;
|
|
5862
6056
|
let reviewBound = 0.75;
|
|
5863
6057
|
if (this.reviewQ.length === 0 && this.failedQ.length === 0 && this.newQ.length === 0) {
|
|
5864
|
-
|
|
5865
|
-
return this._currentCard;
|
|
6058
|
+
return null;
|
|
5866
6059
|
}
|
|
5867
6060
|
if (this._secondsRemaining < 2 && this.failedQ.length === 0) {
|
|
5868
|
-
|
|
5869
|
-
|
|
6061
|
+
return null;
|
|
6062
|
+
}
|
|
6063
|
+
if (this._secondsRemaining <= 0) {
|
|
6064
|
+
if (this.failedQ.length > 0) {
|
|
6065
|
+
return this.failedQ.peek(0);
|
|
6066
|
+
} else {
|
|
6067
|
+
return null;
|
|
6068
|
+
}
|
|
5870
6069
|
}
|
|
5871
6070
|
if (this.newQ.dequeueCount < this.sources.length && this.newQ.length) {
|
|
5872
|
-
|
|
5873
|
-
return this._currentCard;
|
|
6071
|
+
return this.newQ.peek(0);
|
|
5874
6072
|
}
|
|
5875
6073
|
const cleanupTime = this.estimateCleanupTime();
|
|
5876
6074
|
const reviewTime = this.estimateReviewTime();
|
|
@@ -5895,16 +6093,27 @@ var SessionController = class extends Loggable {
|
|
|
5895
6093
|
newBound = reviewBound;
|
|
5896
6094
|
}
|
|
5897
6095
|
if (choice < newBound && this.newQ.length) {
|
|
5898
|
-
|
|
6096
|
+
return this.newQ.peek(0);
|
|
5899
6097
|
} else if (choice < reviewBound && this.reviewQ.length) {
|
|
5900
|
-
|
|
6098
|
+
return this.reviewQ.peek(0);
|
|
5901
6099
|
} else if (this.failedQ.length) {
|
|
5902
|
-
|
|
6100
|
+
return this.failedQ.peek(0);
|
|
5903
6101
|
} else {
|
|
5904
6102
|
this.log(`No more cards available for the session!`);
|
|
5905
|
-
|
|
6103
|
+
return null;
|
|
5906
6104
|
}
|
|
5907
|
-
|
|
6105
|
+
}
|
|
6106
|
+
async nextCard(action = "dismiss-success") {
|
|
6107
|
+
this.dismissCurrentCard(action);
|
|
6108
|
+
let card = this.hydratedQ.dequeue();
|
|
6109
|
+
if (!card && this.hasAvailableCards()) {
|
|
6110
|
+
void this._fillHydratedQueue();
|
|
6111
|
+
card = await this.nextHydratedCard();
|
|
6112
|
+
}
|
|
6113
|
+
if (this.hydratedQ.length < 3) {
|
|
6114
|
+
void this._fillHydratedQueue();
|
|
6115
|
+
}
|
|
6116
|
+
return card;
|
|
5908
6117
|
}
|
|
5909
6118
|
dismissCurrentCard(action = "dismiss-success") {
|
|
5910
6119
|
if (this._currentCard) {
|
|
@@ -5915,7 +6124,6 @@ var SessionController = class extends Loggable {
|
|
|
5915
6124
|
failedItem = {
|
|
5916
6125
|
cardID: this._currentCard.cardID,
|
|
5917
6126
|
courseID: this._currentCard.courseID,
|
|
5918
|
-
qualifiedID: this._currentCard.qualifiedID,
|
|
5919
6127
|
contentSourceID: this._currentCard.contentSourceID,
|
|
5920
6128
|
contentSourceType: this._currentCard.contentSourceType,
|
|
5921
6129
|
status: "failed-review",
|
|
@@ -5925,18 +6133,80 @@ var SessionController = class extends Loggable {
|
|
|
5925
6133
|
failedItem = {
|
|
5926
6134
|
cardID: this._currentCard.cardID,
|
|
5927
6135
|
courseID: this._currentCard.courseID,
|
|
5928
|
-
qualifiedID: this._currentCard.qualifiedID,
|
|
5929
6136
|
contentSourceID: this._currentCard.contentSourceID,
|
|
5930
6137
|
contentSourceType: this._currentCard.contentSourceType,
|
|
5931
6138
|
status: "failed-new"
|
|
5932
6139
|
};
|
|
5933
6140
|
}
|
|
5934
|
-
this.failedQ.add(failedItem);
|
|
6141
|
+
this.failedQ.add(failedItem, failedItem.cardID);
|
|
5935
6142
|
} else if (action === "dismiss-error") {
|
|
5936
6143
|
} else if (action === "dismiss-failed") {
|
|
5937
6144
|
}
|
|
5938
6145
|
}
|
|
5939
6146
|
}
|
|
6147
|
+
hasAvailableCards() {
|
|
6148
|
+
return this.reviewQ.length > 0 || this.newQ.length > 0 || this.failedQ.length > 0;
|
|
6149
|
+
}
|
|
6150
|
+
async nextHydratedCard() {
|
|
6151
|
+
while (this.hydratedQ.length === 0 && this.hasAvailableCards()) {
|
|
6152
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
6153
|
+
}
|
|
6154
|
+
return this.hydratedQ.dequeue();
|
|
6155
|
+
}
|
|
6156
|
+
async _fillHydratedQueue() {
|
|
6157
|
+
if (this.hydration_in_progress) {
|
|
6158
|
+
return;
|
|
6159
|
+
}
|
|
6160
|
+
const BUFFER_SIZE = 5;
|
|
6161
|
+
this.hydration_in_progress = true;
|
|
6162
|
+
while (this.hydratedQ.length < BUFFER_SIZE) {
|
|
6163
|
+
const nextItem = this._selectNextItemToHydrate();
|
|
6164
|
+
if (!nextItem) {
|
|
6165
|
+
return;
|
|
6166
|
+
}
|
|
6167
|
+
try {
|
|
6168
|
+
const cardData = await this.dataLayer.getCourseDB(nextItem.courseID).getCourseDoc(nextItem.cardID);
|
|
6169
|
+
if (!isCourseElo(cardData.elo)) {
|
|
6170
|
+
cardData.elo = toCourseElo3(cardData.elo);
|
|
6171
|
+
}
|
|
6172
|
+
const view = this.getViewComponent(cardData.id_view);
|
|
6173
|
+
const dataDocs = await Promise.all(
|
|
6174
|
+
cardData.id_displayable_data.map(
|
|
6175
|
+
(id) => this.dataLayer.getCourseDB(nextItem.courseID).getCourseDoc(id, {
|
|
6176
|
+
attachments: true,
|
|
6177
|
+
binary: true
|
|
6178
|
+
})
|
|
6179
|
+
)
|
|
6180
|
+
);
|
|
6181
|
+
const data = dataDocs.map(displayableDataToViewData).reverse();
|
|
6182
|
+
this.hydratedQ.add(
|
|
6183
|
+
{
|
|
6184
|
+
item: nextItem,
|
|
6185
|
+
view,
|
|
6186
|
+
data
|
|
6187
|
+
},
|
|
6188
|
+
nextItem.cardID
|
|
6189
|
+
);
|
|
6190
|
+
if (this.reviewQ.peek(0) === nextItem) {
|
|
6191
|
+
this.reviewQ.dequeue();
|
|
6192
|
+
} else if (this.newQ.peek(0) === nextItem) {
|
|
6193
|
+
this.newQ.dequeue();
|
|
6194
|
+
} else {
|
|
6195
|
+
this.failedQ.dequeue();
|
|
6196
|
+
}
|
|
6197
|
+
} catch (e) {
|
|
6198
|
+
this.error(`Error hydrating card ${nextItem.cardID}:`, e);
|
|
6199
|
+
if (this.reviewQ.peek(0) === nextItem) {
|
|
6200
|
+
this.reviewQ.dequeue();
|
|
6201
|
+
} else if (this.newQ.peek(0) === nextItem) {
|
|
6202
|
+
this.newQ.dequeue();
|
|
6203
|
+
} else {
|
|
6204
|
+
this.failedQ.dequeue();
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
6207
|
+
}
|
|
6208
|
+
this.hydration_in_progress = false;
|
|
6209
|
+
}
|
|
5940
6210
|
};
|
|
5941
6211
|
|
|
5942
6212
|
// src/study/SpacedRepetition.ts
|
|
@@ -5962,7 +6232,7 @@ function newQuestionInterval(user, cardHistory) {
|
|
|
5962
6232
|
});
|
|
5963
6233
|
}
|
|
5964
6234
|
if (currentAttempt.isCorrect) {
|
|
5965
|
-
const skill = currentAttempt.performance;
|
|
6235
|
+
const skill = Math.min(1, Math.max(0, currentAttempt.performance));
|
|
5966
6236
|
logger.debug(`Demontrated skill: ${skill}`);
|
|
5967
6237
|
const interval = lastInterval * (0.75 + skill);
|
|
5968
6238
|
cardHistory.lapses = getLapses(cardHistory.records);
|