Commit 78edd852 by Sanford Student

add command

parent 9380b6b5
"""
Command to recalculate grades for all subsections with problem submissions
in the specified time range.
"""
from __future__ import absolute_import, division, print_function, unicode_literals
from datetime import datetime
import logging
from pytz import utc
from django.core.management.base import BaseCommand, CommandError
from lms.djangoapps.grades.constants import ScoreDatabaseTableEnum
from lms.djangoapps.grades.signals.handlers import PROBLEM_SUBMITTED_EVENT_TYPE
from lms.djangoapps.grades.tasks import recalculate_subsection_grade_v3
from courseware.models import StudentModule
from student.models import user_by_anonymous_id
from submissions.models import Submission
from track.event_transaction_utils import create_new_event_transaction_id, set_event_transaction_type
from util.date_utils import to_timestamp
log = logging.getLogger(__name__)
DATE_FORMAT = "%Y-%m-%d %H:%M"
class Command(BaseCommand):
"""
Example usage:
$ ./manage.py lms recalculate_subsection_grades
--modified_start '2016-08-23 16:43' --modified_end '2016-08-25 16:43' --settings=devstack
"""
args = 'fill this in'
help = 'Recalculates subsection grades for all subsections modified within the given time range.'
def add_arguments(self, parser):
"""
Entry point for subclassed commands to add custom arguments.
"""
parser.add_argument(
'--modified_start',
dest='modified_start',
help='Starting range for modified date (inclusive): e.g. "2016-08-23 16:43"; expected in UTC.',
)
parser.add_argument(
'--modified_end',
dest='modified_end',
help='Ending range for modified date (inclusive): e.g. "2016-12-23 16:43"; expected in UTC.',
)
def handle(self, *args, **options):
if 'modified_start' not in options:
raise CommandError('modified_start must be provided.')
if 'modified_end' not in options:
raise CommandError('modified_end must be provided.')
modified_start = utc.localize(datetime.strptime(options['modified_start'], DATE_FORMAT))
modified_end = utc.localize(datetime.strptime(options['modified_end'], DATE_FORMAT))
event_transaction_id = create_new_event_transaction_id()
set_event_transaction_type(PROBLEM_SUBMITTED_EVENT_TYPE)
kwargs = {'modified__range': (modified_start, modified_end), 'module_type': 'problem'}
for record in StudentModule.objects.filter(**kwargs):
task_args = {
"user_id": record.student_id,
"course_id": unicode(record.course_id),
"usage_id": unicode(record.module_state_key),
"only_if_higher": False,
"expected_modified_time": to_timestamp(record.modified),
"score_deleted": False,
"event_transaction_id": unicode(event_transaction_id),
"event_transaction_type": PROBLEM_SUBMITTED_EVENT_TYPE,
"score_db_table": ScoreDatabaseTableEnum.courseware_student_module,
}
recalculate_subsection_grade_v3.apply_async(kwargs=task_args)
kwargs = {'created_at__range': (modified_start, modified_end)}
for record in Submission.objects.filter(**kwargs):
task_args = {
"user_id": user_by_anonymous_id(record.student_item.student_id).id,
"anonymous_user_id": record.student_item.student_id,
"course_id": unicode(record.student_item.course_id),
"usage_id": unicode(record.student_item.item_id),
"only_if_higher": False,
"expected_modified_time": to_timestamp(record.created_at),
"score_deleted": False,
"event_transaction_id": unicode(event_transaction_id),
"event_transaction_type": PROBLEM_SUBMITTED_EVENT_TYPE,
"score_db_table": ScoreDatabaseTableEnum.submissions,
}
recalculate_subsection_grade_v3.apply_async(kwargs=task_args)
"""
Tests for reset_grades management command.
"""
import ddt
from datetime import datetime
from django.conf import settings
from mock import patch, MagicMock
from pytz import utc
from lms.djangoapps.grades.management.commands import recalculate_subsection_grades
from lms.djangoapps.grades.constants import ScoreDatabaseTableEnum
from lms.djangoapps.grades.tests.test_tasks import HasCourseWithProblemsMixin
from track.event_transaction_utils import get_event_transaction_id
from util.date_utils import to_timestamp
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
DATE_FORMAT = "%Y-%m-%d %H:%M"
@patch.dict(settings.FEATURES, {'PERSISTENT_GRADES_ENABLED_FOR_ALL_TESTS': False})
@ddt.ddt
class TestRecalculateSubsectionGrades(HasCourseWithProblemsMixin, ModuleStoreTestCase):
"""
Tests recalculate subsection grades management command.
"""
def setUp(self):
super(TestRecalculateSubsectionGrades, self).setUp()
self.command = recalculate_subsection_grades.Command()
@patch('lms.djangoapps.grades.management.commands.recalculate_subsection_grades.Submission')
@patch('lms.djangoapps.grades.management.commands.recalculate_subsection_grades.user_by_anonymous_id')
@patch('lms.djangoapps.grades.management.commands.recalculate_subsection_grades.recalculate_subsection_grade_v3')
def test_submissions(self, task_mock, id_mock, subs_mock):
submission = MagicMock()
submission.student_item = MagicMock(
student_id="anonymousID",
course_id='x/y/z',
item_id='abc',
)
submission.created_at = utc.localize(datetime.strptime('2016-08-23 16:43', DATE_FORMAT))
subs_mock.objects.filter.return_value = [submission]
id_mock.return_value = MagicMock()
id_mock.return_value.id = "ID"
self._run_command_and_check_output(task_mock, ScoreDatabaseTableEnum.submissions, include_anonymous_id=True)
@patch('lms.djangoapps.grades.management.commands.recalculate_subsection_grades.StudentModule')
@patch('lms.djangoapps.grades.management.commands.recalculate_subsection_grades.user_by_anonymous_id')
@patch('lms.djangoapps.grades.management.commands.recalculate_subsection_grades.recalculate_subsection_grade_v3')
def test_csm(self, task_mock, id_mock, csm_mock):
csm_record = MagicMock()
csm_record.student_id = "ID"
csm_record.course_id = "x/y/z"
csm_record.module_state_key = "abc"
csm_record.modified = utc.localize(datetime.strptime('2016-08-23 16:43', DATE_FORMAT))
csm_mock.objects.filter.return_value = [csm_record]
id_mock.return_value = MagicMock()
id_mock.return_value.id = "ID"
self._run_command_and_check_output(task_mock, ScoreDatabaseTableEnum.courseware_student_module)
def _run_command_and_check_output(self, task_mock, score_db_table, include_anonymous_id=False):
self.command.handle(modified_start='2016-08-25 16:42', modified_end='2018-08-25 16:44')
kwargs = {
"user_id": "ID",
"course_id": u'x/y/z',
"usage_id": u'abc',
"only_if_higher": False,
"expected_modified_time": to_timestamp(utc.localize(datetime.strptime('2016-08-23 16:43', DATE_FORMAT))),
"score_deleted": False,
"event_transaction_id": unicode(get_event_transaction_id()),
"event_transaction_type": u'edx.grades.problem.submitted',
"score_db_table": score_db_table,
}
if include_anonymous_id:
kwargs['anonymous_user_id'] = 'anonymousID'
task_mock.apply_async.assert_called_with(kwargs=kwargs)
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