@vue-skuilder/db 0.1.7 → 0.1.8-0
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/{SyncStrategy-DnJRj-Xp.d.mts → SyncStrategy-CyATpyLQ.d.mts} +6 -0
- package/dist/{SyncStrategy-DnJRj-Xp.d.ts → SyncStrategy-CyATpyLQ.d.ts} +6 -0
- package/dist/core/index.d.mts +5 -5
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +131 -118
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +128 -115
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-BbW9EnZK.d.mts → dataLayerProvider-BInqI_RF.d.mts} +1 -1
- package/dist/{dataLayerProvider-6stCgDME.d.ts → dataLayerProvider-DqtNroSh.d.ts} +1 -1
- package/dist/impl/couch/index.d.mts +6 -6
- package/dist/impl/couch/index.d.ts +6 -6
- package/dist/impl/couch/index.js +1365 -1252
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +1359 -1246
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.mts +8 -6
- package/dist/impl/static/index.d.ts +8 -6
- package/dist/impl/static/index.js +253 -843
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +250 -842
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/index-CLL31bEy.d.ts +137 -0
- package/dist/index-CUNnL38E.d.mts +137 -0
- package/dist/index.d.mts +10 -55
- package/dist/index.d.ts +10 -55
- package/dist/index.js +343 -170
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +340 -167
- package/dist/index.mjs.map +1 -1
- package/dist/{types-BvzcRAys.d.ts → types-BefDGkKa.d.ts} +1 -1
- package/dist/{types-CQQ80R5N.d.mts → types-DC-ckZug.d.mts} +1 -1
- package/dist/{types-legacy-CtrmkOLu.d.mts → types-legacy-Birv-Jx6.d.mts} +2 -2
- package/dist/{types-legacy-CtrmkOLu.d.ts → types-legacy-Birv-Jx6.d.ts} +2 -2
- package/dist/{userDB-DUY63VMN.d.ts → userDB-C33Hzjgn.d.mts} +10 -3
- package/dist/{userDB-7fM4tpgr.d.mts → userDB-DusL7OXe.d.ts} +10 -3
- package/dist/util/packer/index.d.mts +3 -63
- package/dist/util/packer/index.d.ts +3 -63
- package/dist/util/packer/index.js +53 -1
- package/dist/util/packer/index.js.map +1 -1
- package/dist/util/packer/index.mjs +53 -1
- package/dist/util/packer/index.mjs.map +1 -1
- package/package.json +7 -4
- package/src/core/types/types-legacy.ts +13 -1
- package/src/core/types/user.ts +9 -2
- package/src/core/util/index.ts +5 -4
- package/src/impl/common/BaseUserDB.ts +33 -22
- package/src/impl/common/SyncStrategy.ts +7 -0
- package/src/impl/common/index.ts +0 -1
- package/src/impl/common/userDBHelpers.ts +4 -4
- package/src/impl/couch/CouchDBSyncStrategy.ts +10 -0
- package/src/impl/couch/courseAPI.ts +7 -6
- package/src/impl/couch/index.ts +10 -5
- package/src/impl/couch/updateQueue.ts +12 -8
- package/src/impl/couch/user-course-relDB.ts +17 -27
- package/src/impl/static/NoOpSyncStrategy.ts +5 -0
- package/src/impl/static/StaticDataUnpacker.ts +18 -36
- package/src/impl/static/courseDB.ts +135 -17
- package/src/util/migrator/FileSystemAdapter.ts +20 -0
- package/src/util/migrator/StaticToCouchDBMigrator.ts +6 -0
- package/src/util/packer/CouchDBToStaticPacker.ts +92 -2
package/dist/index.mjs
CHANGED
|
@@ -80,7 +80,7 @@ var init_logger = __esm({
|
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
// src/core/types/types-legacy.ts
|
|
83
|
-
var GuestUsername, log, DocType,
|
|
83
|
+
var GuestUsername, log, DocType, DocTypePrefixes;
|
|
84
84
|
var init_types_legacy = __esm({
|
|
85
85
|
"src/core/types/types-legacy.ts"() {
|
|
86
86
|
"use strict";
|
|
@@ -89,20 +89,32 @@ var init_types_legacy = __esm({
|
|
|
89
89
|
log = (message) => {
|
|
90
90
|
logger.log(message);
|
|
91
91
|
};
|
|
92
|
-
DocType = /* @__PURE__ */ ((
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return
|
|
92
|
+
DocType = /* @__PURE__ */ ((DocType3) => {
|
|
93
|
+
DocType3["DISPLAYABLE_DATA"] = "DISPLAYABLE_DATA";
|
|
94
|
+
DocType3["CARD"] = "CARD";
|
|
95
|
+
DocType3["DATASHAPE"] = "DATASHAPE";
|
|
96
|
+
DocType3["QUESTIONTYPE"] = "QUESTION";
|
|
97
|
+
DocType3["VIEW"] = "VIEW";
|
|
98
|
+
DocType3["PEDAGOGY"] = "PEDAGOGY";
|
|
99
|
+
DocType3["CARDRECORD"] = "CARDRECORD";
|
|
100
|
+
DocType3["SCHEDULED_CARD"] = "SCHEDULED_CARD";
|
|
101
|
+
DocType3["TAG"] = "TAG";
|
|
102
|
+
DocType3["NAVIGATION_STRATEGY"] = "NAVIGATION_STRATEGY";
|
|
103
|
+
return DocType3;
|
|
104
104
|
})(DocType || {});
|
|
105
|
-
|
|
105
|
+
DocTypePrefixes = {
|
|
106
|
+
["CARD" /* CARD */]: "c",
|
|
107
|
+
["DISPLAYABLE_DATA" /* DISPLAYABLE_DATA */]: "dd",
|
|
108
|
+
["TAG" /* TAG */]: "TAG",
|
|
109
|
+
["CARDRECORD" /* CARDRECORD */]: "cardH",
|
|
110
|
+
["SCHEDULED_CARD" /* SCHEDULED_CARD */]: "card_review_",
|
|
111
|
+
// Add other doctypes here as they get prefixed IDs
|
|
112
|
+
["DATASHAPE" /* DATASHAPE */]: "DATASHAPE",
|
|
113
|
+
["QUESTION" /* QUESTIONTYPE */]: "QUESTION",
|
|
114
|
+
["VIEW" /* VIEW */]: "VIEW",
|
|
115
|
+
["PEDAGOGY" /* PEDAGOGY */]: "PEDAGOGY",
|
|
116
|
+
["NAVIGATION_STRATEGY" /* NAVIGATION_STRATEGY */]: "NAVIGATION_STRATEGY"
|
|
117
|
+
};
|
|
106
118
|
}
|
|
107
119
|
});
|
|
108
120
|
|
|
@@ -114,16 +126,16 @@ function isQuestionRecord(c) {
|
|
|
114
126
|
return c.userAnswer !== void 0;
|
|
115
127
|
}
|
|
116
128
|
function getCardHistoryID(courseID, cardID) {
|
|
117
|
-
return `${
|
|
129
|
+
return `${DocTypePrefixes["CARDRECORD" /* CARDRECORD */]}-${courseID}-${cardID}`;
|
|
118
130
|
}
|
|
119
131
|
function parseCardHistoryID(id) {
|
|
120
132
|
const split = id.split("-");
|
|
121
133
|
let error = "";
|
|
122
134
|
error += split.length === 3 ? "" : `
|
|
123
135
|
given ID has incorrect number of '-' characters`;
|
|
124
|
-
error += split[0] ===
|
|
125
|
-
given ID does not start with ${
|
|
126
|
-
if (split.length === 3 && split[0] ===
|
|
136
|
+
error += split[0] === DocTypePrefixes["CARDRECORD" /* CARDRECORD */] ? "" : `
|
|
137
|
+
given ID does not start with ${DocTypePrefixes["CARDRECORD" /* CARDRECORD */]}`;
|
|
138
|
+
if (split.length === 3 && split[0] === DocTypePrefixes["CARDRECORD" /* CARDRECORD */]) {
|
|
127
139
|
return {
|
|
128
140
|
courseID: split[1],
|
|
129
141
|
cardID: split[2]
|
|
@@ -341,11 +353,11 @@ function scheduleCardReviewLocal(userDB, review) {
|
|
|
341
353
|
const now = moment.utc();
|
|
342
354
|
logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
|
|
343
355
|
void userDB.put({
|
|
344
|
-
_id:
|
|
356
|
+
_id: DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */] + review.time.format(REVIEW_TIME_FORMAT),
|
|
345
357
|
cardId: review.card_id,
|
|
346
|
-
reviewTime: review.time,
|
|
358
|
+
reviewTime: review.time.toISOString(),
|
|
347
359
|
courseId: review.course_id,
|
|
348
|
-
scheduledAt: now,
|
|
360
|
+
scheduledAt: now.toISOString(),
|
|
349
361
|
scheduledFor: review.scheduledFor,
|
|
350
362
|
schedulingAgentId: review.schedulingAgentId
|
|
351
363
|
});
|
|
@@ -361,14 +373,14 @@ async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
|
|
|
361
373
|
${JSON.stringify(err)}`);
|
|
362
374
|
});
|
|
363
375
|
}
|
|
364
|
-
var
|
|
376
|
+
var REVIEW_TIME_FORMAT, log2;
|
|
365
377
|
var init_userDBHelpers = __esm({
|
|
366
378
|
"src/impl/common/userDBHelpers.ts"() {
|
|
367
379
|
"use strict";
|
|
380
|
+
init_core();
|
|
368
381
|
init_logger();
|
|
369
382
|
init_pouchdb_setup();
|
|
370
383
|
init_dataDirectory();
|
|
371
|
-
REVIEW_PREFIX = "card_review_";
|
|
372
384
|
REVIEW_TIME_FORMAT = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
373
385
|
log2 = (s) => {
|
|
374
386
|
logger.info(s);
|
|
@@ -403,7 +415,10 @@ var init_updateQueue = __esm({
|
|
|
403
415
|
_className = "UpdateQueue";
|
|
404
416
|
pendingUpdates = {};
|
|
405
417
|
inprogressUpdates = {};
|
|
406
|
-
|
|
418
|
+
readDB;
|
|
419
|
+
// Database for read operations
|
|
420
|
+
writeDB;
|
|
421
|
+
// Database for write operations (local-first)
|
|
407
422
|
update(id, update) {
|
|
408
423
|
logger.debug(`Update requested on doc: ${id}`);
|
|
409
424
|
if (this.pendingUpdates[id]) {
|
|
@@ -413,24 +428,25 @@ var init_updateQueue = __esm({
|
|
|
413
428
|
}
|
|
414
429
|
return this.applyUpdates(id);
|
|
415
430
|
}
|
|
416
|
-
constructor(
|
|
431
|
+
constructor(readDB, writeDB) {
|
|
417
432
|
super();
|
|
418
|
-
this.
|
|
433
|
+
this.readDB = readDB;
|
|
434
|
+
this.writeDB = writeDB || readDB;
|
|
419
435
|
logger.debug(`UpdateQ initialized...`);
|
|
420
|
-
void this.
|
|
436
|
+
void this.readDB.info().then((i) => {
|
|
421
437
|
logger.debug(`db info: ${JSON.stringify(i)}`);
|
|
422
438
|
});
|
|
423
439
|
}
|
|
424
440
|
async applyUpdates(id) {
|
|
425
441
|
logger.debug(`Applying updates on doc: ${id}`);
|
|
426
442
|
if (this.inprogressUpdates[id]) {
|
|
427
|
-
await this.
|
|
443
|
+
await this.readDB.info();
|
|
428
444
|
return this.applyUpdates(id);
|
|
429
445
|
} else {
|
|
430
446
|
if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
|
|
431
447
|
this.inprogressUpdates[id] = true;
|
|
432
448
|
try {
|
|
433
|
-
let doc = await this.
|
|
449
|
+
let doc = await this.readDB.get(id);
|
|
434
450
|
logger.debug(`Retrieved doc: ${id}`);
|
|
435
451
|
while (this.pendingUpdates[id].length !== 0) {
|
|
436
452
|
const update = this.pendingUpdates[id].splice(0, 1)[0];
|
|
@@ -443,7 +459,7 @@ var init_updateQueue = __esm({
|
|
|
443
459
|
};
|
|
444
460
|
}
|
|
445
461
|
}
|
|
446
|
-
await this.
|
|
462
|
+
await this.writeDB.put(doc);
|
|
447
463
|
logger.debug(`Put doc: ${id}`);
|
|
448
464
|
if (this.pendingUpdates[id].length === 0) {
|
|
449
465
|
this.inprogressUpdates[id] = false;
|
|
@@ -469,6 +485,60 @@ var init_updateQueue = __esm({
|
|
|
469
485
|
}
|
|
470
486
|
});
|
|
471
487
|
|
|
488
|
+
// src/impl/couch/user-course-relDB.ts
|
|
489
|
+
import moment2 from "moment";
|
|
490
|
+
var UsrCrsData;
|
|
491
|
+
var init_user_course_relDB = __esm({
|
|
492
|
+
"src/impl/couch/user-course-relDB.ts"() {
|
|
493
|
+
"use strict";
|
|
494
|
+
init_logger();
|
|
495
|
+
UsrCrsData = class {
|
|
496
|
+
user;
|
|
497
|
+
_courseId;
|
|
498
|
+
constructor(user, courseId) {
|
|
499
|
+
this.user = user;
|
|
500
|
+
this._courseId = courseId;
|
|
501
|
+
}
|
|
502
|
+
async getReviewsForcast(daysCount) {
|
|
503
|
+
const time = moment2.utc().add(daysCount, "days");
|
|
504
|
+
return this.getReviewstoDate(time);
|
|
505
|
+
}
|
|
506
|
+
async getPendingReviews() {
|
|
507
|
+
const now = moment2.utc();
|
|
508
|
+
return this.getReviewstoDate(now);
|
|
509
|
+
}
|
|
510
|
+
async getScheduledReviewCount() {
|
|
511
|
+
return (await this.getPendingReviews()).length;
|
|
512
|
+
}
|
|
513
|
+
async getCourseSettings() {
|
|
514
|
+
const regDoc = await this.user.getCourseRegistrationsDoc();
|
|
515
|
+
const crsDoc = regDoc.courses.find((c) => c.courseID === this._courseId);
|
|
516
|
+
if (crsDoc && crsDoc.settings) {
|
|
517
|
+
return crsDoc.settings;
|
|
518
|
+
} else {
|
|
519
|
+
logger.warn(`no settings found during lookup on course ${this._courseId}`);
|
|
520
|
+
return {};
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
updateCourseSettings(updates) {
|
|
524
|
+
if ("updateCourseSettings" in this.user) {
|
|
525
|
+
void this.user.updateCourseSettings(this._courseId, updates);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
async getReviewstoDate(targetDate) {
|
|
529
|
+
const allReviews = await this.user.getPendingReviews(this._courseId);
|
|
530
|
+
logger.debug(
|
|
531
|
+
`Fetching ${this.user.getUsername()}'s scheduled reviews for course ${this._courseId}.`
|
|
532
|
+
);
|
|
533
|
+
return allReviews.filter((review) => {
|
|
534
|
+
const reviewTime = moment2.utc(review.reviewTime);
|
|
535
|
+
return targetDate.isAfter(reviewTime);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
});
|
|
541
|
+
|
|
472
542
|
// src/impl/couch/clientCache.ts
|
|
473
543
|
async function GET_CACHED(k, f) {
|
|
474
544
|
if (CLIENT_CACHE[k]) {
|
|
@@ -492,10 +562,12 @@ var init_clientCache = __esm({
|
|
|
492
562
|
import { NameSpacer } from "@vue-skuilder/common";
|
|
493
563
|
import { blankCourseElo, toCourseElo } from "@vue-skuilder/common";
|
|
494
564
|
import { prepareNote55 } from "@vue-skuilder/common";
|
|
565
|
+
import { v4 as uuidv4 } from "uuid";
|
|
495
566
|
async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo()) {
|
|
496
567
|
const db = getCourseDB(courseID);
|
|
497
568
|
const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
|
|
498
|
-
const
|
|
569
|
+
const _id = `${DocTypePrefixes["DISPLAYABLE_DATA" /* DISPLAYABLE_DATA */]}-${uuidv4()}`;
|
|
570
|
+
const result = await db.put({ ...payload, _id });
|
|
499
571
|
const dataShapeId = NameSpacer.getDataShapeString({
|
|
500
572
|
course: codeCourse,
|
|
501
573
|
dataShape: shape.name
|
|
@@ -566,7 +638,9 @@ async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags
|
|
|
566
638
|
}
|
|
567
639
|
async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
|
|
568
640
|
const db = getCourseDB(courseID);
|
|
569
|
-
const
|
|
641
|
+
const _id = `${DocTypePrefixes["CARD" /* CARD */]}-${uuidv4()}`;
|
|
642
|
+
const card = await db.put({
|
|
643
|
+
_id,
|
|
570
644
|
course,
|
|
571
645
|
id_displayable_data,
|
|
572
646
|
id_view,
|
|
@@ -1475,7 +1549,7 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
1475
1549
|
});
|
|
1476
1550
|
|
|
1477
1551
|
// src/impl/couch/classroomDB.ts
|
|
1478
|
-
import
|
|
1552
|
+
import moment3 from "moment";
|
|
1479
1553
|
var classroomLookupDBTitle, CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB, TeacherClassroomDB, ClassroomLookupDB;
|
|
1480
1554
|
var init_classroomDB2 = __esm({
|
|
1481
1555
|
"src/impl/couch/classroomDB.ts"() {
|
|
@@ -1576,9 +1650,9 @@ var init_classroomDB2 = __esm({
|
|
|
1576
1650
|
}
|
|
1577
1651
|
async getNewCards() {
|
|
1578
1652
|
const activeCards = await this._user.getActiveCards();
|
|
1579
|
-
const now =
|
|
1653
|
+
const now = moment3.utc();
|
|
1580
1654
|
const assigned = await this.getAssignedContent();
|
|
1581
|
-
const due = assigned.filter((c) => now.isAfter(
|
|
1655
|
+
const due = assigned.filter((c) => now.isAfter(moment3.utc(c.activeOn, REVIEW_TIME_FORMAT2)));
|
|
1582
1656
|
logger.info(`Due content: ${JSON.stringify(due)}`);
|
|
1583
1657
|
let ret = [];
|
|
1584
1658
|
for (let i = 0; i < due.length; i++) {
|
|
@@ -1669,8 +1743,8 @@ var init_classroomDB2 = __esm({
|
|
|
1669
1743
|
type: "tag",
|
|
1670
1744
|
_id: id,
|
|
1671
1745
|
assignedBy: content.assignedBy,
|
|
1672
|
-
assignedOn:
|
|
1673
|
-
activeOn: content.activeOn ||
|
|
1746
|
+
assignedOn: moment3.utc(),
|
|
1747
|
+
activeOn: content.activeOn || moment3.utc()
|
|
1674
1748
|
});
|
|
1675
1749
|
} else {
|
|
1676
1750
|
put = await this._db.put({
|
|
@@ -1678,8 +1752,8 @@ var init_classroomDB2 = __esm({
|
|
|
1678
1752
|
type: "course",
|
|
1679
1753
|
_id: id,
|
|
1680
1754
|
assignedBy: content.assignedBy,
|
|
1681
|
-
assignedOn:
|
|
1682
|
-
activeOn: content.activeOn ||
|
|
1755
|
+
assignedOn: moment3.utc(),
|
|
1756
|
+
activeOn: content.activeOn || moment3.utc()
|
|
1683
1757
|
});
|
|
1684
1758
|
}
|
|
1685
1759
|
if (put.ok) {
|
|
@@ -1842,6 +1916,13 @@ var init_CouchDBSyncStrategy = __esm({
|
|
|
1842
1916
|
return this.getUserDB(username);
|
|
1843
1917
|
}
|
|
1844
1918
|
}
|
|
1919
|
+
getWriteDB(username) {
|
|
1920
|
+
if (username === GuestUsername || username.startsWith(GuestUsername)) {
|
|
1921
|
+
return getLocalUserDB(username);
|
|
1922
|
+
} else {
|
|
1923
|
+
return this.getUserDB(username);
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1845
1926
|
startSync(localDB, remoteDB) {
|
|
1846
1927
|
if (localDB !== remoteDB) {
|
|
1847
1928
|
this.syncHandle = pouchdb_setup_default.sync(localDB, remoteDB, {
|
|
@@ -1987,7 +2068,7 @@ var init_CouchDBSyncStrategy = __esm({
|
|
|
1987
2068
|
});
|
|
1988
2069
|
|
|
1989
2070
|
// src/impl/couch/index.ts
|
|
1990
|
-
import
|
|
2071
|
+
import moment4 from "moment";
|
|
1991
2072
|
import process2 from "process";
|
|
1992
2073
|
function getCourseDB2(courseID) {
|
|
1993
2074
|
return new pouchdb_setup_default(
|
|
@@ -2021,7 +2102,7 @@ function getStartAndEndKeys2(key) {
|
|
|
2021
2102
|
endkey: key + "\uFFF0"
|
|
2022
2103
|
};
|
|
2023
2104
|
}
|
|
2024
|
-
var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig,
|
|
2105
|
+
var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_TIME_FORMAT2;
|
|
2025
2106
|
var init_couch = __esm({
|
|
2026
2107
|
"src/impl/couch/index.ts"() {
|
|
2027
2108
|
"use strict";
|
|
@@ -2047,78 +2128,10 @@ var init_couch = __esm({
|
|
|
2047
2128
|
return pouchdb_setup_default.fetch(url, opts);
|
|
2048
2129
|
}
|
|
2049
2130
|
};
|
|
2050
|
-
REVIEW_PREFIX2 = "card_review_";
|
|
2051
2131
|
REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
2052
2132
|
}
|
|
2053
2133
|
});
|
|
2054
2134
|
|
|
2055
|
-
// src/impl/couch/user-course-relDB.ts
|
|
2056
|
-
import moment4 from "moment";
|
|
2057
|
-
var UsrCrsData;
|
|
2058
|
-
var init_user_course_relDB = __esm({
|
|
2059
|
-
"src/impl/couch/user-course-relDB.ts"() {
|
|
2060
|
-
"use strict";
|
|
2061
|
-
init_couch();
|
|
2062
|
-
init_courseDB();
|
|
2063
|
-
init_logger();
|
|
2064
|
-
UsrCrsData = class {
|
|
2065
|
-
user;
|
|
2066
|
-
course;
|
|
2067
|
-
_courseId;
|
|
2068
|
-
constructor(user, courseId) {
|
|
2069
|
-
this.user = user;
|
|
2070
|
-
this.course = new CourseDB(courseId, async () => this.user);
|
|
2071
|
-
this._courseId = courseId;
|
|
2072
|
-
}
|
|
2073
|
-
async getReviewsForcast(daysCount) {
|
|
2074
|
-
const time = moment4.utc().add(daysCount, "days");
|
|
2075
|
-
return this.getReviewstoDate(time);
|
|
2076
|
-
}
|
|
2077
|
-
async getPendingReviews() {
|
|
2078
|
-
const now = moment4.utc();
|
|
2079
|
-
return this.getReviewstoDate(now);
|
|
2080
|
-
}
|
|
2081
|
-
async getScheduledReviewCount() {
|
|
2082
|
-
return (await this.getPendingReviews()).length;
|
|
2083
|
-
}
|
|
2084
|
-
async getCourseSettings() {
|
|
2085
|
-
const regDoc = await this.user.getCourseRegistrationsDoc();
|
|
2086
|
-
const crsDoc = regDoc.courses.find((c) => c.courseID === this._courseId);
|
|
2087
|
-
if (crsDoc && crsDoc.settings) {
|
|
2088
|
-
return crsDoc.settings;
|
|
2089
|
-
} else {
|
|
2090
|
-
logger.warn(`no settings found during lookup on course ${this._courseId}`);
|
|
2091
|
-
return {};
|
|
2092
|
-
}
|
|
2093
|
-
}
|
|
2094
|
-
updateCourseSettings(updates) {
|
|
2095
|
-
void this.user.updateCourseSettings(this._courseId, updates);
|
|
2096
|
-
}
|
|
2097
|
-
async getReviewstoDate(targetDate) {
|
|
2098
|
-
const keys = getStartAndEndKeys2(REVIEW_PREFIX2);
|
|
2099
|
-
const reviews = await this.user.remote().allDocs({
|
|
2100
|
-
startkey: keys.startkey,
|
|
2101
|
-
endkey: keys.endkey,
|
|
2102
|
-
include_docs: true
|
|
2103
|
-
});
|
|
2104
|
-
logger.debug(
|
|
2105
|
-
`Fetching ${this.user.getUsername()}'s scheduled reviews for course ${this._courseId}.`
|
|
2106
|
-
);
|
|
2107
|
-
return reviews.rows.filter((r) => {
|
|
2108
|
-
if (r.id.startsWith(REVIEW_PREFIX2)) {
|
|
2109
|
-
const date = moment4.utc(r.id.substr(REVIEW_PREFIX2.length), REVIEW_TIME_FORMAT2);
|
|
2110
|
-
if (targetDate.isAfter(date)) {
|
|
2111
|
-
if (this._courseId === void 0 || r.doc.courseId === this._courseId) {
|
|
2112
|
-
return true;
|
|
2113
|
-
}
|
|
2114
|
-
}
|
|
2115
|
-
}
|
|
2116
|
-
}).map((r) => r.doc);
|
|
2117
|
-
}
|
|
2118
|
-
};
|
|
2119
|
-
}
|
|
2120
|
-
});
|
|
2121
|
-
|
|
2122
2135
|
// src/impl/common/BaseUserDB.ts
|
|
2123
2136
|
import { Status as Status3 } from "@vue-skuilder/common";
|
|
2124
2137
|
import moment5 from "moment";
|
|
@@ -2214,10 +2227,11 @@ async function dropUserFromClassroom(user, classID) {
|
|
|
2214
2227
|
async function getUserClassrooms(user) {
|
|
2215
2228
|
return getOrCreateClassroomRegistrationsDoc(user);
|
|
2216
2229
|
}
|
|
2217
|
-
var log4,
|
|
2230
|
+
var log4, BaseUser, userCoursesDoc, userClassroomsDoc;
|
|
2218
2231
|
var init_BaseUserDB = __esm({
|
|
2219
2232
|
"src/impl/common/BaseUserDB.ts"() {
|
|
2220
2233
|
"use strict";
|
|
2234
|
+
init_core();
|
|
2221
2235
|
init_util();
|
|
2222
2236
|
init_types_legacy();
|
|
2223
2237
|
init_logger();
|
|
@@ -2228,7 +2242,6 @@ var init_BaseUserDB = __esm({
|
|
|
2228
2242
|
log4 = (s) => {
|
|
2229
2243
|
logger.info(s);
|
|
2230
2244
|
};
|
|
2231
|
-
cardHistoryPrefix2 = "cardH-";
|
|
2232
2245
|
BaseUser = class _BaseUser {
|
|
2233
2246
|
static _instance;
|
|
2234
2247
|
static _initialized = false;
|
|
@@ -2249,11 +2262,13 @@ var init_BaseUserDB = __esm({
|
|
|
2249
2262
|
isLoggedIn() {
|
|
2250
2263
|
return !this._username.startsWith(GuestUsername);
|
|
2251
2264
|
}
|
|
2252
|
-
remoteDB;
|
|
2253
2265
|
remote() {
|
|
2254
2266
|
return this.remoteDB;
|
|
2255
2267
|
}
|
|
2256
2268
|
localDB;
|
|
2269
|
+
remoteDB;
|
|
2270
|
+
writeDB;
|
|
2271
|
+
// Database to use for write operations (local-first approach)
|
|
2257
2272
|
updateQueue;
|
|
2258
2273
|
async createAccount(username, password) {
|
|
2259
2274
|
if (!this.syncStrategy.canCreateAccount()) {
|
|
@@ -2317,8 +2332,8 @@ Currently logged-in as ${this._username}.`
|
|
|
2317
2332
|
const allDocs = await localDB.allDocs({ include_docs: false });
|
|
2318
2333
|
const docsToDelete = allDocs.rows.filter((row) => {
|
|
2319
2334
|
const id = row.id;
|
|
2320
|
-
return id.startsWith(
|
|
2321
|
-
id.startsWith(
|
|
2335
|
+
return id.startsWith(DocTypePrefixes["CARDRECORD" /* CARDRECORD */]) || // Card interaction history
|
|
2336
|
+
id.startsWith(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]) || // Scheduled reviews
|
|
2322
2337
|
id === _BaseUser.DOC_IDS.COURSE_REGISTRATIONS || // Course registrations
|
|
2323
2338
|
id === _BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS || // Classroom registrations
|
|
2324
2339
|
id === _BaseUser.DOC_IDS.CONFIG;
|
|
@@ -2387,7 +2402,7 @@ Currently logged-in as ${this._username}.`
|
|
|
2387
2402
|
*
|
|
2388
2403
|
*/
|
|
2389
2404
|
async getActiveCards() {
|
|
2390
|
-
const keys = getStartAndEndKeys(
|
|
2405
|
+
const keys = getStartAndEndKeys(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]);
|
|
2391
2406
|
const reviews = await this.remoteDB.allDocs({
|
|
2392
2407
|
startkey: keys.startkey,
|
|
2393
2408
|
endkey: keys.endkey,
|
|
@@ -2459,7 +2474,7 @@ Currently logged-in as ${this._username}.`
|
|
|
2459
2474
|
}
|
|
2460
2475
|
}
|
|
2461
2476
|
async getReviewstoDate(targetDate, course_id) {
|
|
2462
|
-
const keys = getStartAndEndKeys(
|
|
2477
|
+
const keys = getStartAndEndKeys(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]);
|
|
2463
2478
|
const reviews = await this.remoteDB.allDocs({
|
|
2464
2479
|
startkey: keys.startkey,
|
|
2465
2480
|
endkey: keys.endkey,
|
|
@@ -2469,8 +2484,11 @@ Currently logged-in as ${this._username}.`
|
|
|
2469
2484
|
`Fetching ${this._username}'s scheduled reviews${course_id ? ` for course ${course_id}` : ""}.`
|
|
2470
2485
|
);
|
|
2471
2486
|
return reviews.rows.filter((r) => {
|
|
2472
|
-
if (r.id.startsWith(
|
|
2473
|
-
const date = moment5.utc(
|
|
2487
|
+
if (r.id.startsWith(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */])) {
|
|
2488
|
+
const date = moment5.utc(
|
|
2489
|
+
r.id.substr(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */].length),
|
|
2490
|
+
REVIEW_TIME_FORMAT
|
|
2491
|
+
);
|
|
2474
2492
|
if (targetDate.isAfter(date)) {
|
|
2475
2493
|
if (course_id === void 0 || r.doc.courseId === course_id) {
|
|
2476
2494
|
return true;
|
|
@@ -2585,7 +2603,8 @@ Currently logged-in as ${this._username}.`
|
|
|
2585
2603
|
const defaultConfig = {
|
|
2586
2604
|
_id: _BaseUser.DOC_IDS.CONFIG,
|
|
2587
2605
|
darkMode: false,
|
|
2588
|
-
likesConfetti: false
|
|
2606
|
+
likesConfetti: false,
|
|
2607
|
+
sessionTimeLimit: 5
|
|
2589
2608
|
};
|
|
2590
2609
|
try {
|
|
2591
2610
|
const cfg = await this.localDB.get(_BaseUser.DOC_IDS.CONFIG);
|
|
@@ -2658,7 +2677,8 @@ Currently logged-in as ${this._username}.`
|
|
|
2658
2677
|
setDBandQ() {
|
|
2659
2678
|
this.localDB = getLocalUserDB(this._username);
|
|
2660
2679
|
this.remoteDB = this.syncStrategy.setupRemoteDB(this._username);
|
|
2661
|
-
this.
|
|
2680
|
+
this.writeDB = this.syncStrategy.getWriteDB ? this.syncStrategy.getWriteDB(this._username) : this.localDB;
|
|
2681
|
+
this.updateQueue = new UpdateQueue(this.localDB, this.writeDB);
|
|
2662
2682
|
}
|
|
2663
2683
|
async init() {
|
|
2664
2684
|
_BaseUser._initialized = false;
|
|
@@ -2776,8 +2796,8 @@ Currently logged-in as ${this._username}.`
|
|
|
2776
2796
|
streak: 0,
|
|
2777
2797
|
bestInterval: 0
|
|
2778
2798
|
};
|
|
2779
|
-
|
|
2780
|
-
return initCardHistory;
|
|
2799
|
+
const putResult = await this.writeDB.put(initCardHistory);
|
|
2800
|
+
return { ...initCardHistory, _rev: putResult.rev };
|
|
2781
2801
|
} else {
|
|
2782
2802
|
throw new Error(`putCardRecord failed because of:
|
|
2783
2803
|
name:${reason.name}
|
|
@@ -2812,7 +2832,7 @@ Currently logged-in as ${this._username}.`
|
|
|
2812
2832
|
const deletePromises = duplicateDocIds.map(async (docId) => {
|
|
2813
2833
|
try {
|
|
2814
2834
|
const doc = await this.remoteDB.get(docId);
|
|
2815
|
-
await this.
|
|
2835
|
+
await this.writeDB.remove(doc);
|
|
2816
2836
|
log4(`Successfully removed duplicate review: ${docId}`);
|
|
2817
2837
|
} catch (error) {
|
|
2818
2838
|
log4(`Failed to remove duplicate review ${docId}: ${error}`);
|
|
@@ -2834,7 +2854,7 @@ Currently logged-in as ${this._username}.`
|
|
|
2834
2854
|
* @param course_id optional specification of individual course
|
|
2835
2855
|
*/
|
|
2836
2856
|
async getSeenCards(course_id) {
|
|
2837
|
-
let prefix =
|
|
2857
|
+
let prefix = DocTypePrefixes["CARDRECORD" /* CARDRECORD */];
|
|
2838
2858
|
if (course_id) {
|
|
2839
2859
|
prefix += course_id;
|
|
2840
2860
|
}
|
|
@@ -2843,8 +2863,8 @@ Currently logged-in as ${this._username}.`
|
|
|
2843
2863
|
});
|
|
2844
2864
|
const ret = [];
|
|
2845
2865
|
docs.rows.forEach((row) => {
|
|
2846
|
-
if (row.id.startsWith(
|
|
2847
|
-
ret.push(row.id.substr(
|
|
2866
|
+
if (row.id.startsWith(DocTypePrefixes["CARDRECORD" /* CARDRECORD */])) {
|
|
2867
|
+
ret.push(row.id.substr(DocTypePrefixes["CARDRECORD" /* CARDRECORD */].length));
|
|
2848
2868
|
}
|
|
2849
2869
|
});
|
|
2850
2870
|
return ret;
|
|
@@ -2856,7 +2876,7 @@ Currently logged-in as ${this._username}.`
|
|
|
2856
2876
|
async getHistory() {
|
|
2857
2877
|
const cards = await filterAllDocsByPrefix(
|
|
2858
2878
|
this.remoteDB,
|
|
2859
|
-
|
|
2879
|
+
DocTypePrefixes["CARDRECORD" /* CARDRECORD */],
|
|
2860
2880
|
{
|
|
2861
2881
|
include_docs: true,
|
|
2862
2882
|
attachments: false
|
|
@@ -2897,7 +2917,7 @@ Currently logged-in as ${this._username}.`
|
|
|
2897
2917
|
} catch (e) {
|
|
2898
2918
|
const err = e;
|
|
2899
2919
|
if (err.status === 404) {
|
|
2900
|
-
await this.
|
|
2920
|
+
await this.writeDB.put({
|
|
2901
2921
|
_id: _BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS,
|
|
2902
2922
|
registrations: []
|
|
2903
2923
|
});
|
|
@@ -2945,10 +2965,10 @@ Currently logged-in as ${this._username}.`
|
|
|
2945
2965
|
}
|
|
2946
2966
|
}
|
|
2947
2967
|
async scheduleCardReview(review) {
|
|
2948
|
-
return scheduleCardReviewLocal(this.
|
|
2968
|
+
return scheduleCardReviewLocal(this.writeDB, review);
|
|
2949
2969
|
}
|
|
2950
2970
|
async removeScheduledCardReview(reviewId) {
|
|
2951
|
-
return removeScheduledCardReviewLocal(this.
|
|
2971
|
+
return removeScheduledCardReviewLocal(this.writeDB, reviewId);
|
|
2952
2972
|
}
|
|
2953
2973
|
async registerForClassroom(_classId, _registerAs) {
|
|
2954
2974
|
return registerUserForClassroom(this._username, _classId, _registerAs);
|
|
@@ -3160,18 +3180,21 @@ var init_StaticDataUnpacker = __esm({
|
|
|
3160
3180
|
async getTagsIndex() {
|
|
3161
3181
|
return await this.loadIndex("tags");
|
|
3162
3182
|
}
|
|
3183
|
+
getDocTypeFromId(id) {
|
|
3184
|
+
for (const docTypeKey in DocTypePrefixes) {
|
|
3185
|
+
const prefix = DocTypePrefixes[docTypeKey];
|
|
3186
|
+
if (id.startsWith(`${prefix}-`)) {
|
|
3187
|
+
return docTypeKey;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
return void 0;
|
|
3191
|
+
}
|
|
3163
3192
|
/**
|
|
3164
3193
|
* Find which chunk contains a specific document ID
|
|
3165
3194
|
*/
|
|
3166
3195
|
async findChunkForDocument(docId) {
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
if (docId.startsWith(`${docType}-`)) {
|
|
3170
|
-
expectedDocType = docType;
|
|
3171
|
-
break;
|
|
3172
|
-
}
|
|
3173
|
-
}
|
|
3174
|
-
if (expectedDocType !== void 0) {
|
|
3196
|
+
const expectedDocType = this.getDocTypeFromId(docId);
|
|
3197
|
+
if (expectedDocType) {
|
|
3175
3198
|
const typeChunks = this.manifest.chunks.filter((c) => c.docType === expectedDocType);
|
|
3176
3199
|
for (const chunk of typeChunks) {
|
|
3177
3200
|
if (docId >= chunk.startKey && docId <= chunk.endKey) {
|
|
@@ -3181,21 +3204,8 @@ var init_StaticDataUnpacker = __esm({
|
|
|
3181
3204
|
}
|
|
3182
3205
|
}
|
|
3183
3206
|
}
|
|
3184
|
-
return void 0;
|
|
3185
3207
|
} else {
|
|
3186
|
-
const
|
|
3187
|
-
(c) => c.docType === "DISPLAYABLE_DATA"
|
|
3188
|
-
);
|
|
3189
|
-
for (const chunk of displayableChunks) {
|
|
3190
|
-
if (docId >= chunk.startKey && docId <= chunk.endKey) {
|
|
3191
|
-
const exists = await this.verifyDocumentInChunk(docId, chunk);
|
|
3192
|
-
if (exists) {
|
|
3193
|
-
return chunk;
|
|
3194
|
-
}
|
|
3195
|
-
}
|
|
3196
|
-
}
|
|
3197
|
-
const cardChunks = this.manifest.chunks.filter((c) => c.docType === "CARD");
|
|
3198
|
-
for (const chunk of cardChunks) {
|
|
3208
|
+
for (const chunk of this.manifest.chunks) {
|
|
3199
3209
|
if (docId >= chunk.startKey && docId <= chunk.endKey) {
|
|
3200
3210
|
const exists = await this.verifyDocumentInChunk(docId, chunk);
|
|
3201
3211
|
if (exists) {
|
|
@@ -3216,6 +3226,7 @@ var init_StaticDataUnpacker = __esm({
|
|
|
3216
3226
|
}
|
|
3217
3227
|
return void 0;
|
|
3218
3228
|
}
|
|
3229
|
+
return void 0;
|
|
3219
3230
|
}
|
|
3220
3231
|
/**
|
|
3221
3232
|
* Verify that a document actually exists in a specific chunk by loading and checking it
|
|
@@ -3459,6 +3470,7 @@ var init_courseDB2 = __esm({
|
|
|
3459
3470
|
"use strict";
|
|
3460
3471
|
init_types_legacy();
|
|
3461
3472
|
init_navigators();
|
|
3473
|
+
init_logger();
|
|
3462
3474
|
StaticCourseDB = class {
|
|
3463
3475
|
constructor(courseId, unpacker, userDB, manifest) {
|
|
3464
3476
|
this.courseId = courseId;
|
|
@@ -3480,10 +3492,11 @@ var init_courseDB2 = __esm({
|
|
|
3480
3492
|
throw new Error("Cannot update course config in static mode");
|
|
3481
3493
|
}
|
|
3482
3494
|
async getCourseInfo() {
|
|
3495
|
+
const cardCount = this.manifest.chunks.filter((chunk) => chunk.docType === "CARD" /* CARD */).reduce((total, chunk) => total + chunk.documentCount, 0);
|
|
3483
3496
|
return {
|
|
3484
|
-
cardCount
|
|
3485
|
-
// Would come from manifest
|
|
3497
|
+
cardCount,
|
|
3486
3498
|
registeredUsers: 0
|
|
3499
|
+
// Always 0 in static mode
|
|
3487
3500
|
};
|
|
3488
3501
|
}
|
|
3489
3502
|
async getCourseDoc(id, _options) {
|
|
@@ -3572,12 +3585,56 @@ var init_courseDB2 = __esm({
|
|
|
3572
3585
|
courseID: this.courseId
|
|
3573
3586
|
}));
|
|
3574
3587
|
}
|
|
3575
|
-
async getAppliedTags(
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
rows
|
|
3580
|
-
|
|
3588
|
+
async getAppliedTags(cardId) {
|
|
3589
|
+
try {
|
|
3590
|
+
const tagsIndex = await this.unpacker.getTagsIndex();
|
|
3591
|
+
const cardTags = tagsIndex.byCard[cardId] || [];
|
|
3592
|
+
const rows = await Promise.all(
|
|
3593
|
+
cardTags.map(async (tagName) => {
|
|
3594
|
+
const tagId = `${"TAG" /* TAG */}-${tagName}`;
|
|
3595
|
+
try {
|
|
3596
|
+
const tagDoc = await this.unpacker.getDocument(tagId);
|
|
3597
|
+
return {
|
|
3598
|
+
id: tagId,
|
|
3599
|
+
key: cardId,
|
|
3600
|
+
value: {
|
|
3601
|
+
name: tagDoc.name,
|
|
3602
|
+
snippet: tagDoc.snippet,
|
|
3603
|
+
count: tagDoc.taggedCards?.length || 0
|
|
3604
|
+
}
|
|
3605
|
+
};
|
|
3606
|
+
} catch (error) {
|
|
3607
|
+
if (error && error.status === 404) {
|
|
3608
|
+
logger.warn(`Tag document not found for ${tagName}, creating stub`);
|
|
3609
|
+
} else {
|
|
3610
|
+
logger.error(`Error getting tag document for ${tagName}:`, error);
|
|
3611
|
+
throw error;
|
|
3612
|
+
}
|
|
3613
|
+
return {
|
|
3614
|
+
id: tagId,
|
|
3615
|
+
key: cardId,
|
|
3616
|
+
value: {
|
|
3617
|
+
name: tagName,
|
|
3618
|
+
snippet: `Tag: ${tagName}`,
|
|
3619
|
+
count: tagsIndex.byTag[tagName]?.length || 0
|
|
3620
|
+
}
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
})
|
|
3624
|
+
);
|
|
3625
|
+
return {
|
|
3626
|
+
total_rows: rows.length,
|
|
3627
|
+
offset: 0,
|
|
3628
|
+
rows
|
|
3629
|
+
};
|
|
3630
|
+
} catch (error) {
|
|
3631
|
+
logger.error(`Error getting applied tags for card ${cardId}:`, error);
|
|
3632
|
+
return {
|
|
3633
|
+
total_rows: 0,
|
|
3634
|
+
offset: 0,
|
|
3635
|
+
rows: []
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3581
3638
|
}
|
|
3582
3639
|
async addTagToCard(_cardId, _tagId) {
|
|
3583
3640
|
throw new Error("Cannot modify tags in static mode");
|
|
@@ -3595,11 +3652,69 @@ var init_courseDB2 = __esm({
|
|
|
3595
3652
|
throw new Error("Cannot update tags in static mode");
|
|
3596
3653
|
}
|
|
3597
3654
|
async getCourseTagStubs() {
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3655
|
+
try {
|
|
3656
|
+
const tagsIndex = await this.unpacker.getTagsIndex();
|
|
3657
|
+
if (!tagsIndex || !tagsIndex.byTag) {
|
|
3658
|
+
logger.warn("Tags index not found or empty");
|
|
3659
|
+
return {
|
|
3660
|
+
total_rows: 0,
|
|
3661
|
+
offset: 0,
|
|
3662
|
+
rows: []
|
|
3663
|
+
};
|
|
3664
|
+
}
|
|
3665
|
+
const tagNames = Object.keys(tagsIndex.byTag);
|
|
3666
|
+
const rows = await Promise.all(
|
|
3667
|
+
tagNames.map(async (tagName) => {
|
|
3668
|
+
const cardIds = tagsIndex.byTag[tagName] || [];
|
|
3669
|
+
const tagId = `${"TAG" /* TAG */}-${tagName}`;
|
|
3670
|
+
try {
|
|
3671
|
+
const tagDoc = await this.unpacker.getDocument(tagId);
|
|
3672
|
+
return {
|
|
3673
|
+
id: tagId,
|
|
3674
|
+
key: tagId,
|
|
3675
|
+
value: { rev: "1-static" },
|
|
3676
|
+
doc: tagDoc
|
|
3677
|
+
};
|
|
3678
|
+
} catch (error) {
|
|
3679
|
+
if (error && error.status === 404) {
|
|
3680
|
+
logger.warn(`Tag document not found for ${tagName}, creating stub`);
|
|
3681
|
+
const stubDoc = {
|
|
3682
|
+
_id: tagId,
|
|
3683
|
+
_rev: "1-static",
|
|
3684
|
+
course: this.courseId,
|
|
3685
|
+
docType: "TAG" /* TAG */,
|
|
3686
|
+
name: tagName,
|
|
3687
|
+
snippet: `Tag: ${tagName}`,
|
|
3688
|
+
wiki: "",
|
|
3689
|
+
taggedCards: cardIds,
|
|
3690
|
+
author: "system"
|
|
3691
|
+
};
|
|
3692
|
+
return {
|
|
3693
|
+
id: tagId,
|
|
3694
|
+
key: tagId,
|
|
3695
|
+
value: { rev: "1-static" },
|
|
3696
|
+
doc: stubDoc
|
|
3697
|
+
};
|
|
3698
|
+
} else {
|
|
3699
|
+
logger.error(`Error getting tag document for ${tagName}:`, error);
|
|
3700
|
+
throw error;
|
|
3701
|
+
}
|
|
3702
|
+
}
|
|
3703
|
+
})
|
|
3704
|
+
);
|
|
3705
|
+
return {
|
|
3706
|
+
total_rows: rows.length,
|
|
3707
|
+
offset: 0,
|
|
3708
|
+
rows
|
|
3709
|
+
};
|
|
3710
|
+
} catch (error) {
|
|
3711
|
+
logger.error("Failed to get course tag stubs:", error);
|
|
3712
|
+
return {
|
|
3713
|
+
total_rows: 0,
|
|
3714
|
+
offset: 0,
|
|
3715
|
+
rows: []
|
|
3716
|
+
};
|
|
3717
|
+
}
|
|
3603
3718
|
}
|
|
3604
3719
|
async addNote(_codeCourse, _shape, _data, _author, _tags, _uploads, _elo) {
|
|
3605
3720
|
return {
|
|
@@ -3705,6 +3820,9 @@ var init_NoOpSyncStrategy = __esm({
|
|
|
3705
3820
|
setupRemoteDB(username) {
|
|
3706
3821
|
return getLocalUserDB(username);
|
|
3707
3822
|
}
|
|
3823
|
+
getWriteDB(username) {
|
|
3824
|
+
return getLocalUserDB(username);
|
|
3825
|
+
}
|
|
3708
3826
|
startSync(_localDB, _remoteDB) {
|
|
3709
3827
|
}
|
|
3710
3828
|
stopSync() {
|
|
@@ -4156,6 +4274,57 @@ var CouchDBToStaticPacker = class {
|
|
|
4156
4274
|
attachments
|
|
4157
4275
|
};
|
|
4158
4276
|
}
|
|
4277
|
+
/**
|
|
4278
|
+
* Pack a CouchDB course database and write the static files to disk
|
|
4279
|
+
*/
|
|
4280
|
+
async packCourseToFiles(sourceDB, courseId, outputDir, fsAdapter) {
|
|
4281
|
+
logger.info(`Packing course ${courseId} to files in ${outputDir}`);
|
|
4282
|
+
const packedData = await this.packCourse(sourceDB, courseId);
|
|
4283
|
+
const filesWritten = await this.writePackedDataToFiles(packedData, outputDir, fsAdapter);
|
|
4284
|
+
return {
|
|
4285
|
+
manifest: packedData.manifest,
|
|
4286
|
+
filesWritten,
|
|
4287
|
+
attachmentsFound: packedData.attachments ? packedData.attachments.size : 0
|
|
4288
|
+
};
|
|
4289
|
+
}
|
|
4290
|
+
/**
|
|
4291
|
+
* Write packed course data to files using FileSystemAdapter
|
|
4292
|
+
*/
|
|
4293
|
+
async writePackedDataToFiles(packedData, outputDir, fsAdapter) {
|
|
4294
|
+
let totalFiles = 0;
|
|
4295
|
+
await fsAdapter.ensureDir(outputDir);
|
|
4296
|
+
const manifestPath = fsAdapter.joinPath(outputDir, "manifest.json");
|
|
4297
|
+
await fsAdapter.writeJson(manifestPath, packedData.manifest, { spaces: 2 });
|
|
4298
|
+
totalFiles++;
|
|
4299
|
+
logger.info(`Wrote manifest: ${manifestPath}`);
|
|
4300
|
+
const chunksDir = fsAdapter.joinPath(outputDir, "chunks");
|
|
4301
|
+
const indicesDir = fsAdapter.joinPath(outputDir, "indices");
|
|
4302
|
+
await fsAdapter.ensureDir(chunksDir);
|
|
4303
|
+
await fsAdapter.ensureDir(indicesDir);
|
|
4304
|
+
for (const [chunkId, chunkData] of packedData.chunks) {
|
|
4305
|
+
const chunkPath = fsAdapter.joinPath(chunksDir, `${chunkId}.json`);
|
|
4306
|
+
await fsAdapter.writeJson(chunkPath, chunkData);
|
|
4307
|
+
totalFiles++;
|
|
4308
|
+
}
|
|
4309
|
+
logger.info(`Wrote ${packedData.chunks.size} chunk files`);
|
|
4310
|
+
for (const [indexName, indexData] of packedData.indices) {
|
|
4311
|
+
const indexPath = fsAdapter.joinPath(indicesDir, `${indexName}.json`);
|
|
4312
|
+
await fsAdapter.writeJson(indexPath, indexData, { spaces: 2 });
|
|
4313
|
+
totalFiles++;
|
|
4314
|
+
}
|
|
4315
|
+
logger.info(`Wrote ${packedData.indices.size} index files`);
|
|
4316
|
+
if (packedData.attachments && packedData.attachments.size > 0) {
|
|
4317
|
+
for (const [attachmentPath, attachmentData] of packedData.attachments) {
|
|
4318
|
+
const fullAttachmentPath = fsAdapter.joinPath(outputDir, attachmentPath);
|
|
4319
|
+
const attachmentDir = fsAdapter.dirname(fullAttachmentPath);
|
|
4320
|
+
await fsAdapter.ensureDir(attachmentDir);
|
|
4321
|
+
await fsAdapter.writeFile(fullAttachmentPath, attachmentData.buffer);
|
|
4322
|
+
totalFiles++;
|
|
4323
|
+
}
|
|
4324
|
+
logger.info(`Wrote ${packedData.attachments.size} attachment files`);
|
|
4325
|
+
}
|
|
4326
|
+
return totalFiles;
|
|
4327
|
+
}
|
|
4159
4328
|
async extractCourseConfig(db) {
|
|
4160
4329
|
try {
|
|
4161
4330
|
return await db.get("CourseConfig");
|
|
@@ -4324,7 +4493,8 @@ var CouchDBToStaticPacker = class {
|
|
|
4324
4493
|
}
|
|
4325
4494
|
try {
|
|
4326
4495
|
const designDocId = designDoc._id;
|
|
4327
|
-
const
|
|
4496
|
+
const designDocName = designDocId.replace("_design/", "");
|
|
4497
|
+
const viewPath = `${designDocName}/${viewName}`;
|
|
4328
4498
|
logger.info(`Querying CouchDB view: ${viewPath}`);
|
|
4329
4499
|
const viewResults = await this.sourceDB.query(viewPath, {
|
|
4330
4500
|
include_docs: false
|
|
@@ -5249,6 +5419,7 @@ var StaticToCouchDBMigrator = class {
|
|
|
5249
5419
|
const docsToInsert = batch.map((doc) => {
|
|
5250
5420
|
const cleanDoc = { ...doc };
|
|
5251
5421
|
delete cleanDoc._rev;
|
|
5422
|
+
delete cleanDoc._attachments;
|
|
5252
5423
|
return cleanDoc;
|
|
5253
5424
|
});
|
|
5254
5425
|
const bulkResult = await db.bulkDocs(docsToInsert);
|
|
@@ -5362,9 +5533,11 @@ var StaticToCouchDBMigrator = class {
|
|
|
5362
5533
|
attachmentData = await response.arrayBuffer();
|
|
5363
5534
|
}
|
|
5364
5535
|
}
|
|
5536
|
+
const doc = await db.get(docId);
|
|
5365
5537
|
await db.putAttachment(
|
|
5366
5538
|
docId,
|
|
5367
5539
|
attachmentName,
|
|
5540
|
+
doc._rev,
|
|
5368
5541
|
attachmentData,
|
|
5369
5542
|
// PouchDB accepts both ArrayBuffer and Buffer
|
|
5370
5543
|
attachmentMeta.content_type
|
|
@@ -5832,6 +6005,7 @@ export {
|
|
|
5832
6005
|
CouchDBToStaticPacker,
|
|
5833
6006
|
CourseLookup,
|
|
5834
6007
|
DocType,
|
|
6008
|
+
DocTypePrefixes,
|
|
5835
6009
|
ENV,
|
|
5836
6010
|
FileSystemError,
|
|
5837
6011
|
GuestUsername,
|
|
@@ -5841,7 +6015,6 @@ export {
|
|
|
5841
6015
|
StaticToCouchDBMigrator,
|
|
5842
6016
|
_resetDataLayer,
|
|
5843
6017
|
areQuestionRecords,
|
|
5844
|
-
cardHistoryPrefix,
|
|
5845
6018
|
docIsDeleted,
|
|
5846
6019
|
ensureAppDataDirectory,
|
|
5847
6020
|
getAppDataDirectory,
|