From 05f6e66cfbb8e47f0ce8f286e18bf91ac61022f1 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Thu, 11 Jun 2026 23:01:22 -0400 Subject: [PATCH 1/9] made progress on the task: got confirmation dialog to show in certain cases --- app/controllers/submissions_controller.rb | 5 +++++ app/javascript/Components/submission_file_manager.jsx | 7 +++++++ app/views/submissions/file_manager.html.erb | 1 + 3 files changed, 13 insertions(+) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index e2e6f5e39c..82aa94f765 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -1004,17 +1004,22 @@ def set_filebrowser_vars(grouping) # Used in update_files and file_manager actions. # Requires @grouping and @assignment variables to be set. def flash_file_manager_messages + @past_due_date = false + if @assignment.is_timed && @grouping.start_time.nil? && @grouping.past_collection_date? flash_message(:warning, "#{I18n.t('assignments.timed.past_end_time')} #{I18n.t('submissions.past_collection_time')}") + @past_due_date = true elsif @assignment.is_timed && !@grouping.start_time.nil? && !@assignment.grouping_past_due_date?(@grouping) flash_message(:notice, I18n.t('assignments.timed.time_until_due_warning', due_date: I18n.l(@grouping.due_date))) elsif @grouping.past_collection_date? flash_message(:warning, "#{@assignment.submission_rule.class.human_attribute_name(:after_collection_message)} " \ "#{I18n.t('submissions.past_collection_time')}") + @past_due_date = true elsif @assignment.grouping_past_due_date?(@grouping) flash_message(:warning, @assignment.submission_rule.overtime_message(@grouping)) + @past_due_date = true end unless @grouping.is_valid? diff --git a/app/javascript/Components/submission_file_manager.jsx b/app/javascript/Components/submission_file_manager.jsx index f8bb4227d4..0c624477d8 100644 --- a/app/javascript/Components/submission_file_manager.jsx +++ b/app/javascript/Components/submission_file_manager.jsx @@ -99,6 +99,13 @@ class SubmissionFileManager extends React.Component { }; handleCreateFiles = (files, path, unzip, renameTo = "") => { + if (this.props.past_due_date) { + let result = confirm("Testing 1 2 3"); + if (!result) { + return; + } + } + if ( !this.props.starterFileChanged || confirm(I18n.t("assignments.starter_file.upload_confirmation")) diff --git a/app/views/submissions/file_manager.html.erb b/app/views/submissions/file_manager.html.erb index 593057ace2..c9cbc1f442 100644 --- a/app/views/submissions/file_manager.html.erb +++ b/app/views/submissions/file_manager.html.erb @@ -8,6 +8,7 @@ course_id: <%= @current_course.id %>, assignment_id: <%= @assignment.id %>, grouping_id: <%= @grouping.id %>, + past_due_date: <%= @past_due_date %>, readOnly: <%= !@assignment.allow_web_submits %>, enableSubdirs: <%= allowed_to? :manage_subdirectories? %>, enableUrlSubmit: <%= @grouping.assignment.url_submit %>, From c890aba4ea883a3654b7e616e3f37014829717b5 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Fri, 12 Jun 2026 23:34:33 -0400 Subject: [PATCH 2/9] did some refactoring and completed the assigned task (no tests added yet) --- app/controllers/submissions_controller.rb | 10 +++++----- .../Components/submission_file_manager.jsx | 17 ++++++++++++----- .../penalty_decay_period_submission_rule.rb | 12 ++++++++---- app/models/penalty_period_submission_rule.rb | 12 ++++++++---- app/models/submission_rule.rb | 4 ++++ app/views/submissions/file_manager.html.erb | 2 +- config/locales/views/submissions/en.yml | 1 + 7 files changed, 39 insertions(+), 19 deletions(-) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 82aa94f765..fb82d0163e 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -126,6 +126,11 @@ def file_manager return end + @past_due_date = @assignment.grouping_past_due_date?(@grouping) + @late_penalty = @assignment.submission_rule.penalty_for(@grouping) + @past_collection_date = @grouping.past_collection_date? + @show_late_submit_confirmation = @past_due_date && @late_penalty.positive? && !@past_collection_date + authorize! @grouping, to: :view_file_manager? @path = params[:path] || '/' @@ -1004,22 +1009,17 @@ def set_filebrowser_vars(grouping) # Used in update_files and file_manager actions. # Requires @grouping and @assignment variables to be set. def flash_file_manager_messages - @past_due_date = false - if @assignment.is_timed && @grouping.start_time.nil? && @grouping.past_collection_date? flash_message(:warning, "#{I18n.t('assignments.timed.past_end_time')} #{I18n.t('submissions.past_collection_time')}") - @past_due_date = true elsif @assignment.is_timed && !@grouping.start_time.nil? && !@assignment.grouping_past_due_date?(@grouping) flash_message(:notice, I18n.t('assignments.timed.time_until_due_warning', due_date: I18n.l(@grouping.due_date))) elsif @grouping.past_collection_date? flash_message(:warning, "#{@assignment.submission_rule.class.human_attribute_name(:after_collection_message)} " \ "#{I18n.t('submissions.past_collection_time')}") - @past_due_date = true elsif @assignment.grouping_past_due_date?(@grouping) flash_message(:warning, @assignment.submission_rule.overtime_message(@grouping)) - @past_due_date = true end unless @grouping.is_valid? diff --git a/app/javascript/Components/submission_file_manager.jsx b/app/javascript/Components/submission_file_manager.jsx index 0c624477d8..89a500fdf9 100644 --- a/app/javascript/Components/submission_file_manager.jsx +++ b/app/javascript/Components/submission_file_manager.jsx @@ -77,7 +77,17 @@ class SubmissionFileManager extends React.Component { } } + showConfirmWhenLate = () => { + if (this.props.show_late_submit_confirmation) { + return confirm(I18n.t("submissions.student.upload_file_confirmation_dialog")); + } + return true; + }; + handleCreateUrl = (url, url_text) => { + if (!this.showConfirmWhenLate()) { + return; + } this.setState({showURLModal: false}); const data_to_upload = { new_url: url, @@ -99,11 +109,8 @@ class SubmissionFileManager extends React.Component { }; handleCreateFiles = (files, path, unzip, renameTo = "") => { - if (this.props.past_due_date) { - let result = confirm("Testing 1 2 3"); - if (!result) { - return; - } + if (!this.showConfirmWhenLate()) { + return; } if ( diff --git a/app/models/penalty_decay_period_submission_rule.rb b/app/models/penalty_decay_period_submission_rule.rb index fec3837405..14526a8eeb 100644 --- a/app/models/penalty_decay_period_submission_rule.rb +++ b/app/models/penalty_decay_period_submission_rule.rb @@ -23,10 +23,7 @@ class PenaltyDecayPeriodSubmissionRule < SubmissionRule inclusion: { in: [ExtraMark::PERCENTAGE, ExtraMark::POINTS, ExtraMark::PERCENTAGE_OF_MARK] } def overtime_message(grouping) - # How far are we into overtime? - overtime_hours = calculate_overtime_hours_from(Time.current, grouping) - # Calculate the penalty that the grouping will suffer - potential_penalty = calculate_penalty(overtime_hours) + potential_penalty = penalty_for(grouping) penalty_suffix = penalty_type || ExtraMark::PERCENTAGE I18n.t "penalty_decay_period_submission_rules.overtime_message_#{penalty_suffix}", @@ -79,4 +76,11 @@ def calculate_penalty(overtime_hours) end total_penalty end + + def penalty_for(grouping) + # How far are we into overtime? + overtime_hours = calculate_overtime_hours_from(Time.current, grouping) + # Calculate the penalty that the grouping will suffer + calculate_penalty(overtime_hours) + end end diff --git a/app/models/penalty_period_submission_rule.rb b/app/models/penalty_period_submission_rule.rb index 64b3a429b2..70c29fd50c 100644 --- a/app/models/penalty_period_submission_rule.rb +++ b/app/models/penalty_period_submission_rule.rb @@ -23,10 +23,7 @@ class PenaltyPeriodSubmissionRule < SubmissionRule inclusion: { in: [ExtraMark::PERCENTAGE, ExtraMark::POINTS, ExtraMark::PERCENTAGE_OF_MARK] } def overtime_message(grouping) - # How far are we into overtime? - overtime_hours = calculate_overtime_hours_from(Time.current, grouping) - # Calculate the penalty that the grouping will suffer - potential_penalty = calculate_penalty(overtime_hours) + potential_penalty = penalty_for(grouping) penalty_suffix = penalty_type || ExtraMark::PERCENTAGE I18n.t "penalty_period_submission_rules.overtime_message_#{penalty_suffix}", potential_penalty: potential_penalty @@ -51,6 +48,13 @@ def apply_submission_rule(submission) submission end + def penalty_for(grouping) + # How far are we into overtime? + overtime_hours = calculate_overtime_hours_from(Time.current, grouping) + # Calculate the penalty that the grouping will suffer + calculate_penalty(overtime_hours) + end + private def hours_sum diff --git a/app/models/submission_rule.rb b/app/models/submission_rule.rb index 8ec1c6522d..29a50ade6c 100644 --- a/app/models/submission_rule.rb +++ b/app/models/submission_rule.rb @@ -105,6 +105,10 @@ def reset_collection_time @can_collect_all_now = nil end + def penalty_for(_grouping) + 0 + end + private # Over time hours could be a fraction. This is mostly used for testing diff --git a/app/views/submissions/file_manager.html.erb b/app/views/submissions/file_manager.html.erb index c9cbc1f442..67a7f9e83e 100644 --- a/app/views/submissions/file_manager.html.erb +++ b/app/views/submissions/file_manager.html.erb @@ -8,7 +8,7 @@ course_id: <%= @current_course.id %>, assignment_id: <%= @assignment.id %>, grouping_id: <%= @grouping.id %>, - past_due_date: <%= @past_due_date %>, + show_late_submit_confirmation: <%= @show_late_submit_confirmation %>, readOnly: <%= !@assignment.allow_web_submits %>, enableSubdirs: <%= allowed_to? :manage_subdirectories? %>, enableUrlSubmit: <%= @grouping.assignment.url_submit %>, diff --git a/config/locales/views/submissions/en.yml b/config/locales/views/submissions/en.yml index cb48346b8f..a077d643e3 100644 --- a/config/locales/views/submissions/en.yml +++ b/config/locales/views/submissions/en.yml @@ -127,6 +127,7 @@ en: only_required_files: You may only submit the required files listed above. rename_file_to: "(optional) Rename file to" select_file: Select a file to view + upload_file_confirmation_dialog: You are about to submit a late assignment which will incur a late penalty. This action cannot be undone. Are you sure you would like to proceed? url: URL url_text: Display URL as version_control_warning: Submitting files here overrides any changes made using version control. Make sure you have worked on the most recent files before submitting. From 78c5af7b6ffc27fa5ec6553a055635c9f1edac76 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Sat, 13 Jun 2026 15:29:16 -0400 Subject: [PATCH 3/9] added javascript tests --- .../submission_file_manager.test.jsx | 163 ++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/app/javascript/Components/__tests__/submission_file_manager.test.jsx b/app/javascript/Components/__tests__/submission_file_manager.test.jsx index 53b987774b..5248fbc4de 100644 --- a/app/javascript/Components/__tests__/submission_file_manager.test.jsx +++ b/app/javascript/Components/__tests__/submission_file_manager.test.jsx @@ -209,3 +209,166 @@ describe("For the SubmissionFileManager", () => { }); }); }); + +describe("For the late submit confirm dialog", () => { + const files_sample = { + entries: [ + { + id: 136680, + url: "test.url", + filename: ' HelloWorld.java', + raw_name: "HelloWorld.java", + last_revised_date: "Saturday, May 14, 2022, 09:15:24 PM EDT", + last_modified_revision: "58ca2e15254aa63c4d41cb5db7dfc398b6bda3fb", + revision_by: "c5anthei", + submitted_date: "Saturday, May 14, 2022, 09:15:24 PM EDT", + type: "java", + key: "HelloWorld.java", + modified: 1652577324, + relativeKey: "HelloWorld.java", + }, + ], + only_required_files: false, + required_files: [], + max_file_size: 10, + number_of_missing_files: 0, + }; + + const file = new File(["content"], "test.txt", {type: "text/plain"}); + let confirmSpy; + + const mockPost = () => { + $.post = jest.fn().mockReturnValue({ + then: jest.fn().mockReturnThis(), + fail: jest.fn().mockReturnThis(), + always: jest.fn().mockReturnThis(), + }); + }; + + const renderManager = (props = {}) => { + fetch.mockResponseOnce(JSON.stringify(files_sample)); + document.body.innerHTML = `
`; + render( + + ); + }; + + const submitFileThroughModal = async () => { + const submitLink = screen.getByText(I18n.t("submit_the", {item: I18n.t("file")})); + await userEvent.click(submitLink); + await userEvent.upload(screen.getByTitle(I18n.t("modals.file_upload.file_input_label")), [ + file, + ]); + await userEvent.click(screen.getByRole("button", {name: I18n.t("save"), hidden: true})); + }; + + const submitUrlThroughModal = async () => { + const submitLink = screen.getByText( + I18n.t("submit_the", {item: I18n.t("submissions.student.link")}) + ); + await userEvent.click(submitLink); + await userEvent.type( + document.querySelector('input[name="new_url"]'), + "https://example.com/page" + ); + await userEvent.type(document.querySelector('input[name="new_url_text"]'), "example"); + await userEvent.click(screen.getByRole("button", {name: I18n.t("save"), hidden: true})); + }; + + beforeEach(() => { + fetch.resetMocks(); + confirmSpy = jest.spyOn(window, "confirm").mockReturnValue(true); + mockPost(); + }); + + afterEach(() => { + confirmSpy.mockRestore(); + }); + + describe("For the submission file upload modal", () => { + it("calls confirm when show_late_submit_confirmation is true", async () => { + renderManager({show_late_submit_confirmation: true}); + await screen.findByText("HelloWorld.java"); + await submitFileThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith( + I18n.t("submissions.student.upload_file_confirmation_dialog") + ); + }); + + it("does not call confirm when show_late_submit_confirmation is false", async () => { + renderManager({show_late_submit_confirmation: false}); + await screen.findByText("HelloWorld.java"); + await submitFileThroughModal(); + expect(confirmSpy).not.toHaveBeenCalled(); + expect($.post).toHaveBeenCalled(); + }); + + it("does not upload when user cancels the confirm dialog", async () => { + confirmSpy.mockReturnValue(false); + renderManager({show_late_submit_confirmation: true}); + await screen.findByText("HelloWorld.java"); + await submitFileThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith( + I18n.t("submissions.student.upload_file_confirmation_dialog") + ); + expect($.post).not.toHaveBeenCalled(); + }); + + it("uploads when the user confirms the dialog", async () => { + confirmSpy.mockReturnValue(true); + renderManager({show_late_submit_confirmation: true}); + await screen.findByText("HelloWorld.java"); + await submitFileThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith( + I18n.t("submissions.student.upload_file_confirmation_dialog") + ); + expect($.post).toHaveBeenCalled(); + }); + }); + + describe("For the submit URL upload modal", () => { + it("calls confirm when show_late_submit_confirmation is true", async () => { + renderManager({show_late_submit_confirmation: true, enableUrlSubmit: true}); + await screen.findByText("HelloWorld.java"); + await submitUrlThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith( + I18n.t("submissions.student.upload_file_confirmation_dialog") + ); + }); + + it("does not call confirm when show_late_submit_confirmation is false", async () => { + renderManager({show_late_submit_confirmation: false, enableUrlSubmit: true}); + await screen.findByText("HelloWorld.java"); + await submitUrlThroughModal(); + expect(confirmSpy).not.toHaveBeenCalled(); + expect($.post).toHaveBeenCalled(); + }); + + it("does not upload when user cancels the confirm dialog", async () => { + confirmSpy.mockReturnValue(false); + renderManager({show_late_submit_confirmation: true, enableUrlSubmit: true}); + await screen.findByText("HelloWorld.java"); + await submitUrlThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith( + I18n.t("submissions.student.upload_file_confirmation_dialog") + ); + expect($.post).not.toHaveBeenCalled(); + }); + + it("uploads when the user confirms the dialog", async () => { + confirmSpy.mockReturnValue(true); + renderManager({show_late_submit_confirmation: true, enableUrlSubmit: true}); + await screen.findByText("HelloWorld.java"); + await submitUrlThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith( + I18n.t("submissions.student.upload_file_confirmation_dialog") + ); + expect($.post).toHaveBeenCalled(); + }); + }); +}); From 9def73d07ae1c8041d99e4bf058dcb06d63a169a Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Sun, 14 Jun 2026 21:17:53 -0400 Subject: [PATCH 4/9] added tests for submission_rule penalty_for method --- .../penalty_decay_period_submission_rule.rb | 2 +- ...nalty_decay_period_submission_rule_spec.rb | 28 +++++++++++++++++++ .../penalty_period_submission_rule_spec.rb | 28 +++++++++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/app/models/penalty_decay_period_submission_rule.rb b/app/models/penalty_decay_period_submission_rule.rb index 14526a8eeb..1e587a09c0 100644 --- a/app/models/penalty_decay_period_submission_rule.rb +++ b/app/models/penalty_decay_period_submission_rule.rb @@ -16,7 +16,7 @@ # # rubocop:enable Layout/LineLength, Lint/RedundantCopDisableDirective class PenaltyDecayPeriodSubmissionRule < SubmissionRule - # This message will be dislayed to Students on viewing their file manager + # This message will be displayed to Students on viewing their file manager # after the due date has passed, but before the calculated collection date. validates :penalty_type, presence: true, diff --git a/spec/models/penalty_decay_period_submission_rule_spec.rb b/spec/models/penalty_decay_period_submission_rule_spec.rb index bf55e6bdfe..15e157bae6 100644 --- a/spec/models/penalty_decay_period_submission_rule_spec.rb +++ b/spec/models/penalty_decay_period_submission_rule_spec.rb @@ -19,6 +19,11 @@ context 'when the group submitted on time' do include_context 'submission_rule_on_time' + let(:period) { create(:period, deduction: 1, hours: 10, submission_rule: rule) } + let(:rule) { create(:penalty_decay_period_submission_rule, assignment: assignment) } + let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } + let(:assignment) { create(:assignment, due_date: Time.zone.today + 3.weeks) } + context 'when the student did not submit any files' do let(:grouping_creation_time) { collection_time } let(:submission) { create(:version_used_submission, grouping: grouping, is_empty: true) } @@ -36,6 +41,29 @@ end it_behaves_like 'valid overtime message', 0, -5.days + + it 'should have have no penalty' do + Timecop.freeze(assignment.due_date - 10.hours) do + expect(rule.penalty_for(grouping)).to eq 0 + end + end + end + + context 'when the group submitted late' do + let(:assignment) { create(:assignment, due_date: 2.days.ago) } + let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } + let(:rule) { create(:penalty_decay_period_submission_rule, assignment: assignment) } + + before do + create(:period, deduction: 1, hours: 10, submission_rule: rule) + rule.reload + end + + it 'should have have a penalty' do + Timecop.freeze(assignment.due_date + 10.hours) do + expect(rule.penalty_for(grouping)).to be > 0 + end + end end context 'when the group submitted during the first penalty period' do diff --git a/spec/models/penalty_period_submission_rule_spec.rb b/spec/models/penalty_period_submission_rule_spec.rb index d347cad2af..ef7c327624 100644 --- a/spec/models/penalty_period_submission_rule_spec.rb +++ b/spec/models/penalty_period_submission_rule_spec.rb @@ -19,6 +19,11 @@ context 'when the group submitted on time' do include_context 'submission_rule_on_time' + let(:period) { create(:period, deduction: 1, hours: 10, submission_rule: rule) } + let(:rule) { create(:penalty_period_submission_rule, assignment: assignment) } + let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } + let(:assignment) { create(:assignment, due_date: Time.zone.today + 3.weeks) } + context 'when the student did not submit any files' do let(:grouping_creation_time) { collection_time } let(:submission) { create(:version_used_submission, grouping: grouping, is_empty: true) } @@ -36,6 +41,29 @@ end it_behaves_like 'valid overtime message', 0, -5.days + + it 'should have no penalty' do + Timecop.freeze(assignment.due_date - 10.hours) do + expect(rule.penalty_for(grouping)).to eq 0 + end + end + end + + context 'when the group submitted late' do + let(:assignment) { create(:assignment, due_date: 2.days.ago) } + let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } + let(:rule) { create(:penalty_period_submission_rule, assignment: assignment) } + + before do + create(:period, deduction: 1, hours: 10, submission_rule: rule) + rule.reload + end + + it 'should have have a penalty' do + Timecop.freeze(assignment.due_date + 10.hours) do + expect(rule.penalty_for(grouping)).to be > 0 + end + end end context 'when the group submitted during the first penalty period' do From f39fcae8a535884745c226181d4c0cd093530f5c Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Sun, 14 Jun 2026 21:37:59 -0400 Subject: [PATCH 5/9] added tests for penalty_for method of no_late_submission_rule child of submission_rule --- spec/models/no_late_submission_rule_spec.rb | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/spec/models/no_late_submission_rule_spec.rb b/spec/models/no_late_submission_rule_spec.rb index f2291794c2..11090ebdc5 100644 --- a/spec/models/no_late_submission_rule_spec.rb +++ b/spec/models/no_late_submission_rule_spec.rb @@ -16,6 +16,11 @@ context 'when the group submitted on time' do include_context 'submission_rule_on_time' + let(:period) { create(:period, deduction: 1, hours: 10, submission_rule: rule) } + let(:rule) { create(:no_late_submission_rule, assignment: assignment) } + let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } + let(:assignment) { create(:assignment, due_date: Time.zone.today + 3.weeks) } + it 'should be able to calculate collection time' do expect(assignment.due_date).to eq(rule.calculate_collection_time) end @@ -25,10 +30,25 @@ end it_behaves_like 'valid overtime message', -5.days + + it 'should have no penalty' do + Timecop.freeze(assignment.due_date - 10.hours) do + expect(rule.penalty_for(grouping)).to eq 0 + end + end end context 'when the group submitted late' do include_context 'submission_rule_during_first' + let(:rule) { create(:no_late_submission_rule, assignment: assignment) } + let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } + let(:assignment) { create(:assignment, due_date: 2.days.ago) } + + before do + create(:period, deduction: 1, hours: 10, submission_rule: rule) + rule.reload + end + it 'does not deduct credits' do expect { apply_rule }.not_to(change { grouping.inviter.grace_period_deductions.count }) end @@ -38,5 +58,11 @@ end it_behaves_like 'valid overtime message', 5.days + + it 'should have no penalty' do + Timecop.freeze(assignment.due_date + 10.hours) do + expect(rule.penalty_for(grouping)).to eq 0 + end + end end end From 73bc447396281451f43b1634d12a258d6f2168ce Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Sun, 14 Jun 2026 22:38:09 -0400 Subject: [PATCH 6/9] removed unneccessary variable declaration after I noticed include_context statement --- .../grace_period_submission_rule_spec.rb | 21 ++++++++++ spec/models/no_late_submission_rule_spec.rb | 20 ++------- ...nalty_decay_period_submission_rule_spec.rb | 41 ++++++++----------- .../penalty_period_submission_rule_spec.rb | 39 ++++++++---------- 4 files changed, 58 insertions(+), 63 deletions(-) diff --git a/spec/models/grace_period_submission_rule_spec.rb b/spec/models/grace_period_submission_rule_spec.rb index d50de0ce85..155cba5485 100644 --- a/spec/models/grace_period_submission_rule_spec.rb +++ b/spec/models/grace_period_submission_rule_spec.rb @@ -36,6 +36,13 @@ end end end + + it 'should have no penalty' do + rule.reload + Timecop.freeze(due_date - 10.hours) do + expect(rule.penalty_for(grouping)).to eq 0 + end + end end context 'when the group submitted during the first penalty period' do @@ -129,6 +136,13 @@ end end end + + it 'should have no penalty' do + rule.reload + Timecop.freeze(due_date + 10.hours) do + expect(rule.penalty_for(grouping)).to eq 0 + end + end end context 'when the group submitted during the second penalty period' do @@ -181,5 +195,12 @@ expect(timestamp).to be_within(1.second).of(due_date + 10.hours) end end + + it 'should have no penalty' do + rule.reload + Timecop.freeze(due_date + 25.hours) do + expect(rule.penalty_for(grouping)).to eq 0 + end + end end end diff --git a/spec/models/no_late_submission_rule_spec.rb b/spec/models/no_late_submission_rule_spec.rb index 11090ebdc5..3b48ff038c 100644 --- a/spec/models/no_late_submission_rule_spec.rb +++ b/spec/models/no_late_submission_rule_spec.rb @@ -16,11 +16,6 @@ context 'when the group submitted on time' do include_context 'submission_rule_on_time' - let(:period) { create(:period, deduction: 1, hours: 10, submission_rule: rule) } - let(:rule) { create(:no_late_submission_rule, assignment: assignment) } - let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } - let(:assignment) { create(:assignment, due_date: Time.zone.today + 3.weeks) } - it 'should be able to calculate collection time' do expect(assignment.due_date).to eq(rule.calculate_collection_time) end @@ -32,7 +27,8 @@ it_behaves_like 'valid overtime message', -5.days it 'should have no penalty' do - Timecop.freeze(assignment.due_date - 10.hours) do + rule.reload + Timecop.freeze(due_date - 10.hours) do expect(rule.penalty_for(grouping)).to eq 0 end end @@ -40,15 +36,6 @@ context 'when the group submitted late' do include_context 'submission_rule_during_first' - let(:rule) { create(:no_late_submission_rule, assignment: assignment) } - let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } - let(:assignment) { create(:assignment, due_date: 2.days.ago) } - - before do - create(:period, deduction: 1, hours: 10, submission_rule: rule) - rule.reload - end - it 'does not deduct credits' do expect { apply_rule }.not_to(change { grouping.inviter.grace_period_deductions.count }) end @@ -60,7 +47,8 @@ it_behaves_like 'valid overtime message', 5.days it 'should have no penalty' do - Timecop.freeze(assignment.due_date + 10.hours) do + rule.reload + Timecop.freeze(due_date + 10.hours) do expect(rule.penalty_for(grouping)).to eq 0 end end diff --git a/spec/models/penalty_decay_period_submission_rule_spec.rb b/spec/models/penalty_decay_period_submission_rule_spec.rb index 15e157bae6..9ed996f0c4 100644 --- a/spec/models/penalty_decay_period_submission_rule_spec.rb +++ b/spec/models/penalty_decay_period_submission_rule_spec.rb @@ -19,11 +19,6 @@ context 'when the group submitted on time' do include_context 'submission_rule_on_time' - let(:period) { create(:period, deduction: 1, hours: 10, submission_rule: rule) } - let(:rule) { create(:penalty_decay_period_submission_rule, assignment: assignment) } - let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } - let(:assignment) { create(:assignment, due_date: Time.zone.today + 3.weeks) } - context 'when the student did not submit any files' do let(:grouping_creation_time) { collection_time } let(:submission) { create(:version_used_submission, grouping: grouping, is_empty: true) } @@ -42,26 +37,10 @@ it_behaves_like 'valid overtime message', 0, -5.days - it 'should have have no penalty' do - Timecop.freeze(assignment.due_date - 10.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end - end - - context 'when the group submitted late' do - let(:assignment) { create(:assignment, due_date: 2.days.ago) } - let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } - let(:rule) { create(:penalty_decay_period_submission_rule, assignment: assignment) } - - before do - create(:period, deduction: 1, hours: 10, submission_rule: rule) + it 'should have no penalty' do rule.reload - end - - it 'should have have a penalty' do - Timecop.freeze(assignment.due_date + 10.hours) do - expect(rule.penalty_for(grouping)).to be > 0 + Timecop.freeze(due_date - 10.hours) do + expect(rule.penalty_for(grouping)).to eq 0 end end end @@ -78,6 +57,13 @@ end it_behaves_like 'valid overtime message', 1.0, 10.hours + + it 'should have a penalty' do + rule.reload + Timecop.freeze(due_date + 10.hours) do + expect(rule.penalty_for(grouping)).to eq(1.0) + end + end end context 'when the group submitted during the second penalty period' do @@ -92,6 +78,13 @@ end it_behaves_like 'valid overtime message', 2.0, 25.hours + + it 'should have a penalty' do + rule.reload + Timecop.freeze(due_date + 25.hours) do + expect(rule.penalty_for(grouping)).to eq(2.0) + end + end end context 'when penalty_type is percentage_of_mark' do diff --git a/spec/models/penalty_period_submission_rule_spec.rb b/spec/models/penalty_period_submission_rule_spec.rb index ef7c327624..143db082f3 100644 --- a/spec/models/penalty_period_submission_rule_spec.rb +++ b/spec/models/penalty_period_submission_rule_spec.rb @@ -19,11 +19,6 @@ context 'when the group submitted on time' do include_context 'submission_rule_on_time' - let(:period) { create(:period, deduction: 1, hours: 10, submission_rule: rule) } - let(:rule) { create(:penalty_period_submission_rule, assignment: assignment) } - let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } - let(:assignment) { create(:assignment, due_date: Time.zone.today + 3.weeks) } - context 'when the student did not submit any files' do let(:grouping_creation_time) { collection_time } let(:submission) { create(:version_used_submission, grouping: grouping, is_empty: true) } @@ -43,25 +38,9 @@ it_behaves_like 'valid overtime message', 0, -5.days it 'should have no penalty' do - Timecop.freeze(assignment.due_date - 10.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end - end - - context 'when the group submitted late' do - let(:assignment) { create(:assignment, due_date: 2.days.ago) } - let(:grouping) { create(:grouping_with_inviter, assignment: assignment) } - let(:rule) { create(:penalty_period_submission_rule, assignment: assignment) } - - before do - create(:period, deduction: 1, hours: 10, submission_rule: rule) rule.reload - end - - it 'should have have a penalty' do - Timecop.freeze(assignment.due_date + 10.hours) do - expect(rule.penalty_for(grouping)).to be > 0 + Timecop.freeze(due_date - 10.hours) do + expect(rule.penalty_for(grouping)).to eq 0 end end end @@ -78,6 +57,13 @@ end it_behaves_like 'valid overtime message', 1.0, 10.hours + + it 'should have a penalty' do + rule.reload + Timecop.freeze(due_date + 10.hours) do + expect(rule.penalty_for(grouping)).to eq(1.0) + end + end end context 'when the group submitted during the second penalty period' do @@ -92,6 +78,13 @@ end it_behaves_like 'valid overtime message', 2.0, 25.hours + + it 'should have a penalty' do + rule.reload + Timecop.freeze(due_date + 25.hours) do + expect(rule.penalty_for(grouping)).to eq(2.0) + end + end end context 'when penalty_type is percentage_of_mark' do From 5df732ef540c61fa8ae3dd6e70c815fca12644a3 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Mon, 15 Jun 2026 10:17:48 -0400 Subject: [PATCH 7/9] Added changes to Changelog.md and fixed a problem with a test --- Changelog.md | 1 + .../Components/__tests__/submission_file_manager.test.jsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 70bb332e83..3df37f03a7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ ### 🚨 Breaking changes ### ✨ New features and improvements +- Added a confirm dialog when a student tries to submit work after the deadline has passed (#8003) - Added a confirm dialog to the Upload Scans form that appears when no template divisions are assigned to the selected exam template (#7993) - Migrated `MarkingSchemesTable` component to React Table V8 (#7985) - Removed Graders Subcomponent and added a Graders column in the Assignment Grades tab (#7967) diff --git a/app/javascript/Components/__tests__/submission_file_manager.test.jsx b/app/javascript/Components/__tests__/submission_file_manager.test.jsx index 5248fbc4de..a842001f3e 100644 --- a/app/javascript/Components/__tests__/submission_file_manager.test.jsx +++ b/app/javascript/Components/__tests__/submission_file_manager.test.jsx @@ -205,7 +205,7 @@ describe("For the SubmissionFileManager", () => { ]); await userEvent.click(screen.getByRole("button", {name: I18n.t("save"), hidden: true})); - expect(screen.findByRole("progressbar", {hidden: true})).rejects.toThrow(); + await expect(screen.findByRole("progressbar", {hidden: true})).rejects.toThrow(); }); }); }); From cbcabffaaa4cee4ff27ca95dec527ab6d62b6dff Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Fri, 19 Jun 2026 20:04:36 -0400 Subject: [PATCH 8/9] renamed the function to confirmWhenLate --- app/javascript/Components/submission_file_manager.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/Components/submission_file_manager.jsx b/app/javascript/Components/submission_file_manager.jsx index 89a500fdf9..a2229629ee 100644 --- a/app/javascript/Components/submission_file_manager.jsx +++ b/app/javascript/Components/submission_file_manager.jsx @@ -77,7 +77,7 @@ class SubmissionFileManager extends React.Component { } } - showConfirmWhenLate = () => { + confirmWhenLate = () => { if (this.props.show_late_submit_confirmation) { return confirm(I18n.t("submissions.student.upload_file_confirmation_dialog")); } @@ -85,7 +85,7 @@ class SubmissionFileManager extends React.Component { }; handleCreateUrl = (url, url_text) => { - if (!this.showConfirmWhenLate()) { + if (!this.confirmWhenLate()) { return; } this.setState({showURLModal: false}); @@ -109,7 +109,7 @@ class SubmissionFileManager extends React.Component { }; handleCreateFiles = (files, path, unzip, renameTo = "") => { - if (!this.showConfirmWhenLate()) { + if (!this.confirmWhenLate()) { return; } From f23f7be30d6e41b1d1cfd053c304f15aceb2cf19 Mon Sep 17 00:00:00 2001 From: Daniel Rafailov Date: Fri, 19 Jun 2026 22:37:57 -0400 Subject: [PATCH 9/9] Fixed problems as per comments made on PR --- app/controllers/submissions_controller.rb | 9 +- .../submission_file_manager.test.jsx | 145 +++++++++++------- .../Components/submission_file_manager.jsx | 21 ++- .../penalty_decay_period_submission_rule.rb | 12 +- app/models/penalty_period_submission_rule.rb | 14 +- app/models/submission_rule.rb | 4 - app/views/submissions/file_manager.html.erb | 1 + config/locales/models/submission_rules/en.yml | 3 + config/locales/views/submissions/en.yml | 1 - .../grace_period_submission_rule_spec.rb | 21 --- spec/models/no_late_submission_rule_spec.rb | 14 -- ...nalty_decay_period_submission_rule_spec.rb | 21 --- .../penalty_period_submission_rule_spec.rb | 21 --- 13 files changed, 130 insertions(+), 157 deletions(-) diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index fb82d0163e..17de5b3e81 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -126,11 +126,6 @@ def file_manager return end - @past_due_date = @assignment.grouping_past_due_date?(@grouping) - @late_penalty = @assignment.submission_rule.penalty_for(@grouping) - @past_collection_date = @grouping.past_collection_date? - @show_late_submit_confirmation = @past_due_date && @late_penalty.positive? && !@past_collection_date - authorize! @grouping, to: :view_file_manager? @path = params[:path] || '/' @@ -140,6 +135,10 @@ def file_manager set_filebrowser_vars(@grouping) flash_file_manager_messages + past_due_date = @assignment.grouping_past_due_date?(@grouping) + past_collection_date = @grouping.past_collection_date? + @show_late_submit_confirmation = past_due_date && !past_collection_date + render 'file_manager', layout: 'assignment_content', locals: {} end diff --git a/app/javascript/Components/__tests__/submission_file_manager.test.jsx b/app/javascript/Components/__tests__/submission_file_manager.test.jsx index a842001f3e..35af318506 100644 --- a/app/javascript/Components/__tests__/submission_file_manager.test.jsx +++ b/app/javascript/Components/__tests__/submission_file_manager.test.jsx @@ -211,6 +211,18 @@ describe("For the SubmissionFileManager", () => { }); describe("For the late submit confirm dialog", () => { + const LATE_SUBMIT_MESSAGES = { + GracePeriodSubmissionRule: I18n.t( + "activerecord.attributes.grace_period_submission_rule.upload_late_confirmation_dialog" + ), + PenaltyDecayPeriodSubmissionRule: I18n.t( + "activerecord.attributes.penalty_decay_period_submission_rule.upload_late_confirmation_dialog" + ), + PenaltyPeriodSubmissionRule: I18n.t( + "activerecord.attributes.penalty_period_submission_rule.upload_late_confirmation_dialog" + ), + }; + const files_sample = { entries: [ { @@ -291,13 +303,44 @@ describe("For the late submit confirm dialog", () => { }); describe("For the submission file upload modal", () => { - it("calls confirm when show_late_submit_confirmation is true", async () => { - renderManager({show_late_submit_confirmation: true}); - await screen.findByText("HelloWorld.java"); - await submitFileThroughModal(); - expect(confirmSpy).toHaveBeenCalledWith( - I18n.t("submissions.student.upload_file_confirmation_dialog") - ); + describe.each([ + ["GracePeriodSubmissionRule"], + ["PenaltyDecayPeriodSubmissionRule"], + ["PenaltyPeriodSubmissionRule"], + ])("when submission_rule is %s", submissionRule => { + it("calls confirm with the correct message", async () => { + renderManager({ + show_late_submit_confirmation: true, + submission_rule: submissionRule, + }); + await screen.findByText("HelloWorld.java"); + await submitFileThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith(LATE_SUBMIT_MESSAGES[submissionRule]); + }); + + it("does not upload when user cancels the confirm dialog", async () => { + confirmSpy.mockReturnValue(false); + renderManager({ + show_late_submit_confirmation: true, + submission_rule: submissionRule, + }); + await screen.findByText("HelloWorld.java"); + await submitFileThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith(LATE_SUBMIT_MESSAGES[submissionRule]); + expect($.post).not.toHaveBeenCalled(); + }); + + it("uploads when the user confirms the dialog", async () => { + confirmSpy.mockReturnValue(true); + renderManager({ + show_late_submit_confirmation: true, + submission_rule: submissionRule, + }); + await screen.findByText("HelloWorld.java"); + await submitFileThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith(LATE_SUBMIT_MESSAGES[submissionRule]); + expect($.post).toHaveBeenCalled(); + }); }); it("does not call confirm when show_late_submit_confirmation is false", async () => { @@ -308,37 +351,57 @@ describe("For the late submit confirm dialog", () => { expect($.post).toHaveBeenCalled(); }); - it("does not upload when user cancels the confirm dialog", async () => { - confirmSpy.mockReturnValue(false); + it("does not call confirm when show_late_submit_confirmation is true but submission_rule is missing", async () => { renderManager({show_late_submit_confirmation: true}); await screen.findByText("HelloWorld.java"); await submitFileThroughModal(); - expect(confirmSpy).toHaveBeenCalledWith( - I18n.t("submissions.student.upload_file_confirmation_dialog") - ); - expect($.post).not.toHaveBeenCalled(); - }); - - it("uploads when the user confirms the dialog", async () => { - confirmSpy.mockReturnValue(true); - renderManager({show_late_submit_confirmation: true}); - await screen.findByText("HelloWorld.java"); - await submitFileThroughModal(); - expect(confirmSpy).toHaveBeenCalledWith( - I18n.t("submissions.student.upload_file_confirmation_dialog") - ); + expect(confirmSpy).not.toHaveBeenCalled(); expect($.post).toHaveBeenCalled(); }); }); describe("For the submit URL upload modal", () => { - it("calls confirm when show_late_submit_confirmation is true", async () => { - renderManager({show_late_submit_confirmation: true, enableUrlSubmit: true}); - await screen.findByText("HelloWorld.java"); - await submitUrlThroughModal(); - expect(confirmSpy).toHaveBeenCalledWith( - I18n.t("submissions.student.upload_file_confirmation_dialog") - ); + describe.each([ + ["GracePeriodSubmissionRule"], + ["PenaltyDecayPeriodSubmissionRule"], + ["PenaltyPeriodSubmissionRule"], + ])("when submission_rule is %s", submissionRule => { + it("calls confirm with the correct message", async () => { + renderManager({ + show_late_submit_confirmation: true, + submission_rule: submissionRule, + enableUrlSubmit: true, + }); + await screen.findByText("HelloWorld.java"); + await submitUrlThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith(LATE_SUBMIT_MESSAGES[submissionRule]); + }); + + it("does not upload when user cancels the confirm dialog", async () => { + confirmSpy.mockReturnValue(false); + renderManager({ + show_late_submit_confirmation: true, + submission_rule: submissionRule, + enableUrlSubmit: true, + }); + await screen.findByText("HelloWorld.java"); + await submitUrlThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith(LATE_SUBMIT_MESSAGES[submissionRule]); + expect($.post).not.toHaveBeenCalled(); + }); + + it("uploads when the user confirms the dialog", async () => { + confirmSpy.mockReturnValue(true); + renderManager({ + show_late_submit_confirmation: true, + submission_rule: submissionRule, + enableUrlSubmit: true, + }); + await screen.findByText("HelloWorld.java"); + await submitUrlThroughModal(); + expect(confirmSpy).toHaveBeenCalledWith(LATE_SUBMIT_MESSAGES[submissionRule]); + expect($.post).toHaveBeenCalled(); + }); }); it("does not call confirm when show_late_submit_confirmation is false", async () => { @@ -348,27 +411,5 @@ describe("For the late submit confirm dialog", () => { expect(confirmSpy).not.toHaveBeenCalled(); expect($.post).toHaveBeenCalled(); }); - - it("does not upload when user cancels the confirm dialog", async () => { - confirmSpy.mockReturnValue(false); - renderManager({show_late_submit_confirmation: true, enableUrlSubmit: true}); - await screen.findByText("HelloWorld.java"); - await submitUrlThroughModal(); - expect(confirmSpy).toHaveBeenCalledWith( - I18n.t("submissions.student.upload_file_confirmation_dialog") - ); - expect($.post).not.toHaveBeenCalled(); - }); - - it("uploads when the user confirms the dialog", async () => { - confirmSpy.mockReturnValue(true); - renderManager({show_late_submit_confirmation: true, enableUrlSubmit: true}); - await screen.findByText("HelloWorld.java"); - await submitUrlThroughModal(); - expect(confirmSpy).toHaveBeenCalledWith( - I18n.t("submissions.student.upload_file_confirmation_dialog") - ); - expect($.post).toHaveBeenCalled(); - }); }); }); diff --git a/app/javascript/Components/submission_file_manager.jsx b/app/javascript/Components/submission_file_manager.jsx index a2229629ee..1619395d33 100644 --- a/app/javascript/Components/submission_file_manager.jsx +++ b/app/javascript/Components/submission_file_manager.jsx @@ -79,7 +79,26 @@ class SubmissionFileManager extends React.Component { confirmWhenLate = () => { if (this.props.show_late_submit_confirmation) { - return confirm(I18n.t("submissions.student.upload_file_confirmation_dialog")); + switch (this.props.submission_rule) { + case "GracePeriodSubmissionRule": + return confirm( + I18n.t( + "activerecord.attributes.grace_period_submission_rule.upload_late_confirmation_dialog" + ) + ); + case "PenaltyDecayPeriodSubmissionRule": + return confirm( + I18n.t( + "activerecord.attributes.penalty_decay_period_submission_rule.upload_late_confirmation_dialog" + ) + ); + case "PenaltyPeriodSubmissionRule": + return confirm( + I18n.t( + "activerecord.attributes.penalty_period_submission_rule.upload_late_confirmation_dialog" + ) + ); + } } return true; }; diff --git a/app/models/penalty_decay_period_submission_rule.rb b/app/models/penalty_decay_period_submission_rule.rb index 1e587a09c0..92e4f146ed 100644 --- a/app/models/penalty_decay_period_submission_rule.rb +++ b/app/models/penalty_decay_period_submission_rule.rb @@ -23,7 +23,10 @@ class PenaltyDecayPeriodSubmissionRule < SubmissionRule inclusion: { in: [ExtraMark::PERCENTAGE, ExtraMark::POINTS, ExtraMark::PERCENTAGE_OF_MARK] } def overtime_message(grouping) - potential_penalty = penalty_for(grouping) + # How far are we into overtime? + overtime_hours = calculate_overtime_hours_from(Time.current, grouping) + # Calculate the penalty that the grouping will suffer + potential_penalty = calculate_penalty(overtime_hours) penalty_suffix = penalty_type || ExtraMark::PERCENTAGE I18n.t "penalty_decay_period_submission_rules.overtime_message_#{penalty_suffix}", @@ -76,11 +79,4 @@ def calculate_penalty(overtime_hours) end total_penalty end - - def penalty_for(grouping) - # How far are we into overtime? - overtime_hours = calculate_overtime_hours_from(Time.current, grouping) - # Calculate the penalty that the grouping will suffer - calculate_penalty(overtime_hours) - end end diff --git a/app/models/penalty_period_submission_rule.rb b/app/models/penalty_period_submission_rule.rb index 70c29fd50c..2cce57bdc1 100644 --- a/app/models/penalty_period_submission_rule.rb +++ b/app/models/penalty_period_submission_rule.rb @@ -16,14 +16,17 @@ # # rubocop:enable Layout/LineLength, Lint/RedundantCopDisableDirective class PenaltyPeriodSubmissionRule < SubmissionRule - # This message will be dislayed to Students on viewing their file manager + # This message will be displayed to Students on viewing their file manager # after the due date has passed, but before the calculated collection date. validates :penalty_type, presence: true, inclusion: { in: [ExtraMark::PERCENTAGE, ExtraMark::POINTS, ExtraMark::PERCENTAGE_OF_MARK] } def overtime_message(grouping) - potential_penalty = penalty_for(grouping) + # How far are we into overtime? + overtime_hours = calculate_overtime_hours_from(Time.current, grouping) + # Calculate the penalty that the grouping will suffer + potential_penalty = calculate_penalty(overtime_hours) penalty_suffix = penalty_type || ExtraMark::PERCENTAGE I18n.t "penalty_period_submission_rules.overtime_message_#{penalty_suffix}", potential_penalty: potential_penalty @@ -48,13 +51,6 @@ def apply_submission_rule(submission) submission end - def penalty_for(grouping) - # How far are we into overtime? - overtime_hours = calculate_overtime_hours_from(Time.current, grouping) - # Calculate the penalty that the grouping will suffer - calculate_penalty(overtime_hours) - end - private def hours_sum diff --git a/app/models/submission_rule.rb b/app/models/submission_rule.rb index 29a50ade6c..8ec1c6522d 100644 --- a/app/models/submission_rule.rb +++ b/app/models/submission_rule.rb @@ -105,10 +105,6 @@ def reset_collection_time @can_collect_all_now = nil end - def penalty_for(_grouping) - 0 - end - private # Over time hours could be a fraction. This is mostly used for testing diff --git a/app/views/submissions/file_manager.html.erb b/app/views/submissions/file_manager.html.erb index 67a7f9e83e..f7cef8ce3a 100644 --- a/app/views/submissions/file_manager.html.erb +++ b/app/views/submissions/file_manager.html.erb @@ -9,6 +9,7 @@ assignment_id: <%= @assignment.id %>, grouping_id: <%= @grouping.id %>, show_late_submit_confirmation: <%= @show_late_submit_confirmation %>, + submission_rule: "<%= @assignment.submission_rule.type %>", readOnly: <%= !@assignment.allow_web_submits %>, enableSubdirs: <%= allowed_to? :manage_subdirectories? %>, enableUrlSubmit: <%= @grouping.assignment.url_submit %>, diff --git a/config/locales/models/submission_rules/en.yml b/config/locales/models/submission_rules/en.yml index e95a72abd0..83dd7330d5 100644 --- a/config/locales/models/submission_rules/en.yml +++ b/config/locales/models/submission_rules/en.yml @@ -7,6 +7,7 @@ en: commit_after_collection_message: The due date for this assignment, plus the maximum grace period, has passed. Your changes have been recorded, but will not be included in the grading. description: You may submit up to a set time past the due date, provided you have enough remaining grace credits to do so. form_description: Automatically deduct grace credits + upload_late_confirmation_dialog: The due date for this assignment has passed. If you submit files now, you will use grace credits, or your submission may not be graded, depending on when you submit and how many grace credits you have remaining. This action cannot be undone. Are you sure you would like to proceed? no_late_submission_rule: after_collection_message: The due date for this assignment has passed. commit_after_collection_message: The due date for this assignment has passed. Your changes have been recorded, but will not be included in the grading. @@ -17,11 +18,13 @@ en: commit_after_collection_message: The due date for this assignment, plus the maximum late penalty period, has passed. Your changes have been recorded, but will not be included in the grading. description: You are able to submit up to a set time past the due date, but with the appropriate percentage deducted from your final grade. form_description: Use penalty decay formula + upload_late_confirmation_dialog: The due date for this assignment has passed. If you submit files now, you will incur a late penalty, or your submission may not be graded, depending on when you submit. This action cannot be undone. Are you sure you would like to proceed? penalty_period_submission_rule: after_collection_message: The maximum late penalty period has passed for this assignment. commit_after_collection_message: The due date for this assignment, plus the maximum late penalty period, has passed. Your changes have been recorded, but will not be included in the grading. description: You are able to submit up to a set time past the due date, but with the appropriate percentage deducted from your final grade. form_description: Set manual penalty periods + upload_late_confirmation_dialog: The due date for this assignment has passed. If you submit files now, you will incur a late penalty, or your submission may not be graded, depending on when you submit. This action cannot be undone. Are you sure you would like to proceed? models: submission_rule: one: Late Submission Policy diff --git a/config/locales/views/submissions/en.yml b/config/locales/views/submissions/en.yml index a077d643e3..cb48346b8f 100644 --- a/config/locales/views/submissions/en.yml +++ b/config/locales/views/submissions/en.yml @@ -127,7 +127,6 @@ en: only_required_files: You may only submit the required files listed above. rename_file_to: "(optional) Rename file to" select_file: Select a file to view - upload_file_confirmation_dialog: You are about to submit a late assignment which will incur a late penalty. This action cannot be undone. Are you sure you would like to proceed? url: URL url_text: Display URL as version_control_warning: Submitting files here overrides any changes made using version control. Make sure you have worked on the most recent files before submitting. diff --git a/spec/models/grace_period_submission_rule_spec.rb b/spec/models/grace_period_submission_rule_spec.rb index 155cba5485..d50de0ce85 100644 --- a/spec/models/grace_period_submission_rule_spec.rb +++ b/spec/models/grace_period_submission_rule_spec.rb @@ -36,13 +36,6 @@ end end end - - it 'should have no penalty' do - rule.reload - Timecop.freeze(due_date - 10.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end end context 'when the group submitted during the first penalty period' do @@ -136,13 +129,6 @@ end end end - - it 'should have no penalty' do - rule.reload - Timecop.freeze(due_date + 10.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end end context 'when the group submitted during the second penalty period' do @@ -195,12 +181,5 @@ expect(timestamp).to be_within(1.second).of(due_date + 10.hours) end end - - it 'should have no penalty' do - rule.reload - Timecop.freeze(due_date + 25.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end end end diff --git a/spec/models/no_late_submission_rule_spec.rb b/spec/models/no_late_submission_rule_spec.rb index 3b48ff038c..f2291794c2 100644 --- a/spec/models/no_late_submission_rule_spec.rb +++ b/spec/models/no_late_submission_rule_spec.rb @@ -25,13 +25,6 @@ end it_behaves_like 'valid overtime message', -5.days - - it 'should have no penalty' do - rule.reload - Timecop.freeze(due_date - 10.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end end context 'when the group submitted late' do @@ -45,12 +38,5 @@ end it_behaves_like 'valid overtime message', 5.days - - it 'should have no penalty' do - rule.reload - Timecop.freeze(due_date + 10.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end end end diff --git a/spec/models/penalty_decay_period_submission_rule_spec.rb b/spec/models/penalty_decay_period_submission_rule_spec.rb index 9ed996f0c4..bf55e6bdfe 100644 --- a/spec/models/penalty_decay_period_submission_rule_spec.rb +++ b/spec/models/penalty_decay_period_submission_rule_spec.rb @@ -36,13 +36,6 @@ end it_behaves_like 'valid overtime message', 0, -5.days - - it 'should have no penalty' do - rule.reload - Timecop.freeze(due_date - 10.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end end context 'when the group submitted during the first penalty period' do @@ -57,13 +50,6 @@ end it_behaves_like 'valid overtime message', 1.0, 10.hours - - it 'should have a penalty' do - rule.reload - Timecop.freeze(due_date + 10.hours) do - expect(rule.penalty_for(grouping)).to eq(1.0) - end - end end context 'when the group submitted during the second penalty period' do @@ -78,13 +64,6 @@ end it_behaves_like 'valid overtime message', 2.0, 25.hours - - it 'should have a penalty' do - rule.reload - Timecop.freeze(due_date + 25.hours) do - expect(rule.penalty_for(grouping)).to eq(2.0) - end - end end context 'when penalty_type is percentage_of_mark' do diff --git a/spec/models/penalty_period_submission_rule_spec.rb b/spec/models/penalty_period_submission_rule_spec.rb index 143db082f3..d347cad2af 100644 --- a/spec/models/penalty_period_submission_rule_spec.rb +++ b/spec/models/penalty_period_submission_rule_spec.rb @@ -36,13 +36,6 @@ end it_behaves_like 'valid overtime message', 0, -5.days - - it 'should have no penalty' do - rule.reload - Timecop.freeze(due_date - 10.hours) do - expect(rule.penalty_for(grouping)).to eq 0 - end - end end context 'when the group submitted during the first penalty period' do @@ -57,13 +50,6 @@ end it_behaves_like 'valid overtime message', 1.0, 10.hours - - it 'should have a penalty' do - rule.reload - Timecop.freeze(due_date + 10.hours) do - expect(rule.penalty_for(grouping)).to eq(1.0) - end - end end context 'when the group submitted during the second penalty period' do @@ -78,13 +64,6 @@ end it_behaves_like 'valid overtime message', 2.0, 25.hours - - it 'should have a penalty' do - rule.reload - Timecop.freeze(due_date + 25.hours) do - expect(rule.penalty_for(grouping)).to eq(2.0) - end - end end context 'when penalty_type is percentage_of_mark' do