Revert "Merge pull request #13614 from edx/efischer/revert_grades"
This reverts commit0ea8e1b9b2, reversing changes made toe3db08f194.
This commit is contained in:
@@ -4,6 +4,7 @@ Enrollment operations for use by instructor APIs.
|
||||
Does not include any access control, be sure to check access before calling.
|
||||
"""
|
||||
|
||||
import crum
|
||||
import json
|
||||
import logging
|
||||
from django.contrib.auth.models import User
|
||||
@@ -13,11 +14,14 @@ from django.core.mail import send_mail
|
||||
from django.utils.translation import override as override_language
|
||||
|
||||
from course_modes.models import CourseMode
|
||||
from student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
from courseware.model_data import FieldDataCache
|
||||
from courseware.module_render import get_module_for_descriptor
|
||||
from courseware.models import StudentModule
|
||||
from edxmako.shortcuts import render_to_string
|
||||
from grades.scores import weighted_score
|
||||
from grades.signals.signals import SCORE_CHANGED
|
||||
from lang_pref import LANGUAGE_KEY
|
||||
|
||||
from student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
from submissions import api as sub_api # installed from the edx-submissions repository
|
||||
from student.models import anonymous_id_for_user
|
||||
from openedx.core.djangoapps.user_api.models import UserPreference
|
||||
@@ -245,6 +249,7 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user
|
||||
)
|
||||
submission_cleared = True
|
||||
except ItemNotFoundError:
|
||||
block = None
|
||||
log.warning("Could not find %s in modulestore when attempting to reset attempts.", module_state_key)
|
||||
|
||||
# Reset the student's score in the submissions API, if xblock.clear_student_state has not done so already.
|
||||
@@ -267,6 +272,7 @@ def reset_student_attempts(course_id, student, module_state_key, requesting_user
|
||||
|
||||
if delete_module:
|
||||
module_to_reset.delete()
|
||||
_fire_score_changed_for_block(course_id, student, block, module_state_key)
|
||||
else:
|
||||
_reset_module_attempts(module_to_reset)
|
||||
|
||||
@@ -287,6 +293,43 @@ def _reset_module_attempts(studentmodule):
|
||||
studentmodule.save()
|
||||
|
||||
|
||||
def _fire_score_changed_for_block(course_id, student, block, module_state_key):
|
||||
"""
|
||||
Fires a SCORE_CHANGED event for the given module. The earned points are
|
||||
always zero. We must retrieve the possible points from the XModule, as
|
||||
noted below.
|
||||
"""
|
||||
if block and block.has_score:
|
||||
cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
course_id=course_id,
|
||||
user=student,
|
||||
descriptor=block,
|
||||
depth=0
|
||||
)
|
||||
# For implementation reasons, we need to pull the max_score from the XModule,
|
||||
# even though the data is not user-specific. Here we bind the data to the
|
||||
# current user.
|
||||
request = crum.get_current_request()
|
||||
module = get_module_for_descriptor(
|
||||
user=student,
|
||||
request=request,
|
||||
descriptor=block,
|
||||
field_data_cache=cache,
|
||||
course_key=course_id
|
||||
)
|
||||
points_earned, points_possible = weighted_score(0, module.max_score(), getattr(module, 'weight', None))
|
||||
else:
|
||||
points_earned, points_possible = 0, 0
|
||||
SCORE_CHANGED.send(
|
||||
sender=None,
|
||||
points_possible=points_possible,
|
||||
points_earned=points_earned,
|
||||
user=student,
|
||||
course_id=course_id,
|
||||
usage_id=module_state_key
|
||||
)
|
||||
|
||||
|
||||
def get_email_params(course, auto_enroll, secure=True, course_key=None, display_name=None):
|
||||
"""
|
||||
Generate parameters used when parsing email templates.
|
||||
|
||||
@@ -8,6 +8,7 @@ import mock
|
||||
from mock import patch
|
||||
from abc import ABCMeta
|
||||
from courseware.models import StudentModule
|
||||
from courseware.tests.helpers import get_request_for_user
|
||||
from django.conf import settings
|
||||
from django.utils.translation import get_language
|
||||
from django.utils.translation import override as override_language
|
||||
@@ -16,7 +17,11 @@ from ccx_keys.locator import CCXLocator
|
||||
from student.tests.factories import UserFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
|
||||
from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory
|
||||
from grades.new.subsection_grade import SubsectionGradeFactory
|
||||
from grades.tests.utils import answer_problem
|
||||
from lms.djangoapps.ccx.tests.factories import CcxFactory
|
||||
from lms.djangoapps.course_blocks.api import get_course_blocks
|
||||
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase
|
||||
from student.models import CourseEnrollment, CourseEnrollmentAllowed
|
||||
from student.roles import CourseCcxCoachRole
|
||||
@@ -494,6 +499,80 @@ class TestInstructorEnrollmentStudentModule(SharedModuleStoreTestCase):
|
||||
self.assertEqual(unrelated_state['brains'], 'zombie')
|
||||
|
||||
|
||||
class TestStudentModuleGrading(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Tests the effects of student module manipulations
|
||||
on student grades.
|
||||
"""
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestStudentModuleGrading, cls).setUpClass()
|
||||
cls.course = CourseFactory.create()
|
||||
cls.chapter = ItemFactory.create(
|
||||
parent=cls.course,
|
||||
category="chapter",
|
||||
display_name="Test Chapter"
|
||||
)
|
||||
cls.sequence = ItemFactory.create(
|
||||
parent=cls.chapter,
|
||||
category='sequential',
|
||||
display_name="Test Sequential 1",
|
||||
graded=True
|
||||
)
|
||||
cls.vertical = ItemFactory.create(
|
||||
parent=cls.sequence,
|
||||
category='vertical',
|
||||
display_name='Test Vertical 1'
|
||||
)
|
||||
problem_xml = MultipleChoiceResponseXMLFactory().build_xml(
|
||||
question_text='The correct answer is Choice 3',
|
||||
choices=[False, False, True, False],
|
||||
choice_names=['choice_0', 'choice_1', 'choice_2', 'choice_3']
|
||||
)
|
||||
cls.problem = ItemFactory.create(
|
||||
parent=cls.vertical,
|
||||
category="problem",
|
||||
display_name="Test Problem",
|
||||
data=problem_xml
|
||||
)
|
||||
cls.request = get_request_for_user(UserFactory())
|
||||
cls.user = cls.request.user
|
||||
|
||||
def _get_subsection_grade_and_verify(self, all_earned, all_possible, graded_earned, graded_possible):
|
||||
"""
|
||||
Retrieves the subsection grade and verifies that
|
||||
its scores match those expected.
|
||||
"""
|
||||
subsection_grade_factory = SubsectionGradeFactory(
|
||||
self.user,
|
||||
self.course,
|
||||
get_course_blocks(self.user, self.course.location)
|
||||
)
|
||||
grade = subsection_grade_factory.update(self.sequence)
|
||||
self.assertEqual(grade.all_total.earned, all_earned)
|
||||
self.assertEqual(grade.graded_total.earned, graded_earned)
|
||||
self.assertEqual(grade.all_total.possible, all_possible)
|
||||
self.assertEqual(grade.graded_total.possible, graded_possible)
|
||||
|
||||
@patch('crum.get_current_request')
|
||||
def test_delete_student_state(self, _crum_mock):
|
||||
problem_location = self.problem.location
|
||||
self._get_subsection_grade_and_verify(0, 1, 0, 1)
|
||||
answer_problem(course=self.course, request=self.request, problem=self.problem, score=1, max_value=1)
|
||||
self._get_subsection_grade_and_verify(1, 1, 1, 1)
|
||||
|
||||
# Delete student state using the instructor dash
|
||||
reset_student_attempts(
|
||||
self.course.id,
|
||||
self.user,
|
||||
problem_location,
|
||||
requesting_user=self.user,
|
||||
delete_module=True,
|
||||
)
|
||||
# Verify that the student's grades are reset
|
||||
self._get_subsection_grade_and_verify(0, 1, 0, 1)
|
||||
|
||||
|
||||
class EnrollmentObjects(object):
|
||||
"""
|
||||
Container for enrollment objects.
|
||||
|
||||
Reference in New Issue
Block a user