117 lines
4.8 KiB
Python
117 lines
4.8 KiB
Python
"""
|
|
API for the gating djangoapp
|
|
"""
|
|
from collections import defaultdict
|
|
from django.contrib.auth.models import User
|
|
from django.test.client import RequestFactory
|
|
import json
|
|
import logging
|
|
|
|
from openedx.core.lib.gating import api as gating_api
|
|
from opaque_keys.edx.keys import UsageKey
|
|
from lms.djangoapps.courseware.entrance_exams import get_entrance_exam_score
|
|
from lms.djangoapps.grades.module_grades import get_module_score
|
|
from util import milestones_helpers
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def _get_xblock_parent(xblock, category=None):
|
|
"""
|
|
Returns the parent of the given XBlock. If an optional category is supplied,
|
|
traverses the ancestors of the XBlock and returns the first with the
|
|
given category.
|
|
|
|
Arguments:
|
|
xblock (XBlock): Get the parent of this XBlock
|
|
category (str): Find an ancestor with this category (e.g. sequential)
|
|
"""
|
|
parent = xblock.get_parent()
|
|
if parent and category:
|
|
if parent.category == category:
|
|
return parent
|
|
else:
|
|
return _get_xblock_parent(parent, category)
|
|
return parent
|
|
|
|
|
|
@gating_api.gating_enabled(default=False)
|
|
def evaluate_prerequisite(course, block, user_id):
|
|
"""
|
|
Finds the parent subsection of the content in the course and evaluates
|
|
any milestone relationships attached to that subsection. If the calculated
|
|
grade of the prerequisite subsection meets the minimum score required by
|
|
dependent subsections, the related milestone will be fulfilled for the user.
|
|
|
|
Arguments:
|
|
course (CourseModule): The course
|
|
prereq_content_key (UsageKey): The prerequisite content usage key
|
|
user_id (int): ID of User for which evaluation should occur
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
sequential = _get_xblock_parent(block, 'sequential')
|
|
if sequential:
|
|
prereq_milestone = gating_api.get_gating_milestone(
|
|
course.id,
|
|
sequential.location.for_branch(None),
|
|
'fulfills'
|
|
)
|
|
if prereq_milestone:
|
|
gated_content_milestones = defaultdict(list)
|
|
for milestone in gating_api.find_gating_milestones(course.id, None, 'requires'):
|
|
gated_content_milestones[milestone['id']].append(milestone)
|
|
|
|
gated_content = gated_content_milestones.get(prereq_milestone['id'])
|
|
if gated_content:
|
|
user = User.objects.get(id=user_id)
|
|
score = get_module_score(user, course, sequential) * 100
|
|
for milestone in gated_content:
|
|
# Default minimum score to 100
|
|
min_score = 100
|
|
requirements = milestone.get('requirements')
|
|
if requirements:
|
|
try:
|
|
min_score = int(requirements.get('min_score'))
|
|
except (ValueError, TypeError):
|
|
log.warning(
|
|
'Failed to find minimum score for gating milestone %s, defaulting to 100',
|
|
json.dumps(milestone)
|
|
)
|
|
|
|
if score >= min_score:
|
|
milestones_helpers.add_user_milestone({'id': user_id}, prereq_milestone)
|
|
else:
|
|
milestones_helpers.remove_user_milestone({'id': user_id}, prereq_milestone)
|
|
|
|
|
|
def evaluate_entrance_exam(course, block, user_id):
|
|
"""
|
|
Update milestone fulfillments for the specified content module
|
|
"""
|
|
# Fulfillment Use Case: Entrance Exam
|
|
# If this module is part of an entrance exam, we'll need to see if the student
|
|
# has reached the point at which they can collect the associated milestone
|
|
if milestones_helpers.is_entrance_exams_enabled():
|
|
entrance_exam_enabled = getattr(course, 'entrance_exam_enabled', False)
|
|
in_entrance_exam = getattr(block, 'in_entrance_exam', False)
|
|
if entrance_exam_enabled and in_entrance_exam:
|
|
# We don't have access to the true request object in this context, but we can use a mock
|
|
request = RequestFactory().request()
|
|
request.user = User.objects.get(id=user_id)
|
|
exam_pct = get_entrance_exam_score(request, course)
|
|
if exam_pct >= course.entrance_exam_minimum_score_pct:
|
|
exam_key = UsageKey.from_string(course.entrance_exam_id)
|
|
relationship_types = milestones_helpers.get_milestone_relationship_types()
|
|
content_milestones = milestones_helpers.get_course_content_milestones(
|
|
course.id,
|
|
exam_key,
|
|
relationship=relationship_types['FULFILLS']
|
|
)
|
|
# Add each milestone to the user's set...
|
|
user = {'id': request.user.id}
|
|
for milestone in content_milestones:
|
|
milestones_helpers.add_user_milestone(user, milestone)
|