From 1e16e873a4440b1d25e42c6617b981b05a7b03c4 Mon Sep 17 00:00:00 2001 From: Saleem Latif Date: Thu, 3 Dec 2015 15:15:33 +0500 Subject: [PATCH] Add Certificate Regeneration history table. --- .../0004_certificategenerationhistory.py | 32 +++++++++++++++ lms/djangoapps/certificates/models.py | 19 +++++++++ lms/djangoapps/instructor/views/api.py | 2 +- lms/djangoapps/instructor_task/api.py | 37 ++++++++++-------- .../instructor_task/tests/test_api.py | 39 +++++++++++++++++-- 5 files changed, 109 insertions(+), 20 deletions(-) create mode 100644 lms/djangoapps/certificates/migrations/0004_certificategenerationhistory.py diff --git a/lms/djangoapps/certificates/migrations/0004_certificategenerationhistory.py b/lms/djangoapps/certificates/migrations/0004_certificategenerationhistory.py new file mode 100644 index 0000000000..e1b496e527 --- /dev/null +++ b/lms/djangoapps/certificates/migrations/0004_certificategenerationhistory.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone +from django.conf import settings +import model_utils.fields +import xmodule_django.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('instructor_task', '0001_initial'), + ('certificates', '0003_data__default_modes'), + ] + + operations = [ + migrations.CreateModel( + name='CertificateGenerationHistory', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)), + ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)), + ('course_id', xmodule_django.models.CourseKeyField(max_length=255)), + ('is_regeneration', models.BooleanField(default=False)), + ('generated_by', models.ForeignKey(to=settings.AUTH_USER_MODEL)), + ('instructor_task', models.ForeignKey(to='instructor_task.InstructorTask')), + ], + ), + ] diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index aa90a22122..20d70c42db 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -68,6 +68,7 @@ from config_models.models import ConfigurationModel from xmodule_django.models import CourseKeyField, NoneToEmptyManager from util.milestones_helpers import fulfill_course_milestone, is_prerequisite_courses_enabled from course_modes.models import CourseMode +from instructor_task.models import InstructorTask LOGGER = logging.getLogger(__name__) @@ -236,6 +237,24 @@ class GeneratedCertificate(models.Model): self.save() +class CertificateGenerationHistory(TimeStampedModel): + """ + Model for storing Certificate Generation History. + """ + + course_id = CourseKeyField(max_length=255) + generated_by = models.ForeignKey(User) + instructor_task = models.ForeignKey(InstructorTask) + is_regeneration = models.BooleanField(default=False) + + class Meta(object): + app_label = "certificates" + + def __unicode__(self): + return u"certificates %s by %s on %s for %s" % \ + ("regenerated" if self.is_regeneration else "generated", self.generated_by, self.created, self.course_id) + + @receiver(post_save, sender=GeneratedCertificate) def handle_post_cert_generated(sender, instance, **kwargs): # pylint: disable=unused-argument """ diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index 2fef32eb76..f688923477 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -2679,7 +2679,7 @@ def start_certificate_generation(request, course_id): Start generating certificates for all students enrolled in given course. """ course_key = CourseKey.from_string(course_id) - task = instructor_task.api.generate_certificates_for_all_students(request, course_key) + task = instructor_task.api.generate_certificates_for_students(request, course_key) message = _('Certificate generation task for all students of this course has been started. ' 'You can view the status of the generation task in the "Pending Tasks" section.') response_payload = { diff --git a/lms/djangoapps/instructor_task/api.py b/lms/djangoapps/instructor_task/api.py index 82a80f7a11..a58973dbb7 100644 --- a/lms/djangoapps/instructor_task/api.py +++ b/lms/djangoapps/instructor_task/api.py @@ -31,6 +31,8 @@ from instructor_task.tasks import ( proctored_exam_results_csv ) +from certificates.models import CertificateGenerationHistory + from instructor_task.api_helper import ( check_arguments_for_rescoring, encode_problem_and_student_input, @@ -422,20 +424,6 @@ def submit_cohort_students(request, course_key, file_name): return submit_task(request, task_type, task_class, course_key, task_input, task_key) -def generate_certificates_for_all_students(request, course_key): # pylint: disable=invalid-name - """ - Submits a task to generate certificates for all students enrolled in the course. - - Raises AlreadyRunningError if certificates are currently being generated. - """ - task_type = 'generate_certificates_all_student' - task_class = generate_certificates - 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 """ Submits a task to generate certificates for given students enrolled in the course or @@ -453,8 +441,16 @@ def generate_certificates_for_students(request, course_key, students=None): # p task_class = generate_certificates task_key = "" + instructor_task = 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) + CertificateGenerationHistory.objects.create( + course_id=course_key, + generated_by=request.user, + instructor_task=instructor_task, + is_regeneration=False + ) + + return instructor_task def regenerate_certificates(request, course_key, statuses_to_regenerate, students=None): @@ -478,4 +474,13 @@ def regenerate_certificates(request, course_key, statuses_to_regenerate, student task_class = generate_certificates task_key = "" - return submit_task(request, task_type, task_class, course_key, task_input, task_key) + instructor_task = submit_task(request, task_type, task_class, course_key, task_input, task_key) + + CertificateGenerationHistory.objects.create( + course_id=course_key, + generated_by=request.user, + instructor_task=instructor_task, + is_regeneration=True + ) + + return instructor_task diff --git a/lms/djangoapps/instructor_task/tests/test_api.py b/lms/djangoapps/instructor_task/tests/test_api.py index 5a7ce849a3..9621c3db81 100644 --- a/lms/djangoapps/instructor_task/tests/test_api.py +++ b/lms/djangoapps/instructor_task/tests/test_api.py @@ -21,7 +21,7 @@ from instructor_task.api import ( submit_calculate_may_enroll_csv, submit_executive_summary_report, submit_course_survey_report, - generate_certificates_for_all_students, + generate_certificates_for_students, regenerate_certificates ) @@ -32,7 +32,7 @@ from instructor_task.tests.test_base import (InstructorTaskTestCase, InstructorTaskModuleTestCase, TestReportMixin, TEST_COURSE_KEY) -from certificates.models import CertificateStatuses +from certificates.models import CertificateStatuses, CertificateGenerationHistory class InstructorTaskReportTest(InstructorTaskTestCase): @@ -260,7 +260,7 @@ class InstructorTaskCourseSubmitTest(TestReportMixin, InstructorTaskCourseTestCa """ Tests certificates generation task submission api """ - api_call = lambda: generate_certificates_for_all_students( + api_call = lambda: generate_certificates_for_students( self.create_task_request(self.instructor), self.course.id ) @@ -280,3 +280,36 @@ class InstructorTaskCourseSubmitTest(TestReportMixin, InstructorTaskCourseTestCa [CertificateStatuses.downloadable, CertificateStatuses.generating] ) self._test_resubmission(api_call) + + def test_certificate_generation_history(self): + """ + Tests that a new record is added whenever certificate generation/regeneration task is submitted. + """ + instructor_task = generate_certificates_for_students( + self.create_task_request(self.instructor), + self.course.id + ) + certificate_generation_history = CertificateGenerationHistory.objects.filter( + course_id=self.course.id, + generated_by=self.instructor, + instructor_task=instructor_task, + is_regeneration=False + ) + + # Validate that record was added to CertificateGenerationHistory + self.assertTrue(certificate_generation_history.exists()) + + instructor_task = regenerate_certificates( + self.create_task_request(self.instructor), + self.course.id, + [CertificateStatuses.downloadable, CertificateStatuses.generating] + ) + certificate_generation_history = CertificateGenerationHistory.objects.filter( + course_id=self.course.id, + generated_by=self.instructor, + instructor_task=instructor_task, + is_regeneration=True + ) + + # Validate that record was added to CertificateGenerationHistory + self.assertTrue(certificate_generation_history.exists())