From 4c9fd2f7cb21758b1f7fc3612ef81abc93628451 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Thu, 10 Aug 2023 07:51:45 -0500 Subject: [PATCH 1/4] feat: add compatibility with individual date extensions for submissions --- openassessment/xblock/openassessmentblock.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/openassessment/xblock/openassessmentblock.py b/openassessment/xblock/openassessmentblock.py index 30727c8673..efbd2a78d1 100644 --- a/openassessment/xblock/openassessmentblock.py +++ b/openassessment/xblock/openassessmentblock.py @@ -1106,7 +1106,12 @@ def is_closed(self, step=None, course_staff=None): datetime.datetime(2015, 3, 27, 22, 7, 38, 788861) """ - submission_range = (self.submission_start, self.submission_due) + if self.due is not None \ + and self.submission_due is not None \ + and parse_date_value(self.due, self._) > parse_date_value(self.submission_due, self._): + submission_range = (self.submission_start, self.due) + else: + submission_range = (self.submission_start, self.submission_due) assessment_ranges = [ (asmnt.get('start'), asmnt.get('due')) for asmnt in self.valid_assessments From 98448785a9edf120ce4931a75af893d48224c912 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Thu, 10 Aug 2023 09:05:24 -0500 Subject: [PATCH 2/4] feat: apply due date extensions for all steps --- openassessment/xblock/openassessmentblock.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/openassessment/xblock/openassessmentblock.py b/openassessment/xblock/openassessmentblock.py index efbd2a78d1..e16a92c3f5 100644 --- a/openassessment/xblock/openassessmentblock.py +++ b/openassessment/xblock/openassessmentblock.py @@ -1110,12 +1110,19 @@ def is_closed(self, step=None, course_staff=None): and self.submission_due is not None \ and parse_date_value(self.due, self._) > parse_date_value(self.submission_due, self._): submission_range = (self.submission_start, self.due) + assessment_ranges = [] + for asmnt in self.valid_assessments: + asmnt_due = asmnt.get('due') + if asmnt_due is None or parse_date_value(self.due, self._) < parse_date_value(asmnt_due, self._): + assessment_ranges.append((asmnt.get('start'), asmnt_due)) + else: + assessment_ranges.append((asmnt.get('start'), self.due)) else: submission_range = (self.submission_start, self.submission_due) - assessment_ranges = [ - (asmnt.get('start'), asmnt.get('due')) - for asmnt in self.valid_assessments - ] + assessment_ranges = [ + (asmnt.get('start'), asmnt.get('due')) + for asmnt in self.valid_assessments + ] # Resolve unspecified dates and date strings to datetimes start, due, date_ranges = resolve_dates( From f4af9fd0449d357d031707e009537d610fdedc51 Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Fri, 11 Aug 2023 13:27:46 -0500 Subject: [PATCH 3/4] fix: add flag for due date extension --- openassessment/xblock/config_mixin.py | 18 ++++- openassessment/xblock/openassessmentblock.py | 80 +++++++++++++++----- settings/base.py | 5 +- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/openassessment/xblock/config_mixin.py b/openassessment/xblock/config_mixin.py index 1d0c820237..6cf726f681 100644 --- a/openassessment/xblock/config_mixin.py +++ b/openassessment/xblock/config_mixin.py @@ -14,13 +14,15 @@ USER_STATE_UPLOAD_DATA = 'user_state_upload_data' RUBRIC_REUSE = 'rubric_reuse' ENHANCED_STAFF_GRADER = 'enhanced_staff_grader' +DUE_DATE_EXTENSION = 'due_date_extension' FEATURE_TOGGLES_BY_FLAG_NAME = { ALL_FILES_URLS: 'ENABLE_ORA_ALL_FILE_URLS', TEAM_SUBMISSIONS: 'ENABLE_ORA_TEAM_SUBMISSIONS', USER_STATE_UPLOAD_DATA: 'ENABLE_ORA_USER_STATE_UPLOAD_DATA', RUBRIC_REUSE: 'ENABLE_ORA_RUBRIC_REUSE', - ENHANCED_STAFF_GRADER: 'ENABLE_ENHANCED_STAFF_GRADER' + ENHANCED_STAFF_GRADER: 'ENABLE_ENHANCED_STAFF_GRADER', + DUE_DATE_EXTENSION: 'ENABLE_ORA_DUE_DATE_EXTENSION', } @@ -153,3 +155,17 @@ def is_enhanced_staff_grader_enabled(self): # .. toggle_creation_date: 2021-08-29 # .. toggle_tickets: https://openedx.atlassian.net/browse/AU-50 return self.is_feature_enabled(ENHANCED_STAFF_GRADER) + + @cached_property + def is_due_date_extension_enabled(self): + """ + Return a boolean indicating the due date extension feature is enabled or not. + """ + # .. toggle_name: FEATURES['ENABLE_ORA_DUE_DATE_EXTENSION'] + # .. toggle_implementation: WaffleFlag + # .. toggle_default: False + # .. toggle_description: Set to True to enable the due date extension feature + # .. toggle_use_cases: circuit_breaker + # .. toggle_creation_date: 2023-08-11 + # .. toggle_tickets: + return self.is_feature_enabled(DUE_DATE_EXTENSION) diff --git a/openassessment/xblock/openassessmentblock.py b/openassessment/xblock/openassessmentblock.py index e16a92c3f5..46944163c4 100644 --- a/openassessment/xblock/openassessmentblock.py +++ b/openassessment/xblock/openassessmentblock.py @@ -1065,6 +1065,68 @@ def render_error(self, error_msg): context = {'error_msg': error_msg} template = get_template('openassessmentblock/oa_error.html') return Response(template.render(context), content_type='application/html', charset='UTF-8') + + def can_use_date_extensions(self): + if not self.is_due_date_extension_enabled: + return False + + if not self.due: + return False + + if not self.submission_due: + return False + + parsed_due = parse_date_value(self.due, self._) + parsed_submission_due_= parse_date_value(self.submission_due, self._) + + has_due_date_extension = parsed_due > parsed_submission_due_ + + if not has_due_date_extension: + return False + + return True + + def date_ranges(self): + """ + Generate a list of dates ranges for the submission and assessment steps. + + Returns: + submission_range (tuple): A tuple of the form (start, end) where + start and end are datetime objects. + assesment_ranges (list): A list of tuples of the form (start, end) + where start and end are datetime objects. + + """ + if self.can_use_date_extensions(): + + parsed_due = parse_date_value(self.due, self._) + + submission_range = (self.submission_start, self.due) + assessment_ranges = [] + + for assesment in self.valid_assessments: + assesment_due = assesment.get('due') + + if assesment_due is None: + assessment_ranges.append((assesment.get('start'), assesment_due)) + continue + + parsed_assesment_due = parse_date_value(assesment_due, self._) + + is_extended = parsed_due < parsed_assesment_due + + if is_extended: + assessment_ranges.append((assesment.get('start'), assesment_due)) + else: + assessment_ranges.append((assesment.get('start'), self.due)) + else: + submission_range = (self.submission_start, self.submission_due) + assessment_ranges = [ + (asmnt.get('start'), asmnt.get('due')) + for asmnt in self.valid_assessments + ] + return submission_range, assessment_ranges + def is_closed(self, step=None, course_staff=None): """ @@ -1106,23 +1168,7 @@ def is_closed(self, step=None, course_staff=None): datetime.datetime(2015, 3, 27, 22, 7, 38, 788861) """ - if self.due is not None \ - and self.submission_due is not None \ - and parse_date_value(self.due, self._) > parse_date_value(self.submission_due, self._): - submission_range = (self.submission_start, self.due) - assessment_ranges = [] - for asmnt in self.valid_assessments: - asmnt_due = asmnt.get('due') - if asmnt_due is None or parse_date_value(self.due, self._) < parse_date_value(asmnt_due, self._): - assessment_ranges.append((asmnt.get('start'), asmnt_due)) - else: - assessment_ranges.append((asmnt.get('start'), self.due)) - else: - submission_range = (self.submission_start, self.submission_due) - assessment_ranges = [ - (asmnt.get('start'), asmnt.get('due')) - for asmnt in self.valid_assessments - ] + submission_range, assessment_ranges = self.date_ranges() # Resolve unspecified dates and date strings to datetimes start, due, date_ranges = resolve_dates( diff --git a/settings/base.py b/settings/base.py index d5cdc3759e..82590e3be5 100644 --- a/settings/base.py +++ b/settings/base.py @@ -167,7 +167,10 @@ # Set to True to enable copying/reusing rubric data # See: https://openedx.atlassian.net/browse/EDUCATOR-5751 - 'ENABLE_ORA_RUBRIC_REUSE': False + 'ENABLE_ORA_RUBRIC_REUSE': False, + + # Set to True to enable individual due date extension for ORA + 'ENABLE_ORA_DUE_DATE_EXTENSION': False, } # disable indexing on history_date From bed275c25aac26d15c47b368054625630b4220cd Mon Sep 17 00:00:00 2001 From: Cristhian Garcia Date: Fri, 11 Aug 2023 14:57:17 -0500 Subject: [PATCH 4/4] test: correct import error on test --- openassessment/xblock/config_mixin.py | 2 +- openassessment/xblock/openassessmentblock.py | 35 +++++++++++-------- openassessment/xblock/test/test_grade.py | 5 +++ .../xblock/test/test_openassessment.py | 3 ++ .../test/test_save_files_descriptions.py | 4 +++ .../xblock/test/test_save_response.py | 4 +++ openassessment/xblock/test/test_self.py | 2 ++ openassessment/xblock/test/test_staff_area.py | 8 +++++ .../xblock/test/test_student_training.py | 4 +++ openassessment/xblock/test/test_submission.py | 10 ++++++ 10 files changed, 61 insertions(+), 16 deletions(-) diff --git a/openassessment/xblock/config_mixin.py b/openassessment/xblock/config_mixin.py index 6cf726f681..2b745dc1d8 100644 --- a/openassessment/xblock/config_mixin.py +++ b/openassessment/xblock/config_mixin.py @@ -167,5 +167,5 @@ def is_due_date_extension_enabled(self): # .. toggle_description: Set to True to enable the due date extension feature # .. toggle_use_cases: circuit_breaker # .. toggle_creation_date: 2023-08-11 - # .. toggle_tickets: + # .. toggle_tickets: return self.is_feature_enabled(DUE_DATE_EXTENSION) diff --git a/openassessment/xblock/openassessmentblock.py b/openassessment/xblock/openassessmentblock.py index 46944163c4..eda5aa02ed 100644 --- a/openassessment/xblock/openassessmentblock.py +++ b/openassessment/xblock/openassessmentblock.py @@ -1065,27 +1065,33 @@ def render_error(self, error_msg): context = {'error_msg': error_msg} template = get_template('openassessmentblock/oa_error.html') return Response(template.render(context), content_type='application/html', charset='UTF-8') - + def can_use_date_extensions(self): + """ + Check if the due date extension feature is enabled. + + The due date extension feature is enabled if the due date extension + feature flag is enabled and the student has received an date extension. + """ if not self.is_due_date_extension_enabled: return False - + if not self.due: return False - + if not self.submission_due: return False - - parsed_due = parse_date_value(self.due, self._) - parsed_submission_due_= parse_date_value(self.submission_due, self._) - - has_due_date_extension = parsed_due > parsed_submission_due_ + + block_due = parse_date_value(self.due, self._) + submission_due = parse_date_value(self.submission_due, self._) + + has_due_date_extension = block_due > submission_due if not has_due_date_extension: return False - + return True - + def date_ranges(self): """ Generate a list of dates ranges for the submission and assessment steps. @@ -1099,8 +1105,8 @@ def date_ranges(self): """ if self.can_use_date_extensions(): - parsed_due = parse_date_value(self.due, self._) - + block_due = parse_date_value(self.due, self._) + submission_range = (self.submission_start, self.due) assessment_ranges = [] @@ -1113,8 +1119,8 @@ def date_ranges(self): parsed_assesment_due = parse_date_value(assesment_due, self._) - is_extended = parsed_due < parsed_assesment_due - + is_extended = block_due < parsed_assesment_due + if is_extended: assessment_ranges.append((assesment.get('start'), assesment_due)) else: @@ -1127,7 +1133,6 @@ def date_ranges(self): ] return submission_range, assessment_ranges - def is_closed(self, step=None, course_staff=None): """ Checks if the question is closed. diff --git a/openassessment/xblock/test/test_grade.py b/openassessment/xblock/test/test_grade.py index 1e1a775065..d39484dfbc 100644 --- a/openassessment/xblock/test/test_grade.py +++ b/openassessment/xblock/test/test_grade.py @@ -5,6 +5,7 @@ import copy import json +from unittest.mock import Mock import ddt @@ -22,6 +23,7 @@ class TestGrade(XBlockHandlerTestCase, SubmitAssessmentsMixin): @scenario('data/grade_scenario.xml', user_id='Greggs') def test_render_grade(self, xblock): # Submit, assess, and render the grade view + xblock.is_due_date_extension_enabled = Mock(return_value=True) self.create_submission_and_assessments( xblock, self.SUBMISSION, self.PEERS, PEER_ASSESSMENTS, SELF_ASSESSMENT ) @@ -56,6 +58,8 @@ def test_render_grade(self, xblock): @scenario('data/grade_scenario_self_only.xml', user_id='Greggs') def test_render_grade_self_only(self, xblock): # Submit, assess, and render the grade view + xblock.is_due_date_extension_enabled = Mock(return_value=True) + self.create_submission_and_assessments( xblock, self.SUBMISSION, [], [], SELF_ASSESSMENT, waiting_for_peer=True @@ -402,6 +406,7 @@ def _test_incomplete_helper(self, xblock, peers, self_assessment): """ Check assessment completition status is shown correctly on assessment page. """ + xblock.is_due_date_extension_enabled = Mock(return_value=True) self.create_submission_and_assessments( xblock, self.SUBMISSION, peers, [PEER_ASSESSMENTS[0]] if peers else [], self_assessment ) diff --git a/openassessment/xblock/test/test_openassessment.py b/openassessment/xblock/test/test_openassessment.py index f183268f3e..cedc8fb0f9 100644 --- a/openassessment/xblock/test/test_openassessment.py +++ b/openassessment/xblock/test/test_openassessment.py @@ -38,6 +38,8 @@ def test_load_student_view(self, xblock): xblock_fragment = self.runtime.render(xblock, "student_view") self.assertIn("OpenAssessmentBlock", xblock_fragment.body_html()) + xblock.is_due_date_extension_enabled = Mock(return_value=True) + # Validate Submission Rendering. submission_response = xblock.render_submission({}) self.assertIsNotNone(submission_response) @@ -292,6 +294,7 @@ def test_load_student_view_with_dates(self, time_zone, expected_date): time_zone_fn.return_value['user_timezone'] = pytz.timezone(time_zone) xblock = self.load_scenario('data/dates_scenario.xml') + xblock.is_due_date_extension_enabled = Mock(return_value=True) xblock_fragment = self.runtime.render(xblock, "student_view") self.assertIn("OpenAssessmentBlock", xblock_fragment.body_html()) diff --git a/openassessment/xblock/test/test_save_files_descriptions.py b/openassessment/xblock/test/test_save_files_descriptions.py index fa1c715ba4..fdd8beffc7 100644 --- a/openassessment/xblock/test/test_save_files_descriptions.py +++ b/openassessment/xblock/test/test_save_files_descriptions.py @@ -33,6 +33,8 @@ def test_save_files_descriptions(self, xblock): # We're not worried about looking up shared uploads in this test xblock.has_team = mock.Mock(return_value=False) + xblock.is_due_date_extension_enabled = mock.Mock(return_value=True) + xblock.xmodule_runtime = mock.Mock( user_is_staff=False, user_is_beta_tester=False, @@ -66,6 +68,8 @@ def test_append_files_descriptions(self, xblock): # We're not worried about looking up shared uploads in this test xblock.has_team = mock.Mock(return_value=False) + xblock.is_due_date_extension_enabled = mock.Mock(return_value=True) + xblock.xmodule_runtime = mock.Mock( user_is_staff=False, user_is_beta_tester=False, diff --git a/openassessment/xblock/test/test_save_response.py b/openassessment/xblock/test/test_save_response.py index 187d43eb51..4af11cf829 100644 --- a/openassessment/xblock/test/test_save_response.py +++ b/openassessment/xblock/test/test_save_response.py @@ -19,6 +19,8 @@ class SaveResponseTest(XBlockHandlerTestCase): def test_default_saved_response_blank(self, xblock): xblock.get_team_info = mock.Mock(return_value={}) + xblock.is_due_date_extension_enabled = mock.Mock(return_value=True) + xblock.xmodule_runtime = mock.Mock( user_is_staff=False, user_is_beta_tester=False, @@ -34,6 +36,8 @@ def test_default_saved_response_blank(self, xblock): def test_save_response(self, xblock, data): xblock.get_team_info = mock.Mock(return_value={}) + xblock.is_due_date_extension_enabled = mock.Mock(return_value=True) + xblock.xmodule_runtime = mock.Mock( user_is_staff=False, user_is_beta_tester=False, diff --git a/openassessment/xblock/test/test_self.py b/openassessment/xblock/test/test_self.py index b034da9019..efc29d92d3 100644 --- a/openassessment/xblock/test/test_self.py +++ b/openassessment/xblock/test/test_self.py @@ -435,6 +435,8 @@ def test_integration(self, xblock): xblock.get_student_item_dict(), ("Test submission 1", "Test submission 2") ) + xblock.is_due_date_extension_enabled = mock.Mock(return_value=True) + xblock.get_workflow_info = mock.Mock(return_value={ 'status': 'self', 'submission_uuid': submission['uuid'] }) diff --git a/openassessment/xblock/test/test_staff_area.py b/openassessment/xblock/test/test_staff_area.py index 83f8f62185..117352de7d 100644 --- a/openassessment/xblock/test/test_staff_area.py +++ b/openassessment/xblock/test/test_staff_area.py @@ -127,6 +127,7 @@ def test_is_course_staff(self, xblock): @scenario('data/basic_scenario.xml', user_id='Bob') def test_course_staff_area(self, xblock): # If we're not course staff, we shouldn't see the staff area + xblock.is_due_date_extension_enabled = Mock(return_value=True) xblock.xmodule_runtime = self._create_mock_runtime( xblock.scope_ids.usage_id, False, False, "Bob" ) @@ -140,6 +141,7 @@ def test_course_staff_area(self, xblock): @scenario('data/basic_scenario.xml', user_id='Bob') def test_view_in_studio_button(self, xblock): + xblock.is_due_date_extension_enabled = Mock(return_value=True) xblock.xmodule_runtime = self._create_mock_runtime( xblock.scope_ids.usage_id, False, False, "Bob" ) @@ -210,6 +212,8 @@ def test_hide_course_staff_area_in_studio_preview(self, xblock): @scenario('data/staff_dates_scenario.xml', user_id='Bob') def test_staff_area_dates_table(self, xblock): # Simulate that we are course staff + xblock.is_due_date_extension_enabled = Mock(return_value=True) + xblock.xmodule_runtime = self._create_mock_runtime( xblock.scope_ids.usage_id, True, False, "Bob" ) @@ -232,6 +236,8 @@ def test_staff_area_dates_table(self, xblock): @scenario('data/basic_scenario.xml', user_id='Bob') def test_staff_area_dates_distant_past_and_future(self, xblock): # Simulate that we are course staff + xblock.is_due_date_extension_enabled = Mock(return_value=True) + xblock.xmodule_runtime = self._create_mock_runtime( xblock.scope_ids.usage_id, True, False, "Bob" ) @@ -1116,6 +1122,7 @@ def test_staff_area_student_user_state_not_used(self, xblock, waffle_patch): @scenario('data/team_submission.xml', user_id='Bob') def test_staff_area_has_team_info(self, xblock): + xblock.is_due_date_extension_enabled = Mock(return_value=True) # Given that we are course staff, managing a team assignment self._setup_xblock_and_create_submission(xblock) @@ -1140,6 +1147,7 @@ def test_staff_area_student_info_has_team_info(self, xblock): @scenario('data/basic_scenario.xml', user_id='Bob') def test_staff_area_has_team_info_individual(self, xblock): # Given that we are course staff, managing an individual assignment + xblock.is_due_date_extension_enabled = Mock(return_value=True) self._setup_xblock_and_create_submission(xblock) # When I get the staff context diff --git a/openassessment/xblock/test/test_student_training.py b/openassessment/xblock/test/test_student_training.py index 8d140cdbfa..21403ea40e 100644 --- a/openassessment/xblock/test/test_student_training.py +++ b/openassessment/xblock/test/test_student_training.py @@ -199,6 +199,7 @@ def test_updates_workflow(self, xblock, data): @scenario('data/feedback_only_criterion_student_training.xml', user_id='Bob') def test_feedback_only_criterion(self, xblock): + xblock.is_due_date_extension_enabled = Mock(return_value=True) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) self.request(xblock, 'render_student_training', json.dumps({})) @@ -264,6 +265,7 @@ def test_no_submission(self, xblock): 'Grammar': 'Poor' } } + xblock.is_due_date_extension_enabled = Mock(return_value=True) resp = self.request(xblock, 'training_assess', json.dumps(selected_data)) self.assertIn("Your scores could not be checked", resp.decode('utf-8')) @@ -315,11 +317,13 @@ def test_no_student_training_defined(self, xblock): @scenario('data/student_training.xml', user_id="Plato") def test_no_submission(self, xblock): + xblock.is_due_date_extension_enabled = Mock(return_value=True) resp = self.request(xblock, 'render_student_training', json.dumps({})) self.assertIn("Not Available", resp.decode('utf-8')) @scenario('data/student_training.xml') def test_studio_preview(self, xblock): + xblock.is_due_date_extension_enabled = Mock(return_value=True) resp = self.request(xblock, 'render_student_training', json.dumps({})) self.assertIn("Not Available", resp.decode('utf-8')) diff --git a/openassessment/xblock/test/test_submission.py b/openassessment/xblock/test/test_submission.py index 979b9c0df2..aa53f687f5 100644 --- a/openassessment/xblock/test/test_submission.py +++ b/openassessment/xblock/test/test_submission.py @@ -150,6 +150,7 @@ def test_cannot_submit_in_preview_mode(self, xblock): @scenario('data/over_grade_scenario.xml', user_id='Alice') def test_closed_submissions(self, xblock): + xblock.is_due_date_extension_enabled = Mock(return_value=True) resp = self.request(xblock, 'render_submission', json.dumps({})) self.assertIn("Incomplete", resp.decode('utf-8')) @@ -157,12 +158,14 @@ def test_closed_submissions(self, xblock): def test_prompt_line_breaks(self, xblock): # Verify that prompts with multiple lines retain line breaks # (backward compatibility in case if prompt_type == 'text') + xblock.is_due_date_extension_enabled = Mock(return_value=True) resp = self.request(xblock, 'render_submission', json.dumps({})) expected_prompt = "


