diff --git a/lms/djangoapps/certificates/models.py b/lms/djangoapps/certificates/models.py index e85b72d942..f1b23f30e1 100644 --- a/lms/djangoapps/certificates/models.py +++ b/lms/djangoapps/certificates/models.py @@ -268,7 +268,8 @@ class CertificateGenerationHistory(TimeStampedModel): """ Return "regenerated" if record corresponds to Certificate Regeneration task, otherwise returns 'generated' """ - return "regenerated" if self.is_regeneration else "generated" + # Translators: This is a past-tense verb that is used for task action messages. + return _("regenerated") if self.is_regeneration else _("generated") def get_certificate_generation_candidates(self): """ @@ -285,7 +286,8 @@ class CertificateGenerationHistory(TimeStampedModel): task_input_json = json.loads(task_input) except ValueError: # if task input is empty, it means certificates were generated for all learners - return "All learners" + # Translators: This string represents task was executed for all learners. + return _("All learners") # get statuses_to_regenerate from task_input convert statuses to human readable strings and return statuses = task_input_json.get('statuses_to_regenerate', None) @@ -294,9 +296,10 @@ class CertificateGenerationHistory(TimeStampedModel): [CertificateStatuses.readable_statuses.get(status, "") for status in statuses] ) - # If statuses_to_regenerate is not present in task_input then, certificate generation task was run to - # generate certificates for white listed students - return "for exceptions" + # If students is present in task_input then, certificate generation task was run to + # generate certificates for white listed students otherwise it is for all students. + # Translators: This string represents task was executed for students having exceptions. + return _("For exceptions") if 'students' in task_input_json else _("All learners") class Meta(object): app_label = "certificates" diff --git a/lms/djangoapps/instructor/views/api.py b/lms/djangoapps/instructor/views/api.py index bab6c47f89..1c16b3ce10 100644 --- a/lms/djangoapps/instructor/views/api.py +++ b/lms/djangoapps/instructor/views/api.py @@ -10,14 +10,13 @@ import json import logging import re import time -import requests from django.conf import settings from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt from django.views.decorators.http import require_POST, require_http_methods from django.views.decorators.cache import cache_control from django.core.exceptions import ValidationError, PermissionDenied from django.core.mail.message import EmailMessage -from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned +from django.core.exceptions import ObjectDoesNotExist from django.db import IntegrityError, transaction from django.core.urlresolvers import reverse from django.core.validators import validate_email @@ -25,11 +24,9 @@ from django.utils.translation import ugettext as _ from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound from django.utils.html import strip_tags from django.shortcuts import redirect -from util.db import outer_atomic import string import random import unicodecsv -import urllib import decimal from student import auth from student.roles import GlobalStaff, CourseSalesAdminRole, CourseFinanceAdminRole @@ -52,7 +49,7 @@ from django_comment_common.models import ( FORUM_ROLE_MODERATOR, FORUM_ROLE_COMMUNITY_TA, ) -from edxmako.shortcuts import render_to_response, render_to_string +from edxmako.shortcuts import render_to_string from courseware.models import StudentModule from shoppingcart.models import ( Coupon, @@ -92,7 +89,7 @@ from instructor.views import INVOICE_KEY from submissions import api as sub_api # installed from the edx-submissions repository from certificates import api as certs_api -from certificates.models import CertificateWhitelist, GeneratedCertificate +from certificates.models import CertificateWhitelist, GeneratedCertificate, CertificateStatuses from bulk_email.models import CourseEmail from student.models import get_user_by_username_or_email @@ -108,7 +105,6 @@ from .tools import ( set_due_date_extension, strip_if_string, bulk_email_is_enabled_for_course, - add_block_ids, ) from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.locations import SlashSeparatedCourseKey @@ -2708,7 +2704,7 @@ def start_certificate_regeneration(request, course_id): ) # Check if the selected statuses are allowed - allowed_statuses = GeneratedCertificate.get_unique_statuses(course_key=course_key, flat=True) + allowed_statuses = [CertificateStatuses.downloadable, CertificateStatuses.error, CertificateStatuses.notpassing] if not set(certificates_statuses).issubset(allowed_statuses): return JsonResponse( {'message': _('Please select certificate statuses from the list only.')}, @@ -2789,11 +2785,18 @@ def add_certificate_exception(course_key, student, certificate_exception): } ) + generated_certificate = GeneratedCertificate.objects.filter( + user=student, + course_id=course_key, + status=CertificateStatuses.downloadable, + ).first() + exception = dict({ 'id': certificate_white_list.id, 'user_email': student.email, 'user_name': student.username, 'user_id': student.id, + 'certificate_generated': generated_certificate and generated_certificate.created_date.strftime("%B %d, %Y"), 'created': certificate_white_list.created.strftime("%A, %B %d, %Y"), }) diff --git a/lms/djangoapps/instructor/views/instructor_dashboard.py b/lms/djangoapps/instructor/views/instructor_dashboard.py index c3d8c00b42..7186df11bf 100644 --- a/lms/djangoapps/instructor/views/instructor_dashboard.py +++ b/lms/djangoapps/instructor/views/instructor_dashboard.py @@ -322,7 +322,8 @@ def _section_certificates(course): 'active_certificate': certs_api.get_active_web_certificate(course), 'certificate_statuses_with_count': certificate_statuses_with_count, 'status': CertificateStatuses, - 'certificate_generation_history': CertificateGenerationHistory.objects.filter(course_id=course.id), + 'certificate_generation_history': + CertificateGenerationHistory.objects.filter(course_id=course.id).order_by("-created"), 'urls': { 'generate_example_certificates': reverse( 'generate_example_certificates', diff --git a/lms/static/js/certificates/views/certificate_whitelist.js b/lms/static/js/certificates/views/certificate_whitelist.js index 783ca77fcb..39f850a7e3 100644 --- a/lms/static/js/certificates/views/certificate_whitelist.js +++ b/lms/static/js/certificates/views/certificate_whitelist.js @@ -32,7 +32,12 @@ render: function(){ var template = this.loadTemplate('certificate-white-list'); this.$el.html(template({certificates: this.collection.models})); - + if (this.collection.isEmpty()) { + this.$("#generate-exception-certificates").addClass("is-disabled"); + } + else { + this.$("#generate-exception-certificates").removeClass("is-disabled"); + } }, loadTemplate: function(name) {