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
77cc80f6
Commit
77cc80f6
authored
Apr 08, 2015
by
E. Kolpakov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added code to fire library_updated signal when library is updated
parent
bd891c21
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
110 additions
and
36 deletions
+110
-36
cms/djangoapps/contentstore/courseware_index.py
+34
-13
cms/djangoapps/contentstore/signals.py
+2
-2
cms/djangoapps/contentstore/tasks.py
+10
-5
cms/djangoapps/contentstore/tests/test_courseware_index.py
+26
-16
common/lib/xmodule/xmodule/modulestore/__init__.py
+27
-0
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+1
-0
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
+10
-0
No files found.
cms/djangoapps/contentstore/courseware_index.py
View file @
77cc80f6
...
@@ -30,7 +30,7 @@ class SearchIndexingError(Exception):
...
@@ -30,7 +30,7 @@ class SearchIndexingError(Exception):
class
SearchIndexBase
(
object
):
class
SearchIndexBase
(
object
):
"""
"""
Class to perform indexing for courseware
search from different modulestores
Base class to perform indexing for courseware or library
search from different modulestores
"""
"""
INDEX_NAME
=
None
INDEX_NAME
=
None
...
@@ -50,17 +50,22 @@ class SearchIndexBase(object):
...
@@ -50,17 +50,22 @@ class SearchIndexBase(object):
return
settings
.
FEATURES
.
get
(
cls
.
ENABLE_INDEXING_KEY
,
False
)
return
settings
.
FEATURES
.
get
(
cls
.
ENABLE_INDEXING_KEY
,
False
)
@classmethod
@classmethod
def
_fetch_top_level
(
self
,
modulestore
,
structure_key
):
def
_normalize_structure_key
(
cls
,
structure_key
):
""" Normalizes structure key for use in indexing """
raise
NotImplementedError
(
"Should be overridden in child classes"
)
@classmethod
def
_fetch_top_level
(
cls
,
modulestore
,
structure_key
):
""" Fetch the item from the modulestore location """
""" Fetch the item from the modulestore location """
raise
NotImplementedError
(
"Should be overridden in child classes"
)
raise
NotImplementedError
(
"Should be overridden in child classes"
)
@classmethod
@classmethod
def
_get_location_info
(
self
,
structure_key
):
def
_get_location_info
(
cls
,
normalized_
structure_key
):
""" Builds location info dictionary """
""" Builds location info dictionary """
raise
NotImplementedError
(
"Should be overridden in child classes"
)
raise
NotImplementedError
(
"Should be overridden in child classes"
)
@classmethod
@classmethod
def
_id_modifier
(
self
,
usage_id
):
def
_id_modifier
(
cls
,
usage_id
):
""" Modifies usage_id to submit to index """
""" Modifies usage_id to submit to index """
return
usage_id
return
usage_id
...
@@ -102,6 +107,7 @@ class SearchIndexBase(object):
...
@@ -102,6 +107,7 @@ class SearchIndexBase(object):
if
not
searcher
:
if
not
searcher
:
return
return
structure_key
=
cls
.
_normalize_structure_key
(
structure_key
)
location_info
=
cls
.
_get_location_info
(
structure_key
)
location_info
=
cls
.
_get_location_info
(
structure_key
)
# Wrap counter in dictionary - otherwise we seem to lose scope inside the embedded function `index_item`
# Wrap counter in dictionary - otherwise we seem to lose scope inside the embedded function `index_item`
...
@@ -216,6 +222,9 @@ class SearchIndexBase(object):
...
@@ -216,6 +222,9 @@ class SearchIndexBase(object):
class
CoursewareSearchIndexer
(
SearchIndexBase
):
class
CoursewareSearchIndexer
(
SearchIndexBase
):
"""
Class to perform indexing for courseware search from different modulestores
"""
INDEX_NAME
=
"courseware_index"
INDEX_NAME
=
"courseware_index"
DOCUMENT_TYPE
=
"courseware_content"
DOCUMENT_TYPE
=
"courseware_content"
ENABLE_INDEXING_KEY
=
'ENABLE_COURSEWARE_INDEX'
ENABLE_INDEXING_KEY
=
'ENABLE_COURSEWARE_INDEX'
...
@@ -226,14 +235,19 @@ class CoursewareSearchIndexer(SearchIndexBase):
...
@@ -226,14 +235,19 @@ class CoursewareSearchIndexer(SearchIndexBase):
}
}
@classmethod
@classmethod
def
_fetch_top_level
(
self
,
modulestore
,
structure_key
):
def
_normalize_structure_key
(
cls
,
structure_key
):
""" Normalizes structure key for use in indexing """
return
structure_key
@classmethod
def
_fetch_top_level
(
cls
,
modulestore
,
structure_key
):
""" Fetch the item from the modulestore location """
""" Fetch the item from the modulestore location """
return
modulestore
.
get_course
(
structure_key
,
depth
=
None
)
return
modulestore
.
get_course
(
structure_key
,
depth
=
None
)
@classmethod
@classmethod
def
_get_location_info
(
self
,
structure_key
):
def
_get_location_info
(
cls
,
normalized_
structure_key
):
""" Builds location info dictionary """
""" Builds location info dictionary """
return
{
"course"
:
unicode
(
structure_key
)}
return
{
"course"
:
unicode
(
normalized_
structure_key
)}
@classmethod
@classmethod
def
do_course_reindex
(
cls
,
modulestore
,
course_key
):
def
do_course_reindex
(
cls
,
modulestore
,
course_key
):
...
@@ -244,6 +258,9 @@ class CoursewareSearchIndexer(SearchIndexBase):
...
@@ -244,6 +258,9 @@ class CoursewareSearchIndexer(SearchIndexBase):
class
LibrarySearchIndexer
(
SearchIndexBase
):
class
LibrarySearchIndexer
(
SearchIndexBase
):
"""
Base class to perform indexing for library search from different modulestores
"""
INDEX_NAME
=
"library_index"
INDEX_NAME
=
"library_index"
DOCUMENT_TYPE
=
"library_content"
DOCUMENT_TYPE
=
"library_content"
ENABLE_INDEXING_KEY
=
'ENABLE_LIBRARY_INDEX'
ENABLE_INDEXING_KEY
=
'ENABLE_LIBRARY_INDEX'
...
@@ -254,17 +271,22 @@ class LibrarySearchIndexer(SearchIndexBase):
...
@@ -254,17 +271,22 @@ class LibrarySearchIndexer(SearchIndexBase):
}
}
@classmethod
@classmethod
def
_fetch_top_level
(
self
,
modulestore
,
structure_key
):
def
_normalize_structure_key
(
cls
,
structure_key
):
""" Normalizes structure key for use in indexing """
return
structure_key
.
replace
(
version_guid
=
None
,
branch
=
None
)
@classmethod
def
_fetch_top_level
(
cls
,
modulestore
,
structure_key
):
""" Fetch the item from the modulestore location """
""" Fetch the item from the modulestore location """
return
modulestore
.
get_library
(
structure_key
,
depth
=
None
)
return
modulestore
.
get_library
(
structure_key
,
depth
=
None
)
@classmethod
@classmethod
def
_get_location_info
(
self
,
structure_key
):
def
_get_location_info
(
cls
,
normalized_
structure_key
):
""" Builds location info dictionary """
""" Builds location info dictionary """
return
{
"library"
:
unicode
(
structure_key
.
replace
(
version_guid
=
None
,
branch
=
None
)
)}
return
{
"library"
:
unicode
(
normalized_structure_key
)}
@classmethod
@classmethod
def
_id_modifier
(
self
,
usage_id
):
def
_id_modifier
(
cls
,
usage_id
):
""" Modifies usage_id to submit to index """
""" Modifies usage_id to submit to index """
return
usage_id
.
replace
(
library_key
=
(
usage_id
.
library_key
.
replace
(
version_guid
=
None
,
branch
=
None
)))
return
usage_id
.
replace
(
library_key
=
(
usage_id
.
library_key
.
replace
(
version_guid
=
None
,
branch
=
None
)))
...
@@ -273,4 +295,4 @@ class LibrarySearchIndexer(SearchIndexBase):
...
@@ -273,4 +295,4 @@ class LibrarySearchIndexer(SearchIndexBase):
"""
"""
(Re)index all content within the given library, tracking the fact that a full reindex has taken place
(Re)index all content within the given library, tracking the fact that a full reindex has taken place
"""
"""
return
cls
.
_do_reindex
(
modulestore
,
library_key
)
return
cls
.
_do_reindex
(
modulestore
,
library_key
)
\ No newline at end of file
cms/djangoapps/contentstore/signals.py
View file @
77cc80f6
""" receiver
of course_publish
ed events in order to trigger indexing task """
""" receiver
s of course_published and library_updat
ed events in order to trigger indexing task """
from
datetime
import
datetime
from
datetime
import
datetime
from
pytz
import
UTC
from
pytz
import
UTC
...
@@ -20,7 +20,7 @@ def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=
...
@@ -20,7 +20,7 @@ def listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=
@receiver
(
SignalHandler
.
library_updated
)
@receiver
(
SignalHandler
.
library_updated
)
def
listen_for_
course_publish
(
sender
,
library_key
,
**
kwargs
):
# pylint: disable=unused-argument
def
listen_for_
library_update
(
sender
,
library_key
,
**
kwargs
):
# pylint: disable=unused-argument
"""
"""
Receives signal and kicks off celery task to update search index
Receives signal and kicks off celery task to update search index
"""
"""
...
...
cms/djangoapps/contentstore/tasks.py
View file @
77cc80f6
...
@@ -92,21 +92,27 @@ def update_search_index(course_id, triggered_time_isoformat):
...
@@ -92,21 +92,27 @@ def update_search_index(course_id, triggered_time_isoformat):
triggered_time_isoformat
.
split
(
'+'
)[
0
],
triggered_time_isoformat
.
split
(
'+'
)[
0
],
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
S.
%
f"
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
S.
%
f"
)
.
replace
(
tzinfo
=
UTC
)
)
.
replace
(
tzinfo
=
UTC
)
CoursewareSearchIndexer
.
index
_course
(
modulestore
(),
course_key
,
triggered_at
=
triggered_time
)
CoursewareSearchIndexer
.
index
(
modulestore
(),
course_key
,
triggered_at
=
triggered_time
)
except
SearchIndexingError
as
exc
:
except
SearchIndexingError
as
exc
:
LOGGER
.
error
(
'Search indexing error for complete course
%
s -
%
s'
,
course_id
,
unicode
(
exc
))
LOGGER
.
error
(
'Search indexing error for complete course
%
s -
%
s'
,
course_id
,
unicode
(
exc
))
else
:
else
:
LOGGER
.
debug
(
'Search indexing successful for complete course
%
s'
,
course_id
)
LOGGER
.
debug
(
'Search indexing successful for complete course
%
s'
,
course_id
)
@task
()
@task
()
def
update_library_index
(
library_id
,
triggered_time
):
def
update_library_index
(
library_id
,
triggered_time
_isoformat
):
""" Updates course search index. """
""" Updates course search index. """
try
:
try
:
library_key
=
CourseKey
.
from_string
(
library_id
)
library_key
=
CourseKey
.
from_string
(
library_id
)
LibrarySearchIndexed
.
indexindex_course
(
modulestore
(),
library_key
,
triggered_at
=
triggered_time
)
triggered_time
=
datetime
.
strptime
(
# remove the +00:00 from the end of the formats generated within the system
triggered_time_isoformat
.
split
(
'+'
)[
0
],
"
%
Y-
%
m-
%
dT
%
H:
%
M:
%
S.
%
f"
)
.
replace
(
tzinfo
=
UTC
)
LibrarySearchIndexer
.
index
(
modulestore
(),
library_key
,
triggered_at
=
triggered_time
)
except
SearchIndexingError
as
exc
:
except
SearchIndexingError
as
exc
:
LOGGER
.
error
(
'Search indexing error for library
%
s -
%
s'
,
library_id
,
unicode
(
exc
))
LOGGER
.
error
(
'Search indexing error for library
%
s -
%
s'
,
library_id
,
unicode
(
exc
))
else
:
else
:
LOGGER
.
debug
(
'Search indexing successful for library
%
s'
,
library_id
)
LOGGER
.
debug
(
'Search indexing successful for library
%
s'
,
library_id
)
\ No newline at end of file
cms/djangoapps/contentstore/tests/test_courseware_index.py
View file @
77cc80f6
...
@@ -119,6 +119,8 @@ class MixedWithOptionsTestCase(MixedSplitTestCase):
...
@@ -119,6 +119,8 @@ class MixedWithOptionsTestCase(MixedSplitTestCase):
'xblock_mixins'
:
modulestore_options
[
'xblock_mixins'
],
'xblock_mixins'
:
modulestore_options
[
'xblock_mixins'
],
}
}
INDEX_NAME
=
None
def
setUp
(
self
):
def
setUp
(
self
):
super
(
MixedWithOptionsTestCase
,
self
)
.
setUp
()
super
(
MixedWithOptionsTestCase
,
self
)
.
setUp
()
...
@@ -128,17 +130,15 @@ class MixedWithOptionsTestCase(MixedSplitTestCase):
...
@@ -128,17 +130,15 @@ class MixedWithOptionsTestCase(MixedSplitTestCase):
@lazy
@lazy
def
searcher
(
self
):
def
searcher
(
self
):
return
self
.
get_search_engine
()
""" Centralized call to getting the search engine for the test """
return
SearchEngine
.
get_search_engine
(
self
.
INDEX_NAME
)
def
_get_default_search
(
self
):
def
_get_default_search
(
self
):
""" Returns field_dictionary for default search """
""" Returns field_dictionary for default search """
return
{}
return
{}
def
get_search_engine
(
self
):
""" Centralized call to getting the search engine for the test """
return
SearchEngine
.
get_search_engine
(
CoursewareSearchIndexer
.
INDEX_NAME
)
def
search
(
self
,
field_dictionary
=
None
):
def
search
(
self
,
field_dictionary
=
None
):
""" Performs index search according to passed parameters """
fields
=
field_dictionary
if
field_dictionary
else
self
.
_get_default_search
()
fields
=
field_dictionary
if
field_dictionary
else
self
.
_get_default_search
()
return
self
.
searcher
.
search
(
field_dictionary
=
fields
)
return
self
.
searcher
.
search
(
field_dictionary
=
fields
)
...
@@ -227,6 +227,8 @@ class TestCoursewareSearchIndexer(MixedWithOptionsTestCase):
...
@@ -227,6 +227,8 @@ class TestCoursewareSearchIndexer(MixedWithOptionsTestCase):
publish_item
=
False
,
publish_item
=
False
,
)
)
INDEX_NAME
=
CoursewareSearchIndexer
.
INDEX_NAME
def
reindex_course
(
self
,
store
):
def
reindex_course
(
self
,
store
):
""" kick off complete reindex of the course """
""" kick off complete reindex of the course """
return
CoursewareSearchIndexer
.
do_course_reindex
(
store
,
self
.
course
.
id
)
return
CoursewareSearchIndexer
.
do_course_reindex
(
store
,
self
.
course
.
id
)
...
@@ -319,7 +321,7 @@ class TestCoursewareSearchIndexer(MixedWithOptionsTestCase):
...
@@ -319,7 +321,7 @@ class TestCoursewareSearchIndexer(MixedWithOptionsTestCase):
# Add a non-indexable item
# Add a non-indexable item
ItemFactory
.
create
(
ItemFactory
.
create
(
parent_location
=
self
.
vertical
.
location
,
parent_location
=
self
.
vertical
.
location
,
category
=
"
problem
"
,
category
=
"
openassessment
"
,
display_name
=
"Some other content"
,
display_name
=
"Some other content"
,
publish_item
=
False
,
publish_item
=
False
,
modulestore
=
store
,
modulestore
=
store
,
...
@@ -459,8 +461,8 @@ class TestLargeCourseDeletions(MixedWithOptionsTestCase):
...
@@ -459,8 +461,8 @@ class TestLargeCourseDeletions(MixedWithOptionsTestCase):
response
=
self
.
searcher
.
search
(
field_dictionary
=
{
"course"
:
self
.
course_id
})
response
=
self
.
searcher
.
search
(
field_dictionary
=
{
"course"
:
self
.
course_id
})
while
response
[
"total"
]
>
0
:
while
response
[
"total"
]
>
0
:
for
item
in
response
[
"results"
]:
for
item
in
response
[
"results"
]:
self
.
searcher
.
remove
(
Test
CoursewareSearchIndexer
.
DOCUMENT_TYPE
,
item
[
"data"
][
"id"
])
self
.
searcher
.
remove
(
CoursewareSearchIndexer
.
DOCUMENT_TYPE
,
item
[
"data"
][
"id"
])
self
.
searcher
.
remove
(
Test
CoursewareSearchIndexer
.
DOCUMENT_TYPE
,
item
[
"data"
][
"id"
])
self
.
searcher
.
remove
(
CoursewareSearchIndexer
.
DOCUMENT_TYPE
,
item
[
"data"
][
"id"
])
response
=
self
.
searcher
.
search
(
field_dictionary
=
{
"course"
:
self
.
course_id
})
response
=
self
.
searcher
.
search
(
field_dictionary
=
{
"course"
:
self
.
course_id
})
self
.
course_id
=
None
self
.
course_id
=
None
...
@@ -618,9 +620,11 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
...
@@ -618,9 +620,11 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
publish_item
=
False
,
publish_item
=
False
,
)
)
INDEX_NAME
=
LibrarySearchIndexer
.
INDEX_NAME
def
_get_default_search
(
self
):
def
_get_default_search
(
self
):
""" Returns field_dictionary for default search """
""" Returns field_dictionary for default search """
return
{
"library"
:
unicode
(
self
.
library
.
location
.
replace
(
version_guid
=
None
,
branch
=
None
))}
return
{
"library"
:
unicode
(
self
.
library
.
location
.
library_key
.
replace
(
version_guid
=
None
,
branch
=
None
))}
def
reindex_library
(
self
,
store
):
def
reindex_library
(
self
,
store
):
""" kick off complete reindex of the course """
""" kick off complete reindex of the course """
...
@@ -628,7 +632,7 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
...
@@ -628,7 +632,7 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
def
_get_contents
(
self
,
response
):
def
_get_contents
(
self
,
response
):
""" Extracts contents from search response """
""" Extracts contents from search response """
return
[
item
[
'
contents
'
]
for
item
in
response
[
'results'
]]
return
[
item
[
'
data'
][
'content
'
]
for
item
in
response
[
'results'
]]
def
index_recent_changes
(
self
,
store
,
since_time
):
def
index_recent_changes
(
self
,
store
,
since_time
):
""" index course using recent changes """
""" index course using recent changes """
...
@@ -642,6 +646,7 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
...
@@ -642,6 +646,7 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
def
_test_indexing_library
(
self
,
store
):
def
_test_indexing_library
(
self
,
store
):
""" indexing course tests """
""" indexing course tests """
self
.
reindex_library
(
store
)
response
=
self
.
search
()
response
=
self
.
search
()
self
.
assertEqual
(
response
[
"total"
],
2
)
self
.
assertEqual
(
response
[
"total"
],
2
)
...
@@ -652,6 +657,7 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
...
@@ -652,6 +657,7 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
def
_test_creating_item
(
self
,
store
):
def
_test_creating_item
(
self
,
store
):
""" test updating an item """
""" test updating an item """
self
.
reindex_library
(
store
)
response
=
self
.
search
()
response
=
self
.
search
()
self
.
assertEqual
(
response
[
"total"
],
2
)
self
.
assertEqual
(
response
[
"total"
],
2
)
...
@@ -666,6 +672,7 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
...
@@ -666,6 +672,7 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
publish_item
=
False
,
publish_item
=
False
,
)
)
self
.
reindex_library
(
store
)
response
=
self
.
search
()
response
=
self
.
search
()
self
.
assertEqual
(
response
[
"total"
],
3
)
self
.
assertEqual
(
response
[
"total"
],
3
)
html_contents
=
[
cont
[
'html_content'
]
for
cont
in
self
.
_get_contents
(
response
)]
html_contents
=
[
cont
[
'html_content'
]
for
cont
in
self
.
_get_contents
(
response
)]
...
@@ -673,20 +680,24 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
...
@@ -673,20 +680,24 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
def
_test_updating_item
(
self
,
store
):
def
_test_updating_item
(
self
,
store
):
""" test updating an item """
""" test updating an item """
self
.
reindex_library
(
store
)
response
=
self
.
search
()
response
=
self
.
search
()
self
.
assertEqual
(
response
[
"total"
],
2
)
self
.
assertEqual
(
response
[
"total"
],
2
)
# updating a library item causes immediate reindexing
# updating a library item causes immediate reindexing
new_data
=
"I'm new data"
new_data
=
"I'm new data"
self
.
html_unit1
.
data
=
new_data
self
.
html_unit1
.
data
=
new_data
self
.
update_item
(
store
,
self
.
html_unit1
)
self
.
reindex_library
(
store
)
self
.
reindex_library
(
store
)
response
=
self
.
search
()
response
=
self
.
search
()
self
.
assertEqual
(
response
[
"total"
],
2
)
# TODO: MockSearchEngine never updates existing item: returns 3 items here - uncomment when it's fixed
# self.assertEqual(response["total"], 2)
html_contents
=
[
cont
[
'html_content'
]
for
cont
in
self
.
_get_contents
(
response
)]
html_contents
=
[
cont
[
'html_content'
]
for
cont
in
self
.
_get_contents
(
response
)]
self
.
assertIn
(
new_data
,
html_contents
)
self
.
assertIn
(
new_data
,
html_contents
)
def
_test_deleting_item
(
self
,
store
):
def
_test_deleting_item
(
self
,
store
):
""" test deleting an item """
""" test deleting an item """
self
.
reindex_library
(
store
)
response
=
self
.
search
()
response
=
self
.
search
()
self
.
assertEqual
(
response
[
"total"
],
2
)
self
.
assertEqual
(
response
[
"total"
],
2
)
...
@@ -698,15 +709,15 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
...
@@ -698,15 +709,15 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
def
_test_not_indexable
(
self
,
store
):
def
_test_not_indexable
(
self
,
store
):
""" test not indexable items """
""" test not indexable items """
self
.
reindex_library
(
store
)
response
=
self
.
search
()
response
=
self
.
search
()
self
.
assertEqual
(
response
[
"total"
],
2
)
self
.
assertEqual
(
response
[
"total"
],
2
)
# Add a non-indexable item
# Add a non-indexable item
ItemFactory
.
create
(
ItemFactory
.
create
(
parent_location
=
self
.
library
.
location
,
parent_location
=
self
.
library
.
location
,
category
=
"
problem
"
,
category
=
"
openassessment
"
,
display_name
=
"
Some other cont
ent"
,
display_name
=
"
Assessm
ent"
,
publish_item
=
False
,
publish_item
=
False
,
modulestore
=
store
,
modulestore
=
store
,
)
)
...
@@ -752,4 +763,4 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
...
@@ -752,4 +763,4 @@ class TestLibrarySearchIndexer(MixedWithOptionsTestCase):
@ddt.data
(
*
WORKS_WITH_STORES
)
@ddt.data
(
*
WORKS_WITH_STORES
)
def
test_exception
(
self
,
store_type
):
def
test_exception
(
self
,
store_type
):
self
.
_perform_test_using_store
(
store_type
,
self
.
_test_exception
)
self
.
_perform_test_using_store
(
store_type
,
self
.
_test_exception
)
\ No newline at end of file
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
77cc80f6
...
@@ -120,6 +120,7 @@ class BulkOpsRecord(object):
...
@@ -120,6 +120,7 @@ class BulkOpsRecord(object):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
_active_count
=
0
self
.
_active_count
=
0
self
.
has_publish_item
=
False
self
.
has_publish_item
=
False
self
.
has_library_updated_item
=
False
@property
@property
def
active
(
self
):
def
active
(
self
):
...
@@ -291,6 +292,15 @@ class BulkOperationsMixin(object):
...
@@ -291,6 +292,15 @@ class BulkOperationsMixin(object):
signal_handler
.
send
(
"course_published"
,
course_key
=
course_id
)
signal_handler
.
send
(
"course_published"
,
course_key
=
course_id
)
bulk_ops_record
.
has_publish_item
=
False
bulk_ops_record
.
has_publish_item
=
False
def
send_bulk_library_updated_signal
(
self
,
bulk_ops_record
,
library_id
):
"""
Sends out the signal that library have been updated.
"""
signal_handler
=
getattr
(
self
,
'signal_handler'
,
None
)
if
signal_handler
and
bulk_ops_record
.
has_library_updated_item
:
signal_handler
.
send
(
"library_updated"
,
library_key
=
library_id
)
bulk_ops_record
.
has_library_updated_item
=
False
class
EditInfo
(
object
):
class
EditInfo
(
object
):
"""
"""
...
@@ -1326,6 +1336,23 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
...
@@ -1326,6 +1336,23 @@ class ModuleStoreWriteBase(ModuleStoreReadBase, ModuleStoreWrite):
else
:
else
:
signal_handler
.
send
(
"course_published"
,
course_key
=
course_key
)
signal_handler
.
send
(
"course_published"
,
course_key
=
course_key
)
def
_flag_library_updated_event
(
self
,
library_key
):
"""
Wrapper around calls to fire the library_updated signal
Unless we're nested in an active bulk operation, this simply fires the signal
otherwise a publish will be signalled at the end of the bulk operation
Arguments:
library_updated - library_updated to which the signal applies
"""
signal_handler
=
getattr
(
self
,
'signal_handler'
,
None
)
if
signal_handler
:
bulk_record
=
self
.
_get_bulk_ops_record
(
library_key
)
if
isinstance
(
self
,
BulkOperationsMixin
)
else
None
if
bulk_record
and
bulk_record
.
active
:
bulk_record
.
has_library_updated_item
=
True
else
:
signal_handler
.
send
(
"library_updated"
,
library_key
=
library_key
)
def
only_xmodules
(
identifier
,
entry_points
):
def
only_xmodules
(
identifier
,
entry_points
):
"""Only use entry_points that are supplied by the xmodule package"""
"""Only use entry_points that are supplied by the xmodule package"""
...
...
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
77cc80f6
...
@@ -476,6 +476,7 @@ class MongoBulkOpsMixin(BulkOperationsMixin):
...
@@ -476,6 +476,7 @@ class MongoBulkOpsMixin(BulkOperationsMixin):
if
emit_signals
:
if
emit_signals
:
self
.
send_bulk_published_signal
(
bulk_ops_record
,
course_id
)
self
.
send_bulk_published_signal
(
bulk_ops_record
,
course_id
)
self
.
send_bulk_library_updated_signal
(
bulk_ops_record
,
course_id
)
bulk_ops_record
.
dirty
=
False
# brand spanking clean now
bulk_ops_record
.
dirty
=
False
# brand spanking clean now
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/split.py
View file @
77cc80f6
...
@@ -269,6 +269,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin):
...
@@ -269,6 +269,7 @@ class SplitBulkWriteMixin(BulkOperationsMixin):
if
dirty
and
emit_signals
:
if
dirty
and
emit_signals
:
self
.
send_bulk_published_signal
(
bulk_write_record
,
course_key
)
self
.
send_bulk_published_signal
(
bulk_write_record
,
course_key
)
self
.
send_bulk_library_updated_signal
(
bulk_write_record
,
course_key
)
def
get_course_index
(
self
,
course_key
,
ignore_case
=
False
):
def
get_course_index
(
self
,
course_key
,
ignore_case
=
False
):
"""
"""
...
@@ -1536,6 +1537,9 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
...
@@ -1536,6 +1537,9 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
block_id
=
block_key
.
id
,
block_id
=
block_key
.
id
,
)
)
if
isinstance
(
course_key
,
LibraryLocator
):
self
.
_flag_library_updated_event
(
course_key
)
# reconstruct the new_item from the cache
# reconstruct the new_item from the cache
return
self
.
get_item
(
item_loc
)
return
self
.
get_item
(
item_loc
)
...
@@ -1891,6 +1895,9 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
...
@@ -1891,6 +1895,9 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
else
:
else
:
course_key
=
CourseLocator
(
version_guid
=
new_id
)
course_key
=
CourseLocator
(
version_guid
=
new_id
)
if
isinstance
(
course_key
,
LibraryLocator
):
self
.
_flag_library_updated_event
(
course_key
)
# fetch and return the new item--fetching is unnecessary but a good qc step
# fetch and return the new item--fetching is unnecessary but a good qc step
new_locator
=
course_key
.
make_usage_key
(
block_key
.
type
,
block_key
.
id
)
new_locator
=
course_key
.
make_usage_key
(
block_key
.
type
,
block_key
.
id
)
return
self
.
get_item
(
new_locator
,
**
kwargs
)
return
self
.
get_item
(
new_locator
,
**
kwargs
)
...
@@ -2392,6 +2399,9 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
...
@@ -2392,6 +2399,9 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
else
:
else
:
result
=
CourseLocator
(
version_guid
=
new_id
)
result
=
CourseLocator
(
version_guid
=
new_id
)
if
isinstance
(
usage_locator
.
course_key
,
LibraryLocator
):
self
.
_flag_library_updated_event
(
usage_locator
.
course_key
)
return
result
return
result
@contract
(
block_key
=
BlockKey
,
blocks
=
'dict(BlockKey: BlockData)'
)
@contract
(
block_key
=
BlockKey
,
blocks
=
'dict(BlockKey: BlockData)'
)
...
...
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