diff --git a/app/controllers/course/assessment/assessments_controller.rb b/app/controllers/course/assessment/assessments_controller.rb index 4a0c58a4ea0..545fe8ddaa8 100644 --- a/app/controllers/course/assessment/assessments_controller.rb +++ b/app/controllers/course/assessment/assessments_controller.rb @@ -237,7 +237,7 @@ def assessment_params :bonus_end_at, :published, :autograded, :show_mcq_mrq_solution, :show_private, :show_evaluation, :use_public, :use_private, :use_evaluation, :has_personal_times, :affects_personal_times, :block_student_viewing_after_submitted, :has_todo, - :allow_record_draft_answer, :time_limit, :is_koditsu_enabled] + :time_limit, :is_koditsu_enabled] base_params += if autograded? [:skippable, :allow_partial_submission, :show_mcq_answer] else diff --git a/app/models/course/assessment/answer.rb b/app/models/course/assessment/answer.rb index e613b8af515..90204d04fcd 100644 --- a/app/models/course/assessment/answer.rb +++ b/app/models/course/assessment/answer.rb @@ -140,10 +140,6 @@ def generate_live_feedback actable.generate_live_feedback end - def draft_answer? - attempting? && !current_answer? - end - protected def finalise diff --git a/app/services/course/assessment/submission/update_service.rb b/app/services/course/assessment/submission/update_service.rb index 89952ccd812..7db382b251b 100644 --- a/app/services/course/assessment/submission/update_service.rb +++ b/app/services/course/assessment/submission/update_service.rb @@ -106,13 +106,13 @@ def update_submission # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComple answer = @submission.answers.includes(:actable).find { |a| a.id == answer_params[:id].to_i } - if answer && !update_answer(answer, answer_params) - logger.error("Failed to update answer #{answer.errors.inspect}") - answer.errors.messages.each do |attribute, message| - @submission.errors.add(attribute, message) - end - raise ActiveRecord::Rollback + next unless answer && !update_answer(answer, answer_params) + + logger.error("Failed to update answer #{answer.errors.inspect}") + answer.errors.messages.each do |attribute, message| + @submission.errors.add(attribute, message) end + raise ActiveRecord::Rollback end end @@ -125,20 +125,6 @@ def update_submission # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComple end end - def attempt_draft_answer(answer) - return unless answer - - reattempt_answer(answer, finalise: false) if should_attempt_draft_answer?(answer) - end - - def should_attempt_draft_answer?(answer) - is_save_draft = update_submission_additional_params[:is_save_draft].to_s.downcase == 'true' - is_programming = answer.actable_type == Course::Assessment::Answer::Programming.name - assessment_save_draft_answer = @assessment.allow_record_draft_answer - - is_save_draft && is_programming && assessment_save_draft_answer - end - def unsubmit? params[:submission] && params[:submission][:unsubmit].present? end diff --git a/app/views/course/assessment/answers/_answer.json.jbuilder b/app/views/course/assessment/answers/_answer.json.jbuilder index 059215c7684..a106b7b3297 100644 --- a/app/views/course/assessment/answers/_answer.json.jbuilder +++ b/app/views/course/assessment/answers/_answer.json.jbuilder @@ -4,7 +4,6 @@ json.questionId answer.question_id json.questionType answer.question.question_type json.createdAt answer.created_at&.iso8601 json.clientVersion answer.client_version -json.isDraftAnswer answer.draft_answer? specific_answer = answer.specific can_grade = can?(:grade, answer.submission) diff --git a/app/views/course/assessment/assessments/edit.json.jbuilder b/app/views/course/assessment/assessments/edit.json.jbuilder index 4a2bc8a7d34..7c579693626 100644 --- a/app/views/course/assessment/assessments/edit.json.jbuilder +++ b/app/views/course/assessment/assessments/edit.json.jbuilder @@ -5,7 +5,7 @@ json.attributes do :skippable, :tabbed_view, :view_password, :session_password, :delayed_grade_publication, :tab_id, :use_public, :use_private, :use_evaluation, :allow_partial_submission, :has_personal_times, :affects_personal_times, :show_mcq_answer, :block_student_viewing_after_submitted, :has_todo, - :allow_record_draft_answer, :time_limit, :is_koditsu_enabled) + :time_limit, :is_koditsu_enabled) # TODO: [PR#5491] Edit Assessment only changes time in the Default Timeline json.start_at @assessment.start_at&.iso8601 diff --git a/app/views/course/assessment/assessments/show.json.jbuilder b/app/views/course/assessment/assessments/show.json.jbuilder index ff1c1b02ca3..8397635c02c 100644 --- a/app/views/course/assessment/assessments/show.json.jbuilder +++ b/app/views/course/assessment/assessments/show.json.jbuilder @@ -100,7 +100,6 @@ if can_attempt && assessment.folder.materials.exists? end if can_observe - json.allowRecordDraftAnswer assessment.allow_record_draft_answer json.showMcqMrqSolution assessment.show_mcq_mrq_solution json.gradedTestCases display_graded_test_types(assessment) diff --git a/client/app/bundles/course/assessment/components/AssessmentForm/index.tsx b/client/app/bundles/course/assessment/components/AssessmentForm/index.tsx index a01b54e983d..1f993f58a04 100644 --- a/client/app/bundles/course/assessment/components/AssessmentForm/index.tsx +++ b/client/app/bundles/course/assessment/components/AssessmentForm/index.tsx @@ -575,18 +575,6 @@ const AssessmentForm = (props: AssessmentFormProps): JSX.Element => { sticksToNavbar={editing} title={t(translations.answersAndTestCases)} > - ( - - )} - /> { {assessment.permissions.canObserve && ( <> - - - {t(translations.allowRecordDraftAnswer)} - - - - {assessment.allowRecordDraftAnswer ? '✅' : '❌'} - - - {t(translations.showMcqMrqSolution)} diff --git a/client/app/bundles/course/assessment/pages/AssessmentsIndex/NewAssessmentFormButton.jsx b/client/app/bundles/course/assessment/pages/AssessmentsIndex/NewAssessmentFormButton.jsx index 88ef6c6d812..5c17831a837 100644 --- a/client/app/bundles/course/assessment/pages/AssessmentsIndex/NewAssessmentFormButton.jsx +++ b/client/app/bundles/course/assessment/pages/AssessmentsIndex/NewAssessmentFormButton.jsx @@ -124,7 +124,6 @@ class NewAssessmentFormButton extends Component { autograded: false, is_koditsu_enabled: false, block_student_viewing_after_submitted: false, - allow_record_draft_answer: false, skippable: false, allow_partial_submission: false, show_mcq_answer: true, diff --git a/client/app/bundles/course/assessment/submission/components/pastAnswers/PastProgrammingAnswer.jsx b/client/app/bundles/course/assessment/submission/components/pastAnswers/PastProgrammingAnswer.jsx index 1a4339701bd..0228b6eed49 100644 --- a/client/app/bundles/course/assessment/submission/components/pastAnswers/PastProgrammingAnswer.jsx +++ b/client/app/bundles/course/assessment/submission/components/pastAnswers/PastProgrammingAnswer.jsx @@ -12,11 +12,7 @@ export default class PastProgrammingAnswer extends Component { return (
- +
); } @@ -33,11 +29,7 @@ export default class PastProgrammingAnswer extends Component { return (
{file && } - +
); } diff --git a/client/app/bundles/course/assessment/submission/containers/PastAnswers.jsx b/client/app/bundles/course/assessment/submission/containers/PastAnswers.jsx index a833c459074..9fbd0af419b 100644 --- a/client/app/bundles/course/assessment/submission/containers/PastAnswers.jsx +++ b/client/app/bundles/course/assessment/submission/containers/PastAnswers.jsx @@ -70,9 +70,6 @@ class PastAnswers extends Component { return ( {formatLongDateTime(answer.createdAt)} - {answer.isDraftAnswer && ( - <> {intl.formatMessage(translations.draftAnswer)} - )} ); }; @@ -100,10 +97,7 @@ class PastAnswers extends Component { return (
- {answer.isDraftAnswer - ? intl.formatMessage(translations.savedAt) - : intl.formatMessage(translations.submittedAt)} - : {date} + {intl.formatMessage(translations.submittedAt)} : {date} {this.getAnswersHistory(question, answer)}
diff --git a/client/app/bundles/course/assessment/submission/containers/TestCaseView/index.jsx b/client/app/bundles/course/assessment/submission/containers/TestCaseView/index.jsx index f31c4e8be48..7cda3dc3a08 100644 --- a/client/app/bundles/course/assessment/submission/containers/TestCaseView/index.jsx +++ b/client/app/bundles/course/assessment/submission/containers/TestCaseView/index.jsx @@ -187,7 +187,7 @@ export class VisibleTestCaseView extends Component { ); } - renderTestCases(testCases, testCaseType, warn, isDraftAnswer) { + renderTestCases(testCases, testCaseType, warn) { const { collapsible, testCases: { canReadTests }, @@ -277,10 +277,10 @@ export class VisibleTestCaseView extends Component { return ( } + icon={} id={testCaseType} subtitle={ warn && @@ -326,7 +326,6 @@ export class VisibleTestCaseView extends Component { testCases, collapsible, showStdoutAndStderr, - isDraftAnswer, } = this.props; if (!testCases) { return null; @@ -349,19 +348,13 @@ export class VisibleTestCaseView extends Component { )} - {this.renderTestCases( - testCases.public_test, - 'publicTestCases', - false, - isDraftAnswer, - )} + {this.renderTestCases(testCases.public_test, 'publicTestCases', false)} {showPrivateTest && this.renderTestCases( testCases.private_test, 'privateTestCases', !showPrivateTestToStudents, - isDraftAnswer, )} {showEvaluationTest && @@ -369,7 +362,6 @@ export class VisibleTestCaseView extends Component { testCases.evaluation_test, 'evaluationTestCases', !showEvaluationTestToStudents, - isDraftAnswer, )} {showOutputStreams && @@ -412,11 +404,10 @@ VisibleTestCaseView.propTypes = { stdout: PropTypes.string, stderr: PropTypes.string, }), - isDraftAnswer: PropTypes.bool, }; function mapStateToProps({ assessments: { submission } }, ownProps) { - const { questionId, answerId, viewHistory, isDraftAnswer } = ownProps; + const { questionId, answerId, viewHistory } = ownProps; let testCases; let isAutograding; if (viewHistory) { @@ -437,7 +428,6 @@ function mapStateToProps({ assessments: { submission } }, ownProps) { collapsible: viewHistory, isAutograding, testCases, - isDraftAnswer, }; } diff --git a/client/app/bundles/course/assessment/submission/propTypes.js b/client/app/bundles/course/assessment/submission/propTypes.js index 931e07a944b..583ff0304e1 100644 --- a/client/app/bundles/course/assessment/submission/propTypes.js +++ b/client/app/bundles/course/assessment/submission/propTypes.js @@ -91,7 +91,6 @@ export const answerShape = PropTypes.shape({ file: PropTypes.object, files: PropTypes.arrayOf(fileShape), option_ids: PropTypes.arrayOf(PropTypes.number), - isDraftAnswer: PropTypes.bool, createdAt: PropTypes.string, }); diff --git a/client/app/bundles/course/assessment/submission/reducers/history/answers.js b/client/app/bundles/course/assessment/submission/reducers/history/answers.js index c3e8511b91e..d8b4fc5c884 100644 --- a/client/app/bundles/course/assessment/submission/reducers/history/answers.js +++ b/client/app/bundles/course/assessment/submission/reducers/history/answers.js @@ -10,7 +10,6 @@ export default function (state = {}, action) { ...obj, [answer.id]: { ...answer.fields, - isDraftAnswer: answer.isDraftAnswer, createdAt: answer.createdAt, }, }), @@ -25,7 +24,6 @@ export default function (state = {}, action) { ...state, [latestAnswer.id]: { ...latestAnswer.fields, - isDraftAnswer: latestAnswer.isDraftAnswer, createdAt: latestAnswer.createdAt, }, }; diff --git a/client/app/bundles/course/assessment/submission/reducers/history/types.ts b/client/app/bundles/course/assessment/submission/reducers/history/types.ts index 3f9e8c22a7b..2bd0ad040fd 100644 --- a/client/app/bundles/course/assessment/submission/reducers/history/types.ts +++ b/client/app/bundles/course/assessment/submission/reducers/history/types.ts @@ -9,6 +9,5 @@ export interface QuestionHistory { } export type AnswerHistory = AnswerData & { - isDraftAnswer: boolean; createdAt: Date; }; diff --git a/client/app/bundles/course/assessment/submission/translations.ts b/client/app/bundles/course/assessment/submission/translations.ts index cf3f1393eb5..73635c02711 100644 --- a/client/app/bundles/course/assessment/submission/translations.ts +++ b/client/app/bundles/course/assessment/submission/translations.ts @@ -164,10 +164,6 @@ const translations = defineMessages({ id: 'course.assessment.submission.attemptedAt', defaultMessage: 'Attempted At', }, - savedAt: { - id: 'course.assessment.submission.savedAt', - defaultMessage: 'Saved At', - }, submittedAt: { id: 'course.assessment.submission.submittedAt', defaultMessage: 'Submitted At', @@ -512,10 +508,6 @@ const translations = defineMessages({ id: 'course.assessment.submission.getPastAnswersFailure', defaultMessage: 'Failed to load past answers', }, - draftAnswer: { - id: 'course.assessment.submission.draftAnswer', - defaultMessage: ' - Draft', - }, statistics: { id: 'course.assessment.submission.statistics', defaultMessage: 'Statistics', diff --git a/client/app/bundles/course/assessment/submission/types.ts b/client/app/bundles/course/assessment/submission/types.ts index 83430871370..705222f930b 100644 --- a/client/app/bundles/course/assessment/submission/types.ts +++ b/client/app/bundles/course/assessment/submission/types.ts @@ -165,7 +165,6 @@ interface Topic { export type TopicState = Record; export type HistoryAnswer = AnswerData & { - isDraftAnswer: boolean; createdAt: Date; }; diff --git a/client/app/bundles/course/assessment/translations.ts b/client/app/bundles/course/assessment/translations.ts index e754f362a22..ed80d5c11e9 100644 --- a/client/app/bundles/course/assessment/translations.ts +++ b/client/app/bundles/course/assessment/translations.ts @@ -235,10 +235,6 @@ const translations = defineMessages({ id: 'course.assessment.show.baseExp', defaultMessage: 'Base EXP', }, - allowRecordDraftAnswer: { - id: 'course.assessment.show.allowRecordDraftAnswer', - defaultMessage: 'Allow versioning of draft programming answer', - }, showMcqMrqSolution: { id: 'course.assessment.show.showMcqMrqSolution', defaultMessage: 'Show MCQ/MRQ solutions', diff --git a/client/app/types/course/assessment/assessments.ts b/client/app/types/course/assessment/assessments.ts index c6f63357464..5442345cbef 100644 --- a/client/app/types/course/assessment/assessments.ts +++ b/client/app/types/course/assessment/assessments.ts @@ -130,7 +130,6 @@ export interface AssessmentData extends AssessmentActionsData { isKoditsuAssessmentEnabled?: boolean; isSyncedWithKoditsu?: boolean; isStudent: boolean; - allowRecordDraftAnswer?: boolean; showMcqMrqSolution?: boolean; gradedTestCases?: string; skippable?: boolean; diff --git a/client/app/types/course/assessment/submission/answer/answer.ts b/client/app/types/course/assessment/submission/answer/answer.ts index d97819b204b..71c5c40fd89 100644 --- a/client/app/types/course/assessment/submission/answer/answer.ts +++ b/client/app/types/course/assessment/submission/answer/answer.ts @@ -5,7 +5,6 @@ export interface AnswerBaseData { questionId: number; createdAt: string; clientVersion: number | null; - isDraftAnswer: boolean; grading: { id: number; // Answer ID grade?: number | null; diff --git a/client/locales/en.json b/client/locales/en.json index ab10a227c2b..f89fbf47a55 100644 --- a/client/locales/en.json +++ b/client/locales/en.json @@ -1085,9 +1085,6 @@ "course.assessment.AssessmentForm.allowPartialSubmission": { "defaultMessage": "Allow submission with incorrect answers" }, - "course.assessment.AssessmentForm.allowRecordDraftAnswer": { - "defaultMessage": "Allow versioning of draft programming answer" - }, "course.assessment.AssessmentForm.answersAndTestCases": { "defaultMessage": "Answers and test cases" }, @@ -2054,9 +2051,6 @@ "course.assessment.show assessmentDeleted": { "defaultMessage": "Assessment successfully deleted." }, - "course.assessment.show.allowRecordDraftAnswer": { - "defaultMessage": "Allow versioning of draft programming answer" - }, "course.assessment.show.allowSkipSteps": { "defaultMessage": "Allow to skip steps" }, @@ -3083,9 +3077,6 @@ "course.assessment.submission.downloadSubmissionsJobPending": { "defaultMessage": "Please wait as your request to download submission answers is being processed." }, - "course.assessment.submission.draftAnswer": { - "defaultMessage": "- Draft" - }, "course.assessment.submission.dueAt": { "defaultMessage": "Due At" }, @@ -3287,9 +3278,6 @@ "course.assessment.submission.saveGrade": { "defaultMessage": "Save Grade" }, - "course.assessment.submission.savedAt": { - "defaultMessage": "Saved At" - }, "course.assessment.submission.sendReminderEmailConfirmation": { "defaultMessage": "Send reminder emails to {unattempted} unattempted and {attempting} attempting user(s) ({selectedUsers}) who have not completed the assessment?" }, @@ -7898,4 +7886,4 @@ "users.troubleSigningIn": { "defaultMessage": "Trouble signing in?" } -} \ No newline at end of file +} diff --git a/client/locales/zh.json b/client/locales/zh.json index a297321e32d..a20354649c7 100644 --- a/client/locales/zh.json +++ b/client/locales/zh.json @@ -1052,9 +1052,6 @@ "course.assessment.AssessmentForm.allowPartialSubmission": { "defaultMessage": "允许提交错误答案" }, - "course.assessment.AssessmentForm.allowRecordDraftAnswer": { - "defaultMessage": "允许对编程答题草稿进行版本控制" - }, "course.assessment.AssessmentForm.answersAndTestCases": { "defaultMessage": "答案和测试用例" }, @@ -2003,9 +2000,6 @@ "course.assessment.show assessmentDeleted": { "defaultMessage": "测验已成功删除。" }, - "course.assessment.show.allowRecordDraftAnswer": { - "defaultMessage": "允许对编程题草稿进行版本控制" - }, "course.assessment.show.allowSkipSteps": { "defaultMessage": "允许跳过步骤" }, @@ -3035,9 +3029,6 @@ "course.assessment.submission.downloadSubmissionsJobPending": { "defaultMessage": "正在处理你下载提交答案的请求,请稍候。" }, - "course.assessment.submission.draftAnswer": { - "defaultMessage": "- 草稿" - }, "course.assessment.submission.dueAt": { "defaultMessage": "截止日期" }, @@ -3239,9 +3230,6 @@ "course.assessment.submission.saveGrade": { "defaultMessage": "保存成绩" }, - "course.assessment.submission.savedAt": { - "defaultMessage": "保存于" - }, "course.assessment.submission.sendReminderEmailConfirmation": { "defaultMessage": "向未完成测验的 {unattempted} 未尝试和 {attempting} 尝试用户 ({selectedUsers}) 发送提醒邮件?" }, @@ -7850,4 +7838,4 @@ "users.troubleSigningIn": { "defaultMessage": "登录遇到问题?" } -} \ No newline at end of file +} diff --git a/db/migrate/20241129164745_remove_draft_programming_answer_column.rb b/db/migrate/20241129164745_remove_draft_programming_answer_column.rb new file mode 100644 index 00000000000..e05bbaa3903 --- /dev/null +++ b/db/migrate/20241129164745_remove_draft_programming_answer_column.rb @@ -0,0 +1,5 @@ +class RemoveDraftProgrammingAnswerColumn < ActiveRecord::Migration[7.0] + def change + remove_column :course_assessments, :allow_record_draft_answer, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index f44da250714..5cb20057a08 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -510,7 +510,6 @@ t.boolean "block_student_viewing_after_submitted", default: false t.integer "satisfiability_type", default: 0 t.bigint "monitor_id" - t.boolean "allow_record_draft_answer", default: false t.integer "time_limit" t.string "koditsu_assessment_id" t.boolean "is_koditsu_enabled" diff --git a/lib/tasks/db/remove_draft_programming_answer.rake b/lib/tasks/db/remove_draft_programming_answer.rake new file mode 100644 index 00000000000..cdcf641ae7e --- /dev/null +++ b/lib/tasks/db/remove_draft_programming_answer.rake @@ -0,0 +1,11 @@ +# frozen_string_literal: true +namespace :db do + task remove_draft_programming_answer: :environment do + ActsAsTenant.without_tenant do + answers = Course::Assessment::Answer.where(current_answer: false, + workflow_state: 'attempting') + + answers.each(&:destroy!) + end + end +end