fix: prevent None entrance_exam_minimum_score_pct from breaking CourseOverview sync (#37339)
* fix: prevent None entrance_exam_minimum_score_pct from breaking CourseOverview sync When entrance exams are disabled in Studio, the field `entrance_exam_minimum_score_pct` was set to `None`. This caused silent failures when saving `CourseOverview` because the database column requires a float (NOT NULL). This patch ensures that: - CourseOverview sanitizes None values by falling back to `settings.ENTRANCE_EXAM_MIN_SCORE_PCT` (default=50). - Studio avoids writing `None` and instead applies the configured default. Impact: - Prevents IntegrityErrors and silent failures when updating course settings. - Restores proper syncing between modulestore (Mongo) and CourseOverview (MySQL). - Fixes reported issues such as display name changes not persisting and course start dates not syncing. Closes: https://github.com/openedx/edx-platform/issues/37319# * refactor: clean up entrance_exam_minimum_score_pct handling - Consolidate logic to avoid repeated assignments - Centralize None fallback and int/float normalization - Improve readability with inline comment and consistency with Open edX style * test: update entrance exam deletion test to expect default min score - Adjusted `test_entrance_exam_created_updated_and_deleted_successfully` to check for `settings.ENTRANCE_EXAM_MIN_SCORE_PCT` instead of `None` after exam deletion - Added handling for both int and float defaults (`/100` for integer case)
This commit is contained in:
committed by
GitHub
parent
e46cfa6b32
commit
e5b497cbba
@@ -501,7 +501,11 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
|
||||
course = modulestore().get_course(self.course.id)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertFalse(course.entrance_exam_enabled)
|
||||
self.assertEqual(course.entrance_exam_minimum_score_pct, None)
|
||||
entrance_exam_minimum_score_pct = float(settings.ENTRANCE_EXAM_MIN_SCORE_PCT)
|
||||
if entrance_exam_minimum_score_pct.is_integer():
|
||||
entrance_exam_minimum_score_pct = entrance_exam_minimum_score_pct / 100
|
||||
|
||||
self.assertEqual(course.entrance_exam_minimum_score_pct, entrance_exam_minimum_score_pct)
|
||||
|
||||
self.assertFalse(milestones_helpers.any_unfulfilled_milestones(self.course.id, self.user.id),
|
||||
msg='The entrance exam should not be required anymore')
|
||||
|
||||
@@ -224,7 +224,7 @@ def _delete_entrance_exam(request, course_key):
|
||||
if course.entrance_exam_id:
|
||||
metadata = {
|
||||
'entrance_exam_enabled': False,
|
||||
'entrance_exam_minimum_score_pct': None,
|
||||
'entrance_exam_minimum_score_pct': _get_default_entrance_exam_minimum_pct(),
|
||||
'entrance_exam_id': None,
|
||||
}
|
||||
CourseMetadata.update_from_dict(metadata, course, request.user)
|
||||
|
||||
@@ -266,10 +266,20 @@ class CourseOverview(TimeStampedModel):
|
||||
course_overview.entrance_exam_id = course.entrance_exam_id or ''
|
||||
# Despite it being a float, the course object defaults to an int. So we will detect that case and update
|
||||
# it to be a float like everything else.
|
||||
if isinstance(course.entrance_exam_minimum_score_pct, int):
|
||||
course_overview.entrance_exam_minimum_score_pct = course.entrance_exam_minimum_score_pct / 100
|
||||
# Extra handling: entrance_exam_minimum_score_pct can be None (e.g. when exams are disabled in Studio),
|
||||
# so we fall back to settings.ENTRANCE_EXAM_MIN_SCORE_PCT to prevent CourseOverview save failures.
|
||||
if course.entrance_exam_minimum_score_pct is None:
|
||||
entrance_exam_minimum_score_pct = float(settings.ENTRANCE_EXAM_MIN_SCORE_PCT)
|
||||
else:
|
||||
course_overview.entrance_exam_minimum_score_pct = course.entrance_exam_minimum_score_pct
|
||||
entrance_exam_minimum_score_pct = course.entrance_exam_minimum_score_pct
|
||||
|
||||
if (
|
||||
isinstance(entrance_exam_minimum_score_pct, int)
|
||||
or (isinstance(entrance_exam_minimum_score_pct, float) and entrance_exam_minimum_score_pct.is_integer())
|
||||
):
|
||||
entrance_exam_minimum_score_pct = entrance_exam_minimum_score_pct / 100
|
||||
|
||||
course_overview.entrance_exam_minimum_score_pct = entrance_exam_minimum_score_pct
|
||||
|
||||
course_overview.force_on_flexible_peer_openassessments = course.force_on_flexible_peer_openassessments
|
||||
|
||||
|
||||
Reference in New Issue
Block a user