Merge pull request #19307 from edx/change_credit_eligibility_deadline

LEARNER-6643 change credit course eligibility deadline
This commit is contained in:
Zainab Amir
2018-11-27 14:55:24 +05:00
committed by GitHub
2 changed files with 208 additions and 0 deletions

View File

@@ -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

View File

@@ -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())