Commit abb8484e by Matt Drayer

Merge pull request #10974 from edx/saleem-latif/SOL-1418-review-changes

SOL-1418: display certificate generate history in reverse chronological order
parents 7c017969 22653b18
...@@ -268,7 +268,8 @@ class CertificateGenerationHistory(TimeStampedModel): ...@@ -268,7 +268,8 @@ class CertificateGenerationHistory(TimeStampedModel):
""" """
Return "regenerated" if record corresponds to Certificate Regeneration task, otherwise returns 'generated' 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): def get_certificate_generation_candidates(self):
""" """
...@@ -285,7 +286,8 @@ class CertificateGenerationHistory(TimeStampedModel): ...@@ -285,7 +286,8 @@ class CertificateGenerationHistory(TimeStampedModel):
task_input_json = json.loads(task_input) task_input_json = json.loads(task_input)
except ValueError: except ValueError:
# if task input is empty, it means certificates were generated for all learners # 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 # get statuses_to_regenerate from task_input convert statuses to human readable strings and return
statuses = task_input_json.get('statuses_to_regenerate', None) statuses = task_input_json.get('statuses_to_regenerate', None)
...@@ -294,9 +296,10 @@ class CertificateGenerationHistory(TimeStampedModel): ...@@ -294,9 +296,10 @@ class CertificateGenerationHistory(TimeStampedModel):
[CertificateStatuses.readable_statuses.get(status, "") for status in statuses] [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 # If students is present in task_input then, certificate generation task was run to
# generate certificates for white listed students # generate certificates for white listed students otherwise it is for all students.
return "for exceptions" # 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): class Meta(object):
app_label = "certificates" app_label = "certificates"
......
...@@ -10,14 +10,13 @@ import json ...@@ -10,14 +10,13 @@ import json
import logging import logging
import re import re
import time import time
import requests
from django.conf import settings from django.conf import settings
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt 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.http import require_POST, require_http_methods
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from django.core.exceptions import ValidationError, PermissionDenied from django.core.exceptions import ValidationError, PermissionDenied
from django.core.mail.message import EmailMessage 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.db import IntegrityError, transaction
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.validators import validate_email from django.core.validators import validate_email
...@@ -25,11 +24,9 @@ from django.utils.translation import ugettext as _ ...@@ -25,11 +24,9 @@ from django.utils.translation import ugettext as _
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotFound
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.shortcuts import redirect from django.shortcuts import redirect
from util.db import outer_atomic
import string import string
import random import random
import unicodecsv import unicodecsv
import urllib
import decimal import decimal
from student import auth from student import auth
from student.roles import GlobalStaff, CourseSalesAdminRole, CourseFinanceAdminRole from student.roles import GlobalStaff, CourseSalesAdminRole, CourseFinanceAdminRole
...@@ -52,7 +49,7 @@ from django_comment_common.models import ( ...@@ -52,7 +49,7 @@ from django_comment_common.models import (
FORUM_ROLE_MODERATOR, FORUM_ROLE_MODERATOR,
FORUM_ROLE_COMMUNITY_TA, 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 courseware.models import StudentModule
from shoppingcart.models import ( from shoppingcart.models import (
Coupon, Coupon,
...@@ -92,7 +89,7 @@ from instructor.views import INVOICE_KEY ...@@ -92,7 +89,7 @@ from instructor.views import INVOICE_KEY
from submissions import api as sub_api # installed from the edx-submissions repository from submissions import api as sub_api # installed from the edx-submissions repository
from certificates import api as certs_api 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 bulk_email.models import CourseEmail
from student.models import get_user_by_username_or_email from student.models import get_user_by_username_or_email
...@@ -108,7 +105,6 @@ from .tools import ( ...@@ -108,7 +105,6 @@ from .tools import (
set_due_date_extension, set_due_date_extension,
strip_if_string, strip_if_string,
bulk_email_is_enabled_for_course, bulk_email_is_enabled_for_course,
add_block_ids,
) )
from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locations import SlashSeparatedCourseKey from opaque_keys.edx.locations import SlashSeparatedCourseKey
...@@ -2708,7 +2704,7 @@ def start_certificate_regeneration(request, course_id): ...@@ -2708,7 +2704,7 @@ def start_certificate_regeneration(request, course_id):
) )
# Check if the selected statuses are allowed # 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): if not set(certificates_statuses).issubset(allowed_statuses):
return JsonResponse( return JsonResponse(
{'message': _('Please select certificate statuses from the list only.')}, {'message': _('Please select certificate statuses from the list only.')},
...@@ -2789,11 +2785,18 @@ def add_certificate_exception(course_key, student, certificate_exception): ...@@ -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({ exception = dict({
'id': certificate_white_list.id, 'id': certificate_white_list.id,
'user_email': student.email, 'user_email': student.email,
'user_name': student.username, 'user_name': student.username,
'user_id': student.id, '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"), 'created': certificate_white_list.created.strftime("%A, %B %d, %Y"),
}) })
......
...@@ -322,7 +322,8 @@ def _section_certificates(course): ...@@ -322,7 +322,8 @@ def _section_certificates(course):
'active_certificate': certs_api.get_active_web_certificate(course), 'active_certificate': certs_api.get_active_web_certificate(course),
'certificate_statuses_with_count': certificate_statuses_with_count, 'certificate_statuses_with_count': certificate_statuses_with_count,
'status': CertificateStatuses, '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': { 'urls': {
'generate_example_certificates': reverse( 'generate_example_certificates': reverse(
'generate_example_certificates', 'generate_example_certificates',
......
...@@ -32,7 +32,12 @@ ...@@ -32,7 +32,12 @@
render: function(){ render: function(){
var template = this.loadTemplate('certificate-white-list'); var template = this.loadTemplate('certificate-white-list');
this.$el.html(template({certificates: this.collection.models})); 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) { loadTemplate: function(name) {
......
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