Commit bcbd1464 by Zia Fazal

New course cannot be started till web certificate is active

fixed broken test

changes based on feedback on 6/24

fixed broken unit test after feedback changes

added more checks and updated tests

fixed broken bok choy test

Fixed pylint quality error

trying to fix pylint quality error
parent 614bcafb
......@@ -52,6 +52,7 @@ class CourseDetailsTestCase(CourseTestCase):
self.assertIsNone(details.intro_video, "intro_video somehow initialized" + str(details.intro_video))
self.assertIsNone(details.effort, "effort somehow initialized" + str(details.effort))
self.assertIsNone(details.language, "language somehow initialized" + str(details.language))
self.assertIsNone(details.has_cert_config)
def test_encoder(self):
details = CourseDetails.fetch(self.course.id)
......@@ -1008,6 +1009,41 @@ class CourseMetadataEditingTest(CourseTestCase):
tab_list.append(self.notes_tab)
self.assertEqual(tab_list, course.tabs)
@override_settings(FEATURES={'CERTIFICATES_HTML_VIEW': True})
def test_web_view_certifcate_configuration_settings(self):
"""
Test that has_cert_config is updated based on cert_html_view_enabled setting.
"""
test_model = CourseMetadata.update_from_json(
self.course,
{
"cert_html_view_enabled": {"value": "true"}
},
user=self.user
)
self.assertIn('cert_html_view_enabled', test_model)
url = get_url(self.course.id)
response = self.client.get_json(url)
course_detail_json = json.loads(response.content)
self.assertFalse(course_detail_json['has_cert_config'])
# Now add a certificate configuration
certificates = [
{
'id': 1,
'name': 'Certificate Config Name',
'course_title': 'Title override',
'org_logo_path': '/c4x/test/CSS101/asset/org_logo.png',
'signatories': [],
'is_active': True
}
]
self.course.certificates = {'certificates': certificates}
modulestore().update_item(self.course, self.user.id)
response = self.client.get_json(url)
course_detail_json = json.loads(response.content)
self.assertTrue(course_detail_json['has_cert_config'])
class CourseGraderUpdatesTest(CourseTestCase):
"""
......
......@@ -310,3 +310,22 @@ def reverse_usage_url(handler_name, usage_key, kwargs=None):
Creates the URL for handlers that use usage_keys as URL parameters.
"""
return reverse_url(handler_name, 'usage_key_string', usage_key, kwargs)
def has_active_web_certificate(course):
"""
Returns True if given course has active web certificate configuration.
If given course has no active web certificate configuration returns False.
Returns None If `CERTIFICATES_HTML_VIEW` is not enabled of course has not enabled
`cert_html_view_enabled` settings.
"""
cert_config = None
if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False) and course.cert_html_view_enabled:
cert_config = False
certificates = getattr(course, 'certificates', {})
configurations = certificates.get('certificates', [])
for config in configurations:
if config.get('is_active'):
cert_config = True
break
return cert_config
......@@ -8,7 +8,7 @@ from django.conf import settings
from opaque_keys.edx.locations import Location
from xmodule.modulestore.exceptions import ItemNotFoundError
from contentstore.utils import course_image_url
from contentstore.utils import course_image_url, has_active_web_certificate
from models.settings import course_grading
from xmodule.fields import Date
from xmodule.modulestore.django import modulestore
......@@ -52,7 +52,8 @@ class CourseDetails(object):
self.entrance_exam_minimum_score_pct = settings.FEATURES.get(
'ENTRANCE_EXAM_MIN_SCORE_PCT',
'50'
) # minimum passing score for entrance exam content module/tree
) # minimum passing score for entrance exam content module/tree,
self.has_cert_config = None # course has active certificate configuration
@classmethod
def _fetch_about_attribute(cls, course_key, attribute):
......@@ -84,6 +85,7 @@ class CourseDetails(object):
course_details.language = descriptor.language
# Default course license is "All Rights Reserved"
course_details.license = getattr(descriptor, "license", "all-rights-reserved")
course_details.has_cert_config = has_active_web_certificate(descriptor)
for attribute in ABOUT_ATTRIBUTES:
value = cls._fetch_about_attribute(course_key, attribute)
......
define(["backbone", "underscore", "gettext", "js/models/validation_helpers"],
function(Backbone, _, gettext, ValidationHelpers) {
define(["backbone", "underscore", "gettext", "js/models/validation_helpers", "js/utils/date_utils"],
function(Backbone, _, gettext, ValidationHelpers, DateUtils) {
var CourseDetails = Backbone.Model.extend({
defaults: {
......@@ -28,14 +28,21 @@ var CourseDetails = Backbone.Model.extend({
// Returns either nothing (no return call) so that validate works or an object of {field: errorstring} pairs
// A bit funny in that the video key validation is asynchronous; so, it won't stop the validation.
var errors = {};
newattrs = DateUtils.convertDateStringsToObjects(
newattrs, ["start_date", "end_date", "enrollment_start", "enrollment_end"]
);
if (newattrs.start_date === null) {
errors.start_date = gettext("The course must have an assigned start date.");
}
if (this.hasChanged("start_date") && this.get("has_cert_config") === false){
errors.start_date = gettext("The course must have at least one active certificate configuration before it can be started.");
}
if (newattrs.start_date && newattrs.end_date && newattrs.start_date >= newattrs.end_date) {
errors.end_date = gettext("The course end date cannot be before the course start date.");
errors.end_date = gettext("The course end date must be later than the course start date.");
}
if (newattrs.start_date && newattrs.enrollment_start && newattrs.start_date < newattrs.enrollment_start) {
errors.enrollment_start = gettext("The course start date cannot be before the enrollment start date.");
errors.enrollment_start = gettext("The course start date must be later than the enrollment start date.");
}
if (newattrs.enrollment_start && newattrs.enrollment_end && newattrs.enrollment_start >= newattrs.enrollment_end) {
errors.enrollment_end = gettext("The enrollment start date cannot be after the enrollment end date.");
......
......@@ -31,7 +31,8 @@ define([
entrance_exam_enabled : '',
entrance_exam_minimum_score_pct: '50',
license: null,
language: ''
language: '',
has_cert_config: false
},
mockSettingsPage = readFixtures('mock/mock-settings-page.underscore');
......@@ -71,6 +72,13 @@ define([
);
});
it('Changing course start date without active certificate configuration should result in error', function () {
this.view.$el.find('#course-start-date')
.val('10/06/2014')
.trigger('change');
expect(this.view.$el.find('span.message-error').text()).toContain("course must have at least one active certificate configuration");
});
it('Selecting a course in pre-requisite drop down should save it as part of course details', function () {
var pre_requisite_courses = ['test/CSS101/2012_T1'];
var requests = AjaxHelpers.requests(this),
......
......@@ -35,9 +35,29 @@ define(["jquery", "date", "jquery.ui", "jquery.timepicker"], function($, date) {
);
};
var parseDateFromString = function(stringDate){
if (stringDate && typeof stringDate === "string"){
return new Date(stringDate);
}
else {
return stringDate;
}
};
var convertDateStringsToObjects = function(obj, dateFields){
for (var i = 0; i < dateFields.length; i++){
if (obj[dateFields[i]]){
obj[dateFields[i]] = parseDateFromString(obj[dateFields[i]]);
}
}
return obj;
};
return {
getDate: getDate,
setDate: setDate,
renderDate: renderDate
renderDate: renderDate,
convertDateStringsToObjects: convertDateStringsToObjects,
parseDateFromString: parseDateFromString
};
});
......@@ -47,9 +47,14 @@ class CertificateDisplayTest(ModuleStoreTestCase):
@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True})
def test_display_download_certificate_button(self, enrollment_mode):
"""
Tests if CERTIFICATES_HTML_VIEW is True and there is no active certificate configuration available
Tests if CERTIFICATES_HTML_VIEW is True
and course has enabled web certificates via cert_html_view_enabled setting
and no active certificate configuration available
then any of the Download certificate button should not be visible.
"""
self.course.cert_html_view_enabled = True
self.course.save()
self.store.update_item(self.course, self.user.id)
self._create_certificate(enrollment_mode)
self._check_can_not_download_certificate()
......@@ -75,6 +80,7 @@ class CertificateDisplayTest(ModuleStoreTestCase):
}
]
self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save() # pylint: disable=no-member
self.store.update_item(self.course, self.user.id)
......
......@@ -59,7 +59,11 @@ from student.forms import AccountCreationForm, PasswordResetFormNoActive
from verify_student.models import SoftwareSecurePhotoVerification # pylint: disable=import-error
from certificates.models import CertificateStatuses, certificate_status_for_student
from certificates.api import get_certificate_url, get_active_web_certificate # pylint: disable=import-error
from certificates.api import ( # pylint: disable=import-error
get_certificate_url,
get_active_web_certificate,
has_html_certificates_enabled,
)
from dark_lang.models import DarkLangConfig
from xmodule.modulestore.django import modulestore
......@@ -305,7 +309,7 @@ def _cert_info(user, course, cert_status, course_mode):
if status == 'ready':
# showing the certificate web view button if certificate is ready state and feature flags are enabled.
if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
if has_html_certificates_enabled(course.id, course):
if get_active_web_certificate(course) is not None:
certificate_url = get_certificate_url(
user_id=user.id,
......
......@@ -712,6 +712,12 @@ class CourseFields(object):
scope=Scope.settings,
default=""
)
cert_html_view_enabled = Boolean(
display_name=_("Certificate Web/HTML View Enabled"),
help=_("If true, certificate Web/HTML views are enabled for the course."),
scope=Scope.settings,
default=False,
)
cert_html_view_overrides = Dict(
# Translators: This field is the container for course-specific certifcate configuration values
display_name=_("Certificate Web/HTML View Overrides"),
......
......@@ -201,4 +201,5 @@ class AdvancedSettingsPage(CoursePage):
'social_sharing_url',
'teams_configuration',
'video_bumper',
'cert_html_view_enabled',
]
......@@ -36,8 +36,11 @@ class CertificateWebViewTest(EventsTestMixin, UniqueCourseTest):
self.course_info["display_name"],
settings=course_settings
)
self.course_fixture.add_advanced_settings({
"cert_html_view_enabled": {"value": "true"}
})
self.course_fixture.install()
self.user_id = "99" # we have createad a user with this id in fixture
self.user_id = "99" # we have created a user with this id in fixture
self.cert_fixture = CertificateConfigFixture(self.course_id, test_certificate_config)
# Load certificate web view page for use by the tests
......
......@@ -10,6 +10,7 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from eventtracking import tracker
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.django import modulestore
......@@ -211,10 +212,14 @@ def has_html_certificates_enabled(course_key, course=None):
It determines if course has html certificates enabled
"""
html_certificates_enabled = False
if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
try:
if not isinstance(course_key, CourseKey):
course_key = CourseKey.from_string(course_key)
course = course if course else modulestore().get_course(course_key, depth=0)
if get_active_web_certificate(course) is not None:
if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False) and course.cert_html_view_enabled:
html_certificates_enabled = True
except: # pylint: disable=bare-except
pass
return html_certificates_enabled
......
......@@ -214,6 +214,7 @@ class GenerateUserCertificatesTest(EventTestMixin, ModuleStoreTestCase):
}
]
self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save()
self.store.update_item(self.course, self.user.id)
......
......@@ -267,6 +267,7 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase):
]
self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save()
self.store.update_item(self.course, self.user.id)
......@@ -422,6 +423,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
]
self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save()
self.store.update_item(self.course, self.user.id)
......@@ -483,6 +485,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
}
]
self.course.certificates = {'certificates': test_certificates}
self.course.cert_html_view_enabled = True
self.course.save()
self.store.update_item(self.course, self.user.id)
response = self.client.get(test_url)
......@@ -506,6 +509,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
}
]
self.course.certificates = {'certificates': test_certificates}
self.course.cert_html_view_enabled = True
self.course.save()
self.store.update_item(self.course, self.user.id)
response = self.client.get(test_url)
......@@ -646,6 +650,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
verify_uuid=self.cert.verify_uuid
)
test_url = '{}?evidence_visit=1'.format(cert_url)
self._add_course_certificates(count=1, signatory_count=2)
self.recreate_tracker()
assertion = BadgeAssertion(
user=self.user, course_id=self.course_id, mode='honor',
......
......@@ -21,7 +21,8 @@ from certificates.api import (
get_active_web_certificate,
get_certificate_url,
generate_user_certificates,
emit_certificate_event
emit_certificate_event,
has_html_certificates_enabled
)
from certificates.models import (
certificate_status_for_student,
......@@ -504,7 +505,7 @@ def render_html_view(request, user_id, course_id):
invalid_template_path = 'certificates/invalid.html'
# Kick the user back to the "Invalid" screen if the feature is disabled
if not settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
if not has_html_certificates_enabled(course_id):
return render_to_response(invalid_template_path, context)
# Load the core building blocks for the view context
......
......@@ -853,6 +853,7 @@ class ProgressPageTests(ModuleStoreTestCase):
]
self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save()
self.store.update_item(self.course, self.user.id)
......
......@@ -1081,7 +1081,7 @@ def _progress(request, course_key, student_id):
if show_generate_cert_btn:
context.update(certs_api.certificate_downloadable_status(student, course_key))
# showing the certificate web view button if feature flags are enabled.
if settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
if certs_api.has_html_certificates_enabled(course_key, course):
if certs_api.get_active_web_certificate(course) is not None:
context.update({
'show_cert_web_view': True,
......
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