Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
af087291
Commit
af087291
authored
Jul 20, 2017
by
George Song
Committed by
GitHub
Jul 20, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #15623 from edx/george/enrollments-prereq
Fixed milestone issues
parents
abc503ba
fc5716d9
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
60 additions
and
12 deletions
+60
-12
AUTHORS
+1
-0
cms/djangoapps/contentstore/tests/test_course_settings.py
+28
-0
cms/djangoapps/contentstore/views/course.py
+7
-0
common/djangoapps/util/milestones_helpers.py
+6
-3
common/djangoapps/util/tests/test_milestones_helpers.py
+18
-9
No files found.
AUTHORS
View file @
af087291
...
@@ -259,6 +259,7 @@ Muhammad Rehan <muhammadrehan69@gmail.com>
...
@@ -259,6 +259,7 @@ Muhammad Rehan <muhammadrehan69@gmail.com>
Shawn Milochik <shawn@milochik.com>
Shawn Milochik <shawn@milochik.com>
Afeef Janjua <janjua.afeef@gmail.com>
Afeef Janjua <janjua.afeef@gmail.com>
Jacek Bzdak <jbzdak@gmail.com>
Jacek Bzdak <jbzdak@gmail.com>
Ahmed Jazzar <ajazzar@qrf.org>
Jillian Vogel <pomegranited@gmail.com>
Jillian Vogel <pomegranited@gmail.com>
Dan Powell <dan@abakas.com>
Dan Powell <dan@abakas.com>
Mariana Araújo <simbelm.ne@gmail.com>
Mariana Araújo <simbelm.ne@gmail.com>
...
...
cms/djangoapps/contentstore/tests/test_course_settings.py
View file @
af087291
...
@@ -15,6 +15,7 @@ from milestones.tests.utils import MilestonesTestCaseMixin
...
@@ -15,6 +15,7 @@ from milestones.tests.utils import MilestonesTestCaseMixin
from
mock
import
Mock
,
patch
from
mock
import
Mock
,
patch
from
contentstore.utils
import
reverse_course_url
,
reverse_usage_url
from
contentstore.utils
import
reverse_course_url
,
reverse_usage_url
from
milestones.models
import
MilestoneRelationshipType
from
models.settings.course_grading
import
CourseGradingModel
,
GRADING_POLICY_CHANGED_EVENT_TYPE
,
hash_grading_policy
from
models.settings.course_grading
import
CourseGradingModel
,
GRADING_POLICY_CHANGED_EVENT_TYPE
,
hash_grading_policy
from
models.settings.course_metadata
import
CourseMetadata
from
models.settings.course_metadata
import
CourseMetadata
from
models.settings.encoder
import
CourseSettingsEncoder
from
models.settings.encoder
import
CourseSettingsEncoder
...
@@ -22,6 +23,7 @@ from openedx.core.djangoapps.models.course_details import CourseDetails
...
@@ -22,6 +23,7 @@ from openedx.core.djangoapps.models.course_details import CourseDetails
from
openedx.core.djangoapps.self_paced.models
import
SelfPacedConfiguration
from
openedx.core.djangoapps.self_paced.models
import
SelfPacedConfiguration
from
student.roles
import
CourseInstructorRole
,
CourseStaffRole
from
student.roles
import
CourseInstructorRole
,
CourseStaffRole
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
UserFactory
from
util
import
milestones_helpers
from
xblock_django.models
import
XBlockStudioConfigurationFlag
from
xblock_django.models
import
XBlockStudioConfigurationFlag
from
xmodule.fields
import
Date
from
xmodule.fields
import
Date
from
xmodule.modulestore
import
ModuleStoreEnum
from
xmodule.modulestore
import
ModuleStoreEnum
...
@@ -100,6 +102,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
...
@@ -100,6 +102,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
resp
=
self
.
client
.
ajax_post
(
url
,
payload
)
resp
=
self
.
client
.
ajax_post
(
url
,
payload
)
self
.
compare_details_with_encoding
(
json
.
loads
(
resp
.
content
),
details
.
__dict__
,
field
+
str
(
val
))
self
.
compare_details_with_encoding
(
json
.
loads
(
resp
.
content
),
details
.
__dict__
,
field
+
str
(
val
))
MilestoneRelationshipType
.
objects
.
get_or_create
(
name
=
'requires'
)
MilestoneRelationshipType
.
objects
.
get_or_create
(
name
=
'fulfills'
)
@staticmethod
@staticmethod
def
convert_datetime_to_iso
(
datetime_obj
):
def
convert_datetime_to_iso
(
datetime_obj
):
"""
"""
...
@@ -172,6 +177,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
...
@@ -172,6 +177,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
@mock.patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
})
@mock.patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
})
def
test_pre_requisite_course_update_and_fetch
(
self
):
def
test_pre_requisite_course_update_and_fetch
(
self
):
self
.
assertFalse
(
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
self
.
user
.
id
),
msg
=
'The initial empty state should be: no prerequisite courses'
)
url
=
get_url
(
self
.
course
.
id
)
url
=
get_url
(
self
.
course
.
id
)
resp
=
self
.
client
.
get_json
(
url
)
resp
=
self
.
client
.
get_json
(
url
)
course_detail_json
=
json
.
loads
(
resp
.
content
)
course_detail_json
=
json
.
loads
(
resp
.
content
)
...
@@ -190,6 +198,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
...
@@ -190,6 +198,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
course_detail_json
=
json
.
loads
(
resp
.
content
)
course_detail_json
=
json
.
loads
(
resp
.
content
)
self
.
assertEqual
(
pre_requisite_course_keys
,
course_detail_json
[
'pre_requisite_courses'
])
self
.
assertEqual
(
pre_requisite_course_keys
,
course_detail_json
[
'pre_requisite_courses'
])
self
.
assertTrue
(
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
self
.
user
.
id
),
msg
=
'Should have prerequisite courses'
)
# remove pre requisite course
# remove pre requisite course
course_detail_json
[
'pre_requisite_courses'
]
=
[]
course_detail_json
[
'pre_requisite_courses'
]
=
[]
self
.
client
.
ajax_post
(
url
,
course_detail_json
)
self
.
client
.
ajax_post
(
url
,
course_detail_json
)
...
@@ -197,6 +208,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
...
@@ -197,6 +208,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
course_detail_json
=
json
.
loads
(
resp
.
content
)
course_detail_json
=
json
.
loads
(
resp
.
content
)
self
.
assertEqual
([],
course_detail_json
[
'pre_requisite_courses'
])
self
.
assertEqual
([],
course_detail_json
[
'pre_requisite_courses'
])
self
.
assertFalse
(
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
self
.
user
.
id
),
msg
=
'Should not have prerequisite courses anymore'
)
@mock.patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
})
@mock.patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENABLE_PREREQUISITE_COURSES'
:
True
})
def
test_invalid_pre_requisite_course
(
self
):
def
test_invalid_pre_requisite_course
(
self
):
url
=
get_url
(
self
.
course
.
id
)
url
=
get_url
(
self
.
course
.
id
)
...
@@ -268,6 +282,14 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
...
@@ -268,6 +282,14 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
@unittest.skipUnless
(
settings
.
FEATURES
.
get
(
'ENTRANCE_EXAMS'
,
False
),
True
)
@unittest.skipUnless
(
settings
.
FEATURES
.
get
(
'ENTRANCE_EXAMS'
,
False
),
True
)
def
test_entrance_exam_created_updated_and_deleted_successfully
(
self
):
def
test_entrance_exam_created_updated_and_deleted_successfully
(
self
):
"""
This tests both of the entrance exam settings and the `any_unfulfilled_milestones` helper.
Splitting the test requires significant refactoring `settings_handler()` view.
"""
self
.
assertFalse
(
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
self
.
user
.
id
),
msg
=
'The initial empty state should be: no entrance exam'
)
settings_details_url
=
get_url
(
self
.
course
.
id
)
settings_details_url
=
get_url
(
self
.
course
.
id
)
data
=
{
data
=
{
'entrance_exam_enabled'
:
'true'
,
'entrance_exam_enabled'
:
'true'
,
...
@@ -299,6 +321,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
...
@@ -299,6 +321,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
self
.
assertTrue
(
course
.
entrance_exam_enabled
)
self
.
assertTrue
(
course
.
entrance_exam_enabled
)
self
.
assertEquals
(
course
.
entrance_exam_minimum_score_pct
,
.
80
)
self
.
assertEquals
(
course
.
entrance_exam_minimum_score_pct
,
.
80
)
self
.
assertTrue
(
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
self
.
user
.
id
),
msg
=
'The entrance exam should be required.'
)
# Delete the entrance exam
# Delete the entrance exam
data
[
'entrance_exam_enabled'
]
=
"false"
data
[
'entrance_exam_enabled'
]
=
"false"
response
=
self
.
client
.
post
(
response
=
self
.
client
.
post
(
...
@@ -312,6 +337,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
...
@@ -312,6 +337,9 @@ class CourseDetailsViewTest(CourseTestCase, MilestonesTestCaseMixin):
self
.
assertFalse
(
course
.
entrance_exam_enabled
)
self
.
assertFalse
(
course
.
entrance_exam_enabled
)
self
.
assertEquals
(
course
.
entrance_exam_minimum_score_pct
,
None
)
self
.
assertEquals
(
course
.
entrance_exam_minimum_score_pct
,
None
)
self
.
assertFalse
(
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
self
.
user
.
id
),
msg
=
'The entrance exam should not be required anymore'
)
@unittest.skipUnless
(
settings
.
FEATURES
.
get
(
'ENTRANCE_EXAMS'
,
False
),
True
)
@unittest.skipUnless
(
settings
.
FEATURES
.
get
(
'ENTRANCE_EXAMS'
,
False
),
True
)
def
test_entrance_exam_store_default_min_score
(
self
):
def
test_entrance_exam_store_default_min_score
(
self
):
"""
"""
...
...
cms/djangoapps/contentstore/views/course.py
View file @
af087291
...
@@ -51,6 +51,7 @@ from course_action_state.managers import CourseActionStateItemNotFoundError
...
@@ -51,6 +51,7 @@ from course_action_state.managers import CourseActionStateItemNotFoundError
from
course_action_state.models
import
CourseRerunState
,
CourseRerunUIStateManager
from
course_action_state.models
import
CourseRerunState
,
CourseRerunUIStateManager
from
course_creators.views
import
add_user_with_status_unrequested
,
get_course_creator_status
from
course_creators.views
import
add_user_with_status_unrequested
,
get_course_creator_status
from
edxmako.shortcuts
import
render_to_response
from
edxmako.shortcuts
import
render_to_response
from
milestones
import
api
as
milestones_api
from
models.settings.course_grading
import
CourseGradingModel
from
models.settings.course_grading
import
CourseGradingModel
from
models.settings.course_metadata
import
CourseMetadata
from
models.settings.course_metadata
import
CourseMetadata
from
models.settings.encoder
import
CourseSettingsEncoder
from
models.settings.encoder
import
CourseSettingsEncoder
...
@@ -73,6 +74,7 @@ from util.milestones_helpers import (
...
@@ -73,6 +74,7 @@ from util.milestones_helpers import (
is_entrance_exams_enabled
,
is_entrance_exams_enabled
,
is_prerequisite_courses_enabled
,
is_prerequisite_courses_enabled
,
is_valid_course_key
,
is_valid_course_key
,
remove_prerequisite_course
,
set_prerequisite_courses
set_prerequisite_courses
)
)
from
util.organizations_helpers
import
add_organization_course
,
get_organization_by_short_name
,
organizations_enabled
from
util.organizations_helpers
import
add_organization_course
,
get_organization_by_short_name
,
organizations_enabled
...
@@ -1075,6 +1077,11 @@ def settings_handler(request, course_key_string):
...
@@ -1075,6 +1077,11 @@ def settings_handler(request, course_key_string):
if
not
all
(
is_valid_course_key
(
course_key
)
for
course_key
in
prerequisite_course_keys
):
if
not
all
(
is_valid_course_key
(
course_key
)
for
course_key
in
prerequisite_course_keys
):
return
JsonResponseBadRequest
({
"error"
:
_
(
"Invalid prerequisite course key"
)})
return
JsonResponseBadRequest
({
"error"
:
_
(
"Invalid prerequisite course key"
)})
set_prerequisite_courses
(
course_key
,
prerequisite_course_keys
)
set_prerequisite_courses
(
course_key
,
prerequisite_course_keys
)
else
:
# None is chosen, so remove the course prerequisites
course_milestones
=
milestones_api
.
get_course_milestones
(
course_key
=
course_key
,
relationship
=
"requires"
)
for
milestone
in
course_milestones
:
remove_prerequisite_course
(
course_key
,
milestone
)
# If the entrance exams feature has been enabled, we'll need to check for some
# If the entrance exams feature has been enabled, we'll need to check for some
# feature-specific settings and handle them accordingly
# feature-specific settings and handle them accordingly
...
...
common/djangoapps/util/milestones_helpers.py
View file @
af087291
...
@@ -384,9 +384,12 @@ def any_unfulfilled_milestones(course_id, user_id):
...
@@ -384,9 +384,12 @@ def any_unfulfilled_milestones(course_id, user_id):
""" Returns a boolean if user has any unfulfilled milestones """
""" Returns a boolean if user has any unfulfilled milestones """
if
not
settings
.
FEATURES
.
get
(
'MILESTONES_APP'
):
if
not
settings
.
FEATURES
.
get
(
'MILESTONES_APP'
):
return
False
return
False
return
bool
(
get_course_milestones_fulfillment_paths
(
course_id
,
{
"id"
:
user_id
})
fulfillment_paths
=
milestones_api
.
get_course_milestones_fulfillment_paths
(
course_id
,
{
'id'
:
user_id
})
)
# Returns True if any of the milestones is unfulfilled. False if
# values is empty or all values are.
return
any
(
fulfillment_paths
.
values
())
def
get_course_milestones_fulfillment_paths
(
course_id
,
user_id
):
def
get_course_milestones_fulfillment_paths
(
course_id
,
user_id
):
...
...
common/djangoapps/util/tests/test_milestones_helpers.py
View file @
af087291
...
@@ -3,15 +3,18 @@ Tests for the milestones helpers library, which is the integration point for the
...
@@ -3,15 +3,18 @@ Tests for the milestones helpers library, which is the integration point for the
"""
"""
import
ddt
import
ddt
from
django.conf
import
settings
from
milestones.exceptions
import
InvalidCourseKeyException
,
InvalidUserException
from
milestones.exceptions
import
InvalidCourseKeyException
,
InvalidUserException
from
mock
import
patch
from
mock
import
patch
from
util
import
milestones_helpers
from
milestones
import
api
as
milestones_api
from
milestones.models
import
MilestoneRelationshipType
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
util
import
milestones_helpers
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'MILESTONES_APP'
:
False
})
@patch.dict
(
settings
.
FEATURES
,
{
'MILESTONES_APP'
:
False
})
@ddt.ddt
@ddt.ddt
class
MilestonesHelpersTestCase
(
ModuleStoreTestCase
):
class
MilestonesHelpersTestCase
(
ModuleStoreTestCase
):
"""
"""
...
@@ -33,11 +36,14 @@ class MilestonesHelpersTestCase(ModuleStoreTestCase):
...
@@ -33,11 +36,14 @@ class MilestonesHelpersTestCase(ModuleStoreTestCase):
self
.
user
=
{
'id'
:
'123'
}
self
.
user
=
{
'id'
:
'123'
}
self
.
milestone
=
{
self
.
milestone
=
milestones_api
.
add_milestone
(
{
'name'
:
'Test Milestone'
,
'name'
:
'Test Milestone'
,
'namespace'
:
'doesnt.matter'
,
'namespace'
:
'doesnt.matter'
,
'description'
:
'Testing Milestones Helpers Library'
,
'description'
:
'Testing Milestones Helpers Library'
,
}
})
MilestoneRelationshipType
.
objects
.
get_or_create
(
name
=
'requires'
)
MilestoneRelationshipType
.
objects
.
get_or_create
(
name
=
'fulfills'
)
@ddt.data
(
@ddt.data
(
(
False
,
False
,
False
),
(
False
,
False
,
False
),
...
@@ -115,13 +121,16 @@ class MilestonesHelpersTestCase(ModuleStoreTestCase):
...
@@ -115,13 +121,16 @@ class MilestonesHelpersTestCase(ModuleStoreTestCase):
response
=
milestones_helpers
.
get_service
()
response
=
milestones_helpers
.
get_service
()
self
.
assertIsNone
(
response
)
self
.
assertIsNone
(
response
)
@patch.dict
(
'django.conf.settings.FEATURES'
,
{
'MILESTONES_APP'
:
True
})
@patch.dict
(
settings
.
FEATURES
,
{
'MILESTONES_APP'
:
True
})
def
test_any_unfulfilled_milestones
(
self
):
def
test_any_unfulfilled_milestones
(
self
):
"""
"""
Tests any_unfulfilled_milestones for invalid arguments with
Tests any_unfulfilled_milestones for invalid arguments with the app enabled.
the app enabled
"""
"""
# Should not raise any exceptions
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
self
.
user
[
'id'
])
with
self
.
assertRaises
(
InvalidCourseKeyException
):
with
self
.
assertRaises
(
InvalidCourseKeyException
):
milestones_helpers
.
any_unfulfilled_milestones
(
None
,
self
.
user
)
milestones_helpers
.
any_unfulfilled_milestones
(
None
,
self
.
user
[
'id'
]
)
with
self
.
assertRaises
(
InvalidUserException
):
with
self
.
assertRaises
(
InvalidUserException
):
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
None
)
milestones_helpers
.
any_unfulfilled_milestones
(
self
.
course
.
id
,
None
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment