chore: replace integrity signature flag with django setting

This commit is contained in:
Bianca Severino
2022-02-14 13:38:59 -05:00
parent aa31f3b255
commit af81b3a609
28 changed files with 165 additions and 238 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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):

View File

@@ -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}, '

View File

@@ -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

View File

@@ -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()

View File

@@ -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:

View File

@@ -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

View File

@@ -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)})

View File

@@ -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')

View File

@@ -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(

View File

@@ -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