Skip to content

Commit 1dfe078

Browse files
authored
feat: Authors can now configure quesition level control over copying, pasting and printing (#206)
* feat: adding question control settings end to end * feat: xlsx support feature (#205) * fix date issue (#207)
1 parent 731dad6 commit 1dfe078

File tree

39 files changed

+2099
-113
lines changed

39 files changed

+2099
-113
lines changed

.secrets.baseline

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": null,
44
"lines": null
55
},
6-
"generated_at": "2025-10-30T07:06:07Z",
6+
"generated_at": "2025-11-15T04:48:47Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -317,15 +317,6 @@
317317
"verified_result": null
318318
}
319319
],
320-
"apps/api/src/middleware/__tests__/data-transform.middleware.test.ts": [
321-
{
322-
"hashed_secret": "c43b74f82f891e351ea8d73c4cac9988f05fa49f",
323-
"is_verified": false,
324-
"line_number": 41,
325-
"type": "Base64 High Entropy String",
326-
"verified_result": null
327-
}
328-
],
329320
"apps/web/.env.template": [
330321
{
331322
"hashed_secret": "d08f88df745fa7950b104e4a707a31cfce7b5841",
@@ -443,49 +434,49 @@
443434
{
444435
"hashed_secret": "b7e41a1408b0de53b6a18b0383983df52151bffd",
445436
"is_verified": false,
446-
"line_number": 60,
437+
"line_number": 57,
447438
"type": "Base64 High Entropy String",
448439
"verified_result": null
449440
},
450441
{
451442
"hashed_secret": "7f8a4c8efb7a9d741a4131e6382406d04920a55c",
452443
"is_verified": false,
453-
"line_number": 75,
444+
"line_number": 72,
454445
"type": "Base64 High Entropy String",
455446
"verified_result": null
456447
},
457448
{
458449
"hashed_secret": "ef584f952dbcdb807b1f2a5b688a6b2ac7beaf76",
459450
"is_verified": false,
460-
"line_number": 158,
451+
"line_number": 155,
461452
"type": "Base64 High Entropy String",
462453
"verified_result": null
463454
},
464455
{
465456
"hashed_secret": "fca9f0a000cc0d99a6b89eff22b280d43b3dc23a",
466457
"is_verified": false,
467-
"line_number": 168,
458+
"line_number": 164,
468459
"type": "Base64 High Entropy String",
469460
"verified_result": null
470461
},
471462
{
472463
"hashed_secret": "ad5a4eb98aace66b683002aa7038bb800f8b0a65",
473464
"is_verified": false,
474-
"line_number": 174,
465+
"line_number": 170,
475466
"type": "Base64 High Entropy String",
476467
"verified_result": null
477468
},
478469
{
479470
"hashed_secret": "28681ff8a70bc722645c0ebe5c21fbd8a2ee904a",
480471
"is_verified": false,
481-
"line_number": 183,
472+
"line_number": 179,
482473
"type": "Base64 High Entropy String",
483474
"verified_result": null
484475
},
485476
{
486477
"hashed_secret": "dd65cdacb216bbeca512abfb1ecd15d1c53ac7bd",
487478
"is_verified": false,
488-
"line_number": 431,
479+
"line_number": 427,
489480
"type": "Base64 High Entropy String",
490481
"verified_result": null
491482
}
@@ -564,21 +555,21 @@
564555
{
565556
"hashed_secret": "e1166e6dd837019ab04f130ab34c425e04161645",
566557
"is_verified": false,
567-
"line_number": 384,
558+
"line_number": 391,
568559
"type": "Secret Keyword",
569560
"verified_result": null
570561
},
571562
{
572563
"hashed_secret": "f0c5bc5473fd2f959bdac630e625aa33346fd12a",
573564
"is_verified": false,
574-
"line_number": 431,
565+
"line_number": 438,
575566
"type": "Secret Keyword",
576567
"verified_result": null
577568
},
578569
{
579570
"hashed_secret": "29080f1c58f9859ddaa6aeda7d2c410c12e222dc",
580571
"is_verified": false,
581-
"line_number": 463,
572+
"line_number": 470,
582573
"type": "Secret Keyword",
583574
"verified_result": null
584575
}
@@ -596,10 +587,19 @@
596587
{
597588
"hashed_secret": "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8",
598589
"is_verified": false,
599-
"line_number": 135,
590+
"line_number": 138,
600591
"type": "Basic Auth Credentials",
601592
"verified_result": null
602593
}
594+
],
595+
"scripts/validate-env.sh": [
596+
{
597+
"hashed_secret": "8da52328e314a37358f9758f38d5bb0e8687b6ab",
598+
"is_verified": false,
599+
"line_number": 74,
600+
"type": "Secret Keyword",
601+
"verified_result": null
602+
}
603603
]
604604
},
605605
"version": "0.13.1+ibm.64.dss",

