97 lines
3.2 KiB
Python
97 lines
3.2 KiB
Python
"""
|
|
Functionality for module-level grades.
|
|
"""
|
|
# TODO The score computation in this file is not accurate
|
|
# since it is summing percentages instead of computing a
|
|
# final percentage of the individual sums.
|
|
# Regardless, this file and its code should be removed soon
|
|
# as part of TNL-5062.
|
|
|
|
from django.test.client import RequestFactory
|
|
from courseware.model_data import FieldDataCache, ScoresClient
|
|
from courseware.module_render import get_module_for_descriptor
|
|
from opaque_keys.edx.locator import BlockUsageLocator
|
|
from util.module_utils import yield_dynamic_descriptor_descendants
|
|
|
|
|
|
def _get_mock_request(student):
|
|
"""
|
|
Make a fake request because grading code expects to be able to look at
|
|
the request. We have to attach the correct user to the request before
|
|
grading that student.
|
|
"""
|
|
request = RequestFactory().get('/')
|
|
request.user = student
|
|
return request
|
|
|
|
|
|
def _calculate_score_for_modules(user_id, course, modules):
|
|
"""
|
|
Calculates the cumulative score (percent) of the given modules
|
|
"""
|
|
|
|
# removing branch and version from exam modules locator
|
|
# otherwise student module would not return scores since module usage keys would not match
|
|
modules = [m for m in modules]
|
|
locations = [
|
|
BlockUsageLocator(
|
|
course_key=course.id,
|
|
block_type=module.location.block_type,
|
|
block_id=module.location.block_id
|
|
)
|
|
if isinstance(module.location, BlockUsageLocator) and module.location.version
|
|
else module.location
|
|
for module in modules
|
|
]
|
|
|
|
scores_client = ScoresClient(course.id, user_id)
|
|
scores_client.fetch_scores(locations)
|
|
|
|
# Iterate over all of the exam modules to get score percentage of user for each of them
|
|
module_percentages = []
|
|
ignore_categories = ['course', 'chapter', 'sequential', 'vertical', 'randomize', 'library_content']
|
|
for index, module in enumerate(modules):
|
|
if module.category not in ignore_categories and (module.graded or module.has_score):
|
|
module_score = scores_client.get(locations[index])
|
|
if module_score:
|
|
correct = module_score.correct or 0
|
|
total = module_score.total or 1
|
|
module_percentages.append(correct / total)
|
|
|
|
return sum(module_percentages) / float(len(module_percentages)) if module_percentages else 0
|
|
|
|
|
|
def get_module_score(user, course, module):
|
|
"""
|
|
Collects all children of the given module and calculates the cumulative
|
|
score for this set of modules for the given user.
|
|
|
|
Arguments:
|
|
user (User): The user
|
|
course (CourseModule): The course
|
|
module (XBlock): The module
|
|
|
|
Returns:
|
|
float: The cumulative score
|
|
"""
|
|
def inner_get_module(descriptor):
|
|
"""
|
|
Delegate to get_module_for_descriptor
|
|
"""
|
|
field_data_cache = FieldDataCache([descriptor], course.id, user)
|
|
return get_module_for_descriptor(
|
|
user,
|
|
_get_mock_request(user),
|
|
descriptor,
|
|
field_data_cache,
|
|
course.id,
|
|
course=course
|
|
)
|
|
|
|
modules = yield_dynamic_descriptor_descendants(
|
|
module,
|
|
user.id,
|
|
inner_get_module
|
|
)
|
|
return _calculate_score_for_modules(user.id, course, modules)
|