@vue-skuilder/db 0.1.1
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/README.md +26 -0
- package/dist/core/index.d.mts +3 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.js +7906 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/index.mjs +7886 -0
- package/dist/core/index.mjs.map +1 -0
- package/dist/index-QMtzQI65.d.mts +734 -0
- package/dist/index-QMtzQI65.d.ts +734 -0
- package/dist/index.d.mts +133 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.js +8726 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8699 -0
- package/dist/index.mjs.map +1 -0
- package/eslint.config.mjs +20 -0
- package/package.json +47 -0
- package/src/core/bulkImport/cardProcessor.ts +165 -0
- package/src/core/bulkImport/index.ts +2 -0
- package/src/core/bulkImport/types.ts +27 -0
- package/src/core/index.ts +9 -0
- package/src/core/interfaces/adminDB.ts +27 -0
- package/src/core/interfaces/classroomDB.ts +75 -0
- package/src/core/interfaces/contentSource.ts +64 -0
- package/src/core/interfaces/courseDB.ts +139 -0
- package/src/core/interfaces/dataLayerProvider.ts +46 -0
- package/src/core/interfaces/index.ts +7 -0
- package/src/core/interfaces/navigationStrategyManager.ts +46 -0
- package/src/core/interfaces/userDB.ts +183 -0
- package/src/core/navigators/elo.ts +76 -0
- package/src/core/navigators/index.ts +57 -0
- package/src/core/readme.md +9 -0
- package/src/core/types/contentNavigationStrategy.ts +21 -0
- package/src/core/types/db.ts +7 -0
- package/src/core/types/types-legacy.ts +155 -0
- package/src/core/types/user.ts +70 -0
- package/src/core/util/index.ts +42 -0
- package/src/factory.ts +86 -0
- package/src/impl/pouch/PouchDataLayerProvider.ts +102 -0
- package/src/impl/pouch/adminDB.ts +91 -0
- package/src/impl/pouch/auth.ts +48 -0
- package/src/impl/pouch/classroomDB.ts +306 -0
- package/src/impl/pouch/clientCache.ts +19 -0
- package/src/impl/pouch/courseAPI.ts +245 -0
- package/src/impl/pouch/courseDB.ts +772 -0
- package/src/impl/pouch/courseLookupDB.ts +135 -0
- package/src/impl/pouch/index.ts +235 -0
- package/src/impl/pouch/pouchdb-setup.ts +16 -0
- package/src/impl/pouch/types.ts +7 -0
- package/src/impl/pouch/updateQueue.ts +89 -0
- package/src/impl/pouch/user-course-relDB.ts +73 -0
- package/src/impl/pouch/userDB.ts +1097 -0
- package/src/index.ts +8 -0
- package/src/study/SessionController.ts +401 -0
- package/src/study/SpacedRepetition.ts +128 -0
- package/src/study/getCardDataShape.ts +34 -0
- package/src/study/index.ts +2 -0
- package/src/util/Loggable.ts +11 -0
- package/src/util/index.ts +1 -0
- package/src/util/logger.ts +55 -0
- package/tsconfig.json +12 -0
- package/tsup.config.ts +17 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import pouch from './pouchdb-setup';
|
|
2
|
+
import { pouchDBincludeCredentialsConfig } from '.';
|
|
3
|
+
import { ENV } from '@/factory';
|
|
4
|
+
// import { DataShape } from '../..base-course/Interfaces/DataShape';
|
|
5
|
+
import { NameSpacer, ShapeDescriptor } from '@vue-skuilder/common';
|
|
6
|
+
import { CourseConfig, DataShape } from '@vue-skuilder/common';
|
|
7
|
+
import { CourseElo, blankCourseElo, toCourseElo } from '@vue-skuilder/common';
|
|
8
|
+
import { CourseDB, createTag, updateCardElo } from './courseDB';
|
|
9
|
+
import { CardData, DisplayableData, DocType, Tag } from '../../core/types/types-legacy';
|
|
10
|
+
import { prepareNote55 } from '@vue-skuilder/common';
|
|
11
|
+
import { User } from './userDB';
|
|
12
|
+
import { logger } from '@/util/logger';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
*
|
|
16
|
+
* @param courseID id of the course (quilt) being added to
|
|
17
|
+
* @param codeCourse
|
|
18
|
+
* @param shape
|
|
19
|
+
* @param data the datashape data - data required for this shape
|
|
20
|
+
* @param author
|
|
21
|
+
* @param uploads optional additional media uploads: img0, img1, ..., aud0, aud1,...
|
|
22
|
+
* @returns
|
|
23
|
+
*/
|
|
24
|
+
export async function addNote55(
|
|
25
|
+
courseID: string,
|
|
26
|
+
codeCourse: string,
|
|
27
|
+
shape: DataShape,
|
|
28
|
+
data: unknown,
|
|
29
|
+
author: string,
|
|
30
|
+
tags: string[],
|
|
31
|
+
uploads?: { [x: string]: PouchDB.Core.FullAttachment },
|
|
32
|
+
elo: CourseElo = blankCourseElo()
|
|
33
|
+
): Promise<PouchDB.Core.Response> {
|
|
34
|
+
const db = getCourseDB(courseID);
|
|
35
|
+
const payload = prepareNote55(courseID, codeCourse, shape, data, author, tags, uploads);
|
|
36
|
+
const result = await db.post<DisplayableData>(payload);
|
|
37
|
+
|
|
38
|
+
const dataShapeId = NameSpacer.getDataShapeString({
|
|
39
|
+
course: codeCourse,
|
|
40
|
+
dataShape: shape.name,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (result.ok) {
|
|
44
|
+
try {
|
|
45
|
+
// create cards
|
|
46
|
+
await createCards(courseID, dataShapeId, result.id, tags, elo);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.error(
|
|
49
|
+
`[addNote55] Failed to create cards for note ${result.id}: ${
|
|
50
|
+
error instanceof Error ? error.message : String(error)
|
|
51
|
+
}`
|
|
52
|
+
);
|
|
53
|
+
// Add info to result to indicate card creation failed
|
|
54
|
+
(result as any).cardCreationFailed = true;
|
|
55
|
+
(result as any).cardCreationError = error instanceof Error ? error.message : String(error);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
logger.error(`[addNote55] Error adding note. Result: ${JSON.stringify(result)}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function createCards(
|
|
65
|
+
courseID: string,
|
|
66
|
+
datashapeID: PouchDB.Core.DocumentId,
|
|
67
|
+
noteID: PouchDB.Core.DocumentId,
|
|
68
|
+
tags: string[],
|
|
69
|
+
elo: CourseElo = blankCourseElo()
|
|
70
|
+
): Promise<void> {
|
|
71
|
+
const cfg = await getCredentialledCourseConfig(courseID);
|
|
72
|
+
const dsDescriptor = NameSpacer.getDataShapeDescriptor(datashapeID);
|
|
73
|
+
let questionViewTypes: string[] = [];
|
|
74
|
+
|
|
75
|
+
for (const ds of cfg.dataShapes) {
|
|
76
|
+
if (ds.name === datashapeID) {
|
|
77
|
+
questionViewTypes = ds.questionTypes;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (questionViewTypes.length === 0) {
|
|
82
|
+
const errorMsg = `No questionViewTypes found for datashapeID: ${datashapeID} in course config. Cards cannot be created.`;
|
|
83
|
+
logger.error(errorMsg);
|
|
84
|
+
throw new Error(errorMsg);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const questionView of questionViewTypes) {
|
|
88
|
+
await createCard(questionView, courseID, dsDescriptor, noteID, tags, elo);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function createCard(
|
|
93
|
+
questionViewName: string,
|
|
94
|
+
courseID: string,
|
|
95
|
+
dsDescriptor: ShapeDescriptor,
|
|
96
|
+
noteID: string,
|
|
97
|
+
tags: string[],
|
|
98
|
+
elo: CourseElo = blankCourseElo()
|
|
99
|
+
): Promise<void> {
|
|
100
|
+
const qDescriptor = NameSpacer.getQuestionDescriptor(questionViewName);
|
|
101
|
+
const cfg = await getCredentialledCourseConfig(courseID);
|
|
102
|
+
|
|
103
|
+
for (const rQ of cfg.questionTypes) {
|
|
104
|
+
if (rQ.name === questionViewName) {
|
|
105
|
+
for (const view of rQ.viewList) {
|
|
106
|
+
await addCard(
|
|
107
|
+
courseID,
|
|
108
|
+
dsDescriptor.course,
|
|
109
|
+
[noteID],
|
|
110
|
+
NameSpacer.getViewString({
|
|
111
|
+
course: qDescriptor.course,
|
|
112
|
+
questionType: qDescriptor.questionType,
|
|
113
|
+
view,
|
|
114
|
+
}),
|
|
115
|
+
elo,
|
|
116
|
+
tags
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
*
|
|
125
|
+
* Adds a card to the DB. This function is called
|
|
126
|
+
* as a side effect of adding either a View or
|
|
127
|
+
* DisplayableData item.
|
|
128
|
+
* @param course The name of the course that the card belongs to
|
|
129
|
+
* @param id_displayable_data C/PouchDB ID of the data used to hydrate the view
|
|
130
|
+
* @param id_view C/PouchDB ID of the view used to display the card
|
|
131
|
+
*
|
|
132
|
+
* @package
|
|
133
|
+
*/
|
|
134
|
+
async function addCard(
|
|
135
|
+
courseID: string,
|
|
136
|
+
course: string,
|
|
137
|
+
id_displayable_data: PouchDB.Core.DocumentId[],
|
|
138
|
+
id_view: PouchDB.Core.DocumentId,
|
|
139
|
+
elo: CourseElo,
|
|
140
|
+
tags: string[]
|
|
141
|
+
): Promise<PouchDB.Core.Response> {
|
|
142
|
+
const card = await getCourseDB(courseID).post<CardData>({
|
|
143
|
+
course,
|
|
144
|
+
id_displayable_data,
|
|
145
|
+
id_view,
|
|
146
|
+
docType: DocType.CARD,
|
|
147
|
+
elo: elo || toCourseElo(990 + Math.round(20 * Math.random())),
|
|
148
|
+
});
|
|
149
|
+
for (const tag of tags) {
|
|
150
|
+
logger.info(`adding tag: ${tag} to card ${card.id}`);
|
|
151
|
+
await addTagToCard(courseID, card.id, tag, false);
|
|
152
|
+
}
|
|
153
|
+
return card;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function getCredentialledCourseConfig(courseID: string): Promise<CourseConfig> {
|
|
157
|
+
try {
|
|
158
|
+
const db = getCourseDB(courseID);
|
|
159
|
+
const ret = await db.get<CourseConfig>('CourseConfig');
|
|
160
|
+
ret.courseID = courseID;
|
|
161
|
+
logger.info(`Returning course config: ${JSON.stringify(ret)}`);
|
|
162
|
+
return ret;
|
|
163
|
+
} catch (e) {
|
|
164
|
+
logger.error(`Error fetching config for ${courseID}:`, e);
|
|
165
|
+
throw e;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
Assciates a tag with a card.
|
|
171
|
+
|
|
172
|
+
NB: DB stores tags as separate documents, with a list of card IDs.
|
|
173
|
+
Consider renaming to `addCardToTag` to reflect this.
|
|
174
|
+
|
|
175
|
+
NB: tags are created if they don't already exist
|
|
176
|
+
|
|
177
|
+
@param updateELO whether to update the ELO of the card with the new tag. Default true.
|
|
178
|
+
@package
|
|
179
|
+
*/
|
|
180
|
+
export async function addTagToCard(
|
|
181
|
+
courseID: string,
|
|
182
|
+
cardID: string,
|
|
183
|
+
tagID: string,
|
|
184
|
+
updateELO: boolean = true
|
|
185
|
+
): Promise<PouchDB.Core.Response> {
|
|
186
|
+
// todo: possible future perf. hit if tags have large #s of taggedCards.
|
|
187
|
+
// In this case, should be converted to a server-request
|
|
188
|
+
const prefixedTagID = getTagID(tagID);
|
|
189
|
+
const courseDB = getCourseDB(courseID);
|
|
190
|
+
const courseApi = new CourseDB(courseID, async () => User.Dummy());
|
|
191
|
+
try {
|
|
192
|
+
logger.info(`Applying tag ${tagID} to card ${courseID + '-' + cardID}...`);
|
|
193
|
+
const tag = await courseDB.get<Tag>(prefixedTagID);
|
|
194
|
+
if (!tag.taggedCards.includes(cardID)) {
|
|
195
|
+
tag.taggedCards.push(cardID);
|
|
196
|
+
|
|
197
|
+
if (updateELO) {
|
|
198
|
+
try {
|
|
199
|
+
const eloData = await courseApi.getCardEloData([cardID]);
|
|
200
|
+
const elo = eloData[0];
|
|
201
|
+
elo.tags[tagID] = {
|
|
202
|
+
count: 0,
|
|
203
|
+
score: elo.global.score, // todo: or 1000?
|
|
204
|
+
};
|
|
205
|
+
await updateCardElo(courseID, cardID, elo);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
logger.error('Failed to update ELO data for card:', cardID, error);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return courseDB.put<Tag>(tag);
|
|
212
|
+
} else throw new AlreadyTaggedErr(`Card ${cardID} is already tagged with ${tagID}`);
|
|
213
|
+
} catch (e) {
|
|
214
|
+
if (e instanceof AlreadyTaggedErr) {
|
|
215
|
+
throw e;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
await createTag(courseID, tagID);
|
|
219
|
+
return addTagToCard(courseID, cardID, tagID, updateELO);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
class AlreadyTaggedErr extends Error {
|
|
224
|
+
constructor(message: string) {
|
|
225
|
+
super(message);
|
|
226
|
+
this.name = 'AlreadyTaggedErr';
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function getTagID(tagName: string): string {
|
|
231
|
+
const tagPrefix = DocType.TAG.valueOf() + '-';
|
|
232
|
+
if (tagName.indexOf(tagPrefix) === 0) {
|
|
233
|
+
return tagName;
|
|
234
|
+
} else {
|
|
235
|
+
return tagPrefix + tagName;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function getCourseDB(courseID: string): PouchDB.Database {
|
|
240
|
+
const dbName = `coursedb-${courseID}`;
|
|
241
|
+
return new pouch(
|
|
242
|
+
ENV.COUCHDB_SERVER_PROTOCOL + '://' + ENV.COUCHDB_SERVER_URL + dbName,
|
|
243
|
+
pouchDBincludeCredentialsConfig
|
|
244
|
+
);
|
|
245
|
+
}
|