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:
0x29a
2023-01-03 13:23:38 +01:00
committed by Agrendalath
parent af13a326be
commit fd191db332
22 changed files with 113 additions and 128 deletions

View File

@@ -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.

View File

@@ -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',

View File

@@ -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):

View File

@@ -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(

View File

@@ -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.

View File

@@ -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):

View File

@@ -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)

View File

@@ -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

View File

@@ -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):

View File

@@ -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")

View File

@@ -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

View File

@@ -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. |
+-------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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,
)

View File

@@ -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:

View File

@@ -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
)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)