@studious-lms/server 1.2.34 → 1.2.35

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.
@@ -38,7 +38,7 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
38
38
  order: number | null;
39
39
  markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
40
40
  updatedAt: Date;
41
- points: number | null;
41
+ points: number;
42
42
  question: string;
43
43
  worksheetId: string;
44
44
  answer: string;
@@ -128,7 +128,7 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
128
128
  order: number | null;
129
129
  markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
130
130
  updatedAt: Date;
131
- points: number | null;
131
+ points: number;
132
132
  question: string;
133
133
  worksheetId: string;
134
134
  answer: string;
@@ -166,7 +166,7 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
166
166
  order: number | null;
167
167
  markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
168
168
  updatedAt: Date;
169
- points: number | null;
169
+ points: number;
170
170
  question: string;
171
171
  worksheetId: string;
172
172
  answer: string;
@@ -186,7 +186,7 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
186
186
  order: number | null;
187
187
  markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
188
188
  updatedAt: Date;
189
- points: number | null;
189
+ points: number;
190
190
  question: string;
191
191
  worksheetId: string;
192
192
  answer: string;
@@ -204,11 +204,13 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
204
204
  feedback: string | null;
205
205
  studentId: string;
206
206
  createdAt: Date;
207
- updatedAt: Date;
207
+ updatedAt: Date | null;
208
+ points: number;
208
209
  questionId: string;
209
210
  response: string;
210
- isCorrect: boolean;
211
211
  studentWorksheetResponseId: string | null;
212
+ isCorrect: boolean;
213
+ markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
212
214
  }[];
213
215
  } & {
214
216
  id: string;
@@ -234,11 +236,13 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
234
236
  feedback: string | null;
235
237
  studentId: string;
236
238
  createdAt: Date;
237
- updatedAt: Date;
239
+ updatedAt: Date | null;
240
+ points: number;
238
241
  questionId: string;
239
242
  response: string;
240
- isCorrect: boolean;
241
243
  studentWorksheetResponseId: string | null;
244
+ isCorrect: boolean;
245
+ markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
242
246
  }[];
243
247
  } & {
244
248
  id: string;
@@ -262,55 +266,14 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
262
266
  feedback: string | null;
263
267
  studentId: string;
264
268
  createdAt: Date;
265
- updatedAt: Date;
269
+ updatedAt: Date | null;
270
+ points: number;
266
271
  questionId: string;
267
272
  response: string;
268
- isCorrect: boolean;
269
273
  studentWorksheetResponseId: string | null;
270
- }[];
271
- } & {
272
- id: string;
273
- submissionId: string | null;
274
- studentId: string;
275
- createdAt: Date;
276
- updatedAt: Date;
277
- submittedAt: Date | null;
278
- submitted: boolean;
279
- worksheetId: string;
280
- };
281
- meta: object;
282
- }>;
283
- getOrCreateWorksheetResponse: import("@trpc/server").TRPCMutationProcedure<{
284
- input: {
285
- studentId: string;
286
- worksheetId: string;
287
- };
288
- output: {
289
- responses: ({
290
- question: {
291
- type: import(".prisma/client").$Enums.WorksheetQuestionType;
292
- id: string;
293
- options: import("@prisma/client/runtime/library.js").JsonValue | null;
294
- createdAt: Date;
295
- order: number | null;
296
- markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
297
- updatedAt: Date;
298
- points: number | null;
299
- question: string;
300
- worksheetId: string;
301
- answer: string;
302
- };
303
- } & {
304
- id: string;
305
- feedback: string | null;
306
- studentId: string;
307
- createdAt: Date;
308
- updatedAt: Date;
309
- questionId: string;
310
- response: string;
311
274
  isCorrect: boolean;
312
- studentWorksheetResponseId: string | null;
313
- })[];
275
+ markschemeState: import("@prisma/client/runtime/library.js").JsonValue | null;
276
+ }[];
314
277
  } & {
315
278
  id: string;
316
279
  submissionId: string | null;
@@ -323,72 +286,34 @@ export declare const worksheetRouter: import("@trpc/server").TRPCBuiltRouter<{
323
286
  };
324
287
  meta: object;
325
288
  }>;
