Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
71cce9bb
Commit
71cce9bb
authored
Feb 11, 2017
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Block Transformers: distinguish between READ and WRITE versions
TNL-6522
parent
85ff8a62
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
154 additions
and
70 deletions
+154
-70
lms/djangoapps/course_api/blocks/transformers/block_counts.py
+2
-1
lms/djangoapps/course_api/blocks/transformers/block_depth.py
+2
-1
lms/djangoapps/course_api/blocks/transformers/blocks_api.py
+2
-1
lms/djangoapps/course_api/blocks/transformers/milestones.py
+2
-1
lms/djangoapps/course_api/blocks/transformers/navigation.py
+2
-1
lms/djangoapps/course_api/blocks/transformers/student_view.py
+2
-1
lms/djangoapps/course_blocks/transformers/hidden_content.py
+2
-1
lms/djangoapps/course_blocks/transformers/library_content.py
+2
-1
lms/djangoapps/course_blocks/transformers/split_test.py
+2
-1
lms/djangoapps/course_blocks/transformers/start_date.py
+2
-1
lms/djangoapps/course_blocks/transformers/user_partitions.py
+2
-1
lms/djangoapps/course_blocks/transformers/visibility.py
+2
-1
lms/djangoapps/grades/tests/test_tasks.py
+2
-1
lms/djangoapps/grades/transformer.py
+2
-5
openedx/core/lib/block_structure/block_structure.py
+3
-3
openedx/core/lib/block_structure/exceptions.py
+24
-2
openedx/core/lib/block_structure/factory.py
+9
-3
openedx/core/lib/block_structure/manager.py
+10
-7
openedx/core/lib/block_structure/tests/helpers.py
+4
-2
openedx/core/lib/block_structure/tests/test_block_structure.py
+4
-3
openedx/core/lib/block_structure/tests/test_factory.py
+2
-3
openedx/core/lib/block_structure/tests/test_manager.py
+13
-4
openedx/core/lib/block_structure/tests/test_transformers.py
+5
-4
openedx/core/lib/block_structure/transformer.py
+40
-13
openedx/core/lib/block_structure/transformers.py
+12
-8
No files found.
lms/djangoapps/course_api/blocks/transformers/block_counts.py
View file @
71cce9bb
...
@@ -8,7 +8,8 @@ class BlockCountsTransformer(BlockStructureTransformer):
...
@@ -8,7 +8,8 @@ class BlockCountsTransformer(BlockStructureTransformer):
"""
"""
Keep a count of descendant blocks of the requested types
Keep a count of descendant blocks of the requested types
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
BLOCK_COUNTS
=
'block_counts'
BLOCK_COUNTS
=
'block_counts'
def
__init__
(
self
,
block_types_to_count
):
def
__init__
(
self
,
block_types_to_count
):
...
...
lms/djangoapps/course_api/blocks/transformers/block_depth.py
View file @
71cce9bb
...
@@ -9,7 +9,8 @@ class BlockDepthTransformer(BlockStructureTransformer):
...
@@ -9,7 +9,8 @@ class BlockDepthTransformer(BlockStructureTransformer):
Keep track of the depth of each block within the block structure. In case
Keep track of the depth of each block within the block structure. In case
of multiple paths to a given node (in a DAG), use the shallowest depth.
of multiple paths to a given node (in a DAG), use the shallowest depth.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
BLOCK_DEPTH
=
'block_depth'
BLOCK_DEPTH
=
'block_depth'
def
__init__
(
self
,
requested_depth
=
None
):
def
__init__
(
self
,
requested_depth
=
None
):
...
...
lms/djangoapps/course_api/blocks/transformers/blocks_api.py
View file @
71cce9bb
...
@@ -22,7 +22,8 @@ class BlocksAPITransformer(BlockStructureTransformer):
...
@@ -22,7 +22,8 @@ class BlocksAPITransformer(BlockStructureTransformer):
Note: BlockDepthTransformer must be executed before BlockNavigationTransformer.
Note: BlockDepthTransformer must be executed before BlockNavigationTransformer.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
STUDENT_VIEW_DATA
=
'student_view_data'
STUDENT_VIEW_DATA
=
'student_view_data'
STUDENT_VIEW_MULTI_DEVICE
=
'student_view_multi_device'
STUDENT_VIEW_MULTI_DEVICE
=
'student_view_multi_device'
...
...
lms/djangoapps/course_api/blocks/transformers/milestones.py
View file @
71cce9bb
...
@@ -13,7 +13,8 @@ class MilestonesTransformer(FilteringTransformerMixin, BlockStructureTransformer
...
@@ -13,7 +13,8 @@ class MilestonesTransformer(FilteringTransformerMixin, BlockStructureTransformer
Excludes all special exams (timed, proctored, practice proctored) from the student view.
Excludes all special exams (timed, proctored, practice proctored) from the student view.
Excludes all blocks with unfulfilled milestones from the student view.
Excludes all blocks with unfulfilled milestones from the student view.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
@classmethod
@classmethod
def
name
(
cls
):
def
name
(
cls
):
...
...
lms/djangoapps/course_api/blocks/transformers/navigation.py
View file @
71cce9bb
...
@@ -20,7 +20,8 @@ class BlockNavigationTransformer(BlockStructureTransformer):
...
@@ -20,7 +20,8 @@ class BlockNavigationTransformer(BlockStructureTransformer):
Prerequisites: BlockDepthTransformer must be run before this in the
Prerequisites: BlockDepthTransformer must be run before this in the
transform phase.
transform phase.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
BLOCK_NAVIGATION
=
'block_nav'
BLOCK_NAVIGATION
=
'block_nav'
BLOCK_NAVIGATION_FOR_CHILDREN
=
'children_block_nav'
BLOCK_NAVIGATION_FOR_CHILDREN
=
'children_block_nav'
...
...
lms/djangoapps/course_api/blocks/transformers/student_view.py
View file @
71cce9bb
...
@@ -8,7 +8,8 @@ class StudentViewTransformer(BlockStructureTransformer):
...
@@ -8,7 +8,8 @@ class StudentViewTransformer(BlockStructureTransformer):
"""
"""
Only show information that is appropriate for a learner
Only show information that is appropriate for a learner
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
STUDENT_VIEW_DATA
=
'student_view_data'
STUDENT_VIEW_DATA
=
'student_view_data'
STUDENT_VIEW_MULTI_DEVICE
=
'student_view_multi_device'
STUDENT_VIEW_MULTI_DEVICE
=
'student_view_multi_device'
...
...
lms/djangoapps/course_blocks/transformers/hidden_content.py
View file @
71cce9bb
...
@@ -25,7 +25,8 @@ class HiddenContentTransformer(FilteringTransformerMixin, BlockStructureTransfor
...
@@ -25,7 +25,8 @@ class HiddenContentTransformer(FilteringTransformerMixin, BlockStructureTransfor
Staff users are exempted from hidden content rules.
Staff users are exempted from hidden content rules.
"""
"""
VERSION
=
2
WRITE_VERSION
=
2
READ_VERSION
=
2
MERGED_DUE_DATE
=
'merged_due_date'
MERGED_DUE_DATE
=
'merged_due_date'
MERGED_HIDE_AFTER_DUE
=
'merged_hide_after_due'
MERGED_HIDE_AFTER_DUE
=
'merged_hide_after_due'
...
...
lms/djangoapps/course_blocks/transformers/library_content.py
View file @
71cce9bb
...
@@ -18,7 +18,8 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
...
@@ -18,7 +18,8 @@ class ContentLibraryTransformer(FilteringTransformerMixin, BlockStructureTransfo
Staff users are *not* exempted from library content pathways.
Staff users are *not* exempted from library content pathways.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
@classmethod
@classmethod
def
name
(
cls
):
def
name
(
cls
):
...
...
lms/djangoapps/course_blocks/transformers/split_test.py
View file @
71cce9bb
...
@@ -19,7 +19,8 @@ class SplitTestTransformer(FilteringTransformerMixin, BlockStructureTransformer)
...
@@ -19,7 +19,8 @@ class SplitTestTransformer(FilteringTransformerMixin, BlockStructureTransformer)
to actually enforce the access using the 'user_partitions' and
to actually enforce the access using the 'user_partitions' and
'group_access' fields.
'group_access' fields.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
@classmethod
@classmethod
def
name
(
cls
):
def
name
(
cls
):
...
...
lms/djangoapps/course_blocks/transformers/start_date.py
View file @
71cce9bb
...
@@ -24,7 +24,8 @@ class StartDateTransformer(FilteringTransformerMixin, BlockStructureTransformer)
...
@@ -24,7 +24,8 @@ class StartDateTransformer(FilteringTransformerMixin, BlockStructureTransformer)
Staff users are exempted from visibility rules.
Staff users are exempted from visibility rules.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
MERGED_START_DATE
=
'merged_start_date'
MERGED_START_DATE
=
'merged_start_date'
@classmethod
@classmethod
...
...
lms/djangoapps/course_blocks/transformers/user_partitions.py
View file @
71cce9bb
...
@@ -16,7 +16,8 @@ class UserPartitionTransformer(FilteringTransformerMixin, BlockStructureTransfor
...
@@ -16,7 +16,8 @@ class UserPartitionTransformer(FilteringTransformerMixin, BlockStructureTransfor
Staff users are *not* exempted from user partition pathways.
Staff users are *not* exempted from user partition pathways.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
@classmethod
@classmethod
def
name
(
cls
):
def
name
(
cls
):
...
...
lms/djangoapps/course_blocks/transformers/visibility.py
View file @
71cce9bb
...
@@ -18,7 +18,8 @@ class VisibilityTransformer(FilteringTransformerMixin, BlockStructureTransformer
...
@@ -18,7 +18,8 @@ class VisibilityTransformer(FilteringTransformerMixin, BlockStructureTransformer
Staff users are exempted from visibility rules.
Staff users are exempted from visibility rules.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
MERGED_VISIBLE_TO_STAFF_ONLY
=
'merged_visible_to_staff_only'
MERGED_VISIBLE_TO_STAFF_ONLY
=
'merged_visible_to_staff_only'
...
...
lms/djangoapps/grades/tests/test_tasks.py
View file @
71cce9bb
...
@@ -13,6 +13,7 @@ from mock import patch, MagicMock
...
@@ -13,6 +13,7 @@ from mock import patch, MagicMock
import
pytz
import
pytz
from
util.date_utils
import
to_timestamp
from
util.date_utils
import
to_timestamp
from
openedx.core.lib.block_structure.exceptions
import
BlockStructureNotFound
from
student.models
import
anonymous_id_for_user
from
student.models
import
anonymous_id_for_user
from
student.tests.factories
import
UserFactory
from
student.tests.factories
import
UserFactory
from
track.event_transaction_utils
import
(
from
track.event_transaction_utils
import
(
...
@@ -130,7 +131,7 @@ class RecalculateSubsectionGradeTest(ModuleStoreTestCase):
...
@@ -130,7 +131,7 @@ class RecalculateSubsectionGradeTest(ModuleStoreTestCase):
self
.
assertTrue
(
PersistentGradesEnabledFlag
.
feature_enabled
(
self
.
course
.
id
))
self
.
assertTrue
(
PersistentGradesEnabledFlag
.
feature_enabled
(
self
.
course
.
id
))
with
patch
(
with
patch
(
'openedx.core.lib.block_structure.factory.BlockStructureFactory.create_from_cache'
,
'openedx.core.lib.block_structure.factory.BlockStructureFactory.create_from_cache'
,
return_value
=
None
,
side_effect
=
BlockStructureNotFound
,
)
as
mock_block_structure_create
:
)
as
mock_block_structure_create
:
self
.
_apply_recalculate_subsection_grade
()
self
.
_apply_recalculate_subsection_grade
()
self
.
assertEquals
(
mock_block_structure_create
.
call_count
,
1
)
self
.
assertEquals
(
mock_block_structure_create
.
call_count
,
1
)
...
...
lms/djangoapps/grades/transformer.py
View file @
71cce9bb
...
@@ -2,17 +2,13 @@
...
@@ -2,17 +2,13 @@
Grades Transformer
Grades Transformer
"""
"""
from
base64
import
b64encode
from
base64
import
b64encode
from
django.test.client
import
RequestFactory
from
functools
import
reduce
as
functools_reduce
from
functools
import
reduce
as
functools_reduce
from
hashlib
import
sha1
from
hashlib
import
sha1
from
logging
import
getLogger
from
logging
import
getLogger
import
json
import
json
from
courseware.model_data
import
FieldDataCache
import
courseware.module_render
from
lms.djangoapps.course_blocks.transformers.utils
import
collect_unioned_set_field
,
get_field_on_block
from
lms.djangoapps.course_blocks.transformers.utils
import
collect_unioned_set_field
,
get_field_on_block
from
openedx.core.lib.block_structure.transformer
import
BlockStructureTransformer
from
openedx.core.lib.block_structure.transformer
import
BlockStructureTransformer
from
openedx.core.djangoapps.util.user_utils
import
SystemUser
log
=
getLogger
(
__name__
)
log
=
getLogger
(
__name__
)
...
@@ -39,7 +35,8 @@ class GradesTransformer(BlockStructureTransformer):
...
@@ -39,7 +35,8 @@ class GradesTransformer(BlockStructureTransformer):
max_score: (numeric)
max_score: (numeric)
"""
"""
VERSION
=
4
WRITE_VERSION
=
4
READ_VERSION
=
4
FIELDS_TO_COLLECT
=
[
u'due'
,
u'format'
,
u'graded'
,
u'has_score'
,
u'weight'
,
u'course_version'
,
u'subtree_edited_on'
]
FIELDS_TO_COLLECT
=
[
u'due'
,
u'format'
,
u'graded'
,
u'has_score'
,
u'weight'
,
u'course_version'
,
u'subtree_edited_on'
]
EXPLICIT_GRADED_FIELD_NAME
=
'explicit_graded'
EXPLICIT_GRADED_FIELD_NAME
=
'explicit_graded'
...
...
openedx/core/lib/block_structure/block_structure.py
View file @
71cce9bb
...
@@ -730,9 +730,9 @@ class BlockStructureBlockData(BlockStructure):
...
@@ -730,9 +730,9 @@ class BlockStructureBlockData(BlockStructure):
Adds the given transformer to the block structure by recording
Adds the given transformer to the block structure by recording
its current version number.
its current version number.
"""
"""
if
transformer
.
VERSION
==
0
:
if
transformer
.
READ_VERSION
==
0
or
transformer
.
WRITE_
VERSION
==
0
:
raise
TransformerException
(
'V
ERSION attribute is
not set on transformer {0}.'
,
transformer
.
name
())
raise
TransformerException
(
'V
ersion attributes are
not set on transformer {0}.'
,
transformer
.
name
())
self
.
set_transformer_data
(
transformer
,
TRANSFORMER_VERSION_KEY
,
transformer
.
VERSION
)
self
.
set_transformer_data
(
transformer
,
TRANSFORMER_VERSION_KEY
,
transformer
.
WRITE_
VERSION
)
def
_get_or_create_block
(
self
,
usage_key
):
def
_get_or_create_block
(
self
,
usage_key
):
"""
"""
...
...
openedx/core/lib/block_structure/exceptions.py
View file @
71cce9bb
...
@@ -3,15 +3,37 @@ Application-specific exceptions raised by the block structure framework.
...
@@ -3,15 +3,37 @@ Application-specific exceptions raised by the block structure framework.
"""
"""
class
TransformerException
(
Exception
):
class
BlockStructureException
(
Exception
):
"""
Base class for all Block Structure framework exceptions.
"""
pass
class
TransformerException
(
BlockStructureException
):
"""
"""
Exception class for Transformer related errors.
Exception class for Transformer related errors.
"""
"""
pass
pass
class
UsageKeyNotInBlockStructure
(
Exception
):
class
UsageKeyNotInBlockStructure
(
BlockStructure
Exception
):
"""
"""
Exception for when a usage key is not found within a block structure.
Exception for when a usage key is not found within a block structure.
"""
"""
pass
pass
class
TransformerDataIncompatible
(
BlockStructureException
):
"""
Exception for when the version of a Transformer's data is not
compatible with the current version of the Transformer.
"""
pass
class
BlockStructureNotFound
(
BlockStructureException
):
"""
Exception for when a Block Structure is not found.
"""
pass
openedx/core/lib/block_structure/factory.py
View file @
71cce9bb
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
Module for factory class for BlockStructure objects.
Module for factory class for BlockStructure objects.
"""
"""
from
.block_structure
import
BlockStructureModulestoreData
,
BlockStructureBlockData
from
.block_structure
import
BlockStructureModulestoreData
,
BlockStructureBlockData
from
.exceptions
import
BlockStructureNotFound
class
BlockStructureFactory
(
object
):
class
BlockStructureFactory
(
object
):
...
@@ -77,11 +78,16 @@ class BlockStructureFactory(object):
...
@@ -77,11 +78,16 @@ class BlockStructureFactory(object):
Returns:
Returns:
BlockStructure - The deserialized block structure starting
BlockStructure - The deserialized block structure starting
at root_block_usage_key, if found in the cache.
at root_block_usage_key, if found in the cache.
NoneType - If the root_block_usage_key is not found in the cache.
Raises:
BlockStructureNotFound - If the root_block_usage_key is not found
in the cache.
"""
"""
return
block_structure_cache
.
get
(
root_block_usage_key
)
block_structure
=
block_structure_cache
.
get
(
root_block_usage_key
)
if
block_structure
is
None
:
raise
BlockStructureNotFound
(
'Block structure for {} not found in the cache.'
.
format
(
root_block_usage_key
))
return
block_structure
@classmethod
@classmethod
def
create_new
(
cls
,
root_block_usage_key
,
block_relations
,
transformer_data
,
block_data_map
):
def
create_new
(
cls
,
root_block_usage_key
,
block_relations
,
transformer_data
,
block_data_map
):
...
...
openedx/core/lib/block_structure/manager.py
View file @
71cce9bb
...
@@ -6,7 +6,7 @@ from contextlib import contextmanager
...
@@ -6,7 +6,7 @@ from contextlib import contextmanager
from
.cache
import
BlockStructureCache
from
.cache
import
BlockStructureCache
from
.factory
import
BlockStructureFactory
from
.factory
import
BlockStructureFactory
from
.exceptions
import
UsageKeyNotInBlockStructure
from
.exceptions
import
UsageKeyNotInBlockStructure
,
TransformerDataIncompatible
,
BlockStructureNotFound
from
.transformers
import
BlockStructureTransformers
from
.transformers
import
BlockStructureTransformers
...
@@ -89,13 +89,16 @@ class BlockStructureManager(object):
...
@@ -89,13 +89,16 @@ class BlockStructureManager(object):
starting at root_block_usage_key, with collected data
starting at root_block_usage_key, with collected data
from each registered transformer.
from each registered transformer.
"""
"""
block_structure
=
BlockStructureFactory
.
create_from_cache
(
try
:
self
.
root_block_usage_key
,
block_structure
=
BlockStructureFactory
.
create_from_cache
(
self
.
block_structure_cache
self
.
root_block_usage_key
,
)
self
.
block_structure_cache
cache_miss
=
block_structure
is
None
)
if
cache_miss
or
BlockStructureTransformers
.
is_collected_outdated
(
block_structure
):
BlockStructureTransformers
.
verify_versions
(
block_structure
)
except
(
BlockStructureNotFound
,
TransformerDataIncompatible
):
block_structure
=
self
.
update_collected
()
block_structure
=
self
.
update_collected
()
return
block_structure
return
block_structure
def
update_collected
(
self
):
def
update_collected
(
self
):
...
...
openedx/core/lib/block_structure/tests/helpers.py
View file @
71cce9bb
...
@@ -136,7 +136,8 @@ class MockTransformer(BlockStructureTransformer):
...
@@ -136,7 +136,8 @@ class MockTransformer(BlockStructureTransformer):
"""
"""
A mock BlockStructureTransformer class.
A mock BlockStructureTransformer class.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
@classmethod
@classmethod
def
name
(
cls
):
def
name
(
cls
):
...
@@ -151,7 +152,8 @@ class MockFilteringTransformer(FilteringTransformerMixin, BlockStructureTransfor
...
@@ -151,7 +152,8 @@ class MockFilteringTransformer(FilteringTransformerMixin, BlockStructureTransfor
"""
"""
A mock FilteringTransformerMixin class.
A mock FilteringTransformerMixin class.
"""
"""
VERSION
=
1
WRITE_VERSION
=
1
READ_VERSION
=
1
@classmethod
@classmethod
def
name
(
cls
):
def
name
(
cls
):
...
...
openedx/core/lib/block_structure/tests/test_block_structure.py
View file @
71cce9bb
...
@@ -56,11 +56,12 @@ class TestBlockStructureData(TestCase, ChildrenMapTestMixin):
...
@@ -56,11 +56,12 @@ class TestBlockStructureData(TestCase, ChildrenMapTestMixin):
"""
"""
Test transformer with default version number (0).
Test transformer with default version number (0).
"""
"""
VERSION
=
0
WRITE_VERSION
=
0
READ_VERSION
=
0
block_structure
=
BlockStructureModulestoreData
(
root_block_usage_key
=
0
)
block_structure
=
BlockStructureModulestoreData
(
root_block_usage_key
=
0
)
with
self
.
assertRaisesRegexp
(
TransformerException
,
"V
ERSION attribute is
not set"
):
with
self
.
assertRaisesRegexp
(
TransformerException
,
"V
ersion attributes are
not set"
):
block_structure
.
_add_transformer
(
TestNonVersionedTransformer
())
block_structure
.
_add_transformer
(
TestNonVersionedTransformer
())
def
test_transformer_data
(
self
):
def
test_transformer_data
(
self
):
...
@@ -103,7 +104,7 @@ class TestBlockStructureData(TestCase, ChildrenMapTestMixin):
...
@@ -103,7 +104,7 @@ class TestBlockStructureData(TestCase, ChildrenMapTestMixin):
for
t_info
in
transformers_info
:
for
t_info
in
transformers_info
:
self
.
assertEquals
(
self
.
assertEquals
(
block_structure
.
_get_transformer_data_version
(
t_info
.
transformer
),
block_structure
.
_get_transformer_data_version
(
t_info
.
transformer
),
MockTransformer
.
VERSION
MockTransformer
.
WRITE_
VERSION
)
)
for
key
,
val
in
t_info
.
structure_wide_data
:
for
key
,
val
in
t_info
.
structure_wide_data
:
self
.
assertEquals
(
self
.
assertEquals
(
...
...
openedx/core/lib/block_structure/tests/test_factory.py
View file @
71cce9bb
...
@@ -6,6 +6,7 @@ from unittest import TestCase
...
@@ -6,6 +6,7 @@ from unittest import TestCase
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
xmodule.modulestore.exceptions
import
ItemNotFoundError
from
..cache
import
BlockStructureCache
from
..cache
import
BlockStructureCache
from
..exceptions
import
BlockStructureNotFound
from
..factory
import
BlockStructureFactory
from
..factory
import
BlockStructureFactory
from
.helpers
import
(
from
.helpers
import
(
MockCache
,
MockModulestoreFactory
,
ChildrenMapTestMixin
MockCache
,
MockModulestoreFactory
,
ChildrenMapTestMixin
...
@@ -43,17 +44,15 @@ class TestBlockStructureFactory(TestCase, ChildrenMapTestMixin):
...
@@ -43,17 +44,15 @@ class TestBlockStructureFactory(TestCase, ChildrenMapTestMixin):
block_structure
.
root_block_usage_key
,
block_structure
.
root_block_usage_key
,
cache
,
cache
,
)
)
self
.
assertIsNotNone
(
from_cache_block_structure
)
self
.
assert_block_structure
(
from_cache_block_structure
,
self
.
children_map
)
self
.
assert_block_structure
(
from_cache_block_structure
,
self
.
children_map
)
def
test_from_cache_none
(
self
):
def
test_from_cache_none
(
self
):
cache
=
BlockStructureCache
(
MockCache
())
cache
=
BlockStructureCache
(
MockCache
())
self
.
assertIsNone
(
with
self
.
assertRaises
(
BlockStructureNotFound
):
BlockStructureFactory
.
create_from_cache
(
BlockStructureFactory
.
create_from_cache
(
root_block_usage_key
=
0
,
root_block_usage_key
=
0
,
block_structure_cache
=
cache
,
block_structure_cache
=
cache
,
)
)
)
def
test_new
(
self
):
def
test_new
(
self
):
block_structure
=
BlockStructureFactory
.
create_from_modulestore
(
block_structure
=
BlockStructureFactory
.
create_from_modulestore
(
...
...
openedx/core/lib/block_structure/tests/test_manager.py
View file @
71cce9bb
...
@@ -167,15 +167,24 @@ class TestBlockStructureManager(TestCase, ChildrenMapTestMixin):
...
@@ -167,15 +167,24 @@ class TestBlockStructureManager(TestCase, ChildrenMapTestMixin):
self
.
collect_and_verify
(
expect_modulestore_called
=
False
,
expect_cache_updated
=
False
)
self
.
collect_and_verify
(
expect_modulestore_called
=
False
,
expect_cache_updated
=
False
)
self
.
assertEquals
(
TestTransformer1
.
collect_call_count
,
1
)
self
.
assertEquals
(
TestTransformer1
.
collect_call_count
,
1
)
def
test_get_collected_
outdated_data
(
self
):
def
test_get_collected_
transformer_version
(
self
):
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
TestTransformer1
.
VERSION
+=
1
# transformer code requires new schema version
# transformer code writes new schema version; data not re-collected
TestTransformer1
.
WRITE_VERSION
+=
1
self
.
collect_and_verify
(
expect_modulestore_called
=
False
,
expect_cache_updated
=
False
)
# transformer code requires new schema version; data re-collected
TestTransformer1
.
READ_VERSION
+=
1
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
TestTransformer1
.
VERSION
-=
1
# old transformer code works with new schema version
# old transformer code can read new schema version; data not re-collected
TestTransformer1
.
READ_VERSION
-=
1
self
.
collect_and_verify
(
expect_modulestore_called
=
False
,
expect_cache_updated
=
False
)
self
.
collect_and_verify
(
expect_modulestore_called
=
False
,
expect_cache_updated
=
False
)
self
.
assertEquals
(
TestTransformer1
.
collect_call_count
,
2
)
self
.
assertEquals
(
TestTransformer1
.
collect_call_count
,
2
)
def
test_get_collected_
version_update
(
self
):
def
test_get_collected_
structure_version
(
self
):
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
BlockStructureBlockData
.
VERSION
+=
1
BlockStructureBlockData
.
VERSION
+=
1
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
self
.
collect_and_verify
(
expect_modulestore_called
=
True
,
expect_cache_updated
=
True
)
...
...
openedx/core/lib/block_structure/tests/test_transformers.py
View file @
71cce9bb
...
@@ -6,7 +6,7 @@ from nose.plugins.attrib import attr
...
@@ -6,7 +6,7 @@ from nose.plugins.attrib import attr
from
unittest
import
TestCase
from
unittest
import
TestCase
from
..block_structure
import
BlockStructureModulestoreData
from
..block_structure
import
BlockStructureModulestoreData
from
..exceptions
import
TransformerException
from
..exceptions
import
TransformerException
,
TransformerDataIncompatible
from
..transformers
import
BlockStructureTransformers
from
..transformers
import
BlockStructureTransformers
from
.helpers
import
(
from
.helpers
import
(
ChildrenMapTestMixin
,
MockTransformer
,
MockFilteringTransformer
,
mock_registered_transformers
ChildrenMapTestMixin
,
MockTransformer
,
MockFilteringTransformer
,
mock_registered_transformers
...
@@ -71,13 +71,14 @@ class TestBlockStructureTransformers(ChildrenMapTestMixin, TestCase):
...
@@ -71,13 +71,14 @@ class TestBlockStructureTransformers(ChildrenMapTestMixin, TestCase):
self
.
transformers
.
transform
(
block_structure
=
MagicMock
())
self
.
transformers
.
transform
(
block_structure
=
MagicMock
())
self
.
assertTrue
(
mock_transform_call
.
called
)
self
.
assertTrue
(
mock_transform_call
.
called
)
def
test_
is_collected_outdated
(
self
):
def
test_
verify_versions
(
self
):
block_structure
=
self
.
create_block_structure
(
block_structure
=
self
.
create_block_structure
(
self
.
SIMPLE_CHILDREN_MAP
,
self
.
SIMPLE_CHILDREN_MAP
,
BlockStructureModulestoreData
BlockStructureModulestoreData
)
)
with
mock_registered_transformers
(
self
.
registered_transformers
):
with
mock_registered_transformers
(
self
.
registered_transformers
):
self
.
assertTrue
(
self
.
transformers
.
is_collected_outdated
(
block_structure
))
with
self
.
assertRaises
(
TransformerDataIncompatible
):
self
.
transformers
.
verify_versions
(
block_structure
)
self
.
transformers
.
collect
(
block_structure
)
self
.
transformers
.
collect
(
block_structure
)
self
.
assert
False
(
self
.
transformers
.
is_collected_outdated
(
block_structure
))
self
.
assert
True
(
self
.
transformers
.
verify_versions
(
block_structure
))
openedx/core/lib/block_structure/transformer.py
View file @
71cce9bb
...
@@ -10,23 +10,50 @@ class BlockStructureTransformer(object):
...
@@ -10,23 +10,50 @@ class BlockStructureTransformer(object):
Abstract base class for all block structure transformers.
Abstract base class for all block structure transformers.
"""
"""
# All Transformers are expected to maintain
a VERSION
class
# All Transformers are expected to maintain
version-related
class
# attribute
. While the value
for the base class is set to 0,
# attribute
s. While the values
for the base class is set to 0,
# the value for each concrete transformer should be 1 or higher.
# the value
s
for each concrete transformer should be 1 or higher.
#
#
# A transformer's version attribute
is
used by the block_structure
# A transformer's version attribute
s are
used by the block_structure
# framework in order to determine whether any collected data for a
# framework in order to determine whether any collected data for a
# transformer is outdated. When a transformer's data is collected
# transformer is outdated because of a data schema change by the
# and cached, it's version number at the time of collection is
# transformer.
# stored along with the data. That version number is then checked
# at the time of accessing the collected data (during the transform
# phase).
#
#
# The version number of a Transformer should be incremented each
# The WRITE_VERSION number is stored along with the transformer's
# time the implementation of its collect method is updated such that
# data when it is collected and cached (during the collect phase).
# its collected data is changed.
# The READ_VERSION number is then verified to be less than or equal
# to the version associated with the collected data when the
# collected data is accessed (during the transform phase).
#
#
VERSION
=
0
# We distinguish between WRITE_VERSION and READ_VERSION numbers in
# order to:
# 1. support blue-green deployments where new and previous versions
# of the code base are simultaneously executing on different
# workers for a period of time.
#
# A 2-phase deployment is used to stagger read and write changes.
#
# 2. scale for large deployments where it is costly to recompute
# block structures for all courses when a transformer's collected
# data schema changes.
#
# A background management command is run to prime the new data.
#
# See the following document for further information:
# https://openedx.atlassian.net/wiki/display/MA/Block+Structure+Cache+Invalidation+Proposal
#
# The WRITE_VERSION number of a Transformer should be incremented
# when it's collect implementation is additively changed. Backward
# compatibility should be maintained with previous READ_VERSIONs
# until all readers are updated.
#
# The READ_VERSION number of a Transformer should be incremented
# when its transform implementation is updated to make use of the
# newly collected data - and released only after all collected
# block structures are updated with the new WRITE_VERSION.
#
WRITE_VERSION
=
0
READ_VERSION
=
0
@classmethod
@classmethod
def
name
(
cls
):
def
name
(
cls
):
...
...
openedx/core/lib/block_structure/transformers.py
View file @
71cce9bb
...
@@ -4,7 +4,7 @@ Module for a collection of BlockStructureTransformers.
...
@@ -4,7 +4,7 @@ Module for a collection of BlockStructureTransformers.
import
functools
import
functools
from
logging
import
getLogger
from
logging
import
getLogger
from
.exceptions
import
TransformerException
from
.exceptions
import
TransformerException
,
TransformerDataIncompatible
from
.transformer
import
FilteringTransformerMixin
from
.transformer
import
FilteringTransformerMixin
from
.transformer_registry
import
TransformerRegistry
from
.transformer_registry
import
TransformerRegistry
...
@@ -83,23 +83,27 @@ class BlockStructureTransformers(object):
...
@@ -83,23 +83,27 @@ class BlockStructureTransformers(object):
block_structure
.
_collect_requested_xblock_fields
()
# pylint: disable=protected-access
block_structure
.
_collect_requested_xblock_fields
()
# pylint: disable=protected-access
@classmethod
@classmethod
def
is_collected_outdated
(
cls
,
block_structure
):
def
verify_versions
(
cls
,
block_structure
):
"""
"""
Returns whether the collected data in the block structure is outdated.
Returns whether the collected data in the block structure is
incompatible with the current version of the registered Transformers.
Raises:
TransformerDataIncompatible with information about all outdated
Transformers.
"""
"""
outdated_transformers
=
[]
outdated_transformers
=
[]
for
transformer
in
TransformerRegistry
.
get_registered_transformers
():
for
transformer
in
TransformerRegistry
.
get_registered_transformers
():
version_in_block_structure
=
block_structure
.
_get_transformer_data_version
(
transformer
)
# pylint: disable=protected-access
version_in_block_structure
=
block_structure
.
_get_transformer_data_version
(
transformer
)
# pylint: disable=protected-access
if
transformer
.
VERSION
>
version_in_block_structure
:
if
transformer
.
READ_
VERSION
>
version_in_block_structure
:
outdated_transformers
.
append
(
transformer
)
outdated_transformers
.
append
(
transformer
)
if
outdated_transformers
:
if
outdated_transformers
:
logger
.
info
(
raise
TransformerDataIncompatible
(
"Collected Block Structure data for the following transformers is outdated: '
%
s'."
,
"Collected Block Structure data for the following transformers is outdated: '
%
s'."
,
[(
transformer
.
name
(),
transformer
.
VERSION
)
for
transformer
in
outdated_transformers
],
[(
transformer
.
name
(),
transformer
.
READ_
VERSION
)
for
transformer
in
outdated_transformers
],
)
)
return
True
return
bool
(
outdated_transformers
)
def
transform
(
self
,
block_structure
):
def
transform
(
self
,
block_structure
):
"""
"""
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment