@citolab/qti-backend-firebase 0.0.3

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.
Files changed (111) hide show
  1. package/LICENSE.md +674 -0
  2. package/dist/api/app-specific/base/application-specific-base.d.ts +45 -0
  3. package/dist/api/app-specific/base/application-specific-base.d.ts.map +1 -0
  4. package/dist/api/app-specific/base/application-specific-base.js +503 -0
  5. package/dist/api/app-specific/base/application-specific-base.js.map +1 -0
  6. package/dist/api/app-specific/base/cheerio-helper.d.ts +5 -0
  7. package/dist/api/app-specific/base/cheerio-helper.d.ts.map +1 -0
  8. package/dist/api/app-specific/base/cheerio-helper.js +16 -0
  9. package/dist/api/app-specific/base/cheerio-helper.js.map +1 -0
  10. package/dist/api/app-specific/baseImplementation.d.ts +6 -0
  11. package/dist/api/app-specific/baseImplementation.d.ts.map +1 -0
  12. package/dist/api/app-specific/baseImplementation.js +11 -0
  13. package/dist/api/app-specific/baseImplementation.js.map +1 -0
  14. package/dist/api/app-specific/index.d.ts +4 -0
  15. package/dist/api/app-specific/index.d.ts.map +1 -0
  16. package/dist/api/app-specific/index.js +4 -0
  17. package/dist/api/app-specific/index.js.map +1 -0
  18. package/dist/api/app-specific/interface/IApplicationSpecific.d.ts +31 -0
  19. package/dist/api/app-specific/interface/IApplicationSpecific.d.ts.map +1 -0
  20. package/dist/api/app-specific/interface/IApplicationSpecific.js +2 -0
  21. package/dist/api/app-specific/interface/IApplicationSpecific.js.map +1 -0
  22. package/dist/api/app-specific/specific.d.ts +1 -0
  23. package/dist/api/app-specific/specific.d.ts.map +1 -0
  24. package/dist/api/app-specific/specific.js +31 -0
  25. package/dist/api/app-specific/specific.js.map +1 -0
  26. package/dist/api/index.d.ts +3 -0
  27. package/dist/api/index.d.ts.map +1 -0
  28. package/dist/api/index.js +15 -0
  29. package/dist/api/index.js.map +1 -0
  30. package/dist/api/qti-data.d.ts +4 -0
  31. package/dist/api/qti-data.d.ts.map +1 -0
  32. package/dist/api/qti-data.js +622 -0
  33. package/dist/api/qti-data.js.map +1 -0
  34. package/dist/api/qti-resources.d.ts +4 -0
  35. package/dist/api/qti-resources.d.ts.map +1 -0
  36. package/dist/api/qti-resources.js +348 -0
  37. package/dist/api/qti-resources.js.map +1 -0
  38. package/dist/api/qti-teacher.d.ts +6 -0
  39. package/dist/api/qti-teacher.d.ts.map +1 -0
  40. package/dist/api/qti-teacher.js +807 -0
  41. package/dist/api/qti-teacher.js.map +1 -0
  42. package/dist/api/qti-tools.d.ts +3 -0
  43. package/dist/api/qti-tools.d.ts.map +1 -0
  44. package/dist/api/qti-tools.js +450 -0
  45. package/dist/api/qti-tools.js.map +1 -0
  46. package/dist/console.d.ts +2 -0
  47. package/dist/console.d.ts.map +1 -0
  48. package/dist/console.js +30 -0
  49. package/dist/console.js.map +1 -0
  50. package/dist/express-test.d.ts +2 -0
  51. package/dist/express-test.d.ts.map +1 -0
  52. package/dist/express-test.js +19 -0
  53. package/dist/express-test.js.map +1 -0
  54. package/dist/helpers/ci-bootstap.d.ts +4 -0
  55. package/dist/helpers/ci-bootstap.d.ts.map +1 -0
  56. package/dist/helpers/ci-bootstap.js +96 -0
  57. package/dist/helpers/ci-bootstap.js.map +1 -0
  58. package/dist/helpers/database.d.ts +110 -0
  59. package/dist/helpers/database.d.ts.map +1 -0
  60. package/dist/helpers/database.js +151 -0
  61. package/dist/helpers/database.js.map +1 -0
  62. package/dist/helpers/db.d.ts +3 -0
  63. package/dist/helpers/db.d.ts.map +1 -0
  64. package/dist/helpers/db.js +10 -0
  65. package/dist/helpers/db.js.map +1 -0
  66. package/dist/helpers/endpoint-helpers.d.ts +33 -0
  67. package/dist/helpers/endpoint-helpers.d.ts.map +1 -0
  68. package/dist/helpers/endpoint-helpers.js +149 -0
  69. package/dist/helpers/endpoint-helpers.js.map +1 -0
  70. package/dist/helpers/firebase.d.ts +7 -0
  71. package/dist/helpers/firebase.d.ts.map +1 -0
  72. package/dist/helpers/firebase.js +15 -0
  73. package/dist/helpers/firebase.js.map +1 -0
  74. package/dist/helpers/index.d.ts +11 -0
  75. package/dist/helpers/index.d.ts.map +1 -0
  76. package/dist/helpers/index.js +11 -0
  77. package/dist/helpers/index.js.map +1 -0
  78. package/dist/helpers/local-helpers.d.ts +2 -0
  79. package/dist/helpers/local-helpers.d.ts.map +1 -0
  80. package/dist/helpers/local-helpers.js +11 -0
  81. package/dist/helpers/local-helpers.js.map +1 -0
  82. package/dist/helpers/logic.d.ts +73 -0
  83. package/dist/helpers/logic.d.ts.map +1 -0
  84. package/dist/helpers/logic.js +759 -0
  85. package/dist/helpers/logic.js.map +1 -0
  86. package/dist/helpers/package-upload.d.ts +16 -0
  87. package/dist/helpers/package-upload.d.ts.map +1 -0
  88. package/dist/helpers/package-upload.js +160 -0
  89. package/dist/helpers/package-upload.js.map +1 -0
  90. package/dist/helpers/package.d.ts +32 -0
  91. package/dist/helpers/package.d.ts.map +1 -0
  92. package/dist/helpers/package.js +373 -0
  93. package/dist/helpers/package.js.map +1 -0
  94. package/dist/helpers/request-headers.d.ts +27 -0
  95. package/dist/helpers/request-headers.d.ts.map +1 -0
  96. package/dist/helpers/request-headers.js +144 -0
  97. package/dist/helpers/request-headers.js.map +1 -0
  98. package/dist/helpers/storage.d.ts +10 -0
  99. package/dist/helpers/storage.d.ts.map +1 -0
  100. package/dist/helpers/storage.js +132 -0
  101. package/dist/helpers/storage.js.map +1 -0
  102. package/dist/helpers/utils.d.ts +42 -0
  103. package/dist/helpers/utils.d.ts.map +1 -0
  104. package/dist/helpers/utils.js +228 -0
  105. package/dist/helpers/utils.js.map +1 -0
  106. package/dist/main.d.ts +2 -0
  107. package/dist/main.d.ts.map +1 -0
  108. package/dist/main.js +14 -0
  109. package/dist/main.js.map +1 -0
  110. package/package.json +101 -0
  111. package/readme.md +11 -0
