Merge pull request #19307 from edx/change_credit_eligibility_deadline
LEARNER-6643 change credit course eligibility deadline
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
""" Command line script to change credit course eligibility deadline. """
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from django.core.management.base import BaseCommand
|
||||
from opaque_keys import InvalidKeyError
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from student.models import CourseEnrollment, User
|
||||
|
||||
from openedx.core.djangoapps.credit.models import CreditEligibility
|
||||
|
||||
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
|
||||
|
||||
DEFAULT_DAYS = 30
|
||||
|
||||
|
||||
class IncorrectDeadline(Exception):
|
||||
"""
|
||||
Exception raised explicitly to use default date when date given by user is prior to today.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
|
||||
help = """
|
||||
Changes the credit course eligibility deadline for a student in a particular course.
|
||||
It can be used to update the expired deadline to make student credit eligible.
|
||||
|
||||
Example:
|
||||
|
||||
Change credit eligibility deadline for user joe enrolled in credit course :
|
||||
|
||||
$ ... change_eligibility_deadline -u joe -d 2018-12-30 -c course-v1:org-course-run
|
||||
"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('-u', '--username',
|
||||
metavar='USERNAME',
|
||||
required=True,
|
||||
help='username of the student')
|
||||
parser.add_argument('-d', '--date',
|
||||
dest='deadline',
|
||||
metavar='DEADLINE',
|
||||
help='Desired eligibility deadline for credit course')
|
||||
parser.add_argument('-c', '--course',
|
||||
metavar='COURSE_KEY',
|
||||
dest='course_key',
|
||||
required=True,
|
||||
help='Course Key')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""
|
||||
Handler for the command
|
||||
|
||||
It performs checks for username, course and enrollment validity and
|
||||
then calls update_deadline for the given arguments
|
||||
"""
|
||||
username = options['username']
|
||||
course_id = options['course_key']
|
||||
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
logger.exception('Invalid or non-existent username {}'.format(username))
|
||||
raise
|
||||
|
||||
try:
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
CourseEnrollment.objects.get(user=user, course_id=course_key, mode=CourseMode.CREDIT_MODE)
|
||||
except InvalidKeyError:
|
||||
logger.exception('Invalid or non-existent course id {}'.format(course_id))
|
||||
raise
|
||||
except CourseEnrollment.DoesNotExist:
|
||||
logger.exception('No enrollment found in database for {username} in course {course_id}'
|
||||
.format(username=username, course_id=course_id))
|
||||
raise
|
||||
|
||||
try:
|
||||
expected_date = datetime.strptime(options['deadline'], '%Y-%m-%d')
|
||||
current_date = datetime.utcnow()
|
||||
if expected_date < current_date:
|
||||
raise IncorrectDeadline('Incorrect Deadline')
|
||||
except (TypeError, KeyError, IncorrectDeadline):
|
||||
logger.warning('Invalid date or date not provided. Setting deadline to one month from now')
|
||||
expected_date = datetime.utcnow() + timedelta(days=DEFAULT_DAYS)
|
||||
|
||||
self.update_credit_eligibility_deadline(username, course_key, expected_date)
|
||||
logger.info("Successfully updated credit eligibility deadline for {}".format(username))
|
||||
|
||||
def update_credit_eligibility_deadline(self, username, course_key, new_deadline):
|
||||
""" Update Credit Eligibility new_deadline for a specific user """
|
||||
try:
|
||||
eligibility_record = CreditEligibility.objects.get(username=username, course__course_key=course_key)
|
||||
eligibility_record.deadline = new_deadline
|
||||
eligibility_record.save()
|
||||
except CreditEligibility.DoesNotExist:
|
||||
logger.exception('User is not credit eligible')
|
||||
raise
|
||||
@@ -0,0 +1,107 @@
|
||||
""" Test the change_eligibility_deadline command line script."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from course_modes.tests.factories import CourseMode
|
||||
from django.core.management import call_command
|
||||
from opaque_keys import InvalidKeyError
|
||||
from six import text_type
|
||||
from student.models import CourseEnrollment, User
|
||||
from student.tests.factories import UserFactory
|
||||
from testfixtures import LogCapture
|
||||
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from openedx.core.djangoapps.credit.models import CreditCourse, CreditEligibility
|
||||
|
||||
LOGGER_NAME = 'student.management.commands.change_eligibility_deadline'
|
||||
command_args = '--username {username} --course {course} --date {date}'
|
||||
|
||||
|
||||
class ChangeEligibilityDeadlineTests(SharedModuleStoreTestCase):
|
||||
""" Test the deadline change functionality of the change_eligibility_deadline script."""
|
||||
|
||||
def setUp(self):
|
||||
""" Initial set up for tests """
|
||||
super(ChangeEligibilityDeadlineTests, self).setUp()
|
||||
self.course = CourseFactory.create()
|
||||
|
||||
self.enrolled_user = UserFactory.create(username='amy', email='amy@pond.com', password='password')
|
||||
CourseEnrollment.enroll(self.enrolled_user, self.course.id, mode=CourseMode.CREDIT_MODE).save()
|
||||
|
||||
credit_course = CreditCourse.objects.create(course_key=self.course.id)
|
||||
self.credit_eligibility = CreditEligibility.objects.create(
|
||||
username=self.enrolled_user.username,
|
||||
course=credit_course,
|
||||
)
|
||||
self.credit_eligibility.deadline = datetime.strptime('2013-12-30', '%Y-%m-%d')
|
||||
self.credit_eligibility.save()
|
||||
|
||||
def test_invalid_command_arguments(self):
|
||||
""" Test command with invalid arguments """
|
||||
course_id_str = text_type(self.course.id)
|
||||
username = self.enrolled_user.username
|
||||
|
||||
# Incorrect username
|
||||
with self.assertRaises(User.DoesNotExist):
|
||||
call_command('change_eligibility_deadline',
|
||||
*command_args.format(username='XYZ', course=course_id_str, date='2018-12-30').split(' ')
|
||||
)
|
||||
# Incorrect course id
|
||||
with self.assertRaises(InvalidKeyError):
|
||||
call_command('change_eligibility_deadline',
|
||||
*command_args.format(username=username, course='XYZ', date='2018-12-30').split(' ')
|
||||
)
|
||||
# Student not enrolled
|
||||
with self.assertRaises(CourseEnrollment.DoesNotExist):
|
||||
unenrolled_user = UserFactory.create()
|
||||
call_command('change_eligibility_deadline',
|
||||
*command_args.format(username=unenrolled_user.username, course=course_id_str,
|
||||
date='2018-12-30').split(' ')
|
||||
)
|
||||
# Date format Invalid
|
||||
with self.assertRaises(ValueError):
|
||||
call_command('change_eligibility_deadline',
|
||||
*command_args.format(username=username, course=course_id_str, date='30-12-2018').split(' ')
|
||||
)
|
||||
# Date not provided
|
||||
with self.assertRaises(KeyError):
|
||||
call_command('change_eligibility_deadline',
|
||||
*command_args.format(username=username, course=course_id_str,).split(' '))
|
||||
|
||||
def test_invalid_date(self):
|
||||
"""
|
||||
Tests the command when the date is prior to today
|
||||
|
||||
In case the date given as deadline is prior to today it sets the deadline to
|
||||
default value which is one month from today. It then continues to run the code
|
||||
to change eligibility deadline.
|
||||
"""
|
||||
course_key = text_type(self.course.id)
|
||||
username = self.enrolled_user.username
|
||||
|
||||
# Test Date set prior to today
|
||||
with LogCapture(LOGGER_NAME) as logger:
|
||||
call_command(
|
||||
'change_eligibility_deadline',
|
||||
*command_args.format(username=username, course=course_key, date='2000-12-30').split(' ')
|
||||
)
|
||||
logger.check(
|
||||
(LOGGER_NAME, 'WARNING', 'Invalid date or date not provided. Setting deadline to one month from now'),
|
||||
(LOGGER_NAME, 'INFO', 'Successfully updated credit eligibility deadline for {}'.format(username))
|
||||
)
|
||||
|
||||
def test_valid_command_arguments(self):
|
||||
""" Test command with valid arguments """
|
||||
course_key = text_type(self.course.id)
|
||||
username = self.enrolled_user.username
|
||||
new_deadline = datetime.utcnow() + timedelta(days=30)
|
||||
|
||||
call_command('change_eligibility_deadline',
|
||||
*command_args.format(username=username, course=course_key, date=new_deadline.date()).split(' ')
|
||||
)
|
||||
|
||||
credit_eligibility = CreditEligibility.objects.get(username=username,
|
||||
course__course_key=self.course.id)
|
||||
credit_deadline = credit_eligibility.deadline.date()
|
||||
self.assertEqual(credit_deadline, new_deadline.date())
|
||||
Reference in New Issue
Block a user