Commit 5d0ed333 by Peter Fogg

Merge pull request #11502 from edx/peter-fogg/audit-cert-cutoff-date

Explicitly cut off cert eligibility.
parents 85591696 2d028f82
......@@ -326,8 +326,8 @@ class XQueueCertInterface(object):
# analytics. Only do this if the certificate is new, or
# already marked as ineligible -- we don't want to mark
# existing audit certs as ineligible.
if (created or cert.status in (CertificateStatuses.audit_passing, CertificateStatuses.audit_notpassing)) \
and not is_eligible_for_certificate:
cutoff = settings.AUDIT_CERT_CUTOFF_DATE
if (cutoff and cert.created_date >= cutoff) and not is_eligible_for_certificate:
cert.status = CertificateStatuses.audit_passing if passing else CertificateStatuses.audit_notpassing
cert.save()
LOGGER.info(
......
# -*- coding: utf-8 -*-
"""Tests for the XQueue certificates interface. """
from contextlib import contextmanager
from datetime import datetime, timedelta
import ddt
import json
from mock import patch, Mock
......@@ -8,6 +9,8 @@ from nose.plugins.attrib import attr
from django.test import TestCase
from django.test.utils import override_settings
import freezegun
import pytz
from course_modes.models import CourseMode
from opaque_keys.edx.locator import CourseLocator
......@@ -81,6 +84,7 @@ class XQueueCertInterfaceAddCertificateTest(ModuleStoreTestCase):
self.assertIsNotNone(certificate.verify_uuid)
@ddt.data('honor', 'audit')
@override_settings(AUDIT_CERT_CUTOFF_DATE=datetime.now(pytz.UTC) - timedelta(days=1))
def test_add_cert_with_honor_certificates(self, mode):
"""Test certificates generations for honor and audit modes."""
template_name = 'certificate-template-{id.org}-{id.course}.pdf'.format(
......@@ -206,13 +210,45 @@ class XQueueCertInterfaceAddCertificateTest(ModuleStoreTestCase):
self.assertFalse(mock_send.called)
@ddt.data(
(CertificateStatuses.downloadable, 'Pass', CertificateStatuses.generating),
(CertificateStatuses.audit_passing, 'Pass', CertificateStatuses.audit_passing),
(CertificateStatuses.audit_notpassing, 'Pass', CertificateStatuses.audit_passing),
(CertificateStatuses.audit_notpassing, None, CertificateStatuses.audit_notpassing),
# Eligible and should stay that way
(
CertificateStatuses.downloadable,
datetime.now(pytz.UTC) - timedelta(days=2),
'Pass',
CertificateStatuses.generating
),
# Ensure that certs in the wrong state can be fixed by regeneration
(
CertificateStatuses.downloadable,
datetime.now(pytz.UTC) - timedelta(hours=1),
'Pass',
CertificateStatuses.audit_passing
),
# Ineligible and should stay that way
(
CertificateStatuses.audit_passing,
datetime.now(pytz.UTC) - timedelta(hours=1),
'Pass',
CertificateStatuses.audit_passing
),
# As above
(
CertificateStatuses.audit_notpassing,
datetime.now(pytz.UTC) - timedelta(hours=1),
'Pass',
CertificateStatuses.audit_passing
),
# As above
(
CertificateStatuses.audit_notpassing,
datetime.now(pytz.UTC) - timedelta(hours=1),
None,
CertificateStatuses.audit_notpassing
),
)
@ddt.unpack
def test_regen_audit_certs_eligibility(self, status, grade, expected_status):
@override_settings(AUDIT_CERT_CUTOFF_DATE=datetime.now(pytz.UTC) - timedelta(days=1))
def test_regen_audit_certs_eligibility(self, status, created_date, grade, expected_status):
"""
Test that existing audit certificates remain eligible even if cert
generation is re-run.
......@@ -224,13 +260,14 @@ class XQueueCertInterfaceAddCertificateTest(ModuleStoreTestCase):
is_active=True,
mode=CourseMode.AUDIT,
)
GeneratedCertificateFactory(
user=self.user_2,
course_id=self.course.id,
grade='1.0',
status=status,
mode=GeneratedCertificate.MODES.audit,
)
with freezegun.freeze_time(created_date):
GeneratedCertificateFactory(
user=self.user_2,
course_id=self.course.id,
grade='1.0',
status=status,
mode=GeneratedCertificate.MODES.audit,
)
# Run grading/cert generation again
with patch('courseware.grades.grade', Mock(return_value={'grade': grade, 'percent': 0.75})):
......
......@@ -19,6 +19,8 @@ Common traits:
import datetime
import json
import dateutil
from .common import *
from openedx.core.lib.logsettings import get_logger_config
import os
......@@ -756,3 +758,7 @@ MICROSITE_DATABASE_TEMPLATE_CACHE_TTL = ENV_TOKENS.get(
# Course Content Bookmarks Settings
MAX_BOOKMARKS_PER_COURSE = ENV_TOKENS.get('MAX_BOOKMARKS_PER_COURSE', MAX_BOOKMARKS_PER_COURSE)
# Cutoff date for granting audit certificates
if ENV_TOKENS.get('AUDIT_CERT_CUTOFF_DATE', None):
AUDIT_CERT_CUTOFF_DATE = dateutil.parser.parse(ENV_TOKENS.get('AUDIT_CERT_CUTOFF_DATE'))
......@@ -2761,6 +2761,10 @@ MOBILE_APP_USER_AGENT_REGEXES = [
DEPRECATED_ADVANCED_COMPONENT_TYPES = []
# Cutoff date for granting audit certificates
AUDIT_CERT_CUTOFF_DATE = None
################################ Settings for Credentials Service ################################
CREDENTIALS_SERVICE_USERNAME = 'credentials_service_user'
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