From 559e7c36ae1bc5c1e094d976eee2c7adf7b5a80d Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 17 Oct 2024 14:51:49 +0200 Subject: [PATCH] Forward auto-grading config when editing an assignment (#6779) --- .../components/FilePickerApp.tsx | 65 ++++++++++++++----- .../components/FilePickerFormFields.tsx | 12 ++++ .../components/test/FilePickerApp-test.js | 33 +++++++++- .../test/FilePickerFormFields-test.js | 17 +++++ 4 files changed, 110 insertions(+), 17 deletions(-) diff --git a/lms/static/scripts/frontend_apps/components/FilePickerApp.tsx b/lms/static/scripts/frontend_apps/components/FilePickerApp.tsx index ab12cf01c1..2217ba6ad4 100644 --- a/lms/static/scripts/frontend_apps/components/FilePickerApp.tsx +++ b/lms/static/scripts/frontend_apps/components/FilePickerApp.tsx @@ -13,7 +13,13 @@ import { } from '@hypothesis/frontend-shared'; import classnames from 'classnames'; import type { ComponentChildren } from 'preact'; -import { useCallback, useEffect, useRef, useState } from 'preact/hooks'; +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'preact/hooks'; import { Link as RouterLink } from 'wouter-preact'; import type { AutoGradingConfig as APIAutoGradingConfig } from '../api-types'; @@ -192,12 +198,47 @@ export default function FilePickerApp({ onSubmit }: FilePickerAppProps) { ); const [autoGradingConfig, setAutoGradingConfig] = useState( - { - gradingType: 'all_or_nothing', - activityCalculation: 'cumulative', - requiredAnnotations: 1, + () => { + const assignmentAutoGradingConfig = assignment?.auto_grading_config; + if (!assignmentAutoGradingConfig) { + return { + enabled: false, + gradingType: 'all_or_nothing', + activityCalculation: 'cumulative', + requiredAnnotations: 1, + }; + } + + // Initialize with the assignment's auto-grading config if it exists + return { + enabled: true, + gradingType: assignmentAutoGradingConfig.grading_type, + activityCalculation: assignmentAutoGradingConfig.activity_calculation, + requiredAnnotations: assignmentAutoGradingConfig.required_annotations, + requiredReplies: assignmentAutoGradingConfig.required_replies, + }; }, ); + // The auto-grading config as expected by the backend + const autoGradingConfigToSave = useMemo( + () => + autoGradingEnabled && autoGradingConfig.enabled + ? { + grading_type: autoGradingConfig.gradingType, + activity_calculation: autoGradingConfig.activityCalculation, + required_annotations: autoGradingConfig.requiredAnnotations, + required_replies: autoGradingConfig.requiredReplies, + } + : null, + [ + autoGradingConfig.activityCalculation, + autoGradingConfig.enabled, + autoGradingConfig.gradingType, + autoGradingConfig.requiredAnnotations, + autoGradingConfig.requiredReplies, + autoGradingEnabled, + ], + ); // Flag indicating if we are editing content that was previously selected. const [editingContent, setEditingContent] = useState(false); @@ -263,15 +304,7 @@ export default function FilePickerApp({ onSubmit }: FilePickerAppProps) { try { const data: DeepLinkingAPIData = { ...deepLinkingAPI.data, - auto_grading_config: - autoGradingEnabled && autoGradingConfig.enabled - ? { - grading_type: autoGradingConfig.gradingType, - activity_calculation: autoGradingConfig.activityCalculation, - required_annotations: autoGradingConfig.requiredAnnotations, - required_replies: autoGradingConfig.requiredReplies, - } - : null, + auto_grading_config: autoGradingConfigToSave, content, group_set: groupConfig.useGroupSet ? groupConfig.groupSet : null, title, @@ -300,8 +333,7 @@ export default function FilePickerApp({ onSubmit }: FilePickerAppProps) { groupConfig.groupSet, groupConfig.useGroupSet, title, - autoGradingEnabled, - autoGradingConfig, + autoGradingConfigToSave, ], ); @@ -518,6 +550,7 @@ export default function FilePickerApp({ onSubmit }: FilePickerAppProps) { content={content} formFields={formFields} groupSet={groupConfig.useGroupSet ? groupConfig.groupSet : null} + autoGradingConfig={autoGradingConfigToSave} /> ) } diff --git a/lms/static/scripts/frontend_apps/components/FilePickerFormFields.tsx b/lms/static/scripts/frontend_apps/components/FilePickerFormFields.tsx index c9ce8be0c4..ca47765d7d 100644 --- a/lms/static/scripts/frontend_apps/components/FilePickerFormFields.tsx +++ b/lms/static/scripts/frontend_apps/components/FilePickerFormFields.tsx @@ -1,3 +1,4 @@ +import type { AutoGradingConfig } from '../api-types'; import type { Content } from '../utils/content-item'; export type FilePickerFormFieldsProps = { @@ -20,6 +21,9 @@ export type FilePickerFormFieldsProps = { /** Assignment title chosen by the user, if supported by the current LMS. */ title: string | null; + + /** Auto-grading configuration for assignments where it is enabled */ + autoGradingConfig: AutoGradingConfig | null; }; /** @@ -33,6 +37,7 @@ export default function FilePickerFormFields({ content, formFields, groupSet, + autoGradingConfig, }: FilePickerFormFieldsProps) { return ( <> @@ -46,6 +51,13 @@ export default function FilePickerFormFields({ )} {title !== null && } + {autoGradingConfig && ( + + )} ); } diff --git a/lms/static/scripts/frontend_apps/components/test/FilePickerApp-test.js b/lms/static/scripts/frontend_apps/components/test/FilePickerApp-test.js index 3a9506d8aa..4bcf473814 100644 --- a/lms/static/scripts/frontend_apps/components/test/FilePickerApp-test.js +++ b/lms/static/scripts/frontend_apps/components/test/FilePickerApp-test.js @@ -72,7 +72,13 @@ describe('FilePickerApp', () => { */ function checkFormFields( wrapper, - { content, groupSet = null, formFields = {}, title = null }, + { + content, + groupSet = null, + formFields = {}, + title = null, + autoGradingConfig = null, + }, ) { const fieldsComponent = wrapper.find('FilePickerFormFields'); assert.deepEqual(fieldsComponent.props(), { @@ -81,6 +87,7 @@ describe('FilePickerApp', () => { formFields: { ...fakeConfig.filePicker.formFields, ...formFields }, groupSet, title, + autoGradingConfig, }); } @@ -406,6 +413,30 @@ describe('FilePickerApp', () => { }); }); + it('initializes auto_grading_config if assignment already has it', () => { + const autoGradingConfig = { + grading_type: 'scaled', + activity_calculation: 'separate', + required_annotations: 10, + required_replies: 5, + }; + const url = 'https://example.com'; + + fakeConfig.assignment = { + auto_grading_config: autoGradingConfig, + document: { url }, + }; + fakeConfig.filePicker.autoGradingEnabled = true; + + const onSubmit = sinon.stub().callsFake(e => e.preventDefault()); + const wrapper = renderFilePicker({ onSubmit }); + + checkFormFields(wrapper, { + content: { type: 'url', url }, + autoGradingConfig, + }); + }); + it('does not submit form when "Continue" is clicked if there are validation errors', () => { fakeConfig.filePicker.promptForTitle = true; diff --git a/lms/static/scripts/frontend_apps/components/test/FilePickerFormFields-test.js b/lms/static/scripts/frontend_apps/components/test/FilePickerFormFields-test.js index e3e93e7e79..a3c11fd1d9 100644 --- a/lms/static/scripts/frontend_apps/components/test/FilePickerFormFields-test.js +++ b/lms/static/scripts/frontend_apps/components/test/FilePickerFormFields-test.js @@ -74,4 +74,21 @@ describe('FilePickerFormFields', () => { assert.isTrue(titleField.exists()); assert.equal(titleField.prop('value'), 'Example assignment'); }); + + it('renders `auto_grading_config` if `autoGradingConfig` prop is set', () => { + const autoGradingConfig = { + grading_type: 'scaled', + activity_calculation: 'separate', + required_annotations: 10, + required_replies: 5, + }; + const formFields = createComponent({ + content: { type: 'url', url: 'https://example.com/' }, + autoGradingConfig, + }); + const configField = formFields.find('input[name="auto_grading_config"]'); + + assert.isTrue(configField.exists()); + assert.equal(configField.prop('value'), JSON.stringify(autoGradingConfig)); + }); });