@vue-skuilder/db 0.1.6 → 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 +825 -762
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +812 -750
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-BZmLyBVw.d.mts → dataLayerProvider-BInqI_RF.d.mts} +1 -1
- package/dist/{dataLayerProvider-BuntXkCs.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 +2261 -2081
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +2274 -2095
- 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 +524 -1064
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +515 -1058
- 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 +200 -9
- package/dist/index.d.ts +200 -9
- package/dist/index.js +4123 -2820
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4119 -2830
- package/dist/index.mjs.map +1 -1
- package/dist/{types-D6SnlHPm.d.ts → types-BefDGkKa.d.ts} +1 -1
- package/dist/{types-DPRvCrIk.d.mts → types-DC-ckZug.d.mts} +1 -1
- package/dist/{types-legacy-WPe8CtO-.d.mts → types-legacy-Birv-Jx6.d.mts} +2 -2
- package/dist/{types-legacy-WPe8CtO-.d.ts → types-legacy-Birv-Jx6.d.ts} +2 -2
- package/dist/{userDB-D9EuWTp1.d.ts → userDB-C33Hzjgn.d.mts} +11 -4
- package/dist/{userDB-31gsvxyd.d.mts → userDB-DusL7OXe.d.ts} +11 -4
- 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/factory.ts +25 -0
- package/src/impl/common/BaseUserDB.ts +62 -28
- package/src/impl/common/SyncStrategy.ts +7 -0
- package/src/impl/common/index.ts +0 -1
- package/src/impl/common/userDBHelpers.ts +15 -5
- package/src/impl/couch/CouchDBSyncStrategy.ts +10 -0
- package/src/impl/couch/courseAPI.ts +7 -6
- package/src/impl/couch/courseLookupDB.ts +24 -0
- 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/dataDirectory.test.ts +53 -0
- package/src/util/dataDirectory.ts +52 -0
- package/src/util/index.ts +3 -0
- package/src/util/migrator/FileSystemAdapter.ts +79 -0
- package/src/util/migrator/StaticToCouchDBMigrator.ts +713 -0
- package/src/util/migrator/index.ts +18 -0
- package/src/util/migrator/types.ts +84 -0
- package/src/util/migrator/validation.ts +517 -0
- package/src/util/packer/CouchDBToStaticPacker.ts +92 -2
- package/src/util/tuiLogger.ts +139 -0
package/dist/core/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __glob = (map) => (
|
|
4
|
-
var fn = map[
|
|
3
|
+
var __glob = (map) => (path2) => {
|
|
4
|
+
var fn = map[path2];
|
|
5
5
|
if (fn) return fn();
|
|
6
|
-
throw new Error("Module not found in bundle: " +
|
|
6
|
+
throw new Error("Module not found in bundle: " + path2);
|
|
7
7
|
};
|
|
8
8
|
var __esm = (fn, res) => function __init() {
|
|
9
9
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
@@ -27,6 +27,13 @@ var init_classroomDB = __esm({
|
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
// src/impl/common/SyncStrategy.ts
|
|
31
|
+
var init_SyncStrategy = __esm({
|
|
32
|
+
"src/impl/common/SyncStrategy.ts"() {
|
|
33
|
+
"use strict";
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
30
37
|
// src/util/logger.ts
|
|
31
38
|
var isDevelopment, logger;
|
|
32
39
|
var init_logger = __esm({
|
|
@@ -72,23 +79,78 @@ var init_logger = __esm({
|
|
|
72
79
|
}
|
|
73
80
|
});
|
|
74
81
|
|
|
75
|
-
// src/
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
return dataLayerInstance;
|
|
81
|
-
}
|
|
82
|
-
var ENV, dataLayerInstance;
|
|
83
|
-
var init_factory = __esm({
|
|
84
|
-
"src/factory.ts"() {
|
|
82
|
+
// src/core/types/types-legacy.ts
|
|
83
|
+
var GuestUsername, log, DocType, DocTypePrefixes;
|
|
84
|
+
var init_types_legacy = __esm({
|
|
85
|
+
"src/core/types/types-legacy.ts"() {
|
|
85
86
|
"use strict";
|
|
86
87
|
init_logger();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
GuestUsername = "Guest";
|
|
89
|
+
log = (message) => {
|
|
90
|
+
logger.log(message);
|
|
90
91
|
};
|
|
91
|
-
|
|
92
|
+
DocType = /* @__PURE__ */ ((DocType2) => {
|
|
93
|
+
DocType2["DISPLAYABLE_DATA"] = "DISPLAYABLE_DATA";
|
|
94
|
+
DocType2["CARD"] = "CARD";
|
|
95
|
+
DocType2["DATASHAPE"] = "DATASHAPE";
|
|
96
|
+
DocType2["QUESTIONTYPE"] = "QUESTION";
|
|
97
|
+
DocType2["VIEW"] = "VIEW";
|
|
98
|
+
DocType2["PEDAGOGY"] = "PEDAGOGY";
|
|
99
|
+
DocType2["CARDRECORD"] = "CARDRECORD";
|
|
100
|
+
DocType2["SCHEDULED_CARD"] = "SCHEDULED_CARD";
|
|
101
|
+
DocType2["TAG"] = "TAG";
|
|
102
|
+
DocType2["NAVIGATION_STRATEGY"] = "NAVIGATION_STRATEGY";
|
|
103
|
+
return DocType2;
|
|
104
|
+
})(DocType || {});
|
|
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
|
+
};
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// src/core/util/index.ts
|
|
122
|
+
function areQuestionRecords(h) {
|
|
123
|
+
return isQuestionRecord(h.records[0]);
|
|
124
|
+
}
|
|
125
|
+
function isQuestionRecord(c) {
|
|
126
|
+
return c.userAnswer !== void 0;
|
|
127
|
+
}
|
|
128
|
+
function getCardHistoryID(courseID, cardID) {
|
|
129
|
+
return `${DocTypePrefixes["CARDRECORD" /* CARDRECORD */]}-${courseID}-${cardID}`;
|
|
130
|
+
}
|
|
131
|
+
function parseCardHistoryID(id) {
|
|
132
|
+
const split = id.split("-");
|
|
133
|
+
let error = "";
|
|
134
|
+
error += split.length === 3 ? "" : `
|
|
135
|
+
given ID has incorrect number of '-' characters`;
|
|
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 */]) {
|
|
139
|
+
return {
|
|
140
|
+
courseID: split[1],
|
|
141
|
+
cardID: split[2]
|
|
142
|
+
};
|
|
143
|
+
} else {
|
|
144
|
+
throw new Error("parseCardHistory Error:" + error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function docIsDeleted(e) {
|
|
148
|
+
return Boolean(e?.error === "not_found" && e?.reason === "deleted");
|
|
149
|
+
}
|
|
150
|
+
var init_util = __esm({
|
|
151
|
+
"src/core/util/index.ts"() {
|
|
152
|
+
"use strict";
|
|
153
|
+
init_types_legacy();
|
|
92
154
|
}
|
|
93
155
|
});
|
|
94
156
|
|
|
@@ -111,54 +173,93 @@ var init_pouchdb_setup = __esm({
|
|
|
111
173
|
}
|
|
112
174
|
});
|
|
113
175
|
|
|
114
|
-
// src/
|
|
115
|
-
var
|
|
116
|
-
|
|
117
|
-
"src/core/types/types-legacy.ts"() {
|
|
176
|
+
// src/util/tuiLogger.ts
|
|
177
|
+
var init_tuiLogger = __esm({
|
|
178
|
+
"src/util/tuiLogger.ts"() {
|
|
118
179
|
"use strict";
|
|
119
|
-
|
|
120
|
-
GuestUsername = "Guest";
|
|
121
|
-
log = (message) => {
|
|
122
|
-
logger.log(message);
|
|
123
|
-
};
|
|
124
|
-
DocType = /* @__PURE__ */ ((DocType2) => {
|
|
125
|
-
DocType2["DISPLAYABLE_DATA"] = "DISPLAYABLE_DATA";
|
|
126
|
-
DocType2["CARD"] = "CARD";
|
|
127
|
-
DocType2["DATASHAPE"] = "DATASHAPE";
|
|
128
|
-
DocType2["QUESTIONTYPE"] = "QUESTION";
|
|
129
|
-
DocType2["VIEW"] = "VIEW";
|
|
130
|
-
DocType2["PEDAGOGY"] = "PEDAGOGY";
|
|
131
|
-
DocType2["CARDRECORD"] = "CARDRECORD";
|
|
132
|
-
DocType2["SCHEDULED_CARD"] = "SCHEDULED_CARD";
|
|
133
|
-
DocType2["TAG"] = "TAG";
|
|
134
|
-
DocType2["NAVIGATION_STRATEGY"] = "NAVIGATION_STRATEGY";
|
|
135
|
-
return DocType2;
|
|
136
|
-
})(DocType || {});
|
|
137
|
-
cardHistoryPrefix = "cardH";
|
|
180
|
+
init_dataDirectory();
|
|
138
181
|
}
|
|
139
182
|
});
|
|
140
183
|
|
|
141
|
-
// src/
|
|
142
|
-
|
|
143
|
-
|
|
184
|
+
// src/util/dataDirectory.ts
|
|
185
|
+
import * as path from "path";
|
|
186
|
+
import * as os from "os";
|
|
187
|
+
function getAppDataDirectory() {
|
|
188
|
+
return path.join(os.homedir(), ".tuilder");
|
|
189
|
+
}
|
|
190
|
+
function getDbPath(dbName) {
|
|
191
|
+
return path.join(getAppDataDirectory(), dbName);
|
|
192
|
+
}
|
|
193
|
+
var init_dataDirectory = __esm({
|
|
194
|
+
"src/util/dataDirectory.ts"() {
|
|
144
195
|
"use strict";
|
|
145
|
-
|
|
146
|
-
init_factory();
|
|
147
|
-
init_logger();
|
|
148
|
-
logger.debug(`COURSELOOKUP FILE RUNNING`);
|
|
196
|
+
init_tuiLogger();
|
|
149
197
|
}
|
|
150
198
|
});
|
|
151
199
|
|
|
152
|
-
// src/impl/
|
|
153
|
-
|
|
154
|
-
|
|
200
|
+
// src/impl/common/userDBHelpers.ts
|
|
201
|
+
import moment from "moment";
|
|
202
|
+
function filterAllDocsByPrefix(db, prefix, opts) {
|
|
203
|
+
const options = {
|
|
204
|
+
startkey: prefix,
|
|
205
|
+
endkey: prefix + "\uFFF0",
|
|
206
|
+
include_docs: true
|
|
207
|
+
};
|
|
208
|
+
if (opts) {
|
|
209
|
+
Object.assign(options, opts);
|
|
210
|
+
}
|
|
211
|
+
return db.allDocs(options);
|
|
212
|
+
}
|
|
213
|
+
function getStartAndEndKeys(key) {
|
|
214
|
+
return {
|
|
215
|
+
startkey: key,
|
|
216
|
+
endkey: key + "\uFFF0"
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function getLocalUserDB(username) {
|
|
220
|
+
const dbName = `userdb-${username}`;
|
|
221
|
+
if (typeof window === "undefined") {
|
|
222
|
+
return new pouchdb_setup_default(getDbPath(dbName), {});
|
|
223
|
+
} else {
|
|
224
|
+
return new pouchdb_setup_default(dbName, {});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function scheduleCardReviewLocal(userDB, review) {
|
|
228
|
+
const now = moment.utc();
|
|
229
|
+
logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
|
|
230
|
+
void userDB.put({
|
|
231
|
+
_id: DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */] + review.time.format(REVIEW_TIME_FORMAT),
|
|
232
|
+
cardId: review.card_id,
|
|
233
|
+
reviewTime: review.time.toISOString(),
|
|
234
|
+
courseId: review.course_id,
|
|
235
|
+
scheduledAt: now.toISOString(),
|
|
236
|
+
scheduledFor: review.scheduledFor,
|
|
237
|
+
schedulingAgentId: review.schedulingAgentId
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
|
|
241
|
+
const reviewDoc = await userDB.get(reviewDocID);
|
|
242
|
+
userDB.remove(reviewDoc).then((res) => {
|
|
243
|
+
if (res.ok) {
|
|
244
|
+
log2(`Removed Review Doc: ${reviewDocID}`);
|
|
245
|
+
}
|
|
246
|
+
}).catch((err) => {
|
|
247
|
+
log2(`Failed to remove Review Doc: ${reviewDocID},
|
|
248
|
+
${JSON.stringify(err)}`);
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
var REVIEW_TIME_FORMAT, log2;
|
|
252
|
+
var init_userDBHelpers = __esm({
|
|
253
|
+
"src/impl/common/userDBHelpers.ts"() {
|
|
155
254
|
"use strict";
|
|
156
|
-
|
|
157
|
-
init_factory();
|
|
158
|
-
init_couch();
|
|
159
|
-
init_classroomDB2();
|
|
160
|
-
init_courseLookupDB();
|
|
255
|
+
init_core();
|
|
161
256
|
init_logger();
|
|
257
|
+
init_pouchdb_setup();
|
|
258
|
+
init_dataDirectory();
|
|
259
|
+
REVIEW_TIME_FORMAT = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
260
|
+
log2 = (s) => {
|
|
261
|
+
logger.info(s);
|
|
262
|
+
};
|
|
162
263
|
}
|
|
163
264
|
});
|
|
164
265
|
|
|
@@ -189,7 +290,10 @@ var init_updateQueue = __esm({
|
|
|
189
290
|
_className = "UpdateQueue";
|
|
190
291
|
pendingUpdates = {};
|
|
191
292
|
inprogressUpdates = {};
|
|
192
|
-
|
|
293
|
+
readDB;
|
|
294
|
+
// Database for read operations
|
|
295
|
+
writeDB;
|
|
296
|
+
// Database for write operations (local-first)
|
|
193
297
|
update(id, update) {
|
|
194
298
|
logger.debug(`Update requested on doc: ${id}`);
|
|
195
299
|
if (this.pendingUpdates[id]) {
|
|
@@ -199,24 +303,25 @@ var init_updateQueue = __esm({
|
|
|
199
303
|
}
|
|
200
304
|
return this.applyUpdates(id);
|
|
201
305
|
}
|
|
202
|
-
constructor(
|
|
306
|
+
constructor(readDB, writeDB) {
|
|
203
307
|
super();
|
|
204
|
-
this.
|
|
308
|
+
this.readDB = readDB;
|
|
309
|
+
this.writeDB = writeDB || readDB;
|
|
205
310
|
logger.debug(`UpdateQ initialized...`);
|
|
206
|
-
void this.
|
|
311
|
+
void this.readDB.info().then((i) => {
|
|
207
312
|
logger.debug(`db info: ${JSON.stringify(i)}`);
|
|
208
313
|
});
|
|
209
314
|
}
|
|
210
315
|
async applyUpdates(id) {
|
|
211
316
|
logger.debug(`Applying updates on doc: ${id}`);
|
|
212
317
|
if (this.inprogressUpdates[id]) {
|
|
213
|
-
await this.
|
|
318
|
+
await this.readDB.info();
|
|
214
319
|
return this.applyUpdates(id);
|
|
215
320
|
} else {
|
|
216
321
|
if (this.pendingUpdates[id] && this.pendingUpdates[id].length > 0) {
|
|
217
322
|
this.inprogressUpdates[id] = true;
|
|
218
323
|
try {
|
|
219
|
-
let doc = await this.
|
|
324
|
+
let doc = await this.readDB.get(id);
|
|
220
325
|
logger.debug(`Retrieved doc: ${id}`);
|
|
221
326
|
while (this.pendingUpdates[id].length !== 0) {
|
|
222
327
|
const update = this.pendingUpdates[id].splice(0, 1)[0];
|
|
@@ -229,7 +334,7 @@ var init_updateQueue = __esm({
|
|
|
229
334
|
};
|
|
230
335
|
}
|
|
231
336
|
}
|
|
232
|
-
await this.
|
|
337
|
+
await this.writeDB.put(doc);
|
|
233
338
|
logger.debug(`Put doc: ${id}`);
|
|
234
339
|
if (this.pendingUpdates[id].length === 0) {
|
|
235
340
|
this.inprogressUpdates[id] = false;
|
|
@@ -255,6 +360,60 @@ var init_updateQueue = __esm({
|
|
|
255
360
|
}
|
|
256
361
|
});
|
|
257
362
|
|
|
363
|
+
// src/impl/couch/user-course-relDB.ts
|
|
364
|
+
import moment2 from "moment";
|
|
365
|
+
var UsrCrsData;
|
|
366
|
+
var init_user_course_relDB = __esm({
|
|
367
|
+
"src/impl/couch/user-course-relDB.ts"() {
|
|
368
|
+
"use strict";
|
|
369
|
+
init_logger();
|
|
370
|
+
UsrCrsData = class {
|
|
371
|
+
user;
|
|
372
|
+
_courseId;
|
|
373
|
+
constructor(user, courseId) {
|
|
374
|
+
this.user = user;
|
|
375
|
+
this._courseId = courseId;
|
|
376
|
+
}
|
|
377
|
+
async getReviewsForcast(daysCount) {
|
|
378
|
+
const time = moment2.utc().add(daysCount, "days");
|
|
379
|
+
return this.getReviewstoDate(time);
|
|
380
|
+
}
|
|
381
|
+
async getPendingReviews() {
|
|
382
|
+
const now = moment2.utc();
|
|
383
|
+
return this.getReviewstoDate(now);
|
|
384
|
+
}
|
|
385
|
+
async getScheduledReviewCount() {
|
|
386
|
+
return (await this.getPendingReviews()).length;
|
|
387
|
+
}
|
|
388
|
+
async getCourseSettings() {
|
|
389
|
+
const regDoc = await this.user.getCourseRegistrationsDoc();
|
|
390
|
+
const crsDoc = regDoc.courses.find((c) => c.courseID === this._courseId);
|
|
391
|
+
if (crsDoc && crsDoc.settings) {
|
|
392
|
+
return crsDoc.settings;
|
|
393
|
+
} else {
|
|
394
|
+
logger.warn(`no settings found during lookup on course ${this._courseId}`);
|
|
395
|
+
return {};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
updateCourseSettings(updates) {
|
|
399
|
+
if ("updateCourseSettings" in this.user) {
|
|
400
|
+
void this.user.updateCourseSettings(this._courseId, updates);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async getReviewstoDate(targetDate) {
|
|
404
|
+
const allReviews = await this.user.getPendingReviews(this._courseId);
|
|
405
|
+
logger.debug(
|
|
406
|
+
`Fetching ${this.user.getUsername()}'s scheduled reviews for course ${this._courseId}.`
|
|
407
|
+
);
|
|
408
|
+
return allReviews.filter((review) => {
|
|
409
|
+
const reviewTime = moment2.utc(review.reviewTime);
|
|
410
|
+
return targetDate.isAfter(reviewTime);
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
|
|
258
417
|
// src/impl/couch/clientCache.ts
|
|
259
418
|
async function GET_CACHED(k, f) {
|
|
260
419
|
if (CLIENT_CACHE[k]) {
|
|
@@ -274,30 +433,236 @@ var init_clientCache = __esm({
|
|
|
274
433
|
}
|
|
275
434
|
});
|
|
276
435
|
|
|
277
|
-
// src/
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
436
|
+
// src/impl/couch/courseAPI.ts
|
|
437
|
+
import { NameSpacer } from "@vue-skuilder/common";
|
|
438
|
+
import { blankCourseElo, toCourseElo } from "@vue-skuilder/common";
|
|
439
|
+
import { prepareNote55 } from "@vue-skuilder/common";
|
|
440
|
+
import { v4 as uuidv4 } from "uuid";
|
|
441
|
+
async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo()) {
|
|
442
|
+
const db = getCourseDB(courseID);
|
|
443
|
+
const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
|
|
444
|
+
const _id = `${DocTypePrefixes["DISPLAYABLE_DATA" /* DISPLAYABLE_DATA */]}-${uuidv4()}`;
|
|
445
|
+
const result = await db.put({ ...payload, _id });
|
|
446
|
+
const dataShapeId = NameSpacer.getDataShapeString({
|
|
447
|
+
course: codeCourse,
|
|
448
|
+
dataShape: shape.name
|
|
449
|
+
});
|
|
450
|
+
if (result.ok) {
|
|
451
|
+
try {
|
|
452
|
+
await createCards(courseID, dataShapeId, result.id, tags, elo, author);
|
|
453
|
+
} catch (error) {
|
|
454
|
+
let errorMessage = "Unknown error";
|
|
455
|
+
if (error instanceof Error) {
|
|
456
|
+
errorMessage = error.message;
|
|
457
|
+
} else if (error && typeof error === "object" && "reason" in error) {
|
|
458
|
+
errorMessage = error.reason;
|
|
459
|
+
} else if (error && typeof error === "object" && "message" in error) {
|
|
460
|
+
errorMessage = error.message;
|
|
461
|
+
} else {
|
|
462
|
+
errorMessage = String(error);
|
|
294
463
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
464
|
+
logger.error(`[addNote55] Failed to create cards for note ${result.id}: ${errorMessage}`);
|
|
465
|
+
result.cardCreationFailed = true;
|
|
466
|
+
result.cardCreationError = errorMessage;
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
logger.error(`[addNote55] Error adding note. Result: ${JSON.stringify(result)}`);
|
|
470
|
+
}
|
|
471
|
+
return result;
|
|
472
|
+
}
|
|
473
|
+
async function createCards(courseID, datashapeID, noteID, tags, elo = blankCourseElo(), author) {
|
|
474
|
+
const cfg = await getCredentialledCourseConfig(courseID);
|
|
475
|
+
const dsDescriptor = NameSpacer.getDataShapeDescriptor(datashapeID);
|
|
476
|
+
let questionViewTypes = [];
|
|
477
|
+
for (const ds of cfg.dataShapes) {
|
|
478
|
+
if (ds.name === datashapeID) {
|
|
479
|
+
questionViewTypes = ds.questionTypes;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (questionViewTypes.length === 0) {
|
|
483
|
+
const errorMsg = `No questionViewTypes found for datashapeID: ${datashapeID} in course config. Cards cannot be created.`;
|
|
484
|
+
logger.error(errorMsg);
|
|
485
|
+
throw new Error(errorMsg);
|
|
486
|
+
}
|
|
487
|
+
for (const questionView of questionViewTypes) {
|
|
488
|
+
await createCard(questionView, courseID, dsDescriptor, noteID, tags, elo, author);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags, elo = blankCourseElo(), author) {
|
|
492
|
+
const qDescriptor = NameSpacer.getQuestionDescriptor(questionViewName);
|
|
493
|
+
const cfg = await getCredentialledCourseConfig(courseID);
|
|
494
|
+
for (const rQ of cfg.questionTypes) {
|
|
495
|
+
if (rQ.name === questionViewName) {
|
|
496
|
+
for (const view of rQ.viewList) {
|
|
497
|
+
await addCard(
|
|
498
|
+
courseID,
|
|
499
|
+
dsDescriptor.course,
|
|
500
|
+
[noteID],
|
|
501
|
+
NameSpacer.getViewString({
|
|
502
|
+
course: qDescriptor.course,
|
|
503
|
+
questionType: qDescriptor.questionType,
|
|
504
|
+
view
|
|
505
|
+
}),
|
|
506
|
+
elo,
|
|
507
|
+
tags,
|
|
508
|
+
author
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
|
|
515
|
+
const db = getCourseDB(courseID);
|
|
516
|
+
const _id = `${DocTypePrefixes["CARD" /* CARD */]}-${uuidv4()}`;
|
|
517
|
+
const card = await db.put({
|
|
518
|
+
_id,
|
|
519
|
+
course,
|
|
520
|
+
id_displayable_data,
|
|
521
|
+
id_view,
|
|
522
|
+
docType: "CARD" /* CARD */,
|
|
523
|
+
elo: elo || toCourseElo(990 + Math.round(20 * Math.random())),
|
|
524
|
+
author
|
|
525
|
+
});
|
|
526
|
+
for (const tag of tags) {
|
|
527
|
+
logger.info(`adding tag: ${tag} to card ${card.id}`);
|
|
528
|
+
await addTagToCard(courseID, card.id, tag, author, false);
|
|
529
|
+
}
|
|
530
|
+
return card;
|
|
531
|
+
}
|
|
532
|
+
async function getCredentialledCourseConfig(courseID) {
|
|
533
|
+
try {
|
|
534
|
+
const db = getCourseDB(courseID);
|
|
535
|
+
const ret = await db.get("CourseConfig");
|
|
536
|
+
ret.courseID = courseID;
|
|
537
|
+
logger.info(`Returning course config: ${JSON.stringify(ret)}`);
|
|
538
|
+
return ret;
|
|
539
|
+
} catch (e) {
|
|
540
|
+
logger.error(`Error fetching config for ${courseID}:`, e);
|
|
541
|
+
throw e;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async function addTagToCard(courseID, cardID, tagID, author, updateELO = true) {
|
|
545
|
+
const prefixedTagID = getTagID(tagID);
|
|
546
|
+
const courseDB = getCourseDB(courseID);
|
|
547
|
+
const courseApi = new CourseDB(courseID, async () => {
|
|
548
|
+
const dummySyncStrategy = {
|
|
549
|
+
setupRemoteDB: () => null,
|
|
550
|
+
startSync: () => {
|
|
551
|
+
},
|
|
552
|
+
canCreateAccount: () => false,
|
|
553
|
+
canAuthenticate: () => false,
|
|
554
|
+
getCurrentUsername: async () => "DummyUser"
|
|
555
|
+
};
|
|
556
|
+
return BaseUser.Dummy(dummySyncStrategy);
|
|
557
|
+
});
|
|
558
|
+
try {
|
|
559
|
+
logger.info(`Applying tag ${tagID} to card ${courseID + "-" + cardID}...`);
|
|
560
|
+
const tag = await courseDB.get(prefixedTagID);
|
|
561
|
+
if (!tag.taggedCards.includes(cardID)) {
|
|
562
|
+
tag.taggedCards.push(cardID);
|
|
563
|
+
if (updateELO) {
|
|
564
|
+
try {
|
|
565
|
+
const eloData = await courseApi.getCardEloData([cardID]);
|
|
566
|
+
const elo = eloData[0];
|
|
567
|
+
elo.tags[tagID] = {
|
|
568
|
+
count: 0,
|
|
569
|
+
score: elo.global.score
|
|
570
|
+
// todo: or 1000?
|
|
571
|
+
};
|
|
572
|
+
await updateCardElo(courseID, cardID, elo);
|
|
573
|
+
} catch (error) {
|
|
574
|
+
logger.error("Failed to update ELO data for card:", cardID, error);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
return courseDB.put(tag);
|
|
578
|
+
} else throw new AlreadyTaggedErr(`Card ${cardID} is already tagged with ${tagID}`);
|
|
579
|
+
} catch (e) {
|
|
580
|
+
if (e instanceof AlreadyTaggedErr) {
|
|
581
|
+
throw e;
|
|
582
|
+
}
|
|
583
|
+
await createTag(courseID, tagID, author);
|
|
584
|
+
return addTagToCard(courseID, cardID, tagID, author, updateELO);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
async function updateCardElo(courseID, cardID, elo) {
|
|
588
|
+
if (elo) {
|
|
589
|
+
const cDB = getCourseDB(courseID);
|
|
590
|
+
const card = await cDB.get(cardID);
|
|
591
|
+
logger.debug(`Replacing ${JSON.stringify(card.elo)} with ${JSON.stringify(elo)}`);
|
|
592
|
+
card.elo = elo;
|
|
593
|
+
return cDB.put(card);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function getTagID(tagName) {
|
|
597
|
+
const tagPrefix = "TAG" /* TAG */.valueOf() + "-";
|
|
598
|
+
if (tagName.indexOf(tagPrefix) === 0) {
|
|
599
|
+
return tagName;
|
|
600
|
+
} else {
|
|
601
|
+
return tagPrefix + tagName;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
function getCourseDB(courseID) {
|
|
605
|
+
const dbName = `coursedb-${courseID}`;
|
|
606
|
+
return new pouchdb_setup_default(
|
|
607
|
+
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
608
|
+
pouchDBincludeCredentialsConfig
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
var AlreadyTaggedErr;
|
|
612
|
+
var init_courseAPI = __esm({
|
|
613
|
+
"src/impl/couch/courseAPI.ts"() {
|
|
614
|
+
"use strict";
|
|
615
|
+
init_pouchdb_setup();
|
|
616
|
+
init_couch();
|
|
617
|
+
init_factory();
|
|
618
|
+
init_courseDB();
|
|
619
|
+
init_types_legacy();
|
|
620
|
+
init_common();
|
|
621
|
+
init_logger();
|
|
622
|
+
AlreadyTaggedErr = class extends Error {
|
|
623
|
+
constructor(message) {
|
|
624
|
+
super(message);
|
|
625
|
+
this.name = "AlreadyTaggedErr";
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// src/impl/couch/courseLookupDB.ts
|
|
632
|
+
var init_courseLookupDB = __esm({
|
|
633
|
+
"src/impl/couch/courseLookupDB.ts"() {
|
|
634
|
+
"use strict";
|
|
635
|
+
init_pouchdb_setup();
|
|
636
|
+
init_factory();
|
|
637
|
+
init_logger();
|
|
638
|
+
logger.debug(`COURSELOOKUP FILE RUNNING`);
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// src/core/navigators/elo.ts
|
|
643
|
+
var elo_exports = {};
|
|
644
|
+
__export(elo_exports, {
|
|
645
|
+
default: () => ELONavigator
|
|
646
|
+
});
|
|
647
|
+
var ELONavigator;
|
|
648
|
+
var init_elo = __esm({
|
|
649
|
+
"src/core/navigators/elo.ts"() {
|
|
650
|
+
"use strict";
|
|
651
|
+
init_navigators();
|
|
652
|
+
ELONavigator = class extends ContentNavigator {
|
|
653
|
+
user;
|
|
654
|
+
course;
|
|
655
|
+
constructor(user, course) {
|
|
656
|
+
super();
|
|
657
|
+
this.user = user;
|
|
658
|
+
this.course = course;
|
|
659
|
+
}
|
|
660
|
+
async getPendingReviews() {
|
|
661
|
+
const reviews = await this.user.getPendingReviews(this.course.getCourseID());
|
|
662
|
+
const elo = await this.course.getCardEloData(reviews.map((r) => r.cardId));
|
|
663
|
+
const ratedReviews = reviews.map((r, i) => {
|
|
664
|
+
const ratedR = {
|
|
665
|
+
...r,
|
|
301
666
|
...elo[i]
|
|
302
667
|
};
|
|
303
668
|
return ratedR;
|
|
@@ -397,16 +762,16 @@ var init_navigators = __esm({
|
|
|
397
762
|
import {
|
|
398
763
|
EloToNumber,
|
|
399
764
|
Status,
|
|
400
|
-
blankCourseElo,
|
|
401
|
-
toCourseElo
|
|
765
|
+
blankCourseElo as blankCourseElo2,
|
|
766
|
+
toCourseElo as toCourseElo2
|
|
402
767
|
} from "@vue-skuilder/common";
|
|
403
768
|
function randIntWeightedTowardZero(n) {
|
|
404
769
|
return Math.floor(Math.random() * Math.random() * Math.random() * n);
|
|
405
770
|
}
|
|
406
771
|
async function getCourseTagStubs(courseID) {
|
|
407
772
|
logger.debug(`Getting tag stubs for course: ${courseID}`);
|
|
408
|
-
const stubs = await
|
|
409
|
-
|
|
773
|
+
const stubs = await filterAllDocsByPrefix2(
|
|
774
|
+
getCourseDB2(courseID),
|
|
410
775
|
"TAG" /* TAG */.valueOf() + "-"
|
|
411
776
|
);
|
|
412
777
|
stubs.rows.forEach((row) => {
|
|
@@ -417,7 +782,7 @@ async function getCourseTagStubs(courseID) {
|
|
|
417
782
|
async function createTag(courseID, tagName, author) {
|
|
418
783
|
logger.debug(`Creating tag: ${tagName}...`);
|
|
419
784
|
const tagID = getTagID(tagName);
|
|
420
|
-
const courseDB =
|
|
785
|
+
const courseDB = getCourseDB2(courseID);
|
|
421
786
|
const resp = await courseDB.put({
|
|
422
787
|
course: courseID,
|
|
423
788
|
docType: "TAG" /* TAG */,
|
|
@@ -432,19 +797,19 @@ async function createTag(courseID, tagName, author) {
|
|
|
432
797
|
}
|
|
433
798
|
async function updateTag(tag) {
|
|
434
799
|
const prior = await getTag(tag.course, tag.name);
|
|
435
|
-
return await
|
|
800
|
+
return await getCourseDB2(tag.course).put({
|
|
436
801
|
...tag,
|
|
437
802
|
_rev: prior._rev
|
|
438
803
|
});
|
|
439
804
|
}
|
|
440
805
|
async function getTag(courseID, tagName) {
|
|
441
806
|
const tagID = getTagID(tagName);
|
|
442
|
-
const courseDB =
|
|
807
|
+
const courseDB = getCourseDB2(courseID);
|
|
443
808
|
return courseDB.get(tagID);
|
|
444
809
|
}
|
|
445
810
|
async function removeTagFromCard(courseID, cardID, tagID) {
|
|
446
811
|
tagID = getTagID(tagID);
|
|
447
|
-
const courseDB =
|
|
812
|
+
const courseDB = getCourseDB2(courseID);
|
|
448
813
|
const tag = await courseDB.get(tagID);
|
|
449
814
|
tag.taggedCards = tag.taggedCards.filter((taggedID) => {
|
|
450
815
|
return cardID !== taggedID;
|
|
@@ -452,7 +817,7 @@ async function removeTagFromCard(courseID, cardID, tagID) {
|
|
|
452
817
|
return courseDB.put(tag);
|
|
453
818
|
}
|
|
454
819
|
async function getAppliedTags(id_course, id_card) {
|
|
455
|
-
const db =
|
|
820
|
+
const db = getCourseDB2(id_course);
|
|
456
821
|
const result = await db.query("getTags", {
|
|
457
822
|
startkey: id_card,
|
|
458
823
|
endkey: id_card
|
|
@@ -465,7 +830,7 @@ async function updateCredentialledCourseConfig(courseID, config) {
|
|
|
465
830
|
|
|
466
831
|
${JSON.stringify(config)}
|
|
467
832
|
`);
|
|
468
|
-
const db =
|
|
833
|
+
const db = getCourseDB2(courseID);
|
|
469
834
|
const old = await getCredentialledCourseConfig(courseID);
|
|
470
835
|
return await db.put({
|
|
471
836
|
...config,
|
|
@@ -497,7 +862,7 @@ var init_courseDB = __esm({
|
|
|
497
862
|
updateQueue;
|
|
498
863
|
constructor(id, userLookup) {
|
|
499
864
|
this.id = id;
|
|
500
|
-
this.db =
|
|
865
|
+
this.db = getCourseDB2(this.id);
|
|
501
866
|
this._getCurrentUser = userLookup;
|
|
502
867
|
this.updateQueue = new UpdateQueue(this.db);
|
|
503
868
|
}
|
|
@@ -553,14 +918,14 @@ var init_courseDB = __esm({
|
|
|
553
918
|
docs.rows.forEach((r) => {
|
|
554
919
|
if (isSuccessRow(r)) {
|
|
555
920
|
if (r.doc && r.doc.elo) {
|
|
556
|
-
ret.push(
|
|
921
|
+
ret.push(toCourseElo2(r.doc.elo));
|
|
557
922
|
} else {
|
|
558
923
|
logger.warn("no elo data for card: " + r.id);
|
|
559
|
-
ret.push(
|
|
924
|
+
ret.push(blankCourseElo2());
|
|
560
925
|
}
|
|
561
926
|
} else {
|
|
562
927
|
logger.warn("no elo data for card: " + JSON.stringify(r));
|
|
563
|
-
ret.push(
|
|
928
|
+
ret.push(blankCourseElo2());
|
|
564
929
|
}
|
|
565
930
|
});
|
|
566
931
|
return ret;
|
|
@@ -713,7 +1078,7 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
713
1078
|
async getCourseTagStubs() {
|
|
714
1079
|
return getCourseTagStubs(this.id);
|
|
715
1080
|
}
|
|
716
|
-
async addNote(codeCourse, shape, data, author, tags, uploads, elo =
|
|
1081
|
+
async addNote(codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo2()) {
|
|
717
1082
|
try {
|
|
718
1083
|
const resp = await addNote55(this.id, codeCourse, shape, data, author, tags, uploads, elo);
|
|
719
1084
|
if (resp.ok) {
|
|
@@ -901,223 +1266,298 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
901
1266
|
}
|
|
902
1267
|
});
|
|
903
1268
|
|
|
904
|
-
// src/impl/
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
});
|
|
910
|
-
|
|
911
|
-
// src/core/util/index.ts
|
|
912
|
-
function areQuestionRecords(h) {
|
|
913
|
-
return isQuestionRecord(h.records[0]);
|
|
914
|
-
}
|
|
915
|
-
function isQuestionRecord(c) {
|
|
916
|
-
return c.userAnswer !== void 0;
|
|
917
|
-
}
|
|
918
|
-
function getCardHistoryID(courseID, cardID) {
|
|
919
|
-
return `${cardHistoryPrefix}-${courseID}-${cardID}`;
|
|
920
|
-
}
|
|
921
|
-
function parseCardHistoryID(id) {
|
|
922
|
-
const split = id.split("-");
|
|
923
|
-
let error = "";
|
|
924
|
-
error += split.length === 3 ? "" : `
|
|
925
|
-
given ID has incorrect number of '-' characters`;
|
|
926
|
-
error += split[0] === cardHistoryPrefix ? "" : `
|
|
927
|
-
given ID does not start with ${cardHistoryPrefix}`;
|
|
928
|
-
if (split.length === 3 && split[0] === cardHistoryPrefix) {
|
|
929
|
-
return {
|
|
930
|
-
courseID: split[1],
|
|
931
|
-
cardID: split[2]
|
|
932
|
-
};
|
|
933
|
-
} else {
|
|
934
|
-
throw new Error("parseCardHistory Error:" + error);
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
function docIsDeleted(e) {
|
|
938
|
-
return Boolean(e?.error === "not_found" && e?.reason === "deleted");
|
|
939
|
-
}
|
|
940
|
-
var init_util = __esm({
|
|
941
|
-
"src/core/util/index.ts"() {
|
|
942
|
-
"use strict";
|
|
943
|
-
init_types_legacy();
|
|
944
|
-
}
|
|
945
|
-
});
|
|
946
|
-
|
|
947
|
-
// src/impl/common/userDBHelpers.ts
|
|
948
|
-
import moment from "moment";
|
|
949
|
-
function filterAllDocsByPrefix2(db, prefix, opts) {
|
|
950
|
-
const options = {
|
|
951
|
-
startkey: prefix,
|
|
952
|
-
endkey: prefix + "\uFFF0",
|
|
953
|
-
include_docs: true
|
|
954
|
-
};
|
|
955
|
-
if (opts) {
|
|
956
|
-
Object.assign(options, opts);
|
|
957
|
-
}
|
|
958
|
-
return db.allDocs(options);
|
|
959
|
-
}
|
|
960
|
-
function getStartAndEndKeys2(key) {
|
|
961
|
-
return {
|
|
962
|
-
startkey: key,
|
|
963
|
-
endkey: key + "\uFFF0"
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
function getLocalUserDB(username) {
|
|
967
|
-
return new pouchdb_setup_default(`userdb-${username}`, {});
|
|
968
|
-
}
|
|
969
|
-
function scheduleCardReviewLocal(userDB, review) {
|
|
970
|
-
const now = moment.utc();
|
|
971
|
-
logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
|
|
972
|
-
void userDB.put({
|
|
973
|
-
_id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
|
|
974
|
-
cardId: review.card_id,
|
|
975
|
-
reviewTime: review.time,
|
|
976
|
-
courseId: review.course_id,
|
|
977
|
-
scheduledAt: now,
|
|
978
|
-
scheduledFor: review.scheduledFor,
|
|
979
|
-
schedulingAgentId: review.schedulingAgentId
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
|
|
983
|
-
const reviewDoc = await userDB.get(reviewDocID);
|
|
984
|
-
userDB.remove(reviewDoc).then((res) => {
|
|
985
|
-
if (res.ok) {
|
|
986
|
-
log2(`Removed Review Doc: ${reviewDocID}`);
|
|
987
|
-
}
|
|
988
|
-
}).catch((err) => {
|
|
989
|
-
log2(`Failed to remove Review Doc: ${reviewDocID},
|
|
990
|
-
${JSON.stringify(err)}`);
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
var REVIEW_PREFIX, REVIEW_TIME_FORMAT, log2;
|
|
994
|
-
var init_userDBHelpers = __esm({
|
|
995
|
-
"src/impl/common/userDBHelpers.ts"() {
|
|
1269
|
+
// src/impl/couch/classroomDB.ts
|
|
1270
|
+
import moment3 from "moment";
|
|
1271
|
+
var CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB;
|
|
1272
|
+
var init_classroomDB2 = __esm({
|
|
1273
|
+
"src/impl/couch/classroomDB.ts"() {
|
|
996
1274
|
"use strict";
|
|
1275
|
+
init_factory();
|
|
997
1276
|
init_logger();
|
|
998
1277
|
init_pouchdb_setup();
|
|
999
|
-
REVIEW_PREFIX = "card_review_";
|
|
1000
|
-
REVIEW_TIME_FORMAT = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
1001
|
-
log2 = (s) => {
|
|
1002
|
-
logger.info(s);
|
|
1003
|
-
};
|
|
1004
|
-
}
|
|
1005
|
-
});
|
|
1006
|
-
|
|
1007
|
-
// src/impl/couch/user-course-relDB.ts
|
|
1008
|
-
import moment2 from "moment";
|
|
1009
|
-
var UsrCrsData;
|
|
1010
|
-
var init_user_course_relDB = __esm({
|
|
1011
|
-
"src/impl/couch/user-course-relDB.ts"() {
|
|
1012
|
-
"use strict";
|
|
1013
1278
|
init_couch();
|
|
1014
1279
|
init_courseDB();
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
this.
|
|
1280
|
+
CLASSROOM_CONFIG = "ClassroomConfig";
|
|
1281
|
+
ClassroomDBBase = class {
|
|
1282
|
+
_id;
|
|
1283
|
+
_db;
|
|
1284
|
+
_cfg;
|
|
1285
|
+
_initComplete = false;
|
|
1286
|
+
_content_prefix = "content";
|
|
1287
|
+
get _content_searchkeys() {
|
|
1288
|
+
return getStartAndEndKeys2(this._content_prefix);
|
|
1024
1289
|
}
|
|
1025
|
-
async
|
|
1026
|
-
|
|
1027
|
-
|
|
1290
|
+
async getAssignedContent() {
|
|
1291
|
+
logger.info(`Getting assigned content...`);
|
|
1292
|
+
const docRows = await this._db.allDocs({
|
|
1293
|
+
startkey: this._content_prefix,
|
|
1294
|
+
endkey: this._content_prefix + `\uFFF0`,
|
|
1295
|
+
include_docs: true
|
|
1296
|
+
});
|
|
1297
|
+
const ret = docRows.rows.map((row) => {
|
|
1298
|
+
return row.doc;
|
|
1299
|
+
});
|
|
1300
|
+
return ret;
|
|
1028
1301
|
}
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1302
|
+
getContentId(content) {
|
|
1303
|
+
if (content.type === "tag") {
|
|
1304
|
+
return `${this._content_prefix}-${content.courseID}-${content.tagID}`;
|
|
1305
|
+
} else {
|
|
1306
|
+
return `${this._content_prefix}-${content.courseID}`;
|
|
1307
|
+
}
|
|
1032
1308
|
}
|
|
1033
|
-
|
|
1034
|
-
return
|
|
1309
|
+
get ready() {
|
|
1310
|
+
return this._initComplete;
|
|
1035
1311
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1312
|
+
getConfig() {
|
|
1313
|
+
return this._cfg;
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
StudentClassroomDB = class _StudentClassroomDB extends ClassroomDBBase {
|
|
1317
|
+
// private readonly _prefix: string = 'content';
|
|
1318
|
+
userMessages;
|
|
1319
|
+
_user;
|
|
1320
|
+
constructor(classID, user) {
|
|
1321
|
+
super();
|
|
1322
|
+
this._id = classID;
|
|
1323
|
+
this._user = user;
|
|
1324
|
+
}
|
|
1325
|
+
async init() {
|
|
1326
|
+
const dbName = `classdb-student-${this._id}`;
|
|
1327
|
+
this._db = new pouchdb_setup_default(
|
|
1328
|
+
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
1329
|
+
pouchDBincludeCredentialsConfig
|
|
1330
|
+
);
|
|
1331
|
+
try {
|
|
1332
|
+
const cfg = await this._db.get(CLASSROOM_CONFIG);
|
|
1333
|
+
this._cfg = cfg;
|
|
1334
|
+
this.userMessages = this._db.changes({
|
|
1335
|
+
since: "now",
|
|
1336
|
+
live: true,
|
|
1337
|
+
include_docs: true
|
|
1338
|
+
});
|
|
1339
|
+
this._initComplete = true;
|
|
1340
|
+
return;
|
|
1341
|
+
} catch (e) {
|
|
1342
|
+
throw new Error(`Error in StudentClassroomDB constructor: ${JSON.stringify(e)}`);
|
|
1044
1343
|
}
|
|
1045
1344
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1345
|
+
static async factory(classID, user) {
|
|
1346
|
+
const ret = new _StudentClassroomDB(classID, user);
|
|
1347
|
+
await ret.init();
|
|
1348
|
+
return ret;
|
|
1048
1349
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1350
|
+
setChangeFcn(f) {
|
|
1351
|
+
void this.userMessages.on("change", f);
|
|
1352
|
+
}
|
|
1353
|
+
async getPendingReviews() {
|
|
1354
|
+
const u = this._user;
|
|
1355
|
+
return (await u.getPendingReviews()).filter((r) => r.scheduledFor === "classroom" && r.schedulingAgentId === this._id).map((r) => {
|
|
1356
|
+
return {
|
|
1357
|
+
...r,
|
|
1358
|
+
qualifiedID: `${r.courseId}-${r.cardId}`,
|
|
1359
|
+
courseID: r.courseId,
|
|
1360
|
+
cardID: r.cardId,
|
|
1361
|
+
contentSourceType: "classroom",
|
|
1362
|
+
contentSourceID: this._id,
|
|
1363
|
+
reviewID: r._id,
|
|
1364
|
+
status: "review"
|
|
1365
|
+
};
|
|
1055
1366
|
});
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
);
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1367
|
+
}
|
|
1368
|
+
async getNewCards() {
|
|
1369
|
+
const activeCards = await this._user.getActiveCards();
|
|
1370
|
+
const now = moment3.utc();
|
|
1371
|
+
const assigned = await this.getAssignedContent();
|
|
1372
|
+
const due = assigned.filter((c) => now.isAfter(moment3.utc(c.activeOn, REVIEW_TIME_FORMAT2)));
|
|
1373
|
+
logger.info(`Due content: ${JSON.stringify(due)}`);
|
|
1374
|
+
let ret = [];
|
|
1375
|
+
for (let i = 0; i < due.length; i++) {
|
|
1376
|
+
const content = due[i];
|
|
1377
|
+
if (content.type === "course") {
|
|
1378
|
+
const db = new CourseDB(content.courseID, async () => this._user);
|
|
1379
|
+
ret = ret.concat(await db.getNewCards());
|
|
1380
|
+
} else if (content.type === "tag") {
|
|
1381
|
+
const tagDoc = await getTag(content.courseID, content.tagID);
|
|
1382
|
+
ret = ret.concat(
|
|
1383
|
+
tagDoc.taggedCards.map((c) => {
|
|
1384
|
+
return {
|
|
1385
|
+
courseID: content.courseID,
|
|
1386
|
+
cardID: c,
|
|
1387
|
+
qualifiedID: `${content.courseID}-${c}`,
|
|
1388
|
+
contentSourceType: "classroom",
|
|
1389
|
+
contentSourceID: this._id,
|
|
1390
|
+
status: "new"
|
|
1391
|
+
};
|
|
1392
|
+
})
|
|
1393
|
+
);
|
|
1394
|
+
} else if (content.type === "card") {
|
|
1395
|
+
ret.push(await getCourseDB2(content.courseID).get(content.cardID));
|
|
1067
1396
|
}
|
|
1068
|
-
}
|
|
1397
|
+
}
|
|
1398
|
+
logger.info(`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => c.qualifiedID)}`);
|
|
1399
|
+
return ret.filter((c) => {
|
|
1400
|
+
if (activeCards.some((ac) => c.qualifiedID.includes(ac))) {
|
|
1401
|
+
return false;
|
|
1402
|
+
} else {
|
|
1403
|
+
return true;
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1069
1406
|
}
|
|
1070
1407
|
};
|
|
1071
1408
|
}
|
|
1072
1409
|
});
|
|
1073
1410
|
|
|
1074
|
-
// src/impl/
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
await getLocalUserDB(user).put({
|
|
1085
|
-
_id: userClassroomsDoc,
|
|
1086
|
-
registrations: []
|
|
1087
|
-
});
|
|
1088
|
-
ret = await getOrCreateClassroomRegistrationsDoc(user);
|
|
1089
|
-
} else {
|
|
1090
|
-
const errorDetails = {
|
|
1091
|
-
name: err.name,
|
|
1092
|
-
status: err.status,
|
|
1093
|
-
message: err.message,
|
|
1094
|
-
reason: err.reason,
|
|
1095
|
-
error: err.error
|
|
1096
|
-
};
|
|
1097
|
-
logger.error(
|
|
1098
|
-
"Database error in getOrCreateClassroomRegistrationsDoc (standalone function):",
|
|
1099
|
-
errorDetails
|
|
1100
|
-
);
|
|
1101
|
-
throw new Error(
|
|
1102
|
-
`Database error accessing classroom registrations: ${err.message || err.name || "Unknown error"} (status: ${err.status})`
|
|
1103
|
-
);
|
|
1104
|
-
}
|
|
1411
|
+
// src/impl/couch/adminDB.ts
|
|
1412
|
+
var init_adminDB2 = __esm({
|
|
1413
|
+
"src/impl/couch/adminDB.ts"() {
|
|
1414
|
+
"use strict";
|
|
1415
|
+
init_pouchdb_setup();
|
|
1416
|
+
init_factory();
|
|
1417
|
+
init_couch();
|
|
1418
|
+
init_classroomDB2();
|
|
1419
|
+
init_courseLookupDB();
|
|
1420
|
+
init_logger();
|
|
1105
1421
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1422
|
+
});
|
|
1423
|
+
|
|
1424
|
+
// src/impl/couch/auth.ts
|
|
1425
|
+
var init_auth = __esm({
|
|
1426
|
+
"src/impl/couch/auth.ts"() {
|
|
1427
|
+
"use strict";
|
|
1428
|
+
init_factory();
|
|
1429
|
+
init_types_legacy();
|
|
1430
|
+
init_logger();
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
// src/impl/couch/CouchDBSyncStrategy.ts
|
|
1435
|
+
import { Status as Status2 } from "@vue-skuilder/common";
|
|
1436
|
+
var init_CouchDBSyncStrategy = __esm({
|
|
1437
|
+
"src/impl/couch/CouchDBSyncStrategy.ts"() {
|
|
1438
|
+
"use strict";
|
|
1439
|
+
init_factory();
|
|
1440
|
+
init_types_legacy();
|
|
1441
|
+
init_logger();
|
|
1442
|
+
init_common();
|
|
1443
|
+
init_pouchdb_setup();
|
|
1444
|
+
init_couch();
|
|
1445
|
+
init_auth();
|
|
1446
|
+
}
|
|
1447
|
+
});
|
|
1448
|
+
|
|
1449
|
+
// src/impl/couch/index.ts
|
|
1450
|
+
import moment4 from "moment";
|
|
1451
|
+
import process2 from "process";
|
|
1452
|
+
function getCourseDB2(courseID) {
|
|
1453
|
+
return new pouchdb_setup_default(
|
|
1454
|
+
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
|
|
1455
|
+
pouchDBincludeCredentialsConfig
|
|
1456
|
+
);
|
|
1457
|
+
}
|
|
1458
|
+
function getCourseDocs(courseID, docIDs, options = {}) {
|
|
1459
|
+
return getCourseDB2(courseID).allDocs({
|
|
1460
|
+
...options,
|
|
1461
|
+
keys: docIDs
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
function getCourseDoc(courseID, docID, options = {}) {
|
|
1465
|
+
return getCourseDB2(courseID).get(docID, options);
|
|
1466
|
+
}
|
|
1467
|
+
function filterAllDocsByPrefix2(db, prefix, opts) {
|
|
1468
|
+
const options = {
|
|
1469
|
+
startkey: prefix,
|
|
1470
|
+
endkey: prefix + "\uFFF0",
|
|
1471
|
+
include_docs: true
|
|
1472
|
+
};
|
|
1473
|
+
if (opts) {
|
|
1474
|
+
Object.assign(options, opts);
|
|
1475
|
+
}
|
|
1476
|
+
return db.allDocs(options);
|
|
1477
|
+
}
|
|
1478
|
+
function getStartAndEndKeys2(key) {
|
|
1479
|
+
return {
|
|
1480
|
+
startkey: key,
|
|
1481
|
+
endkey: key + "\uFFF0"
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_TIME_FORMAT2;
|
|
1485
|
+
var init_couch = __esm({
|
|
1486
|
+
"src/impl/couch/index.ts"() {
|
|
1487
|
+
"use strict";
|
|
1488
|
+
init_factory();
|
|
1489
|
+
init_types_legacy();
|
|
1490
|
+
init_logger();
|
|
1491
|
+
init_pouchdb_setup();
|
|
1492
|
+
init_contentSource();
|
|
1493
|
+
init_adminDB2();
|
|
1494
|
+
init_classroomDB2();
|
|
1495
|
+
init_courseAPI();
|
|
1496
|
+
init_courseDB();
|
|
1497
|
+
init_CouchDBSyncStrategy();
|
|
1498
|
+
isBrowser = typeof window !== "undefined";
|
|
1499
|
+
if (isBrowser) {
|
|
1500
|
+
window.process = process2;
|
|
1501
|
+
}
|
|
1502
|
+
GUEST_LOCAL_DB = `userdb-${GuestUsername}`;
|
|
1503
|
+
localUserDB = new pouchdb_setup_default(GUEST_LOCAL_DB);
|
|
1504
|
+
pouchDBincludeCredentialsConfig = {
|
|
1505
|
+
fetch(url, opts) {
|
|
1506
|
+
opts.credentials = "include";
|
|
1507
|
+
return pouchdb_setup_default.fetch(url, opts);
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
// src/impl/common/BaseUserDB.ts
|
|
1515
|
+
import { Status as Status3 } from "@vue-skuilder/common";
|
|
1516
|
+
import moment5 from "moment";
|
|
1517
|
+
async function getOrCreateClassroomRegistrationsDoc(user) {
|
|
1518
|
+
let ret;
|
|
1519
|
+
try {
|
|
1520
|
+
ret = await getLocalUserDB(user).get(userClassroomsDoc);
|
|
1521
|
+
} catch (e) {
|
|
1522
|
+
const err = e;
|
|
1523
|
+
if (err.status === 404) {
|
|
1524
|
+
await getLocalUserDB(user).put({
|
|
1525
|
+
_id: userClassroomsDoc,
|
|
1526
|
+
registrations: []
|
|
1527
|
+
});
|
|
1528
|
+
ret = await getOrCreateClassroomRegistrationsDoc(user);
|
|
1529
|
+
} else {
|
|
1530
|
+
const errorDetails = {
|
|
1531
|
+
name: err.name,
|
|
1532
|
+
status: err.status,
|
|
1533
|
+
message: err.message,
|
|
1534
|
+
reason: err.reason,
|
|
1535
|
+
error: err.error
|
|
1536
|
+
};
|
|
1537
|
+
logger.error(
|
|
1538
|
+
"Database error in getOrCreateClassroomRegistrationsDoc (standalone function):",
|
|
1539
|
+
errorDetails
|
|
1540
|
+
);
|
|
1541
|
+
throw new Error(
|
|
1542
|
+
`Database error accessing classroom registrations: ${err.message || err.name || "Unknown error"} (status: ${err.status})`
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
return ret;
|
|
1547
|
+
}
|
|
1548
|
+
async function getOrCreateCourseRegistrationsDoc(user) {
|
|
1549
|
+
let ret;
|
|
1550
|
+
try {
|
|
1551
|
+
ret = await getLocalUserDB(user).get(userCoursesDoc);
|
|
1552
|
+
} catch (e) {
|
|
1553
|
+
const err = e;
|
|
1554
|
+
if (err.status === 404) {
|
|
1555
|
+
await getLocalUserDB(user).put({
|
|
1556
|
+
_id: userCoursesDoc,
|
|
1557
|
+
courses: [],
|
|
1558
|
+
studyWeight: {}
|
|
1559
|
+
});
|
|
1560
|
+
ret = await getOrCreateCourseRegistrationsDoc(user);
|
|
1121
1561
|
} else {
|
|
1122
1562
|
throw new Error(
|
|
1123
1563
|
`Unexpected error ${JSON.stringify(e)} in getOrCreateCourseRegistrationDoc...`
|
|
@@ -1166,10 +1606,11 @@ async function dropUserFromClassroom(user, classID) {
|
|
|
1166
1606
|
async function getUserClassrooms(user) {
|
|
1167
1607
|
return getOrCreateClassroomRegistrationsDoc(user);
|
|
1168
1608
|
}
|
|
1169
|
-
var log3,
|
|
1609
|
+
var log3, BaseUser, userCoursesDoc, userClassroomsDoc;
|
|
1170
1610
|
var init_BaseUserDB = __esm({
|
|
1171
1611
|
"src/impl/common/BaseUserDB.ts"() {
|
|
1172
1612
|
"use strict";
|
|
1613
|
+
init_core();
|
|
1173
1614
|
init_util();
|
|
1174
1615
|
init_types_legacy();
|
|
1175
1616
|
init_logger();
|
|
@@ -1180,7 +1621,6 @@ var init_BaseUserDB = __esm({
|
|
|
1180
1621
|
log3 = (s) => {
|
|
1181
1622
|
logger.info(s);
|
|
1182
1623
|
};
|
|
1183
|
-
cardHistoryPrefix2 = "cardH-";
|
|
1184
1624
|
BaseUser = class _BaseUser {
|
|
1185
1625
|
static _instance;
|
|
1186
1626
|
static _initialized = false;
|
|
@@ -1201,11 +1641,13 @@ var init_BaseUserDB = __esm({
|
|
|
1201
1641
|
isLoggedIn() {
|
|
1202
1642
|
return !this._username.startsWith(GuestUsername);
|
|
1203
1643
|
}
|
|
1204
|
-
remoteDB;
|
|
1205
1644
|
remote() {
|
|
1206
1645
|
return this.remoteDB;
|
|
1207
1646
|
}
|
|
1208
1647
|
localDB;
|
|
1648
|
+
remoteDB;
|
|
1649
|
+
writeDB;
|
|
1650
|
+
// Database to use for write operations (local-first approach)
|
|
1209
1651
|
updateQueue;
|
|
1210
1652
|
async createAccount(username, password) {
|
|
1211
1653
|
if (!this.syncStrategy.canCreateAccount()) {
|
|
@@ -1218,10 +1660,14 @@ Currently logged-in as ${this._username}.`
|
|
|
1218
1660
|
);
|
|
1219
1661
|
}
|
|
1220
1662
|
const result = await this.syncStrategy.createAccount(username, password);
|
|
1221
|
-
if (result.status ===
|
|
1663
|
+
if (result.status === Status3.ok) {
|
|
1222
1664
|
log3(`Account created successfully, updating username to ${username}`);
|
|
1223
1665
|
this._username = username;
|
|
1224
|
-
|
|
1666
|
+
try {
|
|
1667
|
+
localStorage.removeItem("dbUUID");
|
|
1668
|
+
} catch (e) {
|
|
1669
|
+
logger.warn("localStorage not available (Node.js environment):", e);
|
|
1670
|
+
}
|
|
1225
1671
|
await this.init();
|
|
1226
1672
|
}
|
|
1227
1673
|
return {
|
|
@@ -1233,15 +1679,22 @@ Currently logged-in as ${this._username}.`
|
|
|
1233
1679
|
if (!this.syncStrategy.canAuthenticate()) {
|
|
1234
1680
|
throw new Error("Authentication not supported by current sync strategy");
|
|
1235
1681
|
}
|
|
1236
|
-
if (!this._username.startsWith(GuestUsername)) {
|
|
1237
|
-
|
|
1238
|
-
|
|
1682
|
+
if (!this._username.startsWith(GuestUsername) && this._username != username) {
|
|
1683
|
+
if (this._username != username) {
|
|
1684
|
+
throw new Error(`Cannot change accounts while logged in.
|
|
1685
|
+
Log out of account ${this.getUsername()} before logging in as ${username}.`);
|
|
1686
|
+
}
|
|
1687
|
+
logger.warn(`User ${this._username} is already logged in, but executing login again.`);
|
|
1239
1688
|
}
|
|
1240
1689
|
const loginResult = await this.syncStrategy.authenticate(username, password);
|
|
1241
1690
|
if (loginResult.ok) {
|
|
1242
1691
|
log3(`Logged in as ${username}`);
|
|
1243
1692
|
this._username = username;
|
|
1244
|
-
|
|
1693
|
+
try {
|
|
1694
|
+
localStorage.removeItem("dbUUID");
|
|
1695
|
+
} catch (e) {
|
|
1696
|
+
logger.warn("localStorage not available (Node.js environment):", e);
|
|
1697
|
+
}
|
|
1245
1698
|
await this.init();
|
|
1246
1699
|
}
|
|
1247
1700
|
return loginResult;
|
|
@@ -1249,7 +1702,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1249
1702
|
async resetUserData() {
|
|
1250
1703
|
if (this.syncStrategy.canAuthenticate()) {
|
|
1251
1704
|
return {
|
|
1252
|
-
status:
|
|
1705
|
+
status: Status3.error,
|
|
1253
1706
|
error: "Reset user data is only available for local-only mode. Use logout instead for remote sync."
|
|
1254
1707
|
};
|
|
1255
1708
|
}
|
|
@@ -1258,8 +1711,8 @@ Currently logged-in as ${this._username}.`
|
|
|
1258
1711
|
const allDocs = await localDB.allDocs({ include_docs: false });
|
|
1259
1712
|
const docsToDelete = allDocs.rows.filter((row) => {
|
|
1260
1713
|
const id = row.id;
|
|
1261
|
-
return id.startsWith(
|
|
1262
|
-
id.startsWith(
|
|
1714
|
+
return id.startsWith(DocTypePrefixes["CARDRECORD" /* CARDRECORD */]) || // Card interaction history
|
|
1715
|
+
id.startsWith(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]) || // Scheduled reviews
|
|
1263
1716
|
id === _BaseUser.DOC_IDS.COURSE_REGISTRATIONS || // Course registrations
|
|
1264
1717
|
id === _BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS || // Classroom registrations
|
|
1265
1718
|
id === _BaseUser.DOC_IDS.CONFIG;
|
|
@@ -1268,11 +1721,11 @@ Currently logged-in as ${this._username}.`
|
|
|
1268
1721
|
await localDB.bulkDocs(docsToDelete);
|
|
1269
1722
|
}
|
|
1270
1723
|
await this.init();
|
|
1271
|
-
return { status:
|
|
1724
|
+
return { status: Status3.ok };
|
|
1272
1725
|
} catch (error) {
|
|
1273
1726
|
logger.error("Failed to reset user data:", error);
|
|
1274
1727
|
return {
|
|
1275
|
-
status:
|
|
1728
|
+
status: Status3.error,
|
|
1276
1729
|
error: error instanceof Error ? error.message : "Unknown error during reset"
|
|
1277
1730
|
};
|
|
1278
1731
|
}
|
|
@@ -1328,7 +1781,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1328
1781
|
*
|
|
1329
1782
|
*/
|
|
1330
1783
|
async getActiveCards() {
|
|
1331
|
-
const keys =
|
|
1784
|
+
const keys = getStartAndEndKeys(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]);
|
|
1332
1785
|
const reviews = await this.remoteDB.allDocs({
|
|
1333
1786
|
startkey: keys.startkey,
|
|
1334
1787
|
endkey: keys.endkey,
|
|
@@ -1400,7 +1853,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1400
1853
|
}
|
|
1401
1854
|
}
|
|
1402
1855
|
async getReviewstoDate(targetDate, course_id) {
|
|
1403
|
-
const keys =
|
|
1856
|
+
const keys = getStartAndEndKeys(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */]);
|
|
1404
1857
|
const reviews = await this.remoteDB.allDocs({
|
|
1405
1858
|
startkey: keys.startkey,
|
|
1406
1859
|
endkey: keys.endkey,
|
|
@@ -1410,8 +1863,11 @@ Currently logged-in as ${this._username}.`
|
|
|
1410
1863
|
`Fetching ${this._username}'s scheduled reviews${course_id ? ` for course ${course_id}` : ""}.`
|
|
1411
1864
|
);
|
|
1412
1865
|
return reviews.rows.filter((r) => {
|
|
1413
|
-
if (r.id.startsWith(
|
|
1414
|
-
const date =
|
|
1866
|
+
if (r.id.startsWith(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */])) {
|
|
1867
|
+
const date = moment5.utc(
|
|
1868
|
+
r.id.substr(DocTypePrefixes["SCHEDULED_CARD" /* SCHEDULED_CARD */].length),
|
|
1869
|
+
REVIEW_TIME_FORMAT
|
|
1870
|
+
);
|
|
1415
1871
|
if (targetDate.isAfter(date)) {
|
|
1416
1872
|
if (course_id === void 0 || r.doc.courseId === course_id) {
|
|
1417
1873
|
return true;
|
|
@@ -1421,11 +1877,11 @@ Currently logged-in as ${this._username}.`
|
|
|
1421
1877
|
}).map((r) => r.doc);
|
|
1422
1878
|
}
|
|
1423
1879
|
async getReviewsForcast(daysCount) {
|
|
1424
|
-
const time =
|
|
1880
|
+
const time = moment5.utc().add(daysCount, "days");
|
|
1425
1881
|
return this.getReviewstoDate(time);
|
|
1426
1882
|
}
|
|
1427
1883
|
async getPendingReviews(course_id) {
|
|
1428
|
-
const now =
|
|
1884
|
+
const now = moment5.utc();
|
|
1429
1885
|
return this.getReviewstoDate(now, course_id);
|
|
1430
1886
|
}
|
|
1431
1887
|
async getScheduledReviewCount(course_id) {
|
|
@@ -1526,7 +1982,8 @@ Currently logged-in as ${this._username}.`
|
|
|
1526
1982
|
const defaultConfig = {
|
|
1527
1983
|
_id: _BaseUser.DOC_IDS.CONFIG,
|
|
1528
1984
|
darkMode: false,
|
|
1529
|
-
likesConfetti: false
|
|
1985
|
+
likesConfetti: false,
|
|
1986
|
+
sessionTimeLimit: 5
|
|
1530
1987
|
};
|
|
1531
1988
|
try {
|
|
1532
1989
|
const cfg = await this.localDB.get(_BaseUser.DOC_IDS.CONFIG);
|
|
@@ -1599,10 +2056,15 @@ Currently logged-in as ${this._username}.`
|
|
|
1599
2056
|
setDBandQ() {
|
|
1600
2057
|
this.localDB = getLocalUserDB(this._username);
|
|
1601
2058
|
this.remoteDB = this.syncStrategy.setupRemoteDB(this._username);
|
|
1602
|
-
this.
|
|
2059
|
+
this.writeDB = this.syncStrategy.getWriteDB ? this.syncStrategy.getWriteDB(this._username) : this.localDB;
|
|
2060
|
+
this.updateQueue = new UpdateQueue(this.localDB, this.writeDB);
|
|
1603
2061
|
}
|
|
1604
2062
|
async init() {
|
|
1605
2063
|
_BaseUser._initialized = false;
|
|
2064
|
+
if (this._username === "admin") {
|
|
2065
|
+
_BaseUser._initialized = true;
|
|
2066
|
+
return;
|
|
2067
|
+
}
|
|
1606
2068
|
this.setDBandQ();
|
|
1607
2069
|
this.syncStrategy.startSync(this.localDB, this.remoteDB);
|
|
1608
2070
|
void this.applyDesignDocs();
|
|
@@ -1624,6 +2086,9 @@ Currently logged-in as ${this._username}.`
|
|
|
1624
2086
|
}
|
|
1625
2087
|
];
|
|
1626
2088
|
async applyDesignDocs() {
|
|
2089
|
+
if (this._username === "admin") {
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
1627
2092
|
for (const doc of _BaseUser.designDocs) {
|
|
1628
2093
|
try {
|
|
1629
2094
|
try {
|
|
@@ -1640,7 +2105,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1640
2105
|
}
|
|
1641
2106
|
}
|
|
1642
2107
|
} catch (error) {
|
|
1643
|
-
if (error
|
|
2108
|
+
if (error.name && error.name === "conflict") {
|
|
1644
2109
|
logger.warn(`Design doc ${doc._id} update conflict - will retry`);
|
|
1645
2110
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
1646
2111
|
await this.applyDesignDoc(doc);
|
|
@@ -1678,7 +2143,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1678
2143
|
*/
|
|
1679
2144
|
async putCardRecord(record) {
|
|
1680
2145
|
const cardHistoryID = getCardHistoryID(record.courseID, record.cardID);
|
|
1681
|
-
record.timeStamp =
|
|
2146
|
+
record.timeStamp = moment5.utc(record.timeStamp).toString();
|
|
1682
2147
|
try {
|
|
1683
2148
|
const cardHistory = await this.update(
|
|
1684
2149
|
cardHistoryID,
|
|
@@ -1694,7 +2159,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1694
2159
|
const ret = {
|
|
1695
2160
|
...record2
|
|
1696
2161
|
};
|
|
1697
|
-
ret.timeStamp =
|
|
2162
|
+
ret.timeStamp = moment5.utc(record2.timeStamp);
|
|
1698
2163
|
return ret;
|
|
1699
2164
|
});
|
|
1700
2165
|
return cardHistory;
|
|
@@ -1710,8 +2175,8 @@ Currently logged-in as ${this._username}.`
|
|
|
1710
2175
|
streak: 0,
|
|
1711
2176
|
bestInterval: 0
|
|
1712
2177
|
};
|
|
1713
|
-
|
|
1714
|
-
return initCardHistory;
|
|
2178
|
+
const putResult = await this.writeDB.put(initCardHistory);
|
|
2179
|
+
return { ...initCardHistory, _rev: putResult.rev };
|
|
1715
2180
|
} else {
|
|
1716
2181
|
throw new Error(`putCardRecord failed because of:
|
|
1717
2182
|
name:${reason.name}
|
|
@@ -1746,7 +2211,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1746
2211
|
const deletePromises = duplicateDocIds.map(async (docId) => {
|
|
1747
2212
|
try {
|
|
1748
2213
|
const doc = await this.remoteDB.get(docId);
|
|
1749
|
-
await this.
|
|
2214
|
+
await this.writeDB.remove(doc);
|
|
1750
2215
|
log3(`Successfully removed duplicate review: ${docId}`);
|
|
1751
2216
|
} catch (error) {
|
|
1752
2217
|
log3(`Failed to remove duplicate review ${docId}: ${error}`);
|
|
@@ -1768,17 +2233,17 @@ Currently logged-in as ${this._username}.`
|
|
|
1768
2233
|
* @param course_id optional specification of individual course
|
|
1769
2234
|
*/
|
|
1770
2235
|
async getSeenCards(course_id) {
|
|
1771
|
-
let prefix =
|
|
2236
|
+
let prefix = DocTypePrefixes["CARDRECORD" /* CARDRECORD */];
|
|
1772
2237
|
if (course_id) {
|
|
1773
2238
|
prefix += course_id;
|
|
1774
2239
|
}
|
|
1775
|
-
const docs = await
|
|
2240
|
+
const docs = await filterAllDocsByPrefix(this.localDB, prefix, {
|
|
1776
2241
|
include_docs: false
|
|
1777
2242
|
});
|
|
1778
2243
|
const ret = [];
|
|
1779
2244
|
docs.rows.forEach((row) => {
|
|
1780
|
-
if (row.id.startsWith(
|
|
1781
|
-
ret.push(row.id.substr(
|
|
2245
|
+
if (row.id.startsWith(DocTypePrefixes["CARDRECORD" /* CARDRECORD */])) {
|
|
2246
|
+
ret.push(row.id.substr(DocTypePrefixes["CARDRECORD" /* CARDRECORD */].length));
|
|
1782
2247
|
}
|
|
1783
2248
|
});
|
|
1784
2249
|
return ret;
|
|
@@ -1788,9 +2253,9 @@ Currently logged-in as ${this._username}.`
|
|
|
1788
2253
|
* @returns A promise of the cards that the user has seen in the past.
|
|
1789
2254
|
*/
|
|
1790
2255
|
async getHistory() {
|
|
1791
|
-
const cards = await
|
|
2256
|
+
const cards = await filterAllDocsByPrefix(
|
|
1792
2257
|
this.remoteDB,
|
|
1793
|
-
|
|
2258
|
+
DocTypePrefixes["CARDRECORD" /* CARDRECORD */],
|
|
1794
2259
|
{
|
|
1795
2260
|
include_docs: true,
|
|
1796
2261
|
attachments: false
|
|
@@ -1831,7 +2296,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1831
2296
|
} catch (e) {
|
|
1832
2297
|
const err = e;
|
|
1833
2298
|
if (err.status === 404) {
|
|
1834
|
-
await this.
|
|
2299
|
+
await this.writeDB.put({
|
|
1835
2300
|
_id: _BaseUser.DOC_IDS.CLASSROOM_REGISTRATIONS,
|
|
1836
2301
|
registrations: []
|
|
1837
2302
|
});
|
|
@@ -1879,10 +2344,10 @@ Currently logged-in as ${this._username}.`
|
|
|
1879
2344
|
}
|
|
1880
2345
|
}
|
|
1881
2346
|
async scheduleCardReview(review) {
|
|
1882
|
-
return scheduleCardReviewLocal(this.
|
|
2347
|
+
return scheduleCardReviewLocal(this.writeDB, review);
|
|
1883
2348
|
}
|
|
1884
2349
|
async removeScheduledCardReview(reviewId) {
|
|
1885
|
-
return removeScheduledCardReviewLocal(this.
|
|
2350
|
+
return removeScheduledCardReviewLocal(this.writeDB, reviewId);
|
|
1886
2351
|
}
|
|
1887
2352
|
async registerForClassroom(_classId, _registerAs) {
|
|
1888
2353
|
return registerUserForClassroom(this._username, _classId, _registerAs);
|
|
@@ -1912,427 +2377,24 @@ var init_common = __esm({
|
|
|
1912
2377
|
}
|
|
1913
2378
|
});
|
|
1914
2379
|
|
|
1915
|
-
// src/
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo2()) {
|
|
1920
|
-
const db = getCourseDB2(courseID);
|
|
1921
|
-
const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
|
|
1922
|
-
const result = await db.post(payload);
|
|
1923
|
-
const dataShapeId = NameSpacer.getDataShapeString({
|
|
1924
|
-
course: codeCourse,
|
|
1925
|
-
dataShape: shape.name
|
|
1926
|
-
});
|
|
1927
|
-
if (result.ok) {
|
|
1928
|
-
try {
|
|
1929
|
-
await createCards(courseID, dataShapeId, result.id, tags, elo, author);
|
|
1930
|
-
} catch (error) {
|
|
1931
|
-
let errorMessage = "Unknown error";
|
|
1932
|
-
if (error instanceof Error) {
|
|
1933
|
-
errorMessage = error.message;
|
|
1934
|
-
} else if (error && typeof error === "object" && "reason" in error) {
|
|
1935
|
-
errorMessage = error.reason;
|
|
1936
|
-
} else if (error && typeof error === "object" && "message" in error) {
|
|
1937
|
-
errorMessage = error.message;
|
|
1938
|
-
} else {
|
|
1939
|
-
errorMessage = String(error);
|
|
1940
|
-
}
|
|
1941
|
-
logger.error(`[addNote55] Failed to create cards for note ${result.id}: ${errorMessage}`);
|
|
1942
|
-
result.cardCreationFailed = true;
|
|
1943
|
-
result.cardCreationError = errorMessage;
|
|
1944
|
-
}
|
|
1945
|
-
} else {
|
|
1946
|
-
logger.error(`[addNote55] Error adding note. Result: ${JSON.stringify(result)}`);
|
|
1947
|
-
}
|
|
1948
|
-
return result;
|
|
1949
|
-
}
|
|
1950
|
-
async function createCards(courseID, datashapeID, noteID, tags, elo = blankCourseElo2(), author) {
|
|
1951
|
-
const cfg = await getCredentialledCourseConfig(courseID);
|
|
1952
|
-
const dsDescriptor = NameSpacer.getDataShapeDescriptor(datashapeID);
|
|
1953
|
-
let questionViewTypes = [];
|
|
1954
|
-
for (const ds of cfg.dataShapes) {
|
|
1955
|
-
if (ds.name === datashapeID) {
|
|
1956
|
-
questionViewTypes = ds.questionTypes;
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
if (questionViewTypes.length === 0) {
|
|
1960
|
-
const errorMsg = `No questionViewTypes found for datashapeID: ${datashapeID} in course config. Cards cannot be created.`;
|
|
1961
|
-
logger.error(errorMsg);
|
|
1962
|
-
throw new Error(errorMsg);
|
|
1963
|
-
}
|
|
1964
|
-
for (const questionView of questionViewTypes) {
|
|
1965
|
-
await createCard(questionView, courseID, dsDescriptor, noteID, tags, elo, author);
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags, elo = blankCourseElo2(), author) {
|
|
1969
|
-
const qDescriptor = NameSpacer.getQuestionDescriptor(questionViewName);
|
|
1970
|
-
const cfg = await getCredentialledCourseConfig(courseID);
|
|
1971
|
-
for (const rQ of cfg.questionTypes) {
|
|
1972
|
-
if (rQ.name === questionViewName) {
|
|
1973
|
-
for (const view of rQ.viewList) {
|
|
1974
|
-
await addCard(
|
|
1975
|
-
courseID,
|
|
1976
|
-
dsDescriptor.course,
|
|
1977
|
-
[noteID],
|
|
1978
|
-
NameSpacer.getViewString({
|
|
1979
|
-
course: qDescriptor.course,
|
|
1980
|
-
questionType: qDescriptor.questionType,
|
|
1981
|
-
view
|
|
1982
|
-
}),
|
|
1983
|
-
elo,
|
|
1984
|
-
tags,
|
|
1985
|
-
author
|
|
1986
|
-
);
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
}
|
|
1991
|
-
async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
|
|
1992
|
-
const db = getCourseDB2(courseID);
|
|
1993
|
-
const card = await db.post({
|
|
1994
|
-
course,
|
|
1995
|
-
id_displayable_data,
|
|
1996
|
-
id_view,
|
|
1997
|
-
docType: "CARD" /* CARD */,
|
|
1998
|
-
elo: elo || toCourseElo2(990 + Math.round(20 * Math.random())),
|
|
1999
|
-
author
|
|
2000
|
-
});
|
|
2001
|
-
for (const tag of tags) {
|
|
2002
|
-
logger.info(`adding tag: ${tag} to card ${card.id}`);
|
|
2003
|
-
await addTagToCard(courseID, card.id, tag, author, false);
|
|
2004
|
-
}
|
|
2005
|
-
return card;
|
|
2006
|
-
}
|
|
2007
|
-
async function getCredentialledCourseConfig(courseID) {
|
|
2008
|
-
try {
|
|
2009
|
-
const db = getCourseDB2(courseID);
|
|
2010
|
-
const ret = await db.get("CourseConfig");
|
|
2011
|
-
ret.courseID = courseID;
|
|
2012
|
-
logger.info(`Returning course config: ${JSON.stringify(ret)}`);
|
|
2013
|
-
return ret;
|
|
2014
|
-
} catch (e) {
|
|
2015
|
-
logger.error(`Error fetching config for ${courseID}:`, e);
|
|
2016
|
-
throw e;
|
|
2380
|
+
// src/factory.ts
|
|
2381
|
+
function getDataLayer() {
|
|
2382
|
+
if (!dataLayerInstance) {
|
|
2383
|
+
throw new Error("Data layer not initialized. Call initializeDataLayer first.");
|
|
2017
2384
|
}
|
|
2385
|
+
return dataLayerInstance;
|
|
2018
2386
|
}
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
const courseApi = new CourseDB(courseID, async () => {
|
|
2023
|
-
const dummySyncStrategy = {
|
|
2024
|
-
setupRemoteDB: () => null,
|
|
2025
|
-
startSync: () => {
|
|
2026
|
-
},
|
|
2027
|
-
canCreateAccount: () => false,
|
|
2028
|
-
canAuthenticate: () => false,
|
|
2029
|
-
getCurrentUsername: async () => "DummyUser"
|
|
2030
|
-
};
|
|
2031
|
-
return BaseUser.Dummy(dummySyncStrategy);
|
|
2032
|
-
});
|
|
2033
|
-
try {
|
|
2034
|
-
logger.info(`Applying tag ${tagID} to card ${courseID + "-" + cardID}...`);
|
|
2035
|
-
const tag = await courseDB.get(prefixedTagID);
|
|
2036
|
-
if (!tag.taggedCards.includes(cardID)) {
|
|
2037
|
-
tag.taggedCards.push(cardID);
|
|
2038
|
-
if (updateELO) {
|
|
2039
|
-
try {
|
|
2040
|
-
const eloData = await courseApi.getCardEloData([cardID]);
|
|
2041
|
-
const elo = eloData[0];
|
|
2042
|
-
elo.tags[tagID] = {
|
|
2043
|
-
count: 0,
|
|
2044
|
-
score: elo.global.score
|
|
2045
|
-
// todo: or 1000?
|
|
2046
|
-
};
|
|
2047
|
-
await updateCardElo(courseID, cardID, elo);
|
|
2048
|
-
} catch (error) {
|
|
2049
|
-
logger.error("Failed to update ELO data for card:", cardID, error);
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
return courseDB.put(tag);
|
|
2053
|
-
} else throw new AlreadyTaggedErr(`Card ${cardID} is already tagged with ${tagID}`);
|
|
2054
|
-
} catch (e) {
|
|
2055
|
-
if (e instanceof AlreadyTaggedErr) {
|
|
2056
|
-
throw e;
|
|
2057
|
-
}
|
|
2058
|
-
await createTag(courseID, tagID, author);
|
|
2059
|
-
return addTagToCard(courseID, cardID, tagID, author, updateELO);
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
async function updateCardElo(courseID, cardID, elo) {
|
|
2063
|
-
if (elo) {
|
|
2064
|
-
const cDB = getCourseDB2(courseID);
|
|
2065
|
-
const card = await cDB.get(cardID);
|
|
2066
|
-
logger.debug(`Replacing ${JSON.stringify(card.elo)} with ${JSON.stringify(elo)}`);
|
|
2067
|
-
card.elo = elo;
|
|
2068
|
-
return cDB.put(card);
|
|
2069
|
-
}
|
|
2070
|
-
}
|
|
2071
|
-
function getTagID(tagName) {
|
|
2072
|
-
const tagPrefix = "TAG" /* TAG */.valueOf() + "-";
|
|
2073
|
-
if (tagName.indexOf(tagPrefix) === 0) {
|
|
2074
|
-
return tagName;
|
|
2075
|
-
} else {
|
|
2076
|
-
return tagPrefix + tagName;
|
|
2077
|
-
}
|
|
2078
|
-
}
|
|
2079
|
-
function getCourseDB2(courseID) {
|
|
2080
|
-
const dbName = `coursedb-${courseID}`;
|
|
2081
|
-
return new pouchdb_setup_default(
|
|
2082
|
-
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
2083
|
-
pouchDBincludeCredentialsConfig
|
|
2084
|
-
);
|
|
2085
|
-
}
|
|
2086
|
-
var AlreadyTaggedErr;
|
|
2087
|
-
var init_courseAPI = __esm({
|
|
2088
|
-
"src/impl/couch/courseAPI.ts"() {
|
|
2089
|
-
"use strict";
|
|
2090
|
-
init_pouchdb_setup();
|
|
2091
|
-
init_couch();
|
|
2092
|
-
init_factory();
|
|
2093
|
-
init_courseDB();
|
|
2094
|
-
init_types_legacy();
|
|
2095
|
-
init_common();
|
|
2096
|
-
init_logger();
|
|
2097
|
-
AlreadyTaggedErr = class extends Error {
|
|
2098
|
-
constructor(message) {
|
|
2099
|
-
super(message);
|
|
2100
|
-
this.name = "AlreadyTaggedErr";
|
|
2101
|
-
}
|
|
2102
|
-
};
|
|
2103
|
-
}
|
|
2104
|
-
});
|
|
2105
|
-
|
|
2106
|
-
// src/impl/couch/auth.ts
|
|
2107
|
-
var init_auth = __esm({
|
|
2108
|
-
"src/impl/couch/auth.ts"() {
|
|
2109
|
-
"use strict";
|
|
2110
|
-
init_factory();
|
|
2111
|
-
init_types_legacy();
|
|
2112
|
-
init_logger();
|
|
2113
|
-
}
|
|
2114
|
-
});
|
|
2115
|
-
|
|
2116
|
-
// src/impl/couch/CouchDBSyncStrategy.ts
|
|
2117
|
-
import { Status as Status3 } from "@vue-skuilder/common";
|
|
2118
|
-
var init_CouchDBSyncStrategy = __esm({
|
|
2119
|
-
"src/impl/couch/CouchDBSyncStrategy.ts"() {
|
|
2387
|
+
var ENV, dataLayerInstance;
|
|
2388
|
+
var init_factory = __esm({
|
|
2389
|
+
"src/factory.ts"() {
|
|
2120
2390
|
"use strict";
|
|
2121
|
-
init_factory();
|
|
2122
|
-
init_types_legacy();
|
|
2123
|
-
init_logger();
|
|
2124
2391
|
init_common();
|
|
2125
|
-
init_pouchdb_setup();
|
|
2126
|
-
init_couch();
|
|
2127
|
-
init_auth();
|
|
2128
|
-
}
|
|
2129
|
-
});
|
|
2130
|
-
|
|
2131
|
-
// src/impl/couch/index.ts
|
|
2132
|
-
import moment4 from "moment";
|
|
2133
|
-
import process2 from "process";
|
|
2134
|
-
function getCourseDB(courseID) {
|
|
2135
|
-
return new pouchdb_setup_default(
|
|
2136
|
-
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
|
|
2137
|
-
pouchDBincludeCredentialsConfig
|
|
2138
|
-
);
|
|
2139
|
-
}
|
|
2140
|
-
function getCourseDocs(courseID, docIDs, options = {}) {
|
|
2141
|
-
return getCourseDB(courseID).allDocs({
|
|
2142
|
-
...options,
|
|
2143
|
-
keys: docIDs
|
|
2144
|
-
});
|
|
2145
|
-
}
|
|
2146
|
-
function getCourseDoc(courseID, docID, options = {}) {
|
|
2147
|
-
return getCourseDB(courseID).get(docID, options);
|
|
2148
|
-
}
|
|
2149
|
-
function filterAllDocsByPrefix(db, prefix, opts) {
|
|
2150
|
-
const options = {
|
|
2151
|
-
startkey: prefix,
|
|
2152
|
-
endkey: prefix + "\uFFF0",
|
|
2153
|
-
include_docs: true
|
|
2154
|
-
};
|
|
2155
|
-
if (opts) {
|
|
2156
|
-
Object.assign(options, opts);
|
|
2157
|
-
}
|
|
2158
|
-
return db.allDocs(options);
|
|
2159
|
-
}
|
|
2160
|
-
function getStartAndEndKeys(key) {
|
|
2161
|
-
return {
|
|
2162
|
-
startkey: key,
|
|
2163
|
-
endkey: key + "\uFFF0"
|
|
2164
|
-
};
|
|
2165
|
-
}
|
|
2166
|
-
var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_PREFIX2, REVIEW_TIME_FORMAT2;
|
|
2167
|
-
var init_couch = __esm({
|
|
2168
|
-
"src/impl/couch/index.ts"() {
|
|
2169
|
-
"use strict";
|
|
2170
|
-
init_factory();
|
|
2171
|
-
init_types_legacy();
|
|
2172
|
-
init_logger();
|
|
2173
|
-
init_pouchdb_setup();
|
|
2174
|
-
init_contentSource();
|
|
2175
|
-
init_adminDB2();
|
|
2176
|
-
init_classroomDB2();
|
|
2177
|
-
init_courseAPI();
|
|
2178
|
-
init_courseDB();
|
|
2179
|
-
init_CouchDBSyncStrategy();
|
|
2180
|
-
isBrowser = typeof window !== "undefined";
|
|
2181
|
-
if (isBrowser) {
|
|
2182
|
-
window.process = process2;
|
|
2183
|
-
}
|
|
2184
|
-
GUEST_LOCAL_DB = `userdb-${GuestUsername}`;
|
|
2185
|
-
localUserDB = new pouchdb_setup_default(GUEST_LOCAL_DB);
|
|
2186
|
-
pouchDBincludeCredentialsConfig = {
|
|
2187
|
-
fetch(url, opts) {
|
|
2188
|
-
opts.credentials = "include";
|
|
2189
|
-
return pouchdb_setup_default.fetch(url, opts);
|
|
2190
|
-
}
|
|
2191
|
-
};
|
|
2192
|
-
REVIEW_PREFIX2 = "card_review_";
|
|
2193
|
-
REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
2194
|
-
}
|
|
2195
|
-
});
|
|
2196
|
-
|
|
2197
|
-
// src/impl/couch/classroomDB.ts
|
|
2198
|
-
import moment5 from "moment";
|
|
2199
|
-
var CLASSROOM_CONFIG, ClassroomDBBase, StudentClassroomDB;
|
|
2200
|
-
var init_classroomDB2 = __esm({
|
|
2201
|
-
"src/impl/couch/classroomDB.ts"() {
|
|
2202
|
-
"use strict";
|
|
2203
|
-
init_factory();
|
|
2204
2392
|
init_logger();
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
CLASSROOM_CONFIG = "ClassroomConfig";
|
|
2209
|
-
ClassroomDBBase = class {
|
|
2210
|
-
_id;
|
|
2211
|
-
_db;
|
|
2212
|
-
_cfg;
|
|
2213
|
-
_initComplete = false;
|
|
2214
|
-
_content_prefix = "content";
|
|
2215
|
-
get _content_searchkeys() {
|
|
2216
|
-
return getStartAndEndKeys(this._content_prefix);
|
|
2217
|
-
}
|
|
2218
|
-
async getAssignedContent() {
|
|
2219
|
-
logger.info(`Getting assigned content...`);
|
|
2220
|
-
const docRows = await this._db.allDocs({
|
|
2221
|
-
startkey: this._content_prefix,
|
|
2222
|
-
endkey: this._content_prefix + `\uFFF0`,
|
|
2223
|
-
include_docs: true
|
|
2224
|
-
});
|
|
2225
|
-
const ret = docRows.rows.map((row) => {
|
|
2226
|
-
return row.doc;
|
|
2227
|
-
});
|
|
2228
|
-
return ret;
|
|
2229
|
-
}
|
|
2230
|
-
getContentId(content) {
|
|
2231
|
-
if (content.type === "tag") {
|
|
2232
|
-
return `${this._content_prefix}-${content.courseID}-${content.tagID}`;
|
|
2233
|
-
} else {
|
|
2234
|
-
return `${this._content_prefix}-${content.courseID}`;
|
|
2235
|
-
}
|
|
2236
|
-
}
|
|
2237
|
-
get ready() {
|
|
2238
|
-
return this._initComplete;
|
|
2239
|
-
}
|
|
2240
|
-
getConfig() {
|
|
2241
|
-
return this._cfg;
|
|
2242
|
-
}
|
|
2243
|
-
};
|
|
2244
|
-
StudentClassroomDB = class _StudentClassroomDB extends ClassroomDBBase {
|
|
2245
|
-
// private readonly _prefix: string = 'content';
|
|
2246
|
-
userMessages;
|
|
2247
|
-
_user;
|
|
2248
|
-
constructor(classID, user) {
|
|
2249
|
-
super();
|
|
2250
|
-
this._id = classID;
|
|
2251
|
-
this._user = user;
|
|
2252
|
-
}
|
|
2253
|
-
async init() {
|
|
2254
|
-
const dbName = `classdb-student-${this._id}`;
|
|
2255
|
-
this._db = new pouchdb_setup_default(
|
|
2256
|
-
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
2257
|
-
pouchDBincludeCredentialsConfig
|
|
2258
|
-
);
|
|
2259
|
-
try {
|
|
2260
|
-
const cfg = await this._db.get(CLASSROOM_CONFIG);
|
|
2261
|
-
this._cfg = cfg;
|
|
2262
|
-
this.userMessages = this._db.changes({
|
|
2263
|
-
since: "now",
|
|
2264
|
-
live: true,
|
|
2265
|
-
include_docs: true
|
|
2266
|
-
});
|
|
2267
|
-
this._initComplete = true;
|
|
2268
|
-
return;
|
|
2269
|
-
} catch (e) {
|
|
2270
|
-
throw new Error(`Error in StudentClassroomDB constructor: ${JSON.stringify(e)}`);
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
static async factory(classID, user) {
|
|
2274
|
-
const ret = new _StudentClassroomDB(classID, user);
|
|
2275
|
-
await ret.init();
|
|
2276
|
-
return ret;
|
|
2277
|
-
}
|
|
2278
|
-
setChangeFcn(f) {
|
|
2279
|
-
void this.userMessages.on("change", f);
|
|
2280
|
-
}
|
|
2281
|
-
async getPendingReviews() {
|
|
2282
|
-
const u = this._user;
|
|
2283
|
-
return (await u.getPendingReviews()).filter((r) => r.scheduledFor === "classroom" && r.schedulingAgentId === this._id).map((r) => {
|
|
2284
|
-
return {
|
|
2285
|
-
...r,
|
|
2286
|
-
qualifiedID: `${r.courseId}-${r.cardId}`,
|
|
2287
|
-
courseID: r.courseId,
|
|
2288
|
-
cardID: r.cardId,
|
|
2289
|
-
contentSourceType: "classroom",
|
|
2290
|
-
contentSourceID: this._id,
|
|
2291
|
-
reviewID: r._id,
|
|
2292
|
-
status: "review"
|
|
2293
|
-
};
|
|
2294
|
-
});
|
|
2295
|
-
}
|
|
2296
|
-
async getNewCards() {
|
|
2297
|
-
const activeCards = await this._user.getActiveCards();
|
|
2298
|
-
const now = moment5.utc();
|
|
2299
|
-
const assigned = await this.getAssignedContent();
|
|
2300
|
-
const due = assigned.filter((c) => now.isAfter(moment5.utc(c.activeOn, REVIEW_TIME_FORMAT2)));
|
|
2301
|
-
logger.info(`Due content: ${JSON.stringify(due)}`);
|
|
2302
|
-
let ret = [];
|
|
2303
|
-
for (let i = 0; i < due.length; i++) {
|
|
2304
|
-
const content = due[i];
|
|
2305
|
-
if (content.type === "course") {
|
|
2306
|
-
const db = new CourseDB(content.courseID, async () => this._user);
|
|
2307
|
-
ret = ret.concat(await db.getNewCards());
|
|
2308
|
-
} else if (content.type === "tag") {
|
|
2309
|
-
const tagDoc = await getTag(content.courseID, content.tagID);
|
|
2310
|
-
ret = ret.concat(
|
|
2311
|
-
tagDoc.taggedCards.map((c) => {
|
|
2312
|
-
return {
|
|
2313
|
-
courseID: content.courseID,
|
|
2314
|
-
cardID: c,
|
|
2315
|
-
qualifiedID: `${content.courseID}-${c}`,
|
|
2316
|
-
contentSourceType: "classroom",
|
|
2317
|
-
contentSourceID: this._id,
|
|
2318
|
-
status: "new"
|
|
2319
|
-
};
|
|
2320
|
-
})
|
|
2321
|
-
);
|
|
2322
|
-
} else if (content.type === "card") {
|
|
2323
|
-
ret.push(await getCourseDB(content.courseID).get(content.cardID));
|
|
2324
|
-
}
|
|
2325
|
-
}
|
|
2326
|
-
logger.info(`New Cards from classroom ${this._cfg.name}: ${ret.map((c) => c.qualifiedID)}`);
|
|
2327
|
-
return ret.filter((c) => {
|
|
2328
|
-
if (activeCards.some((ac) => c.qualifiedID.includes(ac))) {
|
|
2329
|
-
return false;
|
|
2330
|
-
} else {
|
|
2331
|
-
return true;
|
|
2332
|
-
}
|
|
2333
|
-
});
|
|
2334
|
-
}
|
|
2393
|
+
ENV = {
|
|
2394
|
+
COUCHDB_SERVER_PROTOCOL: "NOT_SET",
|
|
2395
|
+
COUCHDB_SERVER_URL: "NOT_SET"
|
|
2335
2396
|
};
|
|
2397
|
+
dataLayerInstance = null;
|
|
2336
2398
|
}
|
|
2337
2399
|
});
|
|
2338
2400
|
|
|
@@ -2549,11 +2611,11 @@ init_core();
|
|
|
2549
2611
|
export {
|
|
2550
2612
|
ContentNavigator,
|
|
2551
2613
|
DocType,
|
|
2614
|
+
DocTypePrefixes,
|
|
2552
2615
|
GuestUsername,
|
|
2553
2616
|
Loggable,
|
|
2554
2617
|
Navigators,
|
|
2555
2618
|
areQuestionRecords,
|
|
2556
|
-
cardHistoryPrefix,
|
|
2557
2619
|
docIsDeleted,
|
|
2558
2620
|
getCardHistoryID,
|
|
2559
2621
|
getStudySource,
|