From 6fdff766bc5b02ee73fec99b994c42b4c12d0b1f Mon Sep 17 00:00:00 2001 From: Ahsan Ulhaq Date: Wed, 17 Jun 2015 19:46:57 +0500 Subject: [PATCH] Credit eligibility requirements display on student progress page ECOM-1523 --- cms/envs/common.py | 6 ++- cms/static/js/views/settings/grading.js | 2 +- .../tests/test_field_override_performance.py | 48 +++++++++---------- lms/djangoapps/courseware/views.py | 20 +++++--- lms/envs/common.py | 4 ++ lms/static/js/courseware/credit_progress.js | 14 ++++-- lms/static/sass/course/_profile.scss | 6 +-- lms/templates/courseware/progress.html | 32 +++++++------ openedx/core/djangoapps/credit/api.py | 28 +++-------- 9 files changed, 84 insertions(+), 76 deletions(-) diff --git a/cms/envs/common.py b/cms/envs/common.py index 46a2dcc184..0057b86f22 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -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 diff --git a/cms/static/js/views/settings/grading.js b/cms/static/js/views/settings/grading.js index 0af6632c7f..def1ff1bc0 100644 --- a/cms/static/js/views/settings/grading.js +++ b/cms/static/js/views/settings/grading.js @@ -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) { diff --git a/lms/djangoapps/ccx/tests/test_field_override_performance.py b/lms/djangoapps/ccx/tests/test_field_override_performance.py index 5bacdd2d98..8d4eff1ac5 100644 --- a/lms/djangoapps/ccx/tests/test_field_override_performance.py +++ b/lms/djangoapps/ccx/tests/test_field_override_performance.py @@ -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), } diff --git a/lms/djangoapps/courseware/views.py b/lms/djangoapps/courseware/views.py index f9e5fc3e0b..2c66444ab9 100644 --- a/lms/djangoapps/courseware/views.py +++ b/lms/djangoapps/courseware/views.py @@ -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: diff --git a/lms/envs/common.py b/lms/envs/common.py index d2fedc6fed..15379eb64e 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -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'): diff --git a/lms/static/js/courseware/credit_progress.js b/lms/static/js/courseware/credit_progress.js index d0600b85c0..61c941206e 100644 --- a/lms/static/js/courseware/credit_progress.js +++ b/lms/static/js/courseware/credit_progress.js @@ -1,11 +1,17 @@ $(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'); }); }); - }); diff --git a/lms/static/sass/course/_profile.scss b/lms/static/sass/course/_profile.scss index 32ff94b641..e43f497777 100644 --- a/lms/static/sass/course/_profile.scss +++ b/lms/static/sass/course/_profile.scss @@ -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; } } } diff --git a/lms/templates/courseware/progress.html b/lms/templates/courseware/progress.html index ce6e5e8d41..d2dbdafc69 100644 --- a/lms/templates/courseware/progress.html +++ b/lms/templates/courseware/progress.html @@ -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 %endif - %if credit_course is not None: + % if is_credit_course:

${_("Requirements for Course Credit")}

- %if credit_course['eligibility_status'] == 'not_eligible': - ${student.username}, ${_("You are no longer eligible for this course.")} - %elif credit_course['eligibility_status'] == 'eligible': - ${student.username}, ${_("You have met the requirements for credit in this course.")} - ${_("Go to your dashboard")} ${_("to purchase course credit.")} + %if credit_course_requirements['eligibility_status'] == 'not_eligible': + ${student.get_full_name()}, ${_("You are no longer eligible for this course.")} + %elif credit_course_requirements['eligibility_status'] == 'eligible': + ${student.get_full_name()}, ${_("You have met the requirements for credit in this course.")} + ${_("{link} to purchase course credit.").format(link="{url_name}".format(url = reverse('dashboard'), url_name = _('Go to your dashboard')))} - %elif credit_course['eligibility_status'] == 'partial_eligible': - ${student.username}, ${_("You have not yet met the requirements for credit.")} + %elif credit_course_requirements['eligibility_status'] == 'partial_eligible': + ${student.get_full_name()}, ${_("You have not yet met the requirements for credit.")} %endif
-
diff --git a/openedx/core/djangoapps/credit/api.py b/openedx/core/djangoapps/credit/api.py index 9658a1c823..ebec6b977d 100644 --- a/openedx/core/djangoapps/credit/api.py +++ b/openedx/core/djangoapps/credit/api.py @@ -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.