chore: replace integrity signature flag with django setting
This commit is contained in:
@@ -7,6 +7,7 @@ cannot be generated, a message is logged and no further action is taken.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from django.conf import settings
|
||||
|
||||
from common.djangoapps.course_modes import api as modes_api
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
@@ -22,7 +23,6 @@ from lms.djangoapps.certificates.utils import has_html_certificates_enabled
|
||||
from lms.djangoapps.grades.api import CourseGradeFactory
|
||||
from lms.djangoapps.instructor.access import is_beta_tester
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
from openedx.core.djangoapps.agreements.toggles import is_integrity_signature_enabled
|
||||
from openedx.core.djangoapps.content.course_overviews.api import get_course_overview_or_none
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -179,7 +179,7 @@ def _can_generate_certificate_common(user, course_key, enrollment_mode):
|
||||
|
||||
# If the IDV check fails we then check if the course-run requires ID verification. Honor and Professional-No-ID
|
||||
# modes do not require IDV for certificate generation.
|
||||
if _required_verification_missing(course_key, user):
|
||||
if _required_verification_missing(user):
|
||||
if enrollment_mode not in CourseMode.NON_VERIFIED_MODES:
|
||||
log.info(f'{user.id} does not have a verified id. Certificate cannot be generated for {course_key}.')
|
||||
return False
|
||||
@@ -232,7 +232,7 @@ def _set_regular_cert_status(user, course_key, enrollment_mode, course_grade):
|
||||
if status is not None:
|
||||
return status
|
||||
|
||||
if not _required_verification_missing(course_key, user) \
|
||||
if not _required_verification_missing(user) \
|
||||
and not _is_passing_grade(course_grade) \
|
||||
and cert is not None:
|
||||
if cert.status != CertificateStatuses.notpassing:
|
||||
@@ -255,8 +255,8 @@ def _get_cert_status_common(user, course_key, enrollment_mode, course_grade, cer
|
||||
cert.invalidate(mode=enrollment_mode, source='certificate_generation')
|
||||
return CertificateStatuses.unavailable
|
||||
|
||||
if _required_verification_missing(course_key, user) and _has_passing_grade_or_is_allowlisted(user, course_key,
|
||||
course_grade):
|
||||
if _required_verification_missing(user) and _has_passing_grade_or_is_allowlisted(user, course_key,
|
||||
course_grade):
|
||||
if cert is None:
|
||||
_generate_certificate_task(user=user, course_key=course_key, enrollment_mode=enrollment_mode,
|
||||
course_grade=course_grade, status=CertificateStatuses.unverified,
|
||||
@@ -416,8 +416,8 @@ def _is_mode_now_eligible(enrollment_mode, cert):
|
||||
return False
|
||||
|
||||
|
||||
def _required_verification_missing(course_key, user):
|
||||
def _required_verification_missing(user):
|
||||
"""
|
||||
Return true if IDV is required for this course and the user does not have it
|
||||
"""
|
||||
return not is_integrity_signature_enabled(course_key) and not IDVerificationService.user_is_verified(user)
|
||||
return not settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE') and not IDVerificationService.user_is_verified(user)
|
||||
|
||||
@@ -13,7 +13,6 @@ from opaque_keys.edx.keys import CourseKey
|
||||
from lms.djangoapps.certificates.data import CertificateStatuses
|
||||
from lms.djangoapps.certificates.generation_handler import generate_certificate_task
|
||||
from lms.djangoapps.certificates.models import GeneratedCertificate
|
||||
from openedx.core.djangoapps.agreements.toggles import is_integrity_signature_enabled
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
User = get_user_model()
|
||||
@@ -106,10 +105,6 @@ def _handle_course(course_key, batch_size, sleep_seconds, count):
|
||||
Returns how many certs were originally unverified and regenerated.
|
||||
"""
|
||||
|
||||
if not is_integrity_signature_enabled(course_key):
|
||||
log.warning(f'Skipping {course_key} which does not have honor code enabled')
|
||||
return 0
|
||||
|
||||
certs = GeneratedCertificate.objects.filter(
|
||||
course_id=course_key,
|
||||
status=CertificateStatuses.unverified,
|
||||
@@ -117,7 +112,7 @@ def _handle_course(course_key, batch_size, sleep_seconds, count):
|
||||
|
||||
log.info(f'Regenerating {len(certs)} unverified certificates for {course_key}')
|
||||
|
||||
return _regenerate_certs(certs, batch_size, sleep_seconds, count, False)
|
||||
return _regenerate_certs(certs, batch_size, sleep_seconds, count)
|
||||
|
||||
|
||||
def _handle_all_courses(batch_size, sleep_seconds, excluded_keys):
|
||||
@@ -130,24 +125,18 @@ def _handle_all_courses(batch_size, sleep_seconds, excluded_keys):
|
||||
.exclude(course_id__in=excluded_keys)
|
||||
log.info(f'Regenerating {len(certs)} unverified certificates in all courses except for excluded keys')
|
||||
|
||||
return _regenerate_certs(certs, batch_size, sleep_seconds, 0, True)
|
||||
return _regenerate_certs(certs, batch_size, sleep_seconds, 0)
|
||||
|
||||
|
||||
def _regenerate_certs(certs, batch_size, sleep_seconds, count, check_integrity_signature_flag):
|
||||
def _regenerate_certs(certs, batch_size, sleep_seconds, count):
|
||||
"""
|
||||
Triggers generate certificate task for a given set of certificates
|
||||
"""
|
||||
for cert in certs:
|
||||
# not ideal to replicate this check, but we have to in the case that we are regenerating certs for all courses.
|
||||
if check_integrity_signature_flag and not is_integrity_signature_enabled(cert.course_id):
|
||||
log.warning(
|
||||
f'Skipping regenerating cert_id={cert.id} because {cert.course_id} does not have honor code enabled'
|
||||
)
|
||||
else:
|
||||
user = User.objects.get(id=cert.user_id)
|
||||
generate_certificate_task(user, cert.course_id, generation_mode='batch', delay_seconds=0)
|
||||
count += 1
|
||||
if count % batch_size == 0:
|
||||
log.info(f'Regenerated {count} unverified certificates. Sleeping for {sleep_seconds} seconds.')
|
||||
time.sleep(sleep_seconds)
|
||||
user = User.objects.get(id=cert.user_id)
|
||||
generate_certificate_task(user, cert.course_id, generation_mode='batch', delay_seconds=0)
|
||||
count += 1
|
||||
if count % batch_size == 0:
|
||||
log.info(f'Regenerated {count} unverified certificates. Sleeping for {sleep_seconds} seconds.')
|
||||
time.sleep(sleep_seconds)
|
||||
return count
|
||||
|
||||
@@ -5,6 +5,7 @@ Tests for the cert_generation command
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.core.management import CommandError, call_command
|
||||
|
||||
from common.djangoapps.course_modes.models import CourseMode
|
||||
@@ -15,19 +16,15 @@ from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFact
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
|
||||
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
|
||||
|
||||
COMMAND_INTEGRITY_ENABLED = \
|
||||
'lms.djangoapps.certificates.management.commands.regenerate_noidv_cert.is_integrity_signature_enabled'
|
||||
INTEGRITY_ENABLED_METHOD = 'lms.djangoapps.certificates.generation_handler.is_integrity_signature_enabled'
|
||||
ID_VERIFIED_METHOD = 'lms.djangoapps.verify_student.services.IDVerificationService.user_is_verified'
|
||||
PASSING_GRADE_METHOD = 'lms.djangoapps.certificates.generation_handler._is_passing_grade'
|
||||
WEB_CERTS_METHOD = 'lms.djangoapps.certificates.generation_handler.has_html_certificates_enabled'
|
||||
|
||||
|
||||
# base setup is unverified users, honor code (integrity) turned on wherever imports it
|
||||
# base setup is unverified users, integrity signature turned on,
|
||||
# and normal passing grade certificates for convenience
|
||||
@mock.patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=True)
|
||||
@mock.patch(ID_VERIFIED_METHOD, mock.Mock(return_value=False))
|
||||
@mock.patch(INTEGRITY_ENABLED_METHOD, mock.Mock(return_value=True))
|
||||
@mock.patch(COMMAND_INTEGRITY_ENABLED, mock.Mock(return_value=True))
|
||||
@mock.patch(PASSING_GRADE_METHOD, mock.Mock(return_value=True))
|
||||
@mock.patch(WEB_CERTS_METHOD, mock.Mock(return_value=True))
|
||||
class RegenerateNoIDVCertTests(ModuleStoreTestCase):
|
||||
@@ -104,31 +101,6 @@ class RegenerateNoIDVCertTests(ModuleStoreTestCase):
|
||||
regenerated = call_command("regenerate_noidv_cert", "-c", course_run_key)
|
||||
self.assertEqual('0', regenerated)
|
||||
|
||||
def test_regeneration_honor_off(self):
|
||||
"""
|
||||
If a course does not have the honor code enabled, no point regenerating
|
||||
"""
|
||||
course_run = CourseFactory()
|
||||
course_run_key = course_run.id
|
||||
|
||||
user = UserFactory()
|
||||
CourseEnrollmentFactory(
|
||||
user=user,
|
||||
course_id=course_run_key,
|
||||
is_active=True,
|
||||
mode=CourseMode.VERIFIED,
|
||||
)
|
||||
GeneratedCertificateFactory(
|
||||
user=user,
|
||||
course_id=course_run_key,
|
||||
mode=GeneratedCertificate.MODES.verified,
|
||||
status=CertificateStatuses.unverified
|
||||
)
|
||||
|
||||
with mock.patch(COMMAND_INTEGRITY_ENABLED, mock.Mock(return_value=False)):
|
||||
regenerated = call_command("regenerate_noidv_cert", "-c", course_run_key)
|
||||
self.assertEqual('0', regenerated)
|
||||
|
||||
def _multisetup(self):
|
||||
"""
|
||||
setup certs across two course runs
|
||||
|
||||
@@ -38,13 +38,11 @@ BETA_TESTER_METHOD = 'lms.djangoapps.certificates.generation_handler.is_beta_tes
|
||||
COURSE_OVERVIEW_METHOD = 'lms.djangoapps.certificates.generation_handler.get_course_overview_or_none'
|
||||
CCX_COURSE_METHOD = 'lms.djangoapps.certificates.generation_handler._is_ccx_course'
|
||||
GET_GRADE_METHOD = 'lms.djangoapps.certificates.generation_handler._get_course_grade'
|
||||
INTEGRITY_ENABLED_METHOD = 'lms.djangoapps.certificates.generation_handler.is_integrity_signature_enabled'
|
||||
ID_VERIFIED_METHOD = 'lms.djangoapps.verify_student.services.IDVerificationService.user_is_verified'
|
||||
PASSING_GRADE_METHOD = 'lms.djangoapps.certificates.generation_handler._is_passing_grade'
|
||||
WEB_CERTS_METHOD = 'lms.djangoapps.certificates.generation_handler.has_html_certificates_enabled'
|
||||
|
||||
|
||||
@mock.patch(INTEGRITY_ENABLED_METHOD, mock.Mock(return_value=False))
|
||||
@mock.patch(ID_VERIFIED_METHOD, mock.Mock(return_value=True))
|
||||
@mock.patch(WEB_CERTS_METHOD, mock.Mock(return_value=True))
|
||||
@ddt.ddt
|
||||
@@ -204,15 +202,15 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
assert generate_certificate_task(self.user, self.course_run_key)
|
||||
|
||||
@ddt.data(False, True)
|
||||
def test_can_generate_not_verified(self, idv_retired):
|
||||
def test_can_generate_not_verified(self, enable_integrity_signature):
|
||||
"""
|
||||
Test handling when the user's id is not verified
|
||||
"""
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False), \
|
||||
mock.patch(INTEGRITY_ENABLED_METHOD, return_value=idv_retired):
|
||||
self.assertEqual(idv_retired,
|
||||
mock.patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=enable_integrity_signature):
|
||||
self.assertEqual(enable_integrity_signature,
|
||||
_can_generate_allowlist_certificate(self.user, self.course_run_key, self.enrollment_mode))
|
||||
self.assertIsNot(idv_retired,
|
||||
self.assertIsNot(enable_integrity_signature,
|
||||
_set_allowlist_cert_status(
|
||||
self.user, self.course_run_key,
|
||||
self.enrollment_mode, self.grade) == CertificateStatuses.unverified)
|
||||
@@ -358,7 +356,7 @@ class AllowlistTests(ModuleStoreTestCase):
|
||||
assert not _can_generate_allowlist_certificate(self.user, course_run_key, enrollment_mode)
|
||||
|
||||
|
||||
@mock.patch(INTEGRITY_ENABLED_METHOD, mock.Mock(return_value=False))
|
||||
@mock.patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=False)
|
||||
@mock.patch(ID_VERIFIED_METHOD, mock.Mock(return_value=True))
|
||||
@mock.patch(CCX_COURSE_METHOD, mock.Mock(return_value=False))
|
||||
@mock.patch(PASSING_GRADE_METHOD, mock.Mock(return_value=True))
|
||||
@@ -516,7 +514,7 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
assert _can_generate_certificate_for_status(None, None, None)
|
||||
|
||||
@ddt.data(False, True)
|
||||
def test_can_generate_not_verified_cert(self, idv_retired):
|
||||
def test_can_generate_not_verified_cert(self, enable_integrity_signature):
|
||||
"""
|
||||
Test handling when the user's id is not verified and they have a cert
|
||||
"""
|
||||
@@ -535,14 +533,16 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False), \
|
||||
mock.patch(INTEGRITY_ENABLED_METHOD, return_value=idv_retired):
|
||||
self.assertEqual(idv_retired, _can_generate_regular_certificate(u, self.course_run_key,
|
||||
self.enrollment_mode, self.grade))
|
||||
self.assertIsNot(idv_retired, _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode,
|
||||
self.grade) == CertificateStatuses.unverified)
|
||||
mock.patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=enable_integrity_signature):
|
||||
self.assertEqual(
|
||||
enable_integrity_signature,
|
||||
_can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
)
|
||||
regular_cert_status = _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
self.assertIsNot(enable_integrity_signature, regular_cert_status == CertificateStatuses.unverified)
|
||||
|
||||
@ddt.data(False, True)
|
||||
def test_can_generate_not_verified_no_cert(self, idv_retired):
|
||||
def test_can_generate_not_verified_no_cert(self, enable_integrity_signature):
|
||||
"""
|
||||
Test handling when the user's id is not verified and they don't have a cert
|
||||
"""
|
||||
@@ -555,14 +555,16 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False), \
|
||||
mock.patch(INTEGRITY_ENABLED_METHOD, return_value=idv_retired):
|
||||
self.assertEqual(idv_retired, _can_generate_regular_certificate(u, self.course_run_key,
|
||||
self.enrollment_mode, self.grade))
|
||||
self.assertIsNot(idv_retired, _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode,
|
||||
self.grade) == CertificateStatuses.unverified)
|
||||
mock.patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=enable_integrity_signature):
|
||||
self.assertEqual(
|
||||
enable_integrity_signature,
|
||||
_can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
)
|
||||
regular_cert_status = _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
self.assertIsNot(enable_integrity_signature, regular_cert_status == CertificateStatuses.unverified)
|
||||
|
||||
@ddt.data(False, True)
|
||||
def test_can_generate_not_verified_not_passing(self, idv_retired):
|
||||
def test_can_generate_not_verified_not_passing(self, enable_integrity_signature):
|
||||
"""
|
||||
Test handling when the user's id is not verified and the user is not passing
|
||||
"""
|
||||
@@ -581,17 +583,17 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
)
|
||||
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False), \
|
||||
mock.patch(INTEGRITY_ENABLED_METHOD, return_value=idv_retired), \
|
||||
mock.patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=enable_integrity_signature), \
|
||||
mock.patch(PASSING_GRADE_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
if idv_retired:
|
||||
if enable_integrity_signature:
|
||||
assert _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode, self.grade) \
|
||||
== CertificateStatuses.notpassing
|
||||
else:
|
||||
assert _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode, self.grade) is None
|
||||
|
||||
@ddt.data(False, True)
|
||||
def test_can_generate_not_verified_not_passing_allowlist(self, idv_retired):
|
||||
def test_can_generate_not_verified_not_passing_allowlist(self, enable_integrity_signature):
|
||||
"""
|
||||
Test handling when the user's id is not verified and the user is not passing but is on the allowlist
|
||||
"""
|
||||
@@ -611,10 +613,10 @@ class CertificateTests(ModuleStoreTestCase):
|
||||
CertificateAllowlistFactory(course_id=self.course_run_key, user=u)
|
||||
|
||||
with mock.patch(ID_VERIFIED_METHOD, return_value=False), \
|
||||
mock.patch(INTEGRITY_ENABLED_METHOD, return_value=idv_retired), \
|
||||
mock.patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=enable_integrity_signature), \
|
||||
mock.patch(PASSING_GRADE_METHOD, return_value=False):
|
||||
assert not _can_generate_regular_certificate(u, self.course_run_key, self.enrollment_mode, self.grade)
|
||||
if idv_retired:
|
||||
if enable_integrity_signature:
|
||||
assert _set_regular_cert_status(u, self.course_run_key, self.enrollment_mode,
|
||||
self.grade) == CertificateStatuses.notpassing
|
||||
else:
|
||||
|
||||
@@ -1643,34 +1643,33 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase)
|
||||
self.assertNotContains(response, verified_name)
|
||||
|
||||
@override_settings(FEATURES=FEATURES_WITH_CUSTOM_CERTS_ENABLED)
|
||||
@patch('lms.djangoapps.certificates.views.webview.is_integrity_signature_enabled')
|
||||
@ddt.data(
|
||||
True,
|
||||
False
|
||||
)
|
||||
def test_verified_certificate_description(self, integrity_signature_enabled, mock_integrity_signature):
|
||||
def test_verified_certificate_description(self, enable_integrity_signature):
|
||||
"""
|
||||
Test that for a verified cert, the correct language is used when the integrity signature feature is enabled.
|
||||
"""
|
||||
mock_integrity_signature.return_value = integrity_signature_enabled
|
||||
self._add_course_certificates(count=1, signatory_count=2, is_active=True)
|
||||
self._create_custom_template_with_verified_description()
|
||||
self.cert.mode = 'verified'
|
||||
self.cert.save()
|
||||
test_url = get_certificate_url(
|
||||
user_id=self.user.id,
|
||||
course_id=str(self.course.id),
|
||||
uuid=self.cert.verify_uuid
|
||||
)
|
||||
with patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=enable_integrity_signature):
|
||||
self._add_course_certificates(count=1, signatory_count=2, is_active=True)
|
||||
self._create_custom_template_with_verified_description()
|
||||
self.cert.mode = 'verified'
|
||||
self.cert.save()
|
||||
test_url = get_certificate_url(
|
||||
user_id=self.user.id,
|
||||
course_id=str(self.course.id),
|
||||
uuid=self.cert.verify_uuid
|
||||
)
|
||||
|
||||
response = self.client.get(test_url)
|
||||
assert response.status_code == 200
|
||||
if not integrity_signature_enabled:
|
||||
self.assertContains(response, 'identity of the learner has been checked and is valid')
|
||||
self.assertNotContains(response, 'Integrity signature enabled')
|
||||
else:
|
||||
self.assertNotContains(response, 'identity of the learner has been checked and is valid')
|
||||
self.assertContains(response, 'Integrity signature enabled')
|
||||
response = self.client.get(test_url)
|
||||
assert response.status_code == 200
|
||||
if not enable_integrity_signature:
|
||||
self.assertContains(response, 'identity of the learner has been checked and is valid')
|
||||
self.assertNotContains(response, 'Integrity signature enabled')
|
||||
else:
|
||||
self.assertNotContains(response, 'identity of the learner has been checked and is valid')
|
||||
self.assertContains(response, 'Integrity signature enabled')
|
||||
|
||||
|
||||
class CertificateEventTests(CommonCertificatesTestCase, EventTrackingTestCase):
|
||||
|
||||
@@ -49,7 +49,6 @@ from lms.djangoapps.certificates.utils import (
|
||||
get_certificate_url,
|
||||
get_preferred_certificate_name
|
||||
)
|
||||
from openedx.core.djangoapps.agreements.toggles import is_integrity_signature_enabled
|
||||
from openedx.core.djangoapps.catalog.api import get_course_run_details
|
||||
from openedx.core.djangoapps.content.course_overviews.api import get_course_overview_or_none
|
||||
from openedx.core.djangoapps.lang_pref.api import get_closest_released_language
|
||||
@@ -85,7 +84,7 @@ def get_certificate_description(mode, certificate_type, platform_name, course_ke
|
||||
"{platform_name} and has completed all of the required tasks for this course "
|
||||
"under its guidelines. ").format(cert_type=certificate_type,
|
||||
platform_name=platform_name)
|
||||
if not is_integrity_signature_enabled(course_key):
|
||||
if not settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE'):
|
||||
certificate_type_description += _("A {cert_type} certificate also indicates that the "
|
||||
"identity of the learner has been checked and "
|
||||
"is valid.").format(cert_type=certificate_type)
|
||||
@@ -253,7 +252,7 @@ def _update_course_context(request, context, course, platform_name):
|
||||
context['accomplishment_copy_course_name'] = accomplishment_copy_course_name
|
||||
course_number = course.display_coursenumber if course.display_coursenumber else course.number
|
||||
context['course_number'] = course_number
|
||||
context['is_integrity_signature_enabled_for_course'] = is_integrity_signature_enabled(course.location.course_key)
|
||||
context['is_integrity_signature_enabled_for_course'] = settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE')
|
||||
if context['organization_long_name']:
|
||||
# Translators: This text represents the description of course
|
||||
context['accomplishment_copy_course_description'] = _('a course of study offered by {partner_short_name}, '
|
||||
|
||||
@@ -24,7 +24,6 @@ from lms.djangoapps.certificates.api import get_active_web_certificate, can_show
|
||||
from lms.djangoapps.courseware.utils import verified_upgrade_deadline_link, can_show_verified_upgrade
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
from openedx.core.djangoapps.agreements.toggles import is_integrity_signature_enabled
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from openedx.features.course_duration_limits.access import get_user_course_expiration_date
|
||||
from openedx.features.course_experience import RELATIVE_DATES_FLAG, UPGRADE_DEADLINE_MESSAGE, CourseHomeMessages
|
||||
@@ -721,7 +720,7 @@ class VerificationDeadlineDate(DateSummary):
|
||||
is_active and
|
||||
mode == 'verified' and
|
||||
self.verification_status in ('expired', 'none', 'must_reverify') and
|
||||
not is_integrity_signature_enabled(self.course_id)
|
||||
not settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE')
|
||||
)
|
||||
|
||||
@lazy
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
import crum
|
||||
import ddt
|
||||
import waffle # lint-amnesty, pylint: disable=invalid-django-waffle-import
|
||||
from django.conf import settings
|
||||
from django.contrib.messages.middleware import MessageMiddleware
|
||||
from django.test import RequestFactory
|
||||
from django.urls import reverse
|
||||
@@ -40,7 +42,6 @@ from lms.djangoapps.courseware.models import (
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory
|
||||
from openedx.core.djangoapps.agreements.toggles import ENABLE_INTEGRITY_SIGNATURE
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
|
||||
from openedx.core.djangoapps.self_paced.models import SelfPacedConfiguration
|
||||
@@ -656,7 +657,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase):
|
||||
block = VerificationDeadlineDate(course, user)
|
||||
assert not block.is_allowed
|
||||
|
||||
@override_waffle_flag(ENABLE_INTEGRITY_SIGNATURE, active=True)
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_INTEGRITY_SIGNATURE': True})
|
||||
def test_verification_deadline_with_integrity_signature(self):
|
||||
course = create_course_run(days_till_start=-1)
|
||||
user = create_user()
|
||||
|
||||
@@ -99,7 +99,6 @@ from lms.djangoapps.grades.config.waffle import waffle_switch as grades_waffle_s
|
||||
from lms.djangoapps.instructor.access import allow_access
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
from openedx.core.djangoapps.agreements.toggles import ENABLE_INTEGRITY_SIGNATURE
|
||||
from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory
|
||||
from openedx.core.djangoapps.catalog.tests.factories import CourseRunFactory, ProgramFactory
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
@@ -1930,7 +1929,7 @@ class ProgressPageTests(ProgressPageBaseTests):
|
||||
assert response.title == 'Your certificate will be available soon!'
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_no_certs_generated_and_not_verified(self, waffle_override):
|
||||
def test_no_certs_generated_and_not_verified(self, enable_integrity_signature):
|
||||
"""
|
||||
Verify if the learner is not ID Verified, and the certs are not yet generated,
|
||||
but the learner is eligible, the get_cert_data would return cert status Unverified
|
||||
@@ -1939,14 +1938,14 @@ class ProgressPageTests(ProgressPageBaseTests):
|
||||
CertificateGenerationCourseSetting(
|
||||
course_key=self.course.id, self_generation_enabled=True
|
||||
).save()
|
||||
with override_waffle_flag(ENABLE_INTEGRITY_SIGNATURE, active=waffle_override):
|
||||
with patch.dict(settings.FEATURES, ENABLE_INTEGRITY_SIGNATURE=enable_integrity_signature):
|
||||
with patch(
|
||||
'lms.djangoapps.certificates.api.certificate_downloadable_status',
|
||||
return_value=self.mock_certificate_downloadable_status()
|
||||
):
|
||||
response = views.get_cert_data(self.user, self.course, CourseMode.VERIFIED, MagicMock(passed=True))
|
||||
|
||||
if waffle_override:
|
||||
if enable_integrity_signature:
|
||||
assert response.cert_status == 'requesting'
|
||||
assert response.title == 'Congratulations, you qualified for a certificate!'
|
||||
else:
|
||||
|
||||
@@ -106,7 +106,6 @@ from lms.djangoapps.instructor.enrollment import uses_shib
|
||||
from lms.djangoapps.instructor.views.api import require_global_staff
|
||||
from lms.djangoapps.survey import views as survey_views
|
||||
from lms.djangoapps.verify_student.services import IDVerificationService
|
||||
from openedx.core.djangoapps.agreements.toggles import is_integrity_signature_enabled
|
||||
from openedx.core.djangoapps.catalog.utils import get_programs, get_programs_with_type
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.credit.api import (
|
||||
@@ -1254,8 +1253,8 @@ def _downloadable_certificate_message(course, cert_downloadable_status): # lint
|
||||
return _downloadable_cert_data(download_url=cert_downloadable_status['download_url'])
|
||||
|
||||
|
||||
def _missing_required_verification(student, enrollment_mode, course_key):
|
||||
return not is_integrity_signature_enabled(course_key) and (
|
||||
def _missing_required_verification(student, enrollment_mode):
|
||||
return not settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE') and (
|
||||
enrollment_mode in CourseMode.VERIFIED_MODES and not IDVerificationService.user_is_verified(student)
|
||||
)
|
||||
|
||||
@@ -1278,7 +1277,7 @@ def _certificate_message(student, course, enrollment_mode): # lint-amnesty, pyl
|
||||
if cert_downloadable_status['is_downloadable']:
|
||||
return _downloadable_certificate_message(course, cert_downloadable_status)
|
||||
|
||||
if _missing_required_verification(student, enrollment_mode, course.id):
|
||||
if _missing_required_verification(student, enrollment_mode):
|
||||
return UNVERIFIED_CERT_DATA
|
||||
|
||||
return REQUESTING_CERT_DATA
|
||||
|
||||
@@ -2812,28 +2812,28 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
|
||||
True,
|
||||
False
|
||||
)
|
||||
@patch('lms.djangoapps.instructor.views.api.is_integrity_signature_enabled')
|
||||
@valid_problem_location
|
||||
def test_idv_retirement_student_features_report(self, toggle_value, mock_toggle):
|
||||
mock_toggle.return_value = toggle_value
|
||||
kwargs = {'course_id': str(self.course.id)}
|
||||
kwargs.update({'csv': '/csv'})
|
||||
url = reverse('get_students_features', kwargs=kwargs)
|
||||
success_status = 'The enrolled learner profile report is being created.'
|
||||
with patch('lms.djangoapps.instructor_task.api.submit_calculate_students_features_csv') as mock_task_endpoint:
|
||||
CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
|
||||
response = self.client.post(url, {})
|
||||
self.assertContains(response, success_status)
|
||||
def test_idv_retirement_student_features_report(self, enable_integrity_signature):
|
||||
with patch.dict(settings.FEATURES, {'ENABLE_INTEGRITY_SIGNATURE': enable_integrity_signature}):
|
||||
kwargs = {'course_id': str(self.course.id)}
|
||||
kwargs.update({'csv': '/csv'})
|
||||
url = reverse('get_students_features', kwargs=kwargs)
|
||||
success_status = 'The enrolled learner profile report is being created.'
|
||||
with patch('lms.djangoapps.instructor_task.api.submit_calculate_students_features_csv') \
|
||||
as mock_task_endpoint:
|
||||
CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
|
||||
response = self.client.post(url, {})
|
||||
self.assertContains(response, success_status)
|
||||
|
||||
# assert that if the integrity signature is enabled, the verification
|
||||
# status is not included as a query feature
|
||||
args = mock_task_endpoint.call_args.args
|
||||
self.assertEqual(len(args), 3)
|
||||
query_features = args[2]
|
||||
if not toggle_value:
|
||||
self.assertIn('verification_status', query_features)
|
||||
else:
|
||||
self.assertNotIn('verification_status', query_features)
|
||||
# assert that if the integrity signature is enabled, the verification
|
||||
# status is not included as a query feature
|
||||
args = mock_task_endpoint.call_args.args
|
||||
self.assertEqual(len(args), 3)
|
||||
query_features = args[2]
|
||||
if not enable_integrity_signature:
|
||||
self.assertIn('verification_status', query_features)
|
||||
else:
|
||||
self.assertNotIn('verification_status', query_features)
|
||||
|
||||
def test_get_ora2_responses_success(self):
|
||||
url = reverse('export_ora2_data', kwargs={'course_id': str(self.course.id)})
|
||||
|
||||
@@ -102,7 +102,6 @@ from lms.djangoapps.instructor_analytics import basic as instructor_analytics_ba
|
||||
from lms.djangoapps.instructor_task import api as task_api
|
||||
from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError
|
||||
from lms.djangoapps.instructor_task.models import ReportStore
|
||||
from openedx.core.djangoapps.agreements.toggles import is_integrity_signature_enabled
|
||||
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
|
||||
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort, is_course_cohorted
|
||||
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
|
||||
@@ -1461,7 +1460,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red
|
||||
query_features.append('team')
|
||||
query_features_names['team'] = _('Team')
|
||||
|
||||
if is_integrity_signature_enabled(course_key):
|
||||
if settings.FEATURES.get('ENABLE_INTEGRITY_SIGNATURE'):
|
||||
if 'verification_status' in query_features:
|
||||
query_features.remove('verification_status')
|
||||
query_features_names.pop('verification_status')
|
||||
|
||||
@@ -2080,7 +2080,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
|
||||
'failed': 0,
|
||||
'skipped': 2
|
||||
}
|
||||
with self.assertNumQueries(83):
|
||||
with self.assertNumQueries(76):
|
||||
self.assertCertificatesGenerated(task_input, expected_results)
|
||||
|
||||
@ddt.data(
|
||||
|
||||
@@ -948,6 +948,18 @@ FEATURES = {
|
||||
# .. toggle_target_removal_date: 2021-10-01
|
||||
# .. toggle_tickets: 'https://openedx.atlassian.net/browse/MICROBA-1405'
|
||||
'ENABLE_V2_CERT_DISPLAY_SETTINGS': False,
|
||||
|
||||
# .. toggle_name: FEATURES['ENABLE_INTEGRITY_SIGNATURE']
|
||||
# .. toggle_implementation: DjangoSetting
|
||||
# .. toggle_default: False
|
||||
# .. toggle_description: Whether to replace ID verification course/certificate requirement
|
||||
# with an in-course Honor Code agreement
|
||||
# (https://github.com/edx/edx-name-affirmation)
|
||||
# .. toggle_use_cases: open_edx
|
||||
# .. toggle_creation_date: 2022-02-15
|
||||
# .. toggle_target_removal_date: None
|
||||
# .. toggle_tickets: 'https://openedx.atlassian.net/browse/MST-1348'
|
||||
'ENABLE_INTEGRITY_SIGNATURE': False,
|
||||
}
|
||||
|
||||
# Specifies extra XBlock fields that should available when requested via the Course Blocks API
|
||||
|
||||
Reference in New Issue
Block a user