MICROBA-918 Move methods to separate certificate generation from celery task to schedule certificate generation. Also standardizes imports. (#26410)

This commit is contained in:
Christie Rice
2021-02-08 10:01:04 -05:00
committed by GitHub
parent 930980d732
commit 6e5a9f5f6f
11 changed files with 294 additions and 179 deletions

View File

@@ -15,9 +15,13 @@ from django.db.models import Q
from eventtracking import tracker
from opaque_keys.edx.django.models import CourseKeyField
from organizations.api import get_course_organization_id
from xmodule.modulestore.django import modulestore
from lms.djangoapps.branding import api as branding_api
from lms.djangoapps.certificates.generation_handler import (
is_using_certificate_allowlist_and_is_on_allowlist as _is_using_certificate_allowlist_and_is_on_allowlist,
generate_user_certificates as _generate_user_certificates,
regenerate_user_certificates as _regenerate_user_certificates
)
from lms.djangoapps.certificates.models import (
CertificateGenerationConfiguration,
CertificateGenerationCourseSetting,
@@ -30,10 +34,8 @@ from lms.djangoapps.certificates.models import (
certificate_status_for_student
)
from lms.djangoapps.certificates.queue import XQueueCertInterface
from lms.djangoapps.certificates.utils import emit_certificate_event as _emit_certificate_event
from lms.djangoapps.certificates.utils import get_certificate_url as _get_certificate_url
from lms.djangoapps.certificates.utils import has_html_certificates_enabled as _has_html_certificates_enabled
from lms.djangoapps.instructor.access import list_with_level
from openedx.core.djangoapps.certificates.api import certificates_viewable_for_course
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
@@ -181,112 +183,12 @@ def get_recently_modified_certificates(course_keys=None, start_date=None, end_da
def generate_user_certificates(student, course_key, course=None, insecure=False, generation_mode='batch',
forced_grade=None):
"""
It will add the add-cert request into the xqueue.
A new record will be created to track the certificate
generation task. If an error occurs while adding the certificate
to the queue, the task will have status 'error'. It also emits
`edx.certificate.created` event for analytics.
Args:
student (User)
course_key (CourseKey)
Keyword Arguments:
course (Course): Optionally provide the course object; if not provided
it will be loaded.
insecure - (Boolean)
generation_mode - who has requested certificate generation. Its value should `batch`
in case of django command and `self` if student initiated the request.
forced_grade - a string indicating to replace grade parameter. if present grading
will be skipped.
"""
if not course:
course = modulestore().get_course(course_key, depth=0)
beta_testers_queryset = list_with_level(course, u'beta')
if beta_testers_queryset.filter(username=student.username):
message = u'Cancelling course certificate generation for user [{}] against course [{}], user is a Beta Tester.'
log.info(message.format(student.username, course_key))
return
xqueue = XQueueCertInterface()
if insecure:
xqueue.use_https = False
generate_pdf = not has_html_certificates_enabled(course)
cert = xqueue.add_cert(
student,
course_key,
course=course,
generate_pdf=generate_pdf,
forced_grade=forced_grade
)
message = u'Queued Certificate Generation task for {user} : {course}'
log.info(message.format(user=student.id, course=course_key))
# If cert_status is not present in certificate valid_statuses (for example unverified) then
# add_cert returns None and raises AttributeError while accessing cert attributes.
if cert is None:
return
if CertificateStatuses.is_passing_status(cert.status):
_emit_certificate_event('created', student, course_key, course, {
'user_id': student.id,
'course_id': six.text_type(course_key),
'certificate_id': cert.verify_uuid,
'enrollment_mode': cert.mode,
'generation_mode': generation_mode
})
return cert.status
return _generate_user_certificates(student, course_key, course, insecure, generation_mode, forced_grade)
def regenerate_user_certificates(student, course_key, course=None,
forced_grade=None, template_file=None, insecure=False):
"""
It will add the regen-cert request into the xqueue.
A new record will be created to track the certificate
generation task. If an error occurs while adding the certificate
to the queue, the task will have status 'error'.
Args:
student (User)
course_key (CourseKey)
Keyword Arguments:
course (Course): Optionally provide the course object; if not provided
it will be loaded.
grade_value - The grade string, such as "Distinction"
template_file - The template file used to render this certificate
insecure - (Boolean)
"""
xqueue = XQueueCertInterface()
if insecure:
xqueue.use_https = False
if not course:
course = modulestore().get_course(course_key, depth=0)
generate_pdf = not has_html_certificates_enabled(course)
log.info(
u"Started regenerating certificates for user %s in course %s with generate_pdf status: %s",
student.username, six.text_type(course_key), generate_pdf
)
return xqueue.regen_cert(
student,
course_key,
course=course,
forced_grade=forced_grade,
template_file=template_file,
generate_pdf=generate_pdf
)
return _regenerate_user_certificates(student, course_key, course, forced_grade, template_file, insecure)
def certificate_downloadable_status(student, course_key):
@@ -634,3 +536,12 @@ def get_certificate_footer_context():
data.update({'company_about_url': about})
return data
def is_using_certificate_allowlist_and_is_on_allowlist(user, course_key):
"""
Return True if both:
1) the course run is using the allowlist, and
2) if the user is on the allowlist for this course run
"""
return _is_using_certificate_allowlist_and_is_on_allowlist(user, course_key)

View File

@@ -6,7 +6,7 @@ existing cert if it does already exist).
For now, these methods deal primarily with allowlist certificates, and are part of the V2 certificates revamp.
These method should be called from tasks.
These methods should be called from tasks.
"""
import logging
@@ -18,8 +18,11 @@ from xmodule.modulestore.django import modulestore
from common.djangoapps.student.models import CourseEnrollment, UserProfile
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
from lms.djangoapps.certificates.queue import XQueueCertInterface
from lms.djangoapps.certificates.utils import emit_certificate_event
from lms.djangoapps.certificates.utils import has_html_certificates_enabled
from lms.djangoapps.grades.api import CourseGradeFactory
from lms.djangoapps.instructor.access import list_with_level
log = logging.getLogger(__name__)
@@ -86,3 +89,74 @@ def _generate_certificate(user, course_id):
created_msg=created_msg
))
return cert
def generate_user_certificates(student, course_key, course=None, insecure=False, generation_mode='batch',
forced_grade=None):
"""
It will add the add-cert request into the xqueue.
A new record will be created to track the certificate
generation task. If an error occurs while adding the certificate
to the queue, the task will have status 'error'. It also emits
`edx.certificate.created` event for analytics.
This method has not yet been updated (it predates the certificates revamp). If modifying this method,
see also generate_user_certificates() in generation_handler.py (which is very similar but is not called from a
celery task). In the future these methods will be unified.
Args:
student (User)
course_key (CourseKey)
Keyword Arguments:
course (Course): Optionally provide the course object; if not provided
it will be loaded.
insecure - (Boolean)
generation_mode - who has requested certificate generation. Its value should `batch`
in case of django command and `self` if student initiated the request.
forced_grade - a string indicating to replace grade parameter. if present grading
will be skipped.
"""
if not course:
course = modulestore().get_course(course_key, depth=0)
beta_testers_queryset = list_with_level(course, 'beta')
if beta_testers_queryset.filter(username=student.username):
message = 'Cancelling course certificate generation for user [{}] against course [{}], user is a Beta Tester.'
log.info(message.format(student.username, course_key))
return
xqueue = XQueueCertInterface()
if insecure:
xqueue.use_https = False
generate_pdf = not has_html_certificates_enabled(course)
cert = xqueue.add_cert(
student,
course_key,
course=course,
generate_pdf=generate_pdf,
forced_grade=forced_grade
)
message = 'Queued Certificate Generation task for {user} : {course}'
log.info(message.format(user=student.id, course=course_key))
# If cert_status is not present in certificate valid_statuses (for example unverified) then
# add_cert returns None and raises AttributeError while accessing cert attributes.
if cert is None:
return
if CertificateStatuses.is_passing_status(cert.status):
emit_certificate_event('created', student, course_key, course, {
'user_id': student.id,
'course_id': str(course_key),
'certificate_id': cert.verify_uuid,
'enrollment_mode': cert.mode,
'generation_mode': generation_mode
})
return cert.status

View File

@@ -4,13 +4,12 @@ Course certificate generation handler.
These methods check to see if a certificate can be generated (created if it does not already exist, or updated if it
exists but its state can be altered). If so, a celery task is launched to do the generation. If the certificate
cannot be generated, a message is logged and no further action is taken.
For now, these methods deal primarily with allowlist certificates, and are part of the V2 certificates revamp.
"""
import logging
from edx_toggles.toggles import LegacyWaffleFlagNamespace
from xmodule.modulestore.django import modulestore
from common.djangoapps.student.models import CourseEnrollment
from lms.djangoapps.certificates.models import (
@@ -19,7 +18,10 @@ from lms.djangoapps.certificates.models import (
CertificateWhitelist,
GeneratedCertificate
)
from lms.djangoapps.certificates.queue import XQueueCertInterface
from lms.djangoapps.certificates.tasks import CERTIFICATE_DELAY_SECONDS, generate_certificate
from lms.djangoapps.certificates.utils import emit_certificate_event, has_html_certificates_enabled
from lms.djangoapps.instructor.access import list_with_level
from lms.djangoapps.verify_student.services import IDVerificationService
from openedx.core.djangoapps.certificates.api import auto_certificate_generation_enabled
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
@@ -162,3 +164,119 @@ def _can_generate_allowlist_certificate_for_status(cert):
'generation'
.format(status=cert.status, user=cert.user.id, course=cert.course_id))
return True
def generate_user_certificates(student, course_key, course=None, insecure=False, generation_mode='batch',
forced_grade=None):
"""
It will add the add-cert request into the xqueue.
A new record will be created to track the certificate
generation task. If an error occurs while adding the certificate
to the queue, the task will have status 'error'. It also emits
`edx.certificate.created` event for analytics.
This method has not yet been updated (it predates the certificates revamp). If modifying this method,
see also generate_user_certificates() in generation.py (which is very similar but is called from a celery task).
In the future these methods will be unified.
Args:
student (User)
course_key (CourseKey)
Keyword Arguments:
course (Course): Optionally provide the course object; if not provided
it will be loaded.
insecure - (Boolean)
generation_mode - who has requested certificate generation. Its value should `batch`
in case of django command and `self` if student initiated the request.
forced_grade - a string indicating to replace grade parameter. if present grading
will be skipped.
"""
if not course:
course = modulestore().get_course(course_key, depth=0)
beta_testers_queryset = list_with_level(course, 'beta')
if beta_testers_queryset.filter(username=student.username):
message = 'Cancelling course certificate generation for user [{}] against course [{}], user is a Beta Tester.'
log.info(message.format(student.username, course_key))
return
xqueue = XQueueCertInterface()
if insecure:
xqueue.use_https = False
generate_pdf = not has_html_certificates_enabled(course)
cert = xqueue.add_cert(
student,
course_key,
course=course,
generate_pdf=generate_pdf,
forced_grade=forced_grade
)
message = 'Queued Certificate Generation task for {user} : {course}'
log.info(message.format(user=student.id, course=course_key))
# If cert_status is not present in certificate valid_statuses (for example unverified) then
# add_cert returns None and raises AttributeError while accessing cert attributes.
if cert is None:
return
if CertificateStatuses.is_passing_status(cert.status):
emit_certificate_event('created', student, course_key, course, {
'user_id': student.id,
'course_id': str(course_key),
'certificate_id': cert.verify_uuid,
'enrollment_mode': cert.mode,
'generation_mode': generation_mode
})
return cert.status
def regenerate_user_certificates(student, course_key, course=None,
forced_grade=None, template_file=None, insecure=False):
"""
Add the regen-cert request into the xqueue.
A new record will be created to track the certificate
generation task. If an error occurs while adding the certificate
to the queue, the task will have status 'error'.
This method has not yet been updated (it predates the certificates revamp).
Args:
student (User)
course_key (CourseKey)
Keyword Arguments:
course (Course): Optionally provide the course object; if not provided
it will be loaded.
grade_value - The grade string, such as "Distinction"
template_file - The template file used to render this certificate
insecure - (Boolean)
"""
xqueue = XQueueCertInterface()
if insecure:
xqueue.use_https = False
if not course:
course = modulestore().get_course(course_key, depth=0)
generate_pdf = not has_html_certificates_enabled(course)
log.info(
"Started regenerating certificates for user %s in course %s with generate_pdf status: %s",
student.username, str(course_key), generate_pdf
)
return xqueue.regen_cert(
student,
course_key,
course=course,
forced_grade=forced_grade,
template_file=template_file,
generate_pdf=generate_pdf
)

View File

@@ -25,11 +25,11 @@ from django.core.management.base import BaseCommand, CommandError
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from six import text_type
from lms.djangoapps.certificates import api as certs_api
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
from xmodule.modulestore.django import modulestore
from lms.djangoapps.certificates.api import generate_user_certificates
from lms.djangoapps.certificates.models import CertificateStatuses, GeneratedCertificate
LOGGER = logging.getLogger(__name__)
@@ -96,7 +96,7 @@ class Command(BaseCommand):
course = self._load_course_with_cache(course_key, course_cache)
if course is not None:
certs_api.generate_user_certificates(user, course_key, course=course)
generate_user_certificates(user, course_key, course=course)
resubmit_count += 1
LOGGER.info(
(

View File

@@ -10,8 +10,7 @@ from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imp
from edx_django_utils.monitoring import set_code_owner_attribute
from opaque_keys.edx.keys import CourseKey
from lms.djangoapps.certificates.api import generate_user_certificates
from lms.djangoapps.certificates.generation import generate_allowlist_certificate
from lms.djangoapps.certificates.generation import generate_allowlist_certificate, generate_user_certificates
from lms.djangoapps.verify_student.services import IDVerificationService
logger = getLogger(__name__)

View File

@@ -18,10 +18,29 @@ from freezegun import freeze_time
from mock import patch
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import CourseLocator
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from lms.djangoapps.certificates import api as certs_api
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.testing import EventTestMixin
from lms.djangoapps.certificates.api import (
cert_generation_enabled,
certificate_downloadable_status,
example_certificates_status,
generate_example_certificates,
generate_user_certificates,
get_certificate_for_user,
get_certificates_for_user,
get_certificates_for_user_by_course_keys,
get_certificate_footer_context,
get_certificate_header_context,
get_certificate_url,
is_certificate_invalid,
set_cert_generation_enabled
)
from lms.djangoapps.certificates.models import (
CertificateGenerationConfiguration,
CertificateStatuses,
@@ -34,11 +53,6 @@ from lms.djangoapps.certificates.tests.factories import CertificateInvalidationF
from lms.djangoapps.courseware.tests.factories import GlobalStaffFactory
from lms.djangoapps.grades.tests.utils import mock_passing_grade
from openedx.core.djangoapps.site_configuration.tests.test_util import with_site_configuration
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.util.testing import EventTestMixin
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy()
FEATURES_WITH_CERTS_ENABLED['CERTIFICATES_HTML_VIEW'] = True
@@ -112,7 +126,7 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes
mode='verified'
)
self.assertEqual(
certs_api.certificate_downloadable_status(self.student, self.course.id),
certificate_downloadable_status(self.student, self.course.id),
{
'is_downloadable': False,
'is_generating': True,
@@ -131,7 +145,7 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes
)
self.assertEqual(
certs_api.certificate_downloadable_status(self.student, self.course.id),
certificate_downloadable_status(self.student, self.course.id),
{
'is_downloadable': False,
'is_generating': True,
@@ -143,7 +157,7 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes
def test_without_cert(self):
self.assertEqual(
certs_api.certificate_downloadable_status(self.student_no_cert, self.course.id),
certificate_downloadable_status(self.student_no_cert, self.course.id),
{
'is_downloadable': False,
'is_generating': False,
@@ -167,7 +181,7 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes
)
self.assertEqual(
certs_api.certificate_downloadable_status(self.student, self.course.id),
certificate_downloadable_status(self.student, self.course.id),
{
'is_downloadable': True,
'is_generating': False,
@@ -190,11 +204,11 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes
CourseEnrollment.enroll(self.student, self.course.id, mode='honor')
self._setup_course_certificate()
with mock_passing_grade():
certs_api.generate_user_certificates(self.student, self.course.id)
generate_user_certificates(self.student, self.course.id)
cert_status = certificate_status_for_student(self.student, self.course.id)
self.assertEqual(
certs_api.certificate_downloadable_status(self.student, self.course.id),
certificate_downloadable_status(self.student, self.course.id),
{
'is_downloadable': True,
'is_generating': False,
@@ -224,9 +238,9 @@ class CertificateDownloadableStatusTests(WebCertificateTestMixin, ModuleStoreTes
CourseEnrollment.enroll(self.student, self.course.id, mode='honor')
self._setup_course_certificate()
with mock_passing_grade():
certs_api.generate_user_certificates(self.student, self.course.id)
generate_user_certificates(self.student, self.course.id)
downloadable_status = certs_api.certificate_downloadable_status(self.student, self.course.id)
downloadable_status = certificate_downloadable_status(self.student, self.course.id)
self.assertEqual(downloadable_status['is_downloadable'], cert_downloadable_status)
self.assertEqual(downloadable_status.get('earned_but_not_available'), earned_but_not_available)
@@ -257,7 +271,7 @@ class CertificateIsInvalid(WebCertificateTestMixin, ModuleStoreTestCase):
# Also check query count for 'is_certificate_invalid' method.
with self.assertNumQueries(1):
self.assertFalse(
certs_api.is_certificate_invalid(self.student, course.id)
is_certificate_invalid(self.student, course.id)
)
@ddt.data(
@@ -275,7 +289,7 @@ class CertificateIsInvalid(WebCertificateTestMixin, ModuleStoreTestCase):
generated_cert = self._generate_cert(status)
self._invalidate_certificate(generated_cert, True)
self.assertTrue(
certs_api.is_certificate_invalid(self.student, self.course.id)
is_certificate_invalid(self.student, self.course.id)
)
@ddt.data(
@@ -293,7 +307,7 @@ class CertificateIsInvalid(WebCertificateTestMixin, ModuleStoreTestCase):
generated_cert = self._generate_cert(status)
self._invalidate_certificate(generated_cert, False)
self.assertFalse(
certs_api.is_certificate_invalid(self.student, self.course.id)
is_certificate_invalid(self.student, self.course.id)
)
@ddt.data(
@@ -318,7 +332,7 @@ class CertificateIsInvalid(WebCertificateTestMixin, ModuleStoreTestCase):
# Also check query count for 'is_certificate_invalid' method.
with self.assertNumQueries(2):
self.assertTrue(
certs_api.is_certificate_invalid(self.student, self.course.id)
is_certificate_invalid(self.student, self.course.id)
)
def _invalidate_certificate(self, certificate, active):
@@ -409,7 +423,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
"""
Test to get a certificate for a user for a specific course.
"""
cert = certs_api.get_certificate_for_user(self.student.username, self.web_cert_course.id)
cert = get_certificate_for_user(self.student.username, self.web_cert_course.id)
self.assertEqual(cert['username'], self.student.username)
self.assertEqual(cert['course_key'], self.web_cert_course.id)
@@ -424,7 +438,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
"""
Test to get all the certificates for a user
"""
certs = certs_api.get_certificates_for_user(self.student.username)
certs = get_certificates_for_user(self.student.username)
self.assertEqual(len(certs), 2)
self.assertEqual(certs[0]['username'], self.student.username)
self.assertEqual(certs[1]['username'], self.student.username)
@@ -448,7 +462,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
Test to get certificates for a user for certain course keys,
in a dictionary indexed by those course keys.
"""
certs = certs_api.get_certificates_for_user_by_course_keys(
certs = get_certificates_for_user_by_course_keys(
user=self.student,
course_keys={self.web_cert_course.id, self.no_cert_course.id},
)
@@ -463,7 +477,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
Test the case when there is no certificate for a user for a specific course.
"""
self.assertIsNone(
certs_api.get_certificate_for_user(self.student_no_cert.username, self.web_cert_course.id)
get_certificate_for_user(self.student_no_cert.username, self.web_cert_course.id)
)
def test_no_certificates_for_user(self):
@@ -471,7 +485,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
Test the case when there are no certificates for a user.
"""
self.assertEqual(
certs_api.get_certificates_for_user(self.student_no_cert.username),
get_certificates_for_user(self.student_no_cert.username),
[]
)
@@ -484,7 +498,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
'certificates:render_cert_by_uuid',
kwargs=dict(certificate_uuid=self.uuid)
)
cert_url = certs_api.get_certificate_url(
cert_url = get_certificate_url(
user_id=self.student.id,
course_id=self.web_cert_course.id,
uuid=self.uuid
@@ -496,7 +510,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
kwargs=dict(certificate_uuid=self.uuid)
)
cert_url = certs_api.get_certificate_url(
cert_url = get_certificate_url(
user_id=self.student.id,
course_id=self.web_cert_course.id,
uuid=self.uuid
@@ -508,7 +522,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
"""
Test the get_certificate_url with a pdf cert course
"""
cert_url = certs_api.get_certificate_url(
cert_url = get_certificate_url(
user_id=self.student.id,
course_id=self.pdf_cert_course.id,
uuid=self.uuid
@@ -520,7 +534,7 @@ class CertificateGetTests(SharedModuleStoreTestCase):
Test the case when there is a certificate but the course was deleted.
"""
self.assertIsNone(
certs_api.get_certificate_for_user(
get_certificate_for_user(
self.student.username,
self.nonexistent_course_id
)
@@ -555,7 +569,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu
def test_new_cert_requests_into_xqueue_returns_generating(self):
with mock_passing_grade():
with self._mock_queue():
certs_api.generate_user_certificates(self.student, self.course.id)
generate_user_certificates(self.student, self.course.id)
# Verify that the certificate has status 'generating'
cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
@@ -564,7 +578,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu
'edx.certificate.created',
user_id=self.student.id,
course_id=six.text_type(self.course.id),
certificate_url=certs_api.get_certificate_url(self.student.id, self.course.id),
certificate_url=get_certificate_url(self.student.id, self.course.id),
certificate_id=cert.verify_uuid,
enrollment_mode=cert.mode,
generation_mode='batch'
@@ -573,7 +587,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu
def test_xqueue_submit_task_error(self):
with mock_passing_grade():
with self._mock_queue(is_successful=False):
certs_api.generate_user_certificates(self.student, self.course.id)
generate_user_certificates(self.student, self.course.id)
# Verify that the certificate has been marked with status error
cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
@@ -597,7 +611,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu
with mock_passing_grade():
with self._mock_queue():
status = certs_api.generate_user_certificates(self.student, self.course.id)
status = generate_user_certificates(self.student, self.course.id)
self.assertEqual(status, 'generating')
@patch.dict(settings.FEATURES, {'CERTIFICATES_HTML_VIEW': True})
@@ -607,7 +621,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu
"""
self._setup_course_certificate()
with mock_passing_grade():
certs_api.generate_user_certificates(self.student, self.course.id)
generate_user_certificates(self.student, self.course.id)
# Verify that the certificate has status 'downloadable'
cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id)
@@ -618,7 +632,7 @@ class GenerateUserCertificatesTest(EventTestMixin, WebCertificateTestMixin, Modu
"""
Test certificate url is empty if html view is not enabled and certificate is not yet generated
"""
url = certs_api.get_certificate_url(self.student.id, self.course.id)
url = get_certificate_url(self.student.id, self.course.id)
self.assertEqual(url, "")
@@ -649,7 +663,7 @@ class CertificateGenerationEnabledTest(EventTestMixin, TestCase):
CertificateGenerationConfiguration.objects.create(enabled=is_feature_enabled)
if is_course_enabled is not None:
certs_api.set_cert_generation_enabled(self.COURSE_KEY, is_course_enabled)
set_cert_generation_enabled(self.COURSE_KEY, is_course_enabled)
cert_event_type = 'enabled' if is_course_enabled else 'disabled'
event_name = '.'.join(['edx', 'certificate', 'generation', cert_event_type])
self.assert_event_emitted(
@@ -664,11 +678,11 @@ class CertificateGenerationEnabledTest(EventTestMixin, TestCase):
CertificateGenerationConfiguration.objects.create(enabled=True)
# Enable for the course
certs_api.set_cert_generation_enabled(self.COURSE_KEY, True)
set_cert_generation_enabled(self.COURSE_KEY, True)
self._assert_enabled_for_course(self.COURSE_KEY, True)
# Disable for the course
certs_api.set_cert_generation_enabled(self.COURSE_KEY, False)
set_cert_generation_enabled(self.COURSE_KEY, False)
self._assert_enabled_for_course(self.COURSE_KEY, False)
def test_setting_is_course_specific(self):
@@ -676,7 +690,7 @@ class CertificateGenerationEnabledTest(EventTestMixin, TestCase):
CertificateGenerationConfiguration.objects.create(enabled=True)
# Enable for one course
certs_api.set_cert_generation_enabled(self.COURSE_KEY, True)
set_cert_generation_enabled(self.COURSE_KEY, True)
self._assert_enabled_for_course(self.COURSE_KEY, True)
# Should be disabled for another course
@@ -685,7 +699,7 @@ class CertificateGenerationEnabledTest(EventTestMixin, TestCase):
def _assert_enabled_for_course(self, course_key, expect_enabled):
"""Check that self-generated certificates are enabled or disabled for the course. """
actual_enabled = certs_api.cert_generation_enabled(course_key)
actual_enabled = cert_generation_enabled(course_key)
self.assertEqual(expect_enabled, actual_enabled)
@@ -698,7 +712,7 @@ class GenerateExampleCertificatesTest(ModuleStoreTestCase):
# Generate certificates for the course
CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug=CourseMode.HONOR)
with self._mock_xqueue() as mock_queue:
certs_api.generate_example_certificates(self.COURSE_KEY)
generate_example_certificates(self.COURSE_KEY)
# Verify that the appropriate certs were added to the queue
self._assert_certs_in_queue(mock_queue, 1)
@@ -716,7 +730,7 @@ class GenerateExampleCertificatesTest(ModuleStoreTestCase):
# Generate certificates for the course
with self._mock_xqueue() as mock_queue:
certs_api.generate_example_certificates(self.COURSE_KEY)
generate_example_certificates(self.COURSE_KEY)
# Verify that the appropriate certs were added to the queue
self._assert_certs_in_queue(mock_queue, 2)
@@ -748,7 +762,7 @@ class GenerateExampleCertificatesTest(ModuleStoreTestCase):
def _assert_cert_status(self, *expected_statuses):
"""Check the example certificate status. """
actual_status = certs_api.example_certificates_status(self.COURSE_KEY)
actual_status = example_certificates_status(self.COURSE_KEY)
self.assertEqual(list(expected_statuses), actual_status)
@@ -775,7 +789,7 @@ class CertificatesBrandingTest(ModuleStoreTestCase):
"""
# Generate certificates for the course
CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug=CourseMode.HONOR)
data = certs_api.get_certificate_header_context(is_secure=True)
data = get_certificate_header_context(is_secure=True)
# Make sure there are not unexpected keys in dict returned by 'get_certificate_header_context'
six.assertCountEqual(
@@ -801,7 +815,7 @@ class CertificatesBrandingTest(ModuleStoreTestCase):
"""
# Generate certificates for the course
CourseModeFactory.create(course_id=self.COURSE_KEY, mode_slug=CourseMode.HONOR)
data = certs_api.get_certificate_footer_context()
data = get_certificate_footer_context()
# Make sure there are not unexpected keys in dict returned by 'get_certificate_footer_context'
six.assertCountEqual(

View File

@@ -167,7 +167,7 @@ class RegenerateCertificatesTest(CertificateManagementTest):
@ddt.data(True, False)
@override_settings(CERT_QUEUE='test-queue')
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_OPENBADGES': True})
@patch('lms.djangoapps.certificates.api.XQueueCertInterface', spec=True)
@patch('lms.djangoapps.certificates.generation_handler.XQueueCertInterface', spec=True)
def test_clear_badge(self, issue_badges, xqueue):
"""
Given that I have a user with a badge

View File

@@ -14,7 +14,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from lms.djangoapps.certificates import api as certs_api
from lms.djangoapps.certificates.api import cert_generation_enabled
from lms.djangoapps.certificates.generation_handler import CERTIFICATES_USE_ALLOWLIST
from lms.djangoapps.certificates.models import (
CertificateGenerationConfiguration,
@@ -49,15 +49,15 @@ class SelfGeneratedCertsSignalTest(ModuleStoreTestCase):
according to course-pacing.
"""
self.course = CourseFactory.create(self_paced=False, emit_signals=True) # lint-amnesty, pylint: disable=attribute-defined-outside-init
self.assertFalse(certs_api.cert_generation_enabled(self.course.id))
self.assertFalse(cert_generation_enabled(self.course.id))
self.course.self_paced = True
self.store.update_item(self.course, self.user.id)
self.assertTrue(certs_api.cert_generation_enabled(self.course.id))
self.assertTrue(cert_generation_enabled(self.course.id))
self.course.self_paced = False
self.store.update_item(self.course, self.user.id)
self.assertFalse(certs_api.cert_generation_enabled(self.course.id))
self.assertFalse(cert_generation_enabled(self.course.id))
class WhitelistGeneratedCertificatesTest(ModuleStoreTestCase):

View File

@@ -8,23 +8,22 @@ from uuid import uuid4
import ddt
import six
from django.conf import settings
from django.test.utils import override_settings
from django.urls import reverse
from mock import patch
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from lms.djangoapps.certificates import api
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import GlobalStaff, SupportStaffRole
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.certificates.api import regenerate_user_certificates
from lms.djangoapps.certificates.models import CertificateInvalidation, CertificateStatuses, GeneratedCertificate
from lms.djangoapps.certificates.tests.factories import CertificateInvalidationFactory
from lms.djangoapps.grades.tests.utils import mock_passing_grade
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import GlobalStaff, SupportStaffRole
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy()
FEATURES_WITH_CERTS_ENABLED['CERTIFICATES_HTML_VIEW'] = True
@@ -113,7 +112,7 @@ class CertificateSearchTests(CertificateSupportTestCase):
)
self.course.cert_html_view_enabled = True
#course certificate configurations
# Course certificate configurations
certificates = [
{
'id': 1,
@@ -316,8 +315,8 @@ class CertificateRegenerateTests(CertificateSupportTestCase):
with mock_passing_grade(percent=0.75):
with patch('common.djangoapps.course_modes.models.CourseMode.mode_for_course') as mock_mode_for_course:
mock_mode_for_course.return_value = 'honor'
api.regenerate_user_certificates(self.student, self.course.id, # lint-amnesty, pylint: disable=no-member
course=self.course)
regenerate_user_certificates(self.student, self.course.id, # lint-amnesty, pylint: disable=no-member
course=self.course)
mock_generate_cert.assert_called()

View File

@@ -18,15 +18,15 @@ from django.utils.translation import ugettext as _
from django.views.decorators.http import require_GET, require_POST
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.django import modulestore
from lms.djangoapps.certificates import api
from common.djangoapps.student.models import CourseEnrollment, User
from common.djangoapps.util.json_request import JsonResponse
from lms.djangoapps.certificates.api import get_certificates_for_user, regenerate_user_certificates
from lms.djangoapps.certificates.models import CertificateInvalidation
from lms.djangoapps.certificates.permissions import GENERATE_ALL_CERTIFICATES, VIEW_ALL_CERTIFICATES
from lms.djangoapps.instructor_task.api import generate_certificates_for_students
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from common.djangoapps.student.models import CourseEnrollment, User
from common.djangoapps.util.json_request import JsonResponse
from xmodule.modulestore.django import modulestore
log = logging.getLogger(__name__)
@@ -100,7 +100,7 @@ def search_certificates(request):
except User.DoesNotExist:
return HttpResponseBadRequest(_(u"user '{user}' does not exist").format(user=user_filter))
certificates = api.get_certificates_for_user(user.username)
certificates = get_certificates_for_user(user.username)
for cert in certificates:
cert["course_key"] = six.text_type(cert["course_key"])
cert["created"] = cert["created"].isoformat()
@@ -201,7 +201,7 @@ def regenerate_certificate_for_user(request):
# Attempt to regenerate certificates
try:
certificate = api.regenerate_user_certificates(params["user"], params["course_key"], course=course)
certificate = regenerate_user_certificates(params["user"], params["course_key"], course=course)
except: # pylint: disable=bare-except
# We are pessimistic about the kinds of errors that might get thrown by the
# certificates API. This may be overkill, but we're logging everything so we can

View File

@@ -1970,7 +1970,7 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll
message = message.format(self.beta_tester.username, self.course.id)
generate_user_certificates(self.beta_tester, self.course.id, self.course)
capture.check_present(('edx.certificate', 'INFO', message))
capture.check_present(('lms.djangoapps.certificates.generation_handler', 'INFO', message))
def test_missing_params(self):
""" Test missing all query parameters. """