Commit 76057fc4 by Syed Hassan Raza Committed by Adam Palay

semantic task_input for certificate generation ECOM-3505

parent 03925474
...@@ -260,5 +260,10 @@ def generate_certificate_for_user(request): ...@@ -260,5 +260,10 @@ def generate_certificate_for_user(request):
return HttpResponseBadRequest(msg) return HttpResponseBadRequest(msg)
# Attempt to generate certificate # Attempt to generate certificate
generate_certificates_for_students(request, params["course_key"], students=[params["user"]]) generate_certificates_for_students(
request,
params["course_key"],
student_set="specific_student",
specific_student_id=params["user"].id
)
return HttpResponse(200) return HttpResponse(200)
...@@ -720,7 +720,6 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): ...@@ -720,7 +720,6 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase):
response = self.client.post( response = self.client.post(
url, url,
data=json.dumps([self.certificate_exception]),
content_type='application/json' content_type='application/json'
) )
# Assert Success # Assert Success
...@@ -736,24 +735,49 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): ...@@ -736,24 +735,49 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase):
u"Certificate generation started for white listed students." u"Certificate generation started for white listed students."
) )
def test_generate_certificate_exceptions_invalid_user_list_error(self): def test_generate_certificate_exceptions_whitelist_not_generated(self):
""" """
Test generate certificates exceptions api endpoint returns error Test generate certificates exceptions api endpoint returns success
when called with certificate exceptions with empty 'user_id' field when calling with new certificate exception.
""" """
url = reverse( url = reverse(
'generate_certificate_exceptions', 'generate_certificate_exceptions',
kwargs={'course_id': unicode(self.course.id), 'generate_for': 'new'} kwargs={'course_id': unicode(self.course.id), 'generate_for': 'new'}
) )
# assign empty user_id response = self.client.post(
self.certificate_exception.update({'user_id': ''}) url,
content_type='application/json'
)
# Assert Success
self.assertEqual(response.status_code, 200)
res_json = json.loads(response.content)
# Assert Request is successful
self.assertTrue(res_json['success'])
# Assert Message
self.assertEqual(
res_json['message'],
u"Certificate generation started for white listed students."
)
def test_generate_certificate_exceptions_generate_for_incorrect_value(self):
"""
Test generate certificates exceptions api endpoint returns error
when calling with generate_for without 'new' or 'all' value.
"""
url = reverse(
'generate_certificate_exceptions',
kwargs={'course_id': unicode(self.course.id), 'generate_for': ''}
)
response = self.client.post( response = self.client.post(
url, url,
data=json.dumps([self.certificate_exception]),
content_type='application/json' content_type='application/json'
) )
# Assert Failure # Assert Failure
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
...@@ -764,7 +788,7 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase): ...@@ -764,7 +788,7 @@ class GenerateCertificatesInstructorApiTest(SharedModuleStoreTestCase):
# Assert Message # Assert Message
self.assertEqual( self.assertEqual(
res_json['message'], res_json['message'],
u"Invalid data, user_id must be present for all certificate exceptions." u'Invalid data, generate_for must be "new" or "all".'
) )
......
...@@ -3032,41 +3032,24 @@ def generate_certificate_exceptions(request, course_id, generate_for=None): ...@@ -3032,41 +3032,24 @@ def generate_certificate_exceptions(request, course_id, generate_for=None):
""" """
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
try:
certificate_white_list = json.loads(request.body)
except ValueError:
return JsonResponse({
'success': False,
'message': _('Invalid Json data, Please refresh the page and then try again.')
}, status=400)
users = [exception.get('user_id', False) for exception in certificate_white_list]
if generate_for == 'all': if generate_for == 'all':
# Generate Certificates for all white listed students # Generate Certificates for all white listed students
students = User.objects.filter( students = 'all_whitelisted'
certificatewhitelist__course_id=course_key,
certificatewhitelist__whitelist=True elif generate_for == 'new':
) students = 'whitelisted_not_generated'
elif not all(users):
# Invalid data, user_id must be present for all certificate exceptions else:
# Invalid data, generate_for must be present for all certificate exceptions
return JsonResponse( return JsonResponse(
{ {
'success': False, 'success': False,
'message': _('Invalid data, user_id must be present for all certificate exceptions.'), 'message': _('Invalid data, generate_for must be "new" or "all".'),
}, },
status=400 status=400
) )
else:
students = User.objects.filter(
id__in=users,
certificatewhitelist__course_id=course_key,
certificatewhitelist__whitelist=True
)
if students: instructor_task.api.generate_certificates_for_students(request, course_key, student_set=students)
# generate certificates for students if 'students' list is not empty
instructor_task.api.generate_certificates_for_students(request, course_key, students=students)
response_payload = { response_payload = {
'success': True, 'success': True,
...@@ -3275,8 +3258,10 @@ def re_validate_certificate(request, course_key, generated_certificate): ...@@ -3275,8 +3258,10 @@ def re_validate_certificate(request, course_key, generated_certificate):
certificate_invalidation.deactivate() certificate_invalidation.deactivate()
# We need to generate certificate only for a single student here # We need to generate certificate only for a single student here
students = [certificate_invalidation.generated_certificate.user] student = certificate_invalidation.generated_certificate.user
instructor_task.api.generate_certificates_for_students(request, course_key, students=students) instructor_task.api.generate_certificates_for_students(
request, course_key, student_set="specific_student", specific_student_id=student.id
)
def validate_request_data_and_get_certificate(certificate_invalidation, course_key): def validate_request_data_and_get_certificate(certificate_invalidation, course_key):
......
...@@ -45,6 +45,13 @@ from bulk_email.models import CourseEmail ...@@ -45,6 +45,13 @@ from bulk_email.models import CourseEmail
from util import milestones_helpers from util import milestones_helpers
class SpecificStudentIdMissingError(Exception):
"""
Exception indicating that a student id was not provided when generating a certificate for a specific student.
"""
pass
def get_running_instructor_tasks(course_id): def get_running_instructor_tasks(course_id):
""" """
Returns a query of InstructorTask objects of running tasks for a given course. Returns a query of InstructorTask objects of running tasks for a given course.
...@@ -437,17 +444,34 @@ def submit_export_ora2_data(request, course_key): ...@@ -437,17 +444,34 @@ def submit_export_ora2_data(request, course_key):
return submit_task(request, task_type, task_class, course_key, task_input, task_key) return submit_task(request, task_type, task_class, course_key, task_input, task_key)
def generate_certificates_for_students(request, course_key, students=None): # pylint: disable=invalid-name def generate_certificates_for_students(request, course_key, student_set=None, specific_student_id=None): # pylint: disable=invalid-name
""" """
Submits a task to generate certificates for given students enrolled in the course or Submits a task to generate certificates for given students enrolled in the course.
all students if argument 'students' is None
Arguments:
course_key : Course Key
student_set : Semantic for student collection for certificate generation.
Options are:
'all_whitelisted': All Whitelisted students.
'whitelisted_not_generated': Whitelisted students which does not got certificates yet.
'specific_student': Single student for certificate generation.
specific_student_id : Student ID when student_set is 'specific_student'
Raises AlreadyRunningError if certificates are currently being generated. Raises AlreadyRunningError if certificates are currently being generated.
""" Raises SpecificStudentIdMissingError if student_set is 'specific_student' and specific_student_id is 'None'
if students: """
task_type = 'generate_certificates_certain_student' if student_set:
students = [student.id for student in students] task_type = 'generate_certificates_student_set'
task_input = {'students': students} task_input = {'student_set': student_set}
if student_set == 'specific_student':
task_type = 'generate_certificates_certain_student'
if specific_student_id is None:
raise SpecificStudentIdMissingError(
"Attempted to generate certificate for a single student, "
"but no specific student id provided"
)
task_input.update({'specific_student_id': specific_student_id})
else: else:
task_type = 'generate_certificates_all_student' task_type = 'generate_certificates_all_student'
task_input = {} task_input = {}
...@@ -466,22 +490,16 @@ def generate_certificates_for_students(request, course_key, students=None): # p ...@@ -466,22 +490,16 @@ def generate_certificates_for_students(request, course_key, students=None): # p
return instructor_task return instructor_task
def regenerate_certificates(request, course_key, statuses_to_regenerate, students=None): def regenerate_certificates(request, course_key, statuses_to_regenerate):
""" """
Submits a task to regenerate certificates for given students enrolled in the course or Submits a task to regenerate certificates for given students enrolled in the course.
all students if argument 'students' is None.
Regenerate Certificate only if the status of the existing generated certificate is in 'statuses_to_regenerate' Regenerate Certificate only if the status of the existing generated certificate is in 'statuses_to_regenerate'
list passed in the arguments. list passed in the arguments.
Raises AlreadyRunningError if certificates are currently being generated. Raises AlreadyRunningError if certificates are currently being generated.
""" """
if students: task_type = 'regenerate_certificates_all_student'
task_type = 'regenerate_certificates_certain_student' task_input = {}
students = [student.id for student in students]
task_input = {'students': students}
else:
task_type = 'regenerate_certificates_all_student'
task_input = {}
task_input.update({"statuses_to_regenerate": statuses_to_regenerate}) task_input.update({"statuses_to_regenerate": statuses_to_regenerate})
task_class = generate_certificates task_class = generate_certificates
......
...@@ -1409,30 +1409,56 @@ def generate_students_certificates( ...@@ -1409,30 +1409,56 @@ def generate_students_certificates(
json column, otherwise generate certificates for all enrolled students. json column, otherwise generate certificates for all enrolled students.
""" """
start_time = time() start_time = time()
enrolled_students = CourseEnrollment.objects.users_enrolled_in(course_id) students_to_generate_certs_for = CourseEnrollment.objects.users_enrolled_in(course_id)
student_set = task_input.get('student_set')
if student_set == 'all_whitelisted':
# Generate Certificates for all white listed students.
students_to_generate_certs_for = students_to_generate_certs_for.filter(
certificatewhitelist__course_id=course_id,
certificatewhitelist__whitelist=True
)
students = task_input.get('students', None) elif student_set == 'whitelisted_not_generated':
# All Whitelisted students
students_to_generate_certs_for = students_to_generate_certs_for.filter(
certificatewhitelist__course_id=course_id,
certificatewhitelist__whitelist=True
)
if students is not None: # Whitelisted students which got certificates already.
enrolled_students = enrolled_students.filter(id__in=students) certificate_generated_students = GeneratedCertificate.objects.filter( # pylint: disable=no-member
course_id=course_id,
)
certificate_generated_students_ids = set(certificate_generated_students.values_list('user_id', flat=True))
task_progress = TaskProgress(action_name, enrolled_students.count(), start_time) students_to_generate_certs_for = students_to_generate_certs_for.exclude(
id__in=certificate_generated_students_ids
)
elif student_set == "specific_student":
specific_student_id = task_input.get('specific_student_id')
students_to_generate_certs_for = students_to_generate_certs_for.filter(id=specific_student_id)
task_progress = TaskProgress(action_name, students_to_generate_certs_for.count(), start_time)
current_step = {'step': 'Calculating students already have certificates'} current_step = {'step': 'Calculating students already have certificates'}
task_progress.update_task_state(extra_meta=current_step) task_progress.update_task_state(extra_meta=current_step)
statuses_to_regenerate = task_input.get('statuses_to_regenerate', []) statuses_to_regenerate = task_input.get('statuses_to_regenerate', [])
if students is not None and not statuses_to_regenerate: if student_set is not None and not statuses_to_regenerate:
# We want to skip 'filtering students' only when students are given and statuses to regenerate are not # We want to skip 'filtering students' only when students are given and statuses to regenerate are not
students_require_certs = enrolled_students students_require_certs = students_to_generate_certs_for
else: else:
students_require_certs = students_require_certificate(course_id, enrolled_students, statuses_to_regenerate) students_require_certs = students_require_certificate(
course_id, students_to_generate_certs_for, statuses_to_regenerate
)
if statuses_to_regenerate: if statuses_to_regenerate:
# Mark existing generated certificates as 'unavailable' before regenerating # Mark existing generated certificates as 'unavailable' before regenerating
# We need to call this method after "students_require_certificate" otherwise "students_require_certificate" # We need to call this method after "students_require_certificate" otherwise "students_require_certificate"
# would return no results. # would return no results.
invalidate_generated_certificates(course_id, enrolled_students, statuses_to_regenerate) invalidate_generated_certificates(course_id, students_to_generate_certs_for, statuses_to_regenerate)
task_progress.skipped = task_progress.total - len(students_require_certs) task_progress.skipped = task_progress.total - len(students_require_certs)
......
...@@ -24,6 +24,7 @@ from instructor_task.api import ( ...@@ -24,6 +24,7 @@ from instructor_task.api import (
generate_certificates_for_students, generate_certificates_for_students,
regenerate_certificates, regenerate_certificates,
submit_export_ora2_data, submit_export_ora2_data,
SpecificStudentIdMissingError,
) )
from instructor_task.api_helper import AlreadyRunningError from instructor_task.api_helper import AlreadyRunningError
...@@ -295,6 +296,18 @@ class InstructorTaskCourseSubmitTest(TestReportMixin, InstructorTaskCourseTestCa ...@@ -295,6 +296,18 @@ class InstructorTaskCourseSubmitTest(TestReportMixin, InstructorTaskCourseTestCa
) )
self._test_resubmission(api_call) self._test_resubmission(api_call)
def test_certificate_generation_no_specific_student_id(self):
"""
Raises ValueError when student_set is 'specific_student' and 'specific_student_id' is None.
"""
with self.assertRaises(SpecificStudentIdMissingError):
generate_certificates_for_students(
self.create_task_request(self.instructor),
self.course.id,
student_set='specific_student',
specific_student_id=None
)
def test_certificate_generation_history(self): def test_certificate_generation_history(self):
""" """
Tests that a new record is added whenever certificate generation/regeneration task is submitted. Tests that a new record is added whenever certificate generation/regeneration task is submitted.
......
...@@ -1628,8 +1628,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1628,8 +1628,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
Verify that certificates generated for all eligible students enrolled in a course. Verify that certificates generated for all eligible students enrolled in a course.
""" """
# create 10 students # create 10 students
students = [self.create_student(username='student_{}'.format(i), email='student_{}@example.com'.format(i)) students = self._create_students(10)
for i in xrange(1, 11)]
# mark 2 students to have certificates generated already # mark 2 students to have certificates generated already
for student in students[:2]: for student in students[:2]:
...@@ -1644,40 +1643,157 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1644,40 +1643,157 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
for student in students[2:7]: for student in students[2:7]:
CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True) CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
current_task = Mock() task_input = {'student_set': None}
current_task.update_state = Mock() expected_results = {
instructor_task = Mock() 'action_name': 'certificates generated',
instructor_task.task_input = json.dumps({'students': None}) 'total': 10,
'attempted': 8,
'succeeded': 5,
'failed': 3,
'skipped': 2
}
with self.assertNumQueries(214): with self.assertNumQueries(214):
with patch('instructor_task.tasks_helper._get_current_task') as mock_current_task: self.assertCertificatesGenerated(task_input, expected_results)
mock_current_task.return_value = current_task
with patch('capa.xqueue_interface.XQueueInterface.send_to_queue') as mock_queue: def test_certificate_generation_all_whitelisted(self):
mock_queue.return_value = (0, "Successfully queued") """
with patch('instructor_task.models.InstructorTask.objects.get') as instructor_task_object: Verify that certificates generated for all white-listed students when using semantic task_input as
instructor_task_object.return_value = instructor_task `all_whitelisted`.
result = generate_students_certificates( """
None, None, self.course.id, {}, 'certificates generated' # create 5 students
) students = self._create_students(5)
self.assertDictContainsSubset(
{ # white-list 5 students
'action_name': 'certificates generated', for student in students:
'total': 10, CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
'attempted': 8,
'succeeded': 5, task_input = {'student_set': 'all_whitelisted'}
'failed': 3, expected_results = {
'skipped': 2 'action_name': 'certificates generated',
}, 'total': 5,
result 'attempted': 5,
'succeeded': 5,
'failed': 0,
'skipped': 0
}
self.assertCertificatesGenerated(task_input, expected_results)
def test_certificate_generation_whitelist_already_generated(self):
"""
Verify that certificates generated for all white-listed students having certifcates already when using
semantic task_input as `all_whitelisted`.
"""
# create 5 students
students = self._create_students(5)
# white-list 5 students
for student in students:
CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
# mark 5 students to have certificates generated already
for student in students:
GeneratedCertificateFactory.create(
user=student,
course_id=self.course.id,
status=CertificateStatuses.downloadable,
mode='honor'
)
task_input = {'student_set': 'all_whitelisted'}
expected_results = {
'action_name': 'certificates generated',
'total': 5,
'attempted': 5,
'succeeded': 5,
'failed': 0,
'skipped': 0
}
self.assertCertificatesGenerated(task_input, expected_results)
def test_certificate_generation_whitelisted_not_generated(self):
"""
Verify that certificates only generated for those students which does not have certificates yet when
using semantic task_input as `whitelisted_not_generated`.
"""
# create 5 students
students = self._create_students(5)
# mark 2 students to have certificates generated already
for student in students[:2]:
GeneratedCertificateFactory.create(
user=student,
course_id=self.course.id,
status=CertificateStatuses.downloadable,
mode='honor'
)
# white-list 5 students
for student in students:
CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
task_input = {'student_set': 'whitelisted_not_generated'}
expected_results = {
'action_name': 'certificates generated',
'total': 3,
'attempted': 3,
'succeeded': 3,
'failed': 0,
'skipped': 0
}
self.assertCertificatesGenerated(
task_input,
expected_results
) )
def test_certificate_generation_specific_student(self):
"""
Tests generating a certificate for a specific student.
"""
student = self.create_student(username="Hamnet", email="ham@ardenforest.co.uk")
CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
task_input = {
'student_set': 'specific_student',
'specific_student_id': student.id
}
expected_results = {
'action_name': 'certificates generated',
'total': 1,
'attempted': 1,
'succeeded': 1,
'failed': 0,
'skipped': 0,
}
self.assertCertificatesGenerated(task_input, expected_results)
def test_specific_student_not_enrolled(self):
"""
Tests generating a certificate for a specific student if that student
is not enrolled in the course.
"""
student = self.create_student(username="jacques", email="antlers@ardenforest.co.uk")
task_input = {
'student_set': 'specific_student',
'specific_student_id': student.id
}
expected_results = {
'action_name': 'certificates generated',
'total': 1,
'attempted': 1,
'succeeded': 0,
'failed': 1,
'skipped': 0,
}
self.assertCertificatesGenerated(task_input, expected_results)
def test_certificate_regeneration_for_statuses_to_regenerate(self): def test_certificate_regeneration_for_statuses_to_regenerate(self):
""" """
Verify that certificates are regenerated for all eligible students enrolled in a course whose generated Verify that certificates are regenerated for all eligible students enrolled in a course whose generated
certificate statuses lies in the list 'statuses_to_regenerate' given in task_input. certificate statuses lies in the list 'statuses_to_regenerate' given in task_input.
""" """
# create 10 students # create 10 students
students = [self.create_student(username='student_{}'.format(i), email='student_{}@example.com'.format(i)) students = self._create_students(10)
for i in xrange(1, 11)]
# mark 2 students to have certificates generated already # mark 2 students to have certificates generated already
for student in students[:2]: for student in students[:2]:
...@@ -1710,31 +1826,22 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1710,31 +1826,22 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
for student in students[:7]: for student in students[:7]:
CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True) CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
current_task = Mock()
current_task.update_state = Mock()
# Certificates should be regenerated for students having generated certificates with status # Certificates should be regenerated for students having generated certificates with status
# 'downloadable' or 'error' which are total of 5 students in this test case # 'downloadable' or 'error' which are total of 5 students in this test case
task_input = {'statuses_to_regenerate': [CertificateStatuses.downloadable, CertificateStatuses.error]} task_input = {'statuses_to_regenerate': [CertificateStatuses.downloadable, CertificateStatuses.error]}
with patch('instructor_task.tasks_helper._get_current_task') as mock_current_task: expected_results = {
mock_current_task.return_value = current_task 'action_name': 'certificates generated',
with patch('capa.xqueue_interface.XQueueInterface.send_to_queue') as mock_queue: 'total': 10,
mock_queue.return_value = (0, "Successfully queued") 'attempted': 5,
result = generate_students_certificates( 'succeeded': 5,
None, None, self.course.id, task_input, 'certificates generated' 'failed': 0,
) 'skipped': 5
}
self.assertDictContainsSubset( self.assertCertificatesGenerated(
{ task_input,
'action_name': 'certificates generated', expected_results
'total': 10,
'attempted': 5,
'succeeded': 5,
'failed': 0,
'skipped': 5
},
result
) )
def test_certificate_regeneration_with_expected_failures(self): def test_certificate_regeneration_with_expected_failures(self):
...@@ -1746,8 +1853,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1746,8 +1853,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
default_grade = '-1' default_grade = '-1'
# create 10 students # create 10 students
students = [self.create_student(username='student_{}'.format(i), email='student_{}@example.com'.format(i)) students = self._create_students(10)
for i in xrange(1, 11)]
# mark 2 students to have certificates generated already # mark 2 students to have certificates generated already
for student in students[:2]: for student in students[:2]:
...@@ -1796,32 +1902,21 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1796,32 +1902,21 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
for student in students[:7]: for student in students[:7]:
CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True) CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
current_task = Mock()
current_task.update_state = Mock()
# Regenerated certificates for students having generated certificates with status # Regenerated certificates for students having generated certificates with status
# 'deleted' or 'generating' # 'deleted' or 'generating'
task_input = {'statuses_to_regenerate': [CertificateStatuses.deleted, CertificateStatuses.generating]} task_input = {'statuses_to_regenerate': [CertificateStatuses.deleted, CertificateStatuses.generating]}
with patch('instructor_task.tasks_helper._get_current_task') as mock_current_task: expected_results = {
mock_current_task.return_value = current_task 'action_name': 'certificates generated',
with patch('capa.xqueue_interface.XQueueInterface.send_to_queue') as mock_queue: 'total': 10,
mock_queue.return_value = (0, "Successfully queued") 'attempted': 5,
result = generate_students_certificates( 'succeeded': 2,
None, None, self.course.id, task_input, 'certificates generated' 'failed': 3,
) 'skipped': 5
}
self.assertCertificatesGenerated(task_input, expected_results)
self.assertDictContainsSubset(
{
'action_name': 'certificates generated',
'total': 10,
'attempted': 5,
'succeeded': 2,
'failed': 3,
'skipped': 5
},
result
)
generated_certificates = GeneratedCertificate.eligible_certificates.filter( generated_certificates = GeneratedCertificate.eligible_certificates.filter(
user__in=students, user__in=students,
course_id=self.course.id, course_id=self.course.id,
...@@ -1852,8 +1947,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1852,8 +1947,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
default_grade = '-1' default_grade = '-1'
# create 10 students # create 10 students
students = [self.create_student(username='student_{}'.format(i), email='student_{}@example.com'.format(i)) students = self._create_students(10)
for i in xrange(1, 11)]
# mark 2 students to have certificates generated already # mark 2 students to have certificates generated already
for student in students[:2]: for student in students[:2]:
...@@ -1899,9 +1993,6 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1899,9 +1993,6 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
for student in students[:]: for student in students[:]:
CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True) CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
current_task = Mock()
current_task.update_state = Mock()
# Regenerated certificates for students having generated certificates with status # Regenerated certificates for students having generated certificates with status
# 'downloadable', 'error' or 'generating' # 'downloadable', 'error' or 'generating'
task_input = { task_input = {
...@@ -1912,24 +2003,18 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1912,24 +2003,18 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
] ]
} }
with patch('instructor_task.tasks_helper._get_current_task') as mock_current_task: expected_results = {
mock_current_task.return_value = current_task 'action_name': 'certificates generated',
with patch('capa.xqueue_interface.XQueueInterface.send_to_queue') as mock_queue: 'total': 10,
mock_queue.return_value = (0, "Successfully queued") 'attempted': 8,
result = generate_students_certificates( 'succeeded': 8,
None, None, self.course.id, task_input, 'certificates generated' 'failed': 0,
) 'skipped': 2
}
self.assertDictContainsSubset( self.assertCertificatesGenerated(
{ task_input,
'action_name': 'certificates generated', expected_results
'total': 10,
'attempted': 8,
'succeeded': 8,
'failed': 0,
'skipped': 2
},
result
) )
generated_certificates = GeneratedCertificate.eligible_certificates.filter( generated_certificates = GeneratedCertificate.eligible_certificates.filter(
...@@ -1963,8 +2048,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -1963,8 +2048,7 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
Verify that certificates are regenerated for all students passed in task_input. Verify that certificates are regenerated for all students passed in task_input.
""" """
# create 10 students # create 10 students
students = [self.create_student(username='student_{}'.format(i), email='student_{}@example.com'.format(i)) students = self._create_students(10)
for i in xrange(1, 11)]
# mark 2 students to have certificates generated already # mark 2 students to have certificates generated already
for student in students[:2]: for student in students[:2]:
...@@ -2006,12 +2090,27 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -2006,12 +2090,27 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
for student in students[:7]: for student in students[:7]:
CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True) CertificateWhitelistFactory.create(user=student, course_id=self.course.id, whitelist=True)
current_task = Mock()
current_task.update_state = Mock()
# Certificates should be regenerated for students having generated certificates with status # Certificates should be regenerated for students having generated certificates with status
# 'downloadable' or 'error' which are total of 5 students in this test case # 'downloadable' or 'error' which are total of 5 students in this test case
task_input = {'students': [student.id for student in students]} task_input = {'student_set': "all_whitelisted"}
expected_results = {
'action_name': 'certificates generated',
'total': 7,
'attempted': 7,
'succeeded': 7,
'failed': 0,
'skipped': 0,
}
self.assertCertificatesGenerated(task_input, expected_results)
def assertCertificatesGenerated(self, task_input, expected_results):
"""
Generate certificates for the given task_input and compare with expected_results.
"""
current_task = Mock()
current_task.update_state = Mock()
with patch('instructor_task.tasks_helper._get_current_task') as mock_current_task: with patch('instructor_task.tasks_helper._get_current_task') as mock_current_task:
mock_current_task.return_value = current_task mock_current_task.return_value = current_task
...@@ -2022,17 +2121,22 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase): ...@@ -2022,17 +2121,22 @@ class TestCertificateGeneration(InstructorTaskModuleTestCase):
) )
self.assertDictContainsSubset( self.assertDictContainsSubset(
{ expected_results,
'action_name': 'certificates generated',
'total': 10,
'attempted': 10,
'succeeded': 7,
'failed': 3,
'skipped': 0,
},
result result
) )
def _create_students(self, number_of_students):
"""
Create Students for course.
"""
return [
self.create_student(
username='student_{}'.format(index),
email='student_{}@example.com'.format(index)
)
for index in xrange(number_of_students)
]
class TestInstructorOra2Report(SharedModuleStoreTestCase): class TestInstructorOra2Report(SharedModuleStoreTestCase):
""" """
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
<p class="under-heading"> <p class="under-heading">
<label> <label>
<input type='radio' name='generate-exception-certificates-radio' checked="checked" value='new' aria-describedby='generate-exception-certificates-radio-new-tip'> <input type='radio' name='generate-exception-certificates-radio' checked="checked" value='new' aria-describedby='generate-exception-certificates-radio-new-tip'>
<span id='generate-exception-certificates-radio-new-tip'><%- gettext('Generate a Certificate for all ') %><strong><%- gettext('New') %></strong> <%- gettext('additions to the Exception list') %></span> <span id='generate-exception-certificates-radio-new-tip'><%- gettext('Generate certificates for all users on the Exception list for whom certificates have not yet been run') %></span>
</label> </label>
<br/> <br/>
<label> <label>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment