def enqueue_subsection_update(sender, **kwargs): # pylint: disable=unused-argument """ Handles the PROBLEM_WEIGHTED_SCORE_CHANGED or SUBSECTION_OVERRIDE_CHANGED signals by enqueueing a subsection update operation to occur asynchronously. """ events.grade_updated(**kwargs) context_key = LearningContextKey.from_string(kwargs['course_id']) if not context_key.is_course: return # If it's not a course, it has no subsections, so skip the subsection grading update recalculate_subsection_grade_v3.apply_async( kwargs=dict( user_id=kwargs['user_id'], anonymous_user_id=kwargs.get('anonymous_user_id'), course_id=kwargs['course_id'], usage_id=kwargs['usage_id'], only_if_higher=kwargs.get('only_if_higher'), expected_modified_time=to_timestamp(kwargs['modified']), score_deleted=kwargs.get('score_deleted', False), event_transaction_id=six.text_type(get_event_transaction_id()), event_transaction_type=six.text_type(get_event_transaction_type()), score_db_table=kwargs['score_db_table'], force_update_subsections=kwargs.get('force_update_subsections', False), ), countdown=RECALCULATE_GRADE_DELAY_SECONDS, )
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): if not record.course_id.is_course: # This is not a course, so we don't store subsection grades for it. continue task_args = { "user_id": record.student_id, "course_id": six.text_type(record.course_id), "usage_id": six.text_type(record.module_state_key), "only_if_higher": False, "expected_modified_time": to_timestamp(record.modified), "score_deleted": False, "event_transaction_id": six.text_type(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): if not record.student_item.course_id.is_course: # This is not a course, so ignore it continue task_args = { "user_id": user_by_anonymous_id(record.student_item.student_id).id, "anonymous_user_id": record.student_item.student_id, "course_id": six.text_type(record.student_item.course_id), "usage_id": six.text_type(record.student_item.item_id), "only_if_higher": False, "expected_modified_time": to_timestamp(record.created_at), "score_deleted": False, "event_transaction_id": six.text_type(event_transaction_id), "event_transaction_type": PROBLEM_SUBMITTED_EVENT_TYPE, "score_db_table": ScoreDatabaseTableEnum.submissions, } recalculate_subsection_grade_v3.apply_async(kwargs=task_args)
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)
def test_recalculate_subsection_grade_v3(self, freeze_flag_value, end_date_adjustment, mock_log): self.set_up_course(course_end=timezone.now() - timedelta(end_date_adjustment)) for user in self.users: CourseEnrollment.enroll(user, self.course.id) with override_waffle_flag(self.freeze_grade_flag, active=freeze_flag_value): modified_datetime = datetime.utcnow().replace(tzinfo=pytz.UTC) - timedelta(days=1) with patch('lms.djangoapps.grades.tasks._has_db_updated_with_new_score') as mock_has_db_updated: result = recalculate_subsection_grade_v3.apply_async(kwargs=self.recalculate_subsection_grade_kwargs) self._assert_for_freeze_grade_flag( result, freeze_flag_value, end_date_adjustment, mock_log, mock_has_db_updated, '_recalculate_subsection_grade' )
def test_recalculate_subsection_grade_v3(self, freeze_flag_value, end_date_adjustment, mock_log): self.set_up_course(course_end=timezone.now() - timedelta(end_date_adjustment)) for user in self.users: CourseEnrollment.enroll(user, self.course.id) with override_waffle_flag(self.freeze_grade_flag, active=freeze_flag_value): modified_datetime = datetime.utcnow().replace(tzinfo=pytz.UTC) - timedelta(days=1) with patch( 'lms.djangoapps.grades.tasks.GradesService', return_value=MockGradesService(mocked_return_value=MagicMock(modified=modified_datetime)) ) as mock_grade_service: result = recalculate_subsection_grade_v3.apply_async(kwargs=self.recalculate_subsection_grade_kwargs) self._assert_for_freeze_grade_flag( result, freeze_flag_value, end_date_adjustment, mock_log, mock_grade_service, '_recalculate_subsection_grade' )
def test_recalculate_subsection_grade_v3(self, freeze_flag_value, end_date_adjustment, mock_log): self.set_up_course(course_end=timezone.now() - timedelta(end_date_adjustment)) for user in self.users: CourseEnrollment.enroll(user, self.course.id) with override_waffle_flag(self.freeze_grade_flag, active=freeze_flag_value): modified_datetime = datetime.utcnow().replace( tzinfo=pytz.UTC) - timedelta(days=1) with patch('lms.djangoapps.grades.api', return_value=MockGradesService( mocked_return_value=MagicMock( modified=modified_datetime)) ) as mock_grade_service: result = recalculate_subsection_grade_v3.apply_async( kwargs=self.recalculate_subsection_grade_kwargs) self._assert_for_freeze_grade_flag( result, freeze_flag_value, end_date_adjustment, mock_log, mock_grade_service, '_recalculate_subsection_grade')