Skip to content

Commit 74d3bd9

Browse files
chore: simplify assignment export logic (#134)
* featue: authors can set cooldown periods for learner attempts * adding migration.sql, fixing imports, hiding retakeAttemptCoolDownMinutes if attemptsBeforeCoolDown is 0. * Delete apps/api/prisma/migrations/20250925181313_assignment_cooldown/migration.sql * chore: simplify assignment export logic * fix: add versioning change --------- Co-authored-by: Magdy-Hafez#030901 <113151015+MmagdyHafezZ@users.noreply.github.com> Co-authored-by: MmagdyhafezZ <magdy.hafez9123@gmail.com>
1 parent 8b0aa54 commit 74d3bd9

File tree

5 files changed

+71
-55
lines changed

5 files changed

+71
-55
lines changed

.secrets.baseline

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": null,
44
"lines": null
55
},
6-
"generated_at": "2025-10-09T10:00:02Z",
6+
"generated_at": "2025-10-09T10:36:06Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,8 @@ export class VersionManagementService {
645645
graded: versionToRestore.graded,
646646
numAttempts: versionToRestore.numAttempts,
647647
attemptsBeforeCoolDown: versionToRestore.attemptsBeforeCoolDown,
648-
retakeAttemptCoolDownMinutes: versionToRestore.retakeAttemptCoolDownMinutes,
648+
retakeAttemptCoolDownMinutes:
649+
versionToRestore.retakeAttemptCoolDownMinutes,
649650
allotedTimeMinutes: versionToRestore.allotedTimeMinutes,
650651
attemptsPerTimeRange: versionToRestore.attemptsPerTimeRange,
651652
attemptsTimeRangeHours: versionToRestore.attemptsTimeRangeHours,
@@ -1643,7 +1644,8 @@ export class VersionManagementService {
16431644
graded: sourceVersion.graded,
16441645
numAttempts: sourceVersion.numAttempts,
16451646
attemptsBeforeCoolDown: sourceVersion.attemptsBeforeCoolDown,
1646-
retakeAttemptCoolDownMinutes: sourceVersion.retakeAttemptCoolDownMinutes,
1647+
retakeAttemptCoolDownMinutes:
1648+
sourceVersion.retakeAttemptCoolDownMinutes,
16471649
allotedTimeMinutes: sourceVersion.allotedTimeMinutes,
16481650
attemptsPerTimeRange: sourceVersion.attemptsPerTimeRange,
16491651
attemptsTimeRangeHours: sourceVersion.attemptsTimeRangeHours,
@@ -2105,9 +2107,11 @@ export class VersionManagementService {
21052107
numAttempts:
21062108
draftData.assignmentData.numAttempts ?? assignment.numAttempts,
21072109
attemptsBeforeCoolDown:
2108-
draftData.assignmentData.attemptsBeforeCoolDown ?? assignment.attemptsBeforeCoolDown,
2110+
draftData.assignmentData.attemptsBeforeCoolDown ??
2111+
assignment.attemptsBeforeCoolDown,
21092112
retakeAttemptCoolDownMinutes:
2110-
draftData.assignmentData.retakeAttemptCoolDownMinutes ?? assignment.retakeAttemptCoolDownMinutes,
2113+
draftData.assignmentData.retakeAttemptCoolDownMinutes ??
2114+
assignment.retakeAttemptCoolDownMinutes,
21112115
allotedTimeMinutes:
21122116
draftData.assignmentData.allotedTimeMinutes ??
21132117
assignment.allotedTimeMinutes,

apps/web/app/author/[assignmentId]/review/page.tsx

Lines changed: 54 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ import {
3333
} from "@heroicons/react/24/solid";
3434
import { QuestionAuthorStore } from "@/config/types";
3535
import ExportModal, { ExportOptions } from "../../(components)/ExportModal";
36+
import { omit } from "../../../../lib/utils";
37+
38+
const CONFIG_KEYS_TO_OMIT = [
39+
"errors",
40+
"updatedAt",
41+
"id",
42+
"name",
43+
"introduction",
44+
"instructions",
45+
"gradingCriteriaOverview",
46+
"type",
47+
"questionOrder",
48+
"published",
49+
"languageCode",
50+
"currentVersionId",
51+
"questions",
52+
];
3653

3754
// Helper function to determine if a validation error is question-related
3855
const isQuestionRelatedValidationError = (message: string): boolean => {
@@ -744,31 +761,7 @@ function Component() {
744761
const [isExportModalOpen, setIsExportModalOpen] = useState(false);
745762
const [isIssuesModalOpen, setIsIssuesModalOpen] = useState(false);
746763

747-
const [
748-
graded,
749-
allotedTimeMinutes,
750-
timeEstimateMinutes,
751-
numAttempts,
752-
attemptsBeforeCoolDown,
753-
retakeAttemptCoolDownMinutes,
754-
passingGrade,
755-
displayOrder,
756-
questionDisplay,
757-
numberOfQuestionsPerAttempt,
758-
strictTimeLimit,
759-
] = useAssignmentConfig((state) => [
760-
state.graded,
761-
state.allotedTimeMinutes,
762-
state.timeEstimateMinutes,
763-
state.numAttempts,
764-
state.attemptsBeforeCoolDown,
765-
state.retakeAttemptCoolDownMinutes,
766-
state.passingGrade,
767-
state.displayOrder,
768-
state.questionDisplay,
769-
state.numberOfQuestionsPerAttempt,
770-
state.strictTimeLimit,
771-
]);
764+
const assignmentConfig = useAssignmentConfig((state) => state);
772765

773766
const [
774767
introduction,
@@ -1124,19 +1117,7 @@ function Component() {
11241117
}
11251118

11261119
if (exportOptions.includeConfig) {
1127-
exportData.config = {
1128-
graded,
1129-
allotedTimeMinutes,
1130-
timeEstimateMinutes,
1131-
numAttempts,
1132-
attemptsBeforeCoolDown,
1133-
retakeAttemptCoolDownMinutes,
1134-
passingGrade,
1135-
displayOrder,
1136-
questionDisplay,
1137-
numberOfQuestionsPerAttempt,
1138-
strictTimeLimit,
1139-
};
1120+
exportData.config = omit(assignmentConfig, CONFIG_KEYS_TO_OMIT);
11401121
}
11411122

11421123
if (exportOptions.includeFeedbackConfig) {
@@ -1698,7 +1679,7 @@ function Component() {
16981679
<ChangeComparison
16991680
label="Assignment Type"
17001681
before={originalAssignment.graded ? "Graded" : "Practice"}
1701-
after={graded ? "Graded" : "Practice"}
1682+
after={assignmentConfig.graded ? "Graded" : "Practice"}
17021683
onNavigate={() =>
17031684
router.push(`/author/${activeAssignmentId}/config`)
17041685
}
@@ -1714,8 +1695,8 @@ function Component() {
17141695
: "No time limit"
17151696
}
17161697
after={
1717-
allotedTimeMinutes
1718-
? `${allotedTimeMinutes} minutes`
1698+
assignmentConfig.allotedTimeMinutes
1699+
? `${assignmentConfig.allotedTimeMinutes} minutes`
17191700
: "No time limit"
17201701
}
17211702
/>
@@ -1730,8 +1711,8 @@ function Component() {
17301711
: "Not set"
17311712
}
17321713
after={
1733-
timeEstimateMinutes
1734-
? `${timeEstimateMinutes} minutes`
1714+
assignmentConfig.timeEstimateMinutes
1715+
? `${assignmentConfig.timeEstimateMinutes} minutes`
17351716
: "Not set"
17361717
}
17371718
/>
@@ -1745,39 +1726,59 @@ function Component() {
17451726
? "Unlimited"
17461727
: originalAssignment.numAttempts
17471728
}
1748-
after={numAttempts === -1 ? "Unlimited" : numAttempts}
1729+
after={
1730+
assignmentConfig.numAttempts === -1
1731+
? "Unlimited"
1732+
: assignmentConfig.numAttempts
1733+
}
17491734
/>
17501735
)}
17511736

17521737
{changes.attemptsBeforeCoolDown && (
17531738
<ChangeComparison
17541739
label="Number of Attempts Before Cooldown Period"
17551740
before={originalAssignment.attemptsBeforeCoolDown}
1756-
after={attemptsBeforeCoolDown}
1741+
after={assignmentConfig.attemptsBeforeCoolDown}
17571742
/>
17581743
)}
17591744

17601745
{changes.retakeAttemptCoolDownMinutes && (
17611746
<ChangeComparison
17621747
label="Number of Minutes Learners Must Wait Between Attempts"
17631748
before={originalAssignment.retakeAttemptCoolDownMinutes}
1764-
after={retakeAttemptCoolDownMinutes}
1749+
after={assignmentConfig.retakeAttemptCoolDownMinutes}
1750+
/>
1751+
)}
1752+
1753+
{changes.attemptsBeforeCoolDown && (
1754+
<ChangeComparison
1755+
label="Number of Attempts Before Cooldown Period"
1756+
before={originalAssignment.attemptsBeforeCoolDown}
1757+
after={changes.attemptsBeforeCoolDown}
1758+
/>
1759+
)}
1760+
1761+
{changes.retakeAttemptCoolDownMinutes && (
1762+
<ChangeComparison
1763+
label="Number of Minutes Learners Must Wait Between Attempts"
1764+
before={originalAssignment.retakeAttemptCoolDownMinutes}
1765+
after={changes.retakeAttemptCoolDownMinutes}
17651766
/>
17661767
)}
17671768

17681769
{changes.passingGrade && (
17691770
<ChangeComparison
17701771
label="Passing Grade"
17711772
before={`${originalAssignment.passingGrade}%`}
1772-
after={`${passingGrade}%`}
1773+
after={`${assignmentConfig.passingGrade}%`}
17731774
/>
17741775
)}
17751776

17761777
{changes.displayOrder && (
17771778
<ChangeComparison
17781779
label="Display Order"
17791780
before={originalAssignment.displayOrder}
1780-
after={displayOrder}
1781+
after={assignmentConfig.displayOrder}
17811782
/>
17821783
)}
17831784

@@ -1788,15 +1789,18 @@ function Component() {
17881789
/_/g,
17891790
" ",
17901791
)}
1791-
after={questionDisplay?.replace(/_/g, " ")}
1792+
after={assignmentConfig.questionDisplay?.replace(
1793+
/_/g,
1794+
" ",
1795+
)}
17921796
/>
17931797
)}
17941798

17951799
{changes.numberOfQuestionsPerAttempt && (
17961800
<ChangeComparison
17971801
label="Questions Per Attempt"
17981802
before={originalAssignment.numberOfQuestionsPerAttempt}
1799-
after={numberOfQuestionsPerAttempt}
1803+
after={assignmentConfig.numberOfQuestionsPerAttempt}
18001804
type="number"
18011805
/>
18021806
)}

apps/web/lib/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ export const getSubmitButtonStatus = (
187187

188188
return { disabled: false, reason: null };
189189
};
190+
190191
export const generateTempQuestionId = (): number => {
191192
return Math.floor(Math.random() * 2e9);
192193
};
194+
195+
export const omit = (obj: object, keys: string[]): object => {
196+
return Object.fromEntries(
197+
Object.entries(obj).filter(([k]) => !keys.includes(k)),
198+
);
199+
};

apps/web/stores/author.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { shallow } from "zustand/shallow";
1616
import { createWithEqualityFn } from "zustand/traditional";
1717
import { withUpdatedAt } from "./middlewares";
1818
import { DraftSummary, VersionSummary } from "@/lib/author";
19+
import { config } from "process";
1920
const NON_PERSIST_KEYS = new Set<keyof AuthorState | keyof AuthorActions>([
2021
// version control state
2122
"versions",

0 commit comments

Comments
 (0)