diff --git a/openassessment/conf/locale/en/LC_MESSAGES/django.po b/openassessment/conf/locale/en/LC_MESSAGES/django.po index df6ab55c8d..2fc1c3c29f 100644 --- a/openassessment/conf/locale/en/LC_MESSAGES/django.po +++ b/openassessment/conf/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-ora2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-08-23 14:25+0000\n" +"POT-Creation-Date: 2023-08-30 22:03+0000\n" "PO-Revision-Date: 2014-06-04 15:41-0400\n" "Last-Translator: Muhammad Ayub khan \n" "Language-Team: openedx-translation \n" @@ -1365,41 +1365,51 @@ msgstr "" msgid "Enter your response to the prompt." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:91 +#: templates/openassessmentblock/response/oa_response.html:92 msgid "" -"\n" -" Your work will save automatically and you can return " -"to complete your response at any time before the due date\n" -" " +"Your work will save automatically and you can return to complete your " +"response at any time before the subsection due date " +msgstr "" + +#: templates/openassessmentblock/response/oa_response.html:94 +msgid "" +"Your work will save automatically and you can return to complete your " +"response at any time before the course ends " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:104 +#: templates/openassessmentblock/response/oa_response.html:96 +msgid "" +"Your work will save automatically and you can return to complete your " +"response at any time before the due date " +msgstr "" + +#: templates/openassessmentblock/response/oa_response.html:108 msgid "" "Your work will save automatically and you can return to complete your " "response at any time." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:106 +#: templates/openassessmentblock/response/oa_response.html:110 msgid "After you submit your response, you cannot edit it" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:116 +#: templates/openassessmentblock/response/oa_response.html:120 msgid "What will this assignment be graded on?" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:130 +#: templates/openassessmentblock/response/oa_response.html:134 msgid "The prompt for this section" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:144 +#: templates/openassessmentblock/response/oa_response.html:148 msgid "You are on team " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:146 +#: templates/openassessmentblock/response/oa_response.html:150 msgid "Team Members: " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:151 +#: templates/openassessmentblock/response/oa_response.html:155 #, python-format msgid "" "\n" @@ -1411,25 +1421,25 @@ msgid "" " " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:167 +#: templates/openassessmentblock/response/oa_response.html:171 msgid "Team Response " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:169 +#: templates/openassessmentblock/response/oa_response.html:173 msgid "Your Response " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:173 -#: templates/openassessmentblock/response/oa_response.html:243 +#: templates/openassessmentblock/response/oa_response.html:177 +#: templates/openassessmentblock/response/oa_response.html:247 msgid "(Required)" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:175 -#: templates/openassessmentblock/response/oa_response.html:245 +#: templates/openassessmentblock/response/oa_response.html:179 +#: templates/openassessmentblock/response/oa_response.html:249 msgid "(Optional)" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:179 +#: templates/openassessmentblock/response/oa_response.html:183 msgid "" "\n" " Teams should designate one " @@ -1444,11 +1454,11 @@ msgid "" " " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:193 +#: templates/openassessmentblock/response/oa_response.html:197 msgid "Enter your response to the prompt above." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:207 +#: templates/openassessmentblock/response/oa_response.html:211 #, python-format msgid "" "\n" @@ -1464,19 +1474,19 @@ msgid "" " " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:220 +#: templates/openassessmentblock/response/oa_response.html:224 msgid "We could not save your progress" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:228 +#: templates/openassessmentblock/response/oa_response.html:232 msgid "Status of Your Response" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:241 +#: templates/openassessmentblock/response/oa_response.html:245 msgid "File Uploads " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:249 +#: templates/openassessmentblock/response/oa_response.html:253 msgid "" "\n" " Upload files and review files uploaded by " @@ -1486,60 +1496,60 @@ msgid "" " " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:257 +#: templates/openassessmentblock/response/oa_response.html:261 msgid "We could not upload files" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:263 +#: templates/openassessmentblock/response/oa_response.html:267 msgid "We could not delete files" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:270 +#: templates/openassessmentblock/response/oa_response.html:274 msgid "Select one or more files to upload for this submission." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:272 +#: templates/openassessmentblock/response/oa_response.html:276 msgid "Select a file to upload for this submission." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:276 +#: templates/openassessmentblock/response/oa_response.html:280 msgid "Supported file types: " msgstr "" -#: templates/openassessmentblock/response/oa_response.html:281 +#: templates/openassessmentblock/response/oa_response.html:285 msgid "Upload files" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:283 +#: templates/openassessmentblock/response/oa_response.html:287 msgid "Upload file" msgstr "" -#: templates/openassessmentblock/response/oa_response.html:300 +#: templates/openassessmentblock/response/oa_response.html:304 msgid "This is a team submission." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:302 +#: templates/openassessmentblock/response/oa_response.html:306 msgid "" "One team member should submit a response with the team’s shared files and a " "text response on behalf of the entire team." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:304 +#: templates/openassessmentblock/response/oa_response.html:308 msgid "" "One team member should submit a response with the team’s shared files on " "behalf of the entire team." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:306 +#: templates/openassessmentblock/response/oa_response.html:310 msgid "" "One team member should submit a text response on behalf of the entire team." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:308 +#: templates/openassessmentblock/response/oa_response.html:312 msgid "One team member should submit on behalf of the entire team." msgstr "" -#: templates/openassessmentblock/response/oa_response.html:310 +#: templates/openassessmentblock/response/oa_response.html:314 msgid "" "\n" " Learn more about team assignments here: (\n" "Language-Team: openedx-translation \n" diff --git a/openassessment/conf/locale/eo/LC_MESSAGES/django.po b/openassessment/conf/locale/eo/LC_MESSAGES/django.po index b3221cc12b..33a66d445b 100644 --- a/openassessment/conf/locale/eo/LC_MESSAGES/django.po +++ b/openassessment/conf/locale/eo/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-ora2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-08-23 14:25+0000\n" +"POT-Creation-Date: 2023-08-30 22:03+0000\n" "PO-Revision-Date: 2014-06-04 15:41-0400\n" "Last-Translator: Muhammad Ayub khan \n" "Language-Team: openedx-translation \n" @@ -1735,13 +1735,27 @@ msgstr "" #: templates/openassessmentblock/response/oa_response.html msgid "" -"\n" -" Your work will save automatically and you can return to complete your response at any time before the due date\n" -" " +"Your work will save automatically and you can return to complete your " +"response at any time before the subsection due date " msgstr "" -"\n" -" Ýöür wörk wïll sävé äütömätïçällý änd ýöü çän rétürn tö çömplété ýöür réspönsé ät äný tïmé ßéföré thé düé däté\n" -" Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ ησѕтяυ∂ єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт ιη ¢υłρα qυι σƒƒι¢ια ∂єѕєяυηт#" +"Ýöür wörk wïll sävé äütömätïçällý änd ýöü çän rétürn tö çömplété ýöür " +"réspönsé ät äný tïmé ßéföré thé süßséçtïön düé däté Ⱡ'σяєм ιρѕ#" + +#: templates/openassessmentblock/response/oa_response.html +msgid "" +"Your work will save automatically and you can return to complete your " +"response at any time before the course ends " +msgstr "" +"Ýöür wörk wïll sävé äütömätïçällý änd ýöü çän rétürn tö çömplété ýöür " +"réspönsé ät äný tïmé ßéföré thé çöürsé énds Ⱡ'σяєм ιρѕυм ∂σł#" + +#: templates/openassessmentblock/response/oa_response.html +msgid "" +"Your work will save automatically and you can return to complete your " +"response at any time before the due date " +msgstr "" +"Ýöür wörk wïll sävé äütömätïçällý änd ýöü çän rétürn tö çömplété ýöür " +"réspönsé ät äný tïmé ßéföré thé düé däté Ⱡ'σяєм ιρѕυм ∂σłσя #" #: templates/openassessmentblock/response/oa_response.html msgid "" diff --git a/openassessment/conf/locale/eo/LC_MESSAGES/djangojs.po b/openassessment/conf/locale/eo/LC_MESSAGES/djangojs.po index bf05096e6e..c8eccd161d 100644 --- a/openassessment/conf/locale/eo/LC_MESSAGES/djangojs.po +++ b/openassessment/conf/locale/eo/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: edx-ora2\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-08-23 14:25+0000\n" +"POT-Creation-Date: 2023-08-30 22:03+0000\n" "PO-Revision-Date: 2014-06-04 15:41-0400\n" "Last-Translator: Muhammad Ayub khan \n" "Language-Team: openedx-translation \n" diff --git a/openassessment/templates/openassessmentblock/response/oa_response.html b/openassessment/templates/openassessmentblock/response/oa_response.html index 636215b099..ab49b7c48d 100644 --- a/openassessment/templates/openassessmentblock/response/oa_response.html +++ b/openassessment/templates/openassessmentblock/response/oa_response.html @@ -88,9 +88,13 @@

{% else %} {% trans "Enter your response to the prompt." %} {% if submission_due %} - {% blocktrans %} - Your work will save automatically and you can return to complete your response at any time before the due date - {% endblocktrans %} + {% if date_config_type == 'subsection' %} + {% trans "Your work will save automatically and you can return to complete your response at any time before the subsection due date " %} + {% elif date_config_type == 'course_end' %} + {% trans "Your work will save automatically and you can return to complete your response at any time before the course ends " %} + {% else %} + {% trans "Your work will save automatically and you can return to complete your response at any time before the due date " %} + {% endif %} ( + Open Assessment Test + + + + Timeline + ======== + August 1 - Submission opens + 2 - Self Assessment opens + 3 - Peer Assessment opens and submission due + 5 - Self Assessment due + 8 - Peer Assessment due + + + + + + + + + + diff --git a/openassessment/xblock/test/test_openassessment.py b/openassessment/xblock/test/test_openassessment.py index e15812e617..ed3daec70f 100644 --- a/openassessment/xblock/test/test_openassessment.py +++ b/openassessment/xblock/test/test_openassessment.py @@ -21,6 +21,47 @@ from .base import XBlockHandlerTestCase, scenario +def assert_is_closed( + xblock, + now, + step, + expected_is_closed, + expected_reason, + expected_start, + expected_due, + released=None, + course_staff=False, +): + """ + Assert whether the XBlock step is open/closed. + + Args: + xblock (OpenAssessmentBlock): The xblock under test. + now (datetime): Time to patch for the xblock's call to datetime.now() + step (str): The step in the workflow (e.g. "submission", "self-assessment") + expected_is_closed (bool): Do we expect the step to be open or closed? + expected_reason (str): Either "start", "due", or None. + expected_start (datetime): Expected start date. + expected_due (datetime): Expected due date. + + Keyword Arguments: + released (bool): If set, check whether the XBlock has been released. + course_staff (bool): Whether to treat the user as course staff. + + Raises: + AssertionError + """ + with freeze_time(now): + is_closed, reason, start, due = xblock.is_closed(step=step, course_staff=course_staff) + assert is_closed == expected_is_closed + assert reason == expected_reason + assert start == expected_start + assert due == expected_due + + if released is not None: + assert xblock.is_released(step=step) == released + + @ddt.ddt class TestOpenAssessment(XBlockHandlerTestCase): """Test Open Asessessment Xblock functionality""" @@ -628,28 +669,28 @@ def test_start_end_date_checks(self, xblock): xblock.start = dt.datetime(2014, 3, 1).replace(tzinfo=pytz.utc) xblock.due = dt.datetime(2014, 3, 5).replace(tzinfo=pytz.utc) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 2, 28, 23, 59, 59), None, True, "start", xblock.start, xblock.due, released=False ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 3, 1, 1, 1, 1), None, False, None, xblock.start, xblock.due, released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 3, 4, 23, 59, 59), None, False, None, xblock.start, xblock.due, released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 3, 5, 1, 1, 1), None, True, "due", xblock.start, xblock.due, @@ -662,7 +703,7 @@ def test_submission_dates(self, xblock): xblock.start = dt.datetime(2014, 3, 1).replace(tzinfo=pytz.utc).isoformat() xblock.due = None - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 2, 28, 23, 59, 59).replace(tzinfo=pytz.utc), "submission", True, "start", @@ -671,7 +712,7 @@ def test_submission_dates(self, xblock): released=False ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 3, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "submission", False, None, @@ -680,7 +721,7 @@ def test_submission_dates(self, xblock): released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc), "submission", False, None, @@ -689,7 +730,7 @@ def test_submission_dates(self, xblock): released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 4, 1, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "submission", True, "due", @@ -704,7 +745,7 @@ def test_peer_assessment_dates(self, xblock): xblock.start = None xblock.due = None - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2015, 1, 1, 23, 59, 59).replace(tzinfo=pytz.utc), "peer-assessment", True, "start", @@ -713,7 +754,7 @@ def test_peer_assessment_dates(self, xblock): released=False ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2015, 1, 2, 1, 1, 1).replace(tzinfo=pytz.utc), "peer-assessment", False, None, @@ -722,7 +763,7 @@ def test_peer_assessment_dates(self, xblock): released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2015, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc), "peer-assessment", False, None, @@ -731,7 +772,7 @@ def test_peer_assessment_dates(self, xblock): released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2015, 4, 1, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "peer-assessment", True, "due", @@ -746,7 +787,7 @@ def test_self_assessment_dates(self, xblock): xblock.start = None xblock.due = None - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 1, 1, 23, 59, 59).replace(tzinfo=pytz.utc), "self-assessment", True, "start", @@ -755,7 +796,7 @@ def test_self_assessment_dates(self, xblock): released=False ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 1, 2, 1, 1, 1).replace(tzinfo=pytz.utc), "self-assessment", False, None, @@ -764,7 +805,7 @@ def test_self_assessment_dates(self, xblock): released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc), "self-assessment", False, None, @@ -773,7 +814,7 @@ def test_self_assessment_dates(self, xblock): released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 4, 1, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "self-assessment", True, "due", @@ -790,7 +831,7 @@ def test_resolve_dates(self, xblock): xblock.start = dt.datetime(2014, 3, 1).replace(tzinfo=pytz.utc).isoformat() xblock.due = None - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 2, 28, 23, 59, 59).replace(tzinfo=pytz.utc), "peer-assessment", True, "start", @@ -799,7 +840,7 @@ def test_resolve_dates(self, xblock): released=False ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 3, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "peer-assessment", False, None, @@ -808,7 +849,7 @@ def test_resolve_dates(self, xblock): released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 5, 1, 23, 59, 59).replace(tzinfo=pytz.utc), "peer-assessment", False, None, @@ -817,7 +858,7 @@ def test_resolve_dates(self, xblock): released=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 5, 2, 1, 1, 1).replace(tzinfo=pytz.utc), "peer-assessment", True, "due", @@ -910,7 +951,7 @@ def test_course_staff_dates(self, xblock): # The problem should always be open for course staff # The following assertions check before/during/after dates # for submission/peer/self - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 2, 28, 23, 59, 59).replace(tzinfo=pytz.utc), "submission", False, None, @@ -918,7 +959,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 3, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "submission", False, None, @@ -926,7 +967,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc), "submission", False, None, @@ -934,7 +975,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2014, 4, 1, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "submission", False, None, @@ -942,7 +983,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2015, 1, 1, 23, 59, 59).replace(tzinfo=pytz.utc), "peer-assessment", False, None, @@ -950,7 +991,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2015, 1, 2, 1, 1, 1).replace(tzinfo=pytz.utc), "peer-assessment", False, None, @@ -958,7 +999,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2015, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc), "peer-assessment", False, None, @@ -966,7 +1007,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2015, 4, 1, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "peer-assessment", False, None, @@ -974,7 +1015,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 1, 1, 23, 59, 59).replace(tzinfo=pytz.utc), "self-assessment", False, None, @@ -982,7 +1023,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 1, 2, 1, 1, 1).replace(tzinfo=pytz.utc), "self-assessment", False, None, @@ -990,7 +1031,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc), "self-assessment", False, None, @@ -998,7 +1039,7 @@ def test_course_staff_dates(self, xblock): course_staff=True ) - self.assert_is_closed( + assert_is_closed( xblock, dt.datetime(2016, 4, 1, 1, 1, 1, 1).replace(tzinfo=pytz.utc), "self-assessment", False, None, @@ -1007,114 +1048,223 @@ def test_course_staff_dates(self, xblock): ) @scenario('data/basic_scenario.xml') - def test_date_config_type_course_end(self, xblock): + def test_get_username(self, xblock): + user = MagicMock() + user.username = "Bob" + + xblock.xmodule_runtime = MagicMock() + xblock.xmodule_runtime.get_real_user.return_value = user + + self.assertEqual('Bob', xblock.get_username('anon_id')) + + @scenario('data/basic_scenario.xml') + def test_get_username_unknown_id(self, xblock): + xblock.xmodule_runtime = MagicMock() + xblock.xmodule_runtime.get_real_user.return_value = None + + self.assertIsNone(xblock.get_username('unknown_id')) + + +class IsClosedDateConfigTypeTestCase(XBlockHandlerTestCase): + + DEFAULT_COURSE_START = dt.datetime(2023, 5, 1).replace(tzinfo=pytz.utc) + DEFAULT_COURSE_END = dt.datetime(2023, 12, 1).replace(tzinfo=pytz.utc) + DEFAULT_SUBSECTION_START = dt.datetime(2023, 7, 30).replace(tzinfo=pytz.utc) + DEFAULT_SUBSECTION_DUE = dt.datetime(2023, 8, 10).replace(tzinfo=pytz.utc) + + def defined_manual_dates(self, xblock, step): + """ + Helper to get an assessment's start and due dates as datetime object + """ + if step == 'submission': + return ( + dt.datetime.fromisoformat(xblock.submission_start), + dt.datetime.fromisoformat(xblock.submission_due) + ) + for assessment in xblock.valid_assessments: + if assessment['name'] == step + '-assessment': + return ( + dt.datetime.fromisoformat(assessment.get('start')), + dt.datetime.fromisoformat(assessment.get('due')) + ) + return None + + def setup_dates(self, xblock, course_dates=None, subsection_dates=None): + """ + Helper to set up course start and end dates and subsection start and due dates + """ + mock_course = Mock() + if course_dates is None: + course_dates = ( + self.DEFAULT_COURSE_START, + self.DEFAULT_COURSE_END + ) + mock_course.start = course_dates[0] + mock_course.end = course_dates[1] + xblock.course = mock_course + + if subsection_dates is None: + subsection_dates = ( + self.DEFAULT_SUBSECTION_START, + self.DEFAULT_SUBSECTION_DUE + ) + xblock.start = subsection_dates[0] + xblock.due = subsection_dates[1] + + @scenario('data/date_config_scenario.xml') + def test_course_end(self, xblock): """ Test that the date config type field set to course end date """ xblock.date_config_type = defaults.DATE_CONFIG_COURSE_END + self.setup_dates(xblock) - mock_course = Mock() - mock_course.start = dt.datetime(2016, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc) - mock_course.end = dt.datetime(2016, 4, 1, 1, 1, 1, 1).replace(tzinfo=pytz.utc) - xblock.course = mock_course + defined_submission_due = self.defined_manual_dates(xblock, 'submission')[1] + defined_peer_due = self.defined_manual_dates(xblock, 'peer')[1] - # The problem should always be not be close if now is before course start date - self.assert_is_closed( + # The problem should not be closed if now is before course end date + assert_is_closed( xblock, - dt.datetime(2016, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc), - "abitrary", False, None, - mock_course.start, mock_course.end + defined_peer_due + dt.timedelta(hours=3), + "peer", + False, + None, + xblock.course.start, + xblock.course.end + ) + assert_is_closed( + xblock, + xblock.due + dt.timedelta(hours=3), + "peer", + False, + None, + xblock.course.start, + xblock.course.end + ) + assert_is_closed( + xblock, + defined_submission_due + dt.timedelta(hours=3), + "submission", + False, + None, + xblock.course.start, + xblock.course.end + ) + assert_is_closed( + xblock, + xblock.due + dt.timedelta(hours=3), + "submission", + False, + None, + xblock.course.start, + xblock.course.end ) # The problem should be closed if now is after course end date - self.assert_is_closed( + assert_is_closed( + xblock, + xblock.course.end + dt.timedelta(minutes=1), + "peer", + True, + 'due', + xblock.course.start, + xblock.course.end + ) + assert_is_closed( xblock, - dt.datetime(2016, 4, 1, 1, 1, 1, 2).replace(tzinfo=pytz.utc), - "abitrary", True, 'due', - mock_course.start, mock_course.end + xblock.course.end + dt.timedelta(minutes=1), + "submission", + True, + 'due', + xblock.course.start, + xblock.course.end ) - @scenario('data/basic_scenario.xml') - def test_date_config_type_subsection(self, xblock): + @scenario('data/date_config_scenario.xml') + def test_subsection(self, xblock): """ - Test that the date config type field set to section + Test that the date config type field set to submission due date """ xblock.date_config_type = defaults.DATE_CONFIG_SUBSECTION - xblock.start = dt.datetime(2016, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc) - xblock.due = dt.datetime(2016, 4, 1, 1, 1, 1, 1).replace(tzinfo=pytz.utc) + self.setup_dates(xblock) + defined_submission_due = self.defined_manual_dates(xblock, 'submission')[1] + defined_peer_due = self.defined_manual_dates(xblock, 'peer')[1] - # The problem should always be not be close if now is before section start date - self.assert_is_closed( + # The problem should not be closed if now is before subsection due date + assert_is_closed( xblock, - dt.datetime(2016, 3, 31, 23, 59, 59).replace(tzinfo=pytz.utc), - "abitrary", False, None, - xblock.start, xblock.due + defined_peer_due + dt.timedelta(hours=3), + "peer", + False, + None, + xblock.start, + xblock.due + ) + assert_is_closed( + xblock, + defined_submission_due + dt.timedelta(hours=3), + "submission", + False, + None, + xblock.start, + xblock.due ) - # The problem should be closed if now is after section due date - self.assert_is_closed( + # The problem should be closed if now is after subsection due date + assert_is_closed( xblock, - dt.datetime(2016, 4, 1, 1, 1, 1, 2).replace(tzinfo=pytz.utc), - "abitrary", True, 'due', - xblock.start, xblock.due + xblock.due + dt.timedelta(minutes=1), + "submission", + True, + 'due', + xblock.start, + xblock.due + ) + assert_is_closed( + xblock, + xblock.due + dt.timedelta(minutes=1), + "submission", + True, + 'due', + xblock.start, + xblock.due ) - def assert_is_closed( - self, xblock, now, step, expected_is_closed, expected_reason, - expected_start, expected_due, released=None, course_staff=False, - ): + @scenario('data/date_config_scenario.xml') + def test_course_end_no_defined_course_end(self, xblock): """ - Assert whether the XBlock step is open/closed. - - Args: - xblock (OpenAssessmentBlock): The xblock under test. - now (datetime): Time to patch for the xblock's call to datetime.now() - step (str): The step in the workflow (e.g. "submission", "self-assessment") - expected_is_closed (bool): Do we expect the step to be open or closed? - expected_reason (str): Either "start", "due", or None. - expected_start (datetime): Expected start date. - expected_due (datetime): Expected due date. - - Keyword Arguments: - released (bool): If set, check whether the XBlock has been released. - course_staff (bool): Whether to treat the user as course staff. - - Raises: - AssertionError + Test behavior for when course dates aren't set """ - # Need some non-conventional setup to patch datetime because it's a C module. - # http://nedbatchelder.com/blog/201209/mocking_datetimetoday.html - # Thanks Ned! - datetime_patcher = patch.object(openassessmentblock, 'dt', Mock(wraps=dt)) - mocked_datetime = datetime_patcher.start() - self.addCleanup(datetime_patcher.stop) - mocked_datetime.datetime.utcnow.return_value = now - - is_closed, reason, start, due = xblock.is_closed(step=step, course_staff=course_staff) - self.assertEqual(is_closed, expected_is_closed) - self.assertEqual(reason, expected_reason) - self.assertEqual(start, expected_start) - self.assertEqual(due, expected_due) - - if released is not None: - self.assertEqual(xblock.is_released(step=step), released) - - @scenario('data/basic_scenario.xml') - def test_get_username(self, xblock): - user = MagicMock() - user.username = "Bob" - - xblock.xmodule_runtime = MagicMock() - xblock.xmodule_runtime.get_real_user.return_value = user + xblock.date_config_type = defaults.DATE_CONFIG_COURSE_END + self.setup_dates(xblock, course_dates=(None, None)) - self.assertEqual('Bob', xblock.get_username('anon_id')) + assert_is_closed( + xblock, + xblock.due, + "submission", + False, + None, + DISTANT_PAST, + DISTANT_FUTURE, + ) - @scenario('data/basic_scenario.xml') - def test_get_username_unknown_id(self, xblock): - xblock.xmodule_runtime = MagicMock() - xblock.xmodule_runtime.get_real_user.return_value = None + @scenario('data/date_config_scenario.xml') + def test_subsection_none_defined(self, xblock): + """ + Test behavior for when subsection dates aren't defined + """ + xblock.date_config_type = defaults.DATE_CONFIG_SUBSECTION + self.setup_dates(xblock, subsection_dates=(None, None)) - self.assertIsNone(xblock.get_username('unknown_id')) + assert_is_closed( + xblock, + self.defined_manual_dates(xblock, 'submission')[1], + "submission", + False, + None, + DISTANT_PAST, + DISTANT_FUTURE, + ) class OpenAssessmentIndexingTestCase(XBlockHandlerTestCase): diff --git a/openassessment/xblock/test/test_submission.py b/openassessment/xblock/test/test_submission.py index 979b9c0df2..446e68ba62 100644 --- a/openassessment/xblock/test/test_submission.py +++ b/openassessment/xblock/test/test_submission.py @@ -759,6 +759,7 @@ def test_open_unanswered(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': True, 'file_upload_response': None, 'file_upload_type': None, @@ -796,6 +797,7 @@ def test_team_open_unanswered(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': True, 'file_upload_response': None, 'file_upload_type': None, @@ -960,6 +962,7 @@ def test_open_saved_response(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': True, 'file_upload_response': None, 'file_upload_type': None, @@ -1271,6 +1274,7 @@ def test_open_saved_response_old_format(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': True, 'file_upload_response': None, 'file_upload_type': None, @@ -1321,6 +1325,7 @@ def test_open_submitted(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': False, 'file_upload_response': None, 'file_upload_type': None, @@ -1360,6 +1365,7 @@ def test_cancelled_submission(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': False, 'file_upload_response': None, 'file_upload_type': None, @@ -1417,6 +1423,7 @@ def test_cancelled_team_submission(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': False, 'file_upload_response': None, 'file_upload_type': None, @@ -1457,6 +1464,7 @@ def test_open_submitted_old_format(self, xblock, mock_get_user_submission): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': False, 'file_upload_response': None, 'file_upload_type': None, @@ -1484,6 +1492,7 @@ def test_closed_incomplete(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': False, 'file_upload_response': None, 'file_upload_type': None, @@ -1510,6 +1519,7 @@ def test_closed_submitted(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': False, 'file_upload_response': None, 'file_upload_type': None, @@ -1547,6 +1557,7 @@ def test_open_graded(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': False, 'file_upload_response': None, 'file_upload_type': None, @@ -1582,6 +1593,7 @@ def test_closed_graded(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': False, 'has_real_user': False, 'file_upload_response': None, @@ -1769,6 +1781,7 @@ def test_team_has_already_submitted(self, xblock): 'allow_latex': False, 'allow_multiple_files': True, 'base_asset_url': None, + 'date_config_type': 'manual', 'enable_delete_files': True, 'has_real_user': True, 'file_upload_response': None,