diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodeExercise.tsx b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodeExercise.tsx index b4a493159..435e4a0b7 100644 --- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodeExercise.tsx +++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodeExercise.tsx @@ -61,6 +61,7 @@ const getDefaultFormData = (): Partial => ({ enableCodeTailor: false, parsonspersonalize: "", parsonsexample: "", + parsonsPersonalized: true, enableCodelens: true }); @@ -100,6 +101,7 @@ export const ActiveCodeExercise: FC = ({ enableCodeTailor: data.enableCodeTailor, parsonspersonalize: data.parsonspersonalize, parsonsexample: data.parsonsexample, + parsonsPersonalized: data.parsonsPersonalized, enableCodelens: data.enableCodelens } ); @@ -237,6 +239,7 @@ export const ActiveCodeExercise: FC = ({ enableCodeTailor={formData.enableCodeTailor} parsonspersonalize={formData.parsonspersonalize} parsonsexample={formData.parsonsexample} + parsonsPersonalized={formData.parsonsPersonalized} enableCodelens={formData.enableCodelens} /> ); diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodeExerciseSettings.tsx b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodeExerciseSettings.tsx index 8d222e9c0..fe838a700 100644 --- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodeExerciseSettings.tsx +++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodeExerciseSettings.tsx @@ -32,7 +32,8 @@ export const ActiveCodeExerciseSettings: FC = ( // Reset personalization level when disabling parsonspersonalize: enabled ? "movable" : "", // Reset parsons example when disabling - parsonsexample: enabled ? formData.parsonsexample : "" + parsonsexample: enabled ? formData.parsonsexample : "", + parsonsPersonalized: enabled ? (formData.parsonsPersonalized ?? true) : true }); }; @@ -112,6 +113,25 @@ export const ActiveCodeExerciseSettings: FC = ( + +
+
+ onChange({ parsonsPersonalized: e.value })} + /> + + + +
+
)} diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodePreview.tsx b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodePreview.tsx index b3d92b68c..4b4c64cc4 100644 --- a/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodePreview.tsx +++ b/bases/rsptx/assignment_server_api/assignment_builder/src/components/routes/AssignmentBuilder/components/exercises/components/CreateExercise/components/ActiveCodeExercise/ActiveCodePreview.tsx @@ -17,6 +17,7 @@ interface ActiveCodePreviewProps { enableCodeTailor?: boolean; parsonspersonalize?: "movable" | "partial" | ""; parsonsexample?: string; + parsonsPersonalized?: boolean; enableCodelens?: boolean; } @@ -32,6 +33,7 @@ export const ActiveCodePreview: FC = ({ enableCodeTailor, parsonspersonalize, parsonsexample, + parsonsPersonalized, enableCodelens }) => { // Fetch datafiles list to get filenames for selected acids @@ -64,6 +66,7 @@ export const ActiveCodePreview: FC = ({ enableCodeTailor, parsonspersonalize, parsonsexample, + parsonsPersonalized, enableCodelens } )} diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/types/exercises.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/types/exercises.ts index 5d5129080..580003b2b 100644 --- a/bases/rsptx/assignment_server_api/assignment_builder/src/types/exercises.ts +++ b/bases/rsptx/assignment_server_api/assignment_builder/src/types/exercises.ts @@ -110,6 +110,7 @@ export type QuestionJSON = Partial<{ enableCodeTailor: boolean; parsonspersonalize: "movable" | "partial" | ""; parsonsexample: string; + parsonsPersonalized: boolean; enableCodelens: boolean; iframeSrc: string; // Parsons problem options diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/utils/preview/activeCode.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/utils/preview/activeCode.ts index f91d3ec97..b272d8bae 100644 --- a/bases/rsptx/assignment_server_api/assignment_builder/src/utils/preview/activeCode.ts +++ b/bases/rsptx/assignment_server_api/assignment_builder/src/utils/preview/activeCode.ts @@ -9,6 +9,7 @@ export interface CodeTailorOptions { enableCodeTailor?: boolean; parsonspersonalize?: "movable" | "partial" | ""; parsonsexample?: string; + parsonsPersonalized?: boolean; enableCodelens?: boolean; } @@ -39,6 +40,9 @@ export const generateActiveCodePreview = ( // If parsonsexample is provided, use it; otherwise default to LLM-example const parsonsExampleValue = codeTailorOptions.parsonsexample?.trim() || "LLM-example"; codeTailorAttrs += ` data-parsonsexample="${parsonsExampleValue}"`; + if (codeTailorOptions.parsonsPersonalized === false) { + codeTailorAttrs += ` data-parsons-personalized="false"`; + } } // Codelens attribute - defaults to true diff --git a/bases/rsptx/assignment_server_api/assignment_builder/src/utils/questionJson.ts b/bases/rsptx/assignment_server_api/assignment_builder/src/utils/questionJson.ts index b3cf0dc53..a1f300d12 100644 --- a/bases/rsptx/assignment_server_api/assignment_builder/src/utils/questionJson.ts +++ b/bases/rsptx/assignment_server_api/assignment_builder/src/utils/questionJson.ts @@ -16,6 +16,7 @@ export const buildQuestionJson = (data: CreateExerciseFormType) => { enableCodeTailor: data.enableCodeTailor, parsonspersonalize: data.parsonspersonalize, parsonsexample: data.parsonsexample, + parsonsPersonalized: data.parsonsPersonalized, enableCodelens: data.enableCodelens }), ...(data.question_type === "shortanswer" && { @@ -103,6 +104,7 @@ export const getDefaultQuestionJson = (languageOptions: TableDropdownOption[]) = enableCodeTailor: false, parsonspersonalize: "", parsonsexample: "", + parsonsPersonalized: true, enableCodelens: true }); @@ -112,7 +114,7 @@ export const mergeQuestionJsonWithDefaults = ( ): QuestionJSON => { const defaultQuestionJson = getDefaultQuestionJson(languageOptions); - return { + const merged = { ...defaultQuestionJson, ...questionJson, optionList: questionJson?.optionList ?? defaultQuestionJson.optionList, @@ -135,6 +137,8 @@ export const mergeQuestionJsonWithDefaults = ( questionJson?.parsonspersonalize ?? (defaultQuestionJson.parsonspersonalize as "" | "movable" | "partial"), parsonsexample: questionJson?.parsonsexample ?? defaultQuestionJson.parsonsexample, + parsonsPersonalized: questionJson?.parsonsPersonalized ?? defaultQuestionJson.parsonsPersonalized, questionLabels: questionJson?.questionLabels ?? {} }; + return merged; }; diff --git a/bases/rsptx/book_server_api/routers/coach.py b/bases/rsptx/book_server_api/routers/coach.py index 718349e0d..ce1b543f2 100644 --- a/bases/rsptx/book_server_api/routers/coach.py +++ b/bases/rsptx/book_server_api/routers/coach.py @@ -228,6 +228,7 @@ async def parsons_scaffolding(request: Request, course: Optional[str]): internal_test_case = data.get( "internal_test_case" ) # Capture the internal test case from the front end + parsons_personalized = data.get("parsons_personalized", True) print("start_to: get_parsons_help", api_token, language, personalization_level) adaptive_attr = 'data-adaptive="true"' @@ -270,6 +271,68 @@ def parsons_help( } return get_parsons_help(api_token, language, input_dict, personalization_level) + if not parsons_personalized: + # Return example without CodeTailor personalization + if parsonsexample != "LLM-example" and parsonsexample_html: + parsons_html = f""" + + """ + return ( + parsonsexample_code + + "||split||" + + parsons_html + + "||split||" + + personalization_level + + "||split||" + + "example_solution" + ) + else: + from .personalized_parsons.get_personalized_solution import ( + get_example_solution, + ) + from .personalized_parsons.generate_parsons_blocks import ( + generate_Parsons_block, + ) + + example_code = get_example_solution( + api_token, language, problem_description, internal_test_case + ) + if not example_code: + return ( + "emptyHelpCode" + + "||split||emptyHelpParsons" + + "||split||" + + personalization_level + + "||split||example_solution" + ) + example_block = generate_Parsons_block( + "Solution", + language, + "Full", + problem_description, + example_code, + [], + [], + {}, + ) + example_block = re.sub(r"<(?=\S)", "< ", example_block) + parsons_html = f""" + + """ + return ( + example_code + + "||split||" + + parsons_html + + "||split||" + + personalization_level + + "||split||" + + "example_solution" + ) + if personalization_level in ["Solution", "Multiple"]: ( personalized_code_solution, diff --git a/bases/rsptx/interactives/runestone/activecode/js/activecode.js b/bases/rsptx/interactives/runestone/activecode/js/activecode.js index cbb6d7453..ba42735b4 100755 --- a/bases/rsptx/interactives/runestone/activecode/js/activecode.js +++ b/bases/rsptx/interactives/runestone/activecode/js/activecode.js @@ -80,6 +80,9 @@ export class ActiveCode extends RunestoneBase { this.code = $(orig).text() || "\n\n\n\n\n"; this.parsonspersonalize = $(orig).data("parsonspersonalize"); // CodeTailor: Allows the instructor to choose personalization level: solution-level or block-and-solution level this.parsonsexample = $(orig).data("parsonsexample"); // CodeTailor: Allows the instructor to choose an example Parsons problem or LLM-example (default auto-fill, means the example is generated by LLM) + // true by default; only false when explicitly set + this.parsonsPersonalized = $(orig).data("parsons-personalized") !== false + && $(orig).data("parsons-personalized") !== "false"; this.puzzleScaffoldingDivid = `help_puzzle_${this.parsonspersonalize}_${this.divid}`; // CodeTailor: the div id for the puzzle scaffolding help window this.language = $(orig).data("lang"); this.timelimit = $(orig).data("timelimit"); @@ -654,7 +657,8 @@ export class ActiveCode extends RunestoneBase { personalization_level: personalization_level, parsonsexample: parsonsexample, problem_description: problem_description, - internal_test_case: internal_test_case + internal_test_case: internal_test_case, + parsons_personalized: this.parsonsPersonalized }; console.log('body:', body) // CodeTailor: create a promise that rejects if the fetch takes too long