Line 1

Line 2

Line 3

" self.assertIn(expected_prompt, resp.decode('utf-8')) @scenario('data/prompt_html.xml') def test_prompt_html_to_text(self, xblock): + xblock.is_due_date_extension_enabled = Mock(return_value=True) resp = self.request(xblock, 'render_submission', json.dumps({})) expected_prompt = "Question 123" self.assertIn(expected_prompt, resp.decode('utf-8')) @@ -948,6 +951,8 @@ def test_open_saved_response(self, xblock): xblock.file_manager.append_uploads(*file_uploads) + xblock.is_due_date_extension_enabled = Mock(return_value=True) + # Save a response payload = json.dumps({'submission': ('A man must have a code', 'A man must have an umbrella too.')}) resp = self.request(xblock, 'save_submission', payload, response_format='json') @@ -1031,6 +1036,8 @@ def test_open_saved_response_deleted_file_uploads(self, xblock): anonymous_student_id='Pmn' ) + xblock.is_due_date_extension_enabled = Mock(return_value=True) + # delete file-2 with patch('openassessment.fileupload.api.remove_file'): xblock.file_manager.delete_upload(1) @@ -1197,6 +1204,7 @@ def test_render_shared_files(self, xblock, mock_get_download_url): Test that we render files owned by Valchek and their teammates when files are shared with a team. """ + xblock.is_due_date_extension_enabled = Mock(return_value=True) xblock.file_manager.get_uploads = Mock(return_value=[ api.FileUpload( description='file 1 description', @@ -1600,6 +1608,8 @@ def test_closed_graded(self, xblock): @scenario('data/submission_open.xml', user_id="Bob") def test_integration(self, xblock): # Expect that the response step is open and displays the deadline + xblock.is_due_date_extension_enabled = Mock(return_value=True) + resp = self.request(xblock, 'render_submission', json.dumps({})) self.assertIn('Enter your response to the prompt', resp.decode('utf-8')) self.assertIn('2999-05-06T00:00:00+00:00', resp.decode('utf-8'))