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
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user