MICROBA-918 Move methods to separate certificate generation from celery task to schedule certificate generation. Also standardizes imports. (#26410)
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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(
|
||||
(
|
||||
|
||||
@@ -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__)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. """
|
||||
|
||||
Reference in New Issue
Block a user