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
dda0e03f
Commit
dda0e03f
authored
Feb 09, 2017
by
Renzo Lucioni
Committed by
GitHub
Feb 09, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #14488 from edx/renzo/finish-catalog-transition
Finish transition to catalog for program data
parents
c0e45249
0e06e905
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
705 additions
and
771 deletions
+705
-771
common/djangoapps/student/views.py
+2
-6
lms/djangoapps/learner_dashboard/tests/test_programs.py
+2
-9
lms/djangoapps/learner_dashboard/views.py
+3
-7
lms/static/js/learner_dashboard/models/course_card_model.js
+100
-64
lms/static/js/learner_dashboard/models/program_model.js
+7
-7
lms/static/js/learner_dashboard/views/course_enroll_view.js
+16
-18
lms/static/js/learner_dashboard/views/program_card_view.js
+9
-14
lms/static/js/learner_dashboard/views/program_details_view.js
+1
-1
lms/static/js/spec/learner_dashboard/collection_list_view_spec.js
+83
-51
lms/static/js/spec/learner_dashboard/course_card_view_spec.js
+76
-73
lms/static/js/spec/learner_dashboard/course_enroll_view_spec.js
+162
-95
lms/static/js/spec/learner_dashboard/program_card_view_spec.js
+57
-37
lms/static/js/spec/learner_dashboard/program_details_header_spec.js
+40
-21
lms/templates/dashboard.html
+1
-1
lms/templates/dashboard/_dashboard_course_listing.html
+2
-8
lms/templates/learner_dashboard/course_card.underscore
+4
-4
lms/templates/learner_dashboard/course_enroll.underscore
+9
-9
lms/templates/learner_dashboard/program_card.underscore
+16
-16
lms/templates/learner_dashboard/program_header_view.underscore
+9
-9
openedx/core/djangoapps/catalog/tests/test_utils.py
+0
-58
openedx/core/djangoapps/catalog/utils.py
+0
-58
openedx/core/djangoapps/programs/tests/factories.py
+3
-6
openedx/core/djangoapps/programs/tests/test_utils.py
+63
-127
openedx/core/djangoapps/programs/utils.py
+39
-71
themes/edx.org/lms/templates/dashboard.html
+1
-1
No files found.
common/djangoapps/student/views.py
View file @
dda0e03f
...
@@ -122,15 +122,14 @@ import newrelic_custom_metrics
...
@@ -122,15 +122,14 @@ import newrelic_custom_metrics
# Note that this lives in LMS, so this dependency should be refactored.
# Note that this lives in LMS, so this dependency should be refactored.
from
notification_prefs.views
import
enable_notifications
from
notification_prefs.views
import
enable_notifications
from
openedx.core.djangoapps.catalog.utils
import
get_programs_with_type_logo
from
openedx.core.djangoapps.credit.email_utils
import
get_credit_provider_display_names
,
make_providers_strings
from
openedx.core.djangoapps.credit.email_utils
import
get_credit_provider_display_names
,
make_providers_strings
from
openedx.core.djangoapps.lang_pref
import
LANGUAGE_KEY
from
openedx.core.djangoapps.lang_pref
import
LANGUAGE_KEY
from
openedx.core.djangoapps.catalog.utils
import
munge_catalog_program
from
openedx.core.djangoapps.programs.models
import
ProgramsApiConfig
from
openedx.core.djangoapps.programs.models
import
ProgramsApiConfig
from
openedx.core.djangoapps.programs.utils
import
ProgramProgressMeter
from
openedx.core.djangoapps.programs.utils
import
ProgramProgressMeter
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.djangoapps.site_configuration
import
helpers
as
configuration_helpers
from
openedx.core.djangoapps.theming
import
helpers
as
theming_helpers
from
openedx.core.djangoapps.theming
import
helpers
as
theming_helpers
from
openedx.core.djangoapps.user_api.preferences
import
api
as
preferences_api
from
openedx.core.djangoapps.user_api.preferences
import
api
as
preferences_api
from
openedx.core.djangoapps.catalog.utils
import
get_programs_with_type_logo
log
=
logging
.
getLogger
(
"edx.student"
)
log
=
logging
.
getLogger
(
"edx.student"
)
...
@@ -670,9 +669,6 @@ def dashboard(request):
...
@@ -670,9 +669,6 @@ def dashboard(request):
meter
=
ProgramProgressMeter
(
user
,
enrollments
=
course_enrollments
)
meter
=
ProgramProgressMeter
(
user
,
enrollments
=
course_enrollments
)
inverted_programs
=
meter
.
invert_programs
()
inverted_programs
=
meter
.
invert_programs
()
for
program_list
in
inverted_programs
.
itervalues
():
program_list
[:]
=
[
munge_catalog_program
(
program
)
for
program
in
program_list
]
# Construct a dictionary of course mode information
# Construct a dictionary of course mode information
# used to render the course list. We re-use the course modes dict
# used to render the course list. We re-use the course modes dict
# we loaded earlier to avoid hitting the database.
# we loaded earlier to avoid hitting the database.
...
@@ -795,7 +791,7 @@ def dashboard(request):
...
@@ -795,7 +791,7 @@ def dashboard(request):
'order_history_list'
:
order_history_list
,
'order_history_list'
:
order_history_list
,
'courses_requirements_not_met'
:
courses_requirements_not_met
,
'courses_requirements_not_met'
:
courses_requirements_not_met
,
'nav_hidden'
:
True
,
'nav_hidden'
:
True
,
'
programs_by_run
'
:
inverted_programs
,
'
inverted_programs
'
:
inverted_programs
,
'show_program_listing'
:
ProgramsApiConfig
.
current
()
.
show_program_listing
,
'show_program_listing'
:
ProgramsApiConfig
.
current
()
.
show_program_listing
,
'disable_courseware_js'
:
True
,
'disable_courseware_js'
:
True
,
'display_course_modes_on_dashboard'
:
enable_verified_certificates
and
display_course_modes_on_dashboard
,
'display_course_modes_on_dashboard'
:
enable_verified_certificates
and
display_course_modes_on_dashboard
,
...
...
lms/djangoapps/learner_dashboard/tests/test_programs.py
View file @
dda0e03f
...
@@ -16,7 +16,6 @@ import mock
...
@@ -16,7 +16,6 @@ import mock
from
openedx.core.djangoapps.catalog.tests.factories
import
ProgramFactory
,
CourseFactory
,
CourseRunFactory
from
openedx.core.djangoapps.catalog.tests.factories
import
ProgramFactory
,
CourseFactory
,
CourseRunFactory
from
openedx.core.djangoapps.catalog.tests.mixins
import
CatalogIntegrationMixin
from
openedx.core.djangoapps.catalog.tests.mixins
import
CatalogIntegrationMixin
from
openedx.core.djangoapps.catalog.utils
import
munge_catalog_program
from
openedx.core.djangoapps.credentials.tests.factories
import
UserCredential
,
ProgramCredential
from
openedx.core.djangoapps.credentials.tests.factories
import
UserCredential
,
ProgramCredential
from
openedx.core.djangoapps.credentials.tests.mixins
import
CredentialsApiConfigMixin
from
openedx.core.djangoapps.credentials.tests.mixins
import
CredentialsApiConfigMixin
from
openedx.core.djangoapps.programs.tests.mixins
import
ProgramsApiConfigMixin
from
openedx.core.djangoapps.programs.tests.mixins
import
ProgramsApiConfigMixin
...
@@ -64,13 +63,7 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar
...
@@ -64,13 +63,7 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar
"""
"""
Helper function used to sort dictionaries representing programs.
Helper function used to sort dictionaries representing programs.
"""
"""
try
:
return
program
[
'title'
]
return
program
[
'title'
]
except
:
# pylint: disable=bare-except
# This is here temporarily because programs are still being munged
# to look like they came from the programs service before going out
# to the front end.
return
program
[
'name'
]
def
credential_sort_key
(
self
,
credential
):
def
credential_sort_key
(
self
,
credential
):
"""
"""
...
@@ -157,7 +150,7 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar
...
@@ -157,7 +150,7 @@ class TestProgramListing(ProgramsApiConfigMixin, CredentialsApiConfigMixin, Shar
actual
=
sorted
(
actual
,
key
=
self
.
program_sort_key
)
actual
=
sorted
(
actual
,
key
=
self
.
program_sort_key
)
for
index
,
actual_program
in
enumerate
(
actual
):
for
index
,
actual_program
in
enumerate
(
actual
):
expected_program
=
munge_catalog_program
(
self
.
data
[
index
])
expected_program
=
self
.
data
[
index
]
self
.
assert_dict_contains_subset
(
actual_program
,
expected_program
)
self
.
assert_dict_contains_subset
(
actual_program
,
expected_program
)
def
test_program_discovery
(
self
,
mock_get_programs
):
def
test_program_discovery
(
self
,
mock_get_programs
):
...
...
lms/djangoapps/learner_dashboard/views.py
View file @
dda0e03f
...
@@ -6,12 +6,11 @@ from django.views.decorators.http import require_GET
...
@@ -6,12 +6,11 @@ from django.views.decorators.http import require_GET
from
edxmako.shortcuts
import
render_to_response
from
edxmako.shortcuts
import
render_to_response
from
lms.djangoapps.learner_dashboard.utils
import
strip_course_id
,
FAKE_COURSE_KEY
from
lms.djangoapps.learner_dashboard.utils
import
strip_course_id
,
FAKE_COURSE_KEY
from
openedx.core.djangoapps.catalog.utils
import
get_programs
,
munge_catalog_program
from
openedx.core.djangoapps.catalog.utils
import
get_programs
from
openedx.core.djangoapps.credentials.utils
import
get_programs_credentials
from
openedx.core.djangoapps.credentials.utils
import
get_programs_credentials
from
openedx.core.djangoapps.programs.models
import
ProgramsApiConfig
from
openedx.core.djangoapps.programs.models
import
ProgramsApiConfig
from
openedx.core.djangoapps.programs.utils
import
(
from
openedx.core.djangoapps.programs.utils
import
(
get_program_marketing_url
,
get_program_marketing_url
,
munge_progress_map
,
ProgramProgressMeter
,
ProgramProgressMeter
,
ProgramDataExtender
,
ProgramDataExtender
,
)
)
...
@@ -27,16 +26,14 @@ def program_listing(request):
...
@@ -27,16 +26,14 @@ def program_listing(request):
raise
Http404
raise
Http404
meter
=
ProgramProgressMeter
(
request
.
user
)
meter
=
ProgramProgressMeter
(
request
.
user
)
engaged_programs
=
[
munge_catalog_program
(
program
)
for
program
in
meter
.
engaged_programs
]
progress
=
[
munge_progress_map
(
progress_map
)
for
progress_map
in
meter
.
progress
]
context
=
{
context
=
{
'credentials'
:
get_programs_credentials
(
request
.
user
),
'credentials'
:
get_programs_credentials
(
request
.
user
),
'disable_courseware_js'
:
True
,
'disable_courseware_js'
:
True
,
'marketing_url'
:
get_program_marketing_url
(
programs_config
),
'marketing_url'
:
get_program_marketing_url
(
programs_config
),
'nav_hidden'
:
True
,
'nav_hidden'
:
True
,
'programs'
:
engaged_programs
,
'programs'
:
meter
.
engaged_programs
,
'progress'
:
progress
,
'progress'
:
meter
.
progress
,
'show_program_listing'
:
programs_config
.
show_program_listing
,
'show_program_listing'
:
programs_config
.
show_program_listing
,
'uses_pattern_library'
:
True
,
'uses_pattern_library'
:
True
,
}
}
...
@@ -56,7 +53,6 @@ def program_details(request, program_uuid):
...
@@ -56,7 +53,6 @@ def program_details(request, program_uuid):
if
not
program_data
:
if
not
program_data
:
raise
Http404
raise
Http404
program_data
=
munge_catalog_program
(
program_data
)
program_data
=
ProgramDataExtender
(
program_data
,
request
.
user
)
.
extend
()
program_data
=
ProgramDataExtender
(
program_data
,
request
.
user
)
.
extend
()
urls
=
{
urls
=
{
...
...
lms/static/js/learner_dashboard/models/course_card_model.js
View file @
dda0e03f
...
@@ -5,60 +5,93 @@
...
@@ -5,60 +5,93 @@
'use strict'
;
'use strict'
;
define
([
define
([
'backbone'
,
'backbone'
,
'underscore'
,
'jquery'
,
'edx-ui-toolkit/js/utils/date-utils'
'edx-ui-toolkit/js/utils/date-utils'
],
],
function
(
Backbone
,
DateUtils
)
{
function
(
Backbone
,
_
,
$
,
DateUtils
)
{
return
Backbone
.
Model
.
extend
({
return
Backbone
.
Model
.
extend
({
initialize
:
function
(
data
)
{
initialize
:
function
(
data
)
{
if
(
data
)
{
if
(
data
)
{
this
.
context
=
data
;
this
.
context
=
data
;
this
.
setActive
RunMode
(
this
.
getRunMode
(
data
.
run_mode
s
),
data
.
user_preferences
);
this
.
setActive
CourseRun
(
this
.
getCourseRun
(
data
.
course_run
s
),
data
.
user_preferences
);
}
}
},
},
getUnselectedRunMode
:
function
(
runModes
)
{
getCourseRun
:
function
(
courseRuns
)
{
if
(
runModes
&&
runModes
.
length
>
0
)
{
var
enrolledCourseRun
=
_
.
findWhere
(
courseRuns
,
{
is_enrolled
:
true
}),
return
{
openEnrollmentCourseRuns
=
this
.
getEnrollableCourseRuns
(),
course_image_url
:
runModes
[
0
].
course_image_url
,
desiredCourseRun
;
marketing_url
:
runModes
[
0
].
marketing_url
,
is_enrollment_open
:
runModes
[
0
].
is_enrollment_open
// We populate our model by looking at the course runs.
};
if
(
enrolledCourseRun
)
{
// If the learner is already enrolled in a course run, return that one.
desiredCourseRun
=
enrolledCourseRun
;
}
else
if
(
openEnrollmentCourseRuns
.
length
>
0
)
{
if
(
openEnrollmentCourseRuns
.
length
===
1
)
{
desiredCourseRun
=
openEnrollmentCourseRuns
[
0
];
}
else
{
desiredCourseRun
=
this
.
getUnselectedCourseRun
(
openEnrollmentCourseRuns
);
}
}
else
{
desiredCourseRun
=
this
.
getUnselectedCourseRun
(
courseRuns
);
}
}
return
{}
;
return
desiredCourseRun
;
},
},
getRunMode
:
function
(
runModes
)
{
getUnselectedCourseRun
:
function
(
courseRuns
)
{
var
enrolled_mode
=
_
.
findWhere
(
runModes
,
{
is_enrolled
:
true
}),
var
unselectedRun
=
{},
openEnrollmentRunModes
=
this
.
getEnrollableRunModes
(),
courseRun
,
desiredRunMode
;
courseImageUrl
;
// We populate our model by looking at the run modes.
if
(
enrolled_mode
)
{
if
(
courseRuns
&&
courseRuns
.
length
>
0
)
{
// If the learner is already enrolled in a run mode, return that one.
courseRun
=
courseRuns
[
0
];
desiredRunMode
=
enrolled_mode
;
}
else
if
(
openEnrollmentRunModes
.
length
>
0
)
{
if
(
courseRun
.
hasOwnProperty
(
'image'
))
{
if
(
openEnrollmentRunModes
.
length
===
1
)
{
courseImageUrl
=
courseRun
.
image
.
src
;
desiredRunMode
=
openEnrollmentRunModes
[
0
];
}
else
{
}
else
{
desiredRunMode
=
this
.
getUnselectedRunMode
(
openEnrollmentRunModes
);
// The course_image_url property is attached by setActiveCourseRun.
// If that hasn't been called, it won't be present yet.
courseImageUrl
=
courseRun
.
course_image_url
;
}
}
}
else
{
desiredRunMode
=
this
.
getUnselectedRunMode
(
runModes
);
$
.
extend
(
unselectedRun
,
{
course_image_url
:
courseImageUrl
,
marketing_url
:
courseRun
.
marketing_url
,
is_enrollment_open
:
courseRun
.
is_enrollment_open
});
}
}
return
desiredRunMode
;
return
unselectedRun
;
},
},
getEnrollableRunModes
:
function
()
{
getEnrollableCourseRuns
:
function
()
{
return
_
.
where
(
this
.
context
.
run_modes
,
{
var
rawCourseRuns
,
enrollableCourseRuns
;
rawCourseRuns
=
_
.
where
(
this
.
context
.
course_runs
,
{
is_enrollment_open
:
true
,
is_enrollment_open
:
true
,
is_enrolled
:
false
,
is_enrolled
:
false
,
is_course_ended
:
false
is_course_ended
:
false
});
});
// Deep copy to avoid mutating this.context.
enrollableCourseRuns
=
$
.
extend
(
true
,
[],
rawCourseRuns
);
// These are raw course runs from the server. The start
// dates are ISO-8601 formatted strings that need to be
// prepped for display.
_
.
each
(
enrollableCourseRuns
,
(
function
(
courseRun
)
{
// eslint-disable-next-line no-param-reassign
courseRun
.
start_date
=
this
.
formatDate
(
courseRun
.
start
);
}).
bind
(
this
));
return
enrollableCourseRuns
;
},
},
getUpcoming
RunMode
s
:
function
()
{
getUpcoming
CourseRun
s
:
function
()
{
return
_
.
where
(
this
.
context
.
run_mode
s
,
{
return
_
.
where
(
this
.
context
.
course_run
s
,
{
is_enrollment_open
:
false
,
is_enrollment_open
:
false
,
is_enrolled
:
false
,
is_enrolled
:
false
,
is_course_ended
:
false
is_course_ended
:
false
...
@@ -82,51 +115,54 @@
...
@@ -82,51 +115,54 @@
return
DateUtils
.
localize
(
context
);
return
DateUtils
.
localize
(
context
);
},
},
setActiveRunMode
:
function
(
runMode
,
userPreferences
)
{
setActiveCourseRun
:
function
(
courseRun
,
userPreferences
)
{
var
startDateString
;
var
startDateString
,
if
(
runMode
)
{
courseImageUrl
;
if
(
runMode
.
advertised_start
!==
undefined
&&
runMode
.
advertised_start
!==
'None'
)
{
startDateString
=
runMode
.
advertised_start
;
if
(
courseRun
)
{
if
(
courseRun
.
advertised_start
!==
undefined
&&
courseRun
.
advertised_start
!==
'None'
)
{
startDateString
=
courseRun
.
advertised_start
;
}
else
{
}
else
{
startDateString
=
this
.
formatDate
(
startDateString
=
this
.
formatDate
(
courseRun
.
start
,
userPreferences
);
runMode
.
start_date
,
userPreferences
);
}
}
if
(
courseRun
.
hasOwnProperty
(
'image'
))
{
courseImageUrl
=
courseRun
.
image
.
src
;
}
else
{
courseImageUrl
=
courseRun
.
course_image_url
;
}
this
.
set
({
this
.
set
({
certificate_url
:
runMode
.
certificate_url
,
certificate_url
:
courseRun
.
certificate_url
,
course_image_url
:
runMode
.
course_image_url
||
''
,
course_image_url
:
courseImageUrl
||
''
,
course_key
:
runMode
.
course_key
,
course_run_key
:
courseRun
.
key
,
course_url
:
runMode
.
course_url
||
''
,
course_url
:
courseRun
.
course_url
||
''
,
display_name
:
this
.
context
.
display_name
,
title
:
this
.
context
.
title
,
end_date
:
this
.
formatDate
(
end_date
:
this
.
formatDate
(
courseRun
.
end
,
userPreferences
),
runMode
.
end_date
,
enrollable_course_runs
:
this
.
getEnrollableCourseRuns
(),
userPreferences
is_course_ended
:
courseRun
.
is_course_ended
,
),
is_enrolled
:
courseRun
.
is_enrolled
,
enrollable_run_modes
:
this
.
getEnrollableRunModes
(),
is_enrollment_open
:
courseRun
.
is_enrollment_open
,
is_course_ended
:
runMode
.
is_course_ended
,
course_key
:
this
.
context
.
key
,
is_enrolled
:
runMode
.
is_enrolled
,
marketing_url
:
courseRun
.
marketing_url
,
is_enrollment_open
:
runMode
.
is_enrollment_open
,
mode_slug
:
courseRun
.
type
,
key
:
this
.
context
.
key
,
marketing_url
:
runMode
.
marketing_url
,
mode_slug
:
runMode
.
mode_slug
,
run_key
:
runMode
.
run_key
,
start_date
:
startDateString
,
start_date
:
startDateString
,
upcoming_
run_modes
:
this
.
getUpcomingRunMode
s
(),
upcoming_
course_runs
:
this
.
getUpcomingCourseRun
s
(),
upgrade_url
:
runMode
.
upgrade_url
upgrade_url
:
courseRun
.
upgrade_url
});
});
}
}
},
},
setUnselected
:
function
()
{
setUnselected
:
function
()
{
// Called to reset the model back to the unselected state.
// Called to reset the model back to the unselected state.
var
unselected
Mode
=
this
.
getUnselectedRunMode
(
this
.
get
(
'enrollable_run_mode
s'
));
var
unselected
CourseRun
=
this
.
getUnselectedCourseRun
(
this
.
get
(
'enrollable_course_run
s'
));
this
.
setActive
RunMode
(
unselectedMode
);
this
.
setActive
CourseRun
(
unselectedCourseRun
);
},
},
update
Run
:
function
(
r
unKey
)
{
update
CourseRun
:
function
(
courseR
unKey
)
{
var
selected
Run
=
_
.
findWhere
(
this
.
get
(
'run_modes'
),
{
run_key
:
r
unKey
});
var
selected
CourseRun
=
_
.
findWhere
(
this
.
get
(
'course_runs'
),
{
key
:
courseR
unKey
});
if
(
selectedRun
)
{
if
(
selected
Course
Run
)
{
this
.
setActive
RunMode
(
selected
Run
);
this
.
setActive
CourseRun
(
selectedCourse
Run
);
}
}
}
}
});
});
...
...
lms/static/js/learner_dashboard/models/program_model.js
View file @
dda0e03f
...
@@ -11,17 +11,17 @@
...
@@ -11,17 +11,17 @@
initialize
:
function
(
data
)
{
initialize
:
function
(
data
)
{
if
(
data
)
{
if
(
data
)
{
this
.
set
({
this
.
set
({
name
:
data
.
nam
e
,
title
:
data
.
titl
e
,
category
:
data
.
category
,
type
:
data
.
type
,
subtitle
:
data
.
subtitle
,
subtitle
:
data
.
subtitle
,
organizations
:
data
.
organizations
,
authoring_organizations
:
data
.
authoring_
organizations
,
detailUrl
:
data
.
detail_url
,
detailUrl
:
data
.
detail_url
,
smallBannerUrl
:
data
.
banner_image_urls
.
w348h116
,
xsmallBannerUrl
:
data
.
banner_image
[
'x-small'
].
url
,
mediumBannerUrl
:
data
.
banner_image_urls
.
w435h145
,
smallBannerUrl
:
data
.
banner_image
.
small
.
url
,
largeBannerUrl
:
data
.
banner_image_urls
.
w726h242
,
mediumBannerUrl
:
data
.
banner_image
.
medium
.
url
,
breakpoints
:
{
breakpoints
:
{
max
:
{
max
:
{
tiny
:
'320px'
,
xsmall
:
'320px'
,
small
:
'540px'
,
small
:
'540px'
,
medium
:
'768px'
,
medium
:
'768px'
,
large
:
'979px'
large
:
'979px'
...
...
lms/static/js/learner_dashboard/views/course_enroll_view.js
View file @
dda0e03f
...
@@ -21,7 +21,7 @@
...
@@ -21,7 +21,7 @@
events
:
{
events
:
{
'click .enroll-button'
:
'handleEnroll'
,
'click .enroll-button'
:
'handleEnroll'
,
'change .run-select'
:
'handleRunSelect'
'change .run-select'
:
'handle
Course
RunSelect'
},
},
initialize
:
function
(
options
)
{
initialize
:
function
(
options
)
{
...
@@ -45,12 +45,12 @@
...
@@ -45,12 +45,12 @@
handleEnroll
:
function
()
{
handleEnroll
:
function
()
{
// Enrollment click event handled here
// Enrollment click event handled here
if
(
!
this
.
model
.
get
(
'course_key'
))
{
if
(
!
this
.
model
.
get
(
'course_
run_
key'
))
{
this
.
$
(
'.select-error'
).
css
(
'visibility'
,
'visible'
);
this
.
$
(
'.select-error'
).
css
(
'visibility'
,
'visible'
);
}
else
if
(
!
this
.
model
.
get
(
'is_enrolled'
))
{
}
else
if
(
!
this
.
model
.
get
(
'is_enrolled'
))
{
// actually enroll
// Create the enrollment.
this
.
enrollModel
.
save
({
this
.
enrollModel
.
save
({
course_id
:
this
.
model
.
get
(
'course_key'
)
course_id
:
this
.
model
.
get
(
'course_
run_
key'
)
},
{
},
{
success
:
_
.
bind
(
this
.
enrollSuccess
,
this
),
success
:
_
.
bind
(
this
.
enrollSuccess
,
this
),
error
:
_
.
bind
(
this
.
enrollError
,
this
)
error
:
_
.
bind
(
this
.
enrollError
,
this
)
...
@@ -58,24 +58,22 @@
...
@@ -58,24 +58,22 @@
}
}
},
},
handleRunSelect
:
function
(
event
)
{
handleCourseRunSelect
:
function
(
event
)
{
var
runKey
;
var
courseRunKey
=
$
(
event
.
target
).
val
();
if
(
event
.
target
)
{
runKey
=
$
(
event
.
target
).
val
();
if
(
courseRunKey
)
{
if
(
runKey
)
{
this
.
model
.
updateCourseRun
(
courseRunKey
);
this
.
model
.
updateRun
(
runKey
);
}
else
{
}
else
{
// Set back the unselected states
// Set back the unselected states
this
.
model
.
setUnselected
();
this
.
model
.
setUnselected
();
}
}
}
},
},
enrollSuccess
:
function
()
{
enrollSuccess
:
function
()
{
var
course
Key
=
this
.
model
.
get
(
'course
_key'
);
var
course
RunKey
=
this
.
model
.
get
(
'course_run
_key'
);
if
(
this
.
trackSelectionUrl
)
{
if
(
this
.
trackSelectionUrl
)
{
// Go to track selection page
// Go to track selection page
this
.
redirect
(
this
.
trackSelectionUrl
+
courseKey
);
this
.
redirect
(
this
.
trackSelectionUrl
+
course
Run
Key
);
}
else
{
}
else
{
this
.
model
.
set
({
this
.
model
.
set
({
is_enrolled
:
true
is_enrolled
:
true
...
@@ -98,7 +96,7 @@
...
@@ -98,7 +96,7 @@
* This can occur, for example, when a course does not
* This can occur, for example, when a course does not
* have a free enrollment mode, so we can't auto-enroll.
* have a free enrollment mode, so we can't auto-enroll.
*/
*/
this
.
redirect
(
this
.
trackSelectionUrl
+
this
.
model
.
get
(
'course_key'
));
this
.
redirect
(
this
.
trackSelectionUrl
+
this
.
model
.
get
(
'course_
run_
key'
));
}
}
},
},
...
...
lms/static/js/learner_dashboard/views/program_card_view.js
View file @
dda0e03f
...
@@ -22,7 +22,7 @@
...
@@ -22,7 +22,7 @@
attributes
:
function
()
{
attributes
:
function
()
{
return
{
return
{
'aria-labelledby'
:
'program-'
+
this
.
model
.
get
(
'id'
),
'aria-labelledby'
:
'program-'
+
this
.
model
.
get
(
'
uu
id'
),
'role'
:
'group'
'role'
:
'group'
};
};
},
},
...
@@ -33,14 +33,14 @@
...
@@ -33,14 +33,14 @@
this
.
progressCollection
=
data
.
context
.
progressCollection
;
this
.
progressCollection
=
data
.
context
.
progressCollection
;
if
(
this
.
progressCollection
)
{
if
(
this
.
progressCollection
)
{
this
.
progressModel
=
this
.
progressCollection
.
findWhere
({
this
.
progressModel
=
this
.
progressCollection
.
findWhere
({
id
:
this
.
model
.
get
(
'
id'
)
uuid
:
this
.
model
.
get
(
'uu
id'
)
});
});
}
}
this
.
render
();
this
.
render
();
},
},
render
:
function
()
{
render
:
function
()
{
var
orgList
=
_
.
map
(
this
.
model
.
get
(
'organizations'
),
function
(
org
)
{
var
orgList
=
_
.
map
(
this
.
model
.
get
(
'
authoring_
organizations'
),
function
(
org
)
{
return
gettext
(
org
.
key
);
return
gettext
(
org
.
key
);
}),
}),
data
=
$
.
extend
(
data
=
$
.
extend
(
...
@@ -56,7 +56,7 @@
...
@@ -56,7 +56,7 @@
postRender
:
function
()
{
postRender
:
function
()
{
// Add describedby to parent only if progess is present
// Add describedby to parent only if progess is present
if
(
this
.
progressModel
)
{
if
(
this
.
progressModel
)
{
this
.
$el
.
attr
(
'aria-describedby'
,
'status-'
+
this
.
model
.
get
(
'id'
));
this
.
$el
.
attr
(
'aria-describedby'
,
'status-'
+
this
.
model
.
get
(
'
uu
id'
));
}
}
if
(
navigator
.
userAgent
.
indexOf
(
'MSIE'
)
!==
-
1
||
if
(
navigator
.
userAgent
.
indexOf
(
'MSIE'
)
!==
-
1
||
...
@@ -73,19 +73,14 @@
...
@@ -73,19 +73,14 @@
var
progress
=
this
.
progressModel
?
this
.
progressModel
.
toJSON
()
:
false
;
var
progress
=
this
.
progressModel
?
this
.
progressModel
.
toJSON
()
:
false
;
if
(
progress
)
{
if
(
progress
)
{
progress
.
total
=
{
completed
:
progress
.
completed
.
length
,
in_progress
:
progress
.
in_progress
.
length
,
not_started
:
progress
.
not_started
.
length
};
progress
.
total
.
courses
=
progress
.
total
.
completed
+
progress
.
total
=
progress
.
completed
+
progress
.
total
.
in_progress
+
progress
.
in_progress
+
progress
.
total
.
not_started
;
progress
.
not_started
;
progress
.
percentage
=
{
progress
.
percentage
=
{
completed
:
this
.
getWidth
(
progress
.
total
.
completed
,
progress
.
total
.
courses
),
completed
:
this
.
getWidth
(
progress
.
completed
,
progress
.
total
),
in_progress
:
this
.
getWidth
(
progress
.
total
.
in_progress
,
progress
.
total
.
courses
)
in_progress
:
this
.
getWidth
(
progress
.
in_progress
,
progress
.
total
)
};
};
}
}
...
...
lms/static/js/learner_dashboard/views/program_details_view.js
View file @
dda0e03f
...
@@ -33,7 +33,7 @@
...
@@ -33,7 +33,7 @@
this
.
options
=
options
;
this
.
options
=
options
;
this
.
programModel
=
new
Backbone
.
Model
(
this
.
options
.
programData
);
this
.
programModel
=
new
Backbone
.
Model
(
this
.
options
.
programData
);
this
.
courseCardCollection
=
new
CourseCardCollection
(
this
.
courseCardCollection
=
new
CourseCardCollection
(
this
.
programModel
.
get
(
'course
_code
s'
),
this
.
programModel
.
get
(
'courses'
),
this
.
options
.
userPreferences
this
.
options
.
userPreferences
);
);
this
.
render
();
this
.
render
();
...
...
lms/static/js/spec/learner_dashboard/collection_list_view_spec.js
View file @
dda0e03f
...
@@ -5,10 +5,9 @@ define([
...
@@ -5,10 +5,9 @@ define([
'js/learner_dashboard/collections/program_collection'
,
'js/learner_dashboard/collections/program_collection'
,
'js/learner_dashboard/views/collection_list_view'
,
'js/learner_dashboard/views/collection_list_view'
,
'js/learner_dashboard/collections/program_progress_collection'
'js/learner_dashboard/collections/program_progress_collection'
],
function
(
Backbone
,
$
,
ProgramCardView
,
ProgramCollection
,
CollectionListView
,
],
function
(
Backbone
,
$
,
ProgramCardView
,
ProgramCollection
,
CollectionListView
,
ProgressCollection
)
{
ProgressCollection
)
{
'use strict'
;
'use strict'
;
/* jslint maxlen: 500 */
/* jslint maxlen: 500 */
describe
(
'Collection List View'
,
function
()
{
describe
(
'Collection List View'
,
function
()
{
var
view
=
null
,
var
view
=
null
,
...
@@ -17,62 +16,90 @@ define([
...
@@ -17,62 +16,90 @@ define([
context
=
{
context
=
{
programsData
:
[
programsData
:
[
{
{
category
:
'xseries'
,
uuid
:
'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8'
,
status
:
'active'
,
title
:
'Food Security and Sustainability'
,
subtitle
:
'program 1'
,
subtitle
:
'Learn how to feed all people in the world in a sustainable way.'
,
name
:
'test program 1'
,
type
:
'XSeries'
,
organizations
:
[
detail_url
:
'https://www.edx.org/foo/bar'
,
banner_image
:
{
medium
:
{
height
:
242
,
width
:
726
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.medium.jpg'
},
'x-small'
:
{
height
:
116
,
width
:
348
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.x-small.jpg'
},
small
:
{
height
:
145
,
width
:
435
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.small.jpg'
},
large
:
{
height
:
480
,
width
:
1440
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.large.jpg'
}
},
authoring_organizations
:
[
{
{
display_name
:
'edX'
,
uuid
:
'0c6e5fa2-96e8-40b2-9ebe-c8b0df2a3b22'
,
key
:
'edx'
key
:
'WageningenX'
,
name
:
'Wageningen University & Research'
}
}
],
]
created
:
'2016-03-03T19:18:50.061136Z'
,
modified
:
'2016-03-25T13:45:21.220732Z'
,
marketing_slug
:
'p_2?param=haha&test=b'
,
id
:
146
,
marketing_url
:
'http://www.edx.org/xseries/p_2?param=haha&test=b'
,
banner_image_urls
:
{
w348h116
:
'http://www.edx.org/images/org1/test1'
,
w435h145
:
'http://www.edx.org/images/org1/test2'
,
w726h242
:
'http://www.edx.org/images/org1/test3'
}
},
},
{
{
category
:
'xseries'
,
uuid
:
'91d144d2-1bb1-4afe-90df-d5cff63fa6e2'
,
status
:
'active'
,
title
:
'edX Course Creator'
,
subtitle
:
'fda'
,
subtitle
:
'Become an expert in creating courses for the edX platform.'
,
name
:
'fda'
,
type
:
'XSeries'
,
organizations
:
[
detail_url
:
'https://www.edx.org/foo/bar'
,
banner_image
:
{
medium
:
{
height
:
242
,
width
:
726
,
url
:
'https://example.com/91d144d2-1bb1-4afe-90df-d5cff63fa6e2.medium.jpg'
},
'x-small'
:
{
height
:
116
,
width
:
348
,
url
:
'https://example.com/91d144d2-1bb1-4afe-90df-d5cff63fa6e2.x-small.jpg'
},
small
:
{
height
:
145
,
width
:
435
,
url
:
'https://example.com/91d144d2-1bb1-4afe-90df-d5cff63fa6e2.small.jpg'
},
large
:
{
height
:
480
,
width
:
1440
,
url
:
'https://example.com/91d144d2-1bb1-4afe-90df-d5cff63fa6e2.large.jpg'
}
},
authoring_organizations
:
[
{
{
display_name
:
'edX'
,
uuid
:
'4f8cb2c9-589b-4d1e-88c1-b01a02db3a9c'
,
key
:
'edx'
key
:
'edX'
,
name
:
'edX'
}
}
],
]
created
:
'2016-03-09T14:30:41.484848Z'
,
modified
:
'2016-03-09T14:30:52.840898Z'
,
marketing_slug
:
'gdaf'
,
id
:
147
,
marketing_url
:
'http://www.edx.org/xseries/gdaf'
,
banner_image_urls
:
{
w348h116
:
'http://www.edx.org/images/org2/test1'
,
w435h145
:
'http://www.edx.org/images/org2/test2'
,
w726h242
:
'http://www.edx.org/images/org2/test3'
}
}
}
],
],
userProgress
:
[
userProgress
:
[
{
{
id
:
146
,
uuid
:
'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8'
,
completed
:
[
'courses'
,
'the'
,
'user'
,
'completed'
]
,
completed
:
4
,
in_progress
:
[
'in'
,
'progress'
]
,
in_progress
:
2
,
not_started
:
[
'courses'
,
'not'
,
'yet'
,
'started'
]
not_started
:
4
},
},
{
{
id
:
147
,
uuid
:
'91d144d2-1bb1-4afe-90df-d5cff63fa6e2'
,
completed
:
[
'Course 1'
]
,
completed
:
1
,
in_progress
:
[]
,
in_progress
:
0
,
not_started
:
[
'Course 2'
,
'Course 3'
,
'Course 4'
]
not_started
:
3
}
}
]
]
};
};
...
@@ -105,7 +132,8 @@ define([
...
@@ -105,7 +132,8 @@ define([
var
$cards
=
view
.
$el
.
find
(
'.program-card'
);
var
$cards
=
view
.
$el
.
find
(
'.program-card'
);
expect
(
$cards
.
length
).
toBe
(
2
);
expect
(
$cards
.
length
).
toBe
(
2
);
$cards
.
each
(
function
(
index
,
el
)
{
$cards
.
each
(
function
(
index
,
el
)
{
expect
(
$
(
el
).
find
(
'.title'
).
html
().
trim
()).
toEqual
(
context
.
programsData
[
index
].
name
);
// eslint-disable-next-line newline-per-chained-call
expect
(
$
(
el
).
find
(
'.title'
).
html
().
trim
()).
toEqual
(
context
.
programsData
[
index
].
title
);
});
});
});
});
...
@@ -116,13 +144,14 @@ define([
...
@@ -116,13 +144,14 @@ define([
view
=
new
CollectionListView
({
view
=
new
CollectionListView
({
el
:
'.program-cards-container'
,
el
:
'.program-cards-container'
,
childView
:
ProgramCardView
,
childView
:
ProgramCardView
,
context
:
{
'xseriesUrl'
:
'/programs'
},
context
:
{},
collection
:
programCollection
collection
:
programCollection
});
});
view
.
render
();
view
.
render
();
$cards
=
view
.
$el
.
find
(
'.program-card'
);
$cards
=
view
.
$el
.
find
(
'.program-card'
);
expect
(
$cards
.
length
).
toBe
(
0
);
expect
(
$cards
.
length
).
toBe
(
0
);
});
});
it
(
'should have no title when title not provided'
,
function
()
{
it
(
'should have no title when title not provided'
,
function
()
{
var
$title
;
var
$title
;
setFixtures
(
'<div class="test-container"><div class="program-cards-container"></div></div>'
);
setFixtures
(
'<div class="test-container"><div class="program-cards-container"></div></div>'
);
...
@@ -132,15 +161,18 @@ define([
...
@@ -132,15 +161,18 @@ define([
$title
=
view
.
$el
.
parent
().
find
(
'.collection-title'
);
$title
=
view
.
$el
.
parent
().
find
(
'.collection-title'
);
expect
(
$title
.
html
()).
not
.
toBeDefined
();
expect
(
$title
.
html
()).
not
.
toBeDefined
();
});
});
it
(
'should display screen reader header when provided'
,
function
()
{
it
(
'should display screen reader header when provided'
,
function
()
{
var
$title
,
titleContext
=
{
el
:
'h2'
,
title
:
'list start'
};
var
titleContext
=
{
el
:
'h2'
,
title
:
'list start'
},
$title
;
view
.
remove
();
view
.
remove
();
setFixtures
(
'<div class="test-container"><div class="program-cards-container"></div></div>'
);
setFixtures
(
'<div class="test-container"><div class="program-cards-container"></div></div>'
);
programCollection
=
new
ProgramCollection
(
context
.
programsData
);
programCollection
=
new
ProgramCollection
(
context
.
programsData
);
view
=
new
CollectionListView
({
view
=
new
CollectionListView
({
el
:
'.program-cards-container'
,
el
:
'.program-cards-container'
,
childView
:
ProgramCardView
,
childView
:
ProgramCardView
,
context
:
{
'xseriesUrl'
:
'/programs'
}
,
context
:
context
,
collection
:
programCollection
,
collection
:
programCollection
,
titleContext
:
titleContext
titleContext
:
titleContext
});
});
...
...
lms/static/js/spec/learner_dashboard/course_card_view_spec.js
View file @
dda0e03f
...
@@ -9,12 +9,14 @@ define([
...
@@ -9,12 +9,14 @@ define([
describe
(
'Course Card View'
,
function
()
{
describe
(
'Course Card View'
,
function
()
{
var
view
=
null
,
var
view
=
null
,
courseCardModel
,
courseCardModel
,
context
,
course
,
startDate
=
'Feb 28, 2017'
,
endDate
=
'May 30, 2017'
,
setupView
=
function
(
data
,
isEnrolled
)
{
setupView
=
function
(
data
,
isEnrolled
)
{
var
programData
=
$
.
extend
({},
data
);
var
programData
=
$
.
extend
({},
data
);
programData
.
run_mode
s
[
0
].
is_enrolled
=
isEnrolled
;
programData
.
course_run
s
[
0
].
is_enrolled
=
isEnrolled
;
setFixtures
(
'<div class="course-card card"></div>'
);
setFixtures
(
'<div class="course-card card"></div>'
);
courseCardModel
=
new
CourseCardModel
(
programData
);
courseCardModel
=
new
CourseCardModel
(
programData
);
view
=
new
CourseCardView
({
view
=
new
CourseCardView
({
...
@@ -24,48 +26,49 @@ define([
...
@@ -24,48 +26,49 @@ define([
validateCourseInfoDisplay
=
function
()
{
validateCourseInfoDisplay
=
function
()
{
// DRY validation for course card in enrolled state
// DRY validation for course card in enrolled state
expect
(
view
.
$
(
'.header-img'
).
attr
(
'src'
)).
toEqual
(
co
ntext
.
run_modes
[
0
].
course_image_url
);
expect
(
view
.
$
(
'.header-img'
).
attr
(
'src'
)).
toEqual
(
co
urse
.
course_runs
[
0
].
image
.
src
);
expect
(
view
.
$
(
'.course-details .course-title-link'
).
text
().
trim
()).
toEqual
(
co
ntext
.
display_nam
e
);
expect
(
view
.
$
(
'.course-details .course-title-link'
).
text
().
trim
()).
toEqual
(
co
urse
.
titl
e
);
expect
(
view
.
$
(
'.course-details .course-title-link'
).
attr
(
'href'
)).
toEqual
(
expect
(
view
.
$
(
'.course-details .course-title-link'
).
attr
(
'href'
)).
toEqual
(
co
ntext
.
run_mode
s
[
0
].
marketing_url
co
urse
.
course_run
s
[
0
].
marketing_url
);
);
expect
(
view
.
$
(
'.course-details .course-text .course-key'
).
html
()).
toEqual
(
co
ntext
.
key
);
expect
(
view
.
$
(
'.course-details .course-text .course-key'
).
html
()).
toEqual
(
co
urse
.
key
);
expect
(
view
.
$
(
'.course-details .course-text .run-period'
).
html
()).
toEqual
(
expect
(
view
.
$
(
'.course-details .course-text .run-period'
).
html
()).
toEqual
(
context
.
run_modes
[
0
].
start_date
+
' - '
+
context
.
run_modes
[
0
].
end_d
ate
startDate
+
' - '
+
endD
ate
);
);
};
};
beforeEach
(
function
()
{
beforeEach
(
function
()
{
// Redefine this data prior to each test case so that tests can't
// NOTE: This data is redefined prior to each test case so that tests
// break each other by modifying data copied by reference.
// can't break each other by modifying data copied by reference.
context
=
{
course
=
{
course_modes
:
[],
key
:
'WageningenX+FFESx'
,
display_name
:
'Astrophysics: Exploring Exoplanets'
,
uuid
:
'9f8562eb-f99b-45c7-b437-799fd0c15b6a'
,
key
:
'ANU-ASTRO1x'
,
title
:
'Systems thinking and environmental sustainability'
,
organization
:
{
course_runs
:
[
display_name
:
'Australian National University'
,
{
key
:
'ANUx'
key
:
'course-v1:WageningenX+FFESx+1T2017'
,
},
title
:
'Food Security and Sustainability: Systems thinking and environmental sustainability'
,
run_modes
:
[{
image
:
{
certificate_url
:
''
,
src
:
'https://example.com/9f8562eb-f99b-45c7-b437-799fd0c15b6a.jpg'
course_image_url
:
'http://test.com/image1'
,
},
course_key
:
'course-v1:ANUx+ANU-ASTRO1x+3T2015'
,
marketing_url
:
'https://www.edx.org/course/food-security-sustainability'
,
course_started
:
true
,
start
:
'2017-02-28T05:00:00Z'
,
course_url
:
'https://courses.example.com/courses/course-v1:edX+DemoX+Demo_Course'
,
end
:
'2017-05-30T23:00:00Z'
,
end_date
:
'Jun 13, 2019'
,
enrollment_start
:
'2017-01-18T00:00:00Z'
,
enrollment_open_date
:
'Apr 1, 2016'
,
enrollment_end
:
null
,
is_course_ended
:
false
,
type
:
'verified'
,
is_enrolled
:
true
,
certificate_url
:
''
,
is_enrollment_open
:
true
,
course_url
:
'https://courses.example.com/courses/course-v1:WageningenX+FFESx+1T2017'
,
marketing_url
:
'https://www.example.com/marketing/site'
,
enrollment_open_date
:
'Jan 18, 2016'
,
mode_slug
:
'verified'
,
is_course_ended
:
false
,
run_key
:
'2T2016'
,
is_enrolled
:
true
,
start_date
:
'Apr 25, 2016'
,
is_enrollment_open
:
true
,
upgrade_url
:
''
upgrade_url
:
''
}]
}
]
};
};
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
});
});
afterEach
(
function
()
{
afterEach
(
function
()
{
...
@@ -78,7 +81,7 @@ define([
...
@@ -78,7 +81,7 @@ define([
it
(
'should render the course card based on the data enrolled'
,
function
()
{
it
(
'should render the course card based on the data enrolled'
,
function
()
{
view
.
remove
();
view
.
remove
();
setupView
(
co
ntext
,
true
);
setupView
(
co
urse
,
true
);
validateCourseInfoDisplay
();
validateCourseInfoDisplay
();
});
});
...
@@ -94,11 +97,11 @@ define([
...
@@ -94,11 +97,11 @@ define([
});
});
it
(
'should show the course advertised start date'
,
function
()
{
it
(
'should show the course advertised start date'
,
function
()
{
var
advertisedStart
=
'
This is an advertised start
'
;
var
advertisedStart
=
'
A long time ago...
'
;
co
ntext
.
run_mode
s
[
0
].
advertised_start
=
advertisedStart
;
co
urse
.
course_run
s
[
0
].
advertised_start
=
advertisedStart
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
expect
(
view
.
$
(
'.course-details .course-text .run-period'
).
html
()).
toEqual
(
expect
(
view
.
$
(
'.course-details .course-text .run-period'
).
html
()).
toEqual
(
advertisedStart
+
' - '
+
context
.
run_modes
[
0
].
end_d
ate
advertisedStart
+
' - '
+
endD
ate
);
);
});
});
...
@@ -108,8 +111,8 @@ define([
...
@@ -108,8 +111,8 @@ define([
expect
(
view
.
$
(
'.certificate-status'
).
length
).
toEqual
(
0
);
expect
(
view
.
$
(
'.certificate-status'
).
length
).
toEqual
(
0
);
view
.
remove
();
view
.
remove
();
co
ntext
.
run_mode
s
[
0
].
certificate_url
=
certUrl
;
co
urse
.
course_run
s
[
0
].
certificate_url
=
certUrl
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
expect
(
view
.
$
(
'.certificate-status'
).
length
).
toEqual
(
1
);
expect
(
view
.
$
(
'.certificate-status'
).
length
).
toEqual
(
1
);
expect
(
view
.
$
(
'.certificate-status .cta-secondary'
).
attr
(
'href'
)).
toEqual
(
certUrl
);
expect
(
view
.
$
(
'.certificate-status .cta-secondary'
).
attr
(
'href'
)).
toEqual
(
certUrl
);
});
});
...
@@ -120,53 +123,53 @@ define([
...
@@ -120,53 +123,53 @@ define([
expect
(
view
.
$
(
'.upgrade-message'
).
length
).
toEqual
(
0
);
expect
(
view
.
$
(
'.upgrade-message'
).
length
).
toEqual
(
0
);
view
.
remove
();
view
.
remove
();
co
ntext
.
run_mode
s
[
0
].
upgrade_url
=
upgradeUrl
;
co
urse
.
course_run
s
[
0
].
upgrade_url
=
upgradeUrl
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
expect
(
view
.
$
(
'.upgrade-message'
).
length
).
toEqual
(
1
);
expect
(
view
.
$
(
'.upgrade-message'
).
length
).
toEqual
(
1
);
expect
(
view
.
$
(
'.upgrade-message .cta-primary'
).
attr
(
'href'
)).
toEqual
(
upgradeUrl
);
expect
(
view
.
$
(
'.upgrade-message .cta-primary'
).
attr
(
'href'
)).
toEqual
(
upgradeUrl
);
});
});
it
(
'should not show both the upgrade message and certificate status sections'
,
function
()
{
it
(
'should not show both the upgrade message and certificate status sections'
,
function
()
{
// Verify that no empty elements are left in the DOM.
// Verify that no empty elements are left in the DOM.
co
ntext
.
run_mode
s
[
0
].
upgrade_url
=
''
;
co
urse
.
course_run
s
[
0
].
upgrade_url
=
''
;
co
ntext
.
run_mode
s
[
0
].
certificate_url
=
''
;
co
urse
.
course_run
s
[
0
].
certificate_url
=
''
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
expect
(
view
.
$
(
'.upgrade-message'
).
length
).
toEqual
(
0
);
expect
(
view
.
$
(
'.upgrade-message'
).
length
).
toEqual
(
0
);
expect
(
view
.
$
(
'.certificate-status'
).
length
).
toEqual
(
0
);
expect
(
view
.
$
(
'.certificate-status'
).
length
).
toEqual
(
0
);
view
.
remove
();
view
.
remove
();
// Verify that the upgrade message takes priority.
// Verify that the upgrade message takes priority.
co
ntext
.
run_mode
s
[
0
].
upgrade_url
=
'/path/to/upgrade'
;
co
urse
.
course_run
s
[
0
].
upgrade_url
=
'/path/to/upgrade'
;
co
ntext
.
run_mode
s
[
0
].
certificate_url
=
'/path/to/certificate'
;
co
urse
.
course_run
s
[
0
].
certificate_url
=
'/path/to/certificate'
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
expect
(
view
.
$
(
'.upgrade-message'
).
length
).
toEqual
(
1
);
expect
(
view
.
$
(
'.upgrade-message'
).
length
).
toEqual
(
1
);
expect
(
view
.
$
(
'.certificate-status'
).
length
).
toEqual
(
0
);
expect
(
view
.
$
(
'.certificate-status'
).
length
).
toEqual
(
0
);
});
});
it
(
'should show a message if an there is an upcoming course run'
,
function
()
{
it
(
'should show a message if an there is an upcoming course run'
,
function
()
{
co
ntext
.
run_mode
s
[
0
].
is_enrollment_open
=
false
;
co
urse
.
course_run
s
[
0
].
is_enrollment_open
=
false
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
expect
(
view
.
$
(
'.header-img'
).
attr
(
'src'
)).
toEqual
(
co
ntext
.
run_modes
[
0
].
course_image_url
);
expect
(
view
.
$
(
'.header-img'
).
attr
(
'src'
)).
toEqual
(
co
urse
.
course_runs
[
0
].
image
.
src
);
expect
(
view
.
$
(
'.course-details .course-title'
).
text
().
trim
()).
toEqual
(
co
ntext
.
display_nam
e
);
expect
(
view
.
$
(
'.course-details .course-title'
).
text
().
trim
()).
toEqual
(
co
urse
.
titl
e
);
expect
(
view
.
$
(
'.course-details .course-text .course-key'
).
html
()).
toEqual
(
co
ntext
.
key
);
expect
(
view
.
$
(
'.course-details .course-text .course-key'
).
html
()).
toEqual
(
co
urse
.
key
);
expect
(
view
.
$
(
'.course-details .course-text .run-period'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.course-details .course-text .run-period'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.no-action-message'
).
text
().
trim
()).
toBe
(
'Coming Soon'
);
expect
(
view
.
$
(
'.no-action-message'
).
text
().
trim
()).
toBe
(
'Coming Soon'
);
expect
(
view
.
$
(
'.enrollment-open-date'
).
text
().
trim
()).
toEqual
(
expect
(
view
.
$
(
'.enrollment-open-date'
).
text
().
trim
()).
toEqual
(
context
.
run_mode
s
[
0
].
enrollment_open_date
course
.
course_run
s
[
0
].
enrollment_open_date
);
);
});
});
it
(
'should show a message if there are no
known
upcoming course runs'
,
function
()
{
it
(
'should show a message if there are no upcoming course runs'
,
function
()
{
co
ntext
.
run_mode
s
[
0
].
is_enrollment_open
=
false
;
co
urse
.
course_run
s
[
0
].
is_enrollment_open
=
false
;
co
ntext
.
run_mode
s
[
0
].
is_course_ended
=
true
;
co
urse
.
course_run
s
[
0
].
is_course_ended
=
true
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
expect
(
view
.
$
(
'.header-img'
).
attr
(
'src'
)).
toEqual
(
co
ntext
.
run_modes
[
0
].
course_image_url
);
expect
(
view
.
$
(
'.header-img'
).
attr
(
'src'
)).
toEqual
(
co
urse
.
course_runs
[
0
].
image
.
src
);
expect
(
view
.
$
(
'.course-details .course-title'
).
text
().
trim
()).
toEqual
(
co
ntext
.
display_nam
e
);
expect
(
view
.
$
(
'.course-details .course-title'
).
text
().
trim
()).
toEqual
(
co
urse
.
titl
e
);
expect
(
view
.
$
(
'.course-details .course-text .course-key'
).
html
()).
toEqual
(
co
ntext
.
key
);
expect
(
view
.
$
(
'.course-details .course-text .course-key'
).
html
()).
toEqual
(
co
urse
.
key
);
expect
(
view
.
$
(
'.course-details .course-text .run-period'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.course-details .course-text .run-period'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.no-action-message'
).
text
().
trim
()).
toBe
(
'Not Currently Available'
);
expect
(
view
.
$
(
'.no-action-message'
).
text
().
trim
()).
toBe
(
'Not Currently Available'
);
expect
(
view
.
$
(
'.enrollment-opens'
).
length
).
toEqual
(
0
);
expect
(
view
.
$
(
'.enrollment-opens'
).
length
).
toEqual
(
0
);
...
@@ -174,23 +177,23 @@ define([
...
@@ -174,23 +177,23 @@ define([
it
(
'should link to the marketing site when a URL is available'
,
function
()
{
it
(
'should link to the marketing site when a URL is available'
,
function
()
{
$
.
each
([
'.course-image-link'
,
'.course-title-link'
],
function
(
index
,
selector
)
{
$
.
each
([
'.course-image-link'
,
'.course-title-link'
],
function
(
index
,
selector
)
{
expect
(
view
.
$
(
selector
).
attr
(
'href'
)).
toEqual
(
co
ntext
.
run_mode
s
[
0
].
marketing_url
);
expect
(
view
.
$
(
selector
).
attr
(
'href'
)).
toEqual
(
co
urse
.
course_run
s
[
0
].
marketing_url
);
});
});
});
});
it
(
'should link to the course home when no marketing URL is available'
,
function
()
{
it
(
'should link to the course home when no marketing URL is available'
,
function
()
{
co
ntext
.
run_mode
s
[
0
].
marketing_url
=
null
;
co
urse
.
course_run
s
[
0
].
marketing_url
=
null
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
$
.
each
([
'.course-image-link'
,
'.course-title-link'
],
function
(
index
,
selector
)
{
$
.
each
([
'.course-image-link'
,
'.course-title-link'
],
function
(
index
,
selector
)
{
expect
(
view
.
$
(
selector
).
attr
(
'href'
)).
toEqual
(
co
ntext
.
run_mode
s
[
0
].
course_url
);
expect
(
view
.
$
(
selector
).
attr
(
'href'
)).
toEqual
(
co
urse
.
course_run
s
[
0
].
course_url
);
});
});
});
});
it
(
'should not link to the marketing site or the course home if neither URL is available'
,
function
()
{
it
(
'should not link to the marketing site or the course home if neither URL is available'
,
function
()
{
co
ntext
.
run_mode
s
[
0
].
marketing_url
=
null
;
co
urse
.
course_run
s
[
0
].
marketing_url
=
null
;
co
ntext
.
run_mode
s
[
0
].
course_url
=
null
;
co
urse
.
course_run
s
[
0
].
course_url
=
null
;
setupView
(
co
ntext
,
false
);
setupView
(
co
urse
,
false
);
$
.
each
([
'.course-image-link'
,
'.course-title-link'
],
function
(
index
,
selector
)
{
$
.
each
([
'.course-image-link'
,
'.course-title-link'
],
function
(
index
,
selector
)
{
expect
(
view
.
$
(
selector
).
length
).
toEqual
(
0
);
expect
(
view
.
$
(
selector
).
length
).
toEqual
(
0
);
...
...
lms/static/js/spec/learner_dashboard/course_enroll_view_spec.js
View file @
dda0e03f
...
@@ -13,75 +13,102 @@ define([
...
@@ -13,75 +13,102 @@ define([
courseEnrollModel
,
courseEnrollModel
,
urlModel
,
urlModel
,
setupView
,
setupView
,
singleRunModeList
,
singleCourseRunList
,
multiRunModeList
,
multiCourseRunList
,
context
=
{
course
=
{
display_name
:
'Edx Demo course'
,
key
:
'WageningenX+FFESx'
,
key
:
'edX+DemoX+Demo_Course'
,
uuid
:
'9f8562eb-f99b-45c7-b437-799fd0c15b6a'
,
organization
:
{
title
:
'Systems thinking and environmental sustainability'
,
display_name
:
'edx.org'
,
owners
:
[
key
:
'edX'
{
}
uuid
:
'0c6e5fa2-96e8-40b2-9ebe-c8b0df2a3b22'
,
key
:
'WageningenX'
,
name
:
'Wageningen University & Research'
}
]
},
},
urls
=
{
urls
=
{
dashboard_url
:
'/dashboard'
,
commerce_api_url
:
'/commerce'
,
id_verification_url
:
'/verify_student/start_flow/'
,
track_selection_url
:
'/select_track/course/'
track_selection_url
:
'/select_track/course/'
};
};
beforeEach
(
function
()
{
beforeEach
(
function
()
{
// Redefine this data prior to each test case so that tests can't
// NOTE: This data is redefined prior to each test case so that tests
// break each other by modifying data copied by reference.
// can't break each other by modifying data copied by reference.
singleRunModeList
=
[{
singleCourseRunList
=
[{
start_date
:
'Apr 25, 2016'
,
key
:
'course-v1:WageningenX+FFESx+1T2017'
,
end_date
:
'Jun 13, 2016'
,
uuid
:
'2f2edf03-79e6-4e39-aef0-65436a6ee344'
,
course_key
:
'course-v1:course-v1:edX+DemoX+Demo_Course'
,
title
:
'Food Security and Sustainability: Systems thinking and environmental sustainability'
,
course_url
:
'http://localhost:8000/courses/course-v1:edX+DemoX+Demo_Course/info'
,
image
:
{
course_image_url
:
'http://test.com/image1'
,
src
:
'https://example.com/2f2edf03-79e6-4e39-aef0-65436a6ee344.jpg'
marketing_url
:
'http://test.com/image2'
,
},
marketing_url
:
'https://www.edx.org/course/food-security-sustainability-systems-wageningenx-ffesx'
,
start
:
'2017-02-28T05:00:00Z'
,
end
:
'2017-05-30T23:00:00Z'
,
enrollment_start
:
'2017-01-18T00:00:00Z'
,
enrollment_end
:
null
,
type
:
'verified'
,
certificate_url
:
''
,
course_url
:
'https://courses.example.com/courses/course-v1:edX+DemoX+Demo_Course'
,
enrollment_open_date
:
'Jan 18, 2016'
,
is_course_ended
:
false
,
is_course_ended
:
false
,
mode_slug
:
'audit'
,
run_key
:
'2T2016'
,
is_enrolled
:
false
,
is_enrolled
:
false
,
is_enrollment_open
:
true
is_enrollment_open
:
true
,
upgrade_url
:
''
}];
}];
multiRunModeList
=
[{
multiCourseRunList
=
[{
start_date
:
'May 21, 2015'
,
key
:
'course-v1:WageningenX+FFESx+2T2016'
,
end_date
:
'Sep 21, 2015'
,
uuid
:
'9bbb7844-4848-44ab-8e20-0be6604886e9'
,
course_key
:
'course-v1:course-v1:edX+DemoX+Demo_Course'
,
title
:
'Food Security and Sustainability: Systems thinking and environmental sustainability'
,
course_url
:
'http://localhost:8000/courses/course-v1:edX+DemoX+Demo_Course/info'
,
image
:
{
course_image_url
:
'http://test.com/run_2_image_1'
,
src
:
'https://example.com/9bbb7844-4848-44ab-8e20-0be6604886e9.jpg'
marketing_url
:
'http://test.com/run_2_image_2'
,
},
mode_slug
:
'verified'
,
short_description
:
'Learn how to apply systems thinking to improve food production systems.'
,
marketing_url
:
'https://www.edx.org/course/food-security-sustainability-systems-wageningenx-stesx'
,
start
:
'2016-09-08T04:00:00Z'
,
end
:
'2016-11-11T00:00:00Z'
,
enrollment_start
:
null
,
enrollment_end
:
null
,
pacing_type
:
'instructor_paced'
,
type
:
'verified'
,
certificate_url
:
''
,
course_url
:
'https://courses.example.com/courses/course-v1:WageningenX+FFESx+2T2016'
,
enrollment_open_date
:
'Jan 18, 2016'
,
is_course_ended
:
false
,
is_course_ended
:
false
,
run_key
:
'1T2015'
,
is_enrolled
:
false
,
is_enrolled
:
false
,
is_enrollment_open
:
true
is_enrollment_open
:
true
},
{
},
{
start_date
:
'Sep 22, 2015'
,
key
:
'course-v1:WageningenX+FFESx+1T2017'
,
end_date
:
'Dec 28, 2015'
,
uuid
:
'2f2edf03-79e6-4e39-aef0-65436a6ee344'
,
course_key
:
'course-v1:course-v1:edX+DemoX+Demo_Course'
,
title
:
'Food Security and Sustainability: Systems thinking and environmental sustainability'
,
course_url
:
'http://localhost:8000/courses/course-v1:edX+DemoX+Demo_Course/info'
,
image
:
{
course_image_url
:
'http://test.com/run_3_image_1'
,
src
:
'https://example.com/2f2edf03-79e6-4e39-aef0-65436a6ee344.jpg'
marketing_url
:
'http://test.com/run_3_image_2'
,
},
marketing_url
:
'https://www.edx.org/course/food-security-sustainability-systems-wageningenx-ffesx'
,
start
:
'2017-02-28T05:00:00Z'
,
end
:
'2017-05-30T23:00:00Z'
,
enrollment_start
:
'2017-01-18T00:00:00Z'
,
enrollment_end
:
null
,
type
:
'verified'
,
certificate_url
:
''
,
course_url
:
'https://courses.example.com/courses/course-v1:WageningenX+FFESx+1T2017'
,
enrollment_open_date
:
'Jan 18, 2016'
,
is_course_ended
:
false
,
is_course_ended
:
false
,
mode_slug
:
'verified'
,
run_key
:
'2T2015'
,
is_enrolled
:
false
,
is_enrolled
:
false
,
is_enrollment_open
:
true
is_enrollment_open
:
true
}];
}];
});
});
setupView
=
function
(
runModes
,
urls
)
{
setupView
=
function
(
courseRuns
,
urlMap
)
{
co
ntext
.
run_modes
=
runMode
s
;
co
urse
.
course_runs
=
courseRun
s
;
setFixtures
(
'<div class="course-actions"></div>'
);
setFixtures
(
'<div class="course-actions"></div>'
);
courseCardModel
=
new
CourseCardModel
(
co
ntext
);
courseCardModel
=
new
CourseCardModel
(
co
urse
);
courseEnrollModel
=
new
CourseEnrollModel
({},
{
courseEnrollModel
=
new
CourseEnrollModel
({},
{
courseId
:
courseCardModel
.
get
(
'course_key'
)
courseId
:
courseCardModel
.
get
(
'course_
run_
key'
)
});
});
if
(
url
s
)
{
if
(
url
Map
)
{
urlModel
=
new
Backbone
.
Model
(
url
s
);
urlModel
=
new
Backbone
.
Model
(
url
Map
);
}
}
view
=
new
CourseEnrollView
({
view
=
new
CourseEnrollView
({
$parentEl
:
$
(
'.course-actions'
),
$parentEl
:
$
(
'.course-actions'
),
...
@@ -99,143 +126,183 @@ define([
...
@@ -99,143 +126,183 @@ define([
});
});
it
(
'should exist'
,
function
()
{
it
(
'should exist'
,
function
()
{
setupView
(
single
RunMode
List
);
setupView
(
single
CourseRun
List
);
expect
(
view
).
toBeDefined
();
expect
(
view
).
toBeDefined
();
});
});
it
(
'should render the course enroll view based on not enrolled data'
,
function
()
{
it
(
'should render the course enroll view when not enrolled'
,
function
()
{
setupView
(
singleRunModeList
);
setupView
(
singleCourseRunList
);
expect
(
view
.
$
(
'.enrollment-info'
).
html
().
trim
()).
toEqual
(
'not enrolled'
);
expect
(
view
.
$
(
'.enrollment-info'
).
html
().
trim
()).
toEqual
(
'Not Enrolled'
);
expect
(
view
.
$
(
'.enroll-button'
).
text
().
trim
()).
toEqual
(
'Enroll Now'
);
expect
(
view
.
$
(
'.enroll-button'
).
text
().
trim
()).
toEqual
(
'Enroll Now'
);
expect
(
view
.
$
(
'.run-select'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.run-select'
).
length
).
toBe
(
0
);
});
});
it
(
'should render the course enroll view
based on enrolled data
'
,
function
()
{
it
(
'should render the course enroll view
when enrolled
'
,
function
()
{
single
RunMode
List
[
0
].
is_enrolled
=
true
;
single
CourseRun
List
[
0
].
is_enrolled
=
true
;
setupView
(
single
RunMode
List
);
setupView
(
single
CourseRun
List
);
expect
(
view
.
$
(
'.enrollment-info'
).
html
().
trim
()).
toEqual
(
'enrolled'
);
expect
(
view
.
$
(
'.enrollment-info'
).
html
().
trim
()).
toEqual
(
'enrolled'
);
expect
(
view
.
$
(
'.view-course-link'
).
attr
(
'href'
)).
toEqual
(
co
ntext
.
run_mode
s
[
0
].
course_url
);
expect
(
view
.
$
(
'.view-course-link'
).
attr
(
'href'
)).
toEqual
(
co
urse
.
course_run
s
[
0
].
course_url
);
expect
(
view
.
$
(
'.view-course-link'
).
text
().
trim
()).
toEqual
(
'View Course'
);
expect
(
view
.
$
(
'.view-course-link'
).
text
().
trim
()).
toEqual
(
'View Course'
);
expect
(
view
.
$
(
'.run-select'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.run-select'
).
length
).
toBe
(
0
);
});
});
it
(
'should allow the learner to view an archived course'
,
function
()
{
it
(
'should allow the learner to view an archived course'
,
function
()
{
// Regression test for ECOM-4974.
// Regression test for ECOM-4974.
single
RunMode
List
[
0
].
is_enrolled
=
true
;
single
CourseRun
List
[
0
].
is_enrolled
=
true
;
single
RunMode
List
[
0
].
is_enrollment_open
=
false
;
single
CourseRun
List
[
0
].
is_enrollment_open
=
false
;
single
RunMode
List
[
0
].
is_course_ended
=
true
;
single
CourseRun
List
[
0
].
is_course_ended
=
true
;
setupView
(
single
RunMode
List
);
setupView
(
single
CourseRun
List
);
expect
(
view
.
$
(
'.view-course-link'
).
text
().
trim
()).
toEqual
(
'View Archived Course'
);
expect
(
view
.
$
(
'.view-course-link'
).
text
().
trim
()).
toEqual
(
'View Archived Course'
);
});
});
it
(
'should not render anything if
run modes is
empty'
,
function
()
{
it
(
'should not render anything if
course runs are
empty'
,
function
()
{
setupView
([]);
setupView
([]);
expect
(
view
.
$
(
'.enrollment-info'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.enrollment-info'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.run-select'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.run-select'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
0
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
0
);
});
});
it
(
'should render run selection drop down if mulitple run available'
,
function
()
{
it
(
'should render run selection dropdown if multiple course runs are available'
,
function
()
{
setupView
(
multiRunModeList
);
setupView
(
multiCourseRunList
);
expect
(
view
.
$
(
'.run-select'
).
length
).
toBe
(
1
);
expect
(
view
.
$
(
'.run-select'
).
length
).
toBe
(
1
);
expect
(
view
.
$
(
'.run-select'
).
val
()).
toEqual
(
''
);
expect
(
view
.
$
(
'.run-select'
).
val
()).
toEqual
(
''
);
expect
(
view
.
$
(
'.run-select option'
).
length
).
toBe
(
3
);
expect
(
view
.
$
(
'.run-select option'
).
length
).
toBe
(
3
);
});
});
it
(
'should switch run context if dropdown selection changed'
,
function
()
{
it
(
'should switch course run context if an option is selected from the dropdown'
,
function
()
{
setupView
(
multiRunModeList
);
setupView
(
multiCourseRunList
);
spyOn
(
courseCardModel
,
'updateRun'
).
and
.
callThrough
();
spyOn
(
courseCardModel
,
'updateCourseRun'
).
and
.
callThrough
();
expect
(
view
.
$
(
'.run-select'
).
val
()).
toEqual
(
''
);
expect
(
view
.
$
(
'.run-select'
).
val
()).
toEqual
(
''
);
view
.
$
(
'.run-select'
).
val
(
multiRunModeList
[
1
].
run_key
);
view
.
$
(
'.run-select'
).
val
(
multiCourseRunList
[
1
].
key
);
view
.
$
(
'.run-select'
).
trigger
(
'change'
);
view
.
$
(
'.run-select'
).
trigger
(
'change'
);
expect
(
view
.
$
(
'.run-select'
).
val
()).
toEqual
(
multiRunModeList
[
1
].
run_key
);
expect
(
courseCardModel
.
updateRun
)
expect
(
view
.
$
(
'.run-select'
).
val
()).
toEqual
(
multiCourseRunList
[
1
].
key
);
.
toHaveBeenCalledWith
(
multiRunModeList
[
1
].
run_key
);
expect
(
courseCardModel
.
updateCourseRun
)
expect
(
courseCardModel
.
get
(
'run_key'
)).
toEqual
(
multiRunModeList
[
1
].
run_key
);
.
toHaveBeenCalledWith
(
multiCourseRunList
[
1
].
key
);
expect
(
courseCardModel
.
get
(
'course_key'
)).
toEqual
(
course
.
key
);
});
});
it
(
'should enroll learner when enroll button clicked'
,
function
()
{
it
(
'should enroll learner when enroll button is clicked with one course run available'
,
function
()
{
setupView
(
singleRunModeList
);
setupView
(
singleCourseRunList
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
spyOn
(
courseEnrollModel
,
'save'
);
spyOn
(
courseEnrollModel
,
'save'
);
view
.
$
(
'.enroll-button'
).
click
();
view
.
$
(
'.enroll-button'
).
click
();
expect
(
courseEnrollModel
.
save
).
toHaveBeenCalled
();
expect
(
courseEnrollModel
.
save
).
toHaveBeenCalled
();
});
});
it
(
'should enroll learner into the updated run with button click'
,
function
()
{
it
(
'should enroll learner when enroll button is clicked with multiple course runs available'
,
function
()
{
setupView
(
multiRunModeList
);
setupView
(
multiCourseRunList
);
spyOn
(
courseEnrollModel
,
'save'
);
spyOn
(
courseEnrollModel
,
'save'
);
view
.
$
(
'.run-select'
).
val
(
multiRunModeList
[
1
].
run_key
);
view
.
$
(
'.run-select'
).
val
(
multiCourseRunList
[
1
].
key
);
view
.
$
(
'.run-select'
).
trigger
(
'change'
);
view
.
$
(
'.run-select'
).
trigger
(
'change'
);
view
.
$
(
'.enroll-button'
).
click
();
view
.
$
(
'.enroll-button'
).
click
();
expect
(
courseEnrollModel
.
save
).
toHaveBeenCalled
();
expect
(
courseEnrollModel
.
save
).
toHaveBeenCalled
();
});
});
it
(
'should redirect to trackSelectionUrl when enrollment success for audit track'
,
function
()
{
it
(
'should redirect to track selection when audit enrollment succeeds'
,
function
()
{
singleRunModeList
[
0
].
is_enrolled
=
false
;
singleCourseRunList
[
0
].
is_enrolled
=
false
;
singleRunModeList
[
0
].
mode_slug
=
'audit'
;
singleCourseRunList
[
0
].
mode_slug
=
'audit'
;
setupView
(
singleRunModeList
,
urls
);
setupView
(
singleCourseRunList
,
urls
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
trackSelectionUrl
).
toBeDefined
();
expect
(
view
.
trackSelectionUrl
).
toBeDefined
();
spyOn
(
view
,
'redirect'
);
spyOn
(
view
,
'redirect'
);
view
.
enrollSuccess
();
view
.
enrollSuccess
();
expect
(
view
.
redirect
).
toHaveBeenCalledWith
(
expect
(
view
.
redirect
).
toHaveBeenCalledWith
(
view
.
trackSelectionUrl
+
courseCardModel
.
get
(
'course
_key'
));
view
.
trackSelectionUrl
+
courseCardModel
.
get
(
'course_run
_key'
));
});
});
it
(
'should redirect to track selection when enrollment in an unspecified mode is attempted'
,
function
()
{
singleCourseRunList
[
0
].
is_enrolled
=
false
;
singleCourseRunList
[
0
].
mode_slug
=
null
;
setupView
(
singleCourseRunList
,
urls
);
it
(
'should redirect when enrollment success for no track'
,
function
()
{
singleRunModeList
[
0
].
is_enrolled
=
false
;
singleRunModeList
[
0
].
mode_slug
=
null
;
setupView
(
singleRunModeList
,
urls
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
trackSelectionUrl
).
toBeDefined
();
expect
(
view
.
trackSelectionUrl
).
toBeDefined
();
spyOn
(
view
,
'redirect'
);
spyOn
(
view
,
'redirect'
);
view
.
enrollSuccess
();
view
.
enrollSuccess
();
expect
(
view
.
redirect
).
toHaveBeenCalledWith
(
expect
(
view
.
redirect
).
toHaveBeenCalledWith
(
view
.
trackSelectionUrl
+
courseCardModel
.
get
(
'course_key'
));
view
.
trackSelectionUrl
+
courseCardModel
.
get
(
'course_run_key'
)
);
});
});
it
(
'should not redirect when urls not provided'
,
function
()
{
it
(
'should not redirect when urls are not provided'
,
function
()
{
singleRunModeList
[
0
].
is_enrolled
=
false
;
singleCourseRunList
[
0
].
is_enrolled
=
false
;
singleRunModeList
[
0
].
mode_slug
=
'verified'
;
singleCourseRunList
[
0
].
mode_slug
=
'verified'
;
setupView
(
singleRunModeList
);
setupView
(
singleCourseRunList
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
verificationUrl
).
not
.
toBeDefined
();
expect
(
view
.
verificationUrl
).
not
.
toBeDefined
();
expect
(
view
.
dashboardUrl
).
not
.
toBeDefined
();
expect
(
view
.
dashboardUrl
).
not
.
toBeDefined
();
expect
(
view
.
trackSelectionUrl
).
not
.
toBeDefined
();
expect
(
view
.
trackSelectionUrl
).
not
.
toBeDefined
();
spyOn
(
view
,
'redirect'
);
spyOn
(
view
,
'redirect'
);
view
.
enrollSuccess
();
view
.
enrollSuccess
();
expect
(
view
.
redirect
).
not
.
toHaveBeenCalled
();
expect
(
view
.
redirect
).
not
.
toHaveBeenCalled
();
});
});
it
(
'should redirect to track selection on error'
,
function
()
{
it
(
'should redirect to track selection on error'
,
function
()
{
setupView
(
singleRunModeList
,
urls
);
setupView
(
singleCourseRunList
,
urls
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
trackSelectionUrl
).
toBeDefined
();
expect
(
view
.
trackSelectionUrl
).
toBeDefined
();
spyOn
(
view
,
'redirect'
);
spyOn
(
view
,
'redirect'
);
view
.
enrollError
(
courseEnrollModel
,
{
status
:
500
});
view
.
enrollError
(
courseEnrollModel
,
{
status
:
500
});
expect
(
view
.
redirect
).
toHaveBeenCalledWith
(
expect
(
view
.
redirect
).
toHaveBeenCalledWith
(
view
.
trackSelectionUrl
+
courseCardModel
.
get
(
'course_key'
));
view
.
trackSelectionUrl
+
courseCardModel
.
get
(
'course_run_key'
)
);
});
});
it
(
'should redirect to login on 403 error'
,
function
()
{
it
(
'should redirect to login on 403 error'
,
function
()
{
var
response
=
{
var
response
=
{
status
:
403
,
status
:
403
,
responseJSON
:
{
responseJSON
:
{
user_message_url
:
'test_url/haha'
user_message_url
:
'redirect/to/this'
}};
}
setupView
(
singleRunModeList
,
urls
);
};
setupView
(
singleCourseRunList
,
urls
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
$
(
'.enroll-button'
).
length
).
toBe
(
1
);
expect
(
view
.
trackSelectionUrl
).
toBeDefined
();
expect
(
view
.
trackSelectionUrl
).
toBeDefined
();
spyOn
(
view
,
'redirect'
);
spyOn
(
view
,
'redirect'
);
view
.
enrollError
(
courseEnrollModel
,
response
);
view
.
enrollError
(
courseEnrollModel
,
response
);
expect
(
view
.
redirect
).
toHaveBeenCalledWith
(
expect
(
view
.
redirect
).
toHaveBeenCalledWith
(
response
.
responseJSON
.
user_message_url
);
response
.
responseJSON
.
user_message_url
);
});
});
});
});
}
}
...
...
lms/static/js/spec/learner_dashboard/program_card_view_spec.js
View file @
dda0e03f
define
([
define
([
'backbone'
,
'backbone'
,
'underscore'
,
'jquery'
,
'jquery'
,
'js/learner_dashboard/collections/program_progress_collection'
,
'js/learner_dashboard/collections/program_progress_collection'
,
'js/learner_dashboard/models/program_model'
,
'js/learner_dashboard/models/program_model'
,
'js/learner_dashboard/views/program_card_view'
'js/learner_dashboard/views/program_card_view'
],
function
(
Backbone
,
$
,
ProgressCollection
,
ProgramModel
,
ProgramCardView
)
{
],
function
(
Backbone
,
_
,
$
,
ProgressCollection
,
ProgramModel
,
ProgramCardView
)
{
'use strict'
;
'use strict'
;
/* jslint maxlen: 500 */
/* jslint maxlen: 500 */
describe
(
'Program card View'
,
function
()
{
describe
(
'Program card View'
,
function
()
{
var
view
=
null
,
var
view
=
null
,
programModel
,
programModel
,
program
=
{
program
=
{
category
:
'FooBar'
,
uuid
:
'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8'
,
status
:
'active'
,
title
:
'Food Security and Sustainability'
,
subtitle
:
'program 1'
,
subtitle
:
'Learn how to feed all people in the world in a sustainable way.'
,
name
:
'test program 1'
,
type
:
'XSeries'
,
organizations
:
[
detail_url
:
'https://www.edx.org/foo/bar'
,
banner_image
:
{
medium
:
{
height
:
242
,
width
:
726
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.medium.jpg'
},
'x-small'
:
{
height
:
116
,
width
:
348
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.x-small.jpg'
},
small
:
{
height
:
145
,
width
:
435
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.small.jpg'
},
large
:
{
height
:
480
,
width
:
1440
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.large.jpg'
}
},
authoring_organizations
:
[
{
{
display_name
:
'edX'
,
uuid
:
'0c6e5fa2-96e8-40b2-9ebe-c8b0df2a3b22'
,
key
:
'edx'
key
:
'WageningenX'
,
name
:
'Wageningen University & Research'
}
}
],
]
created
:
'2016-03-03T19:18:50.061136Z'
,
modified
:
'2016-03-25T13:45:21.220732Z'
,
marketing_slug
:
'p_2?param=haha&test=b'
,
id
:
146
,
detail_url
:
'http://courses.edx.org/dashboard/programs/1/foo'
,
banner_image_urls
:
{
w348h116
:
'http://www.edx.org/images/test1'
,
w435h145
:
'http://www.edx.org/images/test2'
,
w726h242
:
'http://www.edx.org/images/test3'
}
},
},
userProgress
=
[
userProgress
=
[
{
{
id
:
146
,
uuid
:
'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8'
,
completed
:
[
'courses'
,
'the'
,
'user'
,
'completed'
]
,
completed
:
4
,
in_progress
:
[
'in'
,
'progress'
]
,
in_progress
:
2
,
not_started
:
[
'courses'
,
'not'
,
'yet'
,
'started'
]
not_started
:
4
},
},
{
{
id
:
147
,
uuid
:
'91d144d2-1bb1-4afe-90df-d5cff63fa6e2'
,
completed
:
[
'Course 1'
]
,
completed
:
1
,
in_progress
:
[]
,
in_progress
:
0
,
not_started
:
[
'Course 2'
,
'Course 3'
,
'Course 4'
]
not_started
:
3
}
}
],
],
progressCollection
=
new
ProgressCollection
(),
progressCollection
=
new
ProgressCollection
(),
cardRenders
=
function
(
$card
)
{
cardRenders
=
function
(
$card
)
{
expect
(
$card
).
toBeDefined
();
expect
(
$card
).
toBeDefined
();
expect
(
$card
.
find
(
'.title'
).
html
().
trim
()).
toEqual
(
program
.
nam
e
);
expect
(
$card
.
find
(
'.title'
).
html
().
trim
()).
toEqual
(
program
.
titl
e
);
expect
(
$card
.
find
(
'.category span'
).
html
().
trim
()).
toEqual
(
program
.
category
);
expect
(
$card
.
find
(
'.category span'
).
html
().
trim
()).
toEqual
(
program
.
type
);
expect
(
$card
.
find
(
'.organization'
).
html
().
trim
()).
toEqual
(
program
.
organizations
[
0
].
key
);
expect
(
$card
.
find
(
'.organization'
).
html
().
trim
()).
toEqual
(
program
.
authoring_
organizations
[
0
].
key
);
expect
(
$card
.
find
(
'.card-link'
).
attr
(
'href'
)).
toEqual
(
program
.
detail_url
);
expect
(
$card
.
find
(
'.card-link'
).
attr
(
'href'
)).
toEqual
(
program
.
detail_url
);
};
};
...
@@ -87,12 +102,16 @@ define([
...
@@ -87,12 +102,16 @@ define([
});
});
it
(
'should handle exceptions from reEvaluatePicture'
,
function
()
{
it
(
'should handle exceptions from reEvaluatePicture'
,
function
()
{
var
message
=
'Picturefill had exceptions'
;
spyOn
(
view
,
'reEvaluatePicture'
).
and
.
callFake
(
function
()
{
spyOn
(
view
,
'reEvaluatePicture'
).
and
.
callFake
(
function
()
{
throw
{
name
:
'Picturefill had exceptions'
};
var
error
=
{
name
:
message
};
throw
error
;
});
});
view
.
reLoadBannerImage
();
view
.
reLoadBannerImage
();
expect
(
view
.
reEvaluatePicture
).
toHaveBeenCalled
();
expect
(
view
.
reEvaluatePicture
).
toHaveBeenCalled
();
expect
(
view
.
reLoadBannerImage
).
not
.
toThrow
(
'Picturefill had exceptions'
);
expect
(
view
.
reLoadBannerImage
).
not
.
toThrow
(
message
);
});
});
it
(
'should calculate the correct percentages for progress bars'
,
function
()
{
it
(
'should calculate the correct percentages for progress bars'
,
function
()
{
...
@@ -101,11 +120,12 @@ define([
...
@@ -101,11 +120,12 @@ define([
});
});
it
(
'should display the correct completed courses message'
,
function
()
{
it
(
'should display the correct completed courses message'
,
function
()
{
var
program
=
_
.
findWhere
(
userProgress
,
{
id
:
146
}),
var
program
Progress
=
_
.
findWhere
(
userProgress
,
{
uuid
:
'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8'
}),
completed
=
program
.
completed
.
length
,
completed
=
program
Progress
.
completed
,
total
=
completed
+
program
.
in_progress
.
length
+
program
.
not_started
.
length
;
total
=
completed
+
program
Progress
.
in_progress
+
programProgress
.
not_started
;
expect
(
view
.
$
(
'.certificate-status .status-text'
).
not
(
'.secondary'
).
html
()).
toEqual
(
'You have earned certificates in '
+
completed
+
' of the '
+
total
+
' courses so far.'
);
expect
(
view
.
$
(
'.certificate-status .status-text'
).
not
(
'.secondary'
).
html
())
.
toEqual
(
'You have earned certificates in '
+
completed
+
' of the '
+
total
+
' courses so far.'
);
});
});
it
(
'should render cards if there is no progressData'
,
function
()
{
it
(
'should render cards if there is no progressData'
,
function
()
{
...
...
lms/static/js/spec/learner_dashboard/program_details_header_spec.js
View file @
dda0e03f
...
@@ -12,23 +12,42 @@ define([
...
@@ -12,23 +12,42 @@ define([
program_listing_url
:
'/dashboard/programs'
program_listing_url
:
'/dashboard/programs'
},
},
programData
:
{
programData
:
{
uuid
:
'12-ab'
,
uuid
:
'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8'
,
name
:
'Astrophysics'
,
title
:
'Food Security and Sustainability'
,
subtitle
:
'Learn contemporary astrophysics from the leaders in the field.'
,
subtitle
:
'Learn how to feed all people in the world in a sustainable way.'
,
category
:
'xseries'
,
type
:
'XSeries'
,
organizations
:
[
detail_url
:
'https://www.edx.org/foo/bar'
,
{
banner_image
:
{
display_name
:
'Australian National University'
,
medium
:
{
img
:
'common/test/data/static/picture1.jpg'
,
height
:
242
,
key
:
'ANUx'
width
:
726
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.medium.jpg'
},
'x-small'
:
{
height
:
116
,
width
:
348
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.x-small.jpg'
},
small
:
{
height
:
145
,
width
:
435
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.small.jpg'
},
large
:
{
height
:
480
,
width
:
1440
,
url
:
'https://example.com/a87e5eac-3c93-45a1-a8e1-4c79ca8401c8.large.jpg'
}
}
],
banner_image_urls
:
{
w1440h480
:
'common/test/data/static/picture1.jpg'
,
w726h242
:
'common/test/data/static/picture2.jpg'
,
w348h116
:
'common/test/data/static/picture3.jpg'
},
},
program_details_url
:
'/dashboard/programs'
authoring_organizations
:
[
{
uuid
:
'0c6e5fa2-96e8-40b2-9ebe-c8b0df2a3b22'
,
key
:
'WageningenX'
,
name
:
'Wageningen University & Research'
,
certificate_logo_image_url
:
'https://example.com/org-certificate-logo.jpg'
,
logo_image_url
:
'https://example.com/org-logo.jpg'
}
]
}
}
};
};
...
@@ -51,13 +70,13 @@ define([
...
@@ -51,13 +70,13 @@ define([
it
(
'should render the header based on the passed in model'
,
function
()
{
it
(
'should render the header based on the passed in model'
,
function
()
{
var
programListUrl
=
view
.
$
(
'.breadcrumb-list .crumb:nth-of-type(2) .crumb-link'
).
attr
(
'href'
);
var
programListUrl
=
view
.
$
(
'.breadcrumb-list .crumb:nth-of-type(2) .crumb-link'
).
attr
(
'href'
);
expect
(
view
.
$
(
'.title'
).
html
()).
toEqual
(
context
.
programData
.
nam
e
);
expect
(
view
.
$
(
'.title'
).
html
()).
toEqual
(
context
.
programData
.
titl
e
);
expect
(
view
.
$
(
'.subtitle'
).
html
()).
toEqual
(
context
.
programData
.
subtitle
);
expect
(
view
.
$
(
'.subtitle'
).
html
()).
toEqual
(
context
.
programData
.
subtitle
);
expect
(
view
.
$
(
'.org-logo'
).
length
).
toEqual
(
context
.
programData
.
organizations
.
length
);
expect
(
view
.
$
(
'.org-logo'
).
length
).
toEqual
(
context
.
programData
.
authoring_
organizations
.
length
);
expect
(
view
.
$
(
'.org-logo'
).
attr
(
'src'
))
.
toEqual
(
context
.
programData
.
organizations
[
0
].
img
);
expect
(
view
.
$
(
'.org-logo'
).
attr
(
'src'
))
expect
(
view
.
$
(
'.org-logo'
).
attr
(
'alt'
)).
toEqual
(
.
toEqual
(
context
.
programData
.
authoring_organizations
[
0
].
certificate_logo_image_url
);
context
.
programData
.
organizations
[
0
].
display_name
+
'
\'
s logo'
expect
(
view
.
$
(
'.org-logo'
).
attr
(
'alt'
))
);
.
toEqual
(
context
.
programData
.
authoring_organizations
[
0
].
name
+
'
\'
s logo'
);
expect
(
programListUrl
).
toEqual
(
context
.
urls
.
program_listing_url
);
expect
(
programListUrl
).
toEqual
(
context
.
urls
.
program_listing_url
);
});
});
});
});
...
...
lms/templates/dashboard.html
View file @
dda0e03f
...
@@ -98,7 +98,7 @@ from openedx.core.djangolib.markup import HTML, Text
...
@@ -98,7 +98,7 @@ from openedx.core.djangolib.markup import HTML, Text
<
%
is_course_blocked =
(enrollment.course_id
in
block_courses
)
%
>
<
%
is_course_blocked =
(enrollment.course_id
in
block_courses
)
%
>
<
%
course_verification_status =
verification_status_by_course.get(enrollment.course_id,
{})
%
>
<
%
course_verification_status =
verification_status_by_course.get(enrollment.course_id,
{})
%
>
<
%
course_requirements =
courses_requirements_not_met.get(enrollment.course_id)
%
>
<
%
course_requirements =
courses_requirements_not_met.get(enrollment.course_id)
%
>
<
%
related_programs =
programs_by_run
.get(unicode(enrollment.course_id))
%
>
<
%
related_programs =
inverted_programs
.get(unicode(enrollment.course_id))
%
>
<
%
include
file=
'dashboard/_dashboard_course_listing.html'
args=
'course_overview=enrollment.course_overview, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, show_refund_option=show_refund_option, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard'
/>
<
%
include
file=
'dashboard/_dashboard_course_listing.html'
args=
'course_overview=enrollment.course_overview, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, show_refund_option=show_refund_option, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs, display_course_modes_on_dashboard=display_course_modes_on_dashboard'
/>
% endfor
% endfor
...
...
lms/templates/dashboard/_dashboard_course_listing.html
View file @
dda0e03f
...
@@ -300,8 +300,8 @@ from student.helpers import (
...
@@ -300,8 +300,8 @@ from student.helpers import (
<ul>
<ul>
% for program in related_programs:
% for program in related_programs:
<li>
<li>
<span
class=
"category-icon ${program['
category
'].lower()}-icon"
aria-hidden=
"true"
></span>
<span
class=
"category-icon ${program['
type
'].lower()}-icon"
aria-hidden=
"true"
></span>
<span><a
href=
"${program['detail_url']}"
>
${u'{
name} {category}'.format(name=program['name'], category=program['category
'])}
</a></span>
<span><a
href=
"${program['detail_url']}"
>
${u'{
title} {type}'.format(title=program['title'], type=program['type
'])}
</a></span>
</li>
</li>
% endfor
% endfor
</ul>
</ul>
...
@@ -397,12 +397,6 @@ from student.helpers import (
...
@@ -397,12 +397,6 @@ from student.helpers import (
</div>
</div>
%endif
%endif
% if course_program_info and course_program_info.get('category'):
%for program_data in course_program_info.get('course_program_list', []):
<
%
include
file =
"_dashboard_program_info.html"
args=
"program_data=program_data, enrollment_mode=enrollment.mode, category=course_program_info['category']"
/>
%endfor
% endif
% if is_course_blocked:
% if is_course_blocked:
<p
id=
"block-course-msg"
class=
"course-block"
>
<p
id=
"block-course-msg"
class=
"course-block"
>
${Text(_("You can no longer access this course because payment has not yet been received. "
${Text(_("You can no longer access this course because payment has not yet been received. "
...
...
lms/templates/learner_dashboard/course_card.underscore
View file @
dda0e03f
...
@@ -7,7 +7,7 @@
...
@@ -7,7 +7,7 @@
class="header-img"
class="header-img"
src="<%- course_image_url %>"
src="<%- course_image_url %>"
<% // safe-lint: disable=underscore-not-escaped %>
<% // safe-lint: disable=underscore-not-escaped %>
alt="<%= interpolate(gettext('%(courseName)s Home Page.'), {courseName:
display_nam
e}, true) %>"/>
alt="<%= interpolate(gettext('%(courseName)s Home Page.'), {courseName:
titl
e}, true) %>"/>
</a>
</a>
<% } else { %>
<% } else { %>
<img class="header-img" src="<%- course_image_url %>" alt=""/>
<img class="header-img" src="<%- course_image_url %>" alt=""/>
...
@@ -18,10 +18,10 @@
...
@@ -18,10 +18,10 @@
<h3 class="course-title">
<h3 class="course-title">
<% if ( marketing_url || course_url ) { %>
<% if ( marketing_url || course_url ) { %>
<a href="<%- marketing_url || course_url %>" class="course-title-link">
<a href="<%- marketing_url || course_url %>" class="course-title-link">
<%-
display_nam
e %>
<%-
titl
e %>
</a>
</a>
<% } else { %>
<% } else { %>
<%-
display_nam
e %>
<%-
titl
e %>
<% } %>
<% } %>
</h3>
</h3>
<div class="course-text">
<div class="course-text">
...
@@ -29,7 +29,7 @@
...
@@ -29,7 +29,7 @@
<span class="run-period"><%- start_date %> - <%- end_date %></span>
<span class="run-period"><%- start_date %> - <%- end_date %></span>
-
-
<% } %>
<% } %>
<span class="course-key"><%- key %></span>
<span class="course-key"><%-
course_
key %></span>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
lms/templates/learner_dashboard/course_enroll.underscore
View file @
dda0e03f
...
@@ -10,9 +10,9 @@
...
@@ -10,9 +10,9 @@
</a>
</a>
<% } %>
<% } %>
<% } else { %>
<% } else { %>
<% if (enrollable_
run_mode
s.length > 0) { %>
<% if (enrollable_
course_run
s.length > 0) { %>
<div class="enrollment-info"><%- gettext('
not e
nrolled') %></div>
<div class="enrollment-info"><%- gettext('
Not E
nrolled') %></div>
<% if (enrollable_
run_mode
s.length > 1) { %>
<% if (enrollable_
course_run
s.length > 1) { %>
<div class="run-select-container">
<div class="run-select-container">
<div class="select-error">
<div class="select-error">
<%- gettext('Please select a course date') %>
<%- gettext('Please select a course date') %>
...
@@ -24,16 +24,16 @@
...
@@ -24,16 +24,16 @@
<option value="" selected="selected">
<option value="" selected="selected">
<%- gettext('Choose Course Date') %>
<%- gettext('Choose Course Date') %>
</option>
</option>
<% _.each (enrollable_
run_modes, function(runMode
) { %>
<% _.each (enrollable_
course_runs, function(courseRun
) { %>
<option
<option
value="<%-
runMode.run_
key %>"
value="<%-
courseRun.
key %>"
<% if (
run_key === runMode.run_
key) { %>
<% if (
key === courseRun.
key) { %>
selected="selected"
selected="selected"
<% }%>
<% }%>
>
>
<%= interpolate(
<%= interpolate(
gettext('Starts %(start)s'),
gettext('Starts %(start)s'),
{ start:
runMode
.start_date },
{ start:
courseRun
.start_date },
true)
true)
%>
%>
</option>
</option>
...
@@ -44,14 +44,14 @@
...
@@ -44,14 +44,14 @@
<button type="button" class="btn-brand btn cta-primary enroll-button">
<button type="button" class="btn-brand btn cta-primary enroll-button">
<%- gettext('Enroll Now') %>
<%- gettext('Enroll Now') %>
</button>
</button>
<% } else if (upcoming_
run_mode
s.length > 0) {%>
<% } else if (upcoming_
course_run
s.length > 0) {%>
<div class="no-action-message">
<div class="no-action-message">
<%- gettext('Coming Soon') %>
<%- gettext('Coming Soon') %>
</div>
</div>
<div class="enrollment-opens">
<div class="enrollment-opens">
<%- gettext('Enrollment Opens on') %>
<%- gettext('Enrollment Opens on') %>
<span class="enrollment-open-date">
<span class="enrollment-open-date">
<%- upcoming_
run_mode
s[0].enrollment_open_date %>
<%- upcoming_
course_run
s[0].enrollment_open_date %>
</span>
</span>
</div>
</div>
<% } else { %>
<% } else { %>
...
...
lms/templates/learner_dashboard/program_card.underscore
View file @
dda0e03f
<div class="text-section">
<div class="text-section">
<h3 id="program-<%-
id %>" class="title hd-3"><%- gettext(nam
e) %></h3>
<h3 id="program-<%-
uuid %>" class="title hd-3"><%- gettext(titl
e) %></h3>
<div class="meta-info grid-container">
<div class="meta-info grid-container">
<div class="organization col"><%- orgList %></div>
<div class="organization col"><%- orgList %></div>
<div class="category col col-last">
<div class="category col col-last">
<span class="category-text"><%- gettext(
category
) %></span>
<span class="category-text"><%- gettext(
type
) %></span>
<span class="category-icon <%-
category
.toLowerCase() %>-icon" aria-hidden="true"></span>
<span class="category-icon <%-
type
.toLowerCase() %>-icon" aria-hidden="true"></span>
</div>
</div>
</div>
</div>
<% if (progress) { %>
<% if (progress) { %>
<p class="certificate-status">
<p class="certificate-status">
<a href="<%- detailUrl %>" class="status-text secondary" aria-describedby="program-<%- id %>"><%= interpolate(
<a href="<%- detailUrl %>" class="status-text secondary" aria-describedby="program-<%-
uu
id %>"><%= interpolate(
ngettext(
ngettext(
'%(count)s course is in progress.',
'%(count)s course is in progress.',
'%(count)s courses are in progress.',
'%(count)s courses are in progress.',
progress.
total.
in_progress
progress.in_progress
),
),
{count: progress.
total.
in_progress}, true
{count: progress.in_progress}, true
) %></a>
) %></a>
<a href="<%- detailUrl %>" class="status-text secondary" aria-describedby="program-<%- id %>"><%= interpolate(
<a href="<%- detailUrl %>" class="status-text secondary" aria-describedby="program-<%-
uu
id %>"><%= interpolate(
ngettext(
ngettext(
'%(count)s course has not been started.',
'%(count)s course has not been started.',
'%(count)s courses have not been started.',
'%(count)s courses have not been started.',
progress.
total.
not_started
progress.not_started
),
),
{count: progress.
total.
not_started}, true
{count: progress.not_started}, true
) %></a>
) %></a>
<span id="status-<%- id %>" class="status-text"><%= interpolate(
<span id="status-<%-
uu
id %>" class="status-text"><%= interpolate(
gettext('You have earned certificates in %(completed_courses)s of the %(total_courses)s courses so far.'),
gettext('You have earned certificates in %(completed_courses)s of the %(total_courses)s courses so far.'),
{completed_courses: progress.
total.completed, total_courses: progress.total.courses
}, true
{completed_courses: progress.
completed, total_courses: progress.total
}, true
) %></span>
) %></span>
</p>
</p>
<% } %>
<% } %>
...
@@ -44,11 +44,11 @@
...
@@ -44,11 +44,11 @@
<a href="<%- detailUrl %>" class="card-link">
<a href="<%- detailUrl %>" class="card-link">
<div class="banner-image-container">
<div class="banner-image-container">
<picture>
<picture>
<source srcset="<%-
smallBannerUrl %>" media="(max-width: <%- breakpoints.max.tiny
%>)">
<source srcset="<%-
xsmallBannerUrl %>" media="(max-width: <%- breakpoints.max.xsmall
%>)">
<source srcset="<%-
medium
BannerUrl %>" media="(max-width: <%- breakpoints.max.small %>)">
<source srcset="<%-
small
BannerUrl %>" media="(max-width: <%- breakpoints.max.small %>)">
<source srcset="<%-
large
BannerUrl %>" media="(max-width: <%- breakpoints.max.medium %>)">
<source srcset="<%-
medium
BannerUrl %>" media="(max-width: <%- breakpoints.max.medium %>)">
<source srcset="<%- smallBannerUrl %>" media="(max-width: <%- breakpoints.max.large %>)">
<source srcset="<%-
x
smallBannerUrl %>" media="(max-width: <%- breakpoints.max.large %>)">
<img class="banner-image" srcset="<%-
mediumBannerUrl %>" alt="<%= interpolate(gettext('%(programName)s Home Page.'), {programName: nam
e}, true)%>">
<img class="banner-image" srcset="<%-
smallBannerUrl %>" alt="<%= interpolate(gettext('%(programName)s Home Page.'), {programName: titl
e}, true)%>">
</picture>
</picture>
</div>
</div>
</a>
</a>
lms/templates/learner_dashboard/program_header_view.underscore
View file @
dda0e03f
<div class="banner-background-wrapper">
<div class="banner-background-wrapper">
<picture>
<picture>
<source srcset="<%- programData.banner_image
_urls.w1440h480
%>" media="(min-width: <%- breakpoints.min.large %>)">
<source srcset="<%- programData.banner_image
.large.url
%>" media="(min-width: <%- breakpoints.min.large %>)">
<source srcset="<%- programData.banner_image
_urls.w726h242
%>" media="(min-width: <%- breakpoints.min.medium %>)">
<source srcset="<%- programData.banner_image
.medium.url
%>" media="(min-width: <%- breakpoints.min.medium %>)">
<img class="banner-background-image" srcset="<%- programData.banner_image
_urls.w348h116
%>" alt="">
<img class="banner-background-image" srcset="<%- programData.banner_image
['x-small'].url
%>" alt="">
</picture>
</picture>
<div class="banner-content grid-container">
<div class="banner-content grid-container">
<h2 class="hd-1 title row"><%- programData.
nam
e %></h2>
<h2 class="hd-1 title row"><%- programData.
titl
e %></h2>
<p class="hd-4 subtitle row"><%- programData.subtitle %></p>
<p class="hd-4 subtitle row"><%- programData.subtitle %></p>
<% if (programData.organizations.length) { %>
<% if (programData.
authoring_
organizations.length) { %>
<div class="org-wrapper">
<div class="org-wrapper">
<% _.each(programData.organizations, function(org) { %>
<% _.each(programData.
authoring_
organizations, function(org) { %>
<img src="<%- org.
img
%>" class="org-logo" alt="<%- StringUtils.interpolate(
<img src="<%- org.
certificate_logo_image_url || org.logo_image_url
%>" class="org-logo" alt="<%- StringUtils.interpolate(
gettext('{organization}\'s logo'),
gettext('{organization}\'s logo'),
{organization: org.
display_
name}
{organization: org.name}
) %>">
) %>">
<% }) %>
<% }) %>
</div>
</div>
...
@@ -33,7 +33,7 @@
...
@@ -33,7 +33,7 @@
<span class="crumb-separator fa fa-chevron-right" aria-hidden="true"></span>
<span class="crumb-separator fa fa-chevron-right" aria-hidden="true"></span>
</li>
</li>
<li class="crumb active">
<li class="crumb active">
<%- programData.
nam
e %>
<%- programData.
titl
e %>
</li>
</li>
</ol>
</ol>
</nav>
</nav>
openedx/core/djangoapps/catalog/tests/test_utils.py
View file @
dda0e03f
...
@@ -13,7 +13,6 @@ from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory, Prog
...
@@ -13,7 +13,6 @@ from openedx.core.djangoapps.catalog.tests.factories import ProgramFactory, Prog
from
openedx.core.djangoapps.catalog.tests.mixins
import
CatalogIntegrationMixin
from
openedx.core.djangoapps.catalog.tests.mixins
import
CatalogIntegrationMixin
from
openedx.core.djangoapps.catalog.utils
import
(
from
openedx.core.djangoapps.catalog.utils
import
(
get_programs
,
get_programs
,
munge_catalog_program
,
get_program_types
,
get_program_types
,
get_programs_with_type_logo
,
get_programs_with_type_logo
,
)
)
...
@@ -131,63 +130,6 @@ class TestGetPrograms(CatalogIntegrationMixin, TestCase):
...
@@ -131,63 +130,6 @@ class TestGetPrograms(CatalogIntegrationMixin, TestCase):
self
.
assertEqual
(
data
,
[])
self
.
assertEqual
(
data
,
[])
class
TestMungeCatalogProgram
(
TestCase
):
def
setUp
(
self
):
super
(
TestMungeCatalogProgram
,
self
)
.
setUp
()
self
.
catalog_program
=
ProgramFactory
()
def
assert_munged
(
self
,
program
):
munged
=
munge_catalog_program
(
program
)
expected
=
{
'id'
:
program
[
'uuid'
],
'name'
:
program
[
'title'
],
'subtitle'
:
program
[
'subtitle'
],
'category'
:
program
[
'type'
],
'marketing_slug'
:
program
[
'marketing_slug'
],
'organizations'
:
[
{
'display_name'
:
organization
[
'name'
],
'key'
:
organization
[
'key'
]
}
for
organization
in
program
[
'authoring_organizations'
]
],
'course_codes'
:
[
{
'display_name'
:
course
[
'title'
],
'key'
:
course
[
'key'
],
'organization'
:
{
'display_name'
:
course
[
'owners'
][
0
][
'name'
],
'key'
:
course
[
'owners'
][
0
][
'key'
]
},
'run_modes'
:
[
{
'course_key'
:
course_run
[
'key'
],
'run_key'
:
CourseKey
.
from_string
(
course_run
[
'key'
])
.
run
,
'mode_slug'
:
course_run
[
'type'
],
'marketing_url'
:
course_run
[
'marketing_url'
],
}
for
course_run
in
course
[
'course_runs'
]
],
}
for
course
in
program
[
'courses'
]
],
'banner_image_urls'
:
{
'w1440h480'
:
program
[
'banner_image'
][
'large'
][
'url'
],
'w726h242'
:
program
[
'banner_image'
][
'medium'
][
'url'
],
'w435h145'
:
program
[
'banner_image'
][
'small'
][
'url'
],
'w348h116'
:
program
[
'banner_image'
][
'x-small'
][
'url'
],
},
'detail_url'
:
program
.
get
(
'detail_url'
),
}
self
.
assertEqual
(
munged
,
expected
)
def
test_munge_catalog_program
(
self
):
self
.
assert_munged
(
self
.
catalog_program
)
def
test_munge_with_detail_url
(
self
):
self
.
catalog_program
[
'detail_url'
]
=
'foo'
self
.
assert_munged
(
self
.
catalog_program
)
@skip_unless_lms
@skip_unless_lms
@mock.patch
(
UTILS_MODULE
+
'.get_edx_api_data'
)
@mock.patch
(
UTILS_MODULE
+
'.get_edx_api_data'
)
class
TestGetProgramTypes
(
CatalogIntegrationMixin
,
TestCase
):
class
TestGetProgramTypes
(
CatalogIntegrationMixin
,
TestCase
):
...
...
openedx/core/djangoapps/catalog/utils.py
View file @
dda0e03f
...
@@ -66,64 +66,6 @@ def get_programs(uuid=None, type=None): # pylint: disable=redefined-builtin
...
@@ -66,64 +66,6 @@ def get_programs(uuid=None, type=None): # pylint: disable=redefined-builtin
return
[]
return
[]
def
munge_catalog_program
(
catalog_program
):
"""
Make a program from the catalog service look like it came from the programs service.
We want to display programs from the catalog service on the LMS. The LMS
originally retrieved all program data from the deprecated programs service.
This temporary utility is here to help incrementally swap out the backend.
Clean up of this debt is tracked by ECOM-4418.
Arguments:
catalog_program (dict): The catalog service's representation of a program.
Return:
dict, imitating the schema used by the programs service.
"""
return
{
'id'
:
catalog_program
[
'uuid'
],
'name'
:
catalog_program
[
'title'
],
'subtitle'
:
catalog_program
[
'subtitle'
],
'category'
:
catalog_program
[
'type'
],
'marketing_slug'
:
catalog_program
[
'marketing_slug'
],
'organizations'
:
[
{
'display_name'
:
organization
[
'name'
],
'key'
:
organization
[
'key'
]
}
for
organization
in
catalog_program
[
'authoring_organizations'
]
],
'course_codes'
:
[
{
'display_name'
:
course
[
'title'
],
'key'
:
course
[
'key'
],
'organization'
:
{
# The Programs schema only supports one organization here.
'display_name'
:
course
[
'owners'
][
0
][
'name'
],
'key'
:
course
[
'owners'
][
0
][
'key'
]
}
if
course
[
'owners'
]
else
{},
'run_modes'
:
[
{
'course_key'
:
course_run
[
'key'
],
'run_key'
:
CourseKey
.
from_string
(
course_run
[
'key'
])
.
run
,
'mode_slug'
:
course_run
[
'type'
],
'marketing_url'
:
course_run
[
'marketing_url'
],
}
for
course_run
in
course
[
'course_runs'
]
],
}
for
course
in
catalog_program
[
'courses'
]
],
'banner_image_urls'
:
{
'w1440h480'
:
catalog_program
[
'banner_image'
][
'large'
][
'url'
],
'w726h242'
:
catalog_program
[
'banner_image'
][
'medium'
][
'url'
],
'w435h145'
:
catalog_program
[
'banner_image'
][
'small'
][
'url'
],
'w348h116'
:
catalog_program
[
'banner_image'
][
'x-small'
][
'url'
],
},
# If a detail URL has been added, we don't want to lose it.
'detail_url'
:
catalog_program
.
get
(
'detail_url'
),
}
def
get_program_types
():
def
get_program_types
():
"""Retrieve all program types from the catalog service.
"""Retrieve all program types from the catalog service.
...
...
openedx/core/djangoapps/programs/tests/factories.py
View file @
dda0e03f
...
@@ -4,14 +4,11 @@ import factory
...
@@ -4,14 +4,11 @@ import factory
from
faker
import
Faker
from
faker
import
Faker
fake
=
Faker
()
class
ProgressFactory
(
factory
.
Factory
):
class
ProgressFactory
(
factory
.
Factory
):
class
Meta
(
object
):
class
Meta
(
object
):
model
=
dict
model
=
dict
uuid
=
factory
.
Faker
(
'uuid4'
)
uuid
=
factory
.
Faker
(
'uuid4'
)
completed
=
[]
completed
=
0
in_progress
=
[]
in_progress
=
0
not_started
=
[]
not_started
=
0
openedx/core/djangoapps/programs/tests/test_utils.py
View file @
dda0e03f
"""Tests covering Programs utilities."""
"""Tests covering Programs utilities."""
# pylint: disable=no-member
import
datetime
import
datetime
import
json
import
json
import
uuid
import
uuid
...
@@ -15,7 +16,6 @@ from pytz import utc
...
@@ -15,7 +16,6 @@ from pytz import utc
from
lms.djangoapps.certificates.api
import
MODES
from
lms.djangoapps.certificates.api
import
MODES
from
lms.djangoapps.commerce.tests.test_utils
import
update_commerce_config
from
lms.djangoapps.commerce.tests.test_utils
import
update_commerce_config
from
openedx.core.djangoapps.catalog.utils
import
munge_catalog_program
from
openedx.core.djangoapps.catalog.tests.factories
import
(
from
openedx.core.djangoapps.catalog.tests.factories
import
(
generate_course_run_key
,
generate_course_run_key
,
ProgramFactory
,
ProgramFactory
,
...
@@ -60,10 +60,6 @@ class TestProgramProgressMeter(TestCase):
...
@@ -60,10 +60,6 @@ class TestProgramProgressMeter(TestCase):
"""Variadic helper used to verify progress calculations."""
"""Variadic helper used to verify progress calculations."""
self
.
assertEqual
(
meter
.
progress
,
list
(
progresses
))
self
.
assertEqual
(
meter
.
progress
,
list
(
progresses
))
def
_extract_titles
(
self
,
program
,
*
indices
):
"""Construct a list containing the titles of the indicated courses."""
return
[
program
[
'courses'
][
index
][
'title'
]
for
index
in
indices
]
def
_attach_detail_url
(
self
,
programs
):
def
_attach_detail_url
(
self
,
programs
):
"""Add expected detail URLs to a list of program dicts."""
"""Add expected detail URLs to a list of program dicts."""
for
program
in
programs
:
for
program
in
programs
:
...
@@ -118,10 +114,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -118,10 +114,7 @@ class TestProgramProgressMeter(TestCase):
self
.
assertEqual
(
meter
.
engaged_programs
,
[
program
])
self
.
assertEqual
(
meter
.
engaged_programs
,
[
program
])
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
ProgressFactory
(
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
)
uuid
=
program
[
'uuid'
],
in_progress
=
self
.
_extract_titles
(
program
,
0
)
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
@@ -160,10 +153,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -160,10 +153,7 @@ class TestProgramProgressMeter(TestCase):
self
.
assertEqual
(
meter
.
engaged_programs
,
programs
)
self
.
assertEqual
(
meter
.
engaged_programs
,
programs
)
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
*
(
*
(
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
)
for
program
in
programs
)
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
self
.
_extract_titles
(
program
,
0
))
for
program
in
programs
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
@@ -208,10 +198,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -208,10 +198,7 @@ class TestProgramProgressMeter(TestCase):
self
.
assertEqual
(
meter
.
engaged_programs
,
programs
)
self
.
assertEqual
(
meter
.
engaged_programs
,
programs
)
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
*
(
*
(
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
)
for
program
in
programs
)
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
self
.
_extract_titles
(
program
,
0
))
for
program
in
programs
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
@@ -245,11 +232,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -245,11 +232,7 @@ class TestProgramProgressMeter(TestCase):
program
,
program_uuid
=
data
[
0
],
data
[
0
][
'uuid'
]
program
,
program_uuid
=
data
[
0
],
data
[
0
][
'uuid'
]
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
ProgressFactory
(
ProgressFactory
(
uuid
=
program_uuid
,
in_progress
=
1
,
not_started
=
1
)
uuid
=
program_uuid
,
in_progress
=
self
.
_extract_titles
(
program
,
0
),
not_started
=
self
.
_extract_titles
(
program
,
1
)
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
@@ -258,10 +241,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -258,10 +241,7 @@ class TestProgramProgressMeter(TestCase):
meter
=
ProgramProgressMeter
(
self
.
user
)
meter
=
ProgramProgressMeter
(
self
.
user
)
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
ProgressFactory
(
ProgressFactory
(
uuid
=
program_uuid
,
in_progress
=
2
)
uuid
=
program_uuid
,
in_progress
=
self
.
_extract_titles
(
program
,
0
,
1
)
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
@@ -272,11 +252,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -272,11 +252,7 @@ class TestProgramProgressMeter(TestCase):
meter
=
ProgramProgressMeter
(
self
.
user
)
meter
=
ProgramProgressMeter
(
self
.
user
)
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
ProgressFactory
(
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
,
in_progress
=
1
)
uuid
=
program_uuid
,
completed
=
self
.
_extract_titles
(
program
,
0
),
in_progress
=
self
.
_extract_titles
(
program
,
1
)
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
@@ -288,11 +264,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -288,11 +264,7 @@ class TestProgramProgressMeter(TestCase):
meter
=
ProgramProgressMeter
(
self
.
user
)
meter
=
ProgramProgressMeter
(
self
.
user
)
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
ProgressFactory
(
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
,
in_progress
=
1
)
uuid
=
program_uuid
,
completed
=
self
.
_extract_titles
(
program
,
0
),
in_progress
=
self
.
_extract_titles
(
program
,
1
)
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
@@ -304,10 +276,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -304,10 +276,7 @@ class TestProgramProgressMeter(TestCase):
meter
=
ProgramProgressMeter
(
self
.
user
)
meter
=
ProgramProgressMeter
(
self
.
user
)
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
ProgressFactory
(
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
2
)
uuid
=
program_uuid
,
completed
=
self
.
_extract_titles
(
program
,
0
,
1
)
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[
program_uuid
])
self
.
assertEqual
(
meter
.
completed_programs
,
[
program_uuid
])
...
@@ -340,7 +309,7 @@ class TestProgramProgressMeter(TestCase):
...
@@ -340,7 +309,7 @@ class TestProgramProgressMeter(TestCase):
program
,
program_uuid
=
data
[
0
],
data
[
0
][
'uuid'
]
program
,
program_uuid
=
data
[
0
],
data
[
0
][
'uuid'
]
self
.
_assert_progress
(
self
.
_assert_progress
(
meter
,
meter
,
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
self
.
_extract_titles
(
program
,
0
)
)
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
)
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[
program_uuid
])
self
.
assertEqual
(
meter
.
completed_programs
,
[
program_uuid
])
...
@@ -418,57 +387,56 @@ class TestProgramDataExtender(ModuleStoreTestCase):
...
@@ -418,57 +387,56 @@ class TestProgramDataExtender(ModuleStoreTestCase):
"""Tests of the program data extender utility class."""
"""Tests of the program data extender utility class."""
maxDiff
=
None
maxDiff
=
None
sku
=
'abc123'
sku
=
'abc123'
password
=
'test'
checkout_path
=
'/basket'
checkout_path
=
'/basket'
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestProgramDataExtender
,
self
)
.
setUp
()
super
(
TestProgramDataExtender
,
self
)
.
setUp
()
self
.
user
=
UserFactory
()
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
password
)
self
.
course
=
ModuleStoreCourseFactory
()
self
.
course
=
ModuleStoreCourseFactory
()
self
.
course
.
start
=
datetime
.
datetime
.
now
(
utc
)
-
datetime
.
timedelta
(
days
=
1
)
self
.
course
.
start
=
datetime
.
datetime
.
now
(
utc
)
-
datetime
.
timedelta
(
days
=
1
)
self
.
course
.
end
=
datetime
.
datetime
.
now
(
utc
)
+
datetime
.
timedelta
(
days
=
1
)
self
.
course
.
end
=
datetime
.
datetime
.
now
(
utc
)
+
datetime
.
timedelta
(
days
=
1
)
self
.
course
=
self
.
update_course
(
self
.
course
,
self
.
user
.
id
)
# pylint: disable=no-member
self
.
course
=
self
.
update_course
(
self
.
course
,
self
.
user
.
id
)
organization
=
OrganizationFactory
()
course_run
=
CourseRunFactory
(
key
=
unicode
(
self
.
course
.
id
))
# pylint: disable=no-member
course
=
CourseFactory
(
course_runs
=
[
course_run
])
program
=
ProgramFactory
(
authoring_organizations
=
[
organization
],
courses
=
[
course
])
self
.
program
=
munge_catalog_program
(
program
)
self
.
course_run
=
CourseRunFactory
(
key
=
unicode
(
self
.
course
.
id
)
)
self
.
c
ourse_code
=
self
.
program
[
'course_codes'
][
0
]
self
.
c
atalog_course
=
CourseFactory
(
course_runs
=
[
self
.
course_run
])
self
.
run_mode
=
self
.
course_code
[
'run_modes'
][
0
]
self
.
program
=
ProgramFactory
(
courses
=
[
self
.
catalog_course
])
def
_assert_supplemented
(
self
,
actual
,
**
kwargs
):
def
_assert_supplemented
(
self
,
actual
,
**
kwargs
):
"""DRY helper used to verify that program data is extended correctly."""
"""DRY helper used to verify that program data is extended correctly."""
course_overview
=
CourseOverview
.
get_from_id
(
self
.
course
.
id
)
# pylint: disable=no-member
self
.
course_run
.
update
(
run_mode
=
dict
(
dict
(
{
{
'certificate_url'
:
None
,
'certificate_url'
:
None
,
'course_image_url'
:
course_overview
.
course_image_url
,
'course_url'
:
reverse
(
'course_root'
,
args
=
[
self
.
course
.
id
]),
'course_key'
:
unicode
(
self
.
course
.
id
),
# pylint: disable=no-member
'enrollment_open_date'
:
strftime_localized
(
DEFAULT_ENROLLMENT_START_DATE
,
'SHORT_DATE'
),
'course_url'
:
reverse
(
'course_root'
,
args
=
[
self
.
course
.
id
]),
# pylint: disable=no-member
'is_course_ended'
:
self
.
course
.
end
<
datetime
.
datetime
.
now
(
utc
),
'end_date'
:
self
.
course
.
end
.
replace
(
tzinfo
=
utc
),
'is_enrolled'
:
False
,
'enrollment_open_date'
:
strftime_localized
(
DEFAULT_ENROLLMENT_START_DATE
,
'SHORT_DATE'
),
'is_enrollment_open'
:
True
,
'is_course_ended'
:
self
.
course
.
end
<
datetime
.
datetime
.
now
(
utc
),
'upgrade_url'
:
None
,
'is_enrolled'
:
False
,
'advertised_start'
:
None
,
'is_enrollment_open'
:
True
,
},
'marketing_url'
:
self
.
run_mode
[
'marketing_url'
],
**
kwargs
'mode_slug'
:
'verified'
,
)
'start_date'
:
self
.
course
.
start
.
replace
(
tzinfo
=
utc
),
'upgrade_url'
:
None
,
'advertised_start'
:
None
,
},
**
kwargs
)
)
self
.
c
ourse_code
[
'run_modes'
]
=
[
run_mode
]
self
.
c
atalog_course
[
'course_runs'
]
=
[
self
.
course_run
]
self
.
program
[
'course
_codes'
]
=
[
self
.
course_cod
e
]
self
.
program
[
'course
s'
]
=
[
self
.
catalog_cours
e
]
self
.
assertEqual
(
actual
,
self
.
program
)
self
.
assertEqual
(
actual
,
self
.
program
)
@ddt.data
(
-
1
,
0
,
1
)
def
test_is_enrollment_open
(
self
,
days_offset
):
"""
Verify that changes to the course run end date do not affect our
assessment of the course run being open for enrollment.
"""
self
.
course
.
end
=
datetime
.
datetime
.
now
(
utc
)
+
datetime
.
timedelta
(
days
=
days_offset
)
self
.
course
=
self
.
update_course
(
self
.
course
,
self
.
user
.
id
)
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
self
.
_assert_supplemented
(
data
)
@ddt.data
(
@ddt.data
(
(
False
,
None
,
False
),
(
False
,
None
,
False
),
(
True
,
MODES
.
audit
,
True
),
(
True
,
MODES
.
audit
,
True
),
...
@@ -491,7 +459,7 @@ class TestProgramDataExtender(ModuleStoreTestCase):
...
@@ -491,7 +459,7 @@ class TestProgramDataExtender(ModuleStoreTestCase):
mock_get_mode
.
return_value
=
mock_mode
mock_get_mode
.
return_value
=
mock_mode
if
is_enrolled
:
if
is_enrolled
:
CourseEnrollmentFactory
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
mode
=
enrolled_mode
)
# pylint: disable=no-member
CourseEnrollmentFactory
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
mode
=
enrolled_mode
)
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
...
@@ -503,12 +471,14 @@ class TestProgramDataExtender(ModuleStoreTestCase):
...
@@ -503,12 +471,14 @@ class TestProgramDataExtender(ModuleStoreTestCase):
@ddt.data
(
MODES
.
audit
,
MODES
.
verified
)
@ddt.data
(
MODES
.
audit
,
MODES
.
verified
)
def
test_inactive_enrollment_no_upgrade
(
self
,
enrolled_mode
):
def
test_inactive_enrollment_no_upgrade
(
self
,
enrolled_mode
):
"""Verify that a student with an inactive enrollment isn't encouraged to upgrade."""
"""
Verify that a student with an inactive enrollment isn't encouraged to upgrade.
"""
update_commerce_config
(
enabled
=
True
,
checkout_page
=
self
.
checkout_path
)
update_commerce_config
(
enabled
=
True
,
checkout_page
=
self
.
checkout_path
)
CourseEnrollmentFactory
(
CourseEnrollmentFactory
(
user
=
self
.
user
,
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
# pylint: disable=no-member
course_id
=
self
.
course
.
id
,
mode
=
enrolled_mode
,
mode
=
enrolled_mode
,
is_active
=
False
,
is_active
=
False
,
)
)
...
@@ -519,14 +489,16 @@ class TestProgramDataExtender(ModuleStoreTestCase):
...
@@ -519,14 +489,16 @@ class TestProgramDataExtender(ModuleStoreTestCase):
@mock.patch
(
UTILS_MODULE
+
'.CourseMode.mode_for_course'
)
@mock.patch
(
UTILS_MODULE
+
'.CourseMode.mode_for_course'
)
def
test_ecommerce_disabled
(
self
,
mock_get_mode
):
def
test_ecommerce_disabled
(
self
,
mock_get_mode
):
"""Verify that the utility can operate when the ecommerce service is disabled."""
"""
Verify that the utility can operate when the ecommerce service is disabled.
"""
update_commerce_config
(
enabled
=
False
,
checkout_page
=
self
.
checkout_path
)
update_commerce_config
(
enabled
=
False
,
checkout_page
=
self
.
checkout_path
)
mock_mode
=
mock
.
Mock
()
mock_mode
=
mock
.
Mock
()
mock_mode
.
sku
=
self
.
sku
mock_mode
.
sku
=
self
.
sku
mock_get_mode
.
return_value
=
mock_mode
mock_get_mode
.
return_value
=
mock_mode
CourseEnrollmentFactory
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
mode
=
MODES
.
audit
)
# pylint: disable=no-member
CourseEnrollmentFactory
(
user
=
self
.
user
,
course_id
=
self
.
course
.
id
,
mode
=
MODES
.
audit
)
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
...
@@ -537,14 +509,14 @@ class TestProgramDataExtender(ModuleStoreTestCase):
...
@@ -537,14 +509,14 @@ class TestProgramDataExtender(ModuleStoreTestCase):
(
1
,
-
1
,
True
),
(
1
,
-
1
,
True
),
)
)
@ddt.unpack
@ddt.unpack
def
test_course_enrollment_status
(
self
,
start_offset
,
end_offset
,
is_enrollment_open
):
def
test_course_
run_
enrollment_status
(
self
,
start_offset
,
end_offset
,
is_enrollment_open
):
"""
"""
Verify that course enrollment status is reflected correctly.
Verify that course
run
enrollment status is reflected correctly.
"""
"""
self
.
course
.
enrollment_start
=
datetime
.
datetime
.
now
(
utc
)
-
datetime
.
timedelta
(
days
=
start_offset
)
self
.
course
.
enrollment_start
=
datetime
.
datetime
.
now
(
utc
)
-
datetime
.
timedelta
(
days
=
start_offset
)
self
.
course
.
enrollment_end
=
datetime
.
datetime
.
now
(
utc
)
-
datetime
.
timedelta
(
days
=
end_offset
)
self
.
course
.
enrollment_end
=
datetime
.
datetime
.
now
(
utc
)
-
datetime
.
timedelta
(
days
=
end_offset
)
self
.
course
=
self
.
update_course
(
self
.
course
,
self
.
user
.
id
)
# pylint: disable=no-member
self
.
course
=
self
.
update_course
(
self
.
course
,
self
.
user
.
id
)
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
...
@@ -555,12 +527,12 @@ class TestProgramDataExtender(ModuleStoreTestCase):
...
@@ -555,12 +527,12 @@ class TestProgramDataExtender(ModuleStoreTestCase):
)
)
def
test_no_enrollment_start_date
(
self
):
def
test_no_enrollment_start_date
(
self
):
"""
Verify that a closed course with no explicit enrollment start date doesn't cause an error.
"""
Verify that a closed course run with no explicit enrollment start date
Regression test for ECOM-4973.
doesn't cause an error.
Regression test for ECOM-4973.
"""
"""
self
.
course
.
enrollment_end
=
datetime
.
datetime
.
now
(
utc
)
-
datetime
.
timedelta
(
days
=
1
)
self
.
course
.
enrollment_end
=
datetime
.
datetime
.
now
(
utc
)
-
datetime
.
timedelta
(
days
=
1
)
self
.
course
=
self
.
update_course
(
self
.
course
,
self
.
user
.
id
)
# pylint: disable=no-member
self
.
course
=
self
.
update_course
(
self
.
course
,
self
.
user
.
id
)
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
...
@@ -573,7 +545,10 @@ class TestProgramDataExtender(ModuleStoreTestCase):
...
@@ -573,7 +545,10 @@ class TestProgramDataExtender(ModuleStoreTestCase):
@mock.patch
(
UTILS_MODULE
+
'.certificate_api.certificate_downloadable_status'
)
@mock.patch
(
UTILS_MODULE
+
'.certificate_api.certificate_downloadable_status'
)
@mock.patch
(
CERTIFICATES_API_MODULE
+
'.has_html_certificates_enabled'
)
@mock.patch
(
CERTIFICATES_API_MODULE
+
'.has_html_certificates_enabled'
)
def
test_certificate_url_retrieval
(
self
,
is_uuid_available
,
mock_html_certs_enabled
,
mock_get_cert_data
):
def
test_certificate_url_retrieval
(
self
,
is_uuid_available
,
mock_html_certs_enabled
,
mock_get_cert_data
):
"""Verify that the student's run mode certificate is included, when available."""
"""
Verify that the student's run mode certificate is included,
when available.
"""
test_uuid
=
uuid
.
uuid4
()
.
hex
test_uuid
=
uuid
.
uuid4
()
.
hex
mock_get_cert_data
.
return_value
=
{
'uuid'
:
test_uuid
}
if
is_uuid_available
else
{}
mock_get_cert_data
.
return_value
=
{
'uuid'
:
test_uuid
}
if
is_uuid_available
else
{}
mock_html_certs_enabled
.
return_value
=
True
mock_html_certs_enabled
.
return_value
=
True
...
@@ -586,42 +561,3 @@ class TestProgramDataExtender(ModuleStoreTestCase):
...
@@ -586,42 +561,3 @@ class TestProgramDataExtender(ModuleStoreTestCase):
)
if
is_uuid_available
else
None
)
if
is_uuid_available
else
None
self
.
_assert_supplemented
(
data
,
certificate_url
=
expected_url
)
self
.
_assert_supplemented
(
data
,
certificate_url
=
expected_url
)
@ddt.data
(
-
1
,
0
,
1
)
def
test_course_course_ended
(
self
,
days_offset
):
self
.
course
.
end
=
datetime
.
datetime
.
now
(
utc
)
+
datetime
.
timedelta
(
days
=
days_offset
)
self
.
course
=
self
.
update_course
(
self
.
course
,
self
.
user
.
id
)
# pylint: disable=no-member
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
self
.
_assert_supplemented
(
data
)
@mock.patch
(
UTILS_MODULE
+
'.get_organization_by_short_name'
)
def
test_organization_logo_exists
(
self
,
mock_get_organization_by_short_name
):
""" Verify the logo image is set from the organizations api """
mock_logo_url
=
'edx/logo.png'
mock_image
=
mock
.
Mock
()
mock_image
.
url
=
mock_logo_url
mock_get_organization_by_short_name
.
return_value
=
{
'logo'
:
mock_image
}
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
self
.
assertEqual
(
data
[
'organizations'
][
0
]
.
get
(
'img'
),
mock_logo_url
)
@mock.patch
(
UTILS_MODULE
+
'.get_organization_by_short_name'
)
def
test_organization_missing
(
self
,
mock_get_organization_by_short_name
):
""" Verify the logo image is not set if the organizations api returns None """
mock_get_organization_by_short_name
.
return_value
=
None
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
self
.
assertEqual
(
data
[
'organizations'
][
0
]
.
get
(
'img'
),
None
)
@mock.patch
(
UTILS_MODULE
+
'.get_organization_by_short_name'
)
def
test_organization_logo_missing
(
self
,
mock_get_organization_by_short_name
):
"""
Verify the logo image is not set if the organizations api returns organization,
but the logo is not available
"""
mock_get_organization_by_short_name
.
return_value
=
{
'logo'
:
None
}
data
=
ProgramDataExtender
(
self
.
program
,
self
.
user
)
.
extend
()
self
.
assertEqual
(
data
[
'organizations'
][
0
]
.
get
(
'img'
),
None
)
openedx/core/djangoapps/programs/utils.py
View file @
dda0e03f
...
@@ -18,7 +18,6 @@ from openedx.core.djangoapps.content.course_overviews.models import CourseOvervi
...
@@ -18,7 +18,6 @@ from openedx.core.djangoapps.content.course_overviews.models import CourseOvervi
from
openedx.core.lib.edx_api_utils
import
get_edx_api_data
from
openedx.core.lib.edx_api_utils
import
get_edx_api_data
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
from
util.date_utils
import
strftime_localized
from
util.date_utils
import
strftime_localized
from
util.organizations_helpers
import
get_organization_by_short_name
# The datetime module's strftime() methods require a year >= 1900.
# The datetime module's strftime() methods require a year >= 1900.
...
@@ -26,7 +25,7 @@ DEFAULT_ENROLLMENT_START_DATE = datetime.datetime(1900, 1, 1, tzinfo=utc)
...
@@ -26,7 +25,7 @@ DEFAULT_ENROLLMENT_START_DATE = datetime.datetime(1900, 1, 1, tzinfo=utc)
def
get_program_marketing_url
(
programs_config
):
def
get_program_marketing_url
(
programs_config
):
"""Build a URL
to be used when linking to program details on a
marketing site."""
"""Build a URL
used to link to programs on the
marketing site."""
return
urljoin
(
settings
.
MKTG_URLS
.
get
(
'ROOT'
),
programs_config
.
marketing_path
)
.
rstrip
(
'/'
)
return
urljoin
(
settings
.
MKTG_URLS
.
get
(
'ROOT'
),
programs_config
.
marketing_path
)
.
rstrip
(
'/'
)
...
@@ -47,18 +46,6 @@ def attach_program_detail_url(programs):
...
@@ -47,18 +46,6 @@ def attach_program_detail_url(programs):
return
programs
return
programs
def
munge_progress_map
(
progress_map
):
"""
Temporary utility for making progress maps look like they were built using
data from the deprecated programs service.
Clean up of this debt is tracked by ECOM-4418.
"""
progress_map
[
'id'
]
=
progress_map
.
pop
(
'uuid'
)
return
progress_map
class
ProgramProgressMeter
(
object
):
class
ProgramProgressMeter
(
object
):
"""Utility for gauging a user's progress towards program completion.
"""Utility for gauging a user's progress towards program completion.
...
@@ -139,19 +126,15 @@ class ProgramProgressMeter(object):
...
@@ -139,19 +126,15 @@ class ProgramProgressMeter(object):
"""
"""
progress
=
[]
progress
=
[]
for
program
in
self
.
engaged_programs
:
for
program
in
self
.
engaged_programs
:
completed
,
in_progress
,
not_started
=
[],
[],
[]
completed
,
in_progress
,
not_started
=
0
,
0
,
0
for
course
in
program
[
'courses'
]:
for
course
in
program
[
'courses'
]:
# TODO: What are these titles used for? If they're not used by
# the front-end, pass integer counts instead.
title
=
course
[
'title'
]
if
self
.
_is_course_complete
(
course
):
if
self
.
_is_course_complete
(
course
):
completed
.
append
(
title
)
completed
+=
1
elif
self
.
_is_course_in_progress
(
course
):
elif
self
.
_is_course_in_progress
(
course
):
in_progress
.
append
(
title
)
in_progress
+=
1
else
:
else
:
not_started
.
append
(
title
)
not_started
+=
1
progress
.
append
({
progress
.
append
({
'uuid'
:
program
[
'uuid'
],
'uuid'
:
program
[
'uuid'
],
...
@@ -249,18 +232,18 @@ class ProgramProgressMeter(object):
...
@@ -249,18 +232,18 @@ class ProgramProgressMeter(object):
# pylint: disable=missing-docstring
# pylint: disable=missing-docstring
class
ProgramDataExtender
(
object
):
class
ProgramDataExtender
(
object
):
"""
"""
Utility for extending program
course codes with CourseOverview and
Utility for extending program
data meant for the program detail page with
CourseEnrollment
data.
user-specific (e.g., CourseEnrollment)
data.
Arguments:
Arguments:
program_data (dict): Representation of a program. Note that this dict must
program_data (dict): Representation of a program.
be formatted as if it was returned by the deprecated program service.
user (User): The user whose enrollments to inspect.
user (User): The user whose enrollments to inspect.
"""
"""
def
__init__
(
self
,
program_data
,
user
):
def
__init__
(
self
,
program_data
,
user
):
self
.
data
=
program_data
self
.
data
=
program_data
self
.
user
=
user
self
.
user
=
user
self
.
course_key
=
None
self
.
course_run_key
=
None
self
.
course_overview
=
None
self
.
course_overview
=
None
self
.
enrollment_start
=
None
self
.
enrollment_start
=
None
...
@@ -278,77 +261,62 @@ class ProgramDataExtender(object):
...
@@ -278,77 +261,62 @@ class ProgramDataExtender(object):
"""Returns a generator yielding method names beginning with the given prefix."""
"""Returns a generator yielding method names beginning with the given prefix."""
return
(
name
for
name
in
cls
.
__dict__
if
name
.
startswith
(
prefix
))
return
(
name
for
name
in
cls
.
__dict__
if
name
.
startswith
(
prefix
))
def
_extend_organizations
(
self
):
def
_extend_course_runs
(
self
):
"""Execute organization data handlers."""
"""Execute course run data handlers."""
for
organization
in
self
.
data
[
'organizations'
]:
for
course
in
self
.
data
[
'courses'
]:
self
.
_execute
(
'_attach_organization'
,
organization
)
for
course_run
in
course
[
'course_runs'
]:
def
_extend_run_modes
(
self
):
"""Execute run mode data handlers."""
for
course_code
in
self
.
data
[
'course_codes'
]:
for
run_mode
in
course_code
[
'run_modes'
]:
# State to be shared across handlers.
# State to be shared across handlers.
self
.
course_
key
=
CourseKey
.
from_string
(
run_mode
[
'course_
key'
])
self
.
course_
run_key
=
CourseKey
.
from_string
(
course_run
[
'
key'
])
self
.
course_overview
=
CourseOverview
.
get_from_id
(
self
.
course_key
)
self
.
course_overview
=
CourseOverview
.
get_from_id
(
self
.
course_
run_
key
)
self
.
enrollment_start
=
self
.
course_overview
.
enrollment_start
or
DEFAULT_ENROLLMENT_START_DATE
self
.
enrollment_start
=
self
.
course_overview
.
enrollment_start
or
DEFAULT_ENROLLMENT_START_DATE
self
.
_execute
(
'_attach_run_mode'
,
run_mode
)
self
.
_execute
(
'_attach_course_run'
,
course_run
)
def
_attach_organization_logo
(
self
,
organization
):
# TODO: Cache the results of the get_organization_by_short_name call so
# the database is hit less frequently.
org_obj
=
get_organization_by_short_name
(
organization
[
'key'
])
if
org_obj
and
org_obj
.
get
(
'logo'
):
organization
[
'img'
]
=
org_obj
[
'logo'
]
.
url
def
_attach_
run_mode
_certificate_url
(
self
,
run_mode
):
def
_attach_
course_run
_certificate_url
(
self
,
run_mode
):
certificate_data
=
certificate_api
.
certificate_downloadable_status
(
self
.
user
,
self
.
course_key
)
certificate_data
=
certificate_api
.
certificate_downloadable_status
(
self
.
user
,
self
.
course_
run_
key
)
certificate_uuid
=
certificate_data
.
get
(
'uuid'
)
certificate_uuid
=
certificate_data
.
get
(
'uuid'
)
run_mode
[
'certificate_url'
]
=
certificate_api
.
get_certificate_url
(
run_mode
[
'certificate_url'
]
=
certificate_api
.
get_certificate_url
(
user_id
=
self
.
user
.
id
,
# Providing user_id allows us to fall back to PDF certificates
user_id
=
self
.
user
.
id
,
# Providing user_id allows us to fall back to PDF certificates
# if web certificates are not configured for a given course.
# if web certificates are not configured for a given course.
course_id
=
self
.
course_key
,
course_id
=
self
.
course_
run_
key
,
uuid
=
certificate_uuid
,
uuid
=
certificate_uuid
,
)
if
certificate_uuid
else
None
)
if
certificate_uuid
else
None
def
_attach_run_mode_course_image_url
(
self
,
run_mode
):
def
_attach_course_run_course_url
(
self
,
run_mode
):
run_mode
[
'course_image_url'
]
=
self
.
course_overview
.
course_image_url
run_mode
[
'course_url'
]
=
reverse
(
'course_root'
,
args
=
[
self
.
course_run_key
])
def
_attach_run_mode_course_url
(
self
,
run_mode
):
run_mode
[
'course_url'
]
=
reverse
(
'course_root'
,
args
=
[
self
.
course_key
])
def
_attach_run_mode_end_date
(
self
,
run_mode
):
run_mode
[
'end_date'
]
=
self
.
course_overview
.
end
def
_attach_
run_mode
_enrollment_open_date
(
self
,
run_mode
):
def
_attach_
course_run
_enrollment_open_date
(
self
,
run_mode
):
run_mode
[
'enrollment_open_date'
]
=
strftime_localized
(
self
.
enrollment_start
,
'SHORT_DATE'
)
run_mode
[
'enrollment_open_date'
]
=
strftime_localized
(
self
.
enrollment_start
,
'SHORT_DATE'
)
def
_attach_
run_mode
_is_course_ended
(
self
,
run_mode
):
def
_attach_
course_run
_is_course_ended
(
self
,
run_mode
):
end_date
=
self
.
course_overview
.
end
or
datetime
.
datetime
.
max
.
replace
(
tzinfo
=
utc
)
end_date
=
self
.
course_overview
.
end
or
datetime
.
datetime
.
max
.
replace
(
tzinfo
=
utc
)
run_mode
[
'is_course_ended'
]
=
end_date
<
datetime
.
datetime
.
now
(
utc
)
run_mode
[
'is_course_ended'
]
=
end_date
<
datetime
.
datetime
.
now
(
utc
)
def
_attach_
run_mode
_is_enrolled
(
self
,
run_mode
):
def
_attach_
course_run
_is_enrolled
(
self
,
run_mode
):
run_mode
[
'is_enrolled'
]
=
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course_key
)
run_mode
[
'is_enrolled'
]
=
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course_
run_
key
)
def
_attach_
run_mode
_is_enrollment_open
(
self
,
run_mode
):
def
_attach_
course_run
_is_enrollment_open
(
self
,
run_mode
):
enrollment_end
=
self
.
course_overview
.
enrollment_end
or
datetime
.
datetime
.
max
.
replace
(
tzinfo
=
utc
)
enrollment_end
=
self
.
course_overview
.
enrollment_end
or
datetime
.
datetime
.
max
.
replace
(
tzinfo
=
utc
)
run_mode
[
'is_enrollment_open'
]
=
self
.
enrollment_start
<=
datetime
.
datetime
.
now
(
utc
)
<
enrollment_end
run_mode
[
'is_enrollment_open'
]
=
self
.
enrollment_start
<=
datetime
.
datetime
.
now
(
utc
)
<
enrollment_end
def
_attach_run_mode_start_date
(
self
,
run_mode
):
def
_attach_course_run_advertised_start
(
self
,
run_mode
):
run_mode
[
'start_date'
]
=
self
.
course_overview
.
start
"""
The advertised_start is text a course author can provide to be displayed
def
_attach_run_mode_advertised_start
(
self
,
run_mode
):
instead of their course's start date. For example, if a course run were
to start on December 1, 2016, the author might provide 'Winter 2016' as
the advertised start.
"""
run_mode
[
'advertised_start'
]
=
self
.
course_overview
.
advertised_start
run_mode
[
'advertised_start'
]
=
self
.
course_overview
.
advertised_start
def
_attach_
run_mode
_upgrade_url
(
self
,
run_mode
):
def
_attach_
course_run
_upgrade_url
(
self
,
run_mode
):
required_mode_slug
=
run_mode
[
'
mode_slug
'
]
required_mode_slug
=
run_mode
[
'
type
'
]
enrolled_mode_slug
,
_
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course_key
)
enrolled_mode_slug
,
_
=
CourseEnrollment
.
enrollment_mode_for_user
(
self
.
user
,
self
.
course_
run_
key
)
is_mode_mismatch
=
required_mode_slug
!=
enrolled_mode_slug
is_mode_mismatch
=
required_mode_slug
!=
enrolled_mode_slug
is_upgrade_required
=
is_mode_mismatch
and
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course_key
)
is_upgrade_required
=
is_mode_mismatch
and
CourseEnrollment
.
is_enrolled
(
self
.
user
,
self
.
course_
run_
key
)
if
is_upgrade_required
:
if
is_upgrade_required
:
# Requires that the ecommerce service be in use.
# Requires that the ecommerce service be in use.
required_mode
=
CourseMode
.
mode_for_course
(
self
.
course_key
,
required_mode_slug
)
required_mode
=
CourseMode
.
mode_for_course
(
self
.
course_
run_
key
,
required_mode_slug
)
ecommerce
=
EcommerceService
()
ecommerce
=
EcommerceService
()
sku
=
getattr
(
required_mode
,
'sku'
,
None
)
sku
=
getattr
(
required_mode
,
'sku'
,
None
)
...
...
themes/edx.org/lms/templates/dashboard.html
View file @
dda0e03f
...
@@ -100,7 +100,7 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers
...
@@ -100,7 +100,7 @@ from openedx.core.djangoapps.theming import helpers as theming_helpers
<
%
is_course_blocked =
(enrollment.course_id
in
block_courses
)
%
>
<
%
is_course_blocked =
(enrollment.course_id
in
block_courses
)
%
>
<
%
course_verification_status =
verification_status_by_course.get(enrollment.course_id,
{})
%
>
<
%
course_verification_status =
verification_status_by_course.get(enrollment.course_id,
{})
%
>
<
%
course_requirements =
courses_requirements_not_met.get(enrollment.course_id)
%
>
<
%
course_requirements =
courses_requirements_not_met.get(enrollment.course_id)
%
>
<
%
related_programs =
programs_by_run
.get(unicode(enrollment.course_id))
%
>
<
%
related_programs =
inverted_programs
.get(unicode(enrollment.course_id))
%
>
<
%
include
file =
'dashboard/_dashboard_course_listing.html'
args=
"course_overview=enrollment.course_overview, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, show_refund_option=show_refund_option, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs"
/>
<
%
include
file =
'dashboard/_dashboard_course_listing.html'
args=
"course_overview=enrollment.course_overview, enrollment=enrollment, show_courseware_link=show_courseware_link, cert_status=cert_status, can_unenroll=can_unenroll, credit_status=credit_status, show_email_settings=show_email_settings, course_mode_info=course_mode_info, show_refund_option=show_refund_option, is_paid_course=is_paid_course, is_course_blocked=is_course_blocked, verification_status=course_verification_status, course_requirements=course_requirements, dashboard_index=dashboard_index, share_settings=share_settings, user=user, related_programs=related_programs"
/>
% endfor
% endfor
...
...
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