@@ -0,0 +1,622 @@
1
+ import { checkAssessmentCode, getPlannedStudent, getStartableAssessments, getStudentSessionInfo, getTeacherIdByCode, isStartable, updateStudentSessionState, } from "./../helpers/logic";
2
+ import { dateId } from "./../helpers/utils";
3
+ import { getAssessmentId, getName } from "./../helpers/request-headers";
4
+ import NodeCache from "node-cache";
5
+ import { getDatabase, getStudentGroupSessionInfo } from "../helpers";
6
+ import { getFirestore, getStorage } from "../helpers/firebase";
7
+ import { body, param } from "express-validator";
8
+ import { errorHandler, generalRateLimit, handleValidationErrors, heavyOperationLimit, imageUploadConfig, requireAuth, sendError, sendSuccess, } from "../helpers/endpoint-helpers";
9
+ import { BaseImplementation } from "./app-specific/baseImplementation";
10
+ export function addQtiDataEndpoints(app, specificImplementation = new BaseImplementation()) {
11
+ const cache = new NodeCache({
12
+ stdTTL: 300, // 5 minutes
13
+ checkperiod: 60, // Check for expired keys every minute
14
+ });
15
+ // Apply global error handler
16
+ app.use(errorHandler);
17
+ /**
18
+ * @openapi
19
+ * /checkCode:
20
+ * post:
21
+ * tags:
22
+ * - Session
23
+ * summary: Use to login by code. The code is used to identify the student. Can be created by the teacher or by Cito
24
+ * description: Checks if the provided assessment code is valid and retrieves session information for the student.
25
+ * requestBody:
26
+ * required: true
27
+ * content:
28
+ * application/json:
29
+ * schema:
30
+ * type: object
31
+ * properties:
32
+ * code:
33
+ * type: string
34
+ * description: The assessment code.
35
+ * minLength: 1
36
+ * maxLength: 50
37
+ * responses:
38
+ * 200:
39
+ * description: Successfully retrieved session information or null if not found.
40
+ * 400:
41
+ * description: Validation error
42
+ * 401:
43
+ * description: Authentication failed
44
+ * 500:
45
+ * description: Internal server error
46
+ */
47
+ app.post("/checkCode", [
48
+ generalRateLimit,
49
+ body("code")
50
+ .isString()
51
+ .trim()
52
+ .isLength({ min: 1, max: 50 })
53
+ .withMessage("Code must be between 1 and 50 characters"),
54
+ handleValidationErrors,
55
+ requireAuth,
56
+ ], async (req, res) => {
57
+ try {
58
+ const { code } = req.body;
59
+ const db = getDatabase(req.appId);
60
+ const sessionInfo = await getStudentSessionInfo(code.toUpperCase(), db, req.user.uid);
61
+ if (sessionInfo) {
62
+ return sendSuccess(res, { ...sessionInfo, userId: req.user.uid });
63
+ }
64
+ else {
65
+ return sendSuccess(res, null);
66
+ }
67
+ }
68
+ catch (error) {
69
+ console.error("Error in checkCode:", error);
70
+ return sendError(res, 500, "Failed to check code");
71
+ }
72
+ });
73
+ /**
74
+ * @openapi
75
+ * /checkGroupCode:
76
+ * post:
77
+ * tags:
78
+ * - Session
79
+ * summary: Use to login by group code. Creates a student session for group delivery.
80
+ * description: Checks if the provided group code is valid and creates/retrieves session information for the student.
81
+ * requestBody:
82
+ * required: true
83
+ * content:
84
+ * application/json:
85
+ * schema:
86
+ * type: object
87
+ * required:
88
+ * - groupCode
89
+ * properties:
90
+ * groupCode:
91
+ * type: string
92
+ * description: The group delivery code.
93
+ * minLength: 1
94
+ * maxLength: 50
95
+ * studentIdentification:
96
+ * type: string
97
+ * description: Optional student identification
98
+ * maxLength: 100
99
+ * responses:
100
+ * 200:
101
+ * description: Successfully retrieved/created session information or null if not found.
102
+ * 400:
103
+ * description: Validation error
104
+ * 401:
105
+ * description: Authentication failed
106
+ * 500:
107
+ * description: Internal server error
108
+ */
109
+ app.post("/checkGroupCode", [
110
+ generalRateLimit,
111
+ body("groupCode")
112
+ .isString()
113
+ .trim()
114
+ .isLength({ min: 1, max: 50 })
115
+ .withMessage("Group code must be between 1 and 50 characters"),
116
+ body("studentIdentification")
117
+ .optional()
118
+ .isString()
119
+ .trim()
120
+ .isLength({ max: 100 })
121
+ .withMessage("Student identification must not exceed 100 characters"),
122
+ handleValidationErrors,
123
+ requireAuth,
124
+ ], async (req, res) => {
125
+ try {
126
+ const { groupCode, studentIdentification } = req.body;
127
+ const db = getDatabase(req.appId);
128
+ const sessionInfo = await getStudentGroupSessionInfo(groupCode.toUpperCase(), db, req.user.uid, studentIdentification);
129
+ if (sessionInfo) {
130
+ return sendSuccess(res, { ...sessionInfo, userId: req.user.uid });
131
+ }
132
+ else {
133
+ return sendSuccess(res, null);
134
+ }
135
+ }
136
+ catch (error) {
137
+ console.error("Error in checkGroupCode:", error);
138
+ return sendError(res, 500, "Failed to check group code");
139
+ }
140
+ });
141
+ /**
142
+ * POST /feedback
143
+ * Endpoint to submit user feedback with optional screenshot
144
+ */
145
+ app.post("/feedback", [
146
+ heavyOperationLimit,
147
+ requireAuth,
148
+ imageUploadConfig.single("screenshot"),
149
+ body("type")
150
+ .isString()
151
+ .trim()
152
+ .isLength({ min: 1, max: 50 })
153
+ .withMessage("Type is required and must be between 1 and 50 characters"),
154
+ body("description")
155
+ .isString()
156
+ .trim()
157
+ .isLength({ min: 1, max: 2000 })
158
+ .withMessage("Description is required and must be between 1 and 2000 characters"),
159
+ body("feedbackId")
160
+ .isString()
161
+ .trim()
162
+ .isLength({ min: 1, max: 100 })
163
+ .withMessage("Feedback ID is required"),
164
+ body("email")
165
+ .optional()
166
+ .isEmail()
167
+ .withMessage("Must be a valid email address"),
168
+ body("pageUrl").optional().isURL().withMessage("Must be a valid URL"),
169
+ handleValidationErrors,
170
+ ], async (req, res) => {
171
+ try {
172
+ const { type, description, feedbackId, email, pageUrl } = req.body;
173
+ const userId = req.user.uid;
174
+ const file = req.file;
175
+ // Create feedback document in Firestore
176
+ const firestore = getFirestore();
177
+ const db = getDatabase(req.appId);
178
+ const firebaseDocPath = db.FEEDBACK.DOC(`${feedbackId}_${userId}`);
179
+ const feedbackRef = firestore.doc(firebaseDocPath);
180
+ await feedbackRef.set({
181
+ type,
182
+ description,
183
+ userId,
184
+ email: email || null,
185
+ pageUrl: pageUrl || null,
186
+ hasScreenshot: !!file,
187
+ createdAt: new Date().toISOString(),
188
+ });
189
+ // If there's a screenshot, upload it to Firebase Storage
190
+ if (file) {
191
+ const storage = getStorage();
192
+ const bucket = storage.bucket();
193
+ const storagePath = firebaseDocPath.replace(/^\//, "");
194
+ const storageFile = bucket.file(`${storagePath}.png`);
195
+ await storageFile.save(file.buffer, {
196
+ metadata: {
197
+ contentType: file.mimetype,
198
+ },
199
+ });
200
+ }
201
+ return sendSuccess(res, undefined, "Feedback submitted successfully");
202
+ }
203
+ catch (error) {
204
+ console.error("Error submitting feedback:", error);
205
+ return sendError(res, 500, "Failed to submit feedback");
206
+ }
207
+ });
208
+ /**
209
+ * @openapi
210
+ * /student/log:
211
+ * post:
212
+ * tags:
213
+ * - Student
214
+ * summary: Log student activity
215
+ * description: Logs student activity data for debugging and analytics purposes.
216
+ * requestBody:
217
+ * required: true
218
+ * content:
219
+ * application/json:
220
+ * schema:
221
+ * type: object
222
+ * required:
223
+ * - type
224
+ * - data
225
+ * properties:
226
+ * type:
227
+ * type: string
228
+ * minLength: 1
229
+ * maxLength: 100
230
+ * description: The type/category of the log entry
231
+ * data:
232
+ * description: Any data to be logged
233
+ * responses:
234
+ * 200:
235
+ * description: Successfully logged the data.
236
+ * 400:
237
+ * description: Invalid request data
238
+ * 401:
239
+ * description: Unauthorized
240
+ * 500:
241
+ * description: Internal server error
242
+ */
243
+ app.post("/student/log", [
244
+ generalRateLimit,
245
+ body("type")
246
+ .isString()
247
+ .trim()
248
+ .isLength({ min: 1, max: 100 })
249
+ .withMessage("Type must be a string between 1-100 characters"),
250
+ body("data").exists().withMessage("Data field is required"),
251
+ handleValidationErrors,
252
+ requireAuth,
253
+ ], async (req, res) => {
254
+ try {
255
+ const { type, data } = req.body;
256
+ const db = getDatabase(req.appId);
257
+ // Create log entry with timestamp and student ID
258
+ const logEntry = {
259
+ type,
260
+ data,
261
+ timestamp: new Date().toISOString(),
262
+ studentId: req.user.uid,
263
+ appId: req.appId,
264
+ };
265
+ const firestore = getFirestore();
266
+ await firestore
267
+ .doc(db.STUDENT.LOG.DOC(req.user.uid, dateId()))
268
+ .set(logEntry);
269
+ return sendSuccess(res, undefined, "Log entry created successfully");
270
+ }
271
+ catch (error) {
272
+ console.error("Error creating log entry:", error);
273
+ return sendError(res, 500, "Failed to create log entry");
274
+ }
275
+ });
276
+ /**
277
+ * @openapi
278
+ * /assessment/checkCode:
279
+ * post:
280
+ * tags:
281
+ * - Session
282
+ * summary: Use to login by code. The code is used to identify the assessment.
283
+ * description: Checks if the provided assessment code is valid and retrieves session information for the student.
284
+ * requestBody:
285
+ * required: true
286
+ * content:
287
+ * application/json:
288
+ * schema:
289
+ * type: object
290
+ * properties:
291
+ * code:
292
+ * type: string
293
+ * description: The assessment code.
294
+ * identification:
295
+ * type: string
296
+ * description: Student identification.
297
+ * responses:
298
+ * 200:
299
+ * description: Successfully retrieved session information.
300
+ * 404:
301
+ * description: Assessment not found
302
+ * 500:
303
+ * description: Error creating session
304
+ */
305
+ app.post("/assessment/checkCode", [
306
+ generalRateLimit,
307
+ body("code")
308
+ .isString()
309
+ .trim()
310
+ .isLength({ min: 1, max: 50 })
311
+ .withMessage("Code is required"),
312
+ body("identification")
313
+ .optional()
314
+ .isString()
315
+ .trim()
316
+ .isLength({ max: 100 }),
317
+ handleValidationErrors,
318
+ requireAuth,
319
+ ], async (req, res) => {
320
+ try {
321
+ const { code, identification } = req.body;
322
+ const db = getDatabase(req.appId);
323
+ const assessmentInfo = await checkAssessmentCode(db, code.toUpperCase());
324
+ if (!assessmentInfo) {
325
+ return sendError(res, 404, "Assessment not found");
326
+ }
327
+ if (assessmentInfo.isDemo) {
328
+ const demoInfo = {
329
+ appId: req.appId,
330
+ code,
331
+ currentAssessmentId: assessmentInfo.assessmentId,
332
+ sessions: [
333
+ {
334
+ assessmentId: assessmentInfo.assessmentId,
335
+ assessmentName: assessmentInfo.name,
336
+ sessionState: "not_started",
337
+ packageId: assessmentInfo.packageId,
338
+ },
339
+ ],
340
+ teacherId: assessmentInfo.teacherId || "general",
341
+ isDemo: true,
342
+ };
343
+ return sendSuccess(res, demoInfo);
344
+ }
345
+ const studentInfo = await specificImplementation.createSessionInfoForStudent({
346
+ userId: req.user.uid,
347
+ assessmentId: assessmentInfo.assessmentId,
348
+ identification,
349
+ metadata: {
350
+ isDemo: assessmentInfo.isDemo,
351
+ },
352
+ });
353
+ if (!studentInfo) {
354
+ return sendError(res, 500, "Error creating session");
355
+ }
356
+ return sendSuccess(res, studentInfo);
357
+ }
358
+ catch (error) {
359
+ console.error("Error in assessment checkCode:", error);
360
+ return sendError(res, 500, "Failed to check assessment code");
361
+ }
362
+ });
363
+ /**
364
+ * @openapi
365
+ * /session/start:
366
+ * post:
367
+ * tags:
368
+ * - Session
369
+ * summary: Start a session
370
+ * description: Starts a session for a student with the given assessment ID and identification.
371
+ */
372
+ app.post("/session/start", [
373
+ generalRateLimit,
374
+ body("assessmentId")
375
+ .isString()
376
+ .trim()
377
+ .isLength({ min: 1 })
378
+ .withMessage("Assessment ID is required"),
379
+ body("identification").optional().isString().trim(),
380
+ body("metadata").optional().isObject(),
381
+ handleValidationErrors,
382
+ requireAuth,
383
+ ], async (req, res) => {
384
+ try {
385
+ const { metadata, assessmentId, identification } = req.body;
386
+ const db = getDatabase(req.appId);
387
+ const assessments = await getStartableAssessments(db);
388
+ const assessment = assessments.find((a) => a.assessmentId === assessmentId);
389
+ if (!assessment) {
390
+ return sendError(res, 404, "Assessment not found");
391
+ }
392
+ const validMetadata = metadata && typeof metadata === "object" ? metadata : undefined;
393
+ let sessionInfo = await specificImplementation.getSessionInfoForStudent(req.user.uid, assessmentId, identification);
394
+ const canStart = isStartable(assessment);
395
+ if (!sessionInfo && canStart) {
396
+ sessionInfo =
397
+ await specificImplementation.createSessionInfoForStudent({
398
+ userId: req.user.uid,
399
+ identification,
400
+ assessmentId: assessmentId,
401
+ metadata: validMetadata,
402
+ });
403
+ }
404
+ if ((sessionInfo && canStart) || sessionInfo?.isDemo) {
405
+ return sendSuccess(res, { ...sessionInfo, userId: req.user.uid });
406
+ }
407
+ else {
408
+ return sendSuccess(res, null);
409
+ }
410
+ }
411
+ catch (error) {
412
+ console.error("Error starting session:", error);
413
+ return sendError(res, 500, "Failed to start session");
414
+ }
415
+ });
416
+ /**
417
+ * @openapi
418
+ * /session/info:
419
+ * get:
420
+ * tags:
421
+ * - Session
422
+ * summary: Get session info
423
+ * description: Retrieves session information for the authenticated student.
424
+ */
425
+ app.get("/session/info", [generalRateLimit, requireAuth], async (req, res) => {
426
+ try {
427
+ const assessmentId = getAssessmentId(req);
428
+ const identification = getName(req);
429
+ const sessionInfo = await specificImplementation.getSessionInfoForStudent(req.user.uid, assessmentId, identification);
430
+ if (sessionInfo) {
431
+ return sendSuccess(res, sessionInfo);
432
+ }
433
+ else {
434
+ return sendError(res, 404, "Session not found");
435
+ }
436
+ }
437
+ catch (error) {
438
+ console.error("Error getting session info:", error);
439
+ return sendError(res, 500, "Failed to get session info");
440
+ }
441
+ });
442
+ /**
443
+ * @openapi
444
+ * /session/{assessmentId}/context:
445
+ * get:
446
+ * tags:
447
+ * - Session
448
+ * summary: Get session context
449
+ * description: Retrieves session context for a specific assessment.
450
+ */
451
+ app.get("/session/:assessmentId/context", [
452
+ generalRateLimit,
453
+ param("assessmentId")
454
+ .isString()
455
+ .trim()
456
+ .isLength({ min: 1 })
457
+ .withMessage("Assessment ID is required"),
458
+ handleValidationErrors,
459
+ requireAuth,
460
+ ], async (req, res) => {
461
+ try {
462
+ const { assessmentId } = req.params;
463
+ const db = getDatabase(req.appId);
464
+ const code = await specificImplementation.getCodeByUserId(req.user.uid, assessmentId);
465
+ const firestore = getFirestore();
466
+ const sessionRef = firestore.doc(db.STUDENT.SESSION.DOC(code, assessmentId));
467
+ const context = await sessionRef.get();
468
+ return sendSuccess(res, context.exists ? context.data() : null);
469
+ }
470
+ catch (error) {
471
+ console.error("Error getting session context:", error);
472
+ return sendError(res, 500, "Failed to get session context");
473
+ }
474
+ });
475
+ /**
476
+ * @openapi
477
+ * /session/{assessmentId}/context:
478
+ * post:
479
+ * tags:
480
+ * - Session
481
+ * summary: Update session context
482
+ * description: Updates session context for a specific assessment.
483
+ */
484
+ app.post("/session/:assessmentId/context", [
485
+ generalRateLimit,
486
+ param("assessmentId")
487
+ .isString()
488
+ .trim()
489
+ .isLength({ min: 1 })
490
+ .withMessage("Assessment ID is required"),
491
+ body("state").optional().isString(),
492
+ body("items").optional().isArray(),
493
+ handleValidationErrors,
494
+ requireAuth,
495
+ ], async (req, res) => {
496
+ try {
497
+ const { assessmentId } = req.params;
498
+ const testContext = req.body;
499
+ const db = getDatabase(req.appId);
500
+ const code = await specificImplementation.getCodeByUserId(req.user.uid, assessmentId);
501
+ const cacheKey = `test-context-${req.appId}-${code}-${assessmentId}`;
502
+ let previousState = cache.get(cacheKey);
503
+ const firestore = getFirestore();
504
+ const sessionRef = firestore.doc(db.STUDENT.SESSION.DOC(code, assessmentId));
505
+ if (!previousState) {
506
+ const previousContext = await sessionRef.get();
507
+ if (previousContext.exists) {
508
+ previousState = previousContext.data()
509
+ ?.state;
510
+ }
511
+ }
512
+ const batch = firestore.batch();
513
+ if (previousState !== testContext.state) {
514
+ cache.set(cacheKey, testContext.state, 60 * 60 * 1000); // 1 hour
515
+ const teacherId = await getTeacherIdByCode(db, code);
516
+ if (!teacherId) {
517
+ return sendError(res, 404, "Teacher not found for this code");
518
+ }
519
+ await Promise.all([
520
+ specificImplementation.testContextToTeacher(code, assessmentId, testContext, batch),
521
+ updateStudentSessionState(db, code, assessmentId, testContext?.state ?? "not_started", batch),
522
+ specificImplementation.updateItemStats(teacherId, assessmentId, code, testContext.items, "teacher", batch),
523
+ ]);
524
+ }
525
+ batch.set(sessionRef, testContext);
526
+ await batch.commit();
527
+ return sendSuccess(res, undefined, "Context updated successfully");
528
+ }
529
+ catch (error) {
530
+ console.error("Error updating session context:", error);
531
+ return sendError(res, 500, "Failed to update session context");
532
+ }
533
+ });
534
+ /**
535
+ * @openapi
536
+ * /session/{assessmentId}/sessionState:
537
+ * post:
538
+ * tags:
539
+ * - Session
540
+ * summary: Update session state
541
+ * description: Updates the session state for a specific session.
542
+ */
543
+ app.post("/session/:assessmentId/sessionState", [
544
+ generalRateLimit,
545
+ param("assessmentId")
546
+ .isString()
547
+ .trim()
548
+ .isLength({ min: 1 })
549
+ .withMessage("Assessment ID is required"),
550
+ body("sessionState")
551
+ .isString()
552
+ .trim()
553
+ .isLength({ min: 1 })
554
+ .withMessage("Session state is required"),
555
+ handleValidationErrors,
556
+ requireAuth,
557
+ ], async (req, res) => {
558
+ try {
559
+ const { assessmentId } = req.params;
560
+ const { sessionState } = req.body;
561
+ const db = getDatabase(req.appId);
562
+ const code = await specificImplementation.getCodeByUserId(req.user.uid, assessmentId);
563
+ const firestore = getFirestore();
564
+ const batch = firestore.batch();
565
+ await updateStudentSessionState(db, code, assessmentId, sessionState, batch);
566
+ const teacherId = await getTeacherIdByCode(db, code);
567
+ if (!teacherId) {
568
+ return sendError(res, 404, "Teacher not found for this code");
569
+ }
570
+ const studentSessions = await getPlannedStudent(db, teacherId, code);
571
+ const session = studentSessions.sessions.find((s) => s.assessmentId === assessmentId);
572
+ if (session) {
573
+ await specificImplementation.updatePlannedSession(teacherId, code, { ...session, sessionState }, batch);
574
+ }
575
+ await batch.commit();
576
+ return sendSuccess(res, undefined, "Session state updated successfully");
577
+ }
578
+ catch (error) {
579
+ console.error("Error updating session state:", error);
580
+ return sendError(res, 500, "Failed to update session state");
581
+ }
582
+ });
583
+ /**
584
+ * @openapi
585
+ * /session/{assessmentId}/log:
586
+ * post:
587
+ * tags:
588
+ * - Session
589
+ * summary: Log session data
590
+ * description: Logs session data for a specific assessment.
591
+ */
592
+ app.post("/session/:assessmentId/log", [
593
+ generalRateLimit,
594
+ param("assessmentId")
595
+ .isString()
596
+ .trim()
597
+ .isLength({ min: 1 })
598
+ .withMessage("Assessment ID is required"),
599
+ body("logData").isObject().withMessage("Log data must be an object"),
600
+ handleValidationErrors,
601
+ requireAuth,
602
+ ], async (req, res) => {
603
+ try {
604
+ const { assessmentId } = req.params;
605
+ const db = getDatabase(req.appId);
606
+ const code = await specificImplementation.getCodeByUserId(req.user.uid, assessmentId);
607
+ const firestore = getFirestore();
608
+ const sessionRef = firestore.doc(db.STUDENT.SESSION.LOG(code, assessmentId, dateId()));
609
+ await sessionRef.set({
610
+ ...req.body,
611
+ timestamp: new Date().toISOString(),
612
+ userId: req.user.uid,
613
+ });
614
+ return sendSuccess(res, undefined, "Log data saved successfully");
615
+ }
616
+ catch (error) {
617
+ console.error("Error logging session data:", error);
618
+ return sendError(res, 500, "Failed to log session data");
619
+ }
620
+ });
621
+ }
622
+ //# sourceMappingURL=qti-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qti-data.js","sourceRoot":"","sources":["../../src/api/qti-data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Express } from "express";
2
+ import { IApplicationSpecific } from "./app-specific";
3
+ export declare function addQtiResourcesEndpoint(app: Express, specificImplementation?: IApplicationSpecific): void;
4
+ //# sourceMappingURL=qti-resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qti-resources.d.ts","sourceRoot":"","sources":["../../src/api/qti-resources.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAmC,MAAM,SAAS,CAAC;AASnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAqDtD,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,OAAO,EACZ,sBAAsB,GAAE,oBAA+C,QAmXxE"}