@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.js
CHANGED
|
@@ -187,9 +187,9 @@ var init_pouchdb_setup = __esm({
|
|
|
187
187
|
import_pouchdb.default.plugin(import_pouchdb_find.default);
|
|
188
188
|
import_pouchdb.default.plugin(import_pouchdb_authentication.default);
|
|
189
189
|
import_pouchdb.default.defaults({
|
|
190
|
-
ajax: {
|
|
191
|
-
|
|
192
|
-
}
|
|
190
|
+
// ajax: {
|
|
191
|
+
// timeout: 60000,
|
|
192
|
+
// },
|
|
193
193
|
});
|
|
194
194
|
pouchdb_setup_default = import_pouchdb.default;
|
|
195
195
|
}
|
|
@@ -473,37 +473,48 @@ var init_updateQueue = __esm({
|
|
|
473
473
|
} else {
|
|
474
474
|
if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
|
|
475
475
|
this.inprogressUpdates[id] = true;
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
476
|
+
const MAX_RETRIES = 5;
|
|
477
|
+
for (let i = 0; i < MAX_RETRIES; i++) {
|
|
478
|
+
try {
|
|
479
|
+
const doc = await this.readDB.get(id);
|
|
480
|
+
logger.debug(`Retrieved doc: ${id}`);
|
|
481
|
+
let updatedDoc = { ...doc };
|
|
482
|
+
const updatesToApply = [...this.pendingUpdates[id]];
|
|
483
|
+
for (const update of updatesToApply) {
|
|
484
|
+
if (typeof update === "function") {
|
|
485
|
+
updatedDoc = { ...updatedDoc, ...update(updatedDoc) };
|
|
486
|
+
} else {
|
|
487
|
+
updatedDoc = {
|
|
488
|
+
...updatedDoc,
|
|
489
|
+
...update
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
await this.writeDB.put(updatedDoc);
|
|
494
|
+
logger.debug(`Put doc: ${id}`);
|
|
495
|
+
this.pendingUpdates[id].splice(0, updatesToApply.length);
|
|
496
|
+
if (this.pendingUpdates[id].length === 0) {
|
|
497
|
+
this.inprogressUpdates[id] = false;
|
|
498
|
+
delete this.inprogressUpdates[id];
|
|
483
499
|
} else {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
500
|
+
return this.applyUpdates(id);
|
|
501
|
+
}
|
|
502
|
+
return updatedDoc;
|
|
503
|
+
} catch (e) {
|
|
504
|
+
if (e.name === "conflict" && i < MAX_RETRIES - 1) {
|
|
505
|
+
logger.warn(`Conflict on update for doc ${id}, retry #${i + 1}`);
|
|
506
|
+
await new Promise((res) => setTimeout(res, 50 * Math.random()));
|
|
507
|
+
} else {
|
|
508
|
+
delete this.inprogressUpdates[id];
|
|
509
|
+
if (this.pendingUpdates[id]) {
|
|
510
|
+
delete this.pendingUpdates[id];
|
|
511
|
+
}
|
|
512
|
+
logger.error(`Error on attemped update (retry ${i}): ${JSON.stringify(e)}`);
|
|
513
|
+
throw e;
|
|
488
514
|
}
|
|
489
515
|
}
|
|
490
|
-
await this.writeDB.put(doc);
|
|
491
|
-
logger.debug(`Put doc: ${id}`);
|
|
492
|
-
if (this.pendingUpdates[id].length === 0) {
|
|
493
|
-
this.inprogressUpdates[id] = false;
|
|
494
|
-
delete this.inprogressUpdates[id];
|
|
495
|
-
} else {
|
|
496
|
-
return this.applyUpdates(id);
|
|
497
|
-
}
|
|
498
|
-
return doc;
|
|
499
|
-
} catch (e) {
|
|
500
|
-
delete this.inprogressUpdates[id];
|
|
501
|
-
if (this.pendingUpdates[id]) {
|
|
502
|
-
delete this.pendingUpdates[id];
|
|
503
|
-
}
|
|
504
|
-
logger.error(`Error on attemped update: ${JSON.stringify(e)}`);
|
|
505
|
-
throw e;
|
|
506
516
|
}
|
|
517
|
+
throw new Error(`UpdateQueue failed for doc ${id} after ${MAX_RETRIES} retries.`);
|
|
507
518
|
} else {
|
|
508
519
|
throw new Error(`Empty Updates Queue Triggered`);
|
|
509
520
|
}
|
|
@@ -754,7 +765,7 @@ function getCourseDB(courseID) {
|
|
|
754
765
|
const dbName = `coursedb-${courseID}`;
|
|
755
766
|
return new pouchdb_setup_default(
|
|
756
767
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
757
|
-
|
|
768
|
+
createPouchDBConfig()
|
|
758
769
|
);
|
|
759
770
|
}
|
|
760
771
|
var import_common, import_common2, import_common3, import_uuid, AlreadyTaggedErr;
|
|
@@ -880,6 +891,7 @@ var init_courseLookupDB = __esm({
|
|
|
880
891
|
const doc = await _CourseLookup._db.get(courseID);
|
|
881
892
|
return await _CourseLookup._db.remove(doc);
|
|
882
893
|
}
|
|
894
|
+
// [ ] rename to allCourses()
|
|
883
895
|
static async allCourseWare() {
|
|
884
896
|
const resp = await _CourseLookup._db.allDocs({
|
|
885
897
|
include_docs: true
|
|
@@ -950,13 +962,16 @@ var init_elo = __esm({
|
|
|
950
962
|
}
|
|
951
963
|
async getNewCards(limit = 99) {
|
|
952
964
|
const activeCards = await this.user.getActiveCards();
|
|
953
|
-
return (await this.course.getCardsCenteredAtELO(
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
965
|
+
return (await this.course.getCardsCenteredAtELO(
|
|
966
|
+
{ limit, elo: "user" },
|
|
967
|
+
(c) => {
|
|
968
|
+
if (activeCards.some((ac) => c.cardID === ac.cardID)) {
|
|
969
|
+
return false;
|
|
970
|
+
} else {
|
|
971
|
+
return true;
|
|
972
|
+
}
|
|
958
973
|
}
|
|
959
|
-
|
|
974
|
+
)).map((c) => {
|
|
960
975
|
return {
|
|
961
976
|
...c,
|
|
962
977
|
status: "new"
|
|
@@ -1004,7 +1019,7 @@ var init_navigators = __esm({
|
|
|
1004
1019
|
static async create(user, course, strategyData) {
|
|
1005
1020
|
const implementingClass = strategyData.implementingClass;
|
|
1006
1021
|
let NavigatorImpl;
|
|
1007
|
-
const variations = ["", ".js", "
|
|
1022
|
+
const variations = [".ts", ".js", ""];
|
|
1008
1023
|
for (const ext of variations) {
|
|
1009
1024
|
try {
|
|
1010
1025
|
const module2 = await globImport(`./${implementingClass}${ext}`);
|
|
@@ -1308,7 +1323,13 @@ var init_courseDB = __esm({
|
|
|
1308
1323
|
} else {
|
|
1309
1324
|
return s;
|
|
1310
1325
|
}
|
|
1311
|
-
}).map((c) =>
|
|
1326
|
+
}).map((c) => {
|
|
1327
|
+
return {
|
|
1328
|
+
courseID: this.id,
|
|
1329
|
+
cardID: c.id,
|
|
1330
|
+
elo: c.key
|
|
1331
|
+
};
|
|
1332
|
+
});
|
|
1312
1333
|
const str = `below:
|
|
1313
1334
|
${below.rows.map((r) => ` ${r.id}-${r.key}
|
|
1314
1335
|
`)}
|
|
@@ -1363,7 +1384,13 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
1363
1384
|
}
|
|
1364
1385
|
}
|
|
1365
1386
|
async addTagToCard(cardId, tagId, updateELO) {
|
|
1366
|
-
return await addTagToCard(
|
|
1387
|
+
return await addTagToCard(
|
|
1388
|
+
this.id,
|
|
1389
|
+
cardId,
|
|
1390
|
+
tagId,
|
|
1391
|
+
(await this._getCurrentUser()).getUsername(),
|
|
1392
|
+
updateELO
|
|
1393
|
+
);
|
|
1367
1394
|
}
|
|
1368
1395
|
async removeTagFromCard(cardId, tagId) {
|
|
1369
1396
|
return await removeTagFromCard(this.id, cardId, tagId);
|
|
@@ -1556,17 +1583,93 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
1556
1583
|
selectedCards.push(card);
|
|
1557
1584
|
}
|
|
1558
1585
|
return selectedCards.map((c) => {
|
|
1559
|
-
const split = c.split("-");
|
|
1560
1586
|
return {
|
|
1561
1587
|
courseID: this.id,
|
|
1562
|
-
cardID:
|
|
1563
|
-
qualifiedID: `${split[0]}-${split[1]}`,
|
|
1588
|
+
cardID: c.cardID,
|
|
1564
1589
|
contentSourceType: "course",
|
|
1565
1590
|
contentSourceID: this.id,
|
|
1591
|
+
elo: c.elo,
|
|
1566
1592
|
status: "new"
|
|
1567
1593
|
};
|
|
1568
1594
|
});
|
|
1569
1595
|
}
|
|
1596
|
+
// Admin search methods
|
|
1597
|
+
async searchCards(query) {
|
|
1598
|
+
logger.log(`[CourseDB ${this.id}] Searching for: "${query}"`);
|
|
1599
|
+
let displayableData;
|
|
1600
|
+
try {
|
|
1601
|
+
displayableData = await this.db.find({
|
|
1602
|
+
selector: {
|
|
1603
|
+
docType: "DISPLAYABLE_DATA",
|
|
1604
|
+
"data.0.data": { $regex: `.*${query}.*` }
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
logger.log(`[CourseDB ${this.id}] Regex search on data[0].data successful`);
|
|
1608
|
+
} catch (regexError) {
|
|
1609
|
+
logger.log(
|
|
1610
|
+
`[CourseDB ${this.id}] Regex search failed, falling back to manual search:`,
|
|
1611
|
+
regexError
|
|
1612
|
+
);
|
|
1613
|
+
const allDisplayable = await this.db.find({
|
|
1614
|
+
selector: {
|
|
1615
|
+
docType: "DISPLAYABLE_DATA"
|
|
1616
|
+
}
|
|
1617
|
+
});
|
|
1618
|
+
logger.log(
|
|
1619
|
+
`[CourseDB ${this.id}] Retrieved ${allDisplayable.docs.length} documents for manual filtering`
|
|
1620
|
+
);
|
|
1621
|
+
displayableData = {
|
|
1622
|
+
docs: allDisplayable.docs.filter((doc) => {
|
|
1623
|
+
const docString = JSON.stringify(doc).toLowerCase();
|
|
1624
|
+
const match = docString.includes(query.toLowerCase());
|
|
1625
|
+
if (match) {
|
|
1626
|
+
logger.log(`[CourseDB ${this.id}] Manual match found in document: ${doc._id}`);
|
|
1627
|
+
}
|
|
1628
|
+
return match;
|
|
1629
|
+
})
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
logger.log(
|
|
1633
|
+
`[CourseDB ${this.id}] Found ${displayableData.docs.length} displayable data documents`
|
|
1634
|
+
);
|
|
1635
|
+
if (displayableData.docs.length === 0) {
|
|
1636
|
+
const allDisplayableData = await this.db.find({
|
|
1637
|
+
selector: {
|
|
1638
|
+
docType: "DISPLAYABLE_DATA"
|
|
1639
|
+
},
|
|
1640
|
+
limit: 5
|
|
1641
|
+
// Just sample a few
|
|
1642
|
+
});
|
|
1643
|
+
logger.log(
|
|
1644
|
+
`[CourseDB ${this.id}] Sample displayable data:`,
|
|
1645
|
+
allDisplayableData.docs.map((d) => ({
|
|
1646
|
+
id: d._id,
|
|
1647
|
+
docType: d.docType,
|
|
1648
|
+
dataStructure: d.data ? Object.keys(d.data) : "no data field",
|
|
1649
|
+
dataContent: d.data,
|
|
1650
|
+
fullDoc: d
|
|
1651
|
+
}))
|
|
1652
|
+
);
|
|
1653
|
+
}
|
|
1654
|
+
const allResults = [];
|
|
1655
|
+
for (const dd of displayableData.docs) {
|
|
1656
|
+
const cards = await this.db.find({
|
|
1657
|
+
selector: {
|
|
1658
|
+
docType: "CARD",
|
|
1659
|
+
id_displayable_data: { $in: [dd._id] }
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
logger.log(
|
|
1663
|
+
`[CourseDB ${this.id}] Displayable data ${dd._id} linked to ${cards.docs.length} cards`
|
|
1664
|
+
);
|
|
1665
|
+
allResults.push(...cards.docs);
|
|
1666
|
+
}
|
|
1667
|
+
logger.log(`[CourseDB ${this.id}] Total cards found: ${allResults.length}`);
|
|
1668
|
+
return allResults;
|
|
1669
|
+
}
|
|
1670
|
+
async find(request) {
|
|
1671
|
+
return this.db.find(request);
|
|
1672
|
+
}
|
|
1570
1673
|
};
|
|
1571
1674
|
}
|
|
1572
1675
|
});
|
|
@@ -1632,7 +1735,7 @@ var init_classroomDB2 = __esm({
|
|
|
1632
1735
|
const dbName = `classdb-student-${this._id}`;
|
|
1633
1736
|
this._db = new pouchdb_setup_default(
|
|
1634
1737
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
1635
|
-
|
|
1738
|
+
createPouchDBConfig()
|
|
1636
1739
|
);
|
|
1637
1740
|
try {
|
|
1638
1741
|
const cfg = await this._db.get(CLASSROOM_CONFIG);
|
|
@@ -1701,9 +1804,11 @@ var init_classroomDB2 = __esm({
|
|
|
1701
1804
|
ret.push(await getCourseDB2(content.courseID).get(content.cardID));
|
|
1702
1805
|
}
|
|
1703
1806
|
}
|
|
1704
|
-
logger.info(
|
|
1807
|
+
logger.info(
|
|
1808
|
+
`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => `${c.courseID}-${c.cardID}`)}`
|
|
1809
|
+
);
|
|
1705
1810
|
return ret.filter((c) => {
|
|
1706
|
-
if (activeCards.some((ac) => c.
|
|
1811
|
+
if (activeCards.some((ac) => c.cardID === ac.cardID)) {
|
|
1707
1812
|
return false;
|
|
1708
1813
|
} else {
|
|
1709
1814
|
return true;
|
|
@@ -1722,11 +1827,11 @@ var init_classroomDB2 = __esm({
|
|
|
1722
1827
|
const stuDbName = `classdb-student-${this._id}`;
|
|
1723
1828
|
this._db = new pouchdb_setup_default(
|
|
1724
1829
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
1725
|
-
|
|
1830
|
+
createPouchDBConfig()
|
|
1726
1831
|
);
|
|
1727
1832
|
this._stuDb = new pouchdb_setup_default(
|
|
1728
1833
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + stuDbName,
|
|
1729
|
-
|
|
1834
|
+
createPouchDBConfig()
|
|
1730
1835
|
);
|
|
1731
1836
|
try {
|
|
1732
1837
|
return this._db.get(CLASSROOM_CONFIG).then((cfg) => {
|
|
@@ -1811,7 +1916,7 @@ var init_adminDB2 = __esm({
|
|
|
1811
1916
|
constructor() {
|
|
1812
1917
|
this.usersDB = new pouchdb_setup_default(
|
|
1813
1918
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "_users",
|
|
1814
|
-
|
|
1919
|
+
createPouchDBConfig()
|
|
1815
1920
|
);
|
|
1816
1921
|
}
|
|
1817
1922
|
async getUsers() {
|
|
@@ -1872,9 +1977,10 @@ var init_adminDB2 = __esm({
|
|
|
1872
1977
|
async function getCurrentSession() {
|
|
1873
1978
|
try {
|
|
1874
1979
|
if (ENV.COUCHDB_SERVER_URL === NOT_SET || ENV.COUCHDB_SERVER_PROTOCOL === NOT_SET) {
|
|
1875
|
-
throw new Error(
|
|
1980
|
+
throw new Error(`CouchDB server configuration not properly initialized. Protocol: "${ENV.COUCHDB_SERVER_PROTOCOL}", URL: "${ENV.COUCHDB_SERVER_URL}"`);
|
|
1876
1981
|
}
|
|
1877
|
-
const
|
|
1982
|
+
const baseUrl = ENV.COUCHDB_SERVER_URL.endsWith("/") ? ENV.COUCHDB_SERVER_URL.slice(0, -1) : ENV.COUCHDB_SERVER_URL;
|
|
1983
|
+
const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${baseUrl}/_session`;
|
|
1878
1984
|
logger.debug(`Attempting session check at: ${url}`);
|
|
1879
1985
|
const response = await (0, import_cross_fetch.default)(url, {
|
|
1880
1986
|
method: "GET",
|
|
@@ -1886,8 +1992,10 @@ async function getCurrentSession() {
|
|
|
1886
1992
|
const resp = await response.json();
|
|
1887
1993
|
return resp;
|
|
1888
1994
|
} catch (error) {
|
|
1889
|
-
|
|
1890
|
-
|
|
1995
|
+
const baseUrl = ENV.COUCHDB_SERVER_URL.endsWith("/") ? ENV.COUCHDB_SERVER_URL.slice(0, -1) : ENV.COUCHDB_SERVER_URL;
|
|
1996
|
+
const url = `${ENV.COUCHDB_SERVER_PROTOCOL}://${baseUrl}/_session`;
|
|
1997
|
+
logger.error(`Session check error attempting to connect to: ${url} - ${error}`);
|
|
1998
|
+
throw new Error(`Session check failed connecting to ${url}: ${error}`);
|
|
1891
1999
|
}
|
|
1892
2000
|
}
|
|
1893
2001
|
async function getLoggedInUsername() {
|
|
@@ -2082,7 +2190,7 @@ var init_CouchDBSyncStrategy = __esm({
|
|
|
2082
2190
|
log3(`Fetching user database: ${dbName} (${username})`);
|
|
2083
2191
|
const ret = new pouchdb_setup_default(
|
|
2084
2192
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
2085
|
-
|
|
2193
|
+
createPouchDBConfig()
|
|
2086
2194
|
);
|
|
2087
2195
|
if (guestAccount) {
|
|
2088
2196
|
updateGuestAccountExpirationDate(ret);
|
|
@@ -2094,10 +2202,29 @@ var init_CouchDBSyncStrategy = __esm({
|
|
|
2094
2202
|
});
|
|
2095
2203
|
|
|
2096
2204
|
// src/impl/couch/index.ts
|
|
2205
|
+
function createPouchDBConfig() {
|
|
2206
|
+
const hasExplicitCredentials = ENV.COUCHDB_USERNAME && ENV.COUCHDB_PASSWORD;
|
|
2207
|
+
const isNodeEnvironment2 = typeof window === "undefined";
|
|
2208
|
+
if (hasExplicitCredentials && isNodeEnvironment2) {
|
|
2209
|
+
return {
|
|
2210
|
+
fetch(url, opts = {}) {
|
|
2211
|
+
const basicAuth = btoa(`${ENV.COUCHDB_USERNAME}:${ENV.COUCHDB_PASSWORD}`);
|
|
2212
|
+
const headers = new Headers(opts.headers || {});
|
|
2213
|
+
headers.set("Authorization", `Basic ${basicAuth}`);
|
|
2214
|
+
const newOpts = {
|
|
2215
|
+
...opts,
|
|
2216
|
+
headers
|
|
2217
|
+
};
|
|
2218
|
+
return pouchdb_setup_default.fetch(url, newOpts);
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
}
|
|
2222
|
+
return pouchDBincludeCredentialsConfig;
|
|
2223
|
+
}
|
|
2097
2224
|
function getCourseDB2(courseID) {
|
|
2098
2225
|
return new pouchdb_setup_default(
|
|
2099
2226
|
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
|
|
2100
|
-
|
|
2227
|
+
createPouchDBConfig()
|
|
2101
2228
|
);
|
|
2102
2229
|
}
|
|
2103
2230
|
function getCourseDocs(courseID, docIDs, options = {}) {
|
|
@@ -2389,6 +2516,9 @@ Currently logged-in as ${this._username}.`
|
|
|
2389
2516
|
await this.init();
|
|
2390
2517
|
return ret;
|
|
2391
2518
|
}
|
|
2519
|
+
async get(id) {
|
|
2520
|
+
return this.localDB.get(id);
|
|
2521
|
+
}
|
|
2392
2522
|
update(id, update) {
|
|
2393
2523
|
return this.updateQueue.update(id, update);
|
|
2394
2524
|
}
|
|
@@ -2435,7 +2565,12 @@ Currently logged-in as ${this._username}.`
|
|
|
2435
2565
|
endkey: keys.endkey,
|
|
2436
2566
|
include_docs: true
|
|
2437
2567
|
});
|
|
2438
|
-
return reviews.rows.map((r) =>
|
|
2568
|
+
return reviews.rows.map((r) => {
|
|
2569
|
+
return {
|
|
2570
|
+
courseID: r.doc.courseId,
|
|
2571
|
+
cardID: r.doc.cardId
|
|
2572
|
+
};
|
|
2573
|
+
});
|
|
2439
2574
|
}
|
|
2440
2575
|
async getActivityRecords() {
|
|
2441
2576
|
try {
|
|
@@ -2715,8 +2850,18 @@ Currently logged-in as ${this._username}.`
|
|
|
2715
2850
|
}
|
|
2716
2851
|
this.setDBandQ();
|
|
2717
2852
|
this.syncStrategy.startSync(this.localDB, this.remoteDB);
|
|
2718
|
-
|
|
2719
|
-
|
|
2853
|
+
this.applyDesignDocs().catch((error) => {
|
|
2854
|
+
log4(`Error in applyDesignDocs background task: ${error}`);
|
|
2855
|
+
if (error && typeof error === "object") {
|
|
2856
|
+
log4(`Full error details in applyDesignDocs: ${JSON.stringify(error)}`);
|
|
2857
|
+
}
|
|
2858
|
+
});
|
|
2859
|
+
this.deduplicateReviews().catch((error) => {
|
|
2860
|
+
log4(`Error in deduplicateReviews background task: ${error}`);
|
|
2861
|
+
if (error && typeof error === "object") {
|
|
2862
|
+
log4(`Full error details in background task: ${JSON.stringify(error)}`);
|
|
2863
|
+
}
|
|
2864
|
+
});
|
|
2720
2865
|
_BaseUser._initialized = true;
|
|
2721
2866
|
}
|
|
2722
2867
|
static designDocs = [
|
|
@@ -2734,10 +2879,15 @@ Currently logged-in as ${this._username}.`
|
|
|
2734
2879
|
}
|
|
2735
2880
|
];
|
|
2736
2881
|
async applyDesignDocs() {
|
|
2882
|
+
log4(`Starting applyDesignDocs for user: ${this._username}`);
|
|
2883
|
+
log4(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
|
|
2737
2884
|
if (this._username === "admin") {
|
|
2885
|
+
log4("Skipping design docs for admin user");
|
|
2738
2886
|
return;
|
|
2739
2887
|
}
|
|
2888
|
+
log4(`Applying ${_BaseUser.designDocs.length} design docs`);
|
|
2740
2889
|
for (const doc of _BaseUser.designDocs) {
|
|
2890
|
+
log4(`Applying design doc: ${doc._id}`);
|
|
2741
2891
|
try {
|
|
2742
2892
|
try {
|
|
2743
2893
|
const existingDoc = await this.remoteDB.get(doc._id);
|
|
@@ -2836,8 +2986,13 @@ Currently logged-in as ${this._username}.`
|
|
|
2836
2986
|
async deduplicateReviews() {
|
|
2837
2987
|
try {
|
|
2838
2988
|
log4("Starting deduplication of scheduled reviews...");
|
|
2989
|
+
log4(`Remote DB name: ${this.remoteDB.name || "unknown"}`);
|
|
2990
|
+
log4(`Write DB name: ${this.writeDB.name || "unknown"}`);
|
|
2839
2991
|
const reviewsMap = {};
|
|
2840
2992
|
const duplicateDocIds = [];
|
|
2993
|
+
log4(
|
|
2994
|
+
`Attempting to query remoteDB for reviewCards/reviewCards. Database: ${this.remoteDB.name || "unknown"}`
|
|
2995
|
+
);
|
|
2841
2996
|
const scheduledReviews = await this.remoteDB.query("reviewCards/reviewCards");
|
|
2842
2997
|
log4(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
|
|
2843
2998
|
scheduledReviews.rows.forEach((r) => {
|
|
@@ -2872,6 +3027,17 @@ Currently logged-in as ${this._username}.`
|
|
|
2872
3027
|
}
|
|
2873
3028
|
} catch (error) {
|
|
2874
3029
|
log4(`Error during review deduplication: ${error}`);
|
|
3030
|
+
if (error && typeof error === "object" && "status" in error && error.status === 404) {
|
|
3031
|
+
log4(
|
|
3032
|
+
`Database not found (404) during review deduplication. Database: ${this.remoteDB.name || "unknown"}`
|
|
3033
|
+
);
|
|
3034
|
+
log4(
|
|
3035
|
+
`This might indicate the user database doesn't exist or the reviewCards view isn't available`
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3038
|
+
if (error && typeof error === "object") {
|
|
3039
|
+
log4(`Full error details: ${JSON.stringify(error)}`);
|
|
3040
|
+
}
|
|
2875
3041
|
}
|
|
2876
3042
|
}
|
|
2877
3043
|
/**
|
|
@@ -3105,6 +3271,16 @@ var init_PouchDataLayerProvider = __esm({
|
|
|
3105
3271
|
getAdminDB() {
|
|
3106
3272
|
return new AdminDB();
|
|
3107
3273
|
}
|
|
3274
|
+
async createUserReaderForUser(targetUsername) {
|
|
3275
|
+
const requestingUsername = await getLoggedInUsername();
|
|
3276
|
+
if (requestingUsername !== "admin") {
|
|
3277
|
+
throw new Error("Unauthorized: Only admin users can access other users' data");
|
|
3278
|
+
}
|
|
3279
|
+
logger.info(`Admin user '${requestingUsername}' requesting UserDBReader for '${targetUsername}'`);
|
|
3280
|
+
const syncStrategy = new CouchDBSyncStrategy();
|
|
3281
|
+
const targetUserDB = await BaseUser.instance(syncStrategy, targetUsername);
|
|
3282
|
+
return targetUserDB;
|
|
3283
|
+
}
|
|
3108
3284
|
isReadOnly() {
|
|
3109
3285
|
return false;
|
|
3110
3286
|
}
|
|
@@ -3557,7 +3733,10 @@ var init_courseDB2 = __esm({
|
|
|
3557
3733
|
};
|
|
3558
3734
|
}
|
|
3559
3735
|
async getCardsByELO(elo, limit) {
|
|
3560
|
-
return this.unpacker.queryByElo(elo, limit || 25)
|
|
3736
|
+
return (await this.unpacker.queryByElo(elo, limit || 25)).map((card) => {
|
|
3737
|
+
const [courseID, cardID, elo2] = card.split("-");
|
|
3738
|
+
return { courseID, cardID, elo: elo2 ? parseInt(elo2) : void 0 };
|
|
3739
|
+
});
|
|
3561
3740
|
}
|
|
3562
3741
|
async getCardEloData(cardIds) {
|
|
3563
3742
|
const results = await Promise.all(
|
|
@@ -3601,14 +3780,19 @@ var init_courseDB2 = __esm({
|
|
|
3601
3780
|
} else if (options.elo === "random") {
|
|
3602
3781
|
targetElo = 800 + Math.random() * 400;
|
|
3603
3782
|
}
|
|
3604
|
-
let cardIds = await this.unpacker.queryByElo(targetElo, options.limit * 2)
|
|
3783
|
+
let cardIds = (await this.unpacker.queryByElo(targetElo, options.limit * 2)).map((c) => {
|
|
3784
|
+
return {
|
|
3785
|
+
cardID: c,
|
|
3786
|
+
courseID: this.courseId
|
|
3787
|
+
};
|
|
3788
|
+
});
|
|
3605
3789
|
if (filter) {
|
|
3606
3790
|
cardIds = cardIds.filter(filter);
|
|
3607
3791
|
}
|
|
3608
|
-
return cardIds.slice(0, options.limit).map((
|
|
3792
|
+
return cardIds.slice(0, options.limit).map((card) => ({
|
|
3609
3793
|
status: "new",
|
|
3610
|
-
qualifiedID: `${this.courseId}-${cardId}`,
|
|
3611
|
-
cardID:
|
|
3794
|
+
// qualifiedID: `${this.courseId}-${cardId}`,
|
|
3795
|
+
cardID: card.cardID,
|
|
3612
3796
|
contentSourceType: "course",
|
|
3613
3797
|
contentSourceID: this.courseId,
|
|
3614
3798
|
courseID: this.courseId
|
|
@@ -3800,6 +3984,16 @@ var init_courseDB2 = __esm({
|
|
|
3800
3984
|
async getAttachmentBlob(docId, attachmentName) {
|
|
3801
3985
|
return this.unpacker.getAttachmentBlob(docId, attachmentName);
|
|
3802
3986
|
}
|
|
3987
|
+
// Admin search methods
|
|
3988
|
+
async searchCards(_query) {
|
|
3989
|
+
return [];
|
|
3990
|
+
}
|
|
3991
|
+
async find(_request) {
|
|
3992
|
+
return {
|
|
3993
|
+
docs: [],
|
|
3994
|
+
warning: "Find operations not supported in static mode"
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3803
3997
|
};
|
|
3804
3998
|
}
|
|
3805
3999
|
});
|
|
@@ -3955,6 +4149,12 @@ var init_StaticDataLayerProvider = __esm({
|
|
|
3955
4149
|
getAdminDB() {
|
|
3956
4150
|
throw new Error("Admin functions not supported in static mode");
|
|
3957
4151
|
}
|
|
4152
|
+
async createUserReaderForUser(targetUsername) {
|
|
4153
|
+
logger.warn(`StaticDataLayerProvider: Multi-user access not supported in static mode`);
|
|
4154
|
+
logger.warn(`Request: trying to access data for ${targetUsername}`);
|
|
4155
|
+
logger.warn(`Returning current user's data instead`);
|
|
4156
|
+
return this.getUserDB();
|
|
4157
|
+
}
|
|
3958
4158
|
isReadOnly() {
|
|
3959
4159
|
return true;
|
|
3960
4160
|
}
|
|
@@ -5717,6 +5917,7 @@ init_dataDirectory();
|
|
|
5717
5917
|
init_tuiLogger();
|
|
5718
5918
|
|
|
5719
5919
|
// src/study/SessionController.ts
|
|
5920
|
+
var import_common15 = require("@vue-skuilder/common");
|
|
5720
5921
|
function randomInt(min, max) {
|
|
5721
5922
|
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
5722
5923
|
}
|
|
@@ -5727,15 +5928,15 @@ var ItemQueue = class {
|
|
|
5727
5928
|
get dequeueCount() {
|
|
5728
5929
|
return this._dequeueCount;
|
|
5729
5930
|
}
|
|
5730
|
-
add(item) {
|
|
5731
|
-
if (this.seenCardIds.find((d) => d ===
|
|
5931
|
+
add(item, cardId) {
|
|
5932
|
+
if (this.seenCardIds.find((d) => d === cardId)) {
|
|
5732
5933
|
return;
|
|
5733
5934
|
}
|
|
5734
|
-
this.seenCardIds.push(
|
|
5935
|
+
this.seenCardIds.push(cardId);
|
|
5735
5936
|
this.q.push(item);
|
|
5736
5937
|
}
|
|
5737
|
-
addAll(items) {
|
|
5738
|
-
items.forEach((i) => this.add(i));
|
|
5938
|
+
addAll(items, cardIdExtractor) {
|
|
5939
|
+
items.forEach((i) => this.add(i, cardIdExtractor(i)));
|
|
5739
5940
|
}
|
|
5740
5941
|
get length() {
|
|
5741
5942
|
return this.q.length;
|
|
@@ -5753,12 +5954,14 @@ var ItemQueue = class {
|
|
|
5753
5954
|
}
|
|
5754
5955
|
get toString() {
|
|
5755
5956
|
return `${typeof this.q[0]}:
|
|
5756
|
-
` + this.q.map((i) => ` ${i.
|
|
5957
|
+
` + this.q.map((i) => ` ${i.courseID}+${i.cardID}: ${i.status}`).join("\n");
|
|
5757
5958
|
}
|
|
5758
5959
|
};
|
|
5759
5960
|
var SessionController = class extends Loggable {
|
|
5760
5961
|
_className = "SessionController";
|
|
5761
5962
|
sources;
|
|
5963
|
+
dataLayer;
|
|
5964
|
+
getViewComponent;
|
|
5762
5965
|
_sessionRecord = [];
|
|
5763
5966
|
set sessionRecord(r) {
|
|
5764
5967
|
this._sessionRecord = r;
|
|
@@ -5766,12 +5969,9 @@ var SessionController = class extends Loggable {
|
|
|
5766
5969
|
reviewQ = new ItemQueue();
|
|
5767
5970
|
newQ = new ItemQueue();
|
|
5768
5971
|
failedQ = new ItemQueue();
|
|
5972
|
+
hydratedQ = new ItemQueue();
|
|
5769
5973
|
_currentCard = null;
|
|
5770
|
-
|
|
5771
|
-
* Indicates whether the session has been initialized - eg, the
|
|
5772
|
-
* queues have been populated.
|
|
5773
|
-
*/
|
|
5774
|
-
_isInitialized = false;
|
|
5974
|
+
hydration_in_progress = false;
|
|
5775
5975
|
startTime;
|
|
5776
5976
|
endTime;
|
|
5777
5977
|
_secondsRemaining;
|
|
@@ -5789,12 +5989,14 @@ var SessionController = class extends Loggable {
|
|
|
5789
5989
|
/**
|
|
5790
5990
|
*
|
|
5791
5991
|
*/
|
|
5792
|
-
constructor(sources, time) {
|
|
5992
|
+
constructor(sources, time, dataLayer, getViewComponent) {
|
|
5793
5993
|
super();
|
|
5794
5994
|
this.sources = sources;
|
|
5795
5995
|
this.startTime = /* @__PURE__ */ new Date();
|
|
5796
5996
|
this._secondsRemaining = time;
|
|
5797
5997
|
this.endTime = new Date(this.startTime.valueOf() + 1e3 * this._secondsRemaining);
|
|
5998
|
+
this.dataLayer = dataLayer;
|
|
5999
|
+
this.getViewComponent = getViewComponent;
|
|
5798
6000
|
this.log(`Session constructed:
|
|
5799
6001
|
startTime: ${this.startTime}
|
|
5800
6002
|
endTime: ${this.endTime}`);
|
|
@@ -5845,7 +6047,7 @@ var SessionController = class extends Loggable {
|
|
|
5845
6047
|
} catch (e) {
|
|
5846
6048
|
this.error("Error preparing study session:", e);
|
|
5847
6049
|
}
|
|
5848
|
-
this.
|
|
6050
|
+
await this._fillHydratedQueue();
|
|
5849
6051
|
this._intervalHandle = setInterval(() => {
|
|
5850
6052
|
this.tick();
|
|
5851
6053
|
}, 1e3);
|
|
@@ -5883,12 +6085,8 @@ var SessionController = class extends Loggable {
|
|
|
5883
6085
|
}
|
|
5884
6086
|
}
|
|
5885
6087
|
let report = "Review session created with:\n";
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
this.reviewQ.add(card);
|
|
5889
|
-
report += ` ${card.qualifiedID}}
|
|
5890
|
-
`;
|
|
5891
|
-
}
|
|
6088
|
+
this.reviewQ.addAll(dueCards, (c) => c.cardID);
|
|
6089
|
+
report += dueCards.map((card) => `Card ${card.courseID}::${card.cardID} `).join("\n");
|
|
5892
6090
|
this.log(report);
|
|
5893
6091
|
}
|
|
5894
6092
|
async getNewCards(n = 10) {
|
|
@@ -5903,36 +6101,32 @@ var SessionController = class extends Loggable {
|
|
|
5903
6101
|
for (let i = 0; i < newContent.length; i++) {
|
|
5904
6102
|
if (newContent[i].length > 0) {
|
|
5905
6103
|
const item = newContent[i].splice(0, 1)[0];
|
|
5906
|
-
this.log(`Adding new card: ${item.
|
|
5907
|
-
this.newQ.add(item);
|
|
6104
|
+
this.log(`Adding new card: ${item.courseID}::${item.cardID}`);
|
|
6105
|
+
this.newQ.add(item, item.cardID);
|
|
5908
6106
|
n--;
|
|
5909
6107
|
}
|
|
5910
6108
|
}
|
|
5911
6109
|
}
|
|
5912
6110
|
}
|
|
5913
|
-
|
|
5914
|
-
const item = this.newQ.dequeue();
|
|
5915
|
-
if (this._isInitialized && this.newQ.length < 5) {
|
|
5916
|
-
void this.getNewCards();
|
|
5917
|
-
}
|
|
5918
|
-
return item;
|
|
5919
|
-
}
|
|
5920
|
-
nextCard(action = "dismiss-success") {
|
|
5921
|
-
this.dismissCurrentCard(action);
|
|
6111
|
+
_selectNextItemToHydrate(action = "dismiss-success") {
|
|
5922
6112
|
const choice = Math.random();
|
|
5923
6113
|
let newBound = 0.1;
|
|
5924
6114
|
let reviewBound = 0.75;
|
|
5925
6115
|
if (this.reviewQ.length === 0 && this.failedQ.length === 0 && this.newQ.length === 0) {
|
|
5926
|
-
|
|
5927
|
-
return this._currentCard;
|
|
6116
|
+
return null;
|
|
5928
6117
|
}
|
|
5929
6118
|
if (this._secondsRemaining < 2 && this.failedQ.length === 0) {
|
|
5930
|
-
|
|
5931
|
-
|
|
6119
|
+
return null;
|
|
6120
|
+
}
|
|
6121
|
+
if (this._secondsRemaining <= 0) {
|
|
6122
|
+
if (this.failedQ.length > 0) {
|
|
6123
|
+
return this.failedQ.peek(0);
|
|
6124
|
+
} else {
|
|
6125
|
+
return null;
|
|
6126
|
+
}
|
|
5932
6127
|
}
|
|
5933
6128
|
if (this.newQ.dequeueCount < this.sources.length && this.newQ.length) {
|
|
5934
|
-
|
|
5935
|
-
return this._currentCard;
|
|
6129
|
+
return this.newQ.peek(0);
|
|
5936
6130
|
}
|
|
5937
6131
|
const cleanupTime = this.estimateCleanupTime();
|
|
5938
6132
|
const reviewTime = this.estimateReviewTime();
|
|
@@ -5957,16 +6151,27 @@ var SessionController = class extends Loggable {
|
|
|
5957
6151
|
newBound = reviewBound;
|
|
5958
6152
|
}
|
|
5959
6153
|
if (choice < newBound && this.newQ.length) {
|
|
5960
|
-
|
|
6154
|
+
return this.newQ.peek(0);
|
|
5961
6155
|
} else if (choice < reviewBound && this.reviewQ.length) {
|
|
5962
|
-
|
|
6156
|
+
return this.reviewQ.peek(0);
|
|
5963
6157
|
} else if (this.failedQ.length) {
|
|
5964
|
-
|
|
6158
|
+
return this.failedQ.peek(0);
|
|
5965
6159
|
} else {
|
|
5966
6160
|
this.log(`No more cards available for the session!`);
|
|
5967
|
-
|
|
6161
|
+
return null;
|
|
6162
|
+
}
|
|
6163
|
+
}
|
|
6164
|
+
async nextCard(action = "dismiss-success") {
|
|
6165
|
+
this.dismissCurrentCard(action);
|
|
6166
|
+
let card = this.hydratedQ.dequeue();
|
|
6167
|
+
if (!card && this.hasAvailableCards()) {
|
|
6168
|
+
void this._fillHydratedQueue();
|
|
6169
|
+
card = await this.nextHydratedCard();
|
|
5968
6170
|
}
|
|
5969
|
-
|
|
6171
|
+
if (this.hydratedQ.length < 3) {
|
|
6172
|
+
void this._fillHydratedQueue();
|
|
6173
|
+
}
|
|
6174
|
+
return card;
|
|
5970
6175
|
}
|
|
5971
6176
|
dismissCurrentCard(action = "dismiss-success") {
|
|
5972
6177
|
if (this._currentCard) {
|
|
@@ -5977,7 +6182,6 @@ var SessionController = class extends Loggable {
|
|
|
5977
6182
|
failedItem = {
|
|
5978
6183
|
cardID: this._currentCard.cardID,
|
|
5979
6184
|
courseID: this._currentCard.courseID,
|
|
5980
|
-
qualifiedID: this._currentCard.qualifiedID,
|
|
5981
6185
|
contentSourceID: this._currentCard.contentSourceID,
|
|
5982
6186
|
contentSourceType: this._currentCard.contentSourceType,
|
|
5983
6187
|
status: "failed-review",
|
|
@@ -5987,18 +6191,80 @@ var SessionController = class extends Loggable {
|
|
|
5987
6191
|
failedItem = {
|
|
5988
6192
|
cardID: this._currentCard.cardID,
|
|
5989
6193
|
courseID: this._currentCard.courseID,
|
|
5990
|
-
qualifiedID: this._currentCard.qualifiedID,
|
|
5991
6194
|
contentSourceID: this._currentCard.contentSourceID,
|
|
5992
6195
|
contentSourceType: this._currentCard.contentSourceType,
|
|
5993
6196
|
status: "failed-new"
|
|
5994
6197
|
};
|
|
5995
6198
|
}
|
|
5996
|
-
this.failedQ.add(failedItem);
|
|
6199
|
+
this.failedQ.add(failedItem, failedItem.cardID);
|
|
5997
6200
|
} else if (action === "dismiss-error") {
|
|
5998
6201
|
} else if (action === "dismiss-failed") {
|
|
5999
6202
|
}
|
|
6000
6203
|
}
|
|
6001
6204
|
}
|
|
6205
|
+
hasAvailableCards() {
|
|
6206
|
+
return this.reviewQ.length > 0 || this.newQ.length > 0 || this.failedQ.length > 0;
|
|
6207
|
+
}
|
|
6208
|
+
async nextHydratedCard() {
|
|
6209
|
+
while (this.hydratedQ.length === 0 && this.hasAvailableCards()) {
|
|
6210
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
6211
|
+
}
|
|
6212
|
+
return this.hydratedQ.dequeue();
|
|
6213
|
+
}
|
|
6214
|
+
async _fillHydratedQueue() {
|
|
6215
|
+
if (this.hydration_in_progress) {
|
|
6216
|
+
return;
|
|
6217
|
+
}
|
|
6218
|
+
const BUFFER_SIZE = 5;
|
|
6219
|
+
this.hydration_in_progress = true;
|
|
6220
|
+
while (this.hydratedQ.length < BUFFER_SIZE) {
|
|
6221
|
+
const nextItem = this._selectNextItemToHydrate();
|
|
6222
|
+
if (!nextItem) {
|
|
6223
|
+
return;
|
|
6224
|
+
}
|
|
6225
|
+
try {
|
|
6226
|
+
const cardData = await this.dataLayer.getCourseDB(nextItem.courseID).getCourseDoc(nextItem.cardID);
|
|
6227
|
+
if (!(0, import_common15.isCourseElo)(cardData.elo)) {
|
|
6228
|
+
cardData.elo = (0, import_common15.toCourseElo)(cardData.elo);
|
|
6229
|
+
}
|
|
6230
|
+
const view = this.getViewComponent(cardData.id_view);
|
|
6231
|
+
const dataDocs = await Promise.all(
|
|
6232
|
+
cardData.id_displayable_data.map(
|
|
6233
|
+
(id) => this.dataLayer.getCourseDB(nextItem.courseID).getCourseDoc(id, {
|
|
6234
|
+
attachments: true,
|
|
6235
|
+
binary: true
|
|
6236
|
+
})
|
|
6237
|
+
)
|
|
6238
|
+
);
|
|
6239
|
+
const data = dataDocs.map(import_common15.displayableDataToViewData).reverse();
|
|
6240
|
+
this.hydratedQ.add(
|
|
6241
|
+
{
|
|
6242
|
+
item: nextItem,
|
|
6243
|
+
view,
|
|
6244
|
+
data
|
|
6245
|
+
},
|
|
6246
|
+
nextItem.cardID
|
|
6247
|
+
);
|
|
6248
|
+
if (this.reviewQ.peek(0) === nextItem) {
|
|
6249
|
+
this.reviewQ.dequeue();
|
|
6250
|
+
} else if (this.newQ.peek(0) === nextItem) {
|
|
6251
|
+
this.newQ.dequeue();
|
|
6252
|
+
} else {
|
|
6253
|
+
this.failedQ.dequeue();
|
|
6254
|
+
}
|
|
6255
|
+
} catch (e) {
|
|
6256
|
+
this.error(`Error hydrating card ${nextItem.cardID}:`, e);
|
|
6257
|
+
if (this.reviewQ.peek(0) === nextItem) {
|
|
6258
|
+
this.reviewQ.dequeue();
|
|
6259
|
+
} else if (this.newQ.peek(0) === nextItem) {
|
|
6260
|
+
this.newQ.dequeue();
|
|
6261
|
+
} else {
|
|
6262
|
+
this.failedQ.dequeue();
|
|
6263
|
+
}
|
|
6264
|
+
}
|
|
6265
|
+
}
|
|
6266
|
+
this.hydration_in_progress = false;
|
|
6267
|
+
}
|
|
6002
6268
|
};
|
|
6003
6269
|
|
|
6004
6270
|
// src/study/SpacedRepetition.ts
|
|
@@ -6024,7 +6290,7 @@ function newQuestionInterval(user, cardHistory) {
|
|
|
6024
6290
|
});
|
|
6025
6291
|
}
|
|
6026
6292
|
if (currentAttempt.isCorrect) {
|
|
6027
|
-
const skill = currentAttempt.performance;
|
|
6293
|
+
const skill = Math.min(1, Math.max(0, currentAttempt.performance));
|
|
6028
6294
|
logger.debug(`Demontrated skill: ${skill}`);
|
|
6029
6295
|
const interval = lastInterval * (0.75 + skill);
|
|
6030
6296
|
cardHistory.lapses = getLapses(cardHistory.records);
|