Commit 6fdff766 by Ahsan Ulhaq

Credit eligibility requirements display on student progress page

ECOM-1523
parent 473c6886
......@@ -42,6 +42,9 @@ from lms.envs.common import (
# technically accessible through the CMS via legacy URLs.
PROFILE_IMAGE_BACKEND, PROFILE_IMAGE_DEFAULT_FILENAME, PROFILE_IMAGE_DEFAULT_FILE_EXTENSION,
PROFILE_IMAGE_SECRET_KEY, PROFILE_IMAGE_MIN_BYTES, PROFILE_IMAGE_MAX_BYTES,
# The following setting is included as it is used to check whether to
# display credit eligibility table on the CMS or not.
ENABLE_CREDIT_ELIGIBILITY
)
from path import path
from warnings import simplefilter
......@@ -174,7 +177,7 @@ FEATURES = {
'SHOW_BUMPER_PERIODICITY': 7 * 24 * 3600,
# Enable credit eligibility feature
'ENABLE_CREDIT_ELIGIBILITY': False,
'ENABLE_CREDIT_ELIGIBILITY': ENABLE_CREDIT_ELIGIBILITY,
# Can the visibility of the discussion tab be configured on a per-course basis?
'ALLOW_HIDING_DISCUSSION_TAB': False,
......@@ -248,7 +251,6 @@ from lms.envs.common import (
COURSE_KEY_PATTERN, COURSE_ID_PATTERN, USAGE_KEY_PATTERN, ASSET_KEY_PATTERN
)
######################### CSRF #########################################
# Forwards-compatibility with Django 1.7
......
......@@ -102,7 +102,7 @@ var GradingView = ValidatingView.extend({
renderMinimumGradeCredit: function() {
var minimum_grade_credit = this.model.get('minimum_grade_credit');
this.$el.find('#course-minimum_grade_credit').val(
parseFloat(minimum_grade_credit) * 100 + '%'
Math.round(parseFloat(minimum_grade_credit) * 100) + '%'
);
},
setGracePeriod : function(event) {
......
......@@ -173,18 +173,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
TEST_DATA = {
# (providers, course_width, enable_ccx): # of sql queries, # of mongo queries, # of xblocks
('no_overrides', 1, True): (27, 7, 19),
('no_overrides', 2, True): (135, 7, 131),
('no_overrides', 3, True): (595, 7, 537),
('ccx', 1, True): (27, 7, 47),
('ccx', 2, True): (135, 7, 455),
('ccx', 3, True): (595, 7, 2037),
('no_overrides', 1, False): (27, 7, 19),
('no_overrides', 2, False): (135, 7, 131),
('no_overrides', 3, False): (595, 7, 537),
('ccx', 1, False): (27, 7, 19),
('ccx', 2, False): (135, 7, 131),
('ccx', 3, False): (595, 7, 537),
('no_overrides', 1, True): (26, 7, 19),
('no_overrides', 2, True): (134, 7, 131),
('no_overrides', 3, True): (594, 7, 537),
('ccx', 1, True): (26, 7, 47),
('ccx', 2, True): (134, 7, 455),
('ccx', 3, True): (594, 7, 2037),
('no_overrides', 1, False): (26, 7, 19),
('no_overrides', 2, False): (134, 7, 131),
('no_overrides', 3, False): (594, 7, 537),
('ccx', 1, False): (26, 7, 19),
('ccx', 2, False): (134, 7, 131),
('ccx', 3, False): (594, 7, 537),
}
......@@ -196,16 +196,16 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
__test__ = True
TEST_DATA = {
('no_overrides', 1, True): (27, 4, 9),
('no_overrides', 2, True): (135, 19, 54),
('no_overrides', 3, True): (595, 84, 215),
('ccx', 1, True): (27, 4, 9),
('ccx', 2, True): (135, 19, 54),
('ccx', 3, True): (595, 84, 215),
('no_overrides', 1, False): (27, 4, 9),
('no_overrides', 2, False): (135, 19, 54),
('no_overrides', 3, False): (595, 84, 215),
('ccx', 1, False): (27, 4, 9),
('ccx', 2, False): (135, 19, 54),
('ccx', 3, False): (595, 84, 215),
('no_overrides', 1, True): (26, 4, 9),
('no_overrides', 2, True): (134, 19, 54),
('no_overrides', 3, True): (594, 84, 215),
('ccx', 1, True): (26, 4, 9),
('ccx', 2, True): (134, 19, 54),
('ccx', 3, True): (594, 84, 215),
('no_overrides', 1, False): (26, 4, 9),
('no_overrides', 2, False): (134, 19, 54),
('no_overrides', 3, False): (594, 84, 215),
('ccx', 1, False): (26, 4, 9),
('ccx', 2, False): (134, 19, 54),
('ccx', 3, False): (594, 84, 215),
}
......@@ -7,6 +7,7 @@ import urllib
import json
import cgi
from collections import OrderedDict
from datetime import datetime
from django.utils import translation
from django.utils.translation import ugettext as _
......@@ -1065,7 +1066,9 @@ def _progress(request, course_key, student_id):
# checking certificate generation configuration
show_generate_cert_btn = certs_api.cert_generation_enabled(course_key)
if is_credit_course(course_key):
credit_course_requirements = None
is_course_credit = settings.FEATURES.get("ENABLE_CREDIT_ELIGIBILITY", False) and is_credit_course(course_key)
if is_course_credit:
requirement_statuses = get_credit_requirement_status(course_key, student.username)
if any(requirement['status'] == 'failed' for requirement in requirement_statuses):
eligibility_status = "not_eligible"
......@@ -1073,12 +1076,16 @@ def _progress(request, course_key, student_id):
eligibility_status = "eligible"
else:
eligibility_status = "partial_eligible"
credit_course = {
paired_requirements = {}
for requirement in requirement_statuses:
namespace = requirement.pop("namespace")
paired_requirements.setdefault(namespace, []).append(requirement)
credit_course_requirements = {
'eligibility_status': eligibility_status,
'requirements': requirement_statuses
'requirements': OrderedDict(sorted(paired_requirements.items(), reverse=True))
}
else:
credit_course = None
context = {
'course': course,
......@@ -1089,7 +1096,8 @@ def _progress(request, course_key, student_id):
'student': student,
'passed': is_course_passed(course, grade_summary),
'show_generate_cert_btn': show_generate_cert_btn,
'credit_course': credit_course
'credit_course_requirements': credit_course_requirements,
'is_credit_course': is_course_credit,
}
if show_generate_cert_btn:
......
......@@ -2021,6 +2021,10 @@ FEATURES['CLASS_DASHBOARD'] = False
if FEATURES.get('CLASS_DASHBOARD'):
INSTALLED_APPS += ('class_dashboard',)
################ Enable credit eligibility feature ####################
ENABLE_CREDIT_ELIGIBILITY = False
FEATURES['ENABLE_CREDIT_ELIGIBILITY'] = ENABLE_CREDIT_ELIGIBILITY
######################## CAS authentication ###########################
if FEATURES.get('AUTH_USE_CAS'):
......
$(document).ready(function() {
var container = $('.requirement-container');
var collapse = container.data('eligible');
if (collapse == 'not_eligible') {
container.addClass('is-hidden');
$('.detail-collapse').find('.fa').toggleClass('fa-caret-up fa-caret-down');
$('.requirement-detail').text(gettext('More'));
}
$('.detail-collapse').on('click', function() {
var el = $(this);
$('.requirement-container').toggleClass('is-hidden');
el.find('.fa').toggleClass('fa-caret-down fa-caret-up');
container.toggleClass('is-hidden');
el.find('.fa').toggleClass('fa-caret-up fa-caret-down');
el.find('.requirement-detail').text(function(i, text){
return text === gettext('More') ? gettext('Less') : gettext('More');
return text === gettext('Less') ? gettext('More') : gettext('Less');
});
});
});
......@@ -215,11 +215,11 @@
border-bottom: 1px solid $lightGrey;
padding: lh(0.5);
> .requirement-name {
width: bi-app-invert-percentage(30%);
width: bi-app-invert-percentage(40%);
display: inline-block;
}
> .requirement-status{
width: bi-app-invert-percentage(70%);
width: bi-app-invert-percentage(60%);
@include float(right);
display: inline-block;
.fa-times{
......@@ -231,7 +231,7 @@
color: $success-color;
}
> .not-achieve{
color: $lightGrey;
color: $darkGrey;
}
}
}
......
......@@ -3,7 +3,7 @@
<%!
from django.utils.translation import ugettext as _
from django.core.urlresolvers import reverse
from util.date_utils import get_time_display, DEFAULT_LONG_DATE_FORMAT
from util.date_utils import get_time_display, DEFAULT_SHORT_DATE_FORMAT
from django.conf import settings
from django.utils.http import urlquote_plus
%>
......@@ -103,26 +103,27 @@ from django.utils.http import urlquote_plus
<div class="grade-detail-graph" id="grade-detail-graph" aria-hidden="true"></div>
%endif
%if credit_course is not None:
% if is_credit_course:
<section class="credit-eligibility">
<div class="credit-eligibility-container">
<div class="eligibility-heading">
<h2>${_("Requirements for Course Credit")}</h2>
</div>
%if credit_course['eligibility_status'] == 'not_eligible':
<span class="eligibility_msg">${student.username}, ${_("You are no longer eligible for this course.")}</span>
%elif credit_course['eligibility_status'] == 'eligible':
<span class="eligibility_msg">${student.username}, ${_("You have met the requirements for credit in this course.")}
<a href="#">${_("Go to your dashboard")}</a> ${_("to purchase course credit.")}
%if credit_course_requirements['eligibility_status'] == 'not_eligible':
<span class="eligibility_msg">${student.get_full_name()}, ${_("You are no longer eligible for this course.")}</span>
%elif credit_course_requirements['eligibility_status'] == 'eligible':
<span class="eligibility_msg">${student.get_full_name()}, ${_("You have met the requirements for credit in this course.")}
${_("{link} to purchase course credit.").format(link="<a href={url}>{url_name}</a>".format(url = reverse('dashboard'), url_name = _('Go to your dashboard')))}
</span>
%elif credit_course['eligibility_status'] == 'partial_eligible':
<span>${student.username}, ${_("You have not yet met the requirements for credit.")}</span>
%elif credit_course_requirements['eligibility_status'] == 'partial_eligible':
<span>${student.get_full_name()}, ${_("You have not yet met the requirements for credit.")}</span>
%endif
<button class="credit-help"><i class="fa fa-question"></i><span class="sr">Help regarding credit requirement</span></button><br>
<div class="requirement-container is-hidden">
%for requirement in credit_course['requirements']:
<div class="requirement-container" data-eligible="${credit_course_requirements['eligibility_status']}">
%for namespace in credit_course_requirements['requirements']:
%for requirement in credit_course_requirements['requirements'][namespace]:
<div class="requirement">
<div class="requirement-name">${_(requirement['name'])}</div>
<div class="requirement-name">${_(requirement['display_name'])}</div>
<div class="requirement-status">
%if requirement['status']:
%if requirement['status'] == 'submitted':
......@@ -132,7 +133,7 @@ from django.utils.http import urlquote_plus
<span>${_("Verification Failed" )}</span>
%elif requirement['status'] == 'satisfied':
<i class="fa fa-check"></i>
<span>Verified on ${get_time_display(requirement['status_date'], DEFAULT_LONG_DATE_FORMAT, settings.TIME_ZONE)}</span>
<span>Verified on ${get_time_display(requirement['status_date'], DEFAULT_SHORT_DATE_FORMAT, settings.TIME_ZONE)}</span>
%endif
%else:
<span class="not-achieve">${_("Upcoming")}</span>
......@@ -140,9 +141,10 @@ from django.utils.http import urlquote_plus
</div>
</div>
%endfor
%endfor
</div>
<button class="detail-collapse" aria-live="polite"><i class="fa fa-caret-down"></i>
<span class="requirement-detail">${_("More")}</span><span class="sr"> detail</span>
<button class="detail-collapse" aria-live="polite"><i class="fa fa-caret-up"></i>
<span class="requirement-detail">${_("Less")}</span>
</button>
</div>
</section>
......
......@@ -419,26 +419,23 @@ def get_credit_requirement_status(course_key, username):
{
"namespace": "reverification",
"name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid",
"display_name": "In Course Reverification",
"criteria": {},
"status": "satisfied",
},
{
"namespace": "reverification",
"name": "i4x://edX/DemoX/edx-reverification-block/assessment_uuid",
"criteria": {},
"status": "Not satisfied",
"status": "failed",
},
{
"namespace": "proctored_exam",
"name": "i4x://edX/DemoX/proctoring-block/final_uuid",
"display_name": "Proctored Mid Term Exam",
"criteria": {},
"status": "error",
"status": "satisfied",
},
{
"namespace": "grade",
"name": "i4x://edX/DemoX/proctoring-block/final_uuid",
"display_name": "Minimum Passing Grade",
"criteria": {"min_grade": 0.8},
"status": None,
"status": "failed",
},
]
......@@ -454,6 +451,7 @@ def get_credit_requirement_status(course_key, username):
statuses.append({
"namespace": requirement.namespace,
"name": requirement.name,
"display_name": requirement.display_name,
"criteria": requirement.criteria,
"status": requirement_status.status if requirement_status else None,
"status_date": requirement_status.modified if requirement_status else None,
......@@ -475,18 +473,6 @@ def is_user_eligible_for_credit(username, course_key):
return CreditEligibility.is_user_eligible_for_credit(course_key, username)
def is_credit_course(course_key):
"""Check if the given course is a credit course
Arg:
course_key (CourseKey): The identifier for course
Returns:
True if course is credit course else False
"""
return CreditCourse.is_credit_course(course_key)
def get_credit_requirement(course_key, namespace, name):
"""Returns the requirement of a given course, namespace and name.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment