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
df89a1d2
Commit
df89a1d2
authored
Oct 25, 2017
by
Diana Huang
Committed by
GitHub
Oct 25, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #16314 from edx/diana/programs-dashboard-grades
ECOM-7387 - Add grade display to the programs dashboard
parents
3dee61ec
b0877270
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
169 additions
and
23 deletions
+169
-23
lms/static/js/learner_dashboard/views/course_card_view.js
+5
-0
lms/static/js/learner_dashboard/views/course_enroll_view.js
+7
-2
lms/static/js/learner_dashboard/views/program_details_view.js
+5
-3
lms/static/js/spec/learner_dashboard/course_card_view_spec.js
+28
-3
lms/static/js/spec/learner_dashboard/program_details_view_spec.js
+4
-1
lms/static/sass/views/_program-details.scss
+13
-0
lms/templates/learner_dashboard/course_enroll.underscore
+6
-1
openedx/core/djangoapps/programs/tests/factories.py
+1
-0
openedx/core/djangoapps/programs/tests/test_utils.py
+89
-12
openedx/core/djangoapps/programs/utils.py
+11
-1
No files found.
lms/static/js/learner_dashboard/views/course_card_view.js
View file @
df89a1d2
...
...
@@ -38,6 +38,9 @@
this
.
enrollModel
.
urlRoot
=
this
.
urlModel
.
get
(
'commerce_api_url'
);
}
this
.
context
=
options
.
context
||
{};
this
.
grade
=
this
.
context
.
courseData
.
grades
[
this
.
model
.
get
(
'course_run_key'
)];
this
.
grade
=
this
.
grade
*
100
;
this
.
collectionCourseStatus
=
this
.
context
.
collectionCourseStatus
||
''
;
this
.
render
();
this
.
listenTo
(
this
.
model
,
'change'
,
this
.
render
);
},
...
...
@@ -59,6 +62,8 @@
this
.
enrollView
=
new
CourseEnrollView
({
$parentEl
:
this
.
$
(
'.course-actions'
),
model
:
this
.
model
,
grade
:
this
.
grade
,
collectionCourseStatus
:
this
.
collectionCourseStatus
,
urlModel
:
this
.
urlModel
,
enrollModel
:
this
.
enrollModel
});
...
...
lms/static/js/learner_dashboard/views/course_enroll_view.js
View file @
df89a1d2
...
...
@@ -29,13 +29,18 @@
this
.
$parentEl
=
options
.
$parentEl
;
this
.
enrollModel
=
options
.
enrollModel
;
this
.
urlModel
=
options
.
urlModel
;
this
.
grade
=
options
.
grade
;
this
.
collectionCourseStatus
=
options
.
collectionCourseStatus
;
this
.
render
();
},
render
:
function
()
{
var
filledTemplate
;
var
filledTemplate
,
context
=
this
.
model
.
toJSON
();
if
(
this
.
$parentEl
&&
this
.
enrollModel
)
{
filledTemplate
=
this
.
tpl
(
this
.
model
.
toJSON
());
context
.
grade
=
this
.
grade
;
context
.
collectionCourseStatus
=
this
.
collectionCourseStatus
;
filledTemplate
=
this
.
tpl
(
context
);
HtmlUtils
.
setHtml
(
this
.
$el
,
filledTemplate
);
HtmlUtils
.
setHtml
(
this
.
$parentEl
,
HtmlUtils
.
HTML
(
this
.
$el
));
}
...
...
lms/static/js/learner_dashboard/views/program_details_view.js
View file @
df89a1d2
...
...
@@ -91,7 +91,7 @@
el
:
'.js-course-list-remaining'
,
childView
:
CourseCardView
,
collection
:
this
.
remainingCourseCollection
,
context
:
this
.
options
context
:
$
.
extend
(
this
.
options
,
{
collectionCourseStatus
:
'remaining'
})
}).
render
();
}
...
...
@@ -100,7 +100,7 @@
el
:
'.js-course-list-completed'
,
childView
:
CourseCardView
,
collection
:
this
.
completedCourseCollection
,
context
:
this
.
options
context
:
$
.
extend
(
this
.
options
,
{
collectionCourseStatus
:
'completed'
})
}).
render
();
}
...
...
@@ -110,7 +110,9 @@
el
:
'.js-course-list-in-progress'
,
childView
:
CourseCardView
,
collection
:
this
.
inProgressCourseCollection
,
context
:
$
.
extend
(
this
.
options
,
{
enrolled
:
gettext
(
'Enrolled'
)})
context
:
$
.
extend
(
this
.
options
,
{
enrolled
:
gettext
(
'Enrolled'
),
collectionCourseStatus
:
'in_progress'
}
)
}).
render
();
}
...
...
lms/static/js/spec/learner_dashboard/course_card_view_spec.js
View file @
df89a1d2
...
...
@@ -13,14 +13,27 @@ define([
startDate
=
'Feb 28, 2017'
,
endDate
=
'May 30, 2017'
,
setupView
=
function
(
data
,
isEnrolled
)
{
var
programData
=
$
.
extend
({},
data
);
setupView
=
function
(
data
,
isEnrolled
,
collectionCourseStatus
)
{
var
programData
=
$
.
extend
({},
data
),
context
=
{
courseData
:
{
grades
:
{
'course-v1:WageningenX+FFESx+1T2017'
:
0.8
}
},
collectionCourseStatus
:
collectionCourseStatus
};
if
(
typeof
collectionCourseStatus
===
'undefined'
)
{
context
.
collectionCourseStatus
=
'completed'
;
}
programData
.
course_runs
[
0
].
is_enrolled
=
isEnrolled
;
setFixtures
(
'<div class="program-course-card"></div>'
);
courseCardModel
=
new
CourseCardModel
(
programData
);
view
=
new
CourseCardView
({
model
:
courseCardModel
model
:
courseCardModel
,
context
:
context
});
},
...
...
@@ -84,6 +97,18 @@ define([
validateCourseInfoDisplay
();
});
it
(
'should render final grade if course is completed'
,
function
()
{
view
.
remove
();
setupView
(
course
,
true
);
expect
(
view
.
$
(
'.grade-display'
).
text
()).
toEqual
(
'80%'
);
});
it
(
'should not render final grade if course has not been completed'
,
function
()
{
view
.
remove
();
setupView
(
course
,
true
,
'in_progress'
);
expect
(
view
.
$
(
'.final-grade'
).
length
).
toEqual
(
0
);
});
it
(
'should render the course card based on the data not enrolled'
,
function
()
{
validateCourseInfoDisplay
();
});
...
...
lms/static/js/spec/learner_dashboard/program_details_view_spec.js
View file @
df89a1d2
...
...
@@ -462,7 +462,10 @@ define([
}
]
}
]
],
grades
:
{
'course-v1:Testx+DOGx002+1T2016'
:
0.9
}
},
urls
:
{
program_listing_url
:
'/dashboard/programs/'
,
...
...
lms/static/sass/views/_program-details.scss
View file @
df89a1d2
...
...
@@ -520,6 +520,19 @@
width
:
100%
;
}
.final-grade
{
.grade-header
{
color
:
palette
(
grayscale
,
base
);
font-weight
:
bold
;
}
.grade-display
{
padding-right
:
15px
;
font-size
:
1
.5em
;
color
:
palette
(
primary
,
accent
);
}
}
.upgrade-message
{
flex-wrap
:
wrap
;
...
...
lms/templates/learner_dashboard/course_enroll.underscore
View file @
df89a1d2
<% if (is_enrolled && (typeof expired === 'undefined' || expired === false)) { %>
<% if (is_enrolled && collectionCourseStatus === 'completed') { %>
<div class="final-grade">
<div class="grade-header"><%- gettext('Final Grade') %><span class="sr"><%- StringUtils.interpolate(gettext('for {courseName}'), {courseName: title}) %></span></div>
<div class="grade-display"><%- grade %>%</div>
</div>
<% } else if (is_enrolled && (typeof expired === 'undefined' || expired === false)) { %>
<a href="<%- course_url %>" class="view-course-button btn-brand btn cta-primary">
<% if (is_course_ended) { %>
<%- gettext('View Archived Course') %>
...
...
openedx/core/djangoapps/programs/tests/factories.py
View file @
df89a1d2
...
...
@@ -12,3 +12,4 @@ class ProgressFactory(factory.Factory):
completed
=
0
in_progress
=
0
not_started
=
0
grades
=
dict
()
openedx/core/djangoapps/programs/tests/test_utils.py
View file @
df89a1d2
...
...
@@ -18,6 +18,7 @@ from course_modes.models import CourseMode
from
lms.djangoapps.certificates.api
import
MODES
from
lms.djangoapps.commerce.tests.test_utils
import
update_commerce_config
from
lms.djangoapps.commerce.utils
import
EcommerceService
from
lms.djangoapps.grades.tests.utils
import
mock_passing_grade
from
openedx.core.djangoapps.catalog.tests.factories
import
(
generate_course_run_key
,
ProgramFactory
,
...
...
@@ -138,7 +139,7 @@ class TestProgramProgressMeter(TestCase):
self
.
assertEqual
(
meter
.
engaged_programs
,
[
program
])
self
.
_assert_progress
(
meter
,
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
)
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
,
grades
=
{
course_run_key
:
0.0
}
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
...
@@ -169,7 +170,8 @@ class TestProgramProgressMeter(TestCase):
uuid
=
program
[
'uuid'
],
completed
=
[],
in_progress
=
[
program
[
'courses'
][
0
]],
not_started
=
[]
not_started
=
[],
grades
=
{
course_run_key
:
0.0
},
)
]
...
...
@@ -205,7 +207,8 @@ class TestProgramProgressMeter(TestCase):
uuid
=
program
[
'uuid'
],
completed
=
[],
in_progress
=
[
program
[
'courses'
][
0
]],
not_started
=
[]
not_started
=
[],
grades
=
{
course_run_key
:
0.0
},
)
]
...
...
@@ -246,7 +249,8 @@ class TestProgramProgressMeter(TestCase):
uuid
=
program
[
'uuid'
],
completed
=
0
,
in_progress
=
1
if
offset
in
[
None
,
1
]
else
0
,
not_started
=
1
if
offset
in
[
-
1
]
else
0
not_started
=
1
if
offset
in
[
-
1
]
else
0
,
grades
=
{
course_run_key
:
0.0
},
)
]
...
...
@@ -285,9 +289,14 @@ class TestProgramProgressMeter(TestCase):
self
.
_attach_detail_url
(
data
)
programs
=
data
[:
2
]
self
.
assertEqual
(
meter
.
engaged_programs
,
programs
)
grades
=
{
newer_course_run_key
:
0.0
,
older_course_run_key
:
0.0
,
}
self
.
_assert_progress
(
meter
,
*
(
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
)
for
program
in
programs
)
*
(
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
,
grades
=
grades
)
for
program
in
programs
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
...
@@ -330,9 +339,15 @@ class TestProgramProgressMeter(TestCase):
self
.
_attach_detail_url
(
data
)
programs
=
data
[:
3
]
self
.
assertEqual
(
meter
.
engaged_programs
,
programs
)
grades
=
{
solo_course_run_key
:
0.0
,
shared_course_run_key
:
0.0
,
}
self
.
_assert_progress
(
meter
,
*
(
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
)
for
program
in
programs
)
*
(
ProgressFactory
(
uuid
=
program
[
'uuid'
],
in_progress
=
1
,
grades
=
grades
)
for
program
in
programs
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
...
@@ -366,7 +381,7 @@ class TestProgramProgressMeter(TestCase):
program
,
program_uuid
=
data
[
0
],
data
[
0
][
'uuid'
]
self
.
_assert_progress
(
meter
,
ProgressFactory
(
uuid
=
program_uuid
,
in_progress
=
1
,
not_started
=
1
)
ProgressFactory
(
uuid
=
program_uuid
,
in_progress
=
1
,
not_started
=
1
,
grades
=
{
first_course_run_key
:
0.0
}
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
...
@@ -375,7 +390,14 @@ class TestProgramProgressMeter(TestCase):
meter
=
ProgramProgressMeter
(
self
.
site
,
self
.
user
)
self
.
_assert_progress
(
meter
,
ProgressFactory
(
uuid
=
program_uuid
,
in_progress
=
2
)
ProgressFactory
(
uuid
=
program_uuid
,
in_progress
=
2
,
grades
=
{
first_course_run_key
:
0.0
,
second_course_run_key
:
0.0
,
},
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
...
@@ -386,7 +408,15 @@ class TestProgramProgressMeter(TestCase):
meter
=
ProgramProgressMeter
(
self
.
site
,
self
.
user
)
self
.
_assert_progress
(
meter
,
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
,
in_progress
=
1
)
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
,
in_progress
=
1
,
grades
=
{
first_course_run_key
:
0.0
,
second_course_run_key
:
0.0
,
}
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
...
@@ -398,7 +428,15 @@ class TestProgramProgressMeter(TestCase):
meter
=
ProgramProgressMeter
(
self
.
site
,
self
.
user
)
self
.
_assert_progress
(
meter
,
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
,
in_progress
=
1
)
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
,
in_progress
=
1
,
grades
=
{
first_course_run_key
:
0.0
,
second_course_run_key
:
0.0
,
}
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[])
...
...
@@ -410,7 +448,14 @@ class TestProgramProgressMeter(TestCase):
meter
=
ProgramProgressMeter
(
self
.
site
,
self
.
user
)
self
.
_assert_progress
(
meter
,
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
2
)
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
2
,
grades
=
{
first_course_run_key
:
0.0
,
second_course_run_key
:
0.0
,
}
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[
program_uuid
])
...
...
@@ -443,7 +488,7 @@ class TestProgramProgressMeter(TestCase):
program
,
program_uuid
=
data
[
0
],
data
[
0
][
'uuid'
]
self
.
_assert_progress
(
meter
,
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
)
ProgressFactory
(
uuid
=
program_uuid
,
completed
=
1
,
grades
=
{
course_run_key
:
0.0
}
)
)
self
.
assertEqual
(
meter
.
completed_programs
,
[
program_uuid
])
...
...
@@ -549,6 +594,38 @@ class TestProgramProgressMeter(TestCase):
mock_completed_course_runs
.
return_value
=
[{
'course_run_id'
:
course_run_key
,
'type'
:
'verified'
}]
self
.
assertEqual
(
meter
.
_is_course_complete
(
course
),
True
)
def
test_course_grade_results
(
self
,
mock_get_programs
):
grade_percent
=
.
8
with
mock_passing_grade
(
percent
=
grade_percent
):
course_run_key
=
generate_course_run_key
()
data
=
[
ProgramFactory
(
courses
=
[
CourseFactory
(
course_runs
=
[
CourseRunFactory
(
key
=
course_run_key
),
]),
]
)
]
mock_get_programs
.
return_value
=
data
self
.
_create_enrollments
(
course_run_key
)
meter
=
ProgramProgressMeter
(
self
.
site
,
self
.
user
)
program
=
data
[
0
]
expected
=
[
ProgressFactory
(
uuid
=
program
[
'uuid'
],
completed
=
[],
in_progress
=
[
program
[
'courses'
][
0
]],
not_started
=
[],
grades
=
{
course_run_key
:
grade_percent
},
)
]
self
.
assertEqual
(
meter
.
progress
(
count_only
=
False
),
expected
)
def
_create_course
(
self
,
course_price
,
course_run_count
=
1
):
"""
...
...
openedx/core/djangoapps/programs/utils.py
View file @
df89a1d2
...
...
@@ -22,6 +22,8 @@ from course_modes.models import CourseMode
from
lms.djangoapps.certificates
import
api
as
certificate_api
from
lms.djangoapps.commerce.utils
import
EcommerceService
from
lms.djangoapps.courseware.access
import
has_access
from
lms.djangoapps.courseware.courses
import
get_course_with_access
from
lms.djangoapps.grades.course_grade_factory
import
CourseGradeFactory
from
openedx.core.djangoapps.catalog.utils
import
get_programs
from
openedx.core.djangoapps.commerce.utils
import
ecommerce_api_client
from
openedx.core.djangoapps.content.course_overviews.models
import
CourseOverview
...
...
@@ -89,6 +91,8 @@ class ProgramProgressMeter(object):
# We can't use dict.keys() for this because the course run ids need to be ordered
self
.
course_run_ids
.
append
(
enrollment_id
)
self
.
course_grade_factory
=
CourseGradeFactory
()
if
uuid
:
self
.
programs
=
[
get_programs
(
self
.
site
,
uuid
=
uuid
)]
else
:
...
...
@@ -216,11 +220,17 @@ class ProgramProgressMeter(object):
else
:
not_started
.
append
(
course
)
grades
=
{}
for
run
in
self
.
course_run_ids
:
grade
=
self
.
course_grade_factory
.
read
(
self
.
user
,
course_key
=
CourseKey
.
from_string
(
run
))
grades
[
run
]
=
grade
.
percent
progress
.
append
({
'uuid'
:
program_copy
[
'uuid'
],
'completed'
:
len
(
completed
)
if
count_only
else
completed
,
'in_progress'
:
len
(
in_progress
)
if
count_only
else
in_progress
,
'not_started'
:
len
(
not_started
)
if
count_only
else
not_started
,
'grades'
:
grades
,
})
return
progress
...
...
@@ -325,7 +335,7 @@ class ProgramProgressMeter(object):
course_data
=
{
'course_run_id'
:
unicode
(
certificate
[
'course_key'
]),
'type'
:
certificate_type
'type'
:
certificate_type
,
}
if
certificate_api
.
is_passing_status
(
certificate
[
'status'
]):
...
...
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