.tool-versions

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
nodejs 22.12.0
1+
nodejs 22.0.0
22
yarn 1.22.22

apps/api-gateway/src/auth/jwt/cookie-based/mock.jwt.cookie.auth.guard.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,14 @@ export class MockJwtCookieAuthGuard extends AuthGuard("cookie-strategy") {
2222
const request: RequestWithUserSession = context.switchToHttp().getRequest();
2323

2424
request.user = {
25-
userId: "dev-user",
26-
role: UserRole.LEARNER,
27-
groupId: "string",
28-
assignmentId: 1888,
25+
userId: "magdy.hafez@ibm.com",
26+
role: UserRole.AUTHOR,
27+
groupId: "autogen-faculty-v1-course-v1-IND-AI0103EN-v1",
28+
assignmentId: 1,
2929
gradingCallbackRequired: false,
3030
returnUrl: "https://skills.network",
3131
launch_presentation_locale: "en",
3232
};
33-
3433
return true;
3534
}
3635
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- AlterTable
2+
ALTER TABLE "Assignment" ADD COLUMN "questionControls" JSONB;
3+
4+
-- AlterTable
5+
ALTER TABLE "AssignmentVersion" ADD COLUMN "questionControls" JSONB;

apps/api/prisma/schema.prisma

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ model Assignment {
237237
showSubmissionFeedback Boolean @default(true) /// Should the AI provide feedback when the learner submits a question
238238
showQuestions Boolean @default(true) /// Should the questions be shown to the learner
239239
correctAnswerVisibility CorrectAnswerVisibility @default(ALWAYS) /// When should correct answers be shown to learners
240+
questionControls Json? /// Question-level controls (e.g., allowCopy, allowPaste, allowRightClick)
240241
updatedAt DateTime @default(now()) @updatedAt /// The DateTime at which the assignment was last updated
241242
languageCode String? /// The language code for the assignment
242243
currentVersionId Int? /// The ID of the current active version
@@ -287,6 +288,7 @@ model AssignmentVersion {
287288
showSubmissionFeedback Boolean @default(true) /// Show submission feedback
288289
showQuestions Boolean @default(true) /// Show questions
289290
correctAnswerVisibility CorrectAnswerVisibility @default(ALWAYS) /// When to show correct answers to learners
291+
questionControls Json? /// Question-level controls (e.g., allowCopy, allowPaste, allowRightClick)
290292
languageCode String? /// Language code
291293
createdBy String /// User who created this version
292294
createdAt DateTime @default(now()) /// When version was created

apps/api/src/api/assignment/dto/update.assignment.request.dto.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,19 @@ import {
1111
IsEnum,
1212
IsInt,
1313
IsNumber,
14+
IsObject,
1415
IsOptional,
1516
IsString,
1617
} from "class-validator";
1718

19+
export interface QuestionControls {
20+
allowCopy?: boolean;
21+
allowPaste?: boolean;
22+
allowRightClick?: boolean;
23+
preventPrint?: boolean;
24+
[key: string]: boolean | undefined;
25+
}
26+
1827
export class UpdateAssignmentRequestDto {
1928
@ApiProperty({
2029
description: "The name of the assignment.",
@@ -227,4 +236,13 @@ export class UpdateAssignmentRequestDto {
227236
})
228237
@IsOptional()
229238
correctAnswerVisibility: CorrectAnswerVisibility;
239+
240+
@ApiProperty({
241+
description: "Question-level controls (copy, paste, right-click)",
242+
required: false,
243+
type: "object",
244+
})
245+
@IsOptional()
246+
@IsObject()
247+
questionControls?: QuestionControls;
230248
}

apps/api/src/api/assignment/v1/services/assignment.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ export class AssignmentServiceV1 {
304304
},
305305
});
306306

307-
return authoredAssignments;
307+
return authoredAssignments as AssignmentResponseDto[];
308308
}
309309

310310
const results = await this.prisma.assignmentGroup.findMany({
@@ -322,7 +322,7 @@ export class AssignmentServiceV1 {
322322

323323
return results.map((result) => ({
324324
...result.assignment,
325-
}));
325+
})) as AssignmentResponseDto[];
326326
}
327327

