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
9e9c99ac
Commit
9e9c99ac
authored
Mar 21, 2017
by
Ahsan Ulhaq
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add eligible for financial aid and sync with course discovery
ECOM-7532
parent
82dea974
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
118 additions
and
45 deletions
+118
-45
lms/djangoapps/courseware/tests/test_views.py
+53
-32
lms/djangoapps/courseware/views/views.py
+23
-10
openedx/core/djangoapps/catalog/management/commands/sync_course_runs.py
+10
-0
openedx/core/djangoapps/catalog/management/commands/tests/test_sync_course_runs.py
+11
-3
openedx/core/djangoapps/catalog/tests/factories.py
+1
-0
openedx/core/djangoapps/content/course_overviews/migrations/0012_courseoverview_eligible_for_financial_aid.py
+19
-0
openedx/core/djangoapps/content/course_overviews/models.py
+1
-0
No files found.
lms/djangoapps/courseware/tests/test_views.py
View file @
9e9c99ac
...
...
@@ -49,9 +49,10 @@ from courseware.user_state_client import DjangoXBlockUserStateClient
from
courseware.views.index
import
render_accordion
from
lms.djangoapps.commerce.utils
import
EcommerceService
# pylint: disable=import-error
from
milestones.tests.utils
import
MilestonesTestCaseMixin
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
from
openedx.core.djangoapps.crawlers.models
import
CrawlersConfig
from
openedx.core.djangoapps.self_paced.models
import
SelfPacedConfiguration
from
openedx.core.lib.gating
import
api
as
gating_api
from
openedx.core.djangoapps.crawlers.models
import
CrawlersConfig
from
student.models
import
CourseEnrollment
from
student.tests.factories
import
AdminFactory
,
UserFactory
,
CourseEnrollmentFactory
from
util.tests.mixins.enterprise
import
EnterpriseTestConsentRequired
...
...
@@ -808,44 +809,64 @@ class ViewsTestCase(ModuleStoreTestCase):
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertIn
(
'Financial Assistance Application'
,
response
.
content
)
@ddt.data
(([
CourseMode
.
AUDIT
,
CourseMode
.
VERIFIED
],
CourseMode
.
AUDIT
,
True
,
datetime
.
now
(
UTC
)
-
timedelta
(
days
=
1
)),
([
CourseMode
.
AUDIT
,
CourseMode
.
VERIFIED
],
CourseMode
.
VERIFIED
,
True
,
None
),
([
CourseMode
.
AUDIT
,
CourseMode
.
VERIFIED
],
CourseMode
.
AUDIT
,
False
,
None
),
([
CourseMode
.
AUDIT
],
CourseMode
.
AUDIT
,
False
,
None
))
@ddt.unpack
def
test_financial_assistance_form_course_exclusion
(
self
,
course_modes
,
enrollment_mode
,
eligible_for_aid
,
expiration
):
"""Verify that learner cannot get the financial aid for the courses having one of the
following attributes:
1. User is enrolled in the verified mode.
2. Course is expired.
3. Course does not provide financial assistance.
4. Course does not have verified mode.
"""
# Create course
course
=
CourseFactory
.
create
()
# Create Course Modes
for
mode
in
course_modes
:
CourseModeFactory
.
create
(
mode_slug
=
mode
,
course_id
=
course
.
id
,
expiration_datetime
=
expiration
)
# Enroll user in the course
CourseEnrollmentFactory
(
course_id
=
course
.
id
,
user
=
self
.
user
,
mode
=
enrollment_mode
)
# load course into course overview
CourseOverview
.
get_from_id
(
course
.
id
)
# add whether course is eligible for financial aid or not
course_overview
=
CourseOverview
.
objects
.
get
(
id
=
course
.
id
)
course_overview
.
eligible_for_financial_aid
=
eligible_for_aid
course_overview
.
save
()
url
=
reverse
(
'financial_assistance_form'
)
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertNotIn
(
str
(
course
.
id
),
response
.
content
)
def
test_financial_assistance_form
(
self
):
non_verified_course
=
CourseFactory
.
create
()
.
id
verified_course_verified_track
=
CourseFactory
.
create
()
.
id
verified_course_audit_track
=
CourseFactory
.
create
()
.
id
verified_course_deadline_passed
=
CourseFactory
.
create
()
.
id
unenrolled_course
=
CourseFactory
.
create
()
.
id
enrollments
=
(
(
non_verified_course
,
CourseMode
.
AUDIT
,
None
),
(
verified_course_verified_track
,
CourseMode
.
VERIFIED
,
None
),
(
verified_course_audit_track
,
CourseMode
.
AUDIT
,
None
),
(
verified_course_deadline_passed
,
CourseMode
.
AUDIT
,
datetime
.
now
(
UTC
)
-
timedelta
(
days
=
1
))
)
for
course
,
mode
,
expiration
in
enrollments
:
"""Verify that learner can get the financial aid for the course in which
he/she is enrolled in audit mode whereas the course provide verified mode.
"""
# Create course
course
=
CourseFactory
.
create
()
.
id
# Create Course Modes
CourseModeFactory
.
create
(
mode_slug
=
CourseMode
.
AUDIT
,
course_id
=
course
)
if
course
!=
non_verified_course
:
CourseModeFactory
.
create
(
mode_slug
=
CourseMode
.
VERIFIED
,
course_id
=
course
,
expiration_datetime
=
expiration
)
CourseEnrollmentFactory
(
course_id
=
course
,
user
=
self
.
user
,
mode
=
mode
)
CourseModeFactory
.
create
(
mode_slug
=
CourseMode
.
VERIFIED
,
course_id
=
course
)
# Enroll user in the course
CourseEnrollmentFactory
(
course_id
=
course
,
user
=
self
.
user
,
mode
=
CourseMode
.
AUDIT
)
# load course into course overview
CourseOverview
.
get_from_id
(
course
)
url
=
reverse
(
'financial_assistance_form'
)
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# Ensure that the user can only apply for assistance in
# courses which have a verified mode which hasn't expired yet,
# where the user is not already enrolled in verified mode
self
.
assertIn
(
str
(
verified_course_audit_track
),
response
.
content
)
for
course
in
(
non_verified_course
,
verified_course_verified_track
,
verified_course_deadline_passed
,
unenrolled_course
):
self
.
assertNotIn
(
str
(
course
),
response
.
content
)
self
.
assertIn
(
str
(
course
),
response
.
content
)
def
_submit_financial_assistance_form
(
self
,
data
):
"""Submit a financial assistance request."""
...
...
lms/djangoapps/courseware/views/views.py
View file @
9e9c99ac
...
...
@@ -1536,16 +1536,7 @@ def financial_assistance_request(request):
def
financial_assistance_form
(
request
):
"""Render the financial assistance application form page."""
user
=
request
.
user
enrolled_courses
=
[
{
'name'
:
enrollment
.
course_overview
.
display_name
,
'value'
:
unicode
(
enrollment
.
course_id
)}
for
enrollment
in
CourseEnrollment
.
enrollments_for_user
(
user
)
.
order_by
(
'-created'
)
if
enrollment
.
mode
!=
CourseMode
.
VERIFIED
and
CourseMode
.
objects
.
filter
(
Q
(
_expiration_datetime__isnull
=
True
)
|
Q
(
_expiration_datetime__gt
=
datetime
.
now
(
UTC
())),
course_id
=
enrollment
.
course_id
,
mode_slug
=
CourseMode
.
VERIFIED
)
.
exists
()
]
enrolled_courses
=
get_financial_aid_courses
(
user
)
incomes
=
[
'Less than $5,000'
,
'$5,000 - $10,000'
,
'$10,000 - $15,000'
,
'$15,000 - $20,000'
,
'$20,000 - $25,000'
]
annual_incomes
=
[
{
'name'
:
_
(
income
),
'value'
:
income
}
for
income
in
incomes
# pylint: disable=translation-of-non-string
...
...
@@ -1642,3 +1633,25 @@ def financial_assistance_form(request):
}
],
})
def
get_financial_aid_courses
(
user
):
""" Retrieve the courses eligible for financial assistance. """
financial_aid_courses
=
[]
for
enrollment
in
CourseEnrollment
.
enrollments_for_user
(
user
)
.
order_by
(
'-created'
):
if
enrollment
.
mode
!=
CourseMode
.
VERIFIED
and
\
enrollment
.
course_overview
.
eligible_for_financial_aid
and
\
CourseMode
.
objects
.
filter
(
Q
(
_expiration_datetime__isnull
=
True
)
|
Q
(
_expiration_datetime__gt
=
datetime
.
now
(
UTC
())),
course_id
=
enrollment
.
course_id
,
mode_slug
=
CourseMode
.
VERIFIED
)
.
exists
():
financial_aid_courses
.
append
(
{
'name'
:
enrollment
.
course_overview
.
display_name
,
'value'
:
unicode
(
enrollment
.
course_id
)
}
)
return
financial_aid_courses
openedx/core/djangoapps/catalog/management/commands/sync_course_runs.py
View file @
9e9c99ac
...
...
@@ -35,7 +35,9 @@ class Command(BaseCommand):
course_metadata_updated
=
0
for
course_run
in
course_runs
:
is_course_metadata_updated
=
False
marketing_url
=
course_run
[
'marketing_url'
]
eligible_for_financial_aid
=
course_run
[
'eligible_for_financial_aid'
]
course_key
=
CourseKey
.
from_string
(
course_run
[
'key'
])
try
:
course_overview
=
CourseOverview
.
objects
.
get
(
id
=
course_key
)
...
...
@@ -50,6 +52,14 @@ class Command(BaseCommand):
# Check whether course overview's marketing url is outdated - this saves a db hit.
if
course_overview
.
marketing_url
!=
marketing_url
:
course_overview
.
marketing_url
=
marketing_url
is_course_metadata_updated
=
True
# Check whether course overview's eligible for financial aid is outdated
if
course_overview
.
eligible_for_financial_aid
!=
eligible_for_financial_aid
:
course_overview
.
eligible_for_financial_aid
=
eligible_for_financial_aid
is_course_metadata_updated
=
True
if
is_course_metadata_updated
:
course_overview
.
save
()
course_metadata_updated
+=
1
...
...
openedx/core/djangoapps/catalog/management/commands/tests/test_sync_course_runs.py
View file @
9e9c99ac
...
...
@@ -29,7 +29,8 @@ class TestSyncCourseRunsCommand(ModuleStoreTestCase):
# create a catalog course run with the same course id.
self
.
catalog_course_run
=
CourseRunFactory
(
key
=
unicode
(
self
.
course
.
id
),
marketing_url
=
'test_marketing_url'
marketing_url
=
'test_marketing_url'
,
eligible_for_financial_aid
=
False
)
def
get_course_overview_marketing_url
(
self
,
course_id
):
...
...
@@ -38,18 +39,25 @@ class TestSyncCourseRunsCommand(ModuleStoreTestCase):
"""
return
CourseOverview
.
objects
.
get
(
id
=
course_id
)
.
marketing_url
def
test_
marketing_url_o
n_sync
(
self
,
mock_catalog_course_runs
):
def
test_
course_ru
n_sync
(
self
,
mock_catalog_course_runs
):
"""
Verify the updated marketing url on execution of the management command.
Verify on executing management command course overview data is updated
with course run data from course discovery.
"""
mock_catalog_course_runs
.
return_value
=
[
self
.
catalog_course_run
]
earlier_marketing_url
=
self
.
get_course_overview_marketing_url
(
self
.
course
.
id
)
course_overview
=
CourseOverview
.
objects
.
get
(
id
=
self
.
course
.
id
)
earlier_eligible_for_financial_aid
=
course_overview
.
eligible_for_financial_aid
call_command
(
'sync_course_runs'
)
course_overview
.
refresh_from_db
()
updated_marketing_url
=
self
.
get_course_overview_marketing_url
(
self
.
course
.
id
)
updated_eligible_for_financial_aid
=
course_overview
.
eligible_for_financial_aid
# Assert that the Marketing URL has changed.
self
.
assertNotEqual
(
earlier_marketing_url
,
updated_marketing_url
)
self
.
assertNotEqual
(
earlier_eligible_for_financial_aid
,
updated_eligible_for_financial_aid
)
self
.
assertEqual
(
updated_marketing_url
,
'test_marketing_url'
)
self
.
assertEqual
(
updated_eligible_for_financial_aid
,
False
)
@mock.patch
(
COMMAND_MODULE
+
'.log.info'
)
def
test_course_overview_does_not_exist
(
self
,
mock_log_info
,
mock_catalog_course_runs
):
...
...
openedx/core/djangoapps/catalog/tests/factories.py
View file @
9e9c99ac
...
...
@@ -88,6 +88,7 @@ class CourseRunFactory(DictFactoryBase):
image
=
ImageFactory
()
key
=
factory
.
LazyFunction
(
generate_course_run_key
)
marketing_url
=
factory
.
Faker
(
'url'
)
eligible_for_financial_aid
=
True
seats
=
factory
.
LazyFunction
(
partial
(
generate_instances
,
SeatFactory
))
pacing_type
=
'self_paced'
short_description
=
factory
.
Faker
(
'sentence'
)
...
...
openedx/core/djangoapps/content/course_overviews/migrations/0012_courseoverview_eligible_for_financial_aid.py
0 → 100644
View file @
9e9c99ac
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'course_overviews'
,
'0011_courseoverview_marketing_url'
),
]
operations
=
[
migrations
.
AddField
(
model_name
=
'courseoverview'
,
name
=
'eligible_for_financial_aid'
,
field
=
models
.
BooleanField
(
default
=
True
),
),
]
openedx/core/djangoapps/content/course_overviews/models.py
View file @
9e9c99ac
...
...
@@ -98,6 +98,7 @@ class CourseOverview(TimeStampedModel):
effort
=
TextField
(
null
=
True
)
self_paced
=
BooleanField
(
default
=
False
)
marketing_url
=
TextField
(
null
=
True
)
eligible_for_financial_aid
=
BooleanField
(
default
=
True
)
@classmethod
def
_create_from_course
(
cls
,
course
):
...
...
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