refactor: rename descriptor -> block within lms/djangoapps/courseware
Co-authored-by: Agrendalath <piotr@surowiec.it>
This commit is contained in:
committed by
Agrendalath
parent
79f67b9ce3
commit
c80fba689a
@@ -73,7 +73,7 @@ def has_ccx_coach_role(user, course_key):
|
||||
Check if user is a coach on this ccx.
|
||||
|
||||
Arguments:
|
||||
user (User): the user whose descriptor access we are checking.
|
||||
user (User): the user whose course access we are checking.
|
||||
course_key (CCXLocator): Key to CCX.
|
||||
|
||||
Returns:
|
||||
@@ -113,7 +113,7 @@ def has_access(user, action, obj, course_key=None):
|
||||
user: a Django user object. May be anonymous. If none is passed,
|
||||
anonymous is assumed
|
||||
|
||||
obj: The object to check access for. A block, descriptor, location, or
|
||||
obj: The object to check access for. A block, location, or
|
||||
certain special strings (e.g. 'global')
|
||||
|
||||
action: A string specifying the action that the client is trying to perform.
|
||||
@@ -146,11 +146,11 @@ def has_access(user, action, obj, course_key=None):
|
||||
return _has_access_course(user, action, obj)
|
||||
|
||||
if isinstance(obj, ErrorBlock):
|
||||
return _has_access_error_desc(user, action, obj, course_key)
|
||||
return _has_access_error_block(user, action, obj, course_key)
|
||||
|
||||
# NOTE: any descriptor access checkers need to go above this
|
||||
# NOTE: any block access checkers need to go above this
|
||||
if isinstance(obj, XBlock):
|
||||
return _has_access_descriptor(user, action, obj, course_key)
|
||||
return _has_access_to_block(user, action, obj, course_key)
|
||||
|
||||
if isinstance(obj, CourseKey):
|
||||
return _has_access_course_key(user, action, obj)
|
||||
@@ -200,7 +200,7 @@ def _can_view_courseware_with_prerequisites(user, course):
|
||||
|
||||
return (
|
||||
_is_prerequisites_disabled()
|
||||
or _has_staff_access_to_descriptor(user, course, course.id)
|
||||
or _has_staff_access_to_block(user, course, course.id)
|
||||
or user.is_anonymous
|
||||
or _has_fulfilled_prerequisites(user, [course.id])
|
||||
)
|
||||
@@ -226,7 +226,7 @@ def _can_load_course_on_mobile(user, course):
|
||||
return (
|
||||
is_mobile_available_for_user(user, course) and
|
||||
(
|
||||
_has_staff_access_to_descriptor(user, course, course.id) or
|
||||
_has_staff_access_to_block(user, course, course.id) or
|
||||
_has_fulfilled_all_milestones(user, course.id)
|
||||
)
|
||||
)
|
||||
@@ -244,13 +244,13 @@ def _can_enroll_courselike(user, courselike):
|
||||
Returns:
|
||||
AccessResponse, indicating whether the user can enroll.
|
||||
"""
|
||||
# Courselike objects (e.g., course descriptors and CourseOverviews) have an attribute named `id`
|
||||
# Courselike objects (CourseBlock and CourseOverview) have an attribute named `id`
|
||||
# which actually points to a CourseKey. Sigh.
|
||||
course_key = courselike.id
|
||||
|
||||
course_enrollment_open = courselike.is_enrollment_open()
|
||||
|
||||
user_has_staff_access = _has_staff_access_to_descriptor(user, courselike, course_key)
|
||||
user_has_staff_access = _has_staff_access_to_block(user, courselike, course_key)
|
||||
|
||||
# If the user appears in CourseEnrollmentAllowed paired with the given course key,
|
||||
# they may enroll, except if the CEA has already been used by a different user.
|
||||
@@ -331,14 +331,14 @@ def _has_access_course(user, action, courselike):
|
||||
# _can_view_courseware_with_prerequisites, user, courselike
|
||||
# )
|
||||
# ).or(
|
||||
# _has_staff_access_to_descriptor, user, courselike, courselike.id
|
||||
# _has_staff_access_to_block, user, courselike, courselike.id
|
||||
# )
|
||||
if courselike.id.deprecated: # we no longer support accessing Old Mongo courses
|
||||
return OldMongoAccessError(courselike)
|
||||
|
||||
visible_to_nonstaff = _visible_to_nonstaff_users(courselike)
|
||||
if not visible_to_nonstaff:
|
||||
staff_access = _has_staff_access_to_descriptor(user, courselike, courselike.id)
|
||||
staff_access = _has_staff_access_to_block(user, courselike, courselike.id)
|
||||
if staff_access:
|
||||
return staff_access
|
||||
else:
|
||||
@@ -346,7 +346,7 @@ def _has_access_course(user, action, courselike):
|
||||
|
||||
open_for_learner = check_course_open_for_learner(user, courselike)
|
||||
if not open_for_learner:
|
||||
staff_access = _has_staff_access_to_descriptor(user, courselike, courselike.id)
|
||||
staff_access = _has_staff_access_to_block(user, courselike, courselike.id)
|
||||
if staff_access:
|
||||
return staff_access
|
||||
else:
|
||||
@@ -354,7 +354,7 @@ def _has_access_course(user, action, courselike):
|
||||
|
||||
view_with_prereqs = _can_view_courseware_with_prerequisites(user, courselike)
|
||||
if not view_with_prereqs:
|
||||
staff_access = _has_staff_access_to_descriptor(user, courselike, courselike.id)
|
||||
staff_access = _has_staff_access_to_block(user, courselike, courselike.id)
|
||||
if staff_access:
|
||||
return staff_access
|
||||
else:
|
||||
@@ -362,7 +362,7 @@ def _has_access_course(user, action, courselike):
|
||||
|
||||
has_not_expired = check_course_expired(user, courselike)
|
||||
if not has_not_expired:
|
||||
staff_access = _has_staff_access_to_descriptor(user, courselike, courselike.id)
|
||||
staff_access = _has_staff_access_to_block(user, courselike, courselike.id)
|
||||
if staff_access:
|
||||
return staff_access
|
||||
else:
|
||||
@@ -389,25 +389,25 @@ def _has_access_course(user, action, courselike):
|
||||
def can_see_in_catalog():
|
||||
"""
|
||||
Implements the "can see course in catalog" logic if a course should be visible in the main course catalog
|
||||
In this case we use the catalog_visibility property on the course descriptor
|
||||
In this case we use the catalog_visibility property on the course block
|
||||
but also allow course staff to see this.
|
||||
"""
|
||||
return (
|
||||
_has_catalog_visibility(courselike, CATALOG_VISIBILITY_CATALOG_AND_ABOUT)
|
||||
or _has_staff_access_to_descriptor(user, courselike, courselike.id)
|
||||
or _has_staff_access_to_block(user, courselike, courselike.id)
|
||||
)
|
||||
|
||||
@function_trace('can_see_about_page')
|
||||
def can_see_about_page():
|
||||
"""
|
||||
Implements the "can see course about page" logic if a course about page should be visible
|
||||
In this case we use the catalog_visibility property on the course descriptor
|
||||
In this case we use the catalog_visibility property on the course block
|
||||
but also allow course staff to see this.
|
||||
"""
|
||||
return (
|
||||
_has_catalog_visibility(courselike, CATALOG_VISIBILITY_CATALOG_AND_ABOUT)
|
||||
or _has_catalog_visibility(courselike, CATALOG_VISIBILITY_ABOUT)
|
||||
or _has_staff_access_to_descriptor(user, courselike, courselike.id)
|
||||
or _has_staff_access_to_block(user, courselike, courselike.id)
|
||||
)
|
||||
|
||||
checkers = {
|
||||
@@ -415,8 +415,8 @@ def _has_access_course(user, action, courselike):
|
||||
'load_mobile': lambda: can_load() and _can_load_course_on_mobile(user, courselike),
|
||||
'enroll': can_enroll,
|
||||
'see_exists': see_exists,
|
||||
'staff': lambda: _has_staff_access_to_descriptor(user, courselike, courselike.id),
|
||||
'instructor': lambda: _has_instructor_access_to_descriptor(user, courselike, courselike.id),
|
||||
'staff': lambda: _has_staff_access_to_block(user, courselike, courselike.id),
|
||||
'instructor': lambda: _has_instructor_access_to_block(user, courselike, courselike.id),
|
||||
'see_in_catalog': can_see_in_catalog,
|
||||
'see_about_page': can_see_about_page,
|
||||
}
|
||||
@@ -424,30 +424,30 @@ def _has_access_course(user, action, courselike):
|
||||
return _dispatch(checkers, action, user, courselike)
|
||||
|
||||
|
||||
def _has_access_error_desc(user, action, descriptor, course_key):
|
||||
def _has_access_error_block(user, action, block, course_key):
|
||||
"""
|
||||
Only staff should see error descriptors.
|
||||
Only staff should see error blocks.
|
||||
|
||||
Valid actions:
|
||||
'load' -- load this descriptor, showing it to the user.
|
||||
'staff' -- staff access to descriptor.
|
||||
'load' -- load this block, showing it to the user.
|
||||
'staff' -- staff access to block.
|
||||
"""
|
||||
def check_for_staff():
|
||||
return _has_staff_access_to_descriptor(user, descriptor, course_key)
|
||||
return _has_staff_access_to_block(user, block, course_key)
|
||||
|
||||
checkers = {
|
||||
'load': check_for_staff,
|
||||
'staff': check_for_staff,
|
||||
'instructor': lambda: _has_instructor_access_to_descriptor(user, descriptor, course_key)
|
||||
'instructor': lambda: _has_instructor_access_to_block(user, block, course_key)
|
||||
}
|
||||
|
||||
return _dispatch(checkers, action, user, descriptor)
|
||||
return _dispatch(checkers, action, user, block)
|
||||
|
||||
|
||||
def _has_group_access(descriptor, user, course_key):
|
||||
def _has_group_access(block, user, course_key):
|
||||
"""
|
||||
This function returns a boolean indicating whether or not `user` has
|
||||
sufficient group memberships to "load" a block (the `descriptor`)
|
||||
sufficient group memberships to "load" a block
|
||||
"""
|
||||
# Allow staff and instructors roles group access, as they are not masquerading as a student.
|
||||
if get_user_role(user, course_key) in ['staff', 'instructor']:
|
||||
@@ -455,7 +455,7 @@ def _has_group_access(descriptor, user, course_key):
|
||||
|
||||
# use merged_group_access which takes group access on the block's
|
||||
# parents / ancestors into account
|
||||
merged_access = descriptor.merged_group_access
|
||||
merged_access = block.merged_group_access
|
||||
|
||||
# resolve the partition IDs in group_access to actual
|
||||
# partition objects, skipping those which contain empty group directives.
|
||||
@@ -465,7 +465,7 @@ def _has_group_access(descriptor, user, course_key):
|
||||
partitions = []
|
||||
for partition_id, group_ids in merged_access.items():
|
||||
try:
|
||||
partition = descriptor._get_user_partition(partition_id) # pylint: disable=protected-access
|
||||
partition = block._get_user_partition(partition_id) # pylint: disable=protected-access
|
||||
|
||||
# check for False in merged_access, which indicates that at least one
|
||||
# partition's group list excludes all students.
|
||||
@@ -509,7 +509,7 @@ def _has_group_access(descriptor, user, course_key):
|
||||
# If missing_groups is empty, the user is granted access.
|
||||
# If missing_groups is NOT empty, we generate an error based on one of the particular groups they are missing.
|
||||
missing_groups = []
|
||||
block_key = descriptor.scope_ids.usage_id
|
||||
block_key = block.scope_ids.usage_id
|
||||
for partition, groups in partition_groups:
|
||||
user_group = partition.scheme.get_group_for_user(
|
||||
course_key,
|
||||
@@ -522,7 +522,7 @@ def _has_group_access(descriptor, user, course_key):
|
||||
user_group,
|
||||
groups,
|
||||
partition.access_denied_message(block_key, user, user_group, groups),
|
||||
partition.access_denied_fragment(descriptor, user, user_group, groups),
|
||||
partition.access_denied_fragment(block, user, user_group, groups),
|
||||
))
|
||||
|
||||
if missing_groups:
|
||||
@@ -542,15 +542,15 @@ def _has_group_access(descriptor, user, course_key):
|
||||
return ACCESS_GRANTED
|
||||
|
||||
|
||||
def _has_access_descriptor(user, action, descriptor, course_key=None):
|
||||
def _has_access_to_block(user, action, block, course_key=None):
|
||||
"""
|
||||
Check if user has access to this descriptor.
|
||||
Check if user has access to this block.
|
||||
|
||||
Valid actions:
|
||||
'load' -- load this descriptor, showing it to the user.
|
||||
'staff' -- staff access to descriptor.
|
||||
'load' -- load this block, showing it to the user.
|
||||
'staff' -- staff access to block.
|
||||
|
||||
NOTE: This is the fallback logic for descriptors that don't have custom policy
|
||||
NOTE: This is the fallback logic for blocks that don't have custom policy
|
||||
(e.g. courses). If you call this method directly instead of going through
|
||||
has_access(), it will not do the right thing.
|
||||
"""
|
||||
@@ -562,26 +562,26 @@ def _has_access_descriptor(user, action, descriptor, course_key=None):
|
||||
don't have to hit the enrollments table on every block load.
|
||||
"""
|
||||
# If the user (or the role the user is currently masquerading as) does not have
|
||||
# access to this content, then deny access. The problem with calling _has_staff_access_to_descriptor
|
||||
# before this method is that _has_staff_access_to_descriptor short-circuits and returns True
|
||||
# access to this content, then deny access. The problem with calling _has_staff_access_to_block
|
||||
# before this method is that _has_staff_access_to_block short-circuits and returns True
|
||||
# for staff users in preview mode.
|
||||
group_access_response = _has_group_access(descriptor, user, course_key)
|
||||
group_access_response = _has_group_access(block, user, course_key)
|
||||
if not group_access_response:
|
||||
return group_access_response
|
||||
|
||||
# If the user has staff access, they can load the block and checks below are not needed.
|
||||
staff_access_response = _has_staff_access_to_descriptor(user, descriptor, course_key)
|
||||
staff_access_response = _has_staff_access_to_block(user, block, course_key)
|
||||
if staff_access_response:
|
||||
return staff_access_response
|
||||
|
||||
return (
|
||||
_visible_to_nonstaff_users(descriptor, display_error_to_user=False) and
|
||||
_visible_to_nonstaff_users(block, display_error_to_user=False) and
|
||||
(
|
||||
_has_detached_class_tag(descriptor) or
|
||||
_has_detached_class_tag(block) or
|
||||
check_start_date(
|
||||
user,
|
||||
descriptor.days_early_for_beta,
|
||||
descriptor.start,
|
||||
block.days_early_for_beta,
|
||||
block.start,
|
||||
course_key,
|
||||
display_error_to_user=False
|
||||
)
|
||||
@@ -590,11 +590,11 @@ def _has_access_descriptor(user, action, descriptor, course_key=None):
|
||||
|
||||
checkers = {
|
||||
'load': can_load,
|
||||
'staff': lambda: _has_staff_access_to_descriptor(user, descriptor, course_key),
|
||||
'instructor': lambda: _has_instructor_access_to_descriptor(user, descriptor, course_key)
|
||||
'staff': lambda: _has_staff_access_to_block(user, block, course_key),
|
||||
'instructor': lambda: _has_instructor_access_to_block(user, block, course_key)
|
||||
}
|
||||
|
||||
return _dispatch(checkers, action, user, descriptor)
|
||||
return _dispatch(checkers, action, user, block)
|
||||
|
||||
|
||||
def _has_access_location(user, action, location, course_key):
|
||||
@@ -762,53 +762,53 @@ def administrative_accesses_to_course_for_user(user, course_key):
|
||||
return global_staff, staff_access, instructor_access
|
||||
|
||||
|
||||
@function_trace('_has_instructor_access_to_descriptor')
|
||||
def _has_instructor_access_to_descriptor(user, descriptor, course_key):
|
||||
@function_trace('_has_instructor_access_to_block')
|
||||
def _has_instructor_access_to_block(user, block, course_key):
|
||||
"""Helper method that checks whether the user has staff access to
|
||||
the course of the location.
|
||||
|
||||
descriptor: something that has a location attribute
|
||||
block: something that has a location attribute
|
||||
"""
|
||||
return _has_instructor_access_to_location(user, descriptor.location, course_key)
|
||||
return _has_instructor_access_to_location(user, block.location, course_key)
|
||||
|
||||
|
||||
@function_trace('_has_staff_access_to_descriptor')
|
||||
def _has_staff_access_to_descriptor(user, descriptor, course_key):
|
||||
@function_trace('_has_staff_access_to_block')
|
||||
def _has_staff_access_to_block(user, block, course_key):
|
||||
"""Helper method that checks whether the user has staff access to
|
||||
the course of the location.
|
||||
|
||||
descriptor: something that has a location attribute
|
||||
block: something that has a location attribute
|
||||
"""
|
||||
return _has_staff_access_to_location(user, descriptor.location, course_key)
|
||||
return _has_staff_access_to_location(user, block.location, course_key)
|
||||
|
||||
|
||||
def _visible_to_nonstaff_users(descriptor, display_error_to_user=True):
|
||||
def _visible_to_nonstaff_users(block, display_error_to_user=True):
|
||||
"""
|
||||
Returns if the object is visible to nonstaff users.
|
||||
|
||||
Arguments:
|
||||
descriptor: object to check
|
||||
block: object to check
|
||||
display_error_to_user: If True, show an error message to the user say the content was hidden. Otherwise,
|
||||
hide the content silently.
|
||||
"""
|
||||
if descriptor.visible_to_staff_only:
|
||||
if block.visible_to_staff_only:
|
||||
return VisibilityError(display_error_to_user=display_error_to_user)
|
||||
else:
|
||||
return ACCESS_GRANTED
|
||||
|
||||
|
||||
def _can_access_descriptor_with_milestones(user, descriptor, course_key):
|
||||
def _can_access_block_with_milestones(user, block, course_key):
|
||||
"""
|
||||
Returns if the object is blocked by an unfulfilled milestone.
|
||||
|
||||
Args:
|
||||
user: the user trying to access this content
|
||||
descriptor: the object being accessed
|
||||
course_key: key for the course for this descriptor
|
||||
block: the object being accessed
|
||||
course_key: key for the course
|
||||
"""
|
||||
if milestones_helpers.get_course_content_milestones(
|
||||
course_key,
|
||||
str(descriptor.location),
|
||||
str(block.location),
|
||||
'requires',
|
||||
user.id
|
||||
):
|
||||
@@ -818,14 +818,14 @@ def _can_access_descriptor_with_milestones(user, descriptor, course_key):
|
||||
return ACCESS_GRANTED
|
||||
|
||||
|
||||
def _has_detached_class_tag(descriptor):
|
||||
def _has_detached_class_tag(block):
|
||||
"""
|
||||
Returns if the given descriptor's type is marked as detached.
|
||||
Returns if the given block's type is marked as detached.
|
||||
|
||||
Arguments:
|
||||
descriptor: object to check
|
||||
block: object to check
|
||||
"""
|
||||
return ACCESS_GRANTED if 'detached' in descriptor._class_tags else ACCESS_DENIED # pylint: disable=protected-access
|
||||
return ACCESS_GRANTED if 'detached' in block._class_tags else ACCESS_DENIED # pylint: disable=protected-access
|
||||
|
||||
|
||||
def _has_fulfilled_all_milestones(user, course_id):
|
||||
@@ -859,29 +859,29 @@ def _has_catalog_visibility(course, visibility_type):
|
||||
return ACCESS_GRANTED if course.catalog_visibility == visibility_type else ACCESS_DENIED
|
||||
|
||||
|
||||
def _is_descriptor_mobile_available(descriptor):
|
||||
def _is_block_mobile_available(block):
|
||||
"""
|
||||
Returns if descriptor is available on mobile.
|
||||
Returns if block is available on mobile.
|
||||
"""
|
||||
if IgnoreMobileAvailableFlagConfig.is_enabled() or descriptor.mobile_available:
|
||||
if IgnoreMobileAvailableFlagConfig.is_enabled() or block.mobile_available:
|
||||
return ACCESS_GRANTED
|
||||
else:
|
||||
return MobileAvailabilityError()
|
||||
|
||||
|
||||
def is_mobile_available_for_user(user, descriptor):
|
||||
def is_mobile_available_for_user(user, block):
|
||||
"""
|
||||
Returns whether the given course is mobile_available for the given user.
|
||||
Checks:
|
||||
mobile_available flag on the course
|
||||
Beta User and staff access overrides the mobile_available flag
|
||||
Arguments:
|
||||
descriptor (CourseBlock|CourseOverview): course or overview of course in question
|
||||
block (CourseBlock|CourseOverview): course or overview of course in question
|
||||
"""
|
||||
return (
|
||||
auth.user_has_role(user, CourseBetaTesterRole(descriptor.id))
|
||||
or _has_staff_access_to_descriptor(user, descriptor, descriptor.id)
|
||||
or _is_descriptor_mobile_available(descriptor)
|
||||
auth.user_has_role(user, CourseBetaTesterRole(block.id))
|
||||
or _has_staff_access_to_block(user, block, block.id)
|
||||
or _is_block_mobile_available(block)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -297,7 +297,7 @@ def get_block(user, request, usage_key, field_data_cache, position=None, log_if_
|
||||
XModule javascript to be bound correctly
|
||||
- depth : number of levels of descendents to cache when loading this module.
|
||||
None means cache all descendents
|
||||
- static_asset_path : static asset path to use (overrides descriptor's value); needed
|
||||
- static_asset_path : static asset path to use (overrides block's value); needed
|
||||
by get_course_info_section, because info section modules
|
||||
do not have a course as the parent module, and thus do not
|
||||
inherit this lms key value.
|
||||
@@ -310,8 +310,8 @@ def get_block(user, request, usage_key, field_data_cache, position=None, log_if_
|
||||
if possible. If not possible, return None.
|
||||
"""
|
||||
try:
|
||||
descriptor = modulestore().get_item(usage_key, depth=depth)
|
||||
return get_block_for_descriptor(user, request, descriptor, field_data_cache, usage_key.course_key,
|
||||
block = modulestore().get_item(usage_key, depth=depth)
|
||||
return get_block_for_descriptor(user, request, block, field_data_cache, usage_key.course_key,
|
||||
position=position,
|
||||
wrap_xblock_display=wrap_xblock_display,
|
||||
grade_bucket_type=grade_bucket_type,
|
||||
@@ -365,7 +365,7 @@ def display_access_messages(user, block, view, frag, context): # pylint: disabl
|
||||
|
||||
|
||||
# pylint: disable=too-many-statements
|
||||
def get_block_for_descriptor(user, request, descriptor, field_data_cache, course_key,
|
||||
def get_block_for_descriptor(user, request, block, field_data_cache, course_key,
|
||||
position=None, wrap_xblock_display=True, grade_bucket_type=None,
|
||||
static_asset_path='', disable_staff_debug_info=False,
|
||||
course=None, will_recheck_access=False):
|
||||
@@ -387,7 +387,7 @@ def get_block_for_descriptor(user, request, descriptor, field_data_cache, course
|
||||
|
||||
return get_block_for_descriptor_internal(
|
||||
user=user,
|
||||
descriptor=descriptor,
|
||||
block=block,
|
||||
student_data=student_data,
|
||||
course_id=course_key,
|
||||
track_function=track_function,
|
||||
@@ -469,17 +469,17 @@ def prepare_runtime_for_user(
|
||||
waittime=settings.XQUEUE_WAITTIME_BETWEEN_REQUESTS,
|
||||
)
|
||||
|
||||
def inner_get_block(descriptor):
|
||||
def inner_get_block(block):
|
||||
"""
|
||||
Delegate to get_block_for_descriptor_internal() with all values except `descriptor` set.
|
||||
Delegate to get_block_for_descriptor_internal() with all values except `block` set.
|
||||
|
||||
Because it does an access check, it may return None.
|
||||
"""
|
||||
# TODO: fix this so that make_xqueue_callback uses the descriptor passed into
|
||||
# TODO: fix this so that make_xqueue_callback uses the block passed into
|
||||
# inner_get_block, not the parent's callback. Add it as an argument....
|
||||
return get_block_for_descriptor_internal(
|
||||
user=user,
|
||||
descriptor=descriptor,
|
||||
block=block,
|
||||
student_data=student_data,
|
||||
course_id=course_id,
|
||||
track_function=track_function,
|
||||
@@ -648,7 +648,7 @@ def prepare_runtime_for_user(
|
||||
|
||||
# TODO: Find all the places that this method is called and figure out how to
|
||||
# get a loaded course passed into it
|
||||
def get_block_for_descriptor_internal(user, descriptor, student_data, course_id, track_function, request_token,
|
||||
def get_block_for_descriptor_internal(user, block, student_data, course_id, track_function, request_token,
|
||||
position=None, wrap_xblock_display=True, grade_bucket_type=None,
|
||||
static_asset_path='', user_location=None, disable_staff_debug_info=False,
|
||||
course=None, will_recheck_access=False):
|
||||
@@ -664,7 +664,7 @@ def get_block_for_descriptor_internal(user, descriptor, student_data, course_id,
|
||||
student_data = prepare_runtime_for_user(
|
||||
user=user,
|
||||
student_data=student_data, # These have implicit user bindings, the rest of args are considered not to
|
||||
block=descriptor,
|
||||
block=block,
|
||||
course_id=course_id,
|
||||
track_function=track_function,
|
||||
position=position,
|
||||
@@ -678,7 +678,7 @@ def get_block_for_descriptor_internal(user, descriptor, student_data, course_id,
|
||||
will_recheck_access=will_recheck_access,
|
||||
)
|
||||
|
||||
descriptor.bind_for_student(
|
||||
block.bind_for_student(
|
||||
user.id,
|
||||
[
|
||||
partial(DateLookupFieldData, course_id=course_id, user=user),
|
||||
@@ -687,16 +687,16 @@ def get_block_for_descriptor_internal(user, descriptor, student_data, course_id,
|
||||
],
|
||||
)
|
||||
|
||||
descriptor.scope_ids = descriptor.scope_ids._replace(user_id=user.id)
|
||||
block.scope_ids = block.scope_ids._replace(user_id=user.id)
|
||||
|
||||
# Do not check access when it's a noauth request.
|
||||
# Not that the access check needs to happen after the descriptor is bound
|
||||
# Not that the access check needs to happen after the block is bound
|
||||
# for the student, since there may be field override data for the student
|
||||
# that affects xblock visibility.
|
||||
user_needs_access_check = getattr(user, 'known', True) and not isinstance(user, SystemUser)
|
||||
if user_needs_access_check:
|
||||
access = has_access(user, 'load', descriptor, course_id)
|
||||
# A descriptor should only be returned if either the user has access, or the user doesn't have access, but
|
||||
access = has_access(user, 'load', block, course_id)
|
||||
# A block should only be returned if either the user has access, or the user doesn't have access, but
|
||||
# the failed access has a message for the user and the caller of this function specifies it will check access
|
||||
# again. This allows blocks to show specific error message or upsells when access is denied.
|
||||
caller_will_handle_access_error = (
|
||||
@@ -705,10 +705,10 @@ def get_block_for_descriptor_internal(user, descriptor, student_data, course_id,
|
||||
and (access.user_message or access.user_fragment)
|
||||
)
|
||||
if access or caller_will_handle_access_error:
|
||||
descriptor.has_access_error = bool(caller_will_handle_access_error)
|
||||
return descriptor
|
||||
block.has_access_error = bool(caller_will_handle_access_error)
|
||||
return block
|
||||
return None
|
||||
return descriptor
|
||||
return block
|
||||
|
||||
|
||||
def load_single_xblock(request, user_id, course_id, usage_key_string, course=None, will_recheck_access=False):
|
||||
@@ -719,7 +719,7 @@ def load_single_xblock(request, user_id, course_id, usage_key_string, course=Non
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
usage_key = usage_key.map_into_course(course_key)
|
||||
user = User.objects.get(id=user_id)
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
course_key,
|
||||
user,
|
||||
modulestore().get_item(usage_key),
|
||||
@@ -873,15 +873,15 @@ def _get_usage_key_for_course(course_key, usage_id) -> UsageKey:
|
||||
raise Http404("Invalid location") from exc
|
||||
|
||||
|
||||
def _get_descriptor_by_usage_key(usage_key):
|
||||
def _get_block_by_usage_key(usage_key):
|
||||
"""
|
||||
Gets a descriptor instance based on a mapped-to-course usage_key
|
||||
Gets a block instance based on a mapped-to-course usage_key
|
||||
|
||||
Returns (instance, tracking_context)
|
||||
"""
|
||||
try:
|
||||
descriptor = modulestore().get_item(usage_key)
|
||||
descriptor_orig_usage_key, descriptor_orig_version = modulestore().get_block_original_usage(usage_key)
|
||||
block = modulestore().get_item(usage_key)
|
||||
block_orig_usage_key, block_orig_version = modulestore().get_block_original_usage(usage_key)
|
||||
except ItemNotFoundError as exc:
|
||||
log.warning(
|
||||
"Invalid location for course id %s: %s",
|
||||
@@ -893,17 +893,17 @@ def _get_descriptor_by_usage_key(usage_key):
|
||||
tracking_context = {
|
||||
'module': {
|
||||
# xss-lint: disable=python-deprecated-display-name
|
||||
'display_name': descriptor.display_name_with_default_escaped,
|
||||
'usage_key': str(descriptor.location),
|
||||
'display_name': block.display_name_with_default_escaped,
|
||||
'usage_key': str(block.location),
|
||||
}
|
||||
}
|
||||
|
||||
# For blocks that are inherited from a content library, we add some additional metadata:
|
||||
if descriptor_orig_usage_key is not None:
|
||||
tracking_context['module']['original_usage_key'] = str(descriptor_orig_usage_key)
|
||||
tracking_context['module']['original_usage_version'] = str(descriptor_orig_version)
|
||||
if block_orig_usage_key is not None:
|
||||
tracking_context['module']['original_usage_key'] = str(block_orig_usage_key)
|
||||
tracking_context['module']['original_usage_version'] = str(block_orig_version)
|
||||
|
||||
return descriptor, tracking_context
|
||||
return block, tracking_context
|
||||
|
||||
|
||||
def get_block_by_usage_id(request, course_id, usage_id, disable_staff_debug_info=False, course=None,
|
||||
@@ -915,19 +915,19 @@ def get_block_by_usage_id(request, course_id, usage_id, disable_staff_debug_info
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
usage_key = _get_usage_key_for_course(course_key, usage_id)
|
||||
descriptor, tracking_context = _get_descriptor_by_usage_key(usage_key)
|
||||
block, tracking_context = _get_block_by_usage_key(usage_key)
|
||||
|
||||
_, user = setup_masquerade(request, course_key, has_access(request.user, 'staff', descriptor, course_key))
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
_, user = setup_masquerade(request, course_key, has_access(request.user, 'staff', block, course_key))
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
course_key,
|
||||
user,
|
||||
descriptor,
|
||||
block,
|
||||
read_only=CrawlersConfig.is_crawler(request),
|
||||
)
|
||||
instance = get_block_for_descriptor(
|
||||
user,
|
||||
request,
|
||||
descriptor,
|
||||
block,
|
||||
field_data_cache,
|
||||
usage_key.course_key,
|
||||
disable_staff_debug_info=disable_staff_debug_info,
|
||||
@@ -978,13 +978,13 @@ def _invoke_xblock_handler(request, course_id, usage_id, handler, suffix, course
|
||||
block_usage_key = usage_key
|
||||
|
||||
# Peek at the handler method to see if it actually wants to check access itself. (The handler may not want
|
||||
# inaccessible blocks stripped from the tree.) This ends up doing two modulestore lookups for the descriptor,
|
||||
# inaccessible blocks stripped from the tree.) This ends up doing two modulestore lookups for the block,
|
||||
# but the blocks should be available in the request cache the second time.
|
||||
# At the time of writing, this is only used by one handler. If this usage grows, we may want to re-evaluate
|
||||
# how we do this to something more elegant. If you are the author of a third party block that decides it wants
|
||||
# to set this too, please let us know so we can consider making this easier / better-documented.
|
||||
descriptor, _ = _get_descriptor_by_usage_key(block_usage_key)
|
||||
handler_method = getattr(descriptor, handler, False)
|
||||
block, _ = _get_block_by_usage_key(block_usage_key)
|
||||
handler_method = getattr(block, handler, False)
|
||||
will_recheck_access = handler_method and getattr(handler_method, 'will_recheck_access', False)
|
||||
|
||||
instance, tracking_context = get_block_by_usage_id(
|
||||
|
||||
@@ -76,7 +76,7 @@ _Assignment = namedtuple(
|
||||
|
||||
def get_course(course_id, depth=0):
|
||||
"""
|
||||
Given a course id, return the corresponding course descriptor.
|
||||
Given a course id, return the corresponding course block.
|
||||
|
||||
If the course does not exist, raises a CourseRunNotFound. This is appropriate
|
||||
for internal use.
|
||||
@@ -92,9 +92,9 @@ def get_course(course_id, depth=0):
|
||||
|
||||
def get_course_with_access(user, action, course_key, depth=0, check_if_enrolled=False, check_survey_complete=True, check_if_authenticated=False): # lint-amnesty, pylint: disable=line-too-long
|
||||
"""
|
||||
Given a course_key, look up the corresponding course descriptor,
|
||||
Given a course_key, look up the corresponding course block,
|
||||
check that the user has the access to perform the specified action
|
||||
on the course, and return the descriptor.
|
||||
on the course, and return the block.
|
||||
|
||||
Raises a 404 if the course_key is invalid, or the user doesn't have access.
|
||||
|
||||
@@ -857,26 +857,26 @@ def get_problems_in_section(section):
|
||||
"""
|
||||
This returns a dict having problems in a section.
|
||||
Returning dict has problem location as keys and problem
|
||||
descriptor as values.
|
||||
block as values.
|
||||
"""
|
||||
|
||||
problem_descriptors = defaultdict()
|
||||
problem_blocks = defaultdict()
|
||||
if not isinstance(section, UsageKey):
|
||||
section_key = UsageKey.from_string(section)
|
||||
else:
|
||||
section_key = section
|
||||
# it will be a Mongo performance boost, if you pass in a depth=3 argument here
|
||||
# as it will optimize round trips to the database to fetch all children for the current node
|
||||
section_descriptor = modulestore().get_item(section_key, depth=3)
|
||||
section_block = modulestore().get_item(section_key, depth=3)
|
||||
|
||||
# iterate over section, sub-section, vertical
|
||||
for subsection in section_descriptor.get_children():
|
||||
for subsection in section_block.get_children():
|
||||
for vertical in subsection.get_children():
|
||||
for component in vertical.get_children():
|
||||
if component.location.block_type == 'problem' and getattr(component, 'has_score', False):
|
||||
problem_descriptors[str(component.location)] = component
|
||||
problem_blocks[str(component.location)] = component
|
||||
|
||||
return problem_descriptors
|
||||
return problem_blocks
|
||||
|
||||
|
||||
def get_current_child(xblock, min_depth=None, requested_child=None):
|
||||
|
||||
@@ -17,7 +17,7 @@ from xmodule.modulestore.xml import XMLModuleStore
|
||||
|
||||
def traverse_tree(course):
|
||||
"""
|
||||
Load every descriptor in course. Return bool success value.
|
||||
Load every block in course. Return bool success value.
|
||||
"""
|
||||
queue = [course]
|
||||
while len(queue) > 0:
|
||||
|
||||
@@ -112,8 +112,8 @@ class MasqueradeView(View):
|
||||
group_id=None,
|
||||
user_name=None,
|
||||
)
|
||||
descriptor = modulestore().get_course(course_key)
|
||||
partitions = get_all_partitions_for_course(descriptor, active_only=True)
|
||||
block = modulestore().get_course(course_key)
|
||||
partitions = get_all_partitions_for_course(block, active_only=True)
|
||||
data = {
|
||||
'success': True,
|
||||
'active': {
|
||||
|
||||
@@ -51,30 +51,30 @@ class InvalidWriteError(Exception):
|
||||
"""
|
||||
|
||||
|
||||
def _all_usage_keys(descriptors, aside_types):
|
||||
def _all_usage_keys(blocks, aside_types):
|
||||
"""
|
||||
Return a set of all usage_ids for the `descriptors` and for
|
||||
as all asides in `aside_types` for those descriptors.
|
||||
Return a set of all usage_ids for the `blocks` and for
|
||||
as all asides in `aside_types` for those blocks.
|
||||
"""
|
||||
usage_ids = set()
|
||||
for descriptor in descriptors:
|
||||
usage_ids.add(descriptor.scope_ids.usage_id)
|
||||
for block in blocks:
|
||||
usage_ids.add(block.scope_ids.usage_id)
|
||||
|
||||
for aside_type in aside_types:
|
||||
usage_ids.add(AsideUsageKeyV1(descriptor.scope_ids.usage_id, aside_type))
|
||||
usage_ids.add(AsideUsageKeyV2(descriptor.scope_ids.usage_id, aside_type))
|
||||
usage_ids.add(AsideUsageKeyV1(block.scope_ids.usage_id, aside_type))
|
||||
usage_ids.add(AsideUsageKeyV2(block.scope_ids.usage_id, aside_type))
|
||||
|
||||
return usage_ids
|
||||
|
||||
|
||||
def _all_block_types(descriptors, aside_types):
|
||||
def _all_block_types(blocks, aside_types):
|
||||
"""
|
||||
Return a set of all block_types for the supplied `descriptors` and for
|
||||
the asides types in `aside_types` associated with those descriptors.
|
||||
Return a set of all block_types for the supplied `blocks` and for
|
||||
the asides types in `aside_types` associated with those blocks.
|
||||
"""
|
||||
block_types = set()
|
||||
for descriptor in descriptors:
|
||||
block_types.add(BlockTypeKeyV1(descriptor.entry_point, descriptor.scope_ids.block_type))
|
||||
for block in blocks:
|
||||
block_types.add(BlockTypeKeyV1(block.entry_point, block.scope_ids.block_type))
|
||||
|
||||
for aside_type in aside_types:
|
||||
block_types.add(BlockTypeKeyV1(XBlockAside.entry_point, aside_type))
|
||||
@@ -665,15 +665,15 @@ class FieldDataCache:
|
||||
A cache of django model objects needed to supply the data
|
||||
for a block and its descendants
|
||||
"""
|
||||
def __init__(self, descriptors, course_id, user, asides=None, read_only=False):
|
||||
def __init__(self, blocks, course_id, user, asides=None, read_only=False):
|
||||
"""
|
||||
Find any courseware.models objects that are needed by any descriptor
|
||||
in descriptors. Attempts to minimize the number of queries to the database.
|
||||
Find any courseware.models objects that are needed by any block
|
||||
in blocks. Attempts to minimize the number of queries to the database.
|
||||
Note: Only blocks that have store_state = True or have shared
|
||||
state will have a StudentModule.
|
||||
|
||||
Arguments
|
||||
descriptors: A list of XModuleDescriptors.
|
||||
blocks: A list of XBlocks.
|
||||
course_id: The id of the current course
|
||||
user: The user for which to cache data
|
||||
asides: The list of aside types to load, or None to prefetch no asides.
|
||||
@@ -705,84 +705,84 @@ class FieldDataCache:
|
||||
),
|
||||
}
|
||||
self.scorable_locations = set()
|
||||
self.add_descriptors_to_cache(descriptors)
|
||||
self.add_blocks_to_cache(blocks)
|
||||
|
||||
def add_descriptors_to_cache(self, descriptors):
|
||||
def add_blocks_to_cache(self, blocks):
|
||||
"""
|
||||
Add all `descriptors` to this FieldDataCache.
|
||||
Add all `blocks` to this FieldDataCache.
|
||||
"""
|
||||
if self.user.is_authenticated:
|
||||
self.scorable_locations.update(desc.location for desc in descriptors if desc.has_score)
|
||||
for scope, fields in self._fields_to_cache(descriptors).items():
|
||||
self.scorable_locations.update(block.location for block in blocks if block.has_score)
|
||||
for scope, fields in self._fields_to_cache(blocks).items():
|
||||
if scope not in self.cache:
|
||||
continue
|
||||
|
||||
self.cache[scope].cache_fields(fields, descriptors, self.asides)
|
||||
self.cache[scope].cache_fields(fields, blocks, self.asides)
|
||||
|
||||
def add_descriptor_descendents(self, descriptor, depth=None, descriptor_filter=lambda descriptor: True):
|
||||
def add_block_descendents(self, block, depth=None, block_filter=lambda block: True):
|
||||
"""
|
||||
Add all descendants of `descriptor` to this FieldDataCache.
|
||||
Add all descendants of `block` to this FieldDataCache.
|
||||
|
||||
Arguments:
|
||||
descriptor: An XModuleDescriptor
|
||||
block: An XBlock
|
||||
depth is the number of levels of descendant blocks to load StudentModules for, in addition to
|
||||
the supplied descriptor. If depth is None, load all descendant StudentModules
|
||||
descriptor_filter is a function that accepts a descriptor and return whether the field data
|
||||
the supplied block. If depth is None, load all descendant StudentModules
|
||||
block_filter is a function that accepts a block and return whether the field data
|
||||
should be cached
|
||||
"""
|
||||
|
||||
def get_child_descriptors(descriptor, depth, descriptor_filter):
|
||||
def get_child_blocks(block, depth, block_filter):
|
||||
"""
|
||||
Return a list of all child descriptors down to the specified depth
|
||||
that match the descriptor filter. Includes `descriptor`
|
||||
Return a list of all child blocks down to the specified depth
|
||||
that match the block filter. Includes `block`
|
||||
|
||||
descriptor: The parent to search inside
|
||||
block: The parent to search inside
|
||||
depth: The number of levels to descend, or None for infinite depth
|
||||
descriptor_filter(descriptor): A function that returns True
|
||||
if descriptor should be included in the results
|
||||
block_filter(block): A function that returns True
|
||||
if block should be included in the results
|
||||
"""
|
||||
if descriptor_filter(descriptor):
|
||||
descriptors = [descriptor]
|
||||
if block_filter(block):
|
||||
blocks = [block]
|
||||
else:
|
||||
descriptors = []
|
||||
blocks = []
|
||||
|
||||
if depth is None or depth > 0:
|
||||
new_depth = depth - 1 if depth is not None else depth
|
||||
|
||||
for child in descriptor.get_children() + descriptor.get_required_block_descriptors():
|
||||
descriptors.extend(get_child_descriptors(child, new_depth, descriptor_filter))
|
||||
for child in block.get_children() + block.get_required_block_descriptors():
|
||||
blocks.extend(get_child_blocks(child, new_depth, block_filter))
|
||||
|
||||
return descriptors
|
||||
return blocks
|
||||
|
||||
with modulestore().bulk_operations(descriptor.location.course_key):
|
||||
descriptors = get_child_descriptors(descriptor, depth, descriptor_filter)
|
||||
with modulestore().bulk_operations(block.location.course_key):
|
||||
blocks = get_child_blocks(block, depth, block_filter)
|
||||
|
||||
self.add_descriptors_to_cache(descriptors)
|
||||
self.add_blocks_to_cache(blocks)
|
||||
|
||||
@classmethod
|
||||
def cache_for_descriptor_descendents(cls, course_id, user, descriptor, depth=None,
|
||||
descriptor_filter=lambda descriptor: True,
|
||||
asides=None, read_only=False):
|
||||
def cache_for_block_descendents(cls, course_id, user, block, depth=None,
|
||||
block_filter=lambda block: True,
|
||||
asides=None, read_only=False):
|
||||
"""
|
||||
course_id: the course in the context of which we want StudentModules.
|
||||
user: the django user for whom to load modules.
|
||||
descriptor: An XModuleDescriptor
|
||||
block: An XBlock
|
||||
depth is the number of levels of descendant blocks to load StudentModules for, in addition to
|
||||
the supplied descriptor. If depth is None, load all descendant StudentModules
|
||||
descriptor_filter is a function that accepts a descriptor and return whether the field data
|
||||
the supplied block. If depth is None, load all descendant StudentModules
|
||||
block_filter is a function that accepts a block and return whether the field data
|
||||
should be cached
|
||||
"""
|
||||
cache = FieldDataCache([], course_id, user, asides=asides, read_only=read_only)
|
||||
cache.add_descriptor_descendents(descriptor, depth, descriptor_filter)
|
||||
cache.add_block_descendents(block, depth, block_filter)
|
||||
return cache
|
||||
|
||||
def _fields_to_cache(self, descriptors):
|
||||
def _fields_to_cache(self, blocks):
|
||||
"""
|
||||
Returns a map of scopes to fields in that scope that should be cached
|
||||
"""
|
||||
scope_map = defaultdict(set)
|
||||
for descriptor in descriptors:
|
||||
for field in descriptor.fields.values():
|
||||
for block in blocks:
|
||||
for field in block.fields.values():
|
||||
scope_map[field.scope].add(field)
|
||||
return scope_map
|
||||
|
||||
|
||||
@@ -85,20 +85,20 @@ class BaseTestXmodule(ModuleStoreTestCase):
|
||||
'category': self.CATEGORY
|
||||
})
|
||||
|
||||
self.item_descriptor = BlockFactory.create(**kwargs)
|
||||
self.block = BlockFactory.create(**kwargs)
|
||||
|
||||
self.runtime = self.new_descriptor_runtime()
|
||||
|
||||
field_data = {}
|
||||
field_data.update(self.MODEL_DATA)
|
||||
student_data = DictFieldData(field_data)
|
||||
self.item_descriptor._field_data = LmsFieldData(self.item_descriptor._field_data, student_data) # lint-amnesty, pylint: disable=protected-access
|
||||
self.block._field_data = LmsFieldData(self.block._field_data, student_data) # lint-amnesty, pylint: disable=protected-access
|
||||
|
||||
if runtime_kwargs is None:
|
||||
runtime_kwargs = {}
|
||||
self.new_module_runtime(runtime=self.item_descriptor.runtime, **runtime_kwargs)
|
||||
self.new_module_runtime(runtime=self.block.runtime, **runtime_kwargs)
|
||||
|
||||
self.item_url = str(self.item_descriptor.location)
|
||||
self.item_url = str(self.block.location)
|
||||
|
||||
def setup_course(self): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
self.course = CourseFactory.create(data=self.COURSE_DATA)
|
||||
|
||||
@@ -182,15 +182,15 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
|
||||
self.staff = GlobalStaffFactory()
|
||||
|
||||
def verify_access(self, mock_unit, student_should_have_access, expected_error_type=None):
|
||||
""" Verify the expected result from _has_access_descriptor """
|
||||
response = access._has_access_descriptor(self.anonymous_user, 'load', mock_unit, course_key=self.course.id)
|
||||
""" Verify the expected result from _has_access_to_block """
|
||||
response = access._has_access_to_block(self.anonymous_user, 'load', mock_unit, course_key=self.course.id)
|
||||
assert student_should_have_access == bool(response)
|
||||
|
||||
if expected_error_type is not None:
|
||||
assert isinstance(response, expected_error_type)
|
||||
assert response.to_json()['error_code'] is not None
|
||||
|
||||
assert access._has_access_descriptor(self.course_staff, 'load', mock_unit, course_key=self.course.id)
|
||||
assert access._has_access_to_block(self.course_staff, 'load', mock_unit, course_key=self.course.id)
|
||||
|
||||
def test_has_staff_access_to_preview_mode(self):
|
||||
"""
|
||||
@@ -356,31 +356,31 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
|
||||
('instructor', False, False, True)
|
||||
)
|
||||
@ddt.unpack
|
||||
def test__has_access_error_desc(self, action, expected_student, expected_staff, expected_instructor):
|
||||
descriptor = Mock()
|
||||
def test__has_access_error_block(self, action, expected_student, expected_staff, expected_instructor):
|
||||
block = Mock()
|
||||
|
||||
for (user, expected_response) in (
|
||||
(self.student, expected_student),
|
||||
(self.course_staff, expected_staff),
|
||||
(self.course_instructor, expected_instructor)
|
||||
):
|
||||
assert bool(access._has_access_error_desc(user, action, descriptor, self.course.id)) == expected_response
|
||||
assert bool(access._has_access_error_block(user, action, block, self.course.id)) == expected_response
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
access._has_access_error_desc(self.course_instructor, 'not_load_or_staff', descriptor, self.course.id)
|
||||
access._has_access_error_block(self.course_instructor, 'not_load_or_staff', block, self.course.id)
|
||||
|
||||
def test__has_access_descriptor(self):
|
||||
def test__has_access_to_block(self):
|
||||
# TODO: override DISABLE_START_DATES and test the start date branch of the method
|
||||
user = Mock()
|
||||
descriptor = Mock(user_partitions=[])
|
||||
descriptor._class_tags = {}
|
||||
descriptor.merged_group_access = {}
|
||||
block = Mock(user_partitions=[])
|
||||
block._class_tags = {}
|
||||
block.merged_group_access = {}
|
||||
|
||||
# Always returns true because DISABLE_START_DATES is set in test.py
|
||||
assert access._has_access_descriptor(user, 'load', descriptor)
|
||||
assert access._has_access_descriptor(user, 'instructor', descriptor)
|
||||
assert access._has_access_to_block(user, 'load', block)
|
||||
assert access._has_access_to_block(user, 'instructor', block)
|
||||
with pytest.raises(ValueError):
|
||||
access._has_access_descriptor(user, 'not_load_or_staff', descriptor)
|
||||
access._has_access_to_block(user, 'not_load_or_staff', block)
|
||||
|
||||
@ddt.data(
|
||||
(True, None, access_response.VisibilityError),
|
||||
@@ -392,20 +392,20 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
|
||||
)
|
||||
@ddt.unpack
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
|
||||
def test__has_access_descriptor_staff_lock(self, visible_to_staff_only, start, expected_error_type=None):
|
||||
def test__has_access_to_block_staff_lock(self, visible_to_staff_only, start, expected_error_type=None):
|
||||
"""
|
||||
Tests that "visible_to_staff_only" overrides start date.
|
||||
"""
|
||||
expected_access = expected_error_type is None
|
||||
mock_unit = Mock(location=self.course.location, user_partitions=[])
|
||||
mock_unit._class_tags = {} # Needed for detached check in _has_access_descriptor
|
||||
mock_unit._class_tags = {} # Needed for detached check in _has_access_to_block
|
||||
mock_unit.visible_to_staff_only = visible_to_staff_only
|
||||
mock_unit.start = self.DATES[start]
|
||||
mock_unit.merged_group_access = {}
|
||||
|
||||
self.verify_access(mock_unit, expected_access, expected_error_type)
|
||||
|
||||
def test__has_access_descriptor_beta_user(self):
|
||||
def test__has_access_to_block_beta_user(self):
|
||||
mock_unit = Mock(user_partitions=[])
|
||||
mock_unit._class_tags = {}
|
||||
mock_unit.days_early_for_beta = 2
|
||||
@@ -413,7 +413,7 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
|
||||
mock_unit.visible_to_staff_only = False
|
||||
mock_unit.merged_group_access = {}
|
||||
|
||||
assert bool(access._has_access_descriptor(self.beta_user, 'load', mock_unit, course_key=self.course.id))
|
||||
assert bool(access._has_access_to_block(self.beta_user, 'load', mock_unit, course_key=self.course.id))
|
||||
|
||||
@ddt.data(None, YESTERDAY, TOMORROW)
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
|
||||
@@ -421,12 +421,12 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
|
||||
'lms.djangoapps.courseware.access_utils.get_current_request_hostname',
|
||||
Mock(return_value='preview.localhost')
|
||||
)
|
||||
def test__has_access_descriptor_in_preview_mode(self, start):
|
||||
def test__has_access_to_block_in_preview_mode(self, start):
|
||||
"""
|
||||
Tests that descriptor has access in preview mode.
|
||||
Tests that block has access in preview mode.
|
||||
"""
|
||||
mock_unit = Mock(location=self.course.location, user_partitions=[])
|
||||
mock_unit._class_tags = {} # Needed for detached check in _has_access_descriptor
|
||||
mock_unit._class_tags = {} # Needed for detached check in _has_access_to_block
|
||||
mock_unit.visible_to_staff_only = False
|
||||
mock_unit.start = self.DATES[start]
|
||||
mock_unit.merged_group_access = {}
|
||||
@@ -441,13 +441,13 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
|
||||
@ddt.unpack
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISABLE_START_DATES': False})
|
||||
@patch('lms.djangoapps.courseware.access_utils.get_current_request_hostname', Mock(return_value='localhost'))
|
||||
def test__has_access_descriptor_when_not_in_preview_mode(self, start, expected_error_type):
|
||||
def test__has_access_to_block_when_not_in_preview_mode(self, start, expected_error_type):
|
||||
"""
|
||||
Tests that descriptor has no access when start date in future & without preview.
|
||||
Tests that block has no access when start date in future & without preview.
|
||||
"""
|
||||
expected_access = expected_error_type is None
|
||||
mock_unit = Mock(location=self.course.location, user_partitions=[])
|
||||
mock_unit._class_tags = {} # Needed for detached check in _has_access_descriptor
|
||||
mock_unit._class_tags = {} # Needed for detached check in _has_access_to_block
|
||||
mock_unit.visible_to_staff_only = False
|
||||
mock_unit.start = self.DATES[start]
|
||||
mock_unit.merged_group_access = {}
|
||||
@@ -663,12 +663,12 @@ class AccessTestCase(LoginEnrollmentTestCase, ModuleStoreTestCase, MilestonesTes
|
||||
"""
|
||||
Test course access on mobile for staff and students.
|
||||
"""
|
||||
descriptor = CourseFactory()
|
||||
descriptor.visible_to_staff_only = False
|
||||
descriptor.mobile_available = mobile_available
|
||||
course = CourseFactory()
|
||||
course.visible_to_staff_only = False
|
||||
course.mobile_available = mobile_available
|
||||
|
||||
assert bool(access._has_access_course(self.student, 'load_mobile', descriptor)) == student_expected
|
||||
assert bool(access._has_access_course(self.staff, 'load_mobile', descriptor)) == staff_expected
|
||||
assert bool(access._has_access_course(self.student, 'load_mobile', course)) == student_expected
|
||||
assert bool(access._has_access_course(self.staff, 'load_mobile', course)) == staff_expected
|
||||
|
||||
@patch.dict("django.conf.settings.FEATURES", {'ENABLE_PREREQUISITE_COURSES': True, 'MILESTONES_APP': True})
|
||||
def test_courseware_page_unfulfilled_prereqs(self):
|
||||
|
||||
@@ -251,7 +251,7 @@ class BlockRenderTestCase(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
|
||||
course = get_course_with_access(self.mock_user, 'load', self.course_key)
|
||||
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course_key, self.mock_user, course, depth=2)
|
||||
|
||||
block = render.get_block(
|
||||
@@ -411,14 +411,14 @@ class BlockRenderTestCase(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
request = self.request_factory.get('')
|
||||
request.user = self.mock_user
|
||||
course = CourseFactory()
|
||||
descriptor = BlockFactory(category=block_type, parent=course)
|
||||
field_data_cache = FieldDataCache([self.toy_course, descriptor], self.toy_course.id, self.mock_user)
|
||||
block = BlockFactory(category=block_type, parent=course)
|
||||
field_data_cache = FieldDataCache([self.toy_course, block], self.toy_course.id, self.mock_user)
|
||||
# This is verifying that caching doesn't cause an error during get_block_for_descriptor, which
|
||||
# is why it calls the method twice identically.
|
||||
render.get_block_for_descriptor(
|
||||
self.mock_user,
|
||||
request,
|
||||
descriptor,
|
||||
block,
|
||||
field_data_cache,
|
||||
self.toy_course.id,
|
||||
course=self.toy_course
|
||||
@@ -426,7 +426,7 @@ class BlockRenderTestCase(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
render.get_block_for_descriptor(
|
||||
self.mock_user,
|
||||
request,
|
||||
descriptor,
|
||||
block,
|
||||
field_data_cache,
|
||||
self.toy_course.id,
|
||||
course=self.toy_course
|
||||
@@ -442,7 +442,7 @@ class BlockRenderTestCase(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
@ddt.data('regular', 'test_aside')
|
||||
def test_rebind_different_users(self, block_category):
|
||||
"""
|
||||
This tests the rebinding a descriptor to a student does not result
|
||||
This tests the rebinding a block to a student does not result
|
||||
in overly nested _field_data.
|
||||
"""
|
||||
def create_aside(item, block_type):
|
||||
@@ -467,33 +467,33 @@ class BlockRenderTestCase(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
request.user = self.mock_user
|
||||
course = CourseFactory.create()
|
||||
|
||||
descriptor = BlockFactory(category="html", parent=course)
|
||||
block = BlockFactory(category="html", parent=course)
|
||||
if block_category == 'test_aside':
|
||||
descriptor = create_aside(descriptor, "test_aside")
|
||||
block = create_aside(block, "test_aside")
|
||||
|
||||
field_data_cache = FieldDataCache(
|
||||
[course, descriptor], course.id, self.mock_user
|
||||
[course, block], course.id, self.mock_user
|
||||
)
|
||||
|
||||
# grab what _field_data was originally set to
|
||||
original_field_data = descriptor._field_data # lint-amnesty, pylint: disable=no-member, protected-access
|
||||
original_field_data = block._field_data # lint-amnesty, pylint: disable=no-member, protected-access
|
||||
|
||||
render.get_block_for_descriptor(
|
||||
self.mock_user, request, descriptor, field_data_cache, course.id, course=course
|
||||
self.mock_user, request, block, field_data_cache, course.id, course=course
|
||||
)
|
||||
|
||||
# check that _unwrapped_field_data is the same as the original
|
||||
# _field_data, but now _field_data as been reset.
|
||||
# pylint: disable=protected-access
|
||||
assert descriptor._unwrapped_field_data is original_field_data # lint-amnesty, pylint: disable=no-member
|
||||
assert descriptor._unwrapped_field_data is not descriptor._field_data # lint-amnesty, pylint: disable=no-member
|
||||
assert block._unwrapped_field_data is original_field_data # lint-amnesty, pylint: disable=no-member
|
||||
assert block._unwrapped_field_data is not block._field_data # lint-amnesty, pylint: disable=no-member
|
||||
|
||||
# now bind this block to a few other students
|
||||
for user in [UserFactory(), UserFactory(), self.mock_user]:
|
||||
render.get_block_for_descriptor(
|
||||
user,
|
||||
request,
|
||||
descriptor,
|
||||
block,
|
||||
field_data_cache,
|
||||
course.id,
|
||||
course=course
|
||||
@@ -501,14 +501,14 @@ class BlockRenderTestCase(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
|
||||
# _field_data should now be wrapped by LmsFieldData
|
||||
# pylint: disable=protected-access
|
||||
assert isinstance(descriptor._field_data, LmsFieldData) # lint-amnesty, pylint: disable=no-member
|
||||
assert isinstance(block._field_data, LmsFieldData) # lint-amnesty, pylint: disable=no-member
|
||||
|
||||
# the LmsFieldData should now wrap OverrideFieldData
|
||||
assert isinstance(descriptor._field_data._authored_data._source, OverrideFieldData) # lint-amnesty, pylint: disable=no-member, line-too-long
|
||||
assert isinstance(block._field_data._authored_data._source, OverrideFieldData) # lint-amnesty, pylint: disable=no-member, line-too-long
|
||||
|
||||
# the OverrideFieldData should point to the date FieldData
|
||||
assert isinstance(descriptor._field_data._authored_data._source.fallback, DateLookupFieldData) # lint-amnesty, pylint: disable=no-member, line-too-long
|
||||
assert descriptor._field_data._authored_data._source.fallback._defaults is descriptor._unwrapped_field_data # lint-amnesty, pylint: disable=no-member, line-too-long
|
||||
assert isinstance(block._field_data._authored_data._source.fallback, DateLookupFieldData) # lint-amnesty, pylint: disable=no-member, line-too-long
|
||||
assert block._field_data._authored_data._source.fallback._defaults is block._unwrapped_field_data # lint-amnesty, pylint: disable=no-member, line-too-long
|
||||
|
||||
def test_hash_resource(self):
|
||||
"""
|
||||
@@ -903,17 +903,17 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas
|
||||
@patch('xmodule.services.grades_signals.SCORE_PUBLISHED.send')
|
||||
def test_anonymous_user_not_be_graded(self, mock_score_signal):
|
||||
course = CourseFactory.create()
|
||||
descriptor_kwargs = {
|
||||
block_kwargs = {
|
||||
'category': 'problem',
|
||||
}
|
||||
request = self.request_factory.get('/')
|
||||
request.user = AnonymousUser()
|
||||
descriptor = BlockFactory.create(**descriptor_kwargs)
|
||||
block = BlockFactory.create(**block_kwargs)
|
||||
|
||||
render.handle_xblock_callback(
|
||||
request,
|
||||
str(course.id),
|
||||
quote_slashes(str(descriptor.location)),
|
||||
quote_slashes(str(block.location)),
|
||||
'xmodule_handler',
|
||||
'problem_check',
|
||||
)
|
||||
@@ -929,12 +929,12 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas
|
||||
def test_will_recheck_access_handler_attribute(self, handler, will_recheck_access, mock_get_block):
|
||||
"""Confirm that we pay attention to any 'will_recheck_access' attributes on handler methods"""
|
||||
course = CourseFactory.create()
|
||||
descriptor_kwargs = {
|
||||
block_kwargs = {
|
||||
'category': 'sequential',
|
||||
'parent': course,
|
||||
}
|
||||
descriptor = BlockFactory.create(**descriptor_kwargs)
|
||||
usage_id = str(descriptor.location)
|
||||
block = BlockFactory.create(**block_kwargs)
|
||||
usage_id = str(block.location)
|
||||
|
||||
# Send no special parameters, which will be invalid, but we don't care
|
||||
request = self.request_factory.post('/', data='{}', content_type='application/json')
|
||||
@@ -1021,7 +1021,7 @@ class TestTOC(ModuleStoreTestCase):
|
||||
with self.modulestore.bulk_operations(self.course_key):
|
||||
with check_mongo_calls(num_finds, num_sends):
|
||||
self.toy_course = self.store.get_course(self.course_key, depth=2) # pylint: disable=attribute-defined-outside-init
|
||||
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents( # lint-amnesty, pylint: disable=attribute-defined-outside-init
|
||||
self.field_data_cache = FieldDataCache.cache_for_block_descendents( # lint-amnesty, pylint: disable=attribute-defined-outside-init
|
||||
self.course_key, self.request.user, self.toy_course, depth=2
|
||||
)
|
||||
|
||||
@@ -1113,7 +1113,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
self.modulestore = self.store._get_modulestore_for_courselike(self.course_key) # pylint: disable=protected-access
|
||||
with self.modulestore.bulk_operations(self.course_key):
|
||||
self.toy_course = self.store.get_course(self.course_key, depth=2)
|
||||
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
self.field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course_key, self.request.user, self.toy_course, depth=2
|
||||
)
|
||||
|
||||
@@ -1348,7 +1348,7 @@ class TestProctoringRendering(ModuleStoreTestCase):
|
||||
self.toy_course = self.update_course(self.toy_course, self.user.id)
|
||||
|
||||
# refresh cache after update
|
||||
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
self.field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course_key, self.request.user, self.toy_course, depth=2
|
||||
)
|
||||
|
||||
@@ -1440,7 +1440,7 @@ class TestGatedSubsectionRendering(ModuleStoreTestCase, MilestonesTestCaseMixin)
|
||||
|
||||
self.request = RequestFactoryNoCsrf().get(f'/courses/{self.course.id}/{self.chapter.display_name}')
|
||||
self.request.user = UserFactory()
|
||||
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
self.field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course.id, self.request.user, self.course, depth=2
|
||||
)
|
||||
gating_api.add_prerequisite(self.course.id, self.open_seq.location)
|
||||
@@ -1504,15 +1504,15 @@ class TestHtmlModifiers(ModuleStoreTestCase):
|
||||
self.rewrite_link = '<a href="/static/foo/content">Test rewrite</a>'
|
||||
self.rewrite_bad_link = '<img src="/static//file.jpg" />'
|
||||
self.course_link = '<a href="/course/bar/content">Test course rewrite</a>'
|
||||
self.descriptor = BlockFactory.create(
|
||||
self.block = BlockFactory.create(
|
||||
category='html',
|
||||
data=self.content_string + self.rewrite_link + self.rewrite_bad_link + self.course_link
|
||||
)
|
||||
self.location = self.descriptor.location
|
||||
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
self.location = self.block.location
|
||||
self.field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course.id,
|
||||
self.user,
|
||||
self.descriptor
|
||||
self.block
|
||||
)
|
||||
|
||||
def test_xblock_display_wrapper_enabled(self):
|
||||
@@ -1649,12 +1649,12 @@ class JsonInitDataTest(ModuleStoreTestCase):
|
||||
mock_request = MagicMock()
|
||||
mock_request.user = mock_user
|
||||
course = CourseFactory()
|
||||
descriptor = BlockFactory(category='withjson', parent=course)
|
||||
field_data_cache = FieldDataCache([course, descriptor], course.id, mock_user)
|
||||
block = BlockFactory(category='withjson', parent=course)
|
||||
field_data_cache = FieldDataCache([course, block], course.id, mock_user)
|
||||
block = render.get_block_for_descriptor(
|
||||
mock_user,
|
||||
mock_request,
|
||||
descriptor,
|
||||
block,
|
||||
field_data_cache,
|
||||
course.id,
|
||||
course=course
|
||||
@@ -1704,17 +1704,17 @@ class TestStaffDebugInfo(SharedModuleStoreTestCase):
|
||||
options=['Correct', 'Incorrect'],
|
||||
correct_option='Correct'
|
||||
)
|
||||
self.descriptor = BlockFactory.create(
|
||||
self.block = BlockFactory.create(
|
||||
category='problem',
|
||||
data=problem_xml,
|
||||
display_name='Option Response Problem'
|
||||
)
|
||||
|
||||
self.location = self.descriptor.location
|
||||
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
self.location = self.block.location
|
||||
self.field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course.id,
|
||||
self.user,
|
||||
self.descriptor
|
||||
self.block
|
||||
)
|
||||
|
||||
@patch.dict('django.conf.settings.FEATURES', {'DISPLAY_DEBUG_INFO_TO_STAFF': False})
|
||||
@@ -1757,14 +1757,14 @@ class TestStaffDebugInfo(SharedModuleStoreTestCase):
|
||||
</optionresponse>
|
||||
</problem>
|
||||
"""
|
||||
problem_descriptor = BlockFactory.create(
|
||||
problem_block = BlockFactory.create(
|
||||
category='problem',
|
||||
data=problem_xml
|
||||
)
|
||||
block = render.get_block(
|
||||
self.user,
|
||||
self.request,
|
||||
problem_descriptor.location,
|
||||
problem_block.location,
|
||||
self.field_data_cache
|
||||
)
|
||||
html_fragment = block.render(STUDENT_VIEW)
|
||||
@@ -1774,26 +1774,26 @@ class TestStaffDebugInfo(SharedModuleStoreTestCase):
|
||||
<label for="sd_fs_{block_id}"> / 0</label>
|
||||
</div>""")
|
||||
|
||||
assert expected_score_override_html.format(block_id=problem_descriptor.location.block_id) in\
|
||||
assert expected_score_override_html.format(block_id=problem_block.location.block_id) in\
|
||||
html_fragment.content
|
||||
|
||||
@XBlock.register_temp_plugin(DetachedXBlock, identifier='detached-block')
|
||||
def test_staff_debug_info_disabled_for_detached_blocks(self):
|
||||
"""Staff markup should not be present on detached blocks."""
|
||||
|
||||
descriptor = BlockFactory.create(
|
||||
detached_block = BlockFactory.create(
|
||||
category='detached-block',
|
||||
display_name='Detached Block'
|
||||
)
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course.id,
|
||||
self.user,
|
||||
descriptor
|
||||
detached_block
|
||||
)
|
||||
block = render.get_block(
|
||||
self.user,
|
||||
self.request,
|
||||
descriptor.location,
|
||||
detached_block.location,
|
||||
field_data_cache,
|
||||
)
|
||||
result_fragment = block.render(STUDENT_VIEW)
|
||||
@@ -1813,21 +1813,21 @@ class TestStaffDebugInfo(SharedModuleStoreTestCase):
|
||||
def test_histogram_enabled_for_unscored_xblocks(self):
|
||||
"""Histograms should not display for xblocks which are not scored."""
|
||||
|
||||
html_descriptor = BlockFactory.create(
|
||||
html_block = BlockFactory.create(
|
||||
category='html',
|
||||
data='Here are some course details.'
|
||||
)
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course.id,
|
||||
self.user,
|
||||
self.descriptor
|
||||
self.block
|
||||
)
|
||||
with patch('openedx.core.lib.xblock_utils.grade_histogram') as mock_grade_histogram:
|
||||
mock_grade_histogram.return_value = []
|
||||
block = render.get_block(
|
||||
self.user,
|
||||
self.request,
|
||||
html_descriptor.location,
|
||||
html_block.location,
|
||||
field_data_cache,
|
||||
)
|
||||
block.render(STUDENT_VIEW)
|
||||
@@ -1888,7 +1888,7 @@ class TestAnonymousStudentId(SharedModuleStoreTestCase, LoginEnrollmentTestCase)
|
||||
@patch('lms.djangoapps.courseware.block_render.has_access', Mock(return_value=True, autospec=True))
|
||||
def _get_anonymous_id(self, course_id, xblock_class): # lint-amnesty, pylint: disable=missing-function-docstring
|
||||
location = course_id.make_usage_key('dummy_category', 'dummy_name')
|
||||
descriptor = Mock(
|
||||
block = Mock(
|
||||
spec=xblock_class,
|
||||
_field_data=Mock(spec=FieldData, name='field_data'),
|
||||
location=location,
|
||||
@@ -1901,36 +1901,36 @@ class TestAnonymousStudentId(SharedModuleStoreTestCase, LoginEnrollmentTestCase)
|
||||
name='runtime',
|
||||
),
|
||||
scope_ids=Mock(spec=ScopeIds),
|
||||
name='descriptor',
|
||||
name='block',
|
||||
_field_data_cache={},
|
||||
_dirty_fields={},
|
||||
fields={},
|
||||
days_early_for_beta=None,
|
||||
)
|
||||
descriptor.runtime = DescriptorSystem(None, None, None)
|
||||
block.runtime = DescriptorSystem(None, None, None)
|
||||
# Use the xblock_class's bind_for_student method
|
||||
descriptor.bind_for_student = partial(xblock_class.bind_for_student, descriptor)
|
||||
block.bind_for_student = partial(xblock_class.bind_for_student, block)
|
||||
|
||||
if hasattr(xblock_class, 'module_class'):
|
||||
descriptor.module_class = xblock_class.module_class
|
||||
block.module_class = xblock_class.module_class
|
||||
|
||||
block = render.get_block_for_descriptor_internal(
|
||||
rendered_block = render.get_block_for_descriptor_internal(
|
||||
user=self.user,
|
||||
descriptor=descriptor,
|
||||
block=block,
|
||||
student_data=Mock(spec=FieldData, name='student_data'),
|
||||
course_id=course_id,
|
||||
track_function=Mock(name='track_function'), # Track Function
|
||||
request_token='request_token',
|
||||
course=self.course,
|
||||
)
|
||||
current_user = block.runtime.service(block, 'user').get_current_user()
|
||||
current_user = rendered_block.runtime.service(rendered_block, 'user').get_current_user()
|
||||
return current_user.opt_attrs.get(ATTR_KEY_ANONYMOUS_USER_ID)
|
||||
|
||||
@ddt.data(*PER_STUDENT_ANONYMIZED_XBLOCKS)
|
||||
def test_per_student_anonymized_id(self, descriptor_class):
|
||||
def test_per_student_anonymized_id(self, block_class):
|
||||
for course_id in ('MITx/6.00x/2012_Fall', 'MITx/6.00x/2013_Spring'):
|
||||
assert 'de619ab51c7f4e9c7216b4644c24f3b5' == \
|
||||
self._get_anonymous_id(CourseKey.from_string(course_id), descriptor_class)
|
||||
self._get_anonymous_id(CourseKey.from_string(course_id), block_class)
|
||||
|
||||
@ddt.data(*PER_COURSE_ANONYMIZED_XBLOCKS)
|
||||
def test_per_course_anonymized_id(self, xblock_class):
|
||||
@@ -2014,14 +2014,14 @@ class TestModuleTrackingContext(SharedModuleStoreTestCase):
|
||||
metadata from the emitted problem_check event.
|
||||
"""
|
||||
|
||||
descriptor_kwargs = {
|
||||
block_kwargs = {
|
||||
'category': 'problem',
|
||||
'data': self.problem_xml
|
||||
}
|
||||
if problem_display_name:
|
||||
descriptor_kwargs['display_name'] = problem_display_name
|
||||
block_kwargs['display_name'] = problem_display_name
|
||||
|
||||
descriptor = BlockFactory.create(**descriptor_kwargs)
|
||||
block = BlockFactory.create(**block_kwargs)
|
||||
mock_tracker_for_context = MagicMock()
|
||||
with patch('lms.djangoapps.courseware.block_render.tracker', mock_tracker_for_context), patch(
|
||||
'xmodule.services.tracker', mock_tracker_for_context
|
||||
@@ -2029,7 +2029,7 @@ class TestModuleTrackingContext(SharedModuleStoreTestCase):
|
||||
render.handle_xblock_callback(
|
||||
self.request,
|
||||
str(self.course.id),
|
||||
quote_slashes(str(descriptor.location)),
|
||||
quote_slashes(str(block.location)),
|
||||
'xmodule_handler',
|
||||
'problem_check',
|
||||
)
|
||||
@@ -2096,7 +2096,7 @@ class TestXBlockRuntimeEvent(TestSubmittingProblems):
|
||||
"""Helper function to get useful block at self.location in self.course_id for user"""
|
||||
mock_request = MagicMock()
|
||||
mock_request.user = user
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course.id, user, self.course, depth=2)
|
||||
|
||||
return render.get_block(
|
||||
@@ -2167,7 +2167,7 @@ class TestRebindBlock(TestSubmittingProblems):
|
||||
"""Helper function to get useful block at self.location in self.course_id for user"""
|
||||
mock_request = MagicMock()
|
||||
mock_request.user = user
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course.id, user, self.course, depth=2)
|
||||
|
||||
if item is None:
|
||||
@@ -2248,9 +2248,9 @@ class TestEventPublishing(ModuleStoreTestCase, LoginEnrollmentTestCase):
|
||||
request = self.request_factory.get('')
|
||||
request.user = self.mock_user
|
||||
course = CourseFactory()
|
||||
descriptor = BlockFactory(category='xblock', parent=course)
|
||||
field_data_cache = FieldDataCache([course, descriptor], course.id, self.mock_user)
|
||||
block = render.get_block(self.mock_user, request, descriptor.location, field_data_cache)
|
||||
block = BlockFactory(category='xblock', parent=course)
|
||||
field_data_cache = FieldDataCache([course, block], course.id, self.mock_user)
|
||||
block = render.get_block(self.mock_user, request, block.location, field_data_cache)
|
||||
|
||||
event_type = 'event_type'
|
||||
event = {'event': 'data'}
|
||||
@@ -2273,7 +2273,7 @@ class LMSXBlockServiceMixin(SharedModuleStoreTestCase):
|
||||
_ = render.prepare_runtime_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
@@ -2291,7 +2291,7 @@ class LMSXBlockServiceMixin(SharedModuleStoreTestCase):
|
||||
self.student_data = Mock()
|
||||
self.track_function = Mock()
|
||||
self.request_token = Mock()
|
||||
self.descriptor = BlockFactory(category="pure", parent=self.course)
|
||||
self.block = BlockFactory(category="pure", parent=self.course)
|
||||
self._prepare_runtime()
|
||||
|
||||
|
||||
@@ -2334,19 +2334,19 @@ class LMSXBlockServiceBindingTest(LMSXBlockServiceMixin):
|
||||
"""
|
||||
Tests that the 'user', 'i18n', and 'fs' services are provided by the LMS runtime.
|
||||
"""
|
||||
service = self.descriptor.runtime.service(self.descriptor, expected_service)
|
||||
service = self.block.runtime.service(self.block, expected_service)
|
||||
assert service is not None
|
||||
|
||||
def test_beta_tester_fields_added(self):
|
||||
"""
|
||||
Tests that the beta tester fields are set on LMS runtime.
|
||||
"""
|
||||
self.descriptor.days_early_for_beta = 5
|
||||
self.block.days_early_for_beta = 5
|
||||
self._prepare_runtime()
|
||||
|
||||
# pylint: disable=no-member
|
||||
assert not self.descriptor.runtime.user_is_beta_tester
|
||||
assert self.descriptor.runtime.days_early_for_beta == 5
|
||||
assert not self.block.runtime.user_is_beta_tester
|
||||
assert self.block.runtime.days_early_for_beta == 5
|
||||
|
||||
def test_get_set_tag(self):
|
||||
"""
|
||||
@@ -2356,23 +2356,23 @@ class LMSXBlockServiceBindingTest(LMSXBlockServiceMixin):
|
||||
key = 'key1'
|
||||
|
||||
# test for when we haven't set the tag yet
|
||||
tag = self.descriptor.runtime.service(self.descriptor, 'user_tags').get_tag(scope, key)
|
||||
tag = self.block.runtime.service(self.block, 'user_tags').get_tag(scope, key)
|
||||
assert tag is None
|
||||
|
||||
# set the tag
|
||||
set_value = 'value'
|
||||
self.descriptor.runtime.service(self.descriptor, 'user_tags').set_tag(scope, key, set_value)
|
||||
tag = self.descriptor.runtime.service(self.descriptor, 'user_tags').get_tag(scope, key)
|
||||
self.block.runtime.service(self.block, 'user_tags').set_tag(scope, key, set_value)
|
||||
tag = self.block.runtime.service(self.block, 'user_tags').get_tag(scope, key)
|
||||
|
||||
assert tag == set_value
|
||||
|
||||
# Try to set tag in wrong scope
|
||||
with pytest.raises(ValueError):
|
||||
self.descriptor.runtime.service(self.descriptor, 'user_tags').set_tag('fake_scope', key, set_value)
|
||||
self.block.runtime.service(self.block, 'user_tags').set_tag('fake_scope', key, set_value)
|
||||
|
||||
# Try to get tag in wrong scope
|
||||
with pytest.raises(ValueError):
|
||||
self.descriptor.runtime.service(self.descriptor, 'user_tags').get_tag('fake_scope', key)
|
||||
self.block.runtime.service(self.block, 'user_tags').get_tag('fake_scope', key)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -2382,23 +2382,23 @@ class TestBadgingService(LMSXBlockServiceMixin):
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True})
|
||||
def test_service_rendered(self):
|
||||
self._prepare_runtime()
|
||||
assert self.descriptor.runtime.service(self.descriptor, 'badging')
|
||||
assert self.block.runtime.service(self.block, 'badging')
|
||||
|
||||
def test_no_service_rendered(self):
|
||||
with pytest.raises(NoSuchServiceError):
|
||||
self.descriptor.runtime.service(self.descriptor, 'badging')
|
||||
self.block.runtime.service(self.block, 'badging')
|
||||
|
||||
@ddt.data(True, False)
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True})
|
||||
def test_course_badges_toggle(self, toggle):
|
||||
self.course = CourseFactory.create(metadata={'issue_badges': toggle})
|
||||
self._prepare_runtime()
|
||||
assert self.descriptor.runtime.service(self.descriptor, 'badging').course_badges_enabled is toggle
|
||||
assert self.block.runtime.service(self.block, 'badging').course_badges_enabled is toggle
|
||||
|
||||
@patch.dict(settings.FEATURES, {'ENABLE_OPENBADGES': True})
|
||||
def test_get_badge_class(self):
|
||||
self._prepare_runtime()
|
||||
badge_service = self.descriptor.runtime.service(self.descriptor, 'badging')
|
||||
badge_service = self.block.runtime.service(self.block, 'badging')
|
||||
premade_badge_class = BadgeClassFactory.create()
|
||||
# Ignore additional parameters. This class already exists.
|
||||
# We should get back the first class we created, rather than a new one.
|
||||
@@ -2422,7 +2422,7 @@ class TestI18nService(LMSXBlockServiceMixin):
|
||||
"""
|
||||
Test: module i18n service in LMS
|
||||
"""
|
||||
i18n_service = self.descriptor.runtime.service(self.descriptor, 'i18n')
|
||||
i18n_service = self.block.runtime.service(self.block, 'i18n')
|
||||
assert i18n_service is not None
|
||||
assert isinstance(i18n_service, XBlockI18nService)
|
||||
|
||||
@@ -2430,32 +2430,32 @@ class TestI18nService(LMSXBlockServiceMixin):
|
||||
"""
|
||||
Test: NoSuchServiceError should be raised block declaration returns none
|
||||
"""
|
||||
self.descriptor.service_declaration = Mock(return_value=None)
|
||||
self.block.service_declaration = Mock(return_value=None)
|
||||
with pytest.raises(NoSuchServiceError):
|
||||
self.descriptor.runtime.service(self.descriptor, 'i18n')
|
||||
self.block.runtime.service(self.block, 'i18n')
|
||||
|
||||
def test_no_service_exception_(self):
|
||||
"""
|
||||
Test: NoSuchServiceError should be raised if i18n service is none.
|
||||
"""
|
||||
i18nService = self.descriptor.runtime._services['i18n'] # pylint: disable=protected-access
|
||||
self.descriptor.runtime._runtime_services['i18n'] = None # pylint: disable=protected-access
|
||||
self.descriptor.runtime._services['i18n'] = None # pylint: disable=protected-access
|
||||
i18nService = self.block.runtime._services['i18n'] # pylint: disable=protected-access
|
||||
self.block.runtime._runtime_services['i18n'] = None # pylint: disable=protected-access
|
||||
self.block.runtime._services['i18n'] = None # pylint: disable=protected-access
|
||||
with pytest.raises(NoSuchServiceError):
|
||||
self.descriptor.runtime.service(self.descriptor, 'i18n')
|
||||
self.descriptor.runtime._services['i18n'] = i18nService # pylint: disable=protected-access
|
||||
self.block.runtime.service(self.block, 'i18n')
|
||||
self.block.runtime._services['i18n'] = i18nService # pylint: disable=protected-access
|
||||
|
||||
def test_i18n_service_callable(self):
|
||||
"""
|
||||
Test: _services dict should contain the callable i18n service in LMS.
|
||||
"""
|
||||
assert callable(self.descriptor.runtime._services.get('i18n')) # pylint: disable=protected-access
|
||||
assert callable(self.block.runtime._services.get('i18n')) # pylint: disable=protected-access
|
||||
|
||||
def test_i18n_service_not_callable(self):
|
||||
"""
|
||||
Test: i18n service should not be callable in LMS after initialization.
|
||||
"""
|
||||
assert not callable(self.descriptor.runtime.service(self.descriptor, 'i18n'))
|
||||
assert not callable(self.block.runtime.service(self.block, 'i18n'))
|
||||
|
||||
|
||||
class PureXBlockWithChildren(PureXBlock):
|
||||
@@ -2490,15 +2490,6 @@ class TestFilteredChildren(SharedModuleStoreTestCase):
|
||||
block = self._load_block()
|
||||
self.assertUnboundChildren(block)
|
||||
|
||||
@ddt.data(*USER_NUMBERS)
|
||||
@XBlock.register_temp_plugin(PureXBlockWithChildren, identifier='xblock')
|
||||
def test_unbound_then_bound_as_descriptor(self, user_number):
|
||||
user = self.users[user_number]
|
||||
block = self._load_block()
|
||||
self.assertUnboundChildren(block)
|
||||
self._bind_block(block, user)
|
||||
self.assertBoundChildren(block, user)
|
||||
|
||||
@ddt.data(*USER_NUMBERS)
|
||||
@XBlock.register_temp_plugin(PureXBlockWithChildren, identifier='xblock')
|
||||
def test_unbound_then_bound_as_xblock(self, user_number):
|
||||
@@ -2508,14 +2499,6 @@ class TestFilteredChildren(SharedModuleStoreTestCase):
|
||||
self._bind_block(block, user)
|
||||
self.assertBoundChildren(block, user)
|
||||
|
||||
@ddt.data(*USER_NUMBERS)
|
||||
@XBlock.register_temp_plugin(PureXBlockWithChildren, identifier='xblock')
|
||||
def test_bound_only_as_descriptor(self, user_number):
|
||||
user = self.users[user_number]
|
||||
block = self._load_block()
|
||||
self._bind_block(block, user)
|
||||
self.assertBoundChildren(block, user)
|
||||
|
||||
@ddt.data(*USER_NUMBERS)
|
||||
@XBlock.register_temp_plugin(PureXBlockWithChildren, identifier='xblock')
|
||||
def test_bound_only_as_xblock(self, user_number):
|
||||
@@ -2545,7 +2528,7 @@ class TestFilteredChildren(SharedModuleStoreTestCase):
|
||||
Bind `block` to the supplied `user`.
|
||||
"""
|
||||
course_id = self.course.id
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
course_id,
|
||||
user,
|
||||
block,
|
||||
@@ -2606,25 +2589,25 @@ class TestDisabledXBlockTypes(ModuleStoreTestCase):
|
||||
|
||||
def test_get_item(self):
|
||||
course = CourseFactory()
|
||||
self._verify_descriptor('video', course, 'HiddenBlockWithMixins')
|
||||
self._verify_block('video', course, 'HiddenBlockWithMixins')
|
||||
|
||||
def test_dynamic_updates(self):
|
||||
"""Tests that the list of disabled xblocks can dynamically update."""
|
||||
course = CourseFactory()
|
||||
item_usage_id = self._verify_descriptor('problem', course, 'ProblemBlockWithMixins')
|
||||
item_usage_id = self._verify_block('problem', course, 'ProblemBlockWithMixins')
|
||||
XBlockConfiguration(name='problem', enabled=False).save()
|
||||
|
||||
# First verify that the cached value is used until there is a new request cache.
|
||||
self._verify_descriptor('problem', course, 'ProblemBlockWithMixins', item_usage_id)
|
||||
self._verify_block('problem', course, 'ProblemBlockWithMixins', item_usage_id)
|
||||
|
||||
# Now simulate a new request cache.
|
||||
self.store.request_cache.data.clear()
|
||||
self._verify_descriptor('problem', course, 'HiddenBlockWithMixins', item_usage_id)
|
||||
self._verify_block('problem', course, 'HiddenBlockWithMixins', item_usage_id)
|
||||
|
||||
def _verify_descriptor(self, category, course, descriptor, item_id=None):
|
||||
def _verify_block(self, category, course, block, item_id=None):
|
||||
"""
|
||||
Helper method that gets an item with the specified category from the
|
||||
modulestore and verifies that it has the expected descriptor name.
|
||||
modulestore and verifies that it has the expected block name.
|
||||
|
||||
Returns the item's usage_id.
|
||||
"""
|
||||
@@ -2633,7 +2616,7 @@ class TestDisabledXBlockTypes(ModuleStoreTestCase):
|
||||
item_id = item.scope_ids.usage_id # lint-amnesty, pylint: disable=no-member
|
||||
|
||||
item = self.store.get_item(item_id)
|
||||
assert item.__class__.__name__ == descriptor
|
||||
assert item.__class__.__name__ == block
|
||||
return item_id
|
||||
|
||||
|
||||
@@ -2650,15 +2633,15 @@ class LmsModuleSystemShimTest(SharedModuleStoreTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""
|
||||
Set up the course and descriptor used to instantiate the runtime.
|
||||
Set up the course and block used to instantiate the runtime.
|
||||
"""
|
||||
super().setUpClass()
|
||||
org = 'edX'
|
||||
number = 'LmsModuleShimTest'
|
||||
run = '2021_Fall'
|
||||
cls.course = CourseFactory.create(org=org, number=number, run=run)
|
||||
cls.descriptor = BlockFactory(category="vertical", parent=cls.course)
|
||||
cls.problem_descriptor = BlockFactory(category="problem", parent=cls.course)
|
||||
cls.block = BlockFactory(category="vertical", parent=cls.course)
|
||||
cls.problem_block = BlockFactory(category="problem", parent=cls.course)
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
@@ -2673,7 +2656,7 @@ class LmsModuleSystemShimTest(SharedModuleStoreTestCase):
|
||||
_ = render.prepare_runtime_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
@@ -2690,37 +2673,37 @@ class LmsModuleSystemShimTest(SharedModuleStoreTestCase):
|
||||
"""
|
||||
Tests that the deprecated attributes provided by the user service match expected values.
|
||||
"""
|
||||
assert getattr(self.descriptor.runtime, attribute) == expected_value
|
||||
assert getattr(self.block.runtime, attribute) == expected_value
|
||||
|
||||
@patch('lms.djangoapps.courseware.block_render.has_access', Mock(return_value=True, autospec=True))
|
||||
def test_user_is_staff(self):
|
||||
_ = render.prepare_runtime_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
course=self.course,
|
||||
)
|
||||
assert self.descriptor.runtime.user_is_staff
|
||||
assert self.descriptor.runtime.get_user_role() == 'student'
|
||||
assert self.block.runtime.user_is_staff
|
||||
assert self.block.runtime.get_user_role() == 'student'
|
||||
|
||||
@patch('lms.djangoapps.courseware.block_render.get_user_role', Mock(return_value='instructor', autospec=True))
|
||||
def test_get_user_role(self):
|
||||
_ = render.prepare_runtime_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
course=self.course,
|
||||
)
|
||||
assert self.descriptor.runtime.get_user_role() == 'instructor'
|
||||
assert self.block.runtime.get_user_role() == 'instructor'
|
||||
|
||||
def test_anonymous_student_id(self):
|
||||
assert self.descriptor.runtime.anonymous_student_id == anonymous_id_for_user(self.user, self.course.id)
|
||||
assert self.block.runtime.anonymous_student_id == anonymous_id_for_user(self.user, self.course.id)
|
||||
|
||||
def test_anonymous_student_id_bug(self):
|
||||
"""
|
||||
@@ -2730,76 +2713,76 @@ class LmsModuleSystemShimTest(SharedModuleStoreTestCase):
|
||||
_ = render.prepare_runtime_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.problem_descriptor,
|
||||
self.problem_block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
course=self.course,
|
||||
)
|
||||
# Ensure the problem block returns a per-user anonymous id
|
||||
assert self.problem_descriptor.runtime.anonymous_student_id == anonymous_id_for_user(self.user, None)
|
||||
assert self.problem_block.runtime.anonymous_student_id == anonymous_id_for_user(self.user, None)
|
||||
|
||||
_ = render.prepare_runtime_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
course=self.course,
|
||||
)
|
||||
# Ensure the vertical block returns a per-course+user anonymous id
|
||||
assert self.descriptor.runtime.anonymous_student_id == anonymous_id_for_user(self.user, self.course.id)
|
||||
assert self.block.runtime.anonymous_student_id == anonymous_id_for_user(self.user, self.course.id)
|
||||
|
||||
# Ensure the problem runtime's anonymous student ID is unchanged after the above call.
|
||||
assert self.problem_descriptor.runtime.anonymous_student_id == anonymous_id_for_user(self.user, None)
|
||||
assert self.problem_block.runtime.anonymous_student_id == anonymous_id_for_user(self.user, None)
|
||||
|
||||
def test_user_service_with_anonymous_user(self):
|
||||
_ = render.prepare_runtime_for_user(
|
||||
AnonymousUser(),
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
course=self.course,
|
||||
)
|
||||
assert self.descriptor.runtime.anonymous_student_id is None
|
||||
assert self.descriptor.runtime.seed == 0
|
||||
assert self.descriptor.runtime.user_id is None
|
||||
assert not self.descriptor.runtime.user_is_staff
|
||||
assert not self.descriptor.runtime.get_user_role()
|
||||
assert self.block.runtime.anonymous_student_id is None
|
||||
assert self.block.runtime.seed == 0
|
||||
assert self.block.runtime.user_id is None
|
||||
assert not self.block.runtime.user_is_staff
|
||||
assert not self.block.runtime.get_user_role()
|
||||
|
||||
def test_get_real_user(self):
|
||||
_ = render.prepare_runtime_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
course=self.course,
|
||||
)
|
||||
course_anonymous_student_id = anonymous_id_for_user(self.user, self.course.id)
|
||||
assert self.descriptor.runtime.get_real_user(course_anonymous_student_id) == self.user # pylint: disable=not-callable
|
||||
assert self.block.runtime.get_real_user(course_anonymous_student_id) == self.user # pylint: disable=not-callable
|
||||
|
||||
no_course_anonymous_student_id = anonymous_id_for_user(self.user, None)
|
||||
assert self.descriptor.runtime.get_real_user(no_course_anonymous_student_id) == self.user # pylint: disable=not-callable
|
||||
assert self.block.runtime.get_real_user(no_course_anonymous_student_id) == self.user # pylint: disable=not-callable
|
||||
|
||||
# Tests that the default is to use the user service's anonymous_student_id
|
||||
assert self.descriptor.runtime.get_real_user() == self.user # pylint: disable=not-callable
|
||||
assert self.block.runtime.get_real_user() == self.user # pylint: disable=not-callable
|
||||
|
||||
def test_render_template(self):
|
||||
rendered = self.descriptor.runtime.render_template('templates/edxmako.html', {'element_id': 'hi'}) # pylint: disable=not-callable
|
||||
rendered = self.block.runtime.render_template('templates/edxmako.html', {'element_id': 'hi'}) # pylint: disable=not-callable
|
||||
assert rendered == '<div id="hi" ns="main">Testing the MakoService</div>\n'
|
||||
|
||||
def test_xqueue(self):
|
||||
xqueue = self.descriptor.runtime.xqueue
|
||||
xqueue = self.block.runtime.xqueue
|
||||
assert isinstance(xqueue['interface'], XQueueInterface)
|
||||
assert xqueue['interface'].url == 'http://sandbox-xqueue.edx.org'
|
||||
assert xqueue['default_queuename'] == 'edX-LmsModuleShimTest'
|
||||
assert xqueue['waittime'] == 5
|
||||
callback_url = f'http://localhost:8000/courses/{self.course.id}/xqueue/232/{self.descriptor.location}'
|
||||
callback_url = f'http://localhost:8000/courses/{self.course.id}/xqueue/232/{self.block.location}'
|
||||
assert xqueue['construct_callback']() == f'{callback_url}/score_update'
|
||||
assert xqueue['construct_callback']('mock_dispatch') == f'{callback_url}/mock_dispatch'
|
||||
|
||||
@@ -2819,31 +2802,31 @@ class LmsModuleSystemShimTest(SharedModuleStoreTestCase):
|
||||
_ = render.prepare_runtime_for_user(
|
||||
self.user,
|
||||
self.student_data,
|
||||
self.descriptor,
|
||||
self.block,
|
||||
self.course.id,
|
||||
self.track_function,
|
||||
self.request_token,
|
||||
course=self.course,
|
||||
)
|
||||
xqueue = self.descriptor.runtime.xqueue
|
||||
xqueue = self.block.runtime.xqueue
|
||||
assert isinstance(xqueue['interface'], XQueueInterface)
|
||||
assert xqueue['interface'].url == 'http://xqueue.url'
|
||||
assert xqueue['default_queuename'] == 'edX-LmsModuleShimTest'
|
||||
assert xqueue['waittime'] == 15
|
||||
callback_url = f'http://alt.url/courses/{self.course.id}/xqueue/232/{self.descriptor.location}'
|
||||
callback_url = f'http://alt.url/courses/{self.course.id}/xqueue/232/{self.block.location}'
|
||||
assert xqueue['construct_callback']() == f'{callback_url}/score_update'
|
||||
assert xqueue['construct_callback']('mock_dispatch') == f'{callback_url}/mock_dispatch'
|
||||
|
||||
@override_settings(COURSES_WITH_UNSAFE_CODE=[r'course-v1:edX\+LmsModuleShimTest\+2021_Fall'])
|
||||
def test_can_execute_unsafe_code_when_allowed(self):
|
||||
assert self.descriptor.runtime.can_execute_unsafe_code()
|
||||
assert self.block.runtime.can_execute_unsafe_code()
|
||||
|
||||
@override_settings(COURSES_WITH_UNSAFE_CODE=[r'course-v1:edX\+full\+2021_Fall'])
|
||||
def test_cannot_execute_unsafe_code_when_disallowed(self):
|
||||
assert not self.descriptor.runtime.can_execute_unsafe_code()
|
||||
assert not self.block.runtime.can_execute_unsafe_code()
|
||||
|
||||
def test_cannot_execute_unsafe_code(self):
|
||||
assert not self.descriptor.runtime.can_execute_unsafe_code()
|
||||
assert not self.block.runtime.can_execute_unsafe_code()
|
||||
|
||||
@override_settings(PYTHON_LIB_FILENAME=PYTHON_LIB_FILENAME)
|
||||
def test_get_python_lib_zip(self):
|
||||
@@ -2853,7 +2836,7 @@ class LmsModuleSystemShimTest(SharedModuleStoreTestCase):
|
||||
source_file=self.PYTHON_LIB_SOURCE_FILE,
|
||||
target_filename=self.PYTHON_LIB_FILENAME,
|
||||
)
|
||||
assert self.descriptor.runtime.get_python_lib_zip() == zipfile
|
||||
assert self.block.runtime.get_python_lib_zip() == zipfile
|
||||
|
||||
def test_no_get_python_lib_zip(self):
|
||||
zipfile = upload_file_to_course(
|
||||
@@ -2862,32 +2845,32 @@ class LmsModuleSystemShimTest(SharedModuleStoreTestCase):
|
||||
source_file=self.PYTHON_LIB_SOURCE_FILE,
|
||||
target_filename=self.PYTHON_LIB_FILENAME,
|
||||
)
|
||||
assert self.descriptor.runtime.get_python_lib_zip() is None
|
||||
assert self.block.runtime.get_python_lib_zip() is None
|
||||
|
||||
def test_cache(self):
|
||||
assert hasattr(self.descriptor.runtime.cache, 'get')
|
||||
assert hasattr(self.descriptor.runtime.cache, 'set')
|
||||
assert hasattr(self.block.runtime.cache, 'get')
|
||||
assert hasattr(self.block.runtime.cache, 'set')
|
||||
|
||||
def test_replace_urls(self):
|
||||
html = '<a href="/static/id">'
|
||||
assert self.descriptor.runtime.replace_urls(html) == \
|
||||
assert self.block.runtime.replace_urls(html) == \
|
||||
static_replace.replace_static_urls(html, course_id=self.course.id)
|
||||
|
||||
def test_replace_course_urls(self):
|
||||
html = '<a href="/course/id">'
|
||||
assert self.descriptor.runtime.replace_course_urls(html) == \
|
||||
assert self.block.runtime.replace_course_urls(html) == \
|
||||
static_replace.replace_course_urls(html, course_key=self.course.id)
|
||||
|
||||
def test_replace_jump_to_id_urls(self):
|
||||
html = '<a href="/jump_to_id/id">'
|
||||
jump_to_id_base_url = reverse('jump_to_id', kwargs={'course_id': str(self.course.id), 'module_id': ''})
|
||||
assert self.descriptor.runtime.replace_jump_to_id_urls(html) == \
|
||||
assert self.block.runtime.replace_jump_to_id_urls(html) == \
|
||||
static_replace.replace_jump_to_id_urls(html, self.course.id, jump_to_id_base_url)
|
||||
|
||||
@XBlock.register_temp_plugin(PureXBlock, 'pure')
|
||||
@XBlock.register_temp_plugin(PureXBlockWithChildren, identifier='xblock')
|
||||
def test_course_id(self):
|
||||
descriptor = BlockFactory(category="pure", parent=self.course)
|
||||
block = BlockFactory(category="pure", parent=self.course)
|
||||
|
||||
block = render.get_block(self.user, Mock(), descriptor.location, None)
|
||||
assert str(block.runtime.course_id) == self.COURSE_ID
|
||||
rendered_block = render.get_block(self.user, Mock(), block.location, None)
|
||||
assert str(rendered_block.runtime.course_id) == self.COURSE_ID
|
||||
|
||||
@@ -382,7 +382,7 @@ class CourseInstantiationTests(ModuleStoreTestCase):
|
||||
course = modulestore().get_course(course.id, depth=course_depth)
|
||||
|
||||
for _ in range(loops):
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
course.id, self.user, course, depth=course_depth
|
||||
)
|
||||
course_block = get_block_for_descriptor(
|
||||
|
||||
@@ -295,7 +295,7 @@ class TestXBlockInCourse(SharedModuleStoreTestCase):
|
||||
"""
|
||||
discussion_xblock = get_block_for_descriptor_internal(
|
||||
user=self.user,
|
||||
descriptor=self.discussion,
|
||||
block=self.discussion,
|
||||
student_data=mock.Mock(name='student_data'),
|
||||
course_id=self.course.id,
|
||||
track_function=mock.Mock(name='track_function'),
|
||||
@@ -335,10 +335,10 @@ class TestXBlockInCourse(SharedModuleStoreTestCase):
|
||||
assert orphan_sequential.location.block_type == root.location.block_type
|
||||
assert orphan_sequential.location.block_id == root.location.block_id
|
||||
|
||||
# Get xblock bound to a user and a descriptor.
|
||||
# Get xblock bound to a user and a block.
|
||||
discussion_xblock = get_block_for_descriptor_internal(
|
||||
user=self.user,
|
||||
descriptor=discussion,
|
||||
block=discussion,
|
||||
student_data=mock.Mock(name='student_data'),
|
||||
course_id=self.course.id,
|
||||
track_function=mock.Mock(name='track_function'),
|
||||
@@ -388,7 +388,7 @@ class TestXBlockInCourse(SharedModuleStoreTestCase):
|
||||
|
||||
discussion_xblock = get_block_for_descriptor_internal(
|
||||
user=self.user,
|
||||
descriptor=self.discussion,
|
||||
block=self.discussion,
|
||||
student_data=mock.Mock(name='student_data'),
|
||||
course_id=self.course.id,
|
||||
track_function=mock.Mock(name='track_function'),
|
||||
@@ -438,7 +438,7 @@ class TestXBlockQueryLoad(SharedModuleStoreTestCase):
|
||||
for discussion in discussions:
|
||||
discussion_xblock = get_block_for_descriptor_internal(
|
||||
user=user,
|
||||
descriptor=discussion,
|
||||
block=discussion,
|
||||
student_data=mock.Mock(name='student_data'),
|
||||
course_id=course.id,
|
||||
track_function=mock.Mock(name='track_function'),
|
||||
|
||||
@@ -340,7 +340,7 @@ class EntranceExamTestCases(LoginEnrollmentTestCase, ModuleStoreTestCase, Milest
|
||||
Returns the table of contents for course self.course, for chapter
|
||||
self.entrance_exam, and for section self.exam1
|
||||
"""
|
||||
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents( # pylint: disable=attribute-defined-outside-init
|
||||
self.field_data_cache = FieldDataCache.cache_for_block_descendents( # pylint: disable=attribute-defined-outside-init
|
||||
self.course.id,
|
||||
self.request.user,
|
||||
self.entrance_exam
|
||||
@@ -372,7 +372,7 @@ def answer_entrance_exam_problem(course, request, problem, user=None, value=1, m
|
||||
user = request.user
|
||||
|
||||
grade_dict = {'value': value, 'max_value': max_value, 'user_id': user.id}
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
course.id,
|
||||
user,
|
||||
course,
|
||||
|
||||
@@ -40,11 +40,11 @@ class TestLTI(BaseTestXmodule):
|
||||
mocked_decoded_signature = 'my_signature='
|
||||
|
||||
# Note: this course_id is actually a course_key
|
||||
context_id = str(self.item_descriptor.course_id)
|
||||
user_service = self.item_descriptor.runtime.service(self.item_descriptor, 'user')
|
||||
context_id = str(self.block.course_id)
|
||||
user_service = self.block.runtime.service(self.block, 'user')
|
||||
user_id = str(user_service.get_current_user().opt_attrs.get(ATTR_KEY_ANONYMOUS_USER_ID))
|
||||
hostname = settings.LMS_BASE
|
||||
resource_link_id = str(urllib.parse.quote(f'{hostname}-{self.item_descriptor.location.html_id()}'))
|
||||
resource_link_id = str(urllib.parse.quote(f'{hostname}-{self.block.location.html_id()}'))
|
||||
|
||||
sourcedId = "{context}:{resource_link}:{user_id}".format(
|
||||
context=urllib.parse.quote(context_id),
|
||||
@@ -75,14 +75,14 @@ class TestLTI(BaseTestXmodule):
|
||||
saved_sign = oauthlib.oauth1.Client.sign
|
||||
|
||||
self.expected_context = {
|
||||
'display_name': self.item_descriptor.display_name,
|
||||
'display_name': self.block.display_name,
|
||||
'input_fields': self.correct_headers,
|
||||
'element_class': self.item_descriptor.category,
|
||||
'element_id': self.item_descriptor.location.html_id(),
|
||||
'element_class': self.block.category,
|
||||
'element_id': self.block.location.html_id(),
|
||||
'launch_url': 'http://www.example.com', # default value
|
||||
'open_in_a_new_page': True,
|
||||
'form_url': self.item_descriptor.runtime.handler_url(
|
||||
self.item_descriptor,
|
||||
'form_url': self.block.runtime.handler_url(
|
||||
self.block,
|
||||
'preview_handler'
|
||||
).rstrip('/?'),
|
||||
'hide_launch': False,
|
||||
@@ -90,11 +90,11 @@ class TestLTI(BaseTestXmodule):
|
||||
'module_score': None,
|
||||
'comment': '',
|
||||
'weight': 1.0,
|
||||
'ask_to_send_username': self.item_descriptor.ask_to_send_username,
|
||||
'ask_to_send_email': self.item_descriptor.ask_to_send_email,
|
||||
'description': self.item_descriptor.description,
|
||||
'button_text': self.item_descriptor.button_text,
|
||||
'accept_grades_past_due': self.item_descriptor.accept_grades_past_due,
|
||||
'ask_to_send_username': self.block.ask_to_send_username,
|
||||
'ask_to_send_email': self.block.ask_to_send_email,
|
||||
'description': self.block.description,
|
||||
'button_text': self.block.button_text,
|
||||
'accept_grades_past_due': self.block.accept_grades_past_due,
|
||||
}
|
||||
|
||||
def mocked_sign(self, *args, **kwargs):
|
||||
@@ -117,12 +117,12 @@ class TestLTI(BaseTestXmodule):
|
||||
self.addCleanup(patcher.stop)
|
||||
|
||||
def test_lti_constructor(self):
|
||||
generated_content = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
generated_content = self.block.render(STUDENT_VIEW).content
|
||||
expected_content = self.runtime.render_template('lti.html', self.expected_context)
|
||||
assert generated_content == expected_content
|
||||
|
||||
def test_lti_preview_handler(self):
|
||||
generated_content = self.item_descriptor.preview_handler(None, None).body
|
||||
generated_content = self.block.preview_handler(None, None).body
|
||||
expected_content = self.runtime.render_template('lti_form.html', self.expected_context)
|
||||
assert generated_content.decode('utf-8') == expected_content
|
||||
|
||||
|
||||
@@ -35,13 +35,13 @@ def mock_field(scope, name):
|
||||
return field
|
||||
|
||||
|
||||
def mock_descriptor(fields=[]): # lint-amnesty, pylint: disable=dangerous-default-value, missing-function-docstring
|
||||
descriptor = Mock(entry_point=XBlock.entry_point)
|
||||
descriptor.scope_ids = ScopeIds('user1', 'mock_problem', LOCATION('def_id'), LOCATION('usage_id'))
|
||||
descriptor.module_class.fields.values.return_value = fields
|
||||
descriptor.fields.values.return_value = fields
|
||||
descriptor.module_class.__name__ = 'MockProblemModule'
|
||||
return descriptor
|
||||
def mock_block(fields=[]): # lint-amnesty, pylint: disable=dangerous-default-value, missing-function-docstring
|
||||
block = Mock(entry_point=XBlock.entry_point)
|
||||
block.scope_ids = ScopeIds('user1', 'mock_problem', LOCATION('def_id'), LOCATION('usage_id'))
|
||||
block.module_class.fields.values.return_value = fields
|
||||
block.fields.values.return_value = fields
|
||||
block.module_class.__name__ = 'MockProblemModule'
|
||||
return block
|
||||
|
||||
# The user ids here are 1 because we make a student in the setUp functions, and
|
||||
# they get an id of 1. There's an assertion in setUp to ensure that assumption
|
||||
@@ -63,7 +63,7 @@ class TestInvalidScopes(TestCase): # lint-amnesty, pylint: disable=missing-clas
|
||||
super().setUp()
|
||||
self.user = UserFactory.create(username='user')
|
||||
self.field_data_cache = FieldDataCache(
|
||||
[mock_descriptor([mock_field(Scope.user_state, 'a_field')])],
|
||||
[mock_block([mock_field(Scope.user_state, 'a_field')])],
|
||||
COURSE_KEY,
|
||||
self.user,
|
||||
)
|
||||
@@ -120,10 +120,10 @@ class TestStudentModuleStorage(OtherUserFailureTestMixin, TestCase):
|
||||
assert self.user.id == 1
|
||||
# check our assumption hard-coded in the key functions above.
|
||||
|
||||
# There should be only one query to load a single descriptor with a single user_state field
|
||||
# There should be only one query to load a single block with a single user_state field
|
||||
with self.assertNumQueries(1):
|
||||
self.field_data_cache = FieldDataCache(
|
||||
[mock_descriptor([mock_field(Scope.user_state, 'a_field')])],
|
||||
[mock_block([mock_field(Scope.user_state, 'a_field')])],
|
||||
COURSE_KEY,
|
||||
self.user,
|
||||
)
|
||||
@@ -250,10 +250,10 @@ class TestMissingStudentModule(TestCase): # lint-amnesty, pylint: disable=missi
|
||||
assert self.user.id == 1
|
||||
# check our assumption hard-coded in the key functions above.
|
||||
|
||||
# The descriptor has no fields, so FDC shouldn't send any queries
|
||||
# The block has no fields, so FDC shouldn't send any queries
|
||||
with self.assertNumQueries(0):
|
||||
self.field_data_cache = FieldDataCache(
|
||||
[mock_descriptor()],
|
||||
[mock_block()],
|
||||
COURSE_KEY,
|
||||
self.user,
|
||||
)
|
||||
@@ -318,14 +318,14 @@ class StorageTestBase:
|
||||
self.user = field_storage.student
|
||||
else:
|
||||
self.user = UserFactory.create()
|
||||
self.mock_descriptor = mock_descriptor([
|
||||
self.mock_block = mock_block([
|
||||
mock_field(self.scope, 'existing_field'),
|
||||
mock_field(self.scope, 'other_existing_field')])
|
||||
# Each field is stored as a separate row in the table,
|
||||
# but we can query them in a single query
|
||||
with self.assertNumQueries(1):
|
||||
self.field_data_cache = FieldDataCache(
|
||||
[self.mock_descriptor],
|
||||
[self.mock_block],
|
||||
COURSE_KEY,
|
||||
self.user,
|
||||
)
|
||||
|
||||
@@ -172,12 +172,12 @@ class TestVideo(BaseTestVideoXBlock):
|
||||
assert status_codes.pop() == 404
|
||||
|
||||
def test_handle_ajax_for_speed_with_nan(self):
|
||||
self.item_descriptor.handle_ajax('save_user_state', {'speed': json.dumps(1.0)})
|
||||
assert self.item_descriptor.speed == 1.0
|
||||
assert self.item_descriptor.global_speed == 1.0
|
||||
self.block.handle_ajax('save_user_state', {'speed': json.dumps(1.0)})
|
||||
assert self.block.speed == 1.0
|
||||
assert self.block.global_speed == 1.0
|
||||
|
||||
# try to set NaN value for speed.
|
||||
response = self.item_descriptor.handle_ajax(
|
||||
response = self.block.handle_ajax(
|
||||
'save_user_state', {'speed': json.dumps(float('NaN'))}
|
||||
)
|
||||
|
||||
@@ -186,8 +186,8 @@ class TestVideo(BaseTestVideoXBlock):
|
||||
assert json.loads(response)['error'] == expected_error
|
||||
|
||||
# verify that the speed and global speed are still 1.0
|
||||
assert self.item_descriptor.speed == 1.0
|
||||
assert self.item_descriptor.global_speed == 1.0
|
||||
assert self.block.speed == 1.0
|
||||
assert self.block.global_speed == 1.0
|
||||
|
||||
def test_handle_ajax(self):
|
||||
|
||||
@@ -206,41 +206,41 @@ class TestVideo(BaseTestVideoXBlock):
|
||||
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
assert response.status_code == 200
|
||||
|
||||
assert self.item_descriptor.speed is None
|
||||
self.item_descriptor.handle_ajax('save_user_state', {'speed': json.dumps(2.0)})
|
||||
assert self.item_descriptor.speed == 2.0
|
||||
assert self.item_descriptor.global_speed == 2.0
|
||||
assert self.block.speed is None
|
||||
self.block.handle_ajax('save_user_state', {'speed': json.dumps(2.0)})
|
||||
assert self.block.speed == 2.0
|
||||
assert self.block.global_speed == 2.0
|
||||
|
||||
assert self.item_descriptor.saved_video_position == timedelta(0)
|
||||
self.item_descriptor.handle_ajax('save_user_state', {'saved_video_position': "00:00:10"})
|
||||
assert self.item_descriptor.saved_video_position == timedelta(0, 10)
|
||||
assert self.block.saved_video_position == timedelta(0)
|
||||
self.block.handle_ajax('save_user_state', {'saved_video_position': "00:00:10"})
|
||||
assert self.block.saved_video_position == timedelta(0, 10)
|
||||
|
||||
assert self.item_descriptor.transcript_language == 'en'
|
||||
self.item_descriptor.handle_ajax('save_user_state', {'transcript_language': "uk"})
|
||||
assert self.item_descriptor.transcript_language == 'uk'
|
||||
assert self.block.transcript_language == 'en'
|
||||
self.block.handle_ajax('save_user_state', {'transcript_language': "uk"})
|
||||
assert self.block.transcript_language == 'uk'
|
||||
|
||||
assert self.item_descriptor.bumper_do_not_show_again is False
|
||||
self.item_descriptor.handle_ajax('save_user_state', {'bumper_do_not_show_again': True})
|
||||
assert self.item_descriptor.bumper_do_not_show_again is True
|
||||
assert self.block.bumper_do_not_show_again is False
|
||||
self.block.handle_ajax('save_user_state', {'bumper_do_not_show_again': True})
|
||||
assert self.block.bumper_do_not_show_again is True
|
||||
|
||||
with freezegun.freeze_time(now()):
|
||||
assert self.item_descriptor.bumper_last_view_date is None
|
||||
self.item_descriptor.handle_ajax('save_user_state', {'bumper_last_view_date': True})
|
||||
assert self.item_descriptor.bumper_last_view_date == now()
|
||||
assert self.block.bumper_last_view_date is None
|
||||
self.block.handle_ajax('save_user_state', {'bumper_last_view_date': True})
|
||||
assert self.block.bumper_last_view_date == now()
|
||||
|
||||
response = self.item_descriptor.handle_ajax('save_user_state', {'demoo<EFBFBD>': "sample"})
|
||||
response = self.block.handle_ajax('save_user_state', {'demoo<EFBFBD>': "sample"})
|
||||
assert json.loads(response)['success'] is True
|
||||
|
||||
def get_handler_url(self, handler, suffix):
|
||||
"""
|
||||
Return the URL for the specified handler on self.item_descriptor.
|
||||
Return the URL for the specified handler on self.block.
|
||||
"""
|
||||
return self.item_descriptor.runtime.handler_url(
|
||||
self.item_descriptor, handler, suffix
|
||||
return self.block.runtime.handler_url(
|
||||
self.block, handler, suffix
|
||||
).rstrip('/?')
|
||||
|
||||
def tearDown(self):
|
||||
_clear_assets(self.item_descriptor.location)
|
||||
_clear_assets(self.block.location)
|
||||
super().tearDown()
|
||||
|
||||
|
||||
@@ -268,24 +268,23 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo): # lint-amnesty, p
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.item_descriptor.render(STUDENT_VIEW)
|
||||
self.item = self.item_descriptor
|
||||
self.block.render(STUDENT_VIEW)
|
||||
self.subs = {"start": [10], "end": [100], "text": ["Hi, welcome to Edx."]}
|
||||
|
||||
def test_available_translation_en(self):
|
||||
good_sjson = _create_file(json.dumps(self.subs))
|
||||
_upload_sjson_file(good_sjson, self.item_descriptor.location)
|
||||
self.item.sub = _get_subs_id(good_sjson.name)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
self.block.sub = _get_subs_id(good_sjson.name)
|
||||
|
||||
request = Request.blank('/available_translations')
|
||||
response = self.item.transcript(request=request, dispatch='available_translations')
|
||||
response = self.block.transcript(request=request, dispatch='available_translations')
|
||||
assert json.loads(response.body.decode('utf-8')) == ['en']
|
||||
|
||||
def test_available_translation_non_en(self):
|
||||
_upload_file(_create_srt_file(), self.item_descriptor.location, os.path.split(self.srt_file.name)[1])
|
||||
_upload_file(_create_srt_file(), self.block.location, os.path.split(self.srt_file.name)[1])
|
||||
|
||||
request = Request.blank('/available_translations')
|
||||
response = self.item.transcript(request=request, dispatch='available_translations')
|
||||
response = self.block.transcript(request=request, dispatch='available_translations')
|
||||
assert json.loads(response.body.decode('utf-8')) == ['uk']
|
||||
|
||||
@patch('xmodule.video_block.transcripts_utils.get_video_transcript_content')
|
||||
@@ -302,16 +301,16 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo): # lint-amnesty, p
|
||||
good_sjson = _create_file(json.dumps(self.subs))
|
||||
|
||||
# Upload english transcript.
|
||||
_upload_sjson_file(good_sjson, self.item_descriptor.location)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
|
||||
# Upload non-english transcript.
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, os.path.split(self.srt_file.name)[1])
|
||||
_upload_file(self.srt_file, self.block.location, os.path.split(self.srt_file.name)[1])
|
||||
|
||||
self.item.sub = _get_subs_id(good_sjson.name)
|
||||
self.item.edx_video_id = 'an-edx-video-id'
|
||||
self.block.sub = _get_subs_id(good_sjson.name)
|
||||
self.block.edx_video_id = 'an-edx-video-id'
|
||||
|
||||
request = Request.blank('/available_translations')
|
||||
response = self.item.transcript(request=request, dispatch='available_translations')
|
||||
response = self.block.transcript(request=request, dispatch='available_translations')
|
||||
assert sorted(json.loads(response.body.decode('utf-8'))) == sorted(['en', 'uk'])
|
||||
|
||||
@patch('xmodule.video_block.transcripts_utils.get_video_transcript_content')
|
||||
@@ -365,13 +364,13 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo): # lint-amnesty, p
|
||||
for lang_code, in_content_store in dict(transcripts).items():
|
||||
if in_content_store:
|
||||
file_name, __ = os.path.split(self.srt_file.name)
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, file_name)
|
||||
_upload_file(self.srt_file, self.block.location, file_name)
|
||||
transcripts[lang_code] = file_name
|
||||
else:
|
||||
transcripts[lang_code] = 'non_existent.srt.sjson'
|
||||
if sub:
|
||||
sjson_transcript = _create_file(json.dumps(self.subs))
|
||||
_upload_sjson_file(sjson_transcript, self.item_descriptor.location)
|
||||
_upload_sjson_file(sjson_transcript, self.block.location)
|
||||
sub = _get_subs_id(sjson_transcript.name)
|
||||
|
||||
mock_get_video_transcript_content.return_value = {
|
||||
@@ -383,12 +382,12 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo): # lint-amnesty, p
|
||||
'file_name': 'edx.sjson'
|
||||
}
|
||||
mock_get_transcript_languages.return_value = val_transcripts
|
||||
self.item.transcripts = transcripts
|
||||
self.item.sub = sub
|
||||
self.item.edx_video_id = 'an-edx-video-id'
|
||||
self.block.transcripts = transcripts
|
||||
self.block.sub = sub
|
||||
self.block.edx_video_id = 'an-edx-video-id'
|
||||
# Make request to available translations dispatch.
|
||||
request = Request.blank('/available_translations')
|
||||
response = self.item.transcript(request=request, dispatch='available_translations')
|
||||
response = self.block.transcript(request=request, dispatch='available_translations')
|
||||
self.assertCountEqual(json.loads(response.body.decode('utf-8')), result)
|
||||
|
||||
@patch('xmodule.video_block.transcripts_utils.edxval_api.get_available_transcript_languages')
|
||||
@@ -398,7 +397,7 @@ class TestTranscriptAvailableTranslationsDispatch(TestVideo): # lint-amnesty, p
|
||||
"""
|
||||
mock_get_available_transcript_languages.return_value = ['en', 'de', 'ro']
|
||||
request = Request.blank('/available_translations')
|
||||
response = self.item.transcript(request=request, dispatch='available_translations')
|
||||
response = self.block.transcript(request=request, dispatch='available_translations')
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
@@ -426,19 +425,18 @@ class TestTranscriptAvailableTranslationsBumperDispatch(TestVideo): # lint-amne
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.item_descriptor.render(STUDENT_VIEW)
|
||||
self.item = self.item_descriptor
|
||||
self.block.render(STUDENT_VIEW)
|
||||
self.dispatch = "available_translations/?is_bumper=1"
|
||||
self.item.video_bumper = {"transcripts": {"en": ""}}
|
||||
self.block.video_bumper = {"transcripts": {"en": ""}}
|
||||
|
||||
@ddt.data("en", "uk")
|
||||
def test_available_translation_en_and_non_en(self, lang):
|
||||
filename = os.path.split(self.srt_file.name)[1]
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, filename)
|
||||
self.item.video_bumper["transcripts"][lang] = filename
|
||||
_upload_file(self.srt_file, self.block.location, filename)
|
||||
self.block.video_bumper["transcripts"][lang] = filename
|
||||
|
||||
request = Request.blank('/' + self.dispatch)
|
||||
response = self.item.transcript(request=request, dispatch=self.dispatch)
|
||||
response = self.block.transcript(request=request, dispatch=self.dispatch)
|
||||
assert json.loads(response.body.decode('utf-8')) == [lang]
|
||||
|
||||
@patch('xmodule.video_block.transcripts_utils.get_available_transcript_languages')
|
||||
@@ -453,16 +451,16 @@ class TestTranscriptAvailableTranslationsBumperDispatch(TestVideo): # lint-amne
|
||||
en_translation_filename = os.path.split(en_translation.name)[1]
|
||||
uk_translation_filename = os.path.split(self.srt_file.name)[1]
|
||||
# Upload english transcript.
|
||||
_upload_file(en_translation, self.item_descriptor.location, en_translation_filename)
|
||||
_upload_file(en_translation, self.block.location, en_translation_filename)
|
||||
|
||||
# Upload non-english transcript.
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, uk_translation_filename)
|
||||
_upload_file(self.srt_file, self.block.location, uk_translation_filename)
|
||||
|
||||
self.item.video_bumper["transcripts"]["en"] = en_translation_filename
|
||||
self.item.video_bumper["transcripts"]["uk"] = uk_translation_filename
|
||||
self.block.video_bumper["transcripts"]["en"] = en_translation_filename
|
||||
self.block.video_bumper["transcripts"]["uk"] = uk_translation_filename
|
||||
|
||||
request = Request.blank('/' + self.dispatch)
|
||||
response = self.item.transcript(request=request, dispatch=self.dispatch)
|
||||
response = self.block.transcript(request=request, dispatch=self.dispatch)
|
||||
# Assert that bumper only get its own translations.
|
||||
assert sorted(json.loads(response.body.decode('utf-8'))) == sorted(['en', 'uk'])
|
||||
|
||||
@@ -492,12 +490,11 @@ class TestTranscriptDownloadDispatch(TestVideo): # lint-amnesty, pylint: disabl
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.item_descriptor.render(STUDENT_VIEW)
|
||||
self.item = self.item_descriptor
|
||||
self.block.render(STUDENT_VIEW)
|
||||
|
||||
def test_download_transcript_not_exist(self):
|
||||
request = Request.blank('/download')
|
||||
response = self.item.transcript(request=request, dispatch='download')
|
||||
response = self.block.transcript(request=request, dispatch='download')
|
||||
assert response.status == '404 Not Found'
|
||||
|
||||
@patch(
|
||||
@@ -506,7 +503,7 @@ class TestTranscriptDownloadDispatch(TestVideo): # lint-amnesty, pylint: disabl
|
||||
)
|
||||
def test_download_srt_exist(self, __):
|
||||
request = Request.blank('/download')
|
||||
response = self.item.transcript(request=request, dispatch='download')
|
||||
response = self.block.transcript(request=request, dispatch='download')
|
||||
assert response.body.decode('utf-8') == 'Subs!'
|
||||
assert response.headers['Content-Type'] == 'application/x-subrip; charset=utf-8'
|
||||
assert response.headers['Content-Language'] == 'en'
|
||||
@@ -516,19 +513,19 @@ class TestTranscriptDownloadDispatch(TestVideo): # lint-amnesty, pylint: disabl
|
||||
return_value=('Subs!', 'txt', 'text/plain; charset=utf-8')
|
||||
)
|
||||
def test_download_txt_exist(self, __):
|
||||
self.item.transcript_format = 'txt'
|
||||
self.block.transcript_format = 'txt'
|
||||
request = Request.blank('/download')
|
||||
response = self.item.transcript(request=request, dispatch='download')
|
||||
response = self.block.transcript(request=request, dispatch='download')
|
||||
assert response.body.decode('utf-8') == 'Subs!'
|
||||
assert response.headers['Content-Type'] == 'text/plain; charset=utf-8'
|
||||
assert response.headers['Content-Language'] == 'en'
|
||||
|
||||
def test_download_en_no_sub(self):
|
||||
request = Request.blank('/download')
|
||||
response = self.item.transcript(request=request, dispatch='download')
|
||||
response = self.block.transcript(request=request, dispatch='download')
|
||||
assert response.status == '404 Not Found'
|
||||
with pytest.raises(NotFoundError):
|
||||
get_transcript(self.item)
|
||||
get_transcript(self.block)
|
||||
|
||||
@patch(
|
||||
'xmodule.video_block.transcripts_utils.get_transcript_for_video',
|
||||
@@ -536,7 +533,7 @@ class TestTranscriptDownloadDispatch(TestVideo): # lint-amnesty, pylint: disabl
|
||||
)
|
||||
def test_download_non_en_non_ascii_filename(self, __):
|
||||
request = Request.blank('/download')
|
||||
response = self.item.transcript(request=request, dispatch='download')
|
||||
response = self.block.transcript(request=request, dispatch='download')
|
||||
assert response.body.decode('utf-8') == 'Subs!'
|
||||
assert response.headers['Content-Type'] == 'application/x-subrip; charset=utf-8'
|
||||
assert response.headers['Content-Disposition'] == 'attachment; filename="en_塞.srt"'
|
||||
@@ -558,7 +555,7 @@ class TestTranscriptDownloadDispatch(TestVideo): # lint-amnesty, pylint: disabl
|
||||
|
||||
# Make request to XModule transcript handler
|
||||
request = Request.blank('/download')
|
||||
response = self.item.transcript(request=request, dispatch='download')
|
||||
response = self.block.transcript(request=request, dispatch='download')
|
||||
|
||||
# Expected response
|
||||
expected_content = '0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
|
||||
@@ -602,9 +599,8 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.item_descriptor.render(STUDENT_VIEW)
|
||||
self.item = self.item_descriptor
|
||||
self.item.video_bumper = {"transcripts": {"en": ""}}
|
||||
self.block.render(STUDENT_VIEW)
|
||||
self.block.video_bumper = {"transcripts": {"en": ""}}
|
||||
|
||||
@ddt.data(
|
||||
# No language
|
||||
@@ -623,7 +619,7 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
@ddt.unpack
|
||||
def test_translation_fails(self, url, dispatch, status_code):
|
||||
request = Request.blank(url)
|
||||
response = self.item.transcript(request=request, dispatch=dispatch)
|
||||
response = self.block.transcript(request=request, dispatch=dispatch)
|
||||
assert response.status == status_code
|
||||
|
||||
@ddt.data(
|
||||
@@ -633,12 +629,12 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
def test_translaton_en_youtube_success(self, url, dispatch, attach):
|
||||
subs = {"start": [10], "end": [100], "text": ["Hi, welcome to Edx."]}
|
||||
good_sjson = _create_file(json.dumps(subs))
|
||||
_upload_sjson_file(good_sjson, self.item_descriptor.location)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
subs_id = _get_subs_id(good_sjson.name)
|
||||
|
||||
attach(self.item, subs_id)
|
||||
attach(self.block, subs_id)
|
||||
request = Request.blank(url.format(subs_id))
|
||||
response = self.item.transcript(request=request, dispatch=dispatch)
|
||||
response = self.block.transcript(request=request, dispatch=dispatch)
|
||||
self.assertDictEqual(json.loads(response.body.decode('utf-8')), subs)
|
||||
|
||||
def test_translation_non_en_youtube_success(self):
|
||||
@@ -650,20 +646,20 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
]
|
||||
}
|
||||
self.srt_file.seek(0)
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, os.path.split(self.srt_file.name)[1])
|
||||
_upload_file(self.srt_file, self.block.location, os.path.split(self.srt_file.name)[1])
|
||||
subs_id = _get_subs_id(self.srt_file.name)
|
||||
|
||||
# youtube 1_0 request, will generate for all speeds for existing ids
|
||||
self.item.youtube_id_1_0 = subs_id
|
||||
self.item.youtube_id_0_75 = '0_75'
|
||||
self.store.update_item(self.item, self.user.id)
|
||||
self.block.youtube_id_1_0 = subs_id
|
||||
self.block.youtube_id_0_75 = '0_75'
|
||||
self.store.update_item(self.block, self.user.id)
|
||||
request = Request.blank(f'/translation/uk?videoId={subs_id}')
|
||||
response = self.item.transcript(request=request, dispatch='translation/uk')
|
||||
response = self.block.transcript(request=request, dispatch='translation/uk')
|
||||
self.assertDictEqual(json.loads(response.body.decode('utf-8')), subs)
|
||||
|
||||
# 0_75 subs are exist
|
||||
request = Request.blank('/translation/uk?videoId={}'.format('0_75'))
|
||||
response = self.item.transcript(request=request, dispatch='translation/uk')
|
||||
response = self.block.transcript(request=request, dispatch='translation/uk')
|
||||
calculated_0_75 = {
|
||||
'end': [75],
|
||||
'start': [9],
|
||||
@@ -674,10 +670,10 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
|
||||
self.assertDictEqual(json.loads(response.body.decode('utf-8')), calculated_0_75)
|
||||
# 1_5 will be generated from 1_0
|
||||
self.item.youtube_id_1_5 = '1_5'
|
||||
self.store.update_item(self.item, self.user.id)
|
||||
self.block.youtube_id_1_5 = '1_5'
|
||||
self.store.update_item(self.block, self.user.id)
|
||||
request = Request.blank('/translation/uk?videoId={}'.format('1_5'))
|
||||
response = self.item.transcript(request=request, dispatch='translation/uk')
|
||||
response = self.block.transcript(request=request, dispatch='translation/uk')
|
||||
calculated_1_5 = {
|
||||
'end': [150],
|
||||
'start': [18],
|
||||
@@ -693,13 +689,13 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
@ddt.unpack
|
||||
def test_translaton_en_html5_success(self, url, dispatch, attach):
|
||||
good_sjson = _create_file(json.dumps(TRANSCRIPT))
|
||||
_upload_sjson_file(good_sjson, self.item_descriptor.location)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
subs_id = _get_subs_id(good_sjson.name)
|
||||
|
||||
attach(self.item, subs_id)
|
||||
self.store.update_item(self.item, self.user.id)
|
||||
attach(self.block, subs_id)
|
||||
self.store.update_item(self.block, self.user.id)
|
||||
request = Request.blank(url)
|
||||
response = self.item.transcript(request=request, dispatch=dispatch)
|
||||
response = self.block.transcript(request=request, dispatch=dispatch)
|
||||
self.assertDictEqual(json.loads(response.body.decode('utf-8')), TRANSCRIPT)
|
||||
|
||||
def test_translaton_non_en_html5_success(self):
|
||||
@@ -711,12 +707,12 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
]
|
||||
}
|
||||
self.srt_file.seek(0)
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, os.path.split(self.srt_file.name)[1])
|
||||
_upload_file(self.srt_file, self.block.location, os.path.split(self.srt_file.name)[1])
|
||||
|
||||
# manually clean youtube_id_1_0, as it has default value
|
||||
self.item.youtube_id_1_0 = ""
|
||||
self.block.youtube_id_1_0 = ""
|
||||
request = Request.blank('/translation/uk')
|
||||
response = self.item.transcript(request=request, dispatch='translation/uk')
|
||||
response = self.block.transcript(request=request, dispatch='translation/uk')
|
||||
self.assertDictEqual(json.loads(response.body.decode('utf-8')), subs)
|
||||
|
||||
def test_translation_static_transcript_xml_with_data_dirc(self):
|
||||
@@ -730,25 +726,25 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
test_modulestore = MagicMock()
|
||||
attrs = {'get_course.return_value': Mock(data_dir='dummy/static', static_asset_path='')}
|
||||
test_modulestore.configure_mock(**attrs)
|
||||
self.item_descriptor.runtime.modulestore = test_modulestore
|
||||
self.block.runtime.modulestore = test_modulestore
|
||||
|
||||
# Test youtube style en
|
||||
request = Request.blank('/translation/en?videoId=12345')
|
||||
response = self.item.transcript(request=request, dispatch='translation/en')
|
||||
response = self.block.transcript(request=request, dispatch='translation/en')
|
||||
assert response.status == '307 Temporary Redirect'
|
||||
assert ('Location', '/static/dummy/static/subs_12345.srt.sjson') in response.headerlist
|
||||
|
||||
# Test HTML5 video style
|
||||
self.item.sub = 'OEoXaMPEzfM'
|
||||
self.block.sub = 'OEoXaMPEzfM'
|
||||
request = Request.blank('/translation/en')
|
||||
response = self.item.transcript(request=request, dispatch='translation/en')
|
||||
response = self.block.transcript(request=request, dispatch='translation/en')
|
||||
assert response.status == '307 Temporary Redirect'
|
||||
assert ('Location', '/static/dummy/static/subs_OEoXaMPEzfM.srt.sjson') in response.headerlist
|
||||
|
||||
# Test different language to ensure we are just ignoring it since we can't
|
||||
# translate with static fallback
|
||||
request = Request.blank('/translation/uk')
|
||||
response = self.item.transcript(request=request, dispatch='translation/uk')
|
||||
response = self.block.transcript(request=request, dispatch='translation/uk')
|
||||
assert response.status == '404 Not Found'
|
||||
|
||||
@ddt.data(
|
||||
@@ -774,9 +770,9 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
self._set_static_asset_path()
|
||||
|
||||
if attach:
|
||||
attach(self.item, sub)
|
||||
attach(self.block, sub)
|
||||
request = Request.blank(url)
|
||||
response = self.item.transcript(request=request, dispatch=dispatch)
|
||||
response = self.block.transcript(request=request, dispatch=dispatch)
|
||||
assert response.status == status_code
|
||||
if sub:
|
||||
assert ('Location', f'/static/dummy/static/subs_{sub}.srt.sjson') in response.headerlist
|
||||
@@ -791,7 +787,7 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
|
||||
# When course_id is not mocked out, these values would result in 307, as tested above.
|
||||
request = Request.blank('/translation/en?videoId=12345')
|
||||
response = self.item.transcript(request=request, dispatch='translation/en')
|
||||
response = self.block.transcript(request=request, dispatch='translation/en')
|
||||
assert response.status == '404 Not Found'
|
||||
|
||||
def _set_static_asset_path(self):
|
||||
@@ -821,7 +817,7 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
mock_get_video_transcript_data.return_value = transcript
|
||||
|
||||
# Make request to XModule transcript handler
|
||||
response = self.item.transcript(request=Request.blank('/translation/en'), dispatch='translation/en')
|
||||
response = self.block.transcript(request=Request.blank('/translation/en'), dispatch='translation/en')
|
||||
|
||||
# Expected headers
|
||||
expected_headers = {
|
||||
@@ -842,7 +838,7 @@ class TestTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, pylint:
|
||||
Verify that val transcript is not returned when its feature is disabled.
|
||||
"""
|
||||
# Make request to XModule transcript handler
|
||||
response = self.item.transcript(request=Request.blank('/translation/en'), dispatch='translation/en')
|
||||
response = self.block.transcript(request=Request.blank('/translation/en'), dispatch='translation/en')
|
||||
# Assert the actual response
|
||||
assert response.status_code == 404
|
||||
|
||||
@@ -870,20 +866,20 @@ class TestStudioTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, py
|
||||
def test_translation_fails(self):
|
||||
# No language
|
||||
request = Request.blank("")
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch="translation")
|
||||
response = self.block.studio_transcript(request=request, dispatch="translation")
|
||||
assert response.status == '400 Bad Request'
|
||||
|
||||
# No language_code param in request.GET
|
||||
request = Request.blank("")
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch="translation")
|
||||
response = self.block.studio_transcript(request=request, dispatch="translation")
|
||||
assert response.status == '400 Bad Request'
|
||||
assert response.json['error'] == 'Language is required.'
|
||||
|
||||
# Correct case:
|
||||
filename = os.path.split(self.srt_file.name)[1]
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, filename)
|
||||
_upload_file(self.srt_file, self.block.location, filename)
|
||||
request = Request.blank("translation?language_code=uk")
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch="translation?language_code=uk")
|
||||
response = self.block.studio_transcript(request=request, dispatch="translation?language_code=uk")
|
||||
self.srt_file.seek(0)
|
||||
assert response.body == self.srt_file.read()
|
||||
assert response.headers['Content-Type'] == 'application/x-subrip; charset=utf-8'
|
||||
@@ -892,9 +888,9 @@ class TestStudioTranscriptTranslationGetDispatch(TestVideo): # lint-amnesty, py
|
||||
|
||||
# Non ascii file name download:
|
||||
self.srt_file.seek(0)
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, "塞.srt")
|
||||
_upload_file(self.srt_file, self.block.location, "塞.srt")
|
||||
request = Request.blank("translation?language_code=zh")
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch="translation?language_code=zh")
|
||||
response = self.block.studio_transcript(request=request, dispatch="translation?language_code=zh")
|
||||
self.srt_file.seek(0)
|
||||
assert response.body == self.srt_file.read()
|
||||
assert response.headers['Content-Type'] == 'application/x-subrip; charset=utf-8'
|
||||
@@ -945,9 +941,9 @@ class TestStudioTranscriptTranslationPostDispatch(TestVideo): # lint-amnesty, p
|
||||
Verify that POST request validations works as expected.
|
||||
"""
|
||||
# mock available_translations method
|
||||
self.item_descriptor.available_translations = lambda transcripts, verify_assets: ['ur']
|
||||
self.block.available_translations = lambda transcripts, verify_assets: ['ur']
|
||||
request = Request.blank('/translation', POST=post_data)
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
|
||||
response = self.block.studio_transcript(request=request, dispatch='translation')
|
||||
assert response.json['error'] == error_message
|
||||
|
||||
@ddt.data(
|
||||
@@ -981,11 +977,11 @@ class TestStudioTranscriptTranslationPostDispatch(TestVideo): # lint-amnesty, p
|
||||
})
|
||||
|
||||
request = Request.blank('/translation', POST=post_data)
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
|
||||
response = self.block.studio_transcript(request=request, dispatch='translation')
|
||||
assert response.status == '201 Created'
|
||||
response = json.loads(response.text)
|
||||
assert response['language_code'], 'uk'
|
||||
self.assertDictEqual(self.item_descriptor.transcripts, {})
|
||||
self.assertDictEqual(self.block.transcripts, {})
|
||||
assert edxval_api.get_video_transcript_data(video_id=response['edx_video_id'], language_code='uk')
|
||||
|
||||
def test_studio_transcript_post_bad_content(self):
|
||||
@@ -1000,7 +996,7 @@ class TestStudioTranscriptTranslationPostDispatch(TestVideo): # lint-amnesty, p
|
||||
}
|
||||
|
||||
request = Request.blank("/translation", POST=post_data)
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch="translation")
|
||||
response = self.block.studio_transcript(request=request, dispatch="translation")
|
||||
assert response.status_code == 400
|
||||
assert response.json['error'] == 'There is a problem with this transcript file. Try to upload a different file.'
|
||||
|
||||
@@ -1033,7 +1029,7 @@ class TestStudioTranscriptTranslationDeleteDispatch(TestVideo): # lint-amnesty,
|
||||
Verify that DELETE dispatch works as expected when required args are missing from request
|
||||
"""
|
||||
request = Request(self.REQUEST_META, body=json.dumps(params).encode('utf-8'))
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
|
||||
response = self.block.studio_transcript(request=request, dispatch='translation')
|
||||
assert response.status_code == 400
|
||||
|
||||
def test_translation_delete_w_edx_video_id(self):
|
||||
@@ -1060,8 +1056,8 @@ class TestStudioTranscriptTranslationDeleteDispatch(TestVideo): # lint-amnesty,
|
||||
assert api.get_video_transcript_data(video_id=self.EDX_VIDEO_ID, language_code=self.LANGUAGE_CODE_UK)
|
||||
|
||||
request = Request(self.REQUEST_META, body=request_body.encode('utf-8'))
|
||||
self.item_descriptor.edx_video_id = self.EDX_VIDEO_ID
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
|
||||
self.block.edx_video_id = self.EDX_VIDEO_ID
|
||||
response = self.block.studio_transcript(request=request, dispatch='translation')
|
||||
assert response.status_code == 200
|
||||
|
||||
# verify that a video transcript dose not exist for expected data
|
||||
@@ -1076,20 +1072,20 @@ class TestStudioTranscriptTranslationDeleteDispatch(TestVideo): # lint-amnesty,
|
||||
request = Request(self.REQUEST_META, body=request_body.encode('utf-8'))
|
||||
|
||||
# upload and verify that srt file exists in assets
|
||||
_upload_file(self.SRT_FILE, self.item_descriptor.location, srt_file_name_uk)
|
||||
assert _check_asset(self.item_descriptor.location, srt_file_name_uk)
|
||||
_upload_file(self.SRT_FILE, self.block.location, srt_file_name_uk)
|
||||
assert _check_asset(self.block.location, srt_file_name_uk)
|
||||
|
||||
# verify transcripts field
|
||||
assert self.item_descriptor.transcripts != {}
|
||||
assert self.LANGUAGE_CODE_UK in self.item_descriptor.transcripts
|
||||
assert self.block.transcripts != {}
|
||||
assert self.LANGUAGE_CODE_UK in self.block.transcripts
|
||||
|
||||
# make request and verify response
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
|
||||
response = self.block.studio_transcript(request=request, dispatch='translation')
|
||||
assert response.status_code == 200
|
||||
|
||||
# verify that srt file is deleted
|
||||
assert self.item_descriptor.transcripts == {}
|
||||
assert not _check_asset(self.item_descriptor.location, srt_file_name_uk)
|
||||
assert self.block.transcripts == {}
|
||||
assert not _check_asset(self.block.location, srt_file_name_uk)
|
||||
|
||||
def test_translation_delete_w_english_lang(self):
|
||||
"""
|
||||
@@ -1097,45 +1093,45 @@ class TestStudioTranscriptTranslationDeleteDispatch(TestVideo): # lint-amnesty,
|
||||
"""
|
||||
request_body = json.dumps({'lang': self.LANGUAGE_CODE_EN, 'edx_video_id': ''})
|
||||
srt_file_name_en = subs_filename('english_translation.srt', lang=self.LANGUAGE_CODE_EN)
|
||||
self.item_descriptor.transcripts['en'] = 'english_translation.srt'
|
||||
self.block.transcripts['en'] = 'english_translation.srt'
|
||||
request = Request(self.REQUEST_META, body=request_body.encode('utf-8'))
|
||||
|
||||
# upload and verify that srt file exists in assets
|
||||
_upload_file(self.SRT_FILE, self.item_descriptor.location, srt_file_name_en)
|
||||
assert _check_asset(self.item_descriptor.location, srt_file_name_en)
|
||||
_upload_file(self.SRT_FILE, self.block.location, srt_file_name_en)
|
||||
assert _check_asset(self.block.location, srt_file_name_en)
|
||||
|
||||
# make request and verify response
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
|
||||
response = self.block.studio_transcript(request=request, dispatch='translation')
|
||||
assert response.status_code == 200
|
||||
|
||||
# verify that srt file is deleted
|
||||
assert self.LANGUAGE_CODE_EN not in self.item_descriptor.transcripts
|
||||
assert not _check_asset(self.item_descriptor.location, srt_file_name_en)
|
||||
assert self.LANGUAGE_CODE_EN not in self.block.transcripts
|
||||
assert not _check_asset(self.block.location, srt_file_name_en)
|
||||
|
||||
def test_translation_delete_w_sub(self):
|
||||
"""
|
||||
Verify that DELETE dispatch works as expected when translation is present against `sub` field
|
||||
"""
|
||||
request_body = json.dumps({'lang': self.LANGUAGE_CODE_EN, 'edx_video_id': ''})
|
||||
sub_file_name = subs_filename(self.item_descriptor.sub, lang=self.LANGUAGE_CODE_EN)
|
||||
sub_file_name = subs_filename(self.block.sub, lang=self.LANGUAGE_CODE_EN)
|
||||
request = Request(self.REQUEST_META, body=request_body.encode('utf-8'))
|
||||
|
||||
# sub should not be empy
|
||||
assert not self.item_descriptor.sub == ''
|
||||
assert not self.block.sub == ''
|
||||
# lint-amnesty, pylint: disable=wrong-assert-type
|
||||
|
||||
# upload and verify that srt file exists in assets
|
||||
_upload_file(self.SRT_FILE, self.item_descriptor.location, sub_file_name)
|
||||
assert _check_asset(self.item_descriptor.location, sub_file_name)
|
||||
_upload_file(self.SRT_FILE, self.block.location, sub_file_name)
|
||||
assert _check_asset(self.block.location, sub_file_name)
|
||||
|
||||
# make request and verify response
|
||||
response = self.item_descriptor.studio_transcript(request=request, dispatch='translation')
|
||||
response = self.block.studio_transcript(request=request, dispatch='translation')
|
||||
assert response.status_code == 200
|
||||
|
||||
# verify that sub is empty and transcript is deleted also
|
||||
assert self.item_descriptor.sub == ''
|
||||
assert self.block.sub == ''
|
||||
# lint-amnesty, pylint: disable=wrong-assert-type
|
||||
assert not _check_asset(self.item_descriptor.location, sub_file_name)
|
||||
assert not _check_asset(self.block.location, sub_file_name)
|
||||
|
||||
|
||||
class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inherits-tests
|
||||
@@ -1161,8 +1157,7 @@ class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inheri
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.item_descriptor.render(STUDENT_VIEW)
|
||||
self.item = self.item_descriptor
|
||||
self.block.render(STUDENT_VIEW)
|
||||
|
||||
def test_good_transcript(self):
|
||||
"""
|
||||
@@ -1185,10 +1180,10 @@ class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inheri
|
||||
}
|
||||
"""))
|
||||
|
||||
_upload_sjson_file(good_sjson, self.item.location)
|
||||
self.item.sub = _get_subs_id(good_sjson.name)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
self.block.sub = _get_subs_id(good_sjson.name)
|
||||
|
||||
text, filename, mime_type = get_transcript(self.item)
|
||||
text, filename, mime_type = get_transcript(self.block)
|
||||
|
||||
expected_text = textwrap.dedent("""\
|
||||
0
|
||||
@@ -1202,7 +1197,7 @@ class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inheri
|
||||
""")
|
||||
|
||||
assert text == expected_text
|
||||
assert filename[:(- 4)] == ('en_' + self.item.sub)
|
||||
assert filename[:(- 4)] == ('en_' + self.block.sub)
|
||||
assert mime_type == 'application/x-subrip; charset=utf-8'
|
||||
|
||||
def test_good_txt_transcript(self):
|
||||
@@ -1223,29 +1218,29 @@ class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inheri
|
||||
}
|
||||
"""))
|
||||
|
||||
_upload_sjson_file(good_sjson, self.item.location)
|
||||
self.item.sub = _get_subs_id(good_sjson.name)
|
||||
text, filename, mime_type = get_transcript(self.item, output_format=Transcript.TXT)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
self.block.sub = _get_subs_id(good_sjson.name)
|
||||
text, filename, mime_type = get_transcript(self.block, output_format=Transcript.TXT)
|
||||
expected_text = textwrap.dedent("""\
|
||||
Hi, welcome to Edx.
|
||||
Let's start with what is on your screen right now.""")
|
||||
|
||||
assert text == expected_text
|
||||
assert filename == (('en_' + self.item.sub) + '.txt')
|
||||
assert filename == (('en_' + self.block.sub) + '.txt')
|
||||
assert mime_type == 'text/plain; charset=utf-8'
|
||||
|
||||
def test_en_with_empty_sub(self):
|
||||
|
||||
self.item.sub = ""
|
||||
self.item.transcripts = None
|
||||
self.block.sub = ""
|
||||
self.block.transcripts = None
|
||||
# no self.sub, self.youttube_1_0 exist, but no file in assets
|
||||
with pytest.raises(NotFoundError):
|
||||
get_transcript(self.item)
|
||||
get_transcript(self.block)
|
||||
|
||||
# no self.sub and no self.youtube_1_0, no non-en transcritps
|
||||
self.item.youtube_id_1_0 = None
|
||||
self.block.youtube_id_1_0 = None
|
||||
with pytest.raises(NotFoundError):
|
||||
get_transcript(self.item)
|
||||
get_transcript(self.block)
|
||||
|
||||
# no self.sub but youtube_1_0 exists with file in assets
|
||||
good_sjson = _create_file(content=textwrap.dedent("""\
|
||||
@@ -1264,10 +1259,10 @@ class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inheri
|
||||
]
|
||||
}
|
||||
"""))
|
||||
_upload_sjson_file(good_sjson, self.item.location)
|
||||
self.item.youtube_id_1_0 = _get_subs_id(good_sjson.name)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
self.block.youtube_id_1_0 = _get_subs_id(good_sjson.name)
|
||||
|
||||
text, filename, mime_type = get_transcript(self.item)
|
||||
text, filename, mime_type = get_transcript(self.block)
|
||||
expected_text = textwrap.dedent("""\
|
||||
0
|
||||
00:00:00,270 --> 00:00:02,720
|
||||
@@ -1280,16 +1275,16 @@ class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inheri
|
||||
""")
|
||||
|
||||
assert text == expected_text
|
||||
assert filename == (('en_' + self.item.youtube_id_1_0) + '.srt')
|
||||
assert filename == (('en_' + self.block.youtube_id_1_0) + '.srt')
|
||||
assert mime_type == 'application/x-subrip; charset=utf-8'
|
||||
|
||||
def test_non_en_with_non_ascii_filename(self):
|
||||
self.item.transcript_language = 'zh'
|
||||
self.block.transcript_language = 'zh'
|
||||
self.srt_file.seek(0)
|
||||
_upload_file(self.srt_file, self.item_descriptor.location, "塞.srt")
|
||||
_upload_file(self.srt_file, self.block.location, "塞.srt")
|
||||
|
||||
transcripts = self.item.get_transcripts_info() # lint-amnesty, pylint: disable=unused-variable
|
||||
text, filename, mime_type = get_transcript(self.item)
|
||||
transcripts = self.block.get_transcripts_info() # lint-amnesty, pylint: disable=unused-variable
|
||||
text, filename, mime_type = get_transcript(self.block)
|
||||
expected_text = textwrap.dedent("""
|
||||
0
|
||||
00:00:00,12 --> 00:00:00,100
|
||||
@@ -1302,12 +1297,12 @@ class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inheri
|
||||
def test_value_error_handled(self):
|
||||
good_sjson = _create_file(content='bad content')
|
||||
|
||||
_upload_sjson_file(good_sjson, self.item.location)
|
||||
self.item.sub = _get_subs_id(good_sjson.name)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
self.block.sub = _get_subs_id(good_sjson.name)
|
||||
|
||||
transcripts = self.item.get_transcripts_info() # lint-amnesty, pylint: disable=unused-variable
|
||||
transcripts = self.block.get_transcripts_info() # lint-amnesty, pylint: disable=unused-variable
|
||||
error_transcript = {"start": [], "end": [], "text": ["An error occured obtaining the transcript."]}
|
||||
content, _, _ = get_transcript(self.item)
|
||||
content, _, _ = get_transcript(self.block)
|
||||
assert error_transcript["text"][0] in content
|
||||
|
||||
def test_key_error(self):
|
||||
@@ -1324,9 +1319,9 @@ class TestGetTranscript(TestVideo): # lint-amnesty, pylint: disable=test-inheri
|
||||
}
|
||||
""")
|
||||
|
||||
_upload_sjson_file(good_sjson, self.item.location)
|
||||
self.item.sub = _get_subs_id(good_sjson.name)
|
||||
_upload_sjson_file(good_sjson, self.block.location)
|
||||
self.block.sub = _get_subs_id(good_sjson.name)
|
||||
|
||||
transcripts = self.item.get_transcripts_info() # lint-amnesty, pylint: disable=unused-variable
|
||||
transcripts = self.block.get_transcripts_info() # lint-amnesty, pylint: disable=unused-variable
|
||||
with pytest.raises(KeyError):
|
||||
get_transcript(self.item)
|
||||
get_transcript(self.block)
|
||||
|
||||
@@ -81,7 +81,7 @@ class TestVideoYouTube(TestVideo): # lint-amnesty, pylint: disable=missing-clas
|
||||
|
||||
def test_video_constructor(self):
|
||||
"""Make sure that all parameters extracted correctly from xml"""
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
sources = ['example.mp4', 'example.webm']
|
||||
|
||||
expected_context = {
|
||||
@@ -95,12 +95,12 @@ class TestVideoYouTube(TestVideo): # lint-amnesty, pylint: disable=missing-clas
|
||||
'download_video_link': 'example.mp4',
|
||||
'handout': None,
|
||||
'hide_downloads': False,
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'is_embed': False,
|
||||
'metadata': json.dumps(OrderedDict({
|
||||
'autoAdvance': False,
|
||||
'saveStateEnabled': True,
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'autoplay': False,
|
||||
'streams': '0.75:jNCf2gIqpeE,1.00:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg',
|
||||
'sources': sources,
|
||||
@@ -138,7 +138,7 @@ class TestVideoYouTube(TestVideo): # lint-amnesty, pylint: disable=missing-clas
|
||||
'public_video_url': None,
|
||||
}
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@@ -165,7 +165,7 @@ class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests
|
||||
"""Make sure that if the 'youtube' attribute is omitted in XML, then
|
||||
the template generates an empty string for the YouTube streams.
|
||||
"""
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
sources = ['example.mp4', 'example.webm']
|
||||
|
||||
expected_context = {
|
||||
@@ -180,11 +180,11 @@ class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests
|
||||
'handout': None,
|
||||
'hide_downloads': False,
|
||||
'is_embed': False,
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'metadata': json.dumps(OrderedDict({
|
||||
'autoAdvance': False,
|
||||
'saveStateEnabled': True,
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'autoplay': False,
|
||||
'streams': '1.00:3_yD_cEKoCk',
|
||||
'sources': sources,
|
||||
@@ -222,7 +222,7 @@ class TestVideoNonYouTube(TestVideo): # pylint: disable=test-inherits-tests
|
||||
'public_video_url': None,
|
||||
}
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
expected_result = get_context_dict_from_string(
|
||||
mako_service.render_template('video.html', expected_context)
|
||||
)
|
||||
@@ -249,11 +249,11 @@ class TestVideoPublicAccess(BaseTestVideoXBlock):
|
||||
@ddt.unpack
|
||||
def test_public_video_url(self, is_lms_platform, enable_public_share):
|
||||
"""Test public video url."""
|
||||
assert self.item_descriptor.public_access is True
|
||||
assert self.block.public_access is True
|
||||
if not is_lms_platform:
|
||||
self.item_descriptor.runtime.is_author_mode = True
|
||||
self.block.runtime.is_author_mode = True
|
||||
with patch.object(PUBLIC_VIDEO_SHARE, 'is_enabled', return_value=enable_public_share):
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
# public video url iif PUBLIC_VIDEO_SHARE waffle and is_lms_platform, public_access are true
|
||||
assert bool(get_context_dict_from_string(context)['public_video_url']) \
|
||||
is (is_lms_platform and enable_public_share)
|
||||
@@ -307,10 +307,10 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
def get_handler_url(self, handler, suffix):
|
||||
"""
|
||||
Return the URL for the specified handler on the block represented by
|
||||
self.item_descriptor.
|
||||
self.block.
|
||||
"""
|
||||
return self.item_descriptor.runtime.handler_url(
|
||||
self.item_descriptor, handler, suffix
|
||||
return self.block.runtime.handler_url(
|
||||
self.block, handler, suffix
|
||||
).rstrip('/?')
|
||||
|
||||
def test_get_html_track(self):
|
||||
@@ -379,7 +379,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'download_video_link': 'example.mp4',
|
||||
'handout': None,
|
||||
'hide_downloads': False,
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'is_embed': False,
|
||||
'metadata': '',
|
||||
'track': None,
|
||||
@@ -406,27 +406,27 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
self.initialize_block(data=DATA)
|
||||
track_url = self.get_handler_url('transcript', 'download')
|
||||
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
metadata.update({
|
||||
'transcriptLanguages': {"en": "English"} if not data['transcripts'] else {"uk": 'Українська'},
|
||||
'transcriptLanguage': 'en' if not data['transcripts'] or data.get('sub') else 'uk',
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
'transcriptAvailableTranslationsUrl': self.get_handler_url('transcript', 'available_translations'),
|
||||
'publishCompletionUrl': self.get_handler_url('publish_completion', ''),
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
})
|
||||
expected_context.update({
|
||||
'transcript_download_format': (
|
||||
None if self.item_descriptor.track and self.item_descriptor.download_track else 'srt'
|
||||
None if self.block.track and self.block.download_track else 'srt'
|
||||
),
|
||||
'track': (
|
||||
track_url if data['expected_track_url'] == 'a_sub_file.srt.sjson' else data['expected_track_url']
|
||||
),
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'metadata': json.dumps(metadata)
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@@ -500,7 +500,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'download_video_link': 'example.mp4',
|
||||
'handout': None,
|
||||
'hide_downloads': False,
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'is_embed': False,
|
||||
'metadata': self.default_metadata_dict,
|
||||
'track': None,
|
||||
@@ -521,23 +521,23 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
sources=data['sources']
|
||||
)
|
||||
self.initialize_block(data=DATA)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
'transcriptAvailableTranslationsUrl': self.get_handler_url('transcript', 'available_translations'),
|
||||
'publishCompletionUrl': self.get_handler_url('publish_completion', ''),
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'sources': data['result'].get('sources', []),
|
||||
})
|
||||
expected_context.update({
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'download_video_link': data['result'].get('download_video_link'),
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@@ -581,7 +581,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
|
||||
# Referencing a non-existent VAL ID in courseware won't cause an error --
|
||||
# it'll just fall back to the values in the VideoBlock.
|
||||
assert 'example.mp4' in self.item_descriptor.render(STUDENT_VIEW).content
|
||||
assert 'example.mp4' in self.block.render(STUDENT_VIEW).content
|
||||
|
||||
def test_get_html_with_mocked_edx_video_id(self):
|
||||
# lint-amnesty, pylint: disable=invalid-name, redefined-outer-name
|
||||
@@ -629,7 +629,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'handout': None,
|
||||
'hide_downloads': False,
|
||||
'is_embed': False,
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'track': None,
|
||||
'transcript_download_format': 'srt',
|
||||
'transcript_download_formats_list': [
|
||||
@@ -664,23 +664,23 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
}
|
||||
]
|
||||
}
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
'transcriptAvailableTranslationsUrl': self.get_handler_url('transcript', 'available_translations'),
|
||||
'publishCompletionUrl': self.get_handler_url('publish_completion', ''),
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'sources': data['result']['sources'],
|
||||
})
|
||||
expected_context.update({
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'download_video_link': data['result']['download_video_link'],
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@@ -708,7 +708,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
# context returned by get_html when provided with above data
|
||||
# expected_context, a dict to assert with context
|
||||
context, expected_context = self.helper_get_html_with_edx_video_id(data)
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@@ -739,7 +739,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
# expected_context, a dict to assert with context
|
||||
context, expected_context = self.helper_get_html_with_edx_video_id(data)
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@@ -803,7 +803,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'handout': None,
|
||||
'hide_downloads': False,
|
||||
'is_embed': False,
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'track': None,
|
||||
'transcript_download_format': 'srt',
|
||||
'transcript_download_formats_list': [
|
||||
@@ -824,7 +824,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
)
|
||||
self.initialize_block(data=DATA)
|
||||
# context returned by get_html
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
|
||||
# expected_context, expected context to be returned by get_html
|
||||
expected_context = dict(initial_context)
|
||||
@@ -832,11 +832,11 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
'transcriptAvailableTranslationsUrl': self.get_handler_url('transcript', 'available_translations'),
|
||||
'publishCompletionUrl': self.get_handler_url('publish_completion', ''),
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'sources': data['result']['sources'],
|
||||
})
|
||||
expected_context.update({
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'download_video_link': data['result']['download_video_link'],
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
@@ -940,25 +940,25 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
self.initialize_block(data=DATA, runtime_kwargs={
|
||||
'user_location': 'CN',
|
||||
})
|
||||
user_service = self.item_descriptor.runtime.service(self.item_descriptor, 'user')
|
||||
user_service = self.block.runtime.service(self.block, 'user')
|
||||
user_location = user_service.get_current_user().opt_attrs[ATTR_KEY_REQUEST_COUNTRY_CODE]
|
||||
assert user_location == 'CN'
|
||||
context = self.item_descriptor.render('student_view').content
|
||||
context = self.block.render('student_view').content
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
'transcriptAvailableTranslationsUrl': self.get_handler_url('transcript', 'available_translations'),
|
||||
'publishCompletionUrl': self.get_handler_url('publish_completion', ''),
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'sources': data['result'].get('sources', []),
|
||||
})
|
||||
expected_context.update({
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'download_video_link': data['result'].get('download_video_link'),
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@@ -1048,22 +1048,22 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
'client_video_id': 'external video',
|
||||
'encoded_videos': {}
|
||||
}
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
expected_context = dict(initial_context)
|
||||
expected_context['metadata'].update({
|
||||
'transcriptTranslationUrl': self.get_handler_url('transcript', 'translation/__lang__'),
|
||||
'transcriptAvailableTranslationsUrl': self.get_handler_url('transcript', 'available_translations'),
|
||||
'publishCompletionUrl': self.get_handler_url('publish_completion', ''),
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'sources': data['result'].get('sources', []),
|
||||
})
|
||||
expected_context.update({
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'download_video_link': data['result'].get('download_video_link'),
|
||||
'metadata': json.dumps(expected_context['metadata'])
|
||||
})
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
assert get_context_dict_from_string(context) ==\
|
||||
get_context_dict_from_string(mako_service.render_template('video.html', expected_context))
|
||||
|
||||
@@ -1087,9 +1087,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
feature_enabled.return_value = hls_feature_enabled
|
||||
video_xml = '<video display_name="Video" download_video="true" edx_video_id="12345-67890">[]</video>'
|
||||
self.initialize_block(data=video_xml)
|
||||
self.item_descriptor.render(STUDENT_VIEW)
|
||||
self.block.render(STUDENT_VIEW)
|
||||
get_urls_for_profiles.assert_called_with(
|
||||
self.item_descriptor.edx_video_id,
|
||||
self.block.edx_video_id,
|
||||
expected_val_profiles,
|
||||
)
|
||||
|
||||
@@ -1112,7 +1112,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
}
|
||||
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
|
||||
assert "'download_video_link': 'https://mp4.com/dm.mp4'" in context
|
||||
assert '"streams": "1.00:https://yt.com/?v=v0TFmdO4ZP0"' in context
|
||||
@@ -1130,7 +1130,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
"""
|
||||
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
assert "'download_video_link': None" in context
|
||||
|
||||
def test_get_html_non_hls_video_download(self):
|
||||
@@ -1146,7 +1146,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
"""
|
||||
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
assert "'download_video_link': 'http://example.com/example.mp4'" in context
|
||||
|
||||
def test_html_student_public_view(self):
|
||||
@@ -1160,9 +1160,9 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
"""
|
||||
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
assert '"saveStateEnabled": true' in context
|
||||
context = self.item_descriptor.render(PUBLIC_VIEW).content
|
||||
context = self.block.render(PUBLIC_VIEW).content
|
||||
assert '"saveStateEnabled": false' in context
|
||||
|
||||
@patch('xmodule.video_block.video_block.edxval_api.get_course_video_image_url')
|
||||
@@ -1174,7 +1174,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
get_course_video_image_url.return_value = '/media/video-images/poster.png'
|
||||
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
|
||||
assert '"poster": "/media/video-images/poster.png"' in context
|
||||
|
||||
@@ -1187,7 +1187,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
get_course_video_image_url.return_value = '/media/video-images/poster.png'
|
||||
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
|
||||
assert "'poster': 'null'" in context
|
||||
|
||||
@@ -1198,7 +1198,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
"""
|
||||
video_xml = '<video display_name="Video" download_video="true" edx_video_id="12345-67890">[]</video>'
|
||||
self.initialize_block(data=video_xml)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
assert '"prioritizeHls": false' in context
|
||||
|
||||
@ddt.data(
|
||||
@@ -1252,7 +1252,7 @@ class TestGetHtmlMethod(BaseTestVideoXBlock):
|
||||
with patch.object(WaffleFlagCourseOverrideModel, 'override_value', return_value=data['course_override']):
|
||||
with override_waffle_flag(DEPRECATE_YOUTUBE, active=data['waffle_enabled']):
|
||||
self.initialize_block(data=video_xml, metadata=metadata)
|
||||
context = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
context = self.block.render(STUDENT_VIEW).content
|
||||
assert '"prioritizeHls": {}'.format(data['result']) in context
|
||||
|
||||
|
||||
@@ -1316,7 +1316,7 @@ class TestVideoBlockInitialization(BaseTestVideoXBlock):
|
||||
self.initialize_block(
|
||||
data='<video display_name="Video" download_video="true" edx_video_id="12345-67890">[]</video>'
|
||||
)
|
||||
context = self.item_descriptor.get_context()
|
||||
context = self.block.get_context()
|
||||
assert context['transcripts_basic_tab_metadata']['video_url']['value'] == video_url
|
||||
|
||||
@ddt.data(
|
||||
@@ -1354,7 +1354,7 @@ class TestVideoBlockInitialization(BaseTestVideoXBlock):
|
||||
self.initialize_block(
|
||||
data='<video display_name="Video" youtube_id_1_0="" download_video="true" edx_video_id="12345-67890">[]</video>'
|
||||
)
|
||||
context = self.item_descriptor.get_context()
|
||||
context = self.block.get_context()
|
||||
assert context['transcripts_basic_tab_metadata']['video_url']['value'] == video_url
|
||||
|
||||
|
||||
@@ -1388,7 +1388,7 @@ class TestEditorSavedMethod(BaseTestVideoXBlock):
|
||||
"""
|
||||
self.MODULESTORE = MODULESTORES[default_store] # pylint: disable=invalid-name
|
||||
self.initialize_block(metadata=self.metadata)
|
||||
item = self.store.get_item(self.item_descriptor.location)
|
||||
item = self.store.get_item(self.block.location)
|
||||
with open(self.file_path, "rb") as myfile: # lint-amnesty, pylint: disable=bad-option-value, open-builtin
|
||||
save_to_store(myfile.read(), self.file_name, 'text/sjson', item.location)
|
||||
item.sub = "3_yD_cEKoCk"
|
||||
@@ -1408,7 +1408,7 @@ class TestEditorSavedMethod(BaseTestVideoXBlock):
|
||||
"""
|
||||
self.MODULESTORE = MODULESTORES[default_store]
|
||||
self.initialize_block(metadata=self.metadata)
|
||||
item = self.store.get_item(self.item_descriptor.location)
|
||||
item = self.store.get_item(self.block.location)
|
||||
with open(self.file_path, "rb") as myfile: # lint-amnesty, pylint: disable=bad-option-value, open-builtin
|
||||
save_to_store(myfile.read(), self.file_name, 'text/sjson', item.location)
|
||||
save_to_store(myfile.read(), 'subs_video.srt.sjson', 'text/sjson', item.location)
|
||||
@@ -1433,7 +1433,7 @@ class TestEditorSavedMethod(BaseTestVideoXBlock):
|
||||
'edx_video_id': unstripped_video_id
|
||||
})
|
||||
self.initialize_block(metadata=self.metadata)
|
||||
item = self.store.get_item(self.item_descriptor.location)
|
||||
item = self.store.get_item(self.block.location)
|
||||
assert item.edx_video_id == unstripped_video_id
|
||||
|
||||
# Now, modifying and saving the video block should strip the video id.
|
||||
@@ -1451,7 +1451,7 @@ class TestEditorSavedMethod(BaseTestVideoXBlock):
|
||||
"""
|
||||
self.MODULESTORE = MODULESTORES[default_store]
|
||||
self.initialize_block(metadata=self.metadata)
|
||||
item = self.store.get_item(self.item_descriptor.location)
|
||||
item = self.store.get_item(self.block.location)
|
||||
assert item.youtube_id_1_0 == '3_yD_cEKoCk'
|
||||
|
||||
# Now, modify `edx_video_id` and save should override `youtube_id_1_0`.
|
||||
@@ -1493,7 +1493,7 @@ class TestVideoBlockStudentViewJson(BaseTestVideoXBlock, CacheIsolationTestCase)
|
||||
)
|
||||
self.transcript_url = "transcript_url"
|
||||
self.initialize_block(data=sample_xml)
|
||||
self.video = self.item_descriptor
|
||||
self.video = self.block
|
||||
self.video.runtime.handler_url = Mock(return_value=self.transcript_url)
|
||||
|
||||
def setup_val_video(self, associate_course_in_val=False):
|
||||
@@ -1595,7 +1595,7 @@ class TestVideoBlockStudentViewJson(BaseTestVideoXBlock, CacheIsolationTestCase)
|
||||
])
|
||||
self.transcript_url = "transcript_url"
|
||||
self.initialize_block(data=sample_xml)
|
||||
self.video = self.item_descriptor
|
||||
self.video = self.block
|
||||
self.video.runtime.handler_url = Mock(return_value=self.transcript_url)
|
||||
result = self.get_result()
|
||||
self.verify_result_with_youtube_url(result)
|
||||
@@ -1659,11 +1659,11 @@ class TestVideoBlockStudentViewJson(BaseTestVideoXBlock, CacheIsolationTestCase)
|
||||
@ddt.ddt
|
||||
class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
"""
|
||||
Tests for video descriptor that requires access to django settings.
|
||||
Tests for video block that requires access to django settings.
|
||||
"""
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.descriptor.runtime.handler_url = MagicMock()
|
||||
self.block.runtime.handler_url = MagicMock()
|
||||
self.temp_dir = mkdtemp()
|
||||
file_system = OSFS(self.temp_dir)
|
||||
self.file_system = file_system.makedir(EXPORT_IMPORT_COURSE_DIR, recreate=True)
|
||||
@@ -1695,12 +1695,12 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
'template': 'tabs/metadata-edit-tab.html'
|
||||
}
|
||||
]
|
||||
rendered_context = self.descriptor.get_context()
|
||||
rendered_context = self.block.get_context()
|
||||
self.assertListEqual(rendered_context['tabs'], correct_tabs)
|
||||
|
||||
# Assert that the Video ID field is present in basic tab metadata context.
|
||||
assert rendered_context['transcripts_basic_tab_metadata']['edx_video_id'] ==\
|
||||
self.descriptor.editable_metadata_fields['edx_video_id']
|
||||
self.block.editable_metadata_fields['edx_video_id']
|
||||
|
||||
def test_export_val_data_with_internal(self):
|
||||
"""
|
||||
@@ -1712,11 +1712,11 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
combine(self.temp_dir, EXPORT_IMPORT_COURSE_DIR),
|
||||
combine(EXPORT_IMPORT_STATIC_DIR, transcript_file_name)
|
||||
)
|
||||
self.descriptor.edx_video_id = 'test_edx_video_id'
|
||||
self.block.edx_video_id = 'test_edx_video_id'
|
||||
|
||||
create_profile('mobile')
|
||||
create_video({
|
||||
'edx_video_id': self.descriptor.edx_video_id,
|
||||
'edx_video_id': self.block.edx_video_id,
|
||||
'client_video_id': 'test_client_video_id',
|
||||
'duration': 111.0,
|
||||
'status': 'dummy',
|
||||
@@ -1728,7 +1728,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
}],
|
||||
})
|
||||
create_or_update_video_transcript(
|
||||
video_id=self.descriptor.edx_video_id,
|
||||
video_id=self.block.edx_video_id,
|
||||
language_code=language_code,
|
||||
metadata={
|
||||
'provider': 'Cielo24',
|
||||
@@ -1737,7 +1737,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
file_data=ContentFile(TRANSCRIPT_FILE_SRT_DATA)
|
||||
)
|
||||
|
||||
actual = self.descriptor.definition_to_xml(resource_fs=self.file_system)
|
||||
actual = self.block.definition_to_xml(resource_fs=self.file_system)
|
||||
expected_str = """
|
||||
<video youtube="1.00:3_yD_cEKoCk" url_name="SampleProblem" transcripts='{transcripts}'>
|
||||
<video_asset client_video_id="test_client_video_id" duration="111.0" image="">
|
||||
@@ -1763,7 +1763,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
# Also verify the content of created transcript file.
|
||||
with open(expected_transcript_path) as transcript_path:
|
||||
expected_transcript_content = File(transcript_path).read()
|
||||
transcript = get_video_transcript_data(video_id=self.descriptor.edx_video_id, language_code=language_code)
|
||||
transcript = get_video_transcript_data(video_id=self.block.edx_video_id, language_code=language_code)
|
||||
assert transcript['content'].decode('utf-8') == expected_transcript_content
|
||||
|
||||
@ddt.data(
|
||||
@@ -1775,13 +1775,13 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
"""
|
||||
Tests new transcripts export for backward compatibility.
|
||||
"""
|
||||
self.descriptor.edx_video_id = 'test_video_id'
|
||||
self.descriptor.sub = sub
|
||||
self.block.edx_video_id = 'test_video_id'
|
||||
self.block.sub = sub
|
||||
|
||||
# Setup VAL encode profile, video and transcripts
|
||||
create_profile('mobile')
|
||||
create_video({
|
||||
'edx_video_id': self.descriptor.edx_video_id,
|
||||
'edx_video_id': self.block.edx_video_id,
|
||||
'client_video_id': 'test_client_video_id',
|
||||
'duration': 111.0,
|
||||
'status': 'dummy',
|
||||
@@ -1795,21 +1795,21 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
|
||||
for language in languages:
|
||||
create_video_transcript(
|
||||
video_id=self.descriptor.edx_video_id,
|
||||
video_id=self.block.edx_video_id,
|
||||
language_code=language,
|
||||
file_format=Transcript.SRT,
|
||||
content=ContentFile(TRANSCRIPT_FILE_SRT_DATA)
|
||||
)
|
||||
|
||||
# Export the video block into xml
|
||||
video_xml = self.descriptor.definition_to_xml(resource_fs=self.file_system)
|
||||
video_xml = self.block.definition_to_xml(resource_fs=self.file_system)
|
||||
|
||||
# Assert `sub` and `transcripts` attribute in the xml
|
||||
assert video_xml.get('sub') == expected_sub
|
||||
|
||||
expected_transcripts = {
|
||||
language: "{edx_video_id}-{language}.srt".format(
|
||||
edx_video_id=self.descriptor.edx_video_id,
|
||||
edx_video_id=self.block.edx_video_id,
|
||||
language=language
|
||||
)
|
||||
for language in languages
|
||||
@@ -1824,15 +1824,15 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
)
|
||||
with open(expected_transcript_path) as transcript_path:
|
||||
expected_transcript_content = File(transcript_path).read()
|
||||
transcript = get_video_transcript_data(video_id=self.descriptor.edx_video_id, language_code=language)
|
||||
transcript = get_video_transcript_data(video_id=self.block.edx_video_id, language_code=language)
|
||||
assert transcript['content'].decode('utf-8') == expected_transcript_content
|
||||
|
||||
def test_export_val_data_not_found(self):
|
||||
"""
|
||||
Tests that external video export works as expected.
|
||||
"""
|
||||
self.descriptor.edx_video_id = 'nonexistent'
|
||||
actual = self.descriptor.definition_to_xml(resource_fs=self.file_system)
|
||||
self.block.edx_video_id = 'nonexistent'
|
||||
actual = self.block.definition_to_xml(resource_fs=self.file_system)
|
||||
expected_str = """<video youtube="1.00:3_yD_cEKoCk" url_name="SampleProblem"/>"""
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
expected = etree.XML(expected_str, parser=parser)
|
||||
@@ -1845,7 +1845,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
"""
|
||||
mock_get_video_ids_info.return_value = True, []
|
||||
|
||||
actual = self.descriptor.definition_to_xml(resource_fs=self.file_system)
|
||||
actual = self.block.definition_to_xml(resource_fs=self.file_system)
|
||||
expected_str = '<video youtube="1.00:3_yD_cEKoCk" url_name="SampleProblem"></video>'
|
||||
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
@@ -1883,7 +1883,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
# Create self.sub and self.transcripts transcript.
|
||||
create_file_in_fs(
|
||||
TRANSCRIPT_FILE_SRT_DATA,
|
||||
subs_filename(sub_id, self.descriptor.transcript_language),
|
||||
subs_filename(sub_id, self.block.transcript_language),
|
||||
module_system.resources_fs,
|
||||
EXPORT_IMPORT_STATIC_DIR
|
||||
)
|
||||
@@ -1913,7 +1913,7 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
xml_object = etree.fromstring(xml_data)
|
||||
id_generator = Mock()
|
||||
id_generator.target_course_id = "test_course_id"
|
||||
video = self.descriptor.parse_xml(xml_object, module_system, None, id_generator)
|
||||
video = self.block.parse_xml(xml_object, module_system, None, id_generator)
|
||||
|
||||
assert video.edx_video_id == 'test_edx_video_id'
|
||||
video_data = get_video_info(video.edx_video_id)
|
||||
@@ -1940,9 +1940,9 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
self.assertDictContainsSubset(
|
||||
self.get_video_transcript_data(
|
||||
edx_video_id,
|
||||
language_code=self.descriptor.transcript_language
|
||||
language_code=self.block.transcript_language
|
||||
),
|
||||
get_video_transcript(video.edx_video_id, self.descriptor.transcript_language)
|
||||
get_video_transcript(video.edx_video_id, self.block.transcript_language)
|
||||
)
|
||||
|
||||
# Verify that transcript from transcript field is imported.
|
||||
@@ -1964,9 +1964,9 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
id_generator = Mock()
|
||||
|
||||
# Verify edx_video_id is empty before.
|
||||
assert self.descriptor.edx_video_id == ''
|
||||
assert self.block.edx_video_id == ''
|
||||
|
||||
video = self.descriptor.parse_xml(xml_object, module_system, None, id_generator)
|
||||
video = self.block.parse_xml(xml_object, module_system, None, id_generator)
|
||||
|
||||
# Verify edx_video_id is populated after the import.
|
||||
assert video.edx_video_id != ''
|
||||
@@ -2012,9 +2012,9 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
)
|
||||
|
||||
# Verify edx_video_id is empty before.
|
||||
assert self.descriptor.edx_video_id == ''
|
||||
assert self.block.edx_video_id == ''
|
||||
|
||||
video = self.descriptor.parse_xml(xml_object, module_system, None, id_generator)
|
||||
video = self.block.parse_xml(xml_object, module_system, None, id_generator)
|
||||
|
||||
# Verify edx_video_id is populated after the import.
|
||||
assert video.edx_video_id != ''
|
||||
@@ -2154,9 +2154,9 @@ class VideoBlockTest(TestCase, VideoBlockTestBase):
|
||||
xml_object = etree.fromstring(xml_data)
|
||||
|
||||
# Verify edx_video_id is empty before import.
|
||||
assert self.descriptor.edx_video_id == ''
|
||||
assert self.block.edx_video_id == ''
|
||||
|
||||
video = self.descriptor.parse_xml(xml_object, module_system, None, id_generator)
|
||||
video = self.block.parse_xml(xml_object, module_system, None, id_generator)
|
||||
|
||||
# Verify edx_video_id is not empty after import.
|
||||
assert video.edx_video_id != ''
|
||||
@@ -2215,12 +2215,12 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests
|
||||
"transcripts": {},
|
||||
}
|
||||
with override_settings(FEATURES=self.FEATURES):
|
||||
assert bumper_utils.is_bumper_enabled(self.item_descriptor)
|
||||
assert bumper_utils.is_bumper_enabled(self.block)
|
||||
|
||||
self.FEATURES.update({"ENABLE_VIDEO_BUMPER": False})
|
||||
|
||||
with override_settings(FEATURES=self.FEATURES):
|
||||
assert not bumper_utils.is_bumper_enabled(self.item_descriptor)
|
||||
assert not bumper_utils.is_bumper_enabled(self.block)
|
||||
|
||||
@patch('xmodule.video_block.bumper_utils.is_bumper_enabled')
|
||||
@patch('xmodule.video_block.bumper_utils.get_bumper_settings')
|
||||
@@ -2241,14 +2241,14 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests
|
||||
|
||||
is_bumper_enabled.return_value = True
|
||||
|
||||
content = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
content = self.block.render(STUDENT_VIEW).content
|
||||
sources = ['example.mp4', 'example.webm']
|
||||
expected_context = {
|
||||
'autoadvance_enabled': False,
|
||||
'branding_info': None,
|
||||
'license': None,
|
||||
'bumper_metadata': json.dumps(OrderedDict({
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'showCaptions': 'true',
|
||||
'sources': ['http://test_bumper.mp4'],
|
||||
'streams': '',
|
||||
@@ -2271,11 +2271,11 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests
|
||||
'handout': None,
|
||||
'hide_downloads': False,
|
||||
'is_embed': False,
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'metadata': json.dumps(OrderedDict({
|
||||
'autoAdvance': False,
|
||||
'saveStateEnabled': True,
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'autoplay': False,
|
||||
'streams': '0.75:jNCf2gIqpeE,1.00:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg',
|
||||
'sources': sources,
|
||||
@@ -2316,7 +2316,7 @@ class TestVideoWithBumper(TestVideo): # pylint: disable=test-inherits-tests
|
||||
}))
|
||||
}
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
expected_content = mako_service.render_template('video.html', expected_context)
|
||||
assert get_context_dict_from_string(content) == get_context_dict_from_string(expected_content)
|
||||
|
||||
@@ -2348,12 +2348,12 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
'handout': None,
|
||||
'hide_downloads': False,
|
||||
'is_embed': False,
|
||||
'id': self.item_descriptor.location.html_id(),
|
||||
'id': self.block.location.html_id(),
|
||||
'bumper_metadata': 'null',
|
||||
'metadata': json.dumps(OrderedDict({
|
||||
'autoAdvance': autoadvance_flag,
|
||||
'saveStateEnabled': True,
|
||||
'saveStateUrl': self.item_descriptor.ajax_url + '/save_user_state',
|
||||
'saveStateUrl': self.block.ajax_url + '/save_user_state',
|
||||
'autoplay': False,
|
||||
'streams': '0.75:jNCf2gIqpeE,1.00:ZwkTiUPN0mg,1.25:rsq9auxASqI,1.50:kMyNdzVHHgg',
|
||||
'sources': ['example.mp4', 'example.webm'],
|
||||
@@ -2372,11 +2372,11 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
'ytTestTimeout': 1500,
|
||||
'ytApiUrl': 'https://www.youtube.com/iframe_api',
|
||||
'lmsRootURL': settings.LMS_ROOT_URL,
|
||||
'transcriptTranslationUrl': self.item_descriptor.runtime.handler_url(
|
||||
self.item_descriptor, 'transcript', 'translation/__lang__'
|
||||
'transcriptTranslationUrl': self.block.runtime.handler_url(
|
||||
self.block, 'transcript', 'translation/__lang__'
|
||||
).rstrip('/?'),
|
||||
'transcriptAvailableTranslationsUrl': self.item_descriptor.runtime.handler_url(
|
||||
self.item_descriptor, 'transcript', 'available_translations'
|
||||
'transcriptAvailableTranslationsUrl': self.block.runtime.handler_url(
|
||||
self.block, 'transcript', 'available_translations'
|
||||
).rstrip('/?'),
|
||||
'autohideHtml5': False,
|
||||
'recordedYoutubeIsAvailable': True,
|
||||
@@ -2404,14 +2404,14 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
"""
|
||||
|
||||
with override_settings(FEATURES=self.FEATURES):
|
||||
content = self.item_descriptor.render(STUDENT_VIEW).content
|
||||
content = self.block.render(STUDENT_VIEW).content
|
||||
|
||||
expected_context = self.prepare_expected_context(
|
||||
autoadvanceenabled_flag=autoadvanceenabled_must_be,
|
||||
autoadvance_flag=autoadvance_must_be,
|
||||
)
|
||||
|
||||
mako_service = self.item_descriptor.runtime.service(self.item_descriptor, 'mako')
|
||||
mako_service = self.block.runtime.service(self.block, 'mako')
|
||||
with override_settings(FEATURES=self.FEATURES):
|
||||
expected_content = mako_service.render_template('video.html', expected_context)
|
||||
|
||||
@@ -2424,11 +2424,11 @@ class TestAutoAdvanceVideo(TestVideo): # lint-amnesty, pylint: disable=test-inh
|
||||
Based on test code for video_bumper setting.
|
||||
"""
|
||||
# This first render is done to initialize the instance
|
||||
self.item_descriptor.render(STUDENT_VIEW)
|
||||
self.item_descriptor.video_auto_advance = new_value
|
||||
self.item_descriptor._reset_dirty_field(self.item_descriptor.fields['video_auto_advance']) # pylint: disable=protected-access
|
||||
self.block.render(STUDENT_VIEW)
|
||||
self.block.video_auto_advance = new_value
|
||||
self.block._reset_dirty_field(self.block.fields['video_auto_advance']) # pylint: disable=protected-access
|
||||
# After this step, render() should see the new value
|
||||
# e.g. use self.item_descriptor.render(STUDENT_VIEW).content
|
||||
# e.g. use self.block.render(STUDENT_VIEW).content
|
||||
|
||||
@ddt.data(
|
||||
(False, False),
|
||||
|
||||
@@ -1936,7 +1936,7 @@ class ProgressPageShowCorrectnessTests(ProgressPageBaseTests):
|
||||
Submit the given score to the problem on behalf of the user
|
||||
"""
|
||||
# Get the block for the problem, as viewed by the user
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course.id,
|
||||
self.user,
|
||||
self.course,
|
||||
|
||||
@@ -18,15 +18,6 @@ class TestWordCloud(BaseTestXmodule):
|
||||
"""Integration test for Word Cloud Block."""
|
||||
CATEGORY = "word_cloud"
|
||||
|
||||
def _get_resource_url(self, item):
|
||||
"""
|
||||
Creates a resource URL for a given asset that is compatible with this old XModule testing stuff.
|
||||
"""
|
||||
display_name = self.item_descriptor.display_name.replace(' ', '_')
|
||||
return "resource/i4x://{}/{}/word_cloud/{}/{}".format(
|
||||
self.course.id.org, self.course.id.course, display_name, item
|
||||
)
|
||||
|
||||
def _get_users_state(self):
|
||||
"""Return current state for each user:
|
||||
|
||||
@@ -227,13 +218,13 @@ class TestWordCloud(BaseTestXmodule):
|
||||
"""
|
||||
Make sure that all parameters extracted correctly from xml.
|
||||
"""
|
||||
fragment = self.runtime.render(self.item_descriptor, STUDENT_VIEW)
|
||||
fragment = self.runtime.render(self.block, STUDENT_VIEW)
|
||||
expected_context = {
|
||||
'ajax_url': self.item_descriptor.ajax_url,
|
||||
'display_name': self.item_descriptor.display_name,
|
||||
'instructions': self.item_descriptor.instructions,
|
||||
'element_class': self.item_descriptor.location.block_type,
|
||||
'element_id': self.item_descriptor.location.html_id(),
|
||||
'ajax_url': self.block.ajax_url,
|
||||
'display_name': self.block.display_name,
|
||||
'instructions': self.block.instructions,
|
||||
'element_class': self.block.location.block_type,
|
||||
'element_id': self.block.location.html_id(),
|
||||
'num_inputs': 5, # default value
|
||||
'submitted': False, # default value,
|
||||
}
|
||||
|
||||
@@ -63,32 +63,32 @@ class PageLoaderTestCase(LoginEnrollmentTestCase):
|
||||
self.fail('Could not retrieve any items from course')
|
||||
|
||||
# Try to load each item in the course
|
||||
for descriptor in items:
|
||||
for block in items:
|
||||
|
||||
if descriptor.location.category == 'about':
|
||||
if block.location.category == 'about':
|
||||
self._assert_loads('about_course',
|
||||
{'course_id': str(course_key)},
|
||||
descriptor)
|
||||
block)
|
||||
|
||||
elif descriptor.location.category == 'static_tab':
|
||||
elif block.location.category == 'static_tab':
|
||||
kwargs = {'course_id': str(course_key),
|
||||
'tab_slug': descriptor.location.name}
|
||||
self._assert_loads('static_tab', kwargs, descriptor)
|
||||
'tab_slug': block.location.name}
|
||||
self._assert_loads('static_tab', kwargs, block)
|
||||
|
||||
elif descriptor.location.category == 'course_info':
|
||||
elif block.location.category == 'course_info':
|
||||
self._assert_loads('info', {'course_id': str(course_key)},
|
||||
descriptor)
|
||||
block)
|
||||
|
||||
else:
|
||||
|
||||
kwargs = {'course_id': str(course_key),
|
||||
'location': str(descriptor.location)}
|
||||
'location': str(block.location)}
|
||||
|
||||
self._assert_loads('jump_to', kwargs, descriptor,
|
||||
self._assert_loads('jump_to', kwargs, block,
|
||||
expect_redirect=True,
|
||||
check_content=True)
|
||||
|
||||
def _assert_loads(self, django_url, kwargs, descriptor,
|
||||
def _assert_loads(self, django_url, kwargs, block,
|
||||
expect_redirect=False,
|
||||
check_content=False):
|
||||
"""
|
||||
@@ -103,14 +103,14 @@ class PageLoaderTestCase(LoginEnrollmentTestCase):
|
||||
|
||||
if response.status_code != 200:
|
||||
self.fail('Status %d for page %s' %
|
||||
(response.status_code, descriptor.location))
|
||||
(response.status_code, block.location))
|
||||
|
||||
if expect_redirect:
|
||||
assert response.redirect_chain[0][1] == 302
|
||||
|
||||
if check_content:
|
||||
self.assertNotContains(response, "this module is temporarily unavailable")
|
||||
assert not isinstance(descriptor, ErrorBlock)
|
||||
assert not isinstance(block, ErrorBlock)
|
||||
|
||||
|
||||
class TestMongoCoursesLoad(ModuleStoreTestCase, PageLoaderTestCase):
|
||||
@@ -156,7 +156,7 @@ class TestLmsFieldData(TestCase):
|
||||
# Verify that if an LmsFieldData is passed into LmsFieldData as the
|
||||
# authored_data, that it doesn't produced a nested field data.
|
||||
#
|
||||
# This fixes a bug where re-use of the same descriptor for many modules
|
||||
# This fixes a bug where re-use of the same block for many modules
|
||||
# would cause more and more nesting, until the recursion depth would be
|
||||
# reached on any attribute access
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
|
||||
|
||||
def _nr_attribute_name(self, function_name, stat_name, block_type=None):
|
||||
"""
|
||||
Return an attribute name (string) representing the provided descriptors.
|
||||
Return an attribute name (string) representing the provided blocks.
|
||||
The return value is directly usable for New Relic custom attributes.
|
||||
"""
|
||||
if block_type is None:
|
||||
|
||||
@@ -349,7 +349,7 @@ class CoursewareIndex(View):
|
||||
Prefetches all descendant data for the requested section and
|
||||
sets up the runtime, which binds the request user to the section.
|
||||
"""
|
||||
self.field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
self.field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
self.course_key,
|
||||
self.effective_user,
|
||||
self.course,
|
||||
@@ -374,7 +374,7 @@ class CoursewareIndex(View):
|
||||
"""
|
||||
# Pre-fetch all descendant data
|
||||
self.section = modulestore().get_item(self.section.location, depth=None, lazy=False)
|
||||
self.field_data_cache.add_descriptor_descendents(self.section, depth=None)
|
||||
self.field_data_cache.add_block_descendents(self.section, depth=None)
|
||||
|
||||
# Bind section to user
|
||||
self.section = get_block_for_descriptor(
|
||||
@@ -585,11 +585,11 @@ def save_positions_recursively_up(user, request, field_data_cache, xmodule, cour
|
||||
parent_location = modulestore().get_parent_location(current_block.location)
|
||||
parent = None
|
||||
if parent_location:
|
||||
parent_descriptor = modulestore().get_item(parent_location)
|
||||
parent_block = modulestore().get_item(parent_location)
|
||||
parent = get_block_for_descriptor(
|
||||
user,
|
||||
request,
|
||||
parent_descriptor,
|
||||
parent_block,
|
||||
field_data_cache,
|
||||
current_block.location.course_key,
|
||||
course=course
|
||||
|
||||
@@ -1262,7 +1262,7 @@ def get_static_tab_fragment(request, course, tab):
|
||||
tab.type,
|
||||
tab.url_slug,
|
||||
)
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
field_data_cache = FieldDataCache.cache_for_block_descendents(
|
||||
course.id, request.user, modulestore().get_item(loc), depth=0
|
||||
)
|
||||
tab_block = get_block(
|
||||
@@ -1309,23 +1309,23 @@ def get_course_lti_endpoints(request, course_id):
|
||||
|
||||
anonymous_user = AnonymousUser()
|
||||
anonymous_user.known = False # make these "noauth" requests like block_render.handle_xblock_callback_noauth
|
||||
lti_descriptors = modulestore().get_items(course.id, qualifiers={'category': 'lti'})
|
||||
lti_descriptors.extend(modulestore().get_items(course.id, qualifiers={'category': 'lti_consumer'}))
|
||||
lti_blocks = modulestore().get_items(course.id, qualifiers={'category': 'lti'})
|
||||
lti_blocks.extend(modulestore().get_items(course.id, qualifiers={'category': 'lti_consumer'}))
|
||||
|
||||
lti_noauth_blocks = [
|
||||
get_block_for_descriptor(
|
||||
anonymous_user,
|
||||
request,
|
||||
descriptor,
|
||||
FieldDataCache.cache_for_descriptor_descendents(
|
||||
block,
|
||||
FieldDataCache.cache_for_block_descendents(
|
||||
course_key,
|
||||
anonymous_user,
|
||||
descriptor
|
||||
block
|
||||
),
|
||||
course_key,
|
||||
course=course
|
||||
)
|
||||
for descriptor in lti_descriptors
|
||||
for block in lti_blocks
|
||||
]
|
||||
|
||||
endpoints = [
|
||||
|
||||
Reference in New Issue
Block a user