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): ...@@ -52,6 +52,7 @@ class CourseDetailsTestCase(CourseTestCase):
self.assertIsNone(details.intro_video, "intro_video somehow initialized" + str(details.intro_video)) 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.effort, "effort somehow initialized" + str(details.effort))
self.assertIsNone(details.language, "language somehow initialized" + str(details.language)) self.assertIsNone(details.language, "language somehow initialized" + str(details.language))
self.assertIsNone(details.has_cert_config)
def test_encoder(self): def test_encoder(self):
details = CourseDetails.fetch(self.course.id) details = CourseDetails.fetch(self.course.id)
...@@ -1008,6 +1009,41 @@ class CourseMetadataEditingTest(CourseTestCase): ...@@ -1008,6 +1009,41 @@ class CourseMetadataEditingTest(CourseTestCase):
tab_list.append(self.notes_tab) tab_list.append(self.notes_tab)
self.assertEqual(tab_list, course.tabs) 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): class CourseGraderUpdatesTest(CourseTestCase):
""" """
......
...@@ -310,3 +310,22 @@ def reverse_usage_url(handler_name, usage_key, kwargs=None): ...@@ -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. Creates the URL for handlers that use usage_keys as URL parameters.
""" """
return reverse_url(handler_name, 'usage_key_string', usage_key, kwargs) 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 ...@@ -8,7 +8,7 @@ from django.conf import settings
from opaque_keys.edx.locations import Location from opaque_keys.edx.locations import Location
from xmodule.modulestore.exceptions import ItemNotFoundError 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 models.settings import course_grading
from xmodule.fields import Date from xmodule.fields import Date
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
...@@ -52,7 +52,8 @@ class CourseDetails(object): ...@@ -52,7 +52,8 @@ class CourseDetails(object):
self.entrance_exam_minimum_score_pct = settings.FEATURES.get( self.entrance_exam_minimum_score_pct = settings.FEATURES.get(
'ENTRANCE_EXAM_MIN_SCORE_PCT', 'ENTRANCE_EXAM_MIN_SCORE_PCT',
'50' '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 @classmethod
def _fetch_about_attribute(cls, course_key, attribute): def _fetch_about_attribute(cls, course_key, attribute):
...@@ -84,6 +85,7 @@ class CourseDetails(object): ...@@ -84,6 +85,7 @@ class CourseDetails(object):
course_details.language = descriptor.language course_details.language = descriptor.language
# Default course license is "All Rights Reserved" # Default course license is "All Rights Reserved"
course_details.license = getattr(descriptor, "license", "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: for attribute in ABOUT_ATTRIBUTES:
value = cls._fetch_about_attribute(course_key, attribute) value = cls._fetch_about_attribute(course_key, attribute)
......
define(["backbone", "underscore", "gettext", "js/models/validation_helpers"], define(["backbone", "underscore", "gettext", "js/models/validation_helpers", "js/utils/date_utils"],
function(Backbone, _, gettext, ValidationHelpers) { function(Backbone, _, gettext, ValidationHelpers, DateUtils) {
var CourseDetails = Backbone.Model.extend({ var CourseDetails = Backbone.Model.extend({
defaults: { defaults: {
...@@ -28,14 +28,21 @@ var CourseDetails = Backbone.Model.extend({ ...@@ -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 // 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. // A bit funny in that the video key validation is asynchronous; so, it won't stop the validation.
var errors = {}; var errors = {};
newattrs = DateUtils.convertDateStringsToObjects(
newattrs, ["start_date", "end_date", "enrollment_start", "enrollment_end"]
);
if (newattrs.start_date === null) { if (newattrs.start_date === null) {
errors.start_date = gettext("The course must have an assigned start date."); 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) { 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) { 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) { 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."); errors.enrollment_end = gettext("The enrollment start date cannot be after the enrollment end date.");
......
...@@ -31,7 +31,8 @@ define([ ...@@ -31,7 +31,8 @@ define([
entrance_exam_enabled : '', entrance_exam_enabled : '',
entrance_exam_minimum_score_pct: '50', entrance_exam_minimum_score_pct: '50',
license: null, license: null,
language: '' language: '',
has_cert_config: false
}, },
mockSettingsPage = readFixtures('mock/mock-settings-page.underscore'); mockSettingsPage = readFixtures('mock/mock-settings-page.underscore');
...@@ -71,6 +72,13 @@ define([ ...@@ -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 () { 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 pre_requisite_courses = ['test/CSS101/2012_T1'];
var requests = AjaxHelpers.requests(this), var requests = AjaxHelpers.requests(this),
......
...@@ -35,9 +35,29 @@ define(["jquery", "date", "jquery.ui", "jquery.timepicker"], function($, date) { ...@@ -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 { return {
getDate: getDate, getDate: getDate,
setDate: setDate, setDate: setDate,
renderDate: renderDate renderDate: renderDate,
convertDateStringsToObjects: convertDateStringsToObjects,
parseDateFromString: parseDateFromString
}; };
}); });
...@@ -47,9 +47,14 @@ class CertificateDisplayTest(ModuleStoreTestCase): ...@@ -47,9 +47,14 @@ class CertificateDisplayTest(ModuleStoreTestCase):
@patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True}) @patch.dict('django.conf.settings.FEATURES', {'CERTIFICATES_HTML_VIEW': True})
def test_display_download_certificate_button(self, enrollment_mode): 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. 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._create_certificate(enrollment_mode)
self._check_can_not_download_certificate() self._check_can_not_download_certificate()
...@@ -75,6 +80,7 @@ class CertificateDisplayTest(ModuleStoreTestCase): ...@@ -75,6 +80,7 @@ class CertificateDisplayTest(ModuleStoreTestCase):
} }
] ]
self.course.certificates = {'certificates': certificates} self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save() # pylint: disable=no-member self.course.save() # pylint: disable=no-member
self.store.update_item(self.course, self.user.id) self.store.update_item(self.course, self.user.id)
......
...@@ -59,7 +59,11 @@ from student.forms import AccountCreationForm, PasswordResetFormNoActive ...@@ -59,7 +59,11 @@ from student.forms import AccountCreationForm, PasswordResetFormNoActive
from verify_student.models import SoftwareSecurePhotoVerification # pylint: disable=import-error from verify_student.models import SoftwareSecurePhotoVerification # pylint: disable=import-error
from certificates.models import CertificateStatuses, certificate_status_for_student 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 dark_lang.models import DarkLangConfig
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
...@@ -305,7 +309,7 @@ def _cert_info(user, course, cert_status, course_mode): ...@@ -305,7 +309,7 @@ def _cert_info(user, course, cert_status, course_mode):
if status == 'ready': if status == 'ready':
# showing the certificate web view button if certificate is ready state and feature flags are enabled. # 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: if get_active_web_certificate(course) is not None:
certificate_url = get_certificate_url( certificate_url = get_certificate_url(
user_id=user.id, user_id=user.id,
......
...@@ -712,6 +712,12 @@ class CourseFields(object): ...@@ -712,6 +712,12 @@ class CourseFields(object):
scope=Scope.settings, scope=Scope.settings,
default="" 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( cert_html_view_overrides = Dict(
# Translators: This field is the container for course-specific certifcate configuration values # Translators: This field is the container for course-specific certifcate configuration values
display_name=_("Certificate Web/HTML View Overrides"), display_name=_("Certificate Web/HTML View Overrides"),
......
...@@ -201,4 +201,5 @@ class AdvancedSettingsPage(CoursePage): ...@@ -201,4 +201,5 @@ class AdvancedSettingsPage(CoursePage):
'social_sharing_url', 'social_sharing_url',
'teams_configuration', 'teams_configuration',
'video_bumper', 'video_bumper',
'cert_html_view_enabled',
] ]
...@@ -36,8 +36,11 @@ class CertificateWebViewTest(EventsTestMixin, UniqueCourseTest): ...@@ -36,8 +36,11 @@ class CertificateWebViewTest(EventsTestMixin, UniqueCourseTest):
self.course_info["display_name"], self.course_info["display_name"],
settings=course_settings settings=course_settings
) )
self.course_fixture.add_advanced_settings({
"cert_html_view_enabled": {"value": "true"}
})
self.course_fixture.install() 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) self.cert_fixture = CertificateConfigFixture(self.course_id, test_certificate_config)
# Load certificate web view page for use by the tests # Load certificate web view page for use by the tests
......
...@@ -10,6 +10,7 @@ from django.conf import settings ...@@ -10,6 +10,7 @@ from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from eventtracking import tracker from eventtracking import tracker
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
...@@ -211,10 +212,14 @@ def has_html_certificates_enabled(course_key, course=None): ...@@ -211,10 +212,14 @@ def has_html_certificates_enabled(course_key, course=None):
It determines if course has html certificates enabled It determines if course has html certificates enabled
""" """
html_certificates_enabled = False 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) 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 html_certificates_enabled = True
except: # pylint: disable=bare-except
pass
return html_certificates_enabled return html_certificates_enabled
......
...@@ -214,6 +214,7 @@ class GenerateUserCertificatesTest(EventTestMixin, ModuleStoreTestCase): ...@@ -214,6 +214,7 @@ class GenerateUserCertificatesTest(EventTestMixin, ModuleStoreTestCase):
} }
] ]
self.course.certificates = {'certificates': certificates} self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save() self.course.save()
self.store.update_item(self.course, self.user.id) self.store.update_item(self.course, self.user.id)
......
...@@ -267,6 +267,7 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase): ...@@ -267,6 +267,7 @@ class MicrositeCertificatesViewsTests(ModuleStoreTestCase):
] ]
self.course.certificates = {'certificates': certificates} self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save() self.course.save()
self.store.update_item(self.course, self.user.id) self.store.update_item(self.course, self.user.id)
...@@ -422,6 +423,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): ...@@ -422,6 +423,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
] ]
self.course.certificates = {'certificates': certificates} self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save() self.course.save()
self.store.update_item(self.course, self.user.id) self.store.update_item(self.course, self.user.id)
...@@ -483,6 +485,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): ...@@ -483,6 +485,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
} }
] ]
self.course.certificates = {'certificates': test_certificates} self.course.certificates = {'certificates': test_certificates}
self.course.cert_html_view_enabled = True
self.course.save() self.course.save()
self.store.update_item(self.course, self.user.id) self.store.update_item(self.course, self.user.id)
response = self.client.get(test_url) response = self.client.get(test_url)
...@@ -506,6 +509,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): ...@@ -506,6 +509,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
} }
] ]
self.course.certificates = {'certificates': test_certificates} self.course.certificates = {'certificates': test_certificates}
self.course.cert_html_view_enabled = True
self.course.save() self.course.save()
self.store.update_item(self.course, self.user.id) self.store.update_item(self.course, self.user.id)
response = self.client.get(test_url) response = self.client.get(test_url)
...@@ -646,6 +650,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase): ...@@ -646,6 +650,7 @@ class CertificatesViewsTests(ModuleStoreTestCase, EventTrackingTestCase):
verify_uuid=self.cert.verify_uuid verify_uuid=self.cert.verify_uuid
) )
test_url = '{}?evidence_visit=1'.format(cert_url) test_url = '{}?evidence_visit=1'.format(cert_url)
self._add_course_certificates(count=1, signatory_count=2)
self.recreate_tracker() self.recreate_tracker()
assertion = BadgeAssertion( assertion = BadgeAssertion(
user=self.user, course_id=self.course_id, mode='honor', user=self.user, course_id=self.course_id, mode='honor',
......
...@@ -21,7 +21,8 @@ from certificates.api import ( ...@@ -21,7 +21,8 @@ from certificates.api import (
get_active_web_certificate, get_active_web_certificate,
get_certificate_url, get_certificate_url,
generate_user_certificates, generate_user_certificates,
emit_certificate_event emit_certificate_event,
has_html_certificates_enabled
) )
from certificates.models import ( from certificates.models import (
certificate_status_for_student, certificate_status_for_student,
...@@ -504,7 +505,7 @@ def render_html_view(request, user_id, course_id): ...@@ -504,7 +505,7 @@ def render_html_view(request, user_id, course_id):
invalid_template_path = 'certificates/invalid.html' invalid_template_path = 'certificates/invalid.html'
# Kick the user back to the "Invalid" screen if the feature is disabled # 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) return render_to_response(invalid_template_path, context)
# Load the core building blocks for the view context # Load the core building blocks for the view context
......
...@@ -853,6 +853,7 @@ class ProgressPageTests(ModuleStoreTestCase): ...@@ -853,6 +853,7 @@ class ProgressPageTests(ModuleStoreTestCase):
] ]
self.course.certificates = {'certificates': certificates} self.course.certificates = {'certificates': certificates}
self.course.cert_html_view_enabled = True
self.course.save() self.course.save()
self.store.update_item(self.course, self.user.id) self.store.update_item(self.course, self.user.id)
......
...@@ -1081,7 +1081,7 @@ def _progress(request, course_key, student_id): ...@@ -1081,7 +1081,7 @@ def _progress(request, course_key, student_id):
if show_generate_cert_btn: if show_generate_cert_btn:
context.update(certs_api.certificate_downloadable_status(student, course_key)) context.update(certs_api.certificate_downloadable_status(student, course_key))
# showing the certificate web view button if feature flags are enabled. # 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: if certs_api.get_active_web_certificate(course) is not None:
context.update({ context.update({
'show_cert_web_view': True, '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