326
- getWorksheetResponse: import("@trpc/server").TRPCQueryProcedure<{
289
+ gradeAnswer: import("@trpc/server").TRPCMutationProcedure<{
327
290
  input: {
328
- worksheetId: string;
291
+ questionId: string;
292
+ studentWorksheetResponseId: string;
293
+ isCorrect: boolean;
294
+ feedback?: string | undefined;
295
+ points?: number | undefined;
296
+ response?: string | undefined;
297
+ responseId?: string | undefined;
298
+ markschemeState?: any;
329
299
  };
330
- output: ({
331
- student: {
332
- id: string;
333
- username: string;
334
- profile: {
335
- displayName: string | null;
336
- profilePicture: string | null;
337
- } | null;
338
- };
339
- responses: ({
340
- question: {
341
- type: import(".prisma/client").$Enums.WorksheetQuestionType;
342
- id: string;
343
- options: import("@prisma/client/runtime/library.js").JsonValue | null;
344
- createdAt: Date;
345
- order: number | null;
346
- markScheme: import("@prisma/client/runtime/library.js").JsonValue | null;
347
- updatedAt: Date;
348
- points: number | null;
349
- question: string;
350
- worksheetId: string;
351
- answer: string;
352
- };
353
- } & {
354
- id: string;
355
- feedback: string | null;
356
- studentId: string;
357
- createdAt: Date;
358
- updatedAt: Date;
359
- questionId: string;
360
- response: string;
361
- isCorrect: boolean;
362
- studentWorksheetResponseId: string | null;
363
- })[];
364
- } & {
365
- id: string;
366
- submissionId: string | null;
367
- studentId: string;
368
- createdAt: Date;
369
- updatedAt: Date;
370
- submittedAt: Date | null;
371
- submitted: boolean;
372
- worksheetId: string;
373
- }) | null;
300
+ output: any;
374
301
  meta: object;
375
302
  }>;
376
- gradeAnswer: import("@trpc/server").TRPCMutationProcedure<{
303
+ addComment: import("@trpc/server").TRPCMutationProcedure<{
377
304
  input: {
305
+ comment: string;
378
306
  responseId: string;
379
- isCorrect: boolean;
380
- feedback?: string | undefined;
381
307
  };
382
308
  output: {
383
309
  id: string;
384
- feedback: string | null;
385
- studentId: string;
310
+ content: string;
386
311
  createdAt: Date;
387
- updatedAt: Date;
388
- questionId: string;
389
- response: string;
390
- isCorrect: boolean;
391
- studentWorksheetResponseId: string | null;
312
+ modifiedAt: Date | null;
313
+ announcementId: string | null;
314
+ parentCommentId: string | null;
315
+ authorId: string;
316
+ studentQuestionProgressId: string | null;
392
317
  };
393
318
  meta: object;
394
319
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"worksheet.d.ts","sourceRoot":"","sources":["../../src/routers/worksheet.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBA6KA,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0UhC,CAAC"}
1
+ {"version":3,"file":"worksheet.d.ts","sourceRoot":"","sources":["../../src/routers/worksheet.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBA8KA,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4UhC,CAAC"}
@@ -15,6 +15,7 @@ export const worksheetRouter = createTRPCRouter({
15
15
  include: {
16
16
  questions: {
17
17
  orderBy: { createdAt: 'asc' },
18
+ // select: { id: true, type: true, question: true, answer: true, points: true },
18
19
  },
19
20
  class: true,
20
21
  },
@@ -344,95 +345,99 @@ export const worksheetRouter = createTRPCRouter({
344
345
  // You could integrate with an AI service to auto-grade certain question types
345
346
  return submittedWorksheet;
346
347
  }),
347
- // Get or create a student's worksheet response
348
- getOrCreateWorksheetResponse: protectedProcedure
348
+ // Grade a student's answer
349
+ gradeAnswer: protectedProcedure
349
350
  .input(z.object({
350
- worksheetId: z.string(),
351
- studentId: z.string(),
351
+ questionId: z.string(),
352
+ responseId: z.string().optional(), // StudentQuestionProgress ID (optional for upsert)
353
+ studentWorksheetResponseId: z.string(), // Required for linking to worksheet response
354
+ response: z.string().optional(), // The actual response text (needed if creating new)
355
+ isCorrect: z.boolean(),
356
+ feedback: z.string().optional(),
357
+ markschemeState: z.any().optional(),
358
+ points: z.number().optional(),
352
359
  }))
353
360
  .mutation(async ({ ctx, input }) => {
354
- const { worksheetId, studentId } = input;
355
- // Try to find existing response
356
- let worksheetResponse = await prisma.studentWorksheetResponse.findFirst({
357
- where: {
358
- worksheetId,
359
- studentId,
360
- submitted: false, // Only get unsubmitted responses
361
- },
362
- include: {
363
- responses: {
364
- include: {
365
- question: true,
366
- },
367
- },
368
- },
369
- });
370
- // Create new response if none exists
371
- if (!worksheetResponse) {
372
- worksheetResponse = await prisma.studentWorksheetResponse.create({
361
+ const { responseId, questionId, studentWorksheetResponseId, response, isCorrect, feedback, markschemeState, points } = input;
362
+ let gradedResponse;
363
+ if (responseId) {
364
+ // Update existing progress by ID
365
+ gradedResponse = await prisma.studentQuestionProgress.update({
366
+ where: { id: responseId },
373
367
  data: {
374
- worksheetId,
375
- studentId,
376
- },
377
- include: {
378
- responses: {
379
- include: {
380
- question: true,
381
- },
382
- },
368
+ isCorrect,
369
+ ...(feedback !== undefined && { feedback }),
370
+ ...(markschemeState !== undefined && { markschemeState }),
371
+ ...(points !== undefined && { points }),
383
372
  },
384
373
  });
385
374
  }
386
- return worksheetResponse;
387
- }),
388
- // Get all student responses for a worksheet (teacher view)
389
- getWorksheetResponse: protectedProcedure
390
- .input(z.object({
391
- worksheetId: z.string(),
392
- }))
393
- .query(async ({ ctx, input }) => {
394
- const { worksheetId } = input;
395
- const responses = await prisma.studentWorksheetResponse.findFirst({
396
- where: { worksheetId },
397
- include: {
398
- student: {
399
- select: {
400
- id: true,
401
- username: true,
402
- profile: {
403
- select: {
404
- displayName: true,
405
- profilePicture: true,
406
- },
407
- },
408
- },
375
+ else {
376
+ // Get the studentId from the worksheet response
377
+ const worksheetResponse = await prisma.studentWorksheetResponse.findUnique({
378
+ where: { id: studentWorksheetResponseId },
379
+ select: { studentId: true },
380
+ });
381
+ if (!worksheetResponse) {
382
+ throw new TRPCError({
383
+ code: 'NOT_FOUND',
384
+ message: 'Student worksheet response not found',
385
+ });
386
+ }
387
+ const { studentId } = worksheetResponse;
388
+ // Upsert - find or create the progress record
389
+ const existing = await prisma.studentQuestionProgress.findFirst({
390
+ where: {
391
+ studentId,
392
+ questionId,
393
+ studentWorksheetResponseId,
409
394
  },
410
- responses: {
411
- include: {
412
- question: true,
395
+ });
396
+ if (existing) {
397
+ // Update existing
398
+ gradedResponse = await prisma.studentQuestionProgress.update({
399
+ where: { id: existing.id },
400
+ data: {
401
+ isCorrect,
402
+ ...(response !== undefined && { response }),
403
+ ...(feedback !== undefined && { feedback }),
404
+ ...(markschemeState !== undefined && { markschemeState }),
405
+ ...(points !== undefined && { points }),
413
406
  },
414
- },
415
- },
416
- orderBy: { submittedAt: 'desc' },
417
- });
418
- return responses;
407
+ });
408
+ }
409
+ else {
410
+ // Create new
411
+ gradedResponse = await prisma.studentQuestionProgress.create({
412
+ data: {
413
+ studentId,
414
+ questionId,
415
+ studentWorksheetResponseId,
416
+ response: response || '',
417
+ isCorrect,
418
+ ...(feedback !== undefined && { feedback }),
419
+ ...(markschemeState !== undefined && { markschemeState }),
420
+ ...(points !== undefined && { points: points || 0 }),
421
+ },
422
+ });
423
+ }
424
+ }
425
+ return gradedResponse;
419
426
  }),
420
- // Grade a student's answer
421
- gradeAnswer: protectedProcedure
427
+ addComment: protectedProcedure
422
428
  .input(z.object({
423
- responseId: z.string(), // StudentQuestionProgress ID
424
- isCorrect: z.boolean(),
425
- feedback: z.string().optional(),
429
+ responseId: z.string(),
430
+ comment: z.string(),
426
431
  }))
427
432
  .mutation(async ({ ctx, input }) => {
428
- const { responseId, isCorrect, feedback } = input;
429
- const gradedResponse = await prisma.studentQuestionProgress.update({
430
- where: { id: responseId },
433
+ const { responseId, comment } = input;
434
+ const newComment = await prisma.comment.create({
431
435
  data: {
432
- isCorrect,
433
- ...(feedback !== undefined && { feedback }),
436
+ studentQuestionProgressId: responseId,
437
+ content: comment,
438
+ authorId: ctx.user.id,
434
439
  },
435
440
  });
436
- return gradedResponse;
441
+ return newComment;
437
442
  }),
438
443
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studious-lms/server",
3
- "version": "1.2.34",
3
+ "version": "1.2.35",
4
4
  "description": "Backend server for Studious application",
5
5
  "main": "dist/exportType.js",
6
6
  "types": "dist/exportType.d.ts",
@@ -301,14 +301,16 @@ model Comment {
301
301
  content String
302
302
  author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
303
303
  authorId String
304
- announcement Announcement @relation(fields: [announcementId], references: [id], onDelete: Cascade)
305
- announcementId String
304
+ announcement Announcement? @relation(fields: [announcementId], references: [id], onDelete: Cascade)
305
+ announcementId String?
306
306
  parentComment Comment? @relation("CommentReplies", fields: [parentCommentId], references: [id], onDelete: Cascade)
307
307
  parentCommentId String?
308
308
  replies Comment[] @relation("CommentReplies")
309
309
  reactions Reaction[]
310
310
  createdAt DateTime @default(now())
311
311
  modifiedAt DateTime? @updatedAt
312
+ studentQuestionProgress StudentQuestionProgress? @relation("StudentQuestionProgressComments", fields: [studentQuestionProgressId], references: [id], onDelete: Cascade)
313
+ studentQuestionProgressId String?
312
314
  }
313
315
 
314
316
  model Reaction {
@@ -517,7 +519,7 @@ model WorksheetQuestion {
517
519
  answer String
518
520
  options Json? @default("{}")
519
521
  markScheme Json? @default("{}")
520
- points Int? @default(0)
522
+ points Int @default(0)
521
523
  order Int? @default(0)
522
524
  createdAt DateTime @default(now())
523
525
  updatedAt DateTime @updatedAt
@@ -547,11 +549,16 @@ model StudentQuestionProgress {
547
549
  question WorksheetQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)
548
550
  response String
549
551
  isCorrect Boolean @default(false)
552
+ markschemeState Json? @default("{}")
553
+ points Int @default(0)
554
+ comments Comment[] @relation("StudentQuestionProgressComments")
550
555
  feedback String?
551
556
  createdAt DateTime @default(now())
552
- updatedAt DateTime @updatedAt
557
+ updatedAt DateTime? @updatedAt
553
558
  studentWorksheetResponseId String?
554
559
  studentWorksheetResponse StudentWorksheetResponse? @relation(fields: [studentWorksheetResponseId], references: [id], onDelete: Cascade)
560
+
561
+ @@index([studentId, questionId])
555
562
  }
556
563
 
557
564
  model SchoolDevelopementProgram {
@@ -18,6 +18,7 @@ import { messageRouter } from "./message.js";
18
18
  import { labChatRouter } from "./labChat.js";
19
19
  import { marketingRouter } from "./marketing.js";
20
20
  import { worksheetRouter } from "./worksheet.js";
21
+ import { commentRouter } from "./comment.js";
21
22
 
22
23
  export const appRouter = createTRPCRouter({
23
24
  class: classRouter,
@@ -37,6 +38,7 @@ export const appRouter = createTRPCRouter({
37
38
  labChat: labChatRouter,
38
39
  marketing: marketingRouter,
39
40
  worksheet: worksheetRouter,
41
+ comment: commentRouter,
40
42
  });
41
43
 
42
44
  // Export type router type definition
@@ -667,7 +667,7 @@ export const announcementRouter = createTRPCRouter({
667
667
  // Only the author or a class teacher can delete comments
668
668
  const userId = ctx.user.id;
669
669
  const isAuthor = comment.authorId === userId;
670
- const isClassTeacher = comment.announcement.class.teachers.some(
670
+ const isClassTeacher = comment.announcement!.class.teachers.some(
671
671
  (teacher) => teacher.id === userId
672
672
  );
673
673
 
@@ -858,7 +858,7 @@ export const announcementRouter = createTRPCRouter({
858
858
  });
859
859
  }
860
860
 
861
- if (comment.announcement.classId !== input.classId) {
861
+ if (comment.announcement!.classId !== input.classId) {
862
862
  throw new TRPCError({
863
863
  code: "FORBIDDEN",
864
864
  message: "Comment does not belong to this class",
@@ -1078,7 +1078,7 @@ export const announcementRouter = createTRPCRouter({
1078
1078
  });
1079
1079
  }
1080
1080
 
1081
- if (comment.announcement.classId !== input.classId) {
1081
+ if (comment.announcement!.classId !== input.classId) {
1082
1082
  throw new TRPCError({
1083
1083
  code: "FORBIDDEN",
1084
1084
  message: "Comment does not belong to this class",