@vue-skuilder/db 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/index.d.mts +5 -5
- package/dist/core/index.d.ts +5 -5
- package/dist/core/index.js +717 -667
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +702 -653
- package/dist/core/index.mjs.map +1 -1
- package/dist/{dataLayerProvider-BuntXkCs.d.ts → dataLayerProvider-6stCgDME.d.ts} +1 -1
- package/dist/{dataLayerProvider-BZmLyBVw.d.mts → dataLayerProvider-BbW9EnZK.d.mts} +1 -1
- package/dist/impl/couch/index.d.mts +3 -3
- package/dist/impl/couch/index.d.ts +3 -3
- package/dist/impl/couch/index.js +1940 -1873
- package/dist/impl/couch/index.js.map +1 -1
- package/dist/impl/couch/index.mjs +1894 -1828
- package/dist/impl/couch/index.mjs.map +1 -1
- package/dist/impl/static/index.d.mts +4 -4
- package/dist/impl/static/index.d.ts +4 -4
- package/dist/impl/static/index.js +557 -507
- package/dist/impl/static/index.js.map +1 -1
- package/dist/impl/static/index.mjs +575 -526
- package/dist/impl/static/index.mjs.map +1 -1
- package/dist/index.d.mts +244 -8
- package/dist/index.d.ts +244 -8
- package/dist/index.js +3922 -2792
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3897 -2781
- package/dist/index.mjs.map +1 -1
- package/dist/{types-D6SnlHPm.d.ts → types-BvzcRAys.d.ts} +1 -1
- package/dist/{types-DPRvCrIk.d.mts → types-CQQ80R5N.d.mts} +1 -1
- package/dist/{types-legacy-WPe8CtO-.d.mts → types-legacy-CtrmkOLu.d.mts} +1 -1
- package/dist/{types-legacy-WPe8CtO-.d.ts → types-legacy-CtrmkOLu.d.ts} +1 -1
- package/dist/{userDB-31gsvxyd.d.mts → userDB-7fM4tpgr.d.mts} +2 -2
- package/dist/{userDB-D9EuWTp1.d.ts → userDB-DUY63VMN.d.ts} +2 -2
- package/dist/util/packer/index.d.mts +3 -3
- package/dist/util/packer/index.d.ts +3 -3
- package/package.json +2 -2
- package/src/factory.ts +25 -0
- package/src/impl/common/BaseUserDB.ts +29 -6
- package/src/impl/common/userDBHelpers.ts +11 -1
- package/src/impl/couch/courseLookupDB.ts +24 -0
- 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 +59 -0
- package/src/util/migrator/StaticToCouchDBMigrator.ts +707 -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/tuiLogger.ts +139 -0
|
@@ -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;
|
|
@@ -72,35 +72,10 @@ var init_classroomDB = __esm({
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
// src/
|
|
76
|
-
var
|
|
77
|
-
|
|
78
|
-
"src/factory.ts"() {
|
|
79
|
-
"use strict";
|
|
80
|
-
init_logger();
|
|
81
|
-
ENV = {
|
|
82
|
-
COUCHDB_SERVER_PROTOCOL: "NOT_SET",
|
|
83
|
-
COUCHDB_SERVER_URL: "NOT_SET"
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// src/impl/couch/pouchdb-setup.ts
|
|
89
|
-
import PouchDB from "pouchdb";
|
|
90
|
-
import PouchDBFind from "pouchdb-find";
|
|
91
|
-
import PouchDBAuth from "@nilock2/pouchdb-authentication";
|
|
92
|
-
var pouchdb_setup_default;
|
|
93
|
-
var init_pouchdb_setup = __esm({
|
|
94
|
-
"src/impl/couch/pouchdb-setup.ts"() {
|
|
75
|
+
// src/impl/common/SyncStrategy.ts
|
|
76
|
+
var init_SyncStrategy = __esm({
|
|
77
|
+
"src/impl/common/SyncStrategy.ts"() {
|
|
95
78
|
"use strict";
|
|
96
|
-
PouchDB.plugin(PouchDBFind);
|
|
97
|
-
PouchDB.plugin(PouchDBAuth);
|
|
98
|
-
PouchDB.defaults({
|
|
99
|
-
ajax: {
|
|
100
|
-
timeout: 6e4
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
pouchdb_setup_default = PouchDB;
|
|
104
79
|
}
|
|
105
80
|
});
|
|
106
81
|
|
|
@@ -128,27 +103,123 @@ var init_types_legacy = __esm({
|
|
|
128
103
|
}
|
|
129
104
|
});
|
|
130
105
|
|
|
131
|
-
// src/
|
|
132
|
-
|
|
133
|
-
|
|
106
|
+
// src/core/util/index.ts
|
|
107
|
+
function getCardHistoryID(courseID, cardID) {
|
|
108
|
+
return `${cardHistoryPrefix}-${courseID}-${cardID}`;
|
|
109
|
+
}
|
|
110
|
+
var init_util = __esm({
|
|
111
|
+
"src/core/util/index.ts"() {
|
|
134
112
|
"use strict";
|
|
135
|
-
|
|
136
|
-
init_factory();
|
|
137
|
-
init_logger();
|
|
138
|
-
logger.debug(`COURSELOOKUP FILE RUNNING`);
|
|
113
|
+
init_types_legacy();
|
|
139
114
|
}
|
|
140
115
|
});
|
|
141
116
|
|
|
142
|
-
// src/impl/couch/
|
|
143
|
-
|
|
144
|
-
|
|
117
|
+
// src/impl/couch/pouchdb-setup.ts
|
|
118
|
+
import PouchDB from "pouchdb";
|
|
119
|
+
import PouchDBFind from "pouchdb-find";
|
|
120
|
+
import PouchDBAuth from "@nilock2/pouchdb-authentication";
|
|
121
|
+
var pouchdb_setup_default;
|
|
122
|
+
var init_pouchdb_setup = __esm({
|
|
123
|
+
"src/impl/couch/pouchdb-setup.ts"() {
|
|
124
|
+
"use strict";
|
|
125
|
+
PouchDB.plugin(PouchDBFind);
|
|
126
|
+
PouchDB.plugin(PouchDBAuth);
|
|
127
|
+
PouchDB.defaults({
|
|
128
|
+
ajax: {
|
|
129
|
+
timeout: 6e4
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
pouchdb_setup_default = PouchDB;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// src/util/tuiLogger.ts
|
|
137
|
+
var init_tuiLogger = __esm({
|
|
138
|
+
"src/util/tuiLogger.ts"() {
|
|
139
|
+
"use strict";
|
|
140
|
+
init_dataDirectory();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// src/util/dataDirectory.ts
|
|
145
|
+
import * as path from "path";
|
|
146
|
+
import * as os from "os";
|
|
147
|
+
function getAppDataDirectory() {
|
|
148
|
+
return path.join(os.homedir(), ".tuilder");
|
|
149
|
+
}
|
|
150
|
+
function getDbPath(dbName) {
|
|
151
|
+
return path.join(getAppDataDirectory(), dbName);
|
|
152
|
+
}
|
|
153
|
+
var init_dataDirectory = __esm({
|
|
154
|
+
"src/util/dataDirectory.ts"() {
|
|
155
|
+
"use strict";
|
|
156
|
+
init_tuiLogger();
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// src/impl/common/userDBHelpers.ts
|
|
161
|
+
import moment from "moment";
|
|
162
|
+
function filterAllDocsByPrefix(db, prefix, opts) {
|
|
163
|
+
const options = {
|
|
164
|
+
startkey: prefix,
|
|
165
|
+
endkey: prefix + "\uFFF0",
|
|
166
|
+
include_docs: true
|
|
167
|
+
};
|
|
168
|
+
if (opts) {
|
|
169
|
+
Object.assign(options, opts);
|
|
170
|
+
}
|
|
171
|
+
return db.allDocs(options);
|
|
172
|
+
}
|
|
173
|
+
function getStartAndEndKeys(key) {
|
|
174
|
+
return {
|
|
175
|
+
startkey: key,
|
|
176
|
+
endkey: key + "\uFFF0"
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function getLocalUserDB(username) {
|
|
180
|
+
const dbName = `userdb-${username}`;
|
|
181
|
+
if (typeof window === "undefined") {
|
|
182
|
+
return new pouchdb_setup_default(getDbPath(dbName), {});
|
|
183
|
+
} else {
|
|
184
|
+
return new pouchdb_setup_default(dbName, {});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function scheduleCardReviewLocal(userDB, review) {
|
|
188
|
+
const now = moment.utc();
|
|
189
|
+
logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
|
|
190
|
+
void userDB.put({
|
|
191
|
+
_id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
|
|
192
|
+
cardId: review.card_id,
|
|
193
|
+
reviewTime: review.time,
|
|
194
|
+
courseId: review.course_id,
|
|
195
|
+
scheduledAt: now,
|
|
196
|
+
scheduledFor: review.scheduledFor,
|
|
197
|
+
schedulingAgentId: review.schedulingAgentId
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
|
|
201
|
+
const reviewDoc = await userDB.get(reviewDocID);
|
|
202
|
+
userDB.remove(reviewDoc).then((res) => {
|
|
203
|
+
if (res.ok) {
|
|
204
|
+
log(`Removed Review Doc: ${reviewDocID}`);
|
|
205
|
+
}
|
|
206
|
+
}).catch((err) => {
|
|
207
|
+
log(`Failed to remove Review Doc: ${reviewDocID},
|
|
208
|
+
${JSON.stringify(err)}`);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
var REVIEW_PREFIX, REVIEW_TIME_FORMAT, log;
|
|
212
|
+
var init_userDBHelpers = __esm({
|
|
213
|
+
"src/impl/common/userDBHelpers.ts"() {
|
|
145
214
|
"use strict";
|
|
146
|
-
init_pouchdb_setup();
|
|
147
|
-
init_factory();
|
|
148
|
-
init_couch();
|
|
149
|
-
init_classroomDB2();
|
|
150
|
-
init_courseLookupDB();
|
|
151
215
|
init_logger();
|
|
216
|
+
init_pouchdb_setup();
|
|
217
|
+
init_dataDirectory();
|
|
218
|
+
REVIEW_PREFIX = "card_review_";
|
|
219
|
+
REVIEW_TIME_FORMAT = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
220
|
+
log = (s) => {
|
|
221
|
+
logger.info(s);
|
|
222
|
+
};
|
|
152
223
|
}
|
|
153
224
|
});
|
|
154
225
|
|
|
@@ -264,89 +335,291 @@ var init_clientCache = __esm({
|
|
|
264
335
|
}
|
|
265
336
|
});
|
|
266
337
|
|
|
267
|
-
// src/
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
};
|
|
293
|
-
return ratedR;
|
|
294
|
-
});
|
|
295
|
-
ratedReviews.sort((a, b) => {
|
|
296
|
-
return a.global.score - b.global.score;
|
|
297
|
-
});
|
|
298
|
-
return ratedReviews.map((r) => {
|
|
299
|
-
return {
|
|
300
|
-
...r,
|
|
301
|
-
contentSourceType: "course",
|
|
302
|
-
contentSourceID: this.course.getCourseID(),
|
|
303
|
-
cardID: r.cardId,
|
|
304
|
-
courseID: r.courseId,
|
|
305
|
-
qualifiedID: `${r.courseId}-${r.cardId}`,
|
|
306
|
-
reviewID: r._id,
|
|
307
|
-
status: "review"
|
|
308
|
-
};
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
async getNewCards(limit = 99) {
|
|
312
|
-
const activeCards = await this.user.getActiveCards();
|
|
313
|
-
return (await this.course.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
|
|
314
|
-
if (activeCards.some((ac) => c.includes(ac))) {
|
|
315
|
-
return false;
|
|
316
|
-
} else {
|
|
317
|
-
return true;
|
|
318
|
-
}
|
|
319
|
-
})).map((c) => {
|
|
320
|
-
return {
|
|
321
|
-
...c,
|
|
322
|
-
status: "new"
|
|
323
|
-
};
|
|
324
|
-
});
|
|
338
|
+
// src/impl/couch/courseAPI.ts
|
|
339
|
+
import { NameSpacer } from "@vue-skuilder/common";
|
|
340
|
+
import { blankCourseElo, toCourseElo } from "@vue-skuilder/common";
|
|
341
|
+
import { prepareNote55 } from "@vue-skuilder/common";
|
|
342
|
+
async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo()) {
|
|
343
|
+
const db = getCourseDB(courseID);
|
|
344
|
+
const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
|
|
345
|
+
const result = await db.post(payload);
|
|
346
|
+
const dataShapeId = NameSpacer.getDataShapeString({
|
|
347
|
+
course: codeCourse,
|
|
348
|
+
dataShape: shape.name
|
|
349
|
+
});
|
|
350
|
+
if (result.ok) {
|
|
351
|
+
try {
|
|
352
|
+
await createCards(courseID, dataShapeId, result.id, tags, elo, author);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
let errorMessage = "Unknown error";
|
|
355
|
+
if (error instanceof Error) {
|
|
356
|
+
errorMessage = error.message;
|
|
357
|
+
} else if (error && typeof error === "object" && "reason" in error) {
|
|
358
|
+
errorMessage = error.reason;
|
|
359
|
+
} else if (error && typeof error === "object" && "message" in error) {
|
|
360
|
+
errorMessage = error.message;
|
|
361
|
+
} else {
|
|
362
|
+
errorMessage = String(error);
|
|
325
363
|
}
|
|
326
|
-
|
|
364
|
+
logger.error(`[addNote55] Failed to create cards for note ${result.id}: ${errorMessage}`);
|
|
365
|
+
result.cardCreationFailed = true;
|
|
366
|
+
result.cardCreationError = errorMessage;
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
logger.error(`[addNote55] Error adding note. Result: ${JSON.stringify(result)}`);
|
|
327
370
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
371
|
+
return result;
|
|
372
|
+
}
|
|
373
|
+
async function createCards(courseID, datashapeID, noteID, tags, elo = blankCourseElo(), author) {
|
|
374
|
+
const cfg = await getCredentialledCourseConfig(courseID);
|
|
375
|
+
const dsDescriptor = NameSpacer.getDataShapeDescriptor(datashapeID);
|
|
376
|
+
let questionViewTypes = [];
|
|
377
|
+
for (const ds of cfg.dataShapes) {
|
|
378
|
+
if (ds.name === datashapeID) {
|
|
379
|
+
questionViewTypes = ds.questionTypes;
|
|
380
|
+
}
|
|
338
381
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
382
|
+
if (questionViewTypes.length === 0) {
|
|
383
|
+
const errorMsg = `No questionViewTypes found for datashapeID: ${datashapeID} in course config. Cards cannot be created.`;
|
|
384
|
+
logger.error(errorMsg);
|
|
385
|
+
throw new Error(errorMsg);
|
|
386
|
+
}
|
|
387
|
+
for (const questionView of questionViewTypes) {
|
|
388
|
+
await createCard(questionView, courseID, dsDescriptor, noteID, tags, elo, author);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags, elo = blankCourseElo(), author) {
|
|
392
|
+
const qDescriptor = NameSpacer.getQuestionDescriptor(questionViewName);
|
|
393
|
+
const cfg = await getCredentialledCourseConfig(courseID);
|
|
394
|
+
for (const rQ of cfg.questionTypes) {
|
|
395
|
+
if (rQ.name === questionViewName) {
|
|
396
|
+
for (const view of rQ.viewList) {
|
|
397
|
+
await addCard(
|
|
398
|
+
courseID,
|
|
399
|
+
dsDescriptor.course,
|
|
400
|
+
[noteID],
|
|
401
|
+
NameSpacer.getViewString({
|
|
402
|
+
course: qDescriptor.course,
|
|
403
|
+
questionType: qDescriptor.questionType,
|
|
404
|
+
view
|
|
405
|
+
}),
|
|
406
|
+
elo,
|
|
407
|
+
tags,
|
|
408
|
+
author
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
|
|
415
|
+
const db = getCourseDB(courseID);
|
|
416
|
+
const card = await db.post({
|
|
417
|
+
course,
|
|
418
|
+
id_displayable_data,
|
|
419
|
+
id_view,
|
|
420
|
+
docType: "CARD" /* CARD */,
|
|
421
|
+
elo: elo || toCourseElo(990 + Math.round(20 * Math.random())),
|
|
422
|
+
author
|
|
423
|
+
});
|
|
424
|
+
for (const tag of tags) {
|
|
425
|
+
logger.info(`adding tag: ${tag} to card ${card.id}`);
|
|
426
|
+
await addTagToCard(courseID, card.id, tag, author, false);
|
|
427
|
+
}
|
|
428
|
+
return card;
|
|
429
|
+
}
|
|
430
|
+
async function getCredentialledCourseConfig(courseID) {
|
|
431
|
+
try {
|
|
432
|
+
const db = getCourseDB(courseID);
|
|
433
|
+
const ret = await db.get("CourseConfig");
|
|
434
|
+
ret.courseID = courseID;
|
|
435
|
+
logger.info(`Returning course config: ${JSON.stringify(ret)}`);
|
|
436
|
+
return ret;
|
|
437
|
+
} catch (e) {
|
|
438
|
+
logger.error(`Error fetching config for ${courseID}:`, e);
|
|
439
|
+
throw e;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
async function addTagToCard(courseID, cardID, tagID, author, updateELO = true) {
|
|
443
|
+
const prefixedTagID = getTagID(tagID);
|
|
444
|
+
const courseDB = getCourseDB(courseID);
|
|
445
|
+
const courseApi = new CourseDB(courseID, async () => {
|
|
446
|
+
const dummySyncStrategy = {
|
|
447
|
+
setupRemoteDB: () => null,
|
|
448
|
+
startSync: () => {
|
|
449
|
+
},
|
|
450
|
+
canCreateAccount: () => false,
|
|
451
|
+
canAuthenticate: () => false,
|
|
452
|
+
getCurrentUsername: async () => "DummyUser"
|
|
453
|
+
};
|
|
454
|
+
return BaseUser.Dummy(dummySyncStrategy);
|
|
455
|
+
});
|
|
456
|
+
try {
|
|
457
|
+
logger.info(`Applying tag ${tagID} to card ${courseID + "-" + cardID}...`);
|
|
458
|
+
const tag = await courseDB.get(prefixedTagID);
|
|
459
|
+
if (!tag.taggedCards.includes(cardID)) {
|
|
460
|
+
tag.taggedCards.push(cardID);
|
|
461
|
+
if (updateELO) {
|
|
462
|
+
try {
|
|
463
|
+
const eloData = await courseApi.getCardEloData([cardID]);
|
|
464
|
+
const elo = eloData[0];
|
|
465
|
+
elo.tags[tagID] = {
|
|
466
|
+
count: 0,
|
|
467
|
+
score: elo.global.score
|
|
468
|
+
// todo: or 1000?
|
|
469
|
+
};
|
|
470
|
+
await updateCardElo(courseID, cardID, elo);
|
|
471
|
+
} catch (error) {
|
|
472
|
+
logger.error("Failed to update ELO data for card:", cardID, error);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return courseDB.put(tag);
|
|
476
|
+
} else throw new AlreadyTaggedErr(`Card ${cardID} is already tagged with ${tagID}`);
|
|
477
|
+
} catch (e) {
|
|
478
|
+
if (e instanceof AlreadyTaggedErr) {
|
|
479
|
+
throw e;
|
|
480
|
+
}
|
|
481
|
+
await createTag(courseID, tagID, author);
|
|
482
|
+
return addTagToCard(courseID, cardID, tagID, author, updateELO);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
async function updateCardElo(courseID, cardID, elo) {
|
|
486
|
+
if (elo) {
|
|
487
|
+
const cDB = getCourseDB(courseID);
|
|
488
|
+
const card = await cDB.get(cardID);
|
|
489
|
+
logger.debug(`Replacing ${JSON.stringify(card.elo)} with ${JSON.stringify(elo)}`);
|
|
490
|
+
card.elo = elo;
|
|
491
|
+
return cDB.put(card);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
function getTagID(tagName) {
|
|
495
|
+
const tagPrefix = "TAG" /* TAG */.valueOf() + "-";
|
|
496
|
+
if (tagName.indexOf(tagPrefix) === 0) {
|
|
497
|
+
return tagName;
|
|
498
|
+
} else {
|
|
499
|
+
return tagPrefix + tagName;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
function getCourseDB(courseID) {
|
|
503
|
+
const dbName = `coursedb-${courseID}`;
|
|
504
|
+
return new pouchdb_setup_default(
|
|
505
|
+
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
506
|
+
pouchDBincludeCredentialsConfig
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
var AlreadyTaggedErr;
|
|
510
|
+
var init_courseAPI = __esm({
|
|
511
|
+
"src/impl/couch/courseAPI.ts"() {
|
|
512
|
+
"use strict";
|
|
513
|
+
init_pouchdb_setup();
|
|
514
|
+
init_couch();
|
|
515
|
+
init_factory();
|
|
516
|
+
init_courseDB();
|
|
517
|
+
init_types_legacy();
|
|
518
|
+
init_common();
|
|
519
|
+
init_logger();
|
|
520
|
+
AlreadyTaggedErr = class extends Error {
|
|
521
|
+
constructor(message) {
|
|
522
|
+
super(message);
|
|
523
|
+
this.name = "AlreadyTaggedErr";
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// src/impl/couch/courseLookupDB.ts
|
|
530
|
+
var init_courseLookupDB = __esm({
|
|
531
|
+
"src/impl/couch/courseLookupDB.ts"() {
|
|
532
|
+
"use strict";
|
|
533
|
+
init_pouchdb_setup();
|
|
534
|
+
init_factory();
|
|
535
|
+
init_logger();
|
|
536
|
+
logger.debug(`COURSELOOKUP FILE RUNNING`);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// src/core/navigators/elo.ts
|
|
541
|
+
var elo_exports = {};
|
|
542
|
+
__export(elo_exports, {
|
|
543
|
+
default: () => ELONavigator
|
|
544
|
+
});
|
|
545
|
+
var ELONavigator;
|
|
546
|
+
var init_elo = __esm({
|
|
547
|
+
"src/core/navigators/elo.ts"() {
|
|
548
|
+
"use strict";
|
|
549
|
+
init_navigators();
|
|
550
|
+
ELONavigator = class extends ContentNavigator {
|
|
551
|
+
user;
|
|
552
|
+
course;
|
|
553
|
+
constructor(user, course) {
|
|
554
|
+
super();
|
|
555
|
+
this.user = user;
|
|
556
|
+
this.course = course;
|
|
557
|
+
}
|
|
558
|
+
async getPendingReviews() {
|
|
559
|
+
const reviews = await this.user.getPendingReviews(this.course.getCourseID());
|
|
560
|
+
const elo = await this.course.getCardEloData(reviews.map((r) => r.cardId));
|
|
561
|
+
const ratedReviews = reviews.map((r, i) => {
|
|
562
|
+
const ratedR = {
|
|
563
|
+
...r,
|
|
564
|
+
...elo[i]
|
|
565
|
+
};
|
|
566
|
+
return ratedR;
|
|
567
|
+
});
|
|
568
|
+
ratedReviews.sort((a, b) => {
|
|
569
|
+
return a.global.score - b.global.score;
|
|
570
|
+
});
|
|
571
|
+
return ratedReviews.map((r) => {
|
|
572
|
+
return {
|
|
573
|
+
...r,
|
|
574
|
+
contentSourceType: "course",
|
|
575
|
+
contentSourceID: this.course.getCourseID(),
|
|
576
|
+
cardID: r.cardId,
|
|
577
|
+
courseID: r.courseId,
|
|
578
|
+
qualifiedID: `${r.courseId}-${r.cardId}`,
|
|
579
|
+
reviewID: r._id,
|
|
580
|
+
status: "review"
|
|
581
|
+
};
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
async getNewCards(limit = 99) {
|
|
585
|
+
const activeCards = await this.user.getActiveCards();
|
|
586
|
+
return (await this.course.getCardsCenteredAtELO({ limit, elo: "user" }, (c) => {
|
|
587
|
+
if (activeCards.some((ac) => c.includes(ac))) {
|
|
588
|
+
return false;
|
|
589
|
+
} else {
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
})).map((c) => {
|
|
593
|
+
return {
|
|
594
|
+
...c,
|
|
595
|
+
status: "new"
|
|
596
|
+
};
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// import("./**/*") in src/core/navigators/index.ts
|
|
604
|
+
var globImport;
|
|
605
|
+
var init_ = __esm({
|
|
606
|
+
'import("./**/*") in src/core/navigators/index.ts'() {
|
|
607
|
+
globImport = __glob({
|
|
608
|
+
"./elo.ts": () => Promise.resolve().then(() => (init_elo(), elo_exports)),
|
|
609
|
+
"./index.ts": () => Promise.resolve().then(() => (init_navigators(), navigators_exports))
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// src/core/navigators/index.ts
|
|
615
|
+
var navigators_exports = {};
|
|
616
|
+
__export(navigators_exports, {
|
|
617
|
+
ContentNavigator: () => ContentNavigator,
|
|
618
|
+
Navigators: () => Navigators
|
|
619
|
+
});
|
|
620
|
+
var Navigators, ContentNavigator;
|
|
621
|
+
var init_navigators = __esm({
|
|
622
|
+
"src/core/navigators/index.ts"() {
|
|
350
623
|
"use strict";
|
|
351
624
|
init_logger();
|
|
352
625
|
init_();
|
|
@@ -387,16 +660,16 @@ var init_navigators = __esm({
|
|
|
387
660
|
import {
|
|
388
661
|
EloToNumber,
|
|
389
662
|
Status,
|
|
390
|
-
blankCourseElo,
|
|
391
|
-
toCourseElo
|
|
663
|
+
blankCourseElo as blankCourseElo2,
|
|
664
|
+
toCourseElo as toCourseElo2
|
|
392
665
|
} from "@vue-skuilder/common";
|
|
393
666
|
function randIntWeightedTowardZero(n) {
|
|
394
667
|
return Math.floor(Math.random() * Math.random() * Math.random() * n);
|
|
395
668
|
}
|
|
396
669
|
async function getCourseTagStubs(courseID) {
|
|
397
670
|
logger.debug(`Getting tag stubs for course: ${courseID}`);
|
|
398
|
-
const stubs = await
|
|
399
|
-
|
|
671
|
+
const stubs = await filterAllDocsByPrefix2(
|
|
672
|
+
getCourseDB2(courseID),
|
|
400
673
|
"TAG" /* TAG */.valueOf() + "-"
|
|
401
674
|
);
|
|
402
675
|
stubs.rows.forEach((row) => {
|
|
@@ -407,7 +680,7 @@ async function getCourseTagStubs(courseID) {
|
|
|
407
680
|
async function createTag(courseID, tagName, author) {
|
|
408
681
|
logger.debug(`Creating tag: ${tagName}...`);
|
|
409
682
|
const tagID = getTagID(tagName);
|
|
410
|
-
const courseDB =
|
|
683
|
+
const courseDB = getCourseDB2(courseID);
|
|
411
684
|
const resp = await courseDB.put({
|
|
412
685
|
course: courseID,
|
|
413
686
|
docType: "TAG" /* TAG */,
|
|
@@ -422,19 +695,19 @@ async function createTag(courseID, tagName, author) {
|
|
|
422
695
|
}
|
|
423
696
|
async function updateTag(tag) {
|
|
424
697
|
const prior = await getTag(tag.course, tag.name);
|
|
425
|
-
return await
|
|
698
|
+
return await getCourseDB2(tag.course).put({
|
|
426
699
|
...tag,
|
|
427
700
|
_rev: prior._rev
|
|
428
701
|
});
|
|
429
702
|
}
|
|
430
703
|
async function getTag(courseID, tagName) {
|
|
431
704
|
const tagID = getTagID(tagName);
|
|
432
|
-
const courseDB =
|
|
705
|
+
const courseDB = getCourseDB2(courseID);
|
|
433
706
|
return courseDB.get(tagID);
|
|
434
707
|
}
|
|
435
708
|
async function removeTagFromCard(courseID, cardID, tagID) {
|
|
436
709
|
tagID = getTagID(tagID);
|
|
437
|
-
const courseDB =
|
|
710
|
+
const courseDB = getCourseDB2(courseID);
|
|
438
711
|
const tag = await courseDB.get(tagID);
|
|
439
712
|
tag.taggedCards = tag.taggedCards.filter((taggedID) => {
|
|
440
713
|
return cardID !== taggedID;
|
|
@@ -442,7 +715,7 @@ async function removeTagFromCard(courseID, cardID, tagID) {
|
|
|
442
715
|
return courseDB.put(tag);
|
|
443
716
|
}
|
|
444
717
|
async function getAppliedTags(id_course, id_card) {
|
|
445
|
-
const db =
|
|
718
|
+
const db = getCourseDB2(id_course);
|
|
446
719
|
const result = await db.query("getTags", {
|
|
447
720
|
startkey: id_card,
|
|
448
721
|
endkey: id_card
|
|
@@ -455,7 +728,7 @@ async function updateCredentialledCourseConfig(courseID, config) {
|
|
|
455
728
|
|
|
456
729
|
${JSON.stringify(config)}
|
|
457
730
|
`);
|
|
458
|
-
const db =
|
|
731
|
+
const db = getCourseDB2(courseID);
|
|
459
732
|
const old = await getCredentialledCourseConfig(courseID);
|
|
460
733
|
return await db.put({
|
|
461
734
|
...config,
|
|
@@ -487,7 +760,7 @@ var init_courseDB = __esm({
|
|
|
487
760
|
updateQueue;
|
|
488
761
|
constructor(id, userLookup) {
|
|
489
762
|
this.id = id;
|
|
490
|
-
this.db =
|
|
763
|
+
this.db = getCourseDB2(this.id);
|
|
491
764
|
this._getCurrentUser = userLookup;
|
|
492
765
|
this.updateQueue = new UpdateQueue(this.db);
|
|
493
766
|
}
|
|
@@ -543,14 +816,14 @@ var init_courseDB = __esm({
|
|
|
543
816
|
docs.rows.forEach((r) => {
|
|
544
817
|
if (isSuccessRow(r)) {
|
|
545
818
|
if (r.doc && r.doc.elo) {
|
|
546
|
-
ret.push(
|
|
819
|
+
ret.push(toCourseElo2(r.doc.elo));
|
|
547
820
|
} else {
|
|
548
821
|
logger.warn("no elo data for card: " + r.id);
|
|
549
|
-
ret.push(
|
|
822
|
+
ret.push(blankCourseElo2());
|
|
550
823
|
}
|
|
551
824
|
} else {
|
|
552
825
|
logger.warn("no elo data for card: " + JSON.stringify(r));
|
|
553
|
-
ret.push(
|
|
826
|
+
ret.push(blankCourseElo2());
|
|
554
827
|
}
|
|
555
828
|
});
|
|
556
829
|
return ret;
|
|
@@ -703,7 +976,7 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
703
976
|
async getCourseTagStubs() {
|
|
704
977
|
return getCourseTagStubs(this.id);
|
|
705
978
|
}
|
|
706
|
-
async addNote(codeCourse, shape, data, author, tags, uploads, elo =
|
|
979
|
+
async addNote(codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo2()) {
|
|
707
980
|
try {
|
|
708
981
|
const resp = await addNote55(this.id, codeCourse, shape, data, author, tags, uploads, elo);
|
|
709
982
|
if (resp.ok) {
|
|
@@ -891,26 +1164,75 @@ ${above.rows.map((r) => ` ${r.id}-${r.key}
|
|
|
891
1164
|
}
|
|
892
1165
|
});
|
|
893
1166
|
|
|
894
|
-
// src/impl/
|
|
895
|
-
|
|
896
|
-
|
|
1167
|
+
// src/impl/couch/classroomDB.ts
|
|
1168
|
+
import moment2 from "moment";
|
|
1169
|
+
var init_classroomDB2 = __esm({
|
|
1170
|
+
"src/impl/couch/classroomDB.ts"() {
|
|
897
1171
|
"use strict";
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
|
|
1172
|
+
init_factory();
|
|
1173
|
+
init_logger();
|
|
1174
|
+
init_pouchdb_setup();
|
|
1175
|
+
init_couch();
|
|
1176
|
+
init_courseDB();
|
|
1177
|
+
}
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
// src/impl/couch/adminDB.ts
|
|
1181
|
+
var init_adminDB2 = __esm({
|
|
1182
|
+
"src/impl/couch/adminDB.ts"() {
|
|
1183
|
+
"use strict";
|
|
1184
|
+
init_pouchdb_setup();
|
|
1185
|
+
init_factory();
|
|
1186
|
+
init_couch();
|
|
1187
|
+
init_classroomDB2();
|
|
1188
|
+
init_courseLookupDB();
|
|
1189
|
+
init_logger();
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
// src/impl/couch/auth.ts
|
|
1194
|
+
var init_auth = __esm({
|
|
1195
|
+
"src/impl/couch/auth.ts"() {
|
|
907
1196
|
"use strict";
|
|
1197
|
+
init_factory();
|
|
908
1198
|
init_types_legacy();
|
|
1199
|
+
init_logger();
|
|
909
1200
|
}
|
|
910
1201
|
});
|
|
911
1202
|
|
|
912
|
-
// src/impl/
|
|
913
|
-
import
|
|
1203
|
+
// src/impl/couch/CouchDBSyncStrategy.ts
|
|
1204
|
+
import { Status as Status2 } from "@vue-skuilder/common";
|
|
1205
|
+
var init_CouchDBSyncStrategy = __esm({
|
|
1206
|
+
"src/impl/couch/CouchDBSyncStrategy.ts"() {
|
|
1207
|
+
"use strict";
|
|
1208
|
+
init_factory();
|
|
1209
|
+
init_types_legacy();
|
|
1210
|
+
init_logger();
|
|
1211
|
+
init_common();
|
|
1212
|
+
init_pouchdb_setup();
|
|
1213
|
+
init_couch();
|
|
1214
|
+
init_auth();
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
// src/impl/couch/index.ts
|
|
1219
|
+
import moment3 from "moment";
|
|
1220
|
+
import process2 from "process";
|
|
1221
|
+
function getCourseDB2(courseID) {
|
|
1222
|
+
return new pouchdb_setup_default(
|
|
1223
|
+
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
|
|
1224
|
+
pouchDBincludeCredentialsConfig
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
function getCourseDocs(courseID, docIDs, options = {}) {
|
|
1228
|
+
return getCourseDB2(courseID).allDocs({
|
|
1229
|
+
...options,
|
|
1230
|
+
keys: docIDs
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
function getCourseDoc(courseID, docID, options = {}) {
|
|
1234
|
+
return getCourseDB2(courseID).get(docID, options);
|
|
1235
|
+
}
|
|
914
1236
|
function filterAllDocsByPrefix2(db, prefix, opts) {
|
|
915
1237
|
const options = {
|
|
916
1238
|
startkey: prefix,
|
|
@@ -928,49 +1250,39 @@ function getStartAndEndKeys2(key) {
|
|
|
928
1250
|
endkey: key + "\uFFF0"
|
|
929
1251
|
};
|
|
930
1252
|
}
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
function scheduleCardReviewLocal(userDB, review) {
|
|
935
|
-
const now = moment.utc();
|
|
936
|
-
logger.info(`Scheduling for review in: ${review.time.diff(now, "h") / 24} days`);
|
|
937
|
-
void userDB.put({
|
|
938
|
-
_id: REVIEW_PREFIX + review.time.format(REVIEW_TIME_FORMAT),
|
|
939
|
-
cardId: review.card_id,
|
|
940
|
-
reviewTime: review.time,
|
|
941
|
-
courseId: review.course_id,
|
|
942
|
-
scheduledAt: now,
|
|
943
|
-
scheduledFor: review.scheduledFor,
|
|
944
|
-
schedulingAgentId: review.schedulingAgentId
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
async function removeScheduledCardReviewLocal(userDB, reviewDocID) {
|
|
948
|
-
const reviewDoc = await userDB.get(reviewDocID);
|
|
949
|
-
userDB.remove(reviewDoc).then((res) => {
|
|
950
|
-
if (res.ok) {
|
|
951
|
-
log(`Removed Review Doc: ${reviewDocID}`);
|
|
952
|
-
}
|
|
953
|
-
}).catch((err) => {
|
|
954
|
-
log(`Failed to remove Review Doc: ${reviewDocID},
|
|
955
|
-
${JSON.stringify(err)}`);
|
|
956
|
-
});
|
|
957
|
-
}
|
|
958
|
-
var REVIEW_PREFIX, REVIEW_TIME_FORMAT, log;
|
|
959
|
-
var init_userDBHelpers = __esm({
|
|
960
|
-
"src/impl/common/userDBHelpers.ts"() {
|
|
1253
|
+
var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_PREFIX2, REVIEW_TIME_FORMAT2;
|
|
1254
|
+
var init_couch = __esm({
|
|
1255
|
+
"src/impl/couch/index.ts"() {
|
|
961
1256
|
"use strict";
|
|
1257
|
+
init_factory();
|
|
1258
|
+
init_types_legacy();
|
|
962
1259
|
init_logger();
|
|
963
1260
|
init_pouchdb_setup();
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
1261
|
+
init_contentSource();
|
|
1262
|
+
init_adminDB2();
|
|
1263
|
+
init_classroomDB2();
|
|
1264
|
+
init_courseAPI();
|
|
1265
|
+
init_courseDB();
|
|
1266
|
+
init_CouchDBSyncStrategy();
|
|
1267
|
+
isBrowser = typeof window !== "undefined";
|
|
1268
|
+
if (isBrowser) {
|
|
1269
|
+
window.process = process2;
|
|
1270
|
+
}
|
|
1271
|
+
GUEST_LOCAL_DB = `userdb-${GuestUsername}`;
|
|
1272
|
+
localUserDB = new pouchdb_setup_default(GUEST_LOCAL_DB);
|
|
1273
|
+
pouchDBincludeCredentialsConfig = {
|
|
1274
|
+
fetch(url, opts) {
|
|
1275
|
+
opts.credentials = "include";
|
|
1276
|
+
return pouchdb_setup_default.fetch(url, opts);
|
|
1277
|
+
}
|
|
968
1278
|
};
|
|
1279
|
+
REVIEW_PREFIX2 = "card_review_";
|
|
1280
|
+
REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
969
1281
|
}
|
|
970
1282
|
});
|
|
971
1283
|
|
|
972
1284
|
// src/impl/couch/user-course-relDB.ts
|
|
973
|
-
import
|
|
1285
|
+
import moment4 from "moment";
|
|
974
1286
|
var UsrCrsData;
|
|
975
1287
|
var init_user_course_relDB = __esm({
|
|
976
1288
|
"src/impl/couch/user-course-relDB.ts"() {
|
|
@@ -988,11 +1300,11 @@ var init_user_course_relDB = __esm({
|
|
|
988
1300
|
this._courseId = courseId;
|
|
989
1301
|
}
|
|
990
1302
|
async getReviewsForcast(daysCount) {
|
|
991
|
-
const time =
|
|
1303
|
+
const time = moment4.utc().add(daysCount, "days");
|
|
992
1304
|
return this.getReviewstoDate(time);
|
|
993
1305
|
}
|
|
994
1306
|
async getPendingReviews() {
|
|
995
|
-
const now =
|
|
1307
|
+
const now = moment4.utc();
|
|
996
1308
|
return this.getReviewstoDate(now);
|
|
997
1309
|
}
|
|
998
1310
|
async getScheduledReviewCount() {
|
|
@@ -1012,7 +1324,7 @@ var init_user_course_relDB = __esm({
|
|
|
1012
1324
|
void this.user.updateCourseSettings(this._courseId, updates);
|
|
1013
1325
|
}
|
|
1014
1326
|
async getReviewstoDate(targetDate) {
|
|
1015
|
-
const keys =
|
|
1327
|
+
const keys = getStartAndEndKeys2(REVIEW_PREFIX2);
|
|
1016
1328
|
const reviews = await this.user.remote().allDocs({
|
|
1017
1329
|
startkey: keys.startkey,
|
|
1018
1330
|
endkey: keys.endkey,
|
|
@@ -1023,7 +1335,7 @@ var init_user_course_relDB = __esm({
|
|
|
1023
1335
|
);
|
|
1024
1336
|
return reviews.rows.filter((r) => {
|
|
1025
1337
|
if (r.id.startsWith(REVIEW_PREFIX2)) {
|
|
1026
|
-
const date =
|
|
1338
|
+
const date = moment4.utc(r.id.substr(REVIEW_PREFIX2.length), REVIEW_TIME_FORMAT2);
|
|
1027
1339
|
if (targetDate.isAfter(date)) {
|
|
1028
1340
|
if (this._courseId === void 0 || r.doc.courseId === this._courseId) {
|
|
1029
1341
|
return true;
|
|
@@ -1037,8 +1349,8 @@ var init_user_course_relDB = __esm({
|
|
|
1037
1349
|
});
|
|
1038
1350
|
|
|
1039
1351
|
// src/impl/common/BaseUserDB.ts
|
|
1040
|
-
import { Status as
|
|
1041
|
-
import
|
|
1352
|
+
import { Status as Status3 } from "@vue-skuilder/common";
|
|
1353
|
+
import moment5 from "moment";
|
|
1042
1354
|
async function getOrCreateClassroomRegistrationsDoc(user) {
|
|
1043
1355
|
let ret;
|
|
1044
1356
|
try {
|
|
@@ -1098,7 +1410,7 @@ async function updateUserElo(user, course_id, elo) {
|
|
|
1098
1410
|
return getLocalUserDB(user).put(regDoc);
|
|
1099
1411
|
}
|
|
1100
1412
|
async function registerUserForClassroom(user, classID, registerAs) {
|
|
1101
|
-
|
|
1413
|
+
log3(`Registering user: ${user} in course: ${classID}`);
|
|
1102
1414
|
return getOrCreateClassroomRegistrationsDoc(user).then((doc) => {
|
|
1103
1415
|
const regItem = {
|
|
1104
1416
|
classID,
|
|
@@ -1109,7 +1421,7 @@ async function registerUserForClassroom(user, classID, registerAs) {
|
|
|
1109
1421
|
}).length === 0) {
|
|
1110
1422
|
doc.registrations.push(regItem);
|
|
1111
1423
|
} else {
|
|
1112
|
-
|
|
1424
|
+
log3(`User ${user} is already registered for class ${classID}`);
|
|
1113
1425
|
}
|
|
1114
1426
|
return getLocalUserDB(user).put(doc);
|
|
1115
1427
|
});
|
|
@@ -1131,7 +1443,7 @@ async function dropUserFromClassroom(user, classID) {
|
|
|
1131
1443
|
async function getUserClassrooms(user) {
|
|
1132
1444
|
return getOrCreateClassroomRegistrationsDoc(user);
|
|
1133
1445
|
}
|
|
1134
|
-
var
|
|
1446
|
+
var log3, cardHistoryPrefix2, BaseUser, userCoursesDoc, userClassroomsDoc;
|
|
1135
1447
|
var init_BaseUserDB = __esm({
|
|
1136
1448
|
"src/impl/common/BaseUserDB.ts"() {
|
|
1137
1449
|
"use strict";
|
|
@@ -1142,7 +1454,7 @@ var init_BaseUserDB = __esm({
|
|
|
1142
1454
|
init_updateQueue();
|
|
1143
1455
|
init_user_course_relDB();
|
|
1144
1456
|
init_couch();
|
|
1145
|
-
|
|
1457
|
+
log3 = (s) => {
|
|
1146
1458
|
logger.info(s);
|
|
1147
1459
|
};
|
|
1148
1460
|
cardHistoryPrefix2 = "cardH-";
|
|
@@ -1183,10 +1495,14 @@ Currently logged-in as ${this._username}.`
|
|
|
1183
1495
|
);
|
|
1184
1496
|
}
|
|
1185
1497
|
const result = await this.syncStrategy.createAccount(username, password);
|
|
1186
|
-
if (result.status ===
|
|
1187
|
-
|
|
1498
|
+
if (result.status === Status3.ok) {
|
|
1499
|
+
log3(`Account created successfully, updating username to ${username}`);
|
|
1188
1500
|
this._username = username;
|
|
1189
|
-
|
|
1501
|
+
try {
|
|
1502
|
+
localStorage.removeItem("dbUUID");
|
|
1503
|
+
} catch (e) {
|
|
1504
|
+
logger.warn("localStorage not available (Node.js environment):", e);
|
|
1505
|
+
}
|
|
1190
1506
|
await this.init();
|
|
1191
1507
|
}
|
|
1192
1508
|
return {
|
|
@@ -1198,15 +1514,22 @@ Currently logged-in as ${this._username}.`
|
|
|
1198
1514
|
if (!this.syncStrategy.canAuthenticate()) {
|
|
1199
1515
|
throw new Error("Authentication not supported by current sync strategy");
|
|
1200
1516
|
}
|
|
1201
|
-
if (!this._username.startsWith(GuestUsername)) {
|
|
1202
|
-
|
|
1203
|
-
|
|
1517
|
+
if (!this._username.startsWith(GuestUsername) && this._username != username) {
|
|
1518
|
+
if (this._username != username) {
|
|
1519
|
+
throw new Error(`Cannot change accounts while logged in.
|
|
1520
|
+
Log out of account ${this.getUsername()} before logging in as ${username}.`);
|
|
1521
|
+
}
|
|
1522
|
+
logger.warn(`User ${this._username} is already logged in, but executing login again.`);
|
|
1204
1523
|
}
|
|
1205
1524
|
const loginResult = await this.syncStrategy.authenticate(username, password);
|
|
1206
1525
|
if (loginResult.ok) {
|
|
1207
|
-
|
|
1526
|
+
log3(`Logged in as ${username}`);
|
|
1208
1527
|
this._username = username;
|
|
1209
|
-
|
|
1528
|
+
try {
|
|
1529
|
+
localStorage.removeItem("dbUUID");
|
|
1530
|
+
} catch (e) {
|
|
1531
|
+
logger.warn("localStorage not available (Node.js environment):", e);
|
|
1532
|
+
}
|
|
1210
1533
|
await this.init();
|
|
1211
1534
|
}
|
|
1212
1535
|
return loginResult;
|
|
@@ -1214,7 +1537,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1214
1537
|
async resetUserData() {
|
|
1215
1538
|
if (this.syncStrategy.canAuthenticate()) {
|
|
1216
1539
|
return {
|
|
1217
|
-
status:
|
|
1540
|
+
status: Status3.error,
|
|
1218
1541
|
error: "Reset user data is only available for local-only mode. Use logout instead for remote sync."
|
|
1219
1542
|
};
|
|
1220
1543
|
}
|
|
@@ -1233,11 +1556,11 @@ Currently logged-in as ${this._username}.`
|
|
|
1233
1556
|
await localDB.bulkDocs(docsToDelete);
|
|
1234
1557
|
}
|
|
1235
1558
|
await this.init();
|
|
1236
|
-
return { status:
|
|
1559
|
+
return { status: Status3.ok };
|
|
1237
1560
|
} catch (error) {
|
|
1238
1561
|
logger.error("Failed to reset user data:", error);
|
|
1239
1562
|
return {
|
|
1240
|
-
status:
|
|
1563
|
+
status: Status3.error,
|
|
1241
1564
|
error: error instanceof Error ? error.message : "Unknown error during reset"
|
|
1242
1565
|
};
|
|
1243
1566
|
}
|
|
@@ -1293,7 +1616,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1293
1616
|
*
|
|
1294
1617
|
*/
|
|
1295
1618
|
async getActiveCards() {
|
|
1296
|
-
const keys =
|
|
1619
|
+
const keys = getStartAndEndKeys(REVIEW_PREFIX);
|
|
1297
1620
|
const reviews = await this.remoteDB.allDocs({
|
|
1298
1621
|
startkey: keys.startkey,
|
|
1299
1622
|
endkey: keys.endkey,
|
|
@@ -1365,18 +1688,18 @@ Currently logged-in as ${this._username}.`
|
|
|
1365
1688
|
}
|
|
1366
1689
|
}
|
|
1367
1690
|
async getReviewstoDate(targetDate, course_id) {
|
|
1368
|
-
const keys =
|
|
1691
|
+
const keys = getStartAndEndKeys(REVIEW_PREFIX);
|
|
1369
1692
|
const reviews = await this.remoteDB.allDocs({
|
|
1370
1693
|
startkey: keys.startkey,
|
|
1371
1694
|
endkey: keys.endkey,
|
|
1372
1695
|
include_docs: true
|
|
1373
1696
|
});
|
|
1374
|
-
|
|
1697
|
+
log3(
|
|
1375
1698
|
`Fetching ${this._username}'s scheduled reviews${course_id ? ` for course ${course_id}` : ""}.`
|
|
1376
1699
|
);
|
|
1377
1700
|
return reviews.rows.filter((r) => {
|
|
1378
1701
|
if (r.id.startsWith(REVIEW_PREFIX)) {
|
|
1379
|
-
const date =
|
|
1702
|
+
const date = moment5.utc(r.id.substr(REVIEW_PREFIX.length), REVIEW_TIME_FORMAT);
|
|
1380
1703
|
if (targetDate.isAfter(date)) {
|
|
1381
1704
|
if (course_id === void 0 || r.doc.courseId === course_id) {
|
|
1382
1705
|
return true;
|
|
@@ -1386,11 +1709,11 @@ Currently logged-in as ${this._username}.`
|
|
|
1386
1709
|
}).map((r) => r.doc);
|
|
1387
1710
|
}
|
|
1388
1711
|
async getReviewsForcast(daysCount) {
|
|
1389
|
-
const time =
|
|
1712
|
+
const time = moment5.utc().add(daysCount, "days");
|
|
1390
1713
|
return this.getReviewstoDate(time);
|
|
1391
1714
|
}
|
|
1392
1715
|
async getPendingReviews(course_id) {
|
|
1393
|
-
const now =
|
|
1716
|
+
const now = moment5.utc();
|
|
1394
1717
|
return this.getReviewstoDate(now, course_id);
|
|
1395
1718
|
}
|
|
1396
1719
|
async getScheduledReviewCount(course_id) {
|
|
@@ -1433,12 +1756,12 @@ Currently logged-in as ${this._username}.`
|
|
|
1433
1756
|
if (doc.courses.filter((course) => {
|
|
1434
1757
|
return course.courseID === regItem.courseID;
|
|
1435
1758
|
}).length === 0) {
|
|
1436
|
-
|
|
1759
|
+
log3(`It's a new course registration!`);
|
|
1437
1760
|
doc.courses.push(regItem);
|
|
1438
1761
|
doc.studyWeight[course_id] = 1;
|
|
1439
1762
|
} else {
|
|
1440
1763
|
doc.courses.forEach((c) => {
|
|
1441
|
-
|
|
1764
|
+
log3(`Found the previously registered course!`);
|
|
1442
1765
|
if (c.courseID === course_id) {
|
|
1443
1766
|
c.status = status;
|
|
1444
1767
|
}
|
|
@@ -1446,7 +1769,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1446
1769
|
}
|
|
1447
1770
|
return this.localDB.put(doc);
|
|
1448
1771
|
}).catch((e) => {
|
|
1449
|
-
|
|
1772
|
+
log3(`Registration failed because of: ${JSON.stringify(e)}`);
|
|
1450
1773
|
throw e;
|
|
1451
1774
|
});
|
|
1452
1775
|
}
|
|
@@ -1568,6 +1891,10 @@ Currently logged-in as ${this._username}.`
|
|
|
1568
1891
|
}
|
|
1569
1892
|
async init() {
|
|
1570
1893
|
_BaseUser._initialized = false;
|
|
1894
|
+
if (this._username === "admin") {
|
|
1895
|
+
_BaseUser._initialized = true;
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1571
1898
|
this.setDBandQ();
|
|
1572
1899
|
this.syncStrategy.startSync(this.localDB, this.remoteDB);
|
|
1573
1900
|
void this.applyDesignDocs();
|
|
@@ -1589,6 +1916,9 @@ Currently logged-in as ${this._username}.`
|
|
|
1589
1916
|
}
|
|
1590
1917
|
];
|
|
1591
1918
|
async applyDesignDocs() {
|
|
1919
|
+
if (this._username === "admin") {
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1592
1922
|
for (const doc of _BaseUser.designDocs) {
|
|
1593
1923
|
try {
|
|
1594
1924
|
try {
|
|
@@ -1605,7 +1935,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1605
1935
|
}
|
|
1606
1936
|
}
|
|
1607
1937
|
} catch (error) {
|
|
1608
|
-
if (error
|
|
1938
|
+
if (error.name && error.name === "conflict") {
|
|
1609
1939
|
logger.warn(`Design doc ${doc._id} update conflict - will retry`);
|
|
1610
1940
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
1611
1941
|
await this.applyDesignDoc(doc);
|
|
@@ -1643,7 +1973,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1643
1973
|
*/
|
|
1644
1974
|
async putCardRecord(record) {
|
|
1645
1975
|
const cardHistoryID = getCardHistoryID(record.courseID, record.cardID);
|
|
1646
|
-
record.timeStamp =
|
|
1976
|
+
record.timeStamp = moment5.utc(record.timeStamp).toString();
|
|
1647
1977
|
try {
|
|
1648
1978
|
const cardHistory = await this.update(
|
|
1649
1979
|
cardHistoryID,
|
|
@@ -1659,7 +1989,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1659
1989
|
const ret = {
|
|
1660
1990
|
...record2
|
|
1661
1991
|
};
|
|
1662
|
-
ret.timeStamp =
|
|
1992
|
+
ret.timeStamp = moment5.utc(record2.timeStamp);
|
|
1663
1993
|
return ret;
|
|
1664
1994
|
});
|
|
1665
1995
|
return cardHistory;
|
|
@@ -1687,17 +2017,17 @@ Currently logged-in as ${this._username}.`
|
|
|
1687
2017
|
}
|
|
1688
2018
|
async deduplicateReviews() {
|
|
1689
2019
|
try {
|
|
1690
|
-
|
|
2020
|
+
log3("Starting deduplication of scheduled reviews...");
|
|
1691
2021
|
const reviewsMap = {};
|
|
1692
2022
|
const duplicateDocIds = [];
|
|
1693
2023
|
const scheduledReviews = await this.remoteDB.query("reviewCards/reviewCards");
|
|
1694
|
-
|
|
2024
|
+
log3(`Found ${scheduledReviews.rows.length} scheduled reviews to process`);
|
|
1695
2025
|
scheduledReviews.rows.forEach((r) => {
|
|
1696
2026
|
const qualifiedCardId = r.value;
|
|
1697
2027
|
const docId = r.key;
|
|
1698
2028
|
if (reviewsMap[qualifiedCardId]) {
|
|
1699
|
-
|
|
1700
|
-
|
|
2029
|
+
log3(`Found duplicate scheduled review for card: ${qualifiedCardId}`);
|
|
2030
|
+
log3(
|
|
1701
2031
|
`Marking earlier review ${reviewsMap[qualifiedCardId]} for deletion, keeping ${docId}`
|
|
1702
2032
|
);
|
|
1703
2033
|
duplicateDocIds.push(reviewsMap[qualifiedCardId]);
|
|
@@ -1707,23 +2037,23 @@ Currently logged-in as ${this._username}.`
|
|
|
1707
2037
|
}
|
|
1708
2038
|
});
|
|
1709
2039
|
if (duplicateDocIds.length > 0) {
|
|
1710
|
-
|
|
2040
|
+
log3(`Removing ${duplicateDocIds.length} duplicate reviews...`);
|
|
1711
2041
|
const deletePromises = duplicateDocIds.map(async (docId) => {
|
|
1712
2042
|
try {
|
|
1713
2043
|
const doc = await this.remoteDB.get(docId);
|
|
1714
2044
|
await this.remoteDB.remove(doc);
|
|
1715
|
-
|
|
2045
|
+
log3(`Successfully removed duplicate review: ${docId}`);
|
|
1716
2046
|
} catch (error) {
|
|
1717
|
-
|
|
2047
|
+
log3(`Failed to remove duplicate review ${docId}: ${error}`);
|
|
1718
2048
|
}
|
|
1719
2049
|
});
|
|
1720
2050
|
await Promise.all(deletePromises);
|
|
1721
|
-
|
|
2051
|
+
log3(`Deduplication complete. Processed ${duplicateDocIds.length} duplicates`);
|
|
1722
2052
|
} else {
|
|
1723
|
-
|
|
2053
|
+
log3("No duplicate reviews found");
|
|
1724
2054
|
}
|
|
1725
2055
|
} catch (error) {
|
|
1726
|
-
|
|
2056
|
+
log3(`Error during review deduplication: ${error}`);
|
|
1727
2057
|
}
|
|
1728
2058
|
}
|
|
1729
2059
|
/**
|
|
@@ -1737,7 +2067,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1737
2067
|
if (course_id) {
|
|
1738
2068
|
prefix += course_id;
|
|
1739
2069
|
}
|
|
1740
|
-
const docs = await
|
|
2070
|
+
const docs = await filterAllDocsByPrefix(this.localDB, prefix, {
|
|
1741
2071
|
include_docs: false
|
|
1742
2072
|
});
|
|
1743
2073
|
const ret = [];
|
|
@@ -1753,7 +2083,7 @@ Currently logged-in as ${this._username}.`
|
|
|
1753
2083
|
* @returns A promise of the cards that the user has seen in the past.
|
|
1754
2084
|
*/
|
|
1755
2085
|
async getHistory() {
|
|
1756
|
-
const cards = await
|
|
2086
|
+
const cards = await filterAllDocsByPrefix(
|
|
1757
2087
|
this.remoteDB,
|
|
1758
2088
|
cardHistoryPrefix2,
|
|
1759
2089
|
{
|
|
@@ -1877,298 +2207,17 @@ var init_common = __esm({
|
|
|
1877
2207
|
}
|
|
1878
2208
|
});
|
|
1879
2209
|
|
|
1880
|
-
// src/
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
async function addNote55(courseID, codeCourse, shape, data, author, tags, uploads, elo = blankCourseElo2()) {
|
|
1885
|
-
const db = getCourseDB2(courseID);
|
|
1886
|
-
const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
|
|
1887
|
-
const result = await db.post(payload);
|
|
1888
|
-
const dataShapeId = NameSpacer.getDataShapeString({
|
|
1889
|
-
course: codeCourse,
|
|
1890
|
-
dataShape: shape.name
|
|
1891
|
-
});
|
|
1892
|
-
if (result.ok) {
|
|
1893
|
-
try {
|
|
1894
|
-
await createCards(courseID, dataShapeId, result.id, tags, elo, author);
|
|
1895
|
-
} catch (error) {
|
|
1896
|
-
let errorMessage = "Unknown error";
|
|
1897
|
-
if (error instanceof Error) {
|
|
1898
|
-
errorMessage = error.message;
|
|
1899
|
-
} else if (error && typeof error === "object" && "reason" in error) {
|
|
1900
|
-
errorMessage = error.reason;
|
|
1901
|
-
} else if (error && typeof error === "object" && "message" in error) {
|
|
1902
|
-
errorMessage = error.message;
|
|
1903
|
-
} else {
|
|
1904
|
-
errorMessage = String(error);
|
|
1905
|
-
}
|
|
1906
|
-
logger.error(`[addNote55] Failed to create cards for note ${result.id}: ${errorMessage}`);
|
|
1907
|
-
result.cardCreationFailed = true;
|
|
1908
|
-
result.cardCreationError = errorMessage;
|
|
1909
|
-
}
|
|
1910
|
-
} else {
|
|
1911
|
-
logger.error(`[addNote55] Error adding note. Result: ${JSON.stringify(result)}`);
|
|
1912
|
-
}
|
|
1913
|
-
return result;
|
|
1914
|
-
}
|
|
1915
|
-
async function createCards(courseID, datashapeID, noteID, tags, elo = blankCourseElo2(), author) {
|
|
1916
|
-
const cfg = await getCredentialledCourseConfig(courseID);
|
|
1917
|
-
const dsDescriptor = NameSpacer.getDataShapeDescriptor(datashapeID);
|
|
1918
|
-
let questionViewTypes = [];
|
|
1919
|
-
for (const ds of cfg.dataShapes) {
|
|
1920
|
-
if (ds.name === datashapeID) {
|
|
1921
|
-
questionViewTypes = ds.questionTypes;
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
if (questionViewTypes.length === 0) {
|
|
1925
|
-
const errorMsg = `No questionViewTypes found for datashapeID: ${datashapeID} in course config. Cards cannot be created.`;
|
|
1926
|
-
logger.error(errorMsg);
|
|
1927
|
-
throw new Error(errorMsg);
|
|
1928
|
-
}
|
|
1929
|
-
for (const questionView of questionViewTypes) {
|
|
1930
|
-
await createCard(questionView, courseID, dsDescriptor, noteID, tags, elo, author);
|
|
1931
|
-
}
|
|
1932
|
-
}
|
|
1933
|
-
async function createCard(questionViewName, courseID, dsDescriptor, noteID, tags, elo = blankCourseElo2(), author) {
|
|
1934
|
-
const qDescriptor = NameSpacer.getQuestionDescriptor(questionViewName);
|
|
1935
|
-
const cfg = await getCredentialledCourseConfig(courseID);
|
|
1936
|
-
for (const rQ of cfg.questionTypes) {
|
|
1937
|
-
if (rQ.name === questionViewName) {
|
|
1938
|
-
for (const view of rQ.viewList) {
|
|
1939
|
-
await addCard(
|
|
1940
|
-
courseID,
|
|
1941
|
-
dsDescriptor.course,
|
|
1942
|
-
[noteID],
|
|
1943
|
-
NameSpacer.getViewString({
|
|
1944
|
-
course: qDescriptor.course,
|
|
1945
|
-
questionType: qDescriptor.questionType,
|
|
1946
|
-
view
|
|
1947
|
-
}),
|
|
1948
|
-
elo,
|
|
1949
|
-
tags,
|
|
1950
|
-
author
|
|
1951
|
-
);
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
async function addCard(courseID, course, id_displayable_data, id_view, elo, tags, author) {
|
|
1957
|
-
const db = getCourseDB2(courseID);
|
|
1958
|
-
const card = await db.post({
|
|
1959
|
-
course,
|
|
1960
|
-
id_displayable_data,
|
|
1961
|
-
id_view,
|
|
1962
|
-
docType: "CARD" /* CARD */,
|
|
1963
|
-
elo: elo || toCourseElo2(990 + Math.round(20 * Math.random())),
|
|
1964
|
-
author
|
|
1965
|
-
});
|
|
1966
|
-
for (const tag of tags) {
|
|
1967
|
-
logger.info(`adding tag: ${tag} to card ${card.id}`);
|
|
1968
|
-
await addTagToCard(courseID, card.id, tag, author, false);
|
|
1969
|
-
}
|
|
1970
|
-
return card;
|
|
1971
|
-
}
|
|
1972
|
-
async function getCredentialledCourseConfig(courseID) {
|
|
1973
|
-
try {
|
|
1974
|
-
const db = getCourseDB2(courseID);
|
|
1975
|
-
const ret = await db.get("CourseConfig");
|
|
1976
|
-
ret.courseID = courseID;
|
|
1977
|
-
logger.info(`Returning course config: ${JSON.stringify(ret)}`);
|
|
1978
|
-
return ret;
|
|
1979
|
-
} catch (e) {
|
|
1980
|
-
logger.error(`Error fetching config for ${courseID}:`, e);
|
|
1981
|
-
throw e;
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
async function addTagToCard(courseID, cardID, tagID, author, updateELO = true) {
|
|
1985
|
-
const prefixedTagID = getTagID(tagID);
|
|
1986
|
-
const courseDB = getCourseDB2(courseID);
|
|
1987
|
-
const courseApi = new CourseDB(courseID, async () => {
|
|
1988
|
-
const dummySyncStrategy = {
|
|
1989
|
-
setupRemoteDB: () => null,
|
|
1990
|
-
startSync: () => {
|
|
1991
|
-
},
|
|
1992
|
-
canCreateAccount: () => false,
|
|
1993
|
-
canAuthenticate: () => false,
|
|
1994
|
-
getCurrentUsername: async () => "DummyUser"
|
|
1995
|
-
};
|
|
1996
|
-
return BaseUser.Dummy(dummySyncStrategy);
|
|
1997
|
-
});
|
|
1998
|
-
try {
|
|
1999
|
-
logger.info(`Applying tag ${tagID} to card ${courseID + "-" + cardID}...`);
|
|
2000
|
-
const tag = await courseDB.get(prefixedTagID);
|
|
2001
|
-
if (!tag.taggedCards.includes(cardID)) {
|
|
2002
|
-
tag.taggedCards.push(cardID);
|
|
2003
|
-
if (updateELO) {
|
|
2004
|
-
try {
|
|
2005
|
-
const eloData = await courseApi.getCardEloData([cardID]);
|
|
2006
|
-
const elo = eloData[0];
|
|
2007
|
-
elo.tags[tagID] = {
|
|
2008
|
-
count: 0,
|
|
2009
|
-
score: elo.global.score
|
|
2010
|
-
// todo: or 1000?
|
|
2011
|
-
};
|
|
2012
|
-
await updateCardElo(courseID, cardID, elo);
|
|
2013
|
-
} catch (error) {
|
|
2014
|
-
logger.error("Failed to update ELO data for card:", cardID, error);
|
|
2015
|
-
}
|
|
2016
|
-
}
|
|
2017
|
-
return courseDB.put(tag);
|
|
2018
|
-
} else throw new AlreadyTaggedErr(`Card ${cardID} is already tagged with ${tagID}`);
|
|
2019
|
-
} catch (e) {
|
|
2020
|
-
if (e instanceof AlreadyTaggedErr) {
|
|
2021
|
-
throw e;
|
|
2022
|
-
}
|
|
2023
|
-
await createTag(courseID, tagID, author);
|
|
2024
|
-
return addTagToCard(courseID, cardID, tagID, author, updateELO);
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
async function updateCardElo(courseID, cardID, elo) {
|
|
2028
|
-
if (elo) {
|
|
2029
|
-
const cDB = getCourseDB2(courseID);
|
|
2030
|
-
const card = await cDB.get(cardID);
|
|
2031
|
-
logger.debug(`Replacing ${JSON.stringify(card.elo)} with ${JSON.stringify(elo)}`);
|
|
2032
|
-
card.elo = elo;
|
|
2033
|
-
return cDB.put(card);
|
|
2034
|
-
}
|
|
2035
|
-
}
|
|
2036
|
-
function getTagID(tagName) {
|
|
2037
|
-
const tagPrefix = "TAG" /* TAG */.valueOf() + "-";
|
|
2038
|
-
if (tagName.indexOf(tagPrefix) === 0) {
|
|
2039
|
-
return tagName;
|
|
2040
|
-
} else {
|
|
2041
|
-
return tagPrefix + tagName;
|
|
2042
|
-
}
|
|
2043
|
-
}
|
|
2044
|
-
function getCourseDB2(courseID) {
|
|
2045
|
-
const dbName = `coursedb-${courseID}`;
|
|
2046
|
-
return new pouchdb_setup_default(
|
|
2047
|
-
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + dbName,
|
|
2048
|
-
pouchDBincludeCredentialsConfig
|
|
2049
|
-
);
|
|
2050
|
-
}
|
|
2051
|
-
var AlreadyTaggedErr;
|
|
2052
|
-
var init_courseAPI = __esm({
|
|
2053
|
-
"src/impl/couch/courseAPI.ts"() {
|
|
2054
|
-
"use strict";
|
|
2055
|
-
init_pouchdb_setup();
|
|
2056
|
-
init_couch();
|
|
2057
|
-
init_factory();
|
|
2058
|
-
init_courseDB();
|
|
2059
|
-
init_types_legacy();
|
|
2060
|
-
init_common();
|
|
2061
|
-
init_logger();
|
|
2062
|
-
AlreadyTaggedErr = class extends Error {
|
|
2063
|
-
constructor(message) {
|
|
2064
|
-
super(message);
|
|
2065
|
-
this.name = "AlreadyTaggedErr";
|
|
2066
|
-
}
|
|
2067
|
-
};
|
|
2068
|
-
}
|
|
2069
|
-
});
|
|
2070
|
-
|
|
2071
|
-
// src/impl/couch/auth.ts
|
|
2072
|
-
var init_auth = __esm({
|
|
2073
|
-
"src/impl/couch/auth.ts"() {
|
|
2074
|
-
"use strict";
|
|
2075
|
-
init_factory();
|
|
2076
|
-
init_types_legacy();
|
|
2077
|
-
init_logger();
|
|
2078
|
-
}
|
|
2079
|
-
});
|
|
2080
|
-
|
|
2081
|
-
// src/impl/couch/CouchDBSyncStrategy.ts
|
|
2082
|
-
import { Status as Status3 } from "@vue-skuilder/common";
|
|
2083
|
-
var init_CouchDBSyncStrategy = __esm({
|
|
2084
|
-
"src/impl/couch/CouchDBSyncStrategy.ts"() {
|
|
2210
|
+
// src/factory.ts
|
|
2211
|
+
var ENV;
|
|
2212
|
+
var init_factory = __esm({
|
|
2213
|
+
"src/factory.ts"() {
|
|
2085
2214
|
"use strict";
|
|
2086
|
-
init_factory();
|
|
2087
|
-
init_types_legacy();
|
|
2088
|
-
init_logger();
|
|
2089
2215
|
init_common();
|
|
2090
|
-
init_pouchdb_setup();
|
|
2091
|
-
init_couch();
|
|
2092
|
-
init_auth();
|
|
2093
|
-
}
|
|
2094
|
-
});
|
|
2095
|
-
|
|
2096
|
-
// src/impl/couch/index.ts
|
|
2097
|
-
import moment4 from "moment";
|
|
2098
|
-
import process2 from "process";
|
|
2099
|
-
function getCourseDB(courseID) {
|
|
2100
|
-
return new pouchdb_setup_default(
|
|
2101
|
-
ENV.COUCHDB_SERVER_PROTOCOL + "://" + ENV.COUCHDB_SERVER_URL + "coursedb-" + courseID,
|
|
2102
|
-
pouchDBincludeCredentialsConfig
|
|
2103
|
-
);
|
|
2104
|
-
}
|
|
2105
|
-
function getCourseDocs(courseID, docIDs, options = {}) {
|
|
2106
|
-
return getCourseDB(courseID).allDocs({
|
|
2107
|
-
...options,
|
|
2108
|
-
keys: docIDs
|
|
2109
|
-
});
|
|
2110
|
-
}
|
|
2111
|
-
function getCourseDoc(courseID, docID, options = {}) {
|
|
2112
|
-
return getCourseDB(courseID).get(docID, options);
|
|
2113
|
-
}
|
|
2114
|
-
function filterAllDocsByPrefix(db, prefix, opts) {
|
|
2115
|
-
const options = {
|
|
2116
|
-
startkey: prefix,
|
|
2117
|
-
endkey: prefix + "\uFFF0",
|
|
2118
|
-
include_docs: true
|
|
2119
|
-
};
|
|
2120
|
-
if (opts) {
|
|
2121
|
-
Object.assign(options, opts);
|
|
2122
|
-
}
|
|
2123
|
-
return db.allDocs(options);
|
|
2124
|
-
}
|
|
2125
|
-
function getStartAndEndKeys(key) {
|
|
2126
|
-
return {
|
|
2127
|
-
startkey: key,
|
|
2128
|
-
endkey: key + "\uFFF0"
|
|
2129
|
-
};
|
|
2130
|
-
}
|
|
2131
|
-
var isBrowser, GUEST_LOCAL_DB, localUserDB, pouchDBincludeCredentialsConfig, REVIEW_PREFIX2, REVIEW_TIME_FORMAT2;
|
|
2132
|
-
var init_couch = __esm({
|
|
2133
|
-
"src/impl/couch/index.ts"() {
|
|
2134
|
-
"use strict";
|
|
2135
|
-
init_factory();
|
|
2136
|
-
init_types_legacy();
|
|
2137
2216
|
init_logger();
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
init_classroomDB2();
|
|
2142
|
-
init_courseAPI();
|
|
2143
|
-
init_courseDB();
|
|
2144
|
-
init_CouchDBSyncStrategy();
|
|
2145
|
-
isBrowser = typeof window !== "undefined";
|
|
2146
|
-
if (isBrowser) {
|
|
2147
|
-
window.process = process2;
|
|
2148
|
-
}
|
|
2149
|
-
GUEST_LOCAL_DB = `userdb-${GuestUsername}`;
|
|
2150
|
-
localUserDB = new pouchdb_setup_default(GUEST_LOCAL_DB);
|
|
2151
|
-
pouchDBincludeCredentialsConfig = {
|
|
2152
|
-
fetch(url, opts) {
|
|
2153
|
-
opts.credentials = "include";
|
|
2154
|
-
return pouchdb_setup_default.fetch(url, opts);
|
|
2155
|
-
}
|
|
2217
|
+
ENV = {
|
|
2218
|
+
COUCHDB_SERVER_PROTOCOL: "NOT_SET",
|
|
2219
|
+
COUCHDB_SERVER_URL: "NOT_SET"
|
|
2156
2220
|
};
|
|
2157
|
-
REVIEW_PREFIX2 = "card_review_";
|
|
2158
|
-
REVIEW_TIME_FORMAT2 = "YYYY-MM-DD--kk:mm:ss-SSS";
|
|
2159
|
-
}
|
|
2160
|
-
});
|
|
2161
|
-
|
|
2162
|
-
// src/impl/couch/classroomDB.ts
|
|
2163
|
-
import moment5 from "moment";
|
|
2164
|
-
var init_classroomDB2 = __esm({
|
|
2165
|
-
"src/impl/couch/classroomDB.ts"() {
|
|
2166
|
-
"use strict";
|
|
2167
|
-
init_factory();
|
|
2168
|
-
init_logger();
|
|
2169
|
-
init_pouchdb_setup();
|
|
2170
|
-
init_couch();
|
|
2171
|
-
init_courseDB();
|
|
2172
2221
|
}
|
|
2173
2222
|
});
|
|
2174
2223
|
|
|
@@ -2269,11 +2318,11 @@ var init_StaticDataUnpacker = __esm({
|
|
|
2269
2318
|
init_logger();
|
|
2270
2319
|
init_core();
|
|
2271
2320
|
pathUtils = {
|
|
2272
|
-
isAbsolute: (
|
|
2273
|
-
if (/^[a-zA-Z]:[\\/]/.test(
|
|
2321
|
+
isAbsolute: (path2) => {
|
|
2322
|
+
if (/^[a-zA-Z]:[\\/]/.test(path2) || /^\\\\/.test(path2)) {
|
|
2274
2323
|
return true;
|
|
2275
2324
|
}
|
|
2276
|
-
if (
|
|
2325
|
+
if (path2.startsWith("/")) {
|
|
2277
2326
|
return true;
|
|
2278
2327
|
}
|
|
2279
2328
|
return false;
|