328328
async replace(

apps/api/src/api/assignment/v2/repositories/assignment.repository.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ const FIELDS = [
4242
"showQuestionScore",
4343
"showSubmissionFeedback",
4444
"showQuestions",
45+
"correctAnswerVisibility",
46+
"questionControls",
4547
"languageCode",
4648
] as const;
4749

@@ -215,7 +217,7 @@ export class AssignmentRepository {
215217
},
216218
});
217219

218-
return authoredAssignments;
220+
return authoredAssignments as AssignmentResponseDto[];
219221
}
220222

221223
const results = await this.prisma.assignmentGroup.findMany({
@@ -231,7 +233,7 @@ export class AssignmentRepository {
231233

232234
return results.map((result) => ({
233235
...result.assignment,
234-
}));
236+
})) as AssignmentResponseDto[];
235237
}
236238

237239
/**

apps/api/src/api/assignment/v2/services/version-management.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ import {
1717
Question,
1818
} from "@prisma/client";
1919
import { WINSTON_MODULE_PROVIDER } from "nest-winston";
20+
import { assign } from "nodemailer/lib/shared";
2021
import {
2122
UserRole,
2223
UserSession,
2324
} from "src/auth/interfaces/user.session.interface";
2425
import { Logger } from "winston";
2526
import { PrismaService } from "../../../../database/prisma.service";
2627
import { QuestionDto } from "../../dto/update.questions.request.dto";
27-
import { assign } from "nodemailer/lib/shared";
2828

2929
export interface CreateVersionDto {
3030
versionNumber?: string;
@@ -303,6 +303,7 @@ export class VersionManagementService {
303303
showSubmissionFeedback: assignment.showSubmissionFeedback,
304304
showQuestions: assignment.showQuestions,
305305
correctAnswerVisibility: assignment.correctAnswerVisibility,
306+
questionControls: assignment.questionControls,
306307
languageCode: assignment.languageCode,
307308
createdBy: userSession.userId,
308309
isDraft: createVersionDto.isDraft ?? true,
@@ -551,6 +552,7 @@ export class VersionManagementService {
551552
showSubmissionFeedback: version.showSubmissionFeedback,
552553
showQuestions: version.showQuestions,
553554
correctAnswerVisibility: version.correctAnswerVisibility,
555+
questionControls: version.questionControls,
554556
languageCode: version.languageCode,
555557
questionVersions: questionVersionsWithVariants,
556558
};
@@ -1066,6 +1068,7 @@ export class VersionManagementService {
10661068
showSubmissionFeedback: assignment.showSubmissionFeedback,
10671069
showQuestions: assignment.showQuestions,
10681070
correctAnswerVisibility: assignment.correctAnswerVisibility,
1071+
questionControls: assignment.questionControls,
10691072
languageCode: assignment.languageCode,
10701073
},
10711074
include: { _count: { select: { questionVersions: true } } },
@@ -1358,6 +1361,7 @@ export class VersionManagementService {
13581361
showSubmissionFeedback: assignment.showSubmissionFeedback,
13591362
showQuestions: assignment.showQuestions,
13601363
correctAnswerVisibility: assignment.correctAnswerVisibility,
1364+
questionControls: assignment.questionControls,
13611365
languageCode: assignment.languageCode,
13621366
createdBy: userSession.userId,
13631367
isDraft: true,

apps/api/src/api/assignment/v2/tests/unit/__mocks__/ common-mocks.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,13 @@ export const createMockAssignment = (
970970
updatedAt: new Date(),
971971
languageCode: "en",
972972
currentVersionId: 1,
973+
questionControls: {
974+
allowCopy: true,
975+
allowPaste: true,
976+
allowRightClick: true,
977+
preventPrint: false,
978+
preventScreenshot: false,
979+
},
973980
...overrides,
974981
});
975982

@@ -990,7 +997,7 @@ export const createMockGetAssignmentResponseDto = (
990997
questions,
991998
success: true,
992999
...overrides,
993-
};
1000+
} as GetAssignmentResponseDto;
9941001
};
9951002

9961003
/**
@@ -1006,7 +1013,7 @@ export const createMockLearnerGetAssignmentResponseDto = (
10061013
questions: [] as Question[],
10071014
success: true,
10081015
...overrides,
1009-
};
1016+
} as LearnerGetAssignmentResponseDto;
10101017
};
10111018

10121019
/**
@@ -1020,7 +1027,7 @@ export const createMockAssignmentResponseDto = (
10201027
return {
10211028
...assignment,
10221029
...overrides,
1023-
};
1030+
} as AssignmentResponseDto;
10241031
};
10251032

10261033
/**

0 commit comments

Comments
 (0)