Skip to content

Commit f3d434e

Browse files
authored
Fix/variations (#140)
* fixing variants and removing private drafts * fix variants issue * fix test
1 parent 679d37a commit f3d434e

File tree

18 files changed

+471
-701
lines changed

18 files changed

+471
-701
lines changed

.secrets.baseline

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": null,
44
"lines": null
55
},
6-
"generated_at": "2025-09-21T22:56:11Z",
6+
"generated_at": "2025-10-03T10:22:15Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -306,7 +306,7 @@
306306
{
307307
"hashed_secret": "d3ecb0d890368d7659ee54010045b835dacb8efe",
308308
"is_verified": false,
309-
"line_number": 1596,
309+
"line_number": 1562,
310310
"type": "Secret Keyword",
311311
"verified_result": null
312312
}
@@ -346,7 +346,7 @@
346346
}
347347
]
348348
},
349-
"version": "0.13.1+ibm.62.dss",
349+
"version": "0.13.1+ibm.64.dss",
350350
"word_list": {
351351
"file": null,
352352
"hash": null

apps/api/prisma/schema.prisma

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
generator client {
55
provider = "prisma-client-js"
6-
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
76
}
87

98
// prisma/schema.prisma

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ describe("VersionManagementService", () => {
139139

140140
const result = await service.getVersion(1, 1);
141141

142-
expect(result).toEqual(mockVersion);
142+
expect(result).toMatchObject(mockVersion);
143+
expect(result.questionVersions[0].variants).toEqual([]);
143144
expect(
144145
mockPrismaService.assignmentVersion.findUnique,
145146
).toHaveBeenCalledWith({

apps/api/src/api/assignment/v2/services/translation.service.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,7 +1080,7 @@ export class TranslationService {
10801080
question.id,
10811081
null,
10821082
question.question,
1083-
question.choices as unknown as Choice[],
1083+
question.choices,
10841084
await this.llmFacadeService.getLanguageCode(
10851085
question.question,
10861086
assignmentId,
@@ -1108,7 +1108,7 @@ export class TranslationService {
11081108
question.id,
11091109
variant.id,
11101110
variant.variantContent,
1111-
variant.choices as unknown as Choice[],
1111+
variant.choices,
11121112
await this.llmFacadeService.getLanguageCode(
11131113
variant.variantContent,
11141114
assignmentId,
@@ -1891,10 +1891,30 @@ export class TranslationService {
18911891
questionId: number,
18921892
variantId: number | null,
18931893
originalText: string,
1894-
originalChoices: Choice[] | null,
1894+
originalChoices: Choice[] | null | string | any,
18951895
sourceLanguage: string,
18961896
targetLanguage: string,
18971897
): Promise<void> {
1898+
// Parse originalChoices if it's a string (from database JSON)
1899+
let parsedChoices: Choice[] | null = null;
1900+
if (originalChoices) {
1901+
if (typeof originalChoices === "string") {
1902+
try {
1903+
parsedChoices = JSON.parse(originalChoices) as Choice[];
1904+
} catch (error) {
1905+
this.logger.error(`Failed to parse choices JSON: ${String(error)}`);
1906+
parsedChoices = null;
1907+
}
1908+
} else if (Array.isArray(originalChoices)) {
1909+
parsedChoices = originalChoices as Choice[];
1910+
} else {
1911+
// Handle case where originalChoices is some other type
1912+
this.logger.warn(
1913+
`Unexpected type for originalChoices: ${typeof originalChoices}`,
1914+
);
1915+
parsedChoices = null;
1916+
}
1917+
}
18981918
// Check if we need to reuse existing translation
18991919
// Only reuse if it's from the same assignment (context-aware)
19001920
// const existingTranslation = await this.prisma.translation.findFirst({
@@ -1952,7 +1972,7 @@ export class TranslationService {
19521972
variantId,
19531973
languageCode: targetLanguage,
19541974
untranslatedText: originalText,
1955-
untranslatedChoices: this.prepareJsonValue(originalChoices),
1975+
untranslatedChoices: this.prepareJsonValue(parsedChoices),
19561976
translatedText: originalText,
19571977
translatedChoices: this.prepareJsonValue(originalChoices),
19581978
},
@@ -1979,7 +1999,7 @@ export class TranslationService {
19791999
// Different language - translate it
19802000
const translationPromises: Array<Promise<any>> = [];
19812001
let translatedText: string = originalText;
1982-
let translatedChoices: Choice[] | null = originalChoices;
2002+
let translatedChoices: Choice[] | null = parsedChoices;
19832003

19842004
// Translate the text
19852005
translationPromises.push(
@@ -2007,13 +2027,17 @@ export class TranslationService {
20072027
);
20082028

20092029
// Translate the choices if they exist
2010-
if (originalChoices) {
2030+
if (
2031+
parsedChoices &&
2032+
Array.isArray(parsedChoices) &&
2033+
parsedChoices.length > 0
2034+
) {
20112035
translationPromises.push(
20122036
this.executeWithOptimizedRetry(
20132037
`translateChoices-${questionId}-${targetLanguage}`,
20142038
() =>
20152039
this.llmFacadeService.generateChoicesTranslation(
2016-
originalChoices,
2040+
parsedChoices,
20172041
assignmentId,
20182042
targetLanguage,
20192043
),
@@ -2026,7 +2050,7 @@ export class TranslationService {
20262050
const errorMessage =
20272051
error instanceof Error ? error.message : String(error);
20282052
this.logger.error(`Failed to translate choices: ${errorMessage}`);
2029-
return originalChoices;
2053+
return parsedChoices;
20302054
}),
20312055
);
20322056
}
@@ -2042,7 +2066,7 @@ export class TranslationService {
20422066
variantId,
20432067
languageCode: targetLanguage,
20442068
untranslatedText: originalText,
2045-
untranslatedChoices: this.prepareJsonValue(originalChoices),
2069+
untranslatedChoices: this.prepareJsonValue(parsedChoices),
20462070
translatedText,
20472071
translatedChoices: this.prepareJsonValue(translatedChoices),
20482072
},

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

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,55 @@ export class VersionManagementService {
488488
},
489489
);
490490

491+
// Fetch variants for each question that has a questionId
492+
const questionVersionsWithVariants = await Promise.all(
493+
version.questionVersions.map(async (qv) => {
494+
let variants = [];
495+
if (qv.questionId) {
496+
const originalQuestion = await this.prisma.question.findUnique({
497+
where: { id: qv.questionId },
498+
include: {
499+
variants: {
500+
where: { isDeleted: false },
501+
},
502+
},
503+
});
504+
variants = originalQuestion?.variants || [];
505+
}
506+
507+
return {
508+
id: qv.id,
509+
questionId: qv.questionId,
510+
totalPoints: qv.totalPoints,
511+
type: qv.type,
512+
responseType: qv.responseType,
513+
question: qv.question,
514+
maxWords: qv.maxWords,
515+
scoring: qv.scoring,
516+
choices: qv.choices,
517+
randomizedChoices: qv.randomizedChoices,
518+
answer: qv.answer,
519+
gradingContextQuestionIds: qv.gradingContextQuestionIds,
520+
maxCharacters: qv.maxCharacters,
521+
videoPresentationConfig: qv.videoPresentationConfig,
522+
liveRecordingConfig: qv.liveRecordingConfig,
523+
displayOrder: qv.displayOrder,
524+
variants: variants.map((v) => ({
525+
id: v.id,
526+
variantContent: v.variantContent,
527+
choices: v.choices,
528+
scoring: v.scoring,
529+
maxWords: v.maxWords,
530+
maxCharacters: v.maxCharacters,
531+
variantType: v.variantType,
532+
randomizedChoices: v.randomizedChoices,
533+
isDeleted: v.isDeleted,
534+
answer: v.answer,
535+
})),
536+
};
537+
}),
538+
);
539+
491540
// Transform the response to match the expected format
492541
return {
493542
id: version.id,
@@ -520,25 +569,8 @@ export class VersionManagementService {
520569
showSubmissionFeedback: version.showSubmissionFeedback,
521570
showQuestions: version.showQuestions,
522571
languageCode: version.languageCode,
523-
// Transform questionVersions to the expected questions format
524-
questionVersions: version.questionVersions.map((qv) => ({
525-
id: qv.id,
526-
questionId: qv.questionId,
527-
totalPoints: qv.totalPoints,
528-
type: qv.type,
529-
responseType: qv.responseType,
530-
question: qv.question,
531-
maxWords: qv.maxWords,
532-
scoring: qv.scoring,
533-
choices: qv.choices,
534-
randomizedChoices: qv.randomizedChoices,
535-
answer: qv.answer,
536-
gradingContextQuestionIds: qv.gradingContextQuestionIds,
537-
maxCharacters: qv.maxCharacters,
538-
videoPresentationConfig: qv.videoPresentationConfig,
539-
liveRecordingConfig: qv.liveRecordingConfig,
540-
displayOrder: qv.displayOrder,
541-
})),
572+
// Use the enhanced questionVersions with variants
573+
questionVersions: questionVersionsWithVariants,
542574
};
543575
}
544576

apps/api/src/api/attempt/common/utils/attempt-questions-mapper.util.ts

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ export class AttemptQuestionsMapper {
214214
assignmentAttempt.questionOrder || assignment.questionOrder || [];
215215

216216
const questionVariantsArray = assignmentAttempt.questionVariants ?? [];
217-
218217
const processedQuestions = questionVariantsArray
219218
.map((qv) => {
220219
const variant = qv.questionVariant;
@@ -232,32 +231,15 @@ export class AttemptQuestionsMapper {
232231
? translations.get(variantKey) || {}
233232
: {};
234233

234+
console.log("variantTranslations", variantTranslations);
235+
235236
const questionTranslations = translations.has(questionKey)
236237
? translations.get(questionKey) || {}
237238
: {};
238239

239-
const translationFallback: TranslatedContent = {
240-
translatedText: variant
241-
? variant.variantContent || originalQ.question
242-
: originalQ.question,
243-
translatedChoices: (() => {
244-
const rawChoices = variant
245-
? variant.choices || originalQ.choices
246-
: originalQ.choices;
247-
if (typeof rawChoices === "string") {
248-
return rawChoices;
249-
}
250-
if (Array.isArray(rawChoices)) {
251-
return rawChoices as ExtendedChoice[];
252-
}
253-
return;
254-
})(),
255-
};
256-
257240
const variantTranslation = variantTranslations[language];
258241
const questionTranslation = questionTranslations[language];
259-
const primaryTranslation =
260-
variantTranslation || questionTranslation || translationFallback;
242+
const primaryTranslation = variantTranslation || questionTranslation;
261243

262244
const baseChoices = this.parseChoices(
263245
variant ? variant.choices || originalQ.choices : originalQ.choices,
@@ -318,10 +300,9 @@ export class AttemptQuestionsMapper {
318300

319301
const sanitizedTranslations =
320302
this.sanitizeTranslationsChoices(mergedTranslations);
321-
322303
return {
323304
id: originalQ.id,
324-
question: primaryTranslation.translatedText || originalQ.question,
305+
question: primaryTranslation.translatedText,
325306
choices: sanitizedChoices,
326307
translations: sanitizedTranslations,
327308
maxWords: variant?.maxWords ?? originalQ?.maxWords,

0 commit comments

Comments
 (0)