Commit 8b03ad39 by Matt Drayer

Decoupled entrance exam scoring from milestone fulfillment

parent a9127c18
......@@ -7,6 +7,8 @@ from django.conf import settings
from django.utils.translation import ugettext as _
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from courseware.models import StudentModule
from xmodule.modulestore.django import modulestore
from milestones.api import (
......@@ -148,6 +150,35 @@ def fulfill_course_milestone(course_key, user):
add_user_milestone({'id': user.id}, milestone)
def calculate_entrance_exam_score(user, course_descriptor, exam_modules):
"""
Calculates the score (percent) of the entrance exam using the provided modules
"""
exam_module_ids = [exam_module.location for exam_module in exam_modules]
student_modules = StudentModule.objects.filter(
student=user,
course_id=course_descriptor.id,
module_state_key__in=exam_module_ids,
)
exam_pct = 0
if student_modules:
module_pcts = []
ignore_categories = ['course', 'chapter', 'sequential', 'vertical']
for module in exam_modules:
if module.graded and module.category not in ignore_categories:
module_pct = 0
try:
student_module = student_modules.get(module_state_key=module.location)
if student_module.max_grade:
module_pct = student_module.grade / student_module.max_grade
module_pcts.append(module_pct)
except StudentModule.DoesNotExist:
pass
if module_pcts:
exam_pct = sum(module_pcts) / float(len(module_pcts))
return exam_pct
def milestones_achieved_by_user(user, namespace):
"""
It would fetch list of milestones completed by user
......
......@@ -15,6 +15,7 @@ import dogstats_wrapper as dog_stats_api
from courseware import courses
from courseware.model_data import FieldDataCache
from student.models import anonymous_id_for_user
from util.module_utils import yield_dynamic_descriptor_descendents
from xmodule import graders
from xmodule.graders import Score
from xmodule.modulestore.django import modulestore
......@@ -22,7 +23,6 @@ from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.util.duedate import get_extended_due_date
from .models import StudentModule
from .module_render import get_module_for_descriptor
from .module_utils import yield_dynamic_descriptor_descendents
from submissions import api as sub_api # installed from the edx-submissions repository
from opaque_keys import InvalidKeyError
......
......@@ -25,7 +25,6 @@ from courseware.models import StudentModule
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from lms.djangoapps.lms_xblock.runtime import LmsModuleSystem, unquote_slashes, quote_slashes
from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig
from .module_utils import yield_dynamic_descriptor_descendents
from edxmako.shortcuts import render_to_string
from eventtracking import tracker
from psychometrics.psychoanalyze import make_psychometrics_data_update_handler
......@@ -59,7 +58,8 @@ from util.sandboxing import can_execute_unsafe_code, get_python_lib_zip
if settings.FEATURES.get('MILESTONES_APP', False):
from milestones import api as milestones_api
from milestones.exceptions import InvalidMilestoneRelationshipTypeException
from util.milestones_helpers import serialize_user
from util.milestones_helpers import serialize_user, calculate_entrance_exam_score
from util.module_utils import yield_dynamic_descriptor_descendents
log = logging.getLogger(__name__)
......@@ -382,7 +382,21 @@ def get_module_system_for_user(user, field_data_cache,
request_token=request_token,
)
def _fulfill_content_milestones(course_key, content_key, user_id): # pylint: disable=unused-argument
def _calculate_entrance_exam_score(user, course_descriptor):
"""
Internal helper to calculate a user's score for a course's entrance exam
"""
exam_key = UsageKey.from_string(course_descriptor.entrance_exam_id)
exam_descriptor = modulestore().get_item(exam_key)
exam_module_generators = yield_dynamic_descriptor_descendents(
exam_descriptor,
inner_get_module
)
exam_modules = [module for module in exam_module_generators]
exam_score = calculate_entrance_exam_score(user, course_descriptor, exam_modules)
return exam_score
def _fulfill_content_milestones(user, course_key, content_key):
"""
Internal helper to handle milestone fulfillments for the specified content module
"""
......@@ -395,30 +409,9 @@ def get_module_system_for_user(user, field_data_cache,
entrance_exam_enabled = getattr(course, 'entrance_exam_enabled', False)
in_entrance_exam = getattr(content, 'in_entrance_exam', False)
if entrance_exam_enabled and in_entrance_exam:
exam_key = UsageKey.from_string(course.entrance_exam_id)
exam_descriptor = modulestore().get_item(exam_key)
exam_modules = yield_dynamic_descriptor_descendents(
exam_descriptor,
inner_get_module
)
ignore_categories = ['course', 'chapter', 'sequential', 'vertical']
module_pcts = []
exam_pct = 0
for module in exam_modules:
if module.graded and module.category not in ignore_categories:
module_pct = 0
try:
student_module = StudentModule.objects.get(
module_state_key=module.scope_ids.usage_id,
student_id=user_id
)
if student_module.max_grade:
module_pct = student_module.grade / student_module.max_grade
except StudentModule.DoesNotExist:
pass
module_pcts.append(module_pct)
exam_pct = sum(module_pcts) / float(len(module_pcts))
exam_pct = _calculate_entrance_exam_score(user, course)
if exam_pct >= course.entrance_exam_minimum_score_pct:
exam_key = UsageKey.from_string(course.entrance_exam_id)
relationship_types = milestones_api.get_milestone_relationship_types()
content_milestones = milestones_api.get_course_content_milestones(
course_key,
......@@ -426,7 +419,7 @@ def get_module_system_for_user(user, field_data_cache,
relationship=relationship_types['FULFILLS']
)
# Add each milestone to the user's set...
user = {'id': user_id}
user = {'id': user.id}
for milestone in content_milestones:
milestones_api.add_user_milestone(user, milestone)
......@@ -470,9 +463,9 @@ def get_module_system_for_user(user, field_data_cache,
# thanks to the updated grading information that was just submitted
if settings.FEATURES.get('MILESTONES_APP', False):
_fulfill_content_milestones(
user,
course_id,
descriptor.location,
user_id
)
def publish(block, event_type, event):
......
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