diff --git a/openedx/envs/common.py b/openedx/envs/common.py index cfb82c90946e..9a934aaa776c 100644 --- a/openedx/envs/common.py +++ b/openedx/envs/common.py @@ -995,15 +995,6 @@ def _make_locale_paths(settings): # .. toggle_tickets: 'https://github.com/open-craft/edx-platform/pull/429' DISABLE_UNENROLLMENT = False -# .. toggle_name: ENABLE_GRADING_METHOD_IN_PROBLEMS -# .. toggle_implementation: DjangoSetting -# .. toggle_default: False -# .. toggle_description: Enables the grading method feature in capa problems. -# .. toggle_use_cases: open_edx -# .. toggle_creation_date: 2024-03-22 -# .. toggle_tickets: https://github.com/openedx/edx-platform/pull/33911 -ENABLE_GRADING_METHOD_IN_PROBLEMS = False - # .. toggle_name: BADGES_ENABLED # .. toggle_implementation: DjangoSetting # .. toggle_default: False diff --git a/xmodule/capa/capa_problem.py b/xmodule/capa/capa_problem.py index 3203ad6bf1d9..e8a6132a6317 100644 --- a/xmodule/capa/capa_problem.py +++ b/xmodule/capa/capa_problem.py @@ -246,15 +246,6 @@ def __init__( if extract_tree: self.extracted_tree = self._extract_html(self.tree) - @property - def is_grading_method_enabled(self) -> bool: - """ - Returns whether the grading method feature is enabled. If the - feature is not enabled, the grading method field will not be shown in - Studio settings and the default grading method will be used. - """ - return settings.FEATURES.get("ENABLE_GRADING_METHOD_IN_PROBLEMS", False) - def make_xml_compatible(self, tree): """ Adjust tree xml in-place for compatibility before creating @@ -502,7 +493,7 @@ def get_grade_from_current_answers(self, student_answers, correct_map: Optional[ and student_answers_history). The correct map will always be updated, depending on the student answers. The student answers will always remain the same over time. """ - oldcmap = correct_map if self.is_grading_method_enabled else self.correct_map + oldcmap = correct_map if correct_map is not None else self.correct_map # start new with empty CorrectMap newcmap = CorrectMap() @@ -521,12 +512,7 @@ def get_grade_from_current_answers(self, student_answers, correct_map: Optional[ # submission that would not exist in the persisted "student_answers". # If grading method is enabled, we need to pass each student answers and the # correct map in the history fields. - if ( - "filesubmission" in responder.allowed_inputfields and student_answers is not None - ) or self.is_grading_method_enabled: - results = responder.evaluate_answers(student_answers, oldcmap) - else: - results = responder.evaluate_answers(self.student_answers, oldcmap) + results = responder.evaluate_answers(student_answers, oldcmap) newcmap.update(results) return newcmap diff --git a/xmodule/capa/tests/test_capa_problem.py b/xmodule/capa/tests/test_capa_problem.py index 473f04461a1a..7171ea3c0663 100644 --- a/xmodule/capa/tests/test_capa_problem.py +++ b/xmodule/capa/tests/test_capa_problem.py @@ -8,8 +8,6 @@ import ddt import pytest -from django.conf import settings -from django.test import override_settings from lxml import etree from markupsafe import Markup @@ -19,9 +17,6 @@ from xmodule.capa.tests.helpers import new_loncapa_problem from xmodule.capa.tests.test_util import use_unsafe_codejail -FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS = settings.FEATURES.copy() -FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS["ENABLE_GRADING_METHOD_IN_PROBLEMS"] = True - @ddt.ddt @use_unsafe_codejail() @@ -771,7 +766,6 @@ def test_get_question_answer(self): # function can eventualy be serialized to json without issues. assert isinstance(problem.get_question_answers()["1_solution_1"], str) - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) def test_get_grade_from_current_answers(self): """ Verify that `responder.evaluate_answers` is called with `student_answers` @@ -805,7 +799,6 @@ def test_get_grade_from_current_answers(self): self.assertDictEqual(result.get_dict(), correct_map.get_dict()) responder_mock.evaluate_answers.assert_called_once_with(student_answers, correct_map) - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) def test_get_grade_from_current_answers_without_student_answers(self): """ Verify that `responder.evaluate_answers` is called with appropriate arguments. @@ -839,7 +832,6 @@ def test_get_grade_from_current_answers_without_student_answers(self): self.assertDictEqual(result.get_dict(), correct_map.get_dict()) responder_mock.evaluate_answers.assert_called_once_with(None, correct_map) - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) def test_get_grade_from_current_answers_with_filesubmission(self): """ Verify that an exception is raised when `responder.evaluate_answers` is called diff --git a/xmodule/capa_block.py b/xmodule/capa_block.py index 08790e9ffe69..962bb7e19d5a 100644 --- a/xmodule/capa_block.py +++ b/xmodule/capa_block.py @@ -505,8 +505,7 @@ def display_name_with_default(self): def grading_method_display_name(self) -> str | None: """ - If the `ENABLE_GRADING_METHOD_IN_PROBLEMS` feature flag is enabled, - return the grading method, else return None. + Return the grading method """ _ = self.runtime.service(self, "i18n").gettext display_name = { @@ -515,18 +514,7 @@ def grading_method_display_name(self) -> str | None: GRADING_METHOD.HIGHEST_SCORE: _("Highest Score"), GRADING_METHOD.AVERAGE_SCORE: _("Average Score"), } - if self.is_grading_method_enabled: - return display_name[self.grading_method] - return None - - @property - def is_grading_method_enabled(self) -> bool: - """ - Returns whether the grading method feature is enabled. If the - feature is not enabled, the grading method field will not be shown in - Studio settings and the default grading method will be used. - """ - return settings.FEATURES.get("ENABLE_GRADING_METHOD_IN_PROBLEMS", False) + return display_name[self.grading_method] @property def debug(self): @@ -585,8 +573,6 @@ def non_editable_metadata_fields(self): ProblemBlock.matlab_api_key, ] ) - if not self.is_grading_method_enabled: - non_editable_fields.append(ProblemBlock.grading_method) return non_editable_fields @property @@ -1851,8 +1837,7 @@ def submit_problem(self, data, override_time=False): current_score = self.score_from_lcp(self.lcp) self.score_history.append(current_score) - if self.is_grading_method_enabled: - current_score = self.get_score_with_grading_method(current_score) + current_score = self.get_score_with_grading_method(current_score) self.set_score(current_score) self.set_last_submission_time() @@ -2329,18 +2314,6 @@ def get_score(self): """ return self.score - def update_correctness(self): - """ - Updates correct map of the LCP. - Operates by creating a new correctness map based on the current - state of the LCP, and updating the old correctness map of the LCP. - """ - # Make sure that the attempt number is always at least 1 for grading purposes, - # even if the number of attempts have been reset and this problem is regraded. - self.lcp.context["attempt"] = max(self.attempts, 1) - new_correct_map = self.lcp.get_grade_from_current_answers(None) - self.lcp.correct_map.update(new_correct_map) - def update_correctness_list(self): """ Updates the `correct_map_history` and the `correct_map` of the LCP. @@ -2363,13 +2336,9 @@ def calculate_score(self): """ Returns the score calculated from the current problem state. - If the grading method is enabled, the score is calculated based on the grading method. + The score is calculated based on the grading method. """ - if self.is_grading_method_enabled: - return self.get_rescore_with_grading_method() - self.update_correctness() - new_score = self.lcp.calculate_score() - return Score(raw_earned=new_score["score"], raw_possible=new_score["total"]) + return self.get_rescore_with_grading_method() def calculate_score_list(self): """ diff --git a/xmodule/tests/test_capa_block.py b/xmodule/tests/test_capa_block.py index 4beb2389f7f6..de2ca01625a0 100644 --- a/xmodule/tests/test_capa_block.py +++ b/xmodule/tests/test_capa_block.py @@ -18,7 +18,6 @@ import requests import webob from codejail.safe_exec import SafeExecException -from django.conf import settings from django.test import override_settings from django.utils.encoding import smart_str from lxml import etree @@ -43,9 +42,6 @@ from ..capa_block import RANDOMIZATION, SHOWANSWER from . import get_test_system -FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS = settings.FEATURES.copy() -FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS["ENABLE_GRADING_METHOD_IN_PROBLEMS"] = True - class CapaFactory: """ @@ -827,16 +823,14 @@ def test_submit_problem_correct(self): # and that this was considered attempt number 2 for grading purposes assert block.lcp.context["attempt"] == 2 - @patch("xmodule.capa_block.ProblemBlock.get_score_with_grading_method") @patch("xmodule.capa.correctmap.CorrectMap.is_correct") @patch("xmodule.capa_block.ProblemBlock.get_problem_html") def test_submit_problem_with_grading_method_disable( - self, mock_html: Mock, mock_is_correct: Mock, mock_get_score: Mock + self, mock_html: Mock, mock_is_correct: Mock ): """ - Test that the grading method is disabled by default. Then, the - `get_score_with_grading_method` method should not be called, and - always the last attempt as the final score. + Test that without a specific grading method, the score behaves as + standard (Last Attempt). """ block = CapaFactory.create(attempts=0, max_attempts=3) mock_html.return_value = "Test HTML" @@ -850,7 +844,6 @@ def test_submit_problem_with_grading_method_disable( assert block.attempts == 1 assert block.lcp.context["attempt"] == 1 assert block.score == Score(raw_earned=1, raw_possible=1) - mock_get_score.assert_not_called() # Second Attempt mock_is_correct.return_value = False @@ -861,7 +854,6 @@ def test_submit_problem_with_grading_method_disable( assert block.attempts == 2 assert block.lcp.context["attempt"] == 2 assert block.score == Score(raw_earned=0, raw_possible=1) - mock_get_score.assert_not_called() # Third Attempt mock_is_correct.return_value = True @@ -872,9 +864,7 @@ def test_submit_problem_with_grading_method_disable( assert block.attempts == 3 assert block.lcp.context["attempt"] == 3 assert block.score == Score(raw_earned=1, raw_possible=1) - mock_get_score.assert_not_called() - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) @patch("xmodule.capa.correctmap.CorrectMap.is_correct") @patch("xmodule.capa_block.ProblemBlock.get_problem_html") def test_submit_problem_with_grading_method_enable(self, mock_html: Mock, mock_is_correct: Mock): @@ -899,126 +889,110 @@ def test_submit_problem_with_grading_method_enable(self, mock_html: Mock, mock_i @patch("xmodule.capa.correctmap.CorrectMap.is_correct") @patch("xmodule.capa_block.ProblemBlock.get_problem_html") - def test_submit_problem_grading_method_disable_to_enable(self, mock_html: Mock, mock_is_correct: Mock): + def test_submit_problem_grading_method_always_enabled(self, mock_html: Mock, mock_is_correct: Mock): """ - Test when the grading method is disabled and then enabled. + Test problem submission when grading method is always enabled by default. - When the grading method is disabled, the final score is always the last attempt. - When the grading method is enabled, the final score is calculated according to the grading method. + The final score is calculated according to the grading method, as grading + is now always enabled. """ - block = CapaFactory.create(attempts=0, max_attempts=4) + block = CapaFactory.create(attempts=0, max_attempts=4, grading_method="highest_score") mock_html.return_value = "Test HTML" - # Disabled grading method - with patch( - "xmodule.capa_block.ProblemBlock.is_grading_method_enabled", new_callable=PropertyMock, return_value=False - ): - # First Attempt - mock_is_correct.return_value = True - get_request_dict = {CapaFactory.input_key(): "3.14"} + # First Attempt + mock_is_correct.return_value = True + get_request_dict = {CapaFactory.input_key(): "3.14"} - block.submit_problem(get_request_dict) + block.submit_problem(get_request_dict) - assert block.attempts == 1 - assert block.lcp.context["attempt"] == 1 - assert block.score == Score(raw_earned=1, raw_possible=1) + assert block.attempts == 1 + assert block.lcp.context["attempt"] == 1 + assert block.score == Score(raw_earned=1, raw_possible=1) - # Second Attempt - mock_is_correct.return_value = False - get_request_dict = {CapaFactory.input_key(): "3.50"} + # Second Attempt + mock_is_correct.return_value = False + get_request_dict = {CapaFactory.input_key(): "3.50"} - block.submit_problem(get_request_dict) + block.submit_problem(get_request_dict) - assert block.attempts == 2 - assert block.lcp.context["attempt"] == 2 - assert block.score == Score(raw_earned=0, raw_possible=1) + assert block.attempts == 2 + assert block.lcp.context["attempt"] == 2 + assert block.score == Score(raw_earned=1, raw_possible=1) - # Enabled grading method - with patch( - "xmodule.capa_block.ProblemBlock.is_grading_method_enabled", new_callable=PropertyMock, return_value=True - ): - # Third Attempt - mock_is_correct.return_value = False - get_request_dict = {CapaFactory.input_key(): "3.96"} + # Third Attempt + mock_is_correct.return_value = False + get_request_dict = {CapaFactory.input_key(): "3.96"} - block.submit_problem(get_request_dict) + block.submit_problem(get_request_dict) - assert block.attempts == 3 - assert block.lcp.context["attempt"] == 3 - assert block.score == Score(raw_earned=0, raw_possible=1) + assert block.attempts == 3 + assert block.lcp.context["attempt"] == 3 + assert block.score == Score(raw_earned=1, raw_possible=1) - # Fourth Attempt - block.grading_method = "highest_score" - mock_is_correct.return_value = False - get_request_dict = {CapaFactory.input_key(): "3.99"} + # Fourth Attempt + block.grading_method = "highest_score" + mock_is_correct.return_value = False + get_request_dict = {CapaFactory.input_key(): "3.99"} - block.submit_problem(get_request_dict) + block.submit_problem(get_request_dict) - assert block.attempts == 4 - assert block.lcp.context["attempt"] == 4 - assert block.score == Score(raw_earned=1, raw_possible=1) + assert block.attempts == 4 + assert block.lcp.context["attempt"] == 4 + assert block.score == Score(raw_earned=1, raw_possible=1) @patch("xmodule.capa.correctmap.CorrectMap.is_correct") @patch("xmodule.capa_block.ProblemBlock.get_problem_html") - def test_submit_problem_grading_method_enable_to_disable(self, mock_html: Mock, mock_is_correct: Mock): + def test_submit_problem_grading_method_always_enabled_highest_score(self, mock_html: Mock, mock_is_correct: Mock): """ - Test when the grading method is enabled and then disabled. + Test problem submission when grading method is always enabled by default + with 'highest_score' grading method. - When the grading method is enabled, the final score is calculated according to the grading method. - When the grading method is disabled, the final score is always the last attempt. + The final score is calculated according to the grading method, as grading + is now always enabled. """ block = CapaFactory.create(attempts=0, max_attempts=4, grading_method="highest_score") mock_html.return_value = "Test HTML" - # Enabled grading method - with patch( - "xmodule.capa_block.ProblemBlock.is_grading_method_enabled", new_callable=PropertyMock, return_value=True - ): - # First Attempt - mock_is_correct.return_value = True - get_request_dict = {CapaFactory.input_key(): "3.14"} + # First Attempt + mock_is_correct.return_value = True + get_request_dict = {CapaFactory.input_key(): "3.14"} - block.submit_problem(get_request_dict) + block.submit_problem(get_request_dict) - assert block.attempts == 1 - assert block.lcp.context["attempt"] == 1 - assert block.score == Score(raw_earned=1, raw_possible=1) + assert block.attempts == 1 + assert block.lcp.context["attempt"] == 1 + assert block.score == Score(raw_earned=1, raw_possible=1) - # Second Attempt - mock_is_correct.return_value = False - get_request_dict = {CapaFactory.input_key(): "3.50"} + # Second Attempt + mock_is_correct.return_value = False + get_request_dict = {CapaFactory.input_key(): "3.50"} - block.submit_problem(get_request_dict) + block.submit_problem(get_request_dict) - assert block.attempts == 2 - assert block.lcp.context["attempt"] == 2 - assert block.score == Score(raw_earned=1, raw_possible=1) + assert block.attempts == 2 + assert block.lcp.context["attempt"] == 2 + assert block.score == Score(raw_earned=1, raw_possible=1) - # Disabled grading method - with patch( - "xmodule.capa_block.ProblemBlock.is_grading_method_enabled", new_callable=PropertyMock, return_value=False - ): - # Third Attempt - mock_is_correct.return_value = False - get_request_dict = {CapaFactory.input_key(): "3.96"} + # Third Attempt + mock_is_correct.return_value = False + get_request_dict = {CapaFactory.input_key(): "3.96"} - block.submit_problem(get_request_dict) + block.submit_problem(get_request_dict) - assert block.attempts == 3 - assert block.lcp.context["attempt"] == 3 - assert block.score == Score(raw_earned=0, raw_possible=1) + assert block.attempts == 3 + assert block.lcp.context["attempt"] == 3 + assert block.score == Score(raw_earned=1, raw_possible=1) - # Fourth Attempt - mock_is_correct.return_value = True - get_request_dict = {CapaFactory.input_key(): "3.14"} + # Fourth Attempt + mock_is_correct.return_value = True + get_request_dict = {CapaFactory.input_key(): "3.14"} - block.submit_problem(get_request_dict) + block.submit_problem(get_request_dict) - assert block.attempts == 4 - assert block.lcp.context["attempt"] == 4 - assert block.score == Score(raw_earned=1, raw_possible=1) + assert block.attempts == 4 + assert block.lcp.context["attempt"] == 4 + assert block.score == Score(raw_earned=1, raw_possible=1) - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) @patch("xmodule.capa.correctmap.CorrectMap.is_correct") @patch("xmodule.capa_block.ProblemBlock.get_problem_html") def test_submit_problem_correct_last_score(self, mock_html: Mock, mock_is_correct: Mock): @@ -1052,7 +1026,6 @@ def test_submit_problem_correct_last_score(self, mock_html: Mock, mock_is_correc assert block.lcp.context["attempt"] == 2 assert block.score == Score(raw_earned=0, raw_possible=1) - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) @patch("xmodule.capa.correctmap.CorrectMap.is_correct") @patch("xmodule.capa_block.ProblemBlock.get_problem_html") def test_submit_problem_correct_highest_score(self, mock_html: Mock, mock_is_correct: Mock): @@ -1085,7 +1058,6 @@ def test_submit_problem_correct_highest_score(self, mock_html: Mock, mock_is_cor assert block.lcp.context["attempt"] == 2 assert block.score == Score(raw_earned=1, raw_possible=1) - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) @patch("xmodule.capa.correctmap.CorrectMap.is_correct") @patch("xmodule.capa_block.ProblemBlock.get_problem_html") def test_submit_problem_correct_first_score(self, mock_html: Mock, mock_is_correct: Mock): @@ -1118,7 +1090,6 @@ def test_submit_problem_correct_first_score(self, mock_html: Mock, mock_is_corre assert block.lcp.context["attempt"] == 2 assert block.score == Score(raw_earned=0, raw_possible=1) - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) @patch("xmodule.capa.correctmap.CorrectMap.is_correct") @patch("xmodule.capa_block.ProblemBlock.get_problem_html") def test_submit_problem_correct_average_score(self, mock_html: Mock, mock_is_correct: Mock): @@ -1648,21 +1619,17 @@ def test_rescore_problem_incorrect(self): # and that this is treated as the first attempt for grading purposes assert block.lcp.context["attempt"] == 1 - @patch("xmodule.capa_block.ProblemBlock.get_rescore_with_grading_method") - def test_rescore_problem_with_grading_method_disable(self, mock_get_rescore: Mock): + def test_rescore_problem_with_grading_method_disable(self): """ - Test the rescore method with grading method disabled. - In this case, the rescore method should not call `get_rescore_with_grading_method` method. + Test the rescore method with grading method logic enabled by default. """ - block = CapaFactory.create(attempts=0, done=True) + block = CapaFactory.create(attempts=0, done=True, grading_method="highest_score") block.rescore(only_if_higher=False) assert block.attempts == 0 assert block.lcp.context["attempt"] == 1 - mock_get_rescore.assert_not_called() - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) def test_rescore_problem_with_grading_method_enable(self): """ Test the rescore method with grading method enabled. @@ -1681,12 +1648,12 @@ def test_rescore_problem_with_grading_method_enable(self): mock_get_rescore.assert_called() @patch("xmodule.capa_block.ProblemBlock.publish_grade") - def test_rescore_problem_grading_method_disable_to_enable(self, mock_publish_grade: Mock): + def test_rescore_problem_grading_method_always_enabled(self, mock_publish_grade: Mock): """ - Test the rescore method the grading method is disabled and then enabled. + Test the rescore method when grading method is always enabled by default. - When the grading method is disabled, the final score is always the last score. - When the grading method is enabled, the final score is the score based on the grading method. + The final score is calculated according to the grading method, as grading + is now always enabled. """ block = CapaFactory.create(attempts=0, max_attempts=3) @@ -1699,54 +1666,39 @@ def test_rescore_problem_grading_method_disable_to_enable(self, mock_publish_gra get_request_dict = {CapaFactory.input_key(): "3.14"} block.submit_problem(get_request_dict) - # Disabled grading method - with patch( - "xmodule.capa_block.ProblemBlock.is_grading_method_enabled", new_callable=PropertyMock, return_value=False - ): - # Score is the last score - assert block.score == Score(raw_earned=1, raw_possible=1) - - block.rescore(only_if_higher=False) - - # Still Score is the last score - mock_publish_grade.assert_called_with(score=Score(raw_earned=1, raw_possible=1), only_if_higher=False) - - # Enabled grading method - with patch( - "xmodule.capa_block.ProblemBlock.is_grading_method_enabled", new_callable=PropertyMock, return_value=True - ): - with patch( - "xmodule.capa.capa_problem.LoncapaProblem.is_grading_method_enabled", - new_callable=PropertyMock, - return_value=True, - ): - # Change grading method to 'first_score' - block.grading_method = "first_score" - block.rescore(only_if_higher=False) + # Score is calculated according to the grading method + assert block.score == Score(raw_earned=1, raw_possible=1) + block.rescore(only_if_higher=False) - mock_publish_grade.assert_called_with(score=Score(raw_earned=0, raw_possible=1), only_if_higher=False) + # Still Score is the last score + mock_publish_grade.assert_called_with(score=Score(raw_earned=1, raw_possible=1), only_if_higher=False) - # Change grading method to 'highest_score' - block.grading_method = "highest_score" - block.rescore(only_if_higher=False) + # Rescore with different grading methods + block.grading_method = "first_score" + block.rescore(only_if_higher=False) + mock_publish_grade.assert_called_with(score=Score(raw_earned=0, raw_possible=1), only_if_higher=False) - mock_publish_grade.assert_called_with(score=Score(raw_earned=1, raw_possible=1), only_if_higher=False) + # Change grading method to 'highest_score' + block.grading_method = "highest_score" + block.rescore(only_if_higher=False) + mock_publish_grade.assert_called_with(score=Score(raw_earned=1, raw_possible=1), only_if_higher=False) - # Change grading method to 'average_score' - block.grading_method = "average_score" - block.rescore(only_if_higher=False) + # Change grading method to 'average_score' + block.grading_method = "average_score" + block.rescore(only_if_higher=False) - mock_publish_grade.assert_called_with( - score=Score(raw_earned=0.33, raw_possible=1), only_if_higher=False - ) + mock_publish_grade.assert_called_with( + score=Score(raw_earned=0.33, raw_possible=1), only_if_higher=False + ) @patch("xmodule.capa_block.ProblemBlock.publish_grade") - def test_rescore_problem_grading_method_enable_to_disable(self, mock_publish_grade: Mock): + def test_rescore_problem_grading_method_always_enabled_with_various_methods(self, mock_publish_grade: Mock): """ - Test the rescore method the grading method is enabled and then disabled. + Test the rescore method when grading method is always enabled by default + with different grading methods. - When the grading method is enabled, the final score is the score based on the grading method. - When the grading method is disabled, the final score is always the last score. + The final score is calculated according to the grading method, as grading + is now always enabled. """ block = CapaFactory.create(attempts=0, max_attempts=3) @@ -1759,48 +1711,28 @@ def test_rescore_problem_grading_method_enable_to_disable(self, mock_publish_gra get_request_dict = {CapaFactory.input_key(): "3.14"} block.submit_problem(get_request_dict) - # Enabled grading method - with patch( - "xmodule.capa_block.ProblemBlock.is_grading_method_enabled", new_callable=PropertyMock, return_value=True - ): - with patch( - "xmodule.capa.capa_problem.LoncapaProblem.is_grading_method_enabled", - new_callable=PropertyMock, - return_value=True, - ): - # Grading method is 'last_score' - assert block.grading_method == "last_score" - assert block.score == Score(raw_earned=1, raw_possible=1) - - # Change grading method to 'first_score' - block.grading_method = "first_score" - block.rescore(only_if_higher=False) - - mock_publish_grade.assert_called_with(score=Score(raw_earned=0, raw_possible=1), only_if_higher=False) - - # Change grading method to 'highest_score' - block.grading_method = "highest_score" - block.rescore(only_if_higher=False) + # Grading method is 'last_score' by default + assert block.grading_method == "last_score" + assert block.score == Score(raw_earned=1, raw_possible=1) - mock_publish_grade.assert_called_with(score=Score(raw_earned=1, raw_possible=1), only_if_higher=False) + # Change grading method to 'first_score' + block.grading_method = "first_score" + block.rescore(only_if_higher=False) + mock_publish_grade.assert_called_with(score=Score(raw_earned=0, raw_possible=1), only_if_higher=False) - # Change grading method to 'average_score' - block.grading_method = "average_score" - block.rescore(only_if_higher=False) + # Change grading method to 'highest_score' + block.grading_method = "highest_score" + block.rescore(only_if_higher=False) + mock_publish_grade.assert_called_with(score=Score(raw_earned=1, raw_possible=1), only_if_higher=False) - mock_publish_grade.assert_called_with( - score=Score(raw_earned=0.33, raw_possible=1), only_if_higher=False - ) + # Change grading method to 'average_score' + block.grading_method = "average_score" + block.rescore(only_if_higher=False) + mock_publish_grade.assert_called_with(score=Score(raw_earned=0.33, raw_possible=1), only_if_higher=False) - # Disabled grading method - with patch( - "xmodule.capa_block.ProblemBlock.is_grading_method_enabled", new_callable=PropertyMock, return_value=False - ): - block.rescore(only_if_higher=False) - # The score is the last score - assert block.score == Score(raw_earned=1, raw_possible=1) + block.rescore(only_if_higher=False) + assert block.score == Score(raw_earned=1, raw_possible=1) - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) @patch("xmodule.capa_block.ProblemBlock.publish_grade") def test_rescore_problem_update_grading_method(self, mock_publish_grade: Mock): """ @@ -1930,7 +1862,6 @@ def test_update_correctness_list_without_history(self): self.assertEqual(block.lcp.context["attempt"], 1) block.lcp.get_grade_from_current_answers.assert_not_called() - @override_settings(FEATURES=FEATURES_WITH_GRADING_METHOD_IN_PROBLEMS) def test_get_rescore_with_grading_method(self): """ Test that the `get_rescore_with_grading_method` method returns the correct score. @@ -3957,9 +3888,10 @@ def test_rerandomized_inputs(self): def test_file_inputs(self, mock_xqueue_post): fnames = ["prog1.py", "prog2.py", "prog3.py"] fpaths = [os.path.join(DATA_DIR, "capa", fname) for fname in fnames] - fileobjs = [open(fpath) for fpath in fpaths] - for fileobj in fileobjs: - self.addCleanup(fileobj.close) + fileobjs = [] + for fpath in fpaths: + with open(fpath, encoding="utf-8") as f: + fileobjs.append(f.read()) factory = CapaFactoryWithFiles block = factory.create() @@ -3976,7 +3908,7 @@ def test_file_inputs(self, mock_xqueue_post): assert event["submission"] == { factory.answer_key(2): { "question": "", - "answer": fpaths, + "answer": fileobjs, "response_type": "coderesponse", "input_type": "filesubmission", "correct": False,