refactor: rename module (or item) -> block within remaining lms
Also, removed `_iter_scorable_xmodules` method from `lms/djangoapps/grades/transformer.py` file.
This commit is contained in:
@@ -25,7 +25,7 @@ logger = logging.getLogger(__name__)
|
||||
class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransformer):
|
||||
"""
|
||||
A transformer that manipulates the block structure by removing all
|
||||
blocks within a library_content module to which a user should not
|
||||
blocks within a library_content block to which a user should not
|
||||
have access.
|
||||
|
||||
Staff users are not to be exempted from library content pathways.
|
||||
@@ -90,7 +90,7 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
|
||||
state_dict = get_student_module_as_dict(usage_info.user, usage_info.course_key, block_key)
|
||||
for selected_block in state_dict.get('selected', []):
|
||||
# Add all selected entries for this user for this
|
||||
# library module to the selected list.
|
||||
# library block to the selected list.
|
||||
block_type, block_id = selected_block
|
||||
usage_key = usage_info.course_key.make_usage_key(block_type, block_id)
|
||||
if usage_key in library_children:
|
||||
@@ -184,7 +184,7 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
|
||||
class ContentLibraryOrderTransformer(BlockStructureTransformer):
|
||||
"""
|
||||
A transformer that manipulates the block structure by modifying the order of the
|
||||
selected blocks within a library_content module to match the order of the selections
|
||||
selected blocks within a library_content block to match the order of the selections
|
||||
made by the ContentLibraryTransformer or the corresponding XBlock. So this transformer
|
||||
requires the selections for the randomized content block to be already
|
||||
made either by the ContentLibraryTransformer or the XBlock.
|
||||
|
||||
@@ -12,13 +12,13 @@ from openedx.core.djangoapps.content.block_structure.transformer import (
|
||||
class SplitTestTransformer(FilteringTransformerMixin, BlockStructureTransformer):
|
||||
"""
|
||||
A nested transformer of the UserPartitionTransformer that honors the
|
||||
block structure pathways created by split_test modules.
|
||||
block structure pathways created by split_test blocks.
|
||||
|
||||
To avoid code duplication, the implementation transforms its block
|
||||
access representation to the representation used by user_partitions.
|
||||
Namely, the 'group_id_to_child' field on a split_test module is
|
||||
Namely, the 'group_id_to_child' field on a split_test block is
|
||||
transformed into the, now standard, 'group_access' fields in the
|
||||
split_test module's children.
|
||||
split_test block's children.
|
||||
|
||||
The implementation therefore relies on the UserPartitionTransformer
|
||||
to actually enforce the access using the 'user_partitions' and
|
||||
@@ -61,7 +61,7 @@ class SplitTestTransformer(FilteringTransformerMixin, BlockStructureTransformer)
|
||||
continue
|
||||
|
||||
# Create dict of child location to group_id, using the
|
||||
# group_id_to_child field on the split_test module.
|
||||
# group_id_to_child field on the split_test block.
|
||||
child_to_group = {
|
||||
xblock.group_id_to_child.get(str(group.id), None): group.id
|
||||
for group in partition_for_this_block.groups
|
||||
@@ -80,7 +80,7 @@ class SplitTestTransformer(FilteringTransformerMixin, BlockStructureTransformer)
|
||||
"""
|
||||
|
||||
# The UserPartitionTransformer will enforce group access, so
|
||||
# go ahead and remove all extraneous split_test modules.
|
||||
# go ahead and remove all extraneous split_test blocks.
|
||||
return [
|
||||
block_structure.create_removal_filter(
|
||||
lambda block_key: block_key.block_type == 'split_test',
|
||||
|
||||
@@ -209,9 +209,9 @@ class ThreadActionGroupIdTestCase(
|
||||
|
||||
class ViewsTestCaseMixin:
|
||||
|
||||
def set_up_course(self, module_count=0):
|
||||
def set_up_course(self, block_count=0):
|
||||
"""
|
||||
Creates a course, optionally with module_count discussion modules, and
|
||||
Creates a course, optionally with block_count discussion blocks, and
|
||||
a user with appropriate permissions.
|
||||
"""
|
||||
|
||||
@@ -223,8 +223,8 @@ class ViewsTestCaseMixin:
|
||||
)
|
||||
self.course_id = self.course.id
|
||||
|
||||
# add some discussion modules
|
||||
for i in range(module_count):
|
||||
# add some discussion blocks
|
||||
for i in range(block_count):
|
||||
BlockFactory.create(
|
||||
parent_location=self.course.location,
|
||||
category='discussion',
|
||||
@@ -396,9 +396,9 @@ class ViewsQueryCountTestCase(
|
||||
Decorates test methods to count mongo and SQL calls for a
|
||||
particular modulestore.
|
||||
"""
|
||||
def inner(self, default_store, module_count, mongo_calls, sql_queries, *args, **kwargs):
|
||||
def inner(self, default_store, block_count, mongo_calls, sql_queries, *args, **kwargs):
|
||||
with modulestore().default_store(default_store):
|
||||
self.set_up_course(module_count=module_count)
|
||||
self.set_up_course(block_count=block_count)
|
||||
self.clear_caches()
|
||||
with self.assertNumQueries(sql_queries, table_ignorelist=QUERY_COUNT_TABLE_IGNORELIST):
|
||||
with check_mongo_calls(mongo_calls):
|
||||
|
||||
@@ -177,7 +177,7 @@ class CoursewareContextTestCase(ModuleStoreTestCase):
|
||||
|
||||
def test_empty_discussion_subcategory_title(self):
|
||||
"""
|
||||
Test that for empty subcategory inline discussion modules,
|
||||
Test that for empty subcategory inline discussion blocks,
|
||||
the divider " / " is not rendered on a post or inline discussion topic label.
|
||||
"""
|
||||
discussion = BlockFactory.create(
|
||||
|
||||
@@ -505,8 +505,8 @@ class GetCourseTopicsTest(CommentsServiceMockMixin, ForumsEnableMixin, UrlResetM
|
||||
Test that only topics that a user has access to are returned. The
|
||||
ways in which a user may not have access are:
|
||||
|
||||
* Module is visible to staff only
|
||||
* Module is accessible only to a group the user is not in
|
||||
* Block is visible to staff only
|
||||
* Block is accessible only to a group the user is not in
|
||||
|
||||
Also, there is a case that ensures that a category with no accessible
|
||||
subcategories does not appear in the result.
|
||||
|
||||
@@ -730,7 +730,7 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, CommentsServiceMockMixin,
|
||||
}
|
||||
self.register_get_course_commentable_counts_response(self.course.id, self.thread_counts_map)
|
||||
|
||||
def create_course(self, modules_count, module_store, topics):
|
||||
def create_course(self, blocks_count, module_store, topics):
|
||||
"""
|
||||
Create a course in a specified module store with discussion xblocks and topics
|
||||
"""
|
||||
@@ -745,7 +745,7 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, CommentsServiceMockMixin,
|
||||
CourseEnrollmentFactory.create(user=self.user, course_id=course.id)
|
||||
course_url = reverse("course_topics", kwargs={"course_id": str(course.id)})
|
||||
# add some discussion xblocks
|
||||
for i in range(modules_count):
|
||||
for i in range(blocks_count):
|
||||
BlockFactory.create(
|
||||
parent_location=course.location,
|
||||
category='discussion',
|
||||
@@ -804,8 +804,8 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, CommentsServiceMockMixin,
|
||||
(10, ModuleStoreEnum.Type.split, 2, {"Test Topic 1": {"id": "test_topic_1"}}),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_bulk_response(self, modules_count, module_store, mongo_calls, topics):
|
||||
course_url, course_id = self.create_course(modules_count, module_store, topics)
|
||||
def test_bulk_response(self, blocks_count, module_store, mongo_calls, topics):
|
||||
course_url, course_id = self.create_course(blocks_count, module_store, topics)
|
||||
self.register_get_course_commentable_counts_response(course_id, {})
|
||||
with check_mongo_calls(mongo_calls):
|
||||
with modulestore().default_store(module_store):
|
||||
|
||||
@@ -793,7 +793,7 @@ class SingleThreadGroupIdTestCase(CohortedTestCase, GroupIdAssertionMixin): # l
|
||||
class ForumFormDiscussionContentGroupTestCase(ForumsEnableMixin, ContentGroupTestCase):
|
||||
"""
|
||||
Tests `forum_form_discussion api` works with different content groups.
|
||||
Discussion modules are setup in ContentGroupTestCase class i.e
|
||||
Discussion blocks are setup in ContentGroupTestCase class i.e
|
||||
alpha_block => alpha_group_discussion => alpha_cohort => alpha_user/community_ta
|
||||
beta_block => beta_group_discussion => beta_cohort => beta_user
|
||||
"""
|
||||
@@ -813,7 +813,7 @@ class ForumFormDiscussionContentGroupTestCase(ForumsEnableMixin, ContentGroupTes
|
||||
def assert_has_access(self, response, expected_discussion_threads):
|
||||
"""
|
||||
Verify that a users have access to the threads in their assigned
|
||||
cohorts and non-cohorted modules.
|
||||
cohorts and non-cohorted blocks.
|
||||
"""
|
||||
discussion_data = json.loads(response.content.decode('utf-8'))['discussion_data']
|
||||
assert len(discussion_data) == expected_discussion_threads
|
||||
@@ -902,7 +902,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
|
||||
def test_staff_user(self, mock_request):
|
||||
"""
|
||||
Verify that the staff user can access threads in the alpha,
|
||||
beta, and global discussion modules.
|
||||
beta, and global discussion blocks.
|
||||
"""
|
||||
thread_id = "test_thread_id"
|
||||
mock_request.side_effect = make_mock_request_impl(course=self.course, text="dummy content", thread_id=thread_id)
|
||||
@@ -913,7 +913,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
|
||||
def test_alpha_user(self, mock_request):
|
||||
"""
|
||||
Verify that the alpha user can access threads in the alpha and
|
||||
global discussion modules.
|
||||
global discussion blocks.
|
||||
"""
|
||||
thread_id = "test_thread_id"
|
||||
mock_request.side_effect = make_mock_request_impl(course=self.course, text="dummy content", thread_id=thread_id)
|
||||
@@ -926,7 +926,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
|
||||
def test_beta_user(self, mock_request):
|
||||
"""
|
||||
Verify that the beta user can access threads in the beta and
|
||||
global discussion modules.
|
||||
global discussion blocks.
|
||||
"""
|
||||
thread_id = "test_thread_id"
|
||||
mock_request.side_effect = make_mock_request_impl(course=self.course, text="dummy content", thread_id=thread_id)
|
||||
@@ -939,7 +939,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
|
||||
def test_non_cohorted_user(self, mock_request):
|
||||
"""
|
||||
Verify that the non-cohorted user can access threads in just the
|
||||
global discussion module.
|
||||
global discussion blocks.
|
||||
"""
|
||||
thread_id = "test_thread_id"
|
||||
mock_request.side_effect = make_mock_request_impl(course=self.course, text="dummy content", thread_id=thread_id)
|
||||
@@ -966,7 +966,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
|
||||
"""
|
||||
Verify that standalone threads don't go through discussion_category_id_access method.
|
||||
"""
|
||||
# For this rather pathological test, we are assigning the alpha module discussion_id (commentable_id)
|
||||
# For this rather pathological test, we are assigning the alpha block discussion_id (commentable_id)
|
||||
# to a team so that we can verify that standalone threads don't go through discussion_category_id_access.
|
||||
thread_id = "test_thread_id"
|
||||
CourseTeamFactory(
|
||||
@@ -981,7 +981,7 @@ class SingleThreadContentGroupTestCase(ForumsEnableMixin, UrlResetMixin, Content
|
||||
)
|
||||
|
||||
# If a thread returns context other than "course", the access check is not done, and the beta user
|
||||
# can see the alpha discussion module.
|
||||
# can see the alpha discussion block.
|
||||
self.assert_can_access(self.beta_user, self.alpha_block.discussion_id, thread_id, True)
|
||||
|
||||
|
||||
|
||||
@@ -421,7 +421,7 @@ def _find_thread(request, course, discussion_id, thread_id):
|
||||
)
|
||||
except cc.utils.CommentClientRequestError:
|
||||
return None
|
||||
# Verify that the student has access to this thread if belongs to a course discussion module
|
||||
# Verify that the student has access to this thread if belongs to a course discussion block
|
||||
thread_context = getattr(thread, "context", "course")
|
||||
if thread_context == "course" and not utils.discussion_category_id_access(course, request.user, discussion_id):
|
||||
return None
|
||||
|
||||
@@ -157,7 +157,7 @@ def preprocess_collection(user, course, collection):
|
||||
convert "updated" to date
|
||||
|
||||
Raises:
|
||||
ItemNotFoundError - when appropriate module is not found.
|
||||
ItemNotFoundError - when appropriate block is not found.
|
||||
"""
|
||||
# pylint: disable=too-many-statements
|
||||
|
||||
@@ -185,7 +185,7 @@ def preprocess_collection(user, course, collection):
|
||||
try:
|
||||
item = store.get_item(usage_key)
|
||||
except ItemNotFoundError:
|
||||
log.debug("Module not found: %s", usage_key)
|
||||
log.debug("Block not found: %s", usage_key)
|
||||
continue
|
||||
|
||||
if not has_access(user, "load", item, course_key=course.id):
|
||||
@@ -205,7 +205,7 @@ def preprocess_collection(user, course, collection):
|
||||
if section.location in list(cache.keys()): # lint-amnesty, pylint: disable=consider-iterating-dictionary
|
||||
usage_context = cache[section.location]
|
||||
usage_context.update({
|
||||
"unit": get_module_context(course, unit),
|
||||
"unit": get_block_context(course, unit),
|
||||
})
|
||||
model.update(usage_context)
|
||||
cache[usage_id] = cache[unit.location] = usage_context
|
||||
@@ -219,8 +219,8 @@ def preprocess_collection(user, course, collection):
|
||||
if chapter.location in list(cache.keys()): # lint-amnesty, pylint: disable=consider-iterating-dictionary
|
||||
usage_context = cache[chapter.location]
|
||||
usage_context.update({
|
||||
"unit": get_module_context(course, unit),
|
||||
"section": get_module_context(course, section),
|
||||
"unit": get_block_context(course, unit),
|
||||
"section": get_block_context(course, section),
|
||||
})
|
||||
model.update(usage_context)
|
||||
cache[usage_id] = cache[unit.location] = cache[section.location] = usage_context
|
||||
@@ -228,9 +228,9 @@ def preprocess_collection(user, course, collection):
|
||||
continue
|
||||
|
||||
usage_context = {
|
||||
"unit": get_module_context(course, unit),
|
||||
"section": get_module_context(course, section) if include_path_info else {},
|
||||
"chapter": get_module_context(course, chapter) if include_path_info else {},
|
||||
"unit": get_block_context(course, unit),
|
||||
"section": get_block_context(course, section) if include_path_info else {},
|
||||
"chapter": get_block_context(course, chapter) if include_path_info else {},
|
||||
}
|
||||
model.update(usage_context)
|
||||
if include_path_info:
|
||||
@@ -242,34 +242,34 @@ def preprocess_collection(user, course, collection):
|
||||
return filtered_collection
|
||||
|
||||
|
||||
def get_module_context(course, item):
|
||||
def get_block_context(course, block):
|
||||
"""
|
||||
Returns dispay_name and url for the parent module.
|
||||
Returns dispay_name and url for the parent block.
|
||||
"""
|
||||
item_dict = {
|
||||
'location': str(item.location),
|
||||
'display_name': Text(item.display_name_with_default),
|
||||
block_dict = {
|
||||
'location': str(block.location),
|
||||
'display_name': Text(block.display_name_with_default),
|
||||
}
|
||||
if item.category == 'chapter' and item.get_parent():
|
||||
if block.category == 'chapter' and block.get_parent():
|
||||
# course is a locator w/o branch and version
|
||||
# so for uniformity we replace it with one that has them
|
||||
course = item.get_parent()
|
||||
item_dict['index'] = get_index(item_dict['location'], course.children)
|
||||
elif item.category == 'vertical':
|
||||
section = item.get_parent()
|
||||
course = block.get_parent()
|
||||
block_dict['index'] = get_index(block_dict['location'], course.children)
|
||||
elif block.category == 'vertical':
|
||||
section = block.get_parent()
|
||||
chapter = section.get_parent()
|
||||
# Position starts from 1, that's why we add 1.
|
||||
position = get_index(str(item.location), section.children) + 1
|
||||
item_dict['url'] = reverse('courseware_position', kwargs={
|
||||
position = get_index(str(block.location), section.children) + 1
|
||||
block_dict['url'] = reverse('courseware_position', kwargs={
|
||||
'course_id': str(course.id),
|
||||
'chapter': chapter.url_name,
|
||||
'section': section.url_name,
|
||||
'position': position,
|
||||
})
|
||||
if item.category in ('chapter', 'sequential'):
|
||||
item_dict['children'] = [str(child) for child in item.children]
|
||||
if block.category in ('chapter', 'sequential'):
|
||||
block_dict['children'] = [str(child) for child in block.children]
|
||||
|
||||
return item_dict
|
||||
return block_dict
|
||||
|
||||
|
||||
def get_index(usage_key, children):
|
||||
|
||||
@@ -88,7 +88,7 @@ class TestProblem:
|
||||
|
||||
def get_html(self):
|
||||
"""
|
||||
Imitate get_html in module.
|
||||
Imitate get_html in block.
|
||||
"""
|
||||
return "original_get_html"
|
||||
|
||||
@@ -547,7 +547,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
|
||||
|
||||
def test_preprocess_collection_no_item(self):
|
||||
"""
|
||||
Tests the result if appropriate module is not found.
|
||||
Tests the result if appropriate block is not found.
|
||||
"""
|
||||
initial_collection = [
|
||||
{
|
||||
@@ -590,7 +590,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
|
||||
|
||||
def test_preprocess_collection_has_access(self):
|
||||
"""
|
||||
Tests the result if the user does not have access to some of the modules.
|
||||
Tests the result if the user does not have access to some of the blocks.
|
||||
"""
|
||||
initial_collection = [
|
||||
{
|
||||
@@ -702,9 +702,9 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
|
||||
}
|
||||
]) == len(helpers.preprocess_collection(self.user, self.course, initial_collection))
|
||||
|
||||
def test_get_module_context_sequential(self):
|
||||
def test_get_block_context_sequential(self):
|
||||
"""
|
||||
Tests `get_module_context` method for the sequential.
|
||||
Tests `get_block_context` method for the sequential.
|
||||
"""
|
||||
self.assertDictEqual(
|
||||
{
|
||||
@@ -712,24 +712,24 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
|
||||
"location": str(self.sequential.location),
|
||||
"children": [str(self.vertical.location), str(self.vertical_with_container.location)],
|
||||
},
|
||||
helpers.get_module_context(self.course, self.sequential)
|
||||
helpers.get_block_context(self.course, self.sequential)
|
||||
)
|
||||
|
||||
def test_get_module_context_html_component(self):
|
||||
def test_get_block_context_html_component(self):
|
||||
"""
|
||||
Tests `get_module_context` method for the components.
|
||||
Tests `get_block_context` method for the components.
|
||||
"""
|
||||
self.assertDictEqual(
|
||||
{
|
||||
"display_name": self.html_block_1.display_name_with_default,
|
||||
"location": str(self.html_block_1.location),
|
||||
},
|
||||
helpers.get_module_context(self.course, self.html_block_1)
|
||||
helpers.get_block_context(self.course, self.html_block_1)
|
||||
)
|
||||
|
||||
def test_get_module_context_chapter(self):
|
||||
def test_get_block_context_chapter(self):
|
||||
"""
|
||||
Tests `get_module_context` method for the chapters.
|
||||
Tests `get_block_context` method for the chapters.
|
||||
"""
|
||||
self.assertDictEqual(
|
||||
{
|
||||
@@ -738,7 +738,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
|
||||
"location": str(self.chapter.location),
|
||||
"children": [str(self.sequential.location)],
|
||||
},
|
||||
helpers.get_module_context(self.course, self.chapter)
|
||||
helpers.get_block_context(self.course, self.chapter)
|
||||
)
|
||||
self.assertDictEqual(
|
||||
{
|
||||
@@ -747,7 +747,7 @@ class EdxNotesHelpersTest(ModuleStoreTestCase):
|
||||
"location": str(self.chapter_2.location),
|
||||
"children": [],
|
||||
},
|
||||
helpers.get_module_context(self.course, self.chapter_2)
|
||||
helpers.get_block_context(self.course, self.chapter_2)
|
||||
)
|
||||
|
||||
@override_settings(EDXNOTES_PUBLIC_API="http://example.com")
|
||||
|
||||
@@ -158,13 +158,13 @@ class CourseGradeBase:
|
||||
possible += section.graded_total.possible
|
||||
return compute_percent(earned, possible)
|
||||
|
||||
def score_for_module(self, location):
|
||||
def score_for_block(self, location):
|
||||
"""
|
||||
Calculate the aggregate weighted score for any location in the course.
|
||||
This method returns a tuple containing (earned_score, possible_score).
|
||||
If the location is of 'problem' type, this method will return the
|
||||
possible and earned scores for that problem. If the location refers to a
|
||||
composite module (a vertical or section ) the scores will be the sums of
|
||||
composite block (a vertical or section ) the scores will be the sums of
|
||||
all scored problems that are children of the chosen location.
|
||||
"""
|
||||
if location in self.problem_scores:
|
||||
@@ -173,7 +173,7 @@ class CourseGradeBase:
|
||||
children = self.course_data.structure.get_children(location)
|
||||
earned, possible = 0.0, 0.0
|
||||
for child in children:
|
||||
child_earned, child_possible = self.score_for_module(child)
|
||||
child_earned, child_possible = self.score_for_block(child)
|
||||
earned += child_earned
|
||||
possible += child_possible
|
||||
return earned, possible
|
||||
|
||||
@@ -15,7 +15,7 @@ Terminology
|
||||
+-------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Term | Definition |
|
||||
+-------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Raw Score | Unweighted (earned, possible) points tuple. For a CapaModule (our most common problem type), each response entry is a point. So a single CapaModule problem with two multiple choice responses has a possible raw score of 2. |
|
||||
| Raw Score | Unweighted (earned, possible) points tuple. For a CapaBlock (our most common problem type), each response entry is a point. So a single CapaBlock problem with two multiple choice responses has a possible raw score of 2. |
|
||||
+-------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
| Weighted Score | The result of applying the weight settings-scoped XBlock attribute to the raw score. The weight is an indication of how much the total problem should be worth, so the weighted score tuple looks like ((earned / possible) * weight, weight). So if someone has a raw score of 1/2 and the problem weight is 10, then 5/10 is what will show up on the progress page as the weighted score. Problem weights are attributes that are placed on the XBlock/XModuleDescriptor and can be manipulated via Studio. |
|
||||
+-------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
@@ -106,46 +106,46 @@ class TestScoreForModule(SharedModuleStoreTestCase):
|
||||
set_current_request(None)
|
||||
|
||||
def test_score_chapter(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.a.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.a.location)
|
||||
assert earned == 9
|
||||
assert possible == 24
|
||||
|
||||
def test_score_section_many_leaves(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.b.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.b.location)
|
||||
assert earned == 6
|
||||
assert possible == 14
|
||||
|
||||
def test_score_section_one_leaf(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.c.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.c.location)
|
||||
assert earned == 3
|
||||
assert possible == 10
|
||||
|
||||
def test_score_vertical_two_leaves(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.d.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.d.location)
|
||||
assert earned == 5
|
||||
assert possible == 10
|
||||
|
||||
def test_score_vertical_two_leaves_one_unscored(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.e.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.e.location)
|
||||
assert earned == 1
|
||||
assert possible == 4
|
||||
|
||||
def test_score_vertical_no_score(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.f.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.f.location)
|
||||
assert earned == 0
|
||||
assert possible == 0
|
||||
|
||||
def test_score_vertical_one_leaf(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.g.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.g.location)
|
||||
assert earned == 3
|
||||
assert possible == 10
|
||||
|
||||
def test_score_leaf(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.h.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.h.location)
|
||||
assert earned == 2
|
||||
assert possible == 5
|
||||
|
||||
def test_score_leaf_no_score(self):
|
||||
earned, possible = self.course_grade.score_for_module(self.m.location)
|
||||
earned, possible = self.course_grade.score_for_block(self.m.location)
|
||||
assert earned == 0
|
||||
assert possible == 0
|
||||
|
||||
@@ -79,10 +79,10 @@ def answer_problem(course, request, problem, score=1, max_value=1):
|
||||
course,
|
||||
depth=2
|
||||
)
|
||||
module = get_block(
|
||||
block = get_block(
|
||||
user,
|
||||
request,
|
||||
problem.scope_ids.usage_id,
|
||||
field_data_cache,
|
||||
)
|
||||
module.system.publish(problem, 'grade', grade_dict)
|
||||
block.system.publish(problem, 'grade', grade_dict)
|
||||
|
||||
@@ -144,15 +144,15 @@ class GradesTransformer(BlockStructureTransformer):
|
||||
cls._collect_max_score(block_structure, block)
|
||||
|
||||
@classmethod
|
||||
def _collect_max_score(cls, block_structure, module):
|
||||
def _collect_max_score(cls, block_structure, block):
|
||||
"""
|
||||
Collect the `max_score` from the given module, storing it as a
|
||||
Collect the `max_score` from the given block, storing it as a
|
||||
`transformer_block_field` associated with the `GradesTransformer`.
|
||||
"""
|
||||
max_score = module.max_score()
|
||||
block_structure.set_transformer_block_field(module.location, cls, 'max_score', max_score)
|
||||
max_score = block.max_score()
|
||||
block_structure.set_transformer_block_field(block.location, cls, 'max_score', max_score)
|
||||
if max_score is None:
|
||||
log.warning(f"GradesTransformer: max_score is None for {module.location}")
|
||||
log.warning(f"GradesTransformer: max_score is None for {block.location}")
|
||||
|
||||
@classmethod
|
||||
def _collect_grading_policy_hash(cls, block_structure):
|
||||
@@ -168,18 +168,3 @@ class GradesTransformer(BlockStructureTransformer):
|
||||
"grading_policy_hash",
|
||||
cls.grading_policy_hash(course_block),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _iter_scorable_xmodules(block_structure):
|
||||
"""
|
||||
Loop through all the blocks locators in the block structure, and
|
||||
retrieve the module (XModule or XBlock) associated with that locator.
|
||||
|
||||
For implementation reasons, we need to pull the max_score from the
|
||||
XModule, even though the data is not user specific. Here we bind the
|
||||
data to a SystemUser.
|
||||
"""
|
||||
for block_locator in block_structure.post_order_traversal():
|
||||
block = block_structure.get_xblock(block_locator)
|
||||
if getattr(block, 'has_score', False):
|
||||
yield block
|
||||
|
||||
@@ -18,7 +18,7 @@ class LMSXBlockConfig(AppConfig):
|
||||
def ready(self):
|
||||
from .runtime import handler_url, local_resource_url
|
||||
|
||||
# In order to allow modules to use a handler url, we need to
|
||||
# In order to allow blocks to use a handler url, we need to
|
||||
# monkey-patch the x_module library.
|
||||
# TODO: Remove this code when Runtimes are no longer created by modulestores
|
||||
# https://openedx.atlassian.net/wiki/display/PLAT/Convert+from+Storage-centric+runtimes+to+Application-centric+runtimes
|
||||
|
||||
@@ -53,13 +53,13 @@ class LmsBlockMixin(XBlockMixin):
|
||||
Mixin that defines fields common to all blocks used in the LMS
|
||||
"""
|
||||
hide_from_toc = Boolean(
|
||||
help=_("Whether to display this module in the table of contents"),
|
||||
help=_("Whether to display this block in the table of contents"),
|
||||
default=False,
|
||||
scope=Scope.settings
|
||||
)
|
||||
format = String(
|
||||
# Translators: "TOC" stands for "Table of Contents"
|
||||
help=_("What format this module is in (used for deciding which "
|
||||
help=_("What format this block is in (used for deciding which "
|
||||
"grader to apply, and what to show in the TOC)"),
|
||||
scope=Scope.settings,
|
||||
)
|
||||
|
||||
@@ -55,7 +55,7 @@ def send_composite_outcome(user_id, course_id, assignment_id, version):
|
||||
user = User.objects.get(id=user_id)
|
||||
course = modulestore().get_course(course_key, depth=0)
|
||||
course_grade = CourseGradeFactory().read(user, course)
|
||||
earned, possible = course_grade.score_for_module(mapped_usage_key)
|
||||
earned, possible = course_grade.score_for_block(mapped_usage_key)
|
||||
if possible == 0:
|
||||
weighted_score = 0
|
||||
else:
|
||||
|
||||
@@ -121,7 +121,7 @@ class SendCompositeOutcomeTest(BaseOutcomeTest):
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_outcome_with_score_score(self, earned, possible, expected):
|
||||
self.course_grade.score_for_module = MagicMock(return_value=(earned, possible))
|
||||
self.course_grade.score_for_block = MagicMock(return_value=(earned, possible))
|
||||
tasks.send_composite_outcome(
|
||||
self.user.id, str(self.course_key), self.assignment.id, 1
|
||||
)
|
||||
|
||||
@@ -47,8 +47,8 @@ class CourseUpdatesList(generics.ListAPIView):
|
||||
|
||||
@mobile_course_access()
|
||||
def list(self, request, course, *args, **kwargs): # lint-amnesty, pylint: disable=arguments-differ
|
||||
course_updates_module = get_course_info_section_block(request, request.user, course, 'updates')
|
||||
update_items = get_course_update_items(course_updates_module)
|
||||
course_updates_block = get_course_info_section_block(request, request.user, course, 'updates')
|
||||
update_items = get_course_update_items(course_updates_block)
|
||||
|
||||
updates_to_show = [
|
||||
update for update in update_items
|
||||
@@ -56,7 +56,7 @@ class CourseUpdatesList(generics.ListAPIView):
|
||||
]
|
||||
|
||||
for item in updates_to_show:
|
||||
item['content'] = apply_wrappers_to_content(item['content'], course_updates_module, request)
|
||||
item['content'] = apply_wrappers_to_content(item['content'], course_updates_block, request)
|
||||
|
||||
return Response(updates_to_show)
|
||||
|
||||
@@ -82,26 +82,26 @@ class CourseHandoutsList(generics.ListAPIView):
|
||||
|
||||
@mobile_course_access()
|
||||
def list(self, request, course, *args, **kwargs): # lint-amnesty, pylint: disable=arguments-differ
|
||||
course_handouts_module = get_course_info_section_block(request, request.user, course, 'handouts')
|
||||
if course_handouts_module:
|
||||
if course_handouts_module.data == "<ol></ol>":
|
||||
course_handouts_block = get_course_info_section_block(request, request.user, course, 'handouts')
|
||||
if course_handouts_block:
|
||||
if course_handouts_block.data == "<ol></ol>":
|
||||
handouts_html = None
|
||||
else:
|
||||
handouts_html = apply_wrappers_to_content(course_handouts_module.data, course_handouts_module, request)
|
||||
handouts_html = apply_wrappers_to_content(course_handouts_block.data, course_handouts_block, request)
|
||||
return Response({'handouts_html': handouts_html})
|
||||
else:
|
||||
# course_handouts_module could be None if there are no handouts
|
||||
# course_handouts_block could be None if there are no handouts
|
||||
return Response({'handouts_html': None})
|
||||
|
||||
|
||||
def apply_wrappers_to_content(content, module, request):
|
||||
def apply_wrappers_to_content(content, block, request):
|
||||
"""
|
||||
Updates a piece of html content with the filter functions stored in its module system, then replaces any
|
||||
static urls with absolute urls.
|
||||
|
||||
Args:
|
||||
content: The html content to which to apply the content wrappers generated for this module system.
|
||||
module: The module containing a reference to the module system which contains functions to apply to the
|
||||
block: The block containing a reference to the module system which contains functions to apply to the
|
||||
content. These functions include:
|
||||
* Replacing static url's
|
||||
* Replacing course url's
|
||||
@@ -111,7 +111,7 @@ def apply_wrappers_to_content(content, module, request):
|
||||
Returns: A piece of html content containing the original content updated by each wrapper.
|
||||
|
||||
"""
|
||||
content = module.system.service(module, "replace_urls").replace_urls(content)
|
||||
content = block.system.service(block, "replace_urls").replace_urls(content)
|
||||
|
||||
return make_static_urls_absolute(request, content)
|
||||
|
||||
|
||||
@@ -490,7 +490,7 @@ class TestCourseStatusGET(CourseStatusAPITestCase, MobileAuthUserTestMixin,
|
||||
|
||||
response = self.api_response(api_version=API_V05)
|
||||
assert response.data['last_visited_module_id'] == str(self.sub_section.location)
|
||||
assert response.data['last_visited_module_path'] == [str(module.location) for module in
|
||||
assert response.data['last_visited_module_path'] == [str(block.location) for block in
|
||||
[self.sub_section, self.section, self.course]]
|
||||
|
||||
def test_success_v1(self):
|
||||
@@ -558,12 +558,12 @@ class TestCourseStatusPATCH(CourseStatusAPITestCase, MobileAuthUserTestMixin,
|
||||
response = self.api_response(data={"last_visited_module_id": str(self.other_unit.location)})
|
||||
assert response.data['last_visited_module_id'] == str(self.other_sub_section.location)
|
||||
|
||||
def test_invalid_module(self):
|
||||
def test_invalid_block(self):
|
||||
self.login_and_enroll()
|
||||
response = self.api_response(data={"last_visited_module_id": "abc"}, expected_response_code=400)
|
||||
assert response.data == errors.ERROR_INVALID_MODULE_ID
|
||||
|
||||
def test_nonexistent_module(self):
|
||||
def test_nonexistent_block(self):
|
||||
self.login_and_enroll()
|
||||
non_existent_key = self.course.id.make_usage_key('video', 'non-existent')
|
||||
response = self.api_response(data={"last_visited_module_id": non_existent_key}, expected_response_code=400)
|
||||
|
||||
@@ -134,9 +134,9 @@ class UserCourseStatus(views.APIView):
|
||||
with transaction.atomic():
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def _last_visited_module_path(self, request, course):
|
||||
def _last_visited_block_path(self, request, course):
|
||||
"""
|
||||
Returns the path from the last module visited by the current user in the given course up to
|
||||
Returns the path from the last block visited by the current user in the given course up to
|
||||
the course block. If there is no such visit, the first item deep enough down the course
|
||||
tree is used.
|
||||
"""
|
||||
@@ -162,8 +162,8 @@ class UserCourseStatus(views.APIView):
|
||||
"""
|
||||
Returns the course status
|
||||
"""
|
||||
path = self._last_visited_module_path(request, course)
|
||||
path_ids = [str(module.location) for module in path]
|
||||
path = self._last_visited_block_path(request, course)
|
||||
path_ids = [str(block.location) for block in path]
|
||||
return Response({
|
||||
"last_visited_module_id": path_ids[0],
|
||||
"last_visited_module_path": path_ids,
|
||||
@@ -176,11 +176,11 @@ class UserCourseStatus(views.APIView):
|
||||
field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
|
||||
course.id, request.user, course, depth=2)
|
||||
try:
|
||||
module_descriptor = modulestore().get_item(module_key)
|
||||
block_descriptor = modulestore().get_item(module_key)
|
||||
except ItemNotFoundError:
|
||||
return Response(errors.ERROR_INVALID_MODULE_ID, status=400)
|
||||
module = get_block_for_descriptor(
|
||||
request.user, request, module_descriptor, field_data_cache, course.id, course=course
|
||||
block = get_block_for_descriptor(
|
||||
request.user, request, block_descriptor, field_data_cache, course.id, course=course
|
||||
)
|
||||
|
||||
if modification_date:
|
||||
@@ -195,7 +195,7 @@ class UserCourseStatus(views.APIView):
|
||||
# old modification date so skip update
|
||||
return self._get_course_info(request, course)
|
||||
|
||||
save_positions_recursively_up(request.user, request, field_data_cache, module, course=course)
|
||||
save_positions_recursively_up(request.user, request, field_data_cache, block, course=course)
|
||||
return self._get_course_info(request, course)
|
||||
|
||||
@mobile_course_access(depth=2)
|
||||
|
||||
Reference in New Issue
Block a user