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
b857a0ed
Commit
b857a0ed
authored
Aug 15, 2014
by
John Eskew
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Phase 1 of adding asset metadata saving to old Mongo
parent
98c9880d
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
541 additions
and
5 deletions
+541
-5
common/lib/xmodule/xmodule/assetstore/__init__.py
+168
-0
common/lib/xmodule/xmodule/modulestore/__init__.py
+0
-0
common/lib/xmodule/xmodule/modulestore/mixed.py
+210
-1
common/lib/xmodule/xmodule/modulestore/mongo/base.py
+155
-3
common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py
+5
-1
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
+2
-0
common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
+0
-0
lms/envs/common.py
+1
-0
No files found.
common/lib/xmodule/xmodule/assetstore/__init__.py
0 → 100644
View file @
b857a0ed
"""
Classes representing asset & asset thumbnail metadata.
"""
from
datetime
import
datetime
from
contracts
import
contract
,
new_contract
from
opaque_keys.edx.keys
import
CourseKey
,
AssetKey
new_contract
(
'AssetKey'
,
AssetKey
)
new_contract
(
'datetime'
,
datetime
)
class
IncorrectAssetIdType
(
Exception
):
"""
Raised when the asset ID passed-in to create an AssetMetadata or
AssetThumbnailMetadata is of the wrong type.
"""
pass
class
AssetMetadata
(
object
):
"""
Stores the metadata associated with a particular course asset. The asset metadata gets stored
in the modulestore.
"""
TOP_LEVEL_ATTRS
=
[
'basename'
,
'internal_name'
,
'locked'
,
'contenttype'
,
'md5'
]
EDIT_INFO_ATTRS
=
[
'curr_version'
,
'prev_version'
,
'edited_by'
,
'edited_on'
]
ALLOWED_ATTRS
=
TOP_LEVEL_ATTRS
+
EDIT_INFO_ATTRS
# All AssetMetadata objects should have AssetLocators with this type.
ASSET_TYPE
=
'asset'
@contract
(
asset_id
=
'AssetKey'
,
basename
=
'str | unicode | None'
,
internal_name
=
'str | None'
,
locked
=
'bool | None'
,
contenttype
=
'str | unicode | None'
,
md5
=
'str | None'
,
curr_version
=
'str | None'
,
prev_version
=
'str | None'
)
def
__init__
(
self
,
asset_id
,
basename
=
None
,
internal_name
=
None
,
locked
=
None
,
contenttype
=
None
,
md5
=
None
,
curr_version
=
None
,
prev_version
=
None
):
"""
Construct a AssetMetadata object.
Arguments:
asset_id (AssetKey): Key identifying this particular asset.
basename (str): Original path to file at asset upload time.
internal_name (str): Name under which the file is stored internally.
locked (bool): If True, only course participants can access the asset.
contenttype (str): MIME type of the asset.
curr_version (str): Current version of the asset.
prev_version (str): Previous version of the asset.
"""
if
asset_id
.
asset_type
!=
self
.
ASSET_TYPE
:
raise
IncorrectAssetIdType
()
self
.
asset_id
=
asset_id
self
.
basename
=
basename
# Path w/o filename.
self
.
internal_name
=
internal_name
self
.
locked
=
locked
self
.
contenttype
=
contenttype
self
.
md5
=
md5
self
.
curr_version
=
curr_version
self
.
prev_version
=
prev_version
self
.
edited_by
=
None
self
.
edited_on
=
None
def
__repr__
(
self
):
return
"""AssetMetadata{!r}"""
.
format
((
self
.
asset_id
,
self
.
basename
,
self
.
internal_name
,
self
.
locked
,
self
.
contenttype
,
self
.
md5
,
self
.
curr_version
,
self
.
prev_version
,
self
.
edited_by
,
self
.
edited_on
))
def
update
(
self
,
attr_dict
):
"""
Set the attributes on the metadata. Ignore all those outside the known fields.
Arguments:
attr_dict: Prop, val dictionary of all attributes to set.
"""
for
attr
,
val
in
attr_dict
.
iteritems
():
if
attr
in
self
.
ALLOWED_ATTRS
:
setattr
(
self
,
attr
,
val
)
def
to_mongo
(
self
):
"""
Converts metadata properties into a MongoDB-storable dict.
"""
return
{
'filename'
:
self
.
asset_id
.
path
,
'basename'
:
self
.
basename
,
'internal_name'
:
self
.
internal_name
,
'locked'
:
self
.
locked
,
'contenttype'
:
self
.
contenttype
,
'md5'
:
self
.
md5
,
'edit_info'
:
{
'curr_version'
:
self
.
curr_version
,
'prev_version'
:
self
.
prev_version
,
'edited_by'
:
self
.
edited_by
,
'edited_on'
:
self
.
edited_on
}
}
@contract
(
asset_doc
=
'dict | None'
)
def
from_mongo
(
self
,
asset_doc
):
"""
Fill in all metadata fields from a MongoDB document.
The asset_id prop is initialized upon construction only.
"""
if
asset_doc
is
None
:
return
self
.
basename
=
asset_doc
[
'basename'
]
self
.
internal_name
=
asset_doc
[
'internal_name'
]
self
.
locked
=
asset_doc
[
'locked'
]
self
.
contenttype
=
asset_doc
[
'contenttype'
]
self
.
md5
=
asset_doc
[
'md5'
]
edit_info
=
asset_doc
[
'edit_info'
]
self
.
curr_version
=
edit_info
[
'curr_version'
]
self
.
prev_version
=
edit_info
[
'prev_version'
]
self
.
edited_by
=
edit_info
[
'edited_by'
]
self
.
edited_on
=
edit_info
[
'edited_on'
]
class
AssetThumbnailMetadata
(
object
):
"""
Stores the metadata associated with the thumbnail of a course asset.
"""
# All AssetThumbnailMetadata objects should have AssetLocators with this type.
ASSET_TYPE
=
'thumbnail'
@contract
(
asset_id
=
'AssetKey'
,
internal_name
=
'str | unicode | None'
)
def
__init__
(
self
,
asset_id
,
internal_name
=
None
):
"""
Construct a AssetThumbnailMetadata object.
Arguments:
asset_id (AssetKey): Key identifying this particular asset.
internal_name (str): Name under which the file is stored internally.
"""
if
asset_id
.
asset_type
!=
self
.
ASSET_TYPE
:
raise
IncorrectAssetIdType
()
self
.
asset_id
=
asset_id
self
.
internal_name
=
internal_name
def
__repr__
(
self
):
return
"""AssetMetadata{!r}"""
.
format
((
self
.
asset_id
,
self
.
internal_name
))
def
to_mongo
(
self
):
"""
Converts metadata properties into a MongoDB-storable dict.
"""
return
{
'filename'
:
self
.
asset_id
.
path
,
'internal_name'
:
self
.
internal_name
}
@contract
(
thumbnail_doc
=
'dict | None'
)
def
from_mongo
(
self
,
thumbnail_doc
):
"""
Fill in all metadata fields from a MongoDB document.
The asset_id prop is initialized upon construction only.
"""
if
thumbnail_doc
is
None
:
return
self
.
internal_name
=
thumbnail_doc
[
'internal_name'
]
common/lib/xmodule/xmodule/modulestore/__init__.py
View file @
b857a0ed
This diff is collapsed.
Click to expand it.
common/lib/xmodule/xmodule/modulestore/mixed.py
View file @
b857a0ed
...
@@ -9,10 +9,12 @@ import logging
...
@@ -9,10 +9,12 @@ import logging
from
contextlib
import
contextmanager
from
contextlib
import
contextmanager
import
itertools
import
itertools
import
functools
import
functools
from
contracts
import
contract
,
new_contract
from
opaque_keys
import
InvalidKeyError
from
opaque_keys
import
InvalidKeyError
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
,
AssetKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
xmodule.assetstore
import
AssetMetadata
,
AssetThumbnailMetadata
from
.
import
ModuleStoreWriteBase
from
.
import
ModuleStoreWriteBase
from
.
import
ModuleStoreEnum
from
.
import
ModuleStoreEnum
...
@@ -20,6 +22,10 @@ from .exceptions import ItemNotFoundError, DuplicateCourseError
...
@@ -20,6 +22,10 @@ from .exceptions import ItemNotFoundError, DuplicateCourseError
from
.draft_and_published
import
ModuleStoreDraftAndPublished
from
.draft_and_published
import
ModuleStoreDraftAndPublished
from
.split_migrator
import
SplitMigrator
from
.split_migrator
import
SplitMigrator
new_contract
(
'CourseKey'
,
CourseKey
)
new_contract
(
'AssetKey'
,
AssetKey
)
new_contract
(
'AssetMetadata'
,
AssetMetadata
)
new_contract
(
'AssetThumbnailMetadata'
,
AssetThumbnailMetadata
)
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
...
@@ -309,6 +315,209 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
...
@@ -309,6 +315,209 @@ class MixedModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase):
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
return
store
.
delete_course
(
course_key
,
user_id
)
return
store
.
delete_course
(
course_key
,
user_id
)
def
_save_asset_info
(
self
,
course_key
,
asset_metadata
,
user_id
,
thumbnail
=
False
):
"""
Base method to over-ride in modulestore.
"""
raise
NotImplementedError
()
def
_delete_asset_data
(
self
,
asset_key
,
thumbnail
=
False
):
"""
Base method to over-ride in modulestore.
"""
raise
NotImplementedError
()
def
_find_course_assets
(
self
,
course_key
):
"""
Base method to override.
"""
raise
NotImplementedError
()
@contract
(
course_key
=
'CourseKey'
,
asset_metadata
=
'AssetMetadata'
)
def
save_asset_metadata
(
self
,
course_key
,
asset_metadata
,
user_id
):
"""
Saves the asset metadata for a particular course's asset.
Args:
course_key (CourseKey): course identifier
asset_metadata (AssetMetadata): data about the course asset data
Returns:
bool: True if metadata save was successful, else False
"""
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
return
store
.
save_asset_metadata
(
course_key
,
asset_metadata
,
user_id
)
@contract
(
course_key
=
'CourseKey'
,
asset_thumbnail_metadata
=
'AssetThumbnailMetadata'
)
def
save_asset_thumbnail_metadata
(
self
,
course_key
,
asset_thumbnail_metadata
):
"""
Saves the asset thumbnail metadata for a particular course asset's thumbnail.
Arguments:
course_key (CourseKey): course identifier
asset_thumbnail_metadata (AssetThumbnailMetadata): data about the course asset thumbnail
Returns:
True if thumbnail metadata save was successful, else False
"""
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
return
store
.
save_asset_metadata
(
course_key
,
asset_thumbnail_metadata
)
@contract
(
asset_key
=
'AssetKey'
)
def
find_asset_metadata
(
self
,
asset_key
):
"""
Find the metadata for a particular course asset.
Args:
asset_key (AssetKey): locator containing original asset filename
Returns:
asset metadata (AssetMetadata) -or- None if not found
"""
store
=
self
.
_get_modulestore_for_courseid
(
asset_key
.
course_key
)
return
store
.
find_asset_metadata
(
asset_key
)
@contract
(
asset_key
=
'AssetKey'
)
def
find_asset_thumbnail_metadata
(
self
,
asset_key
):
"""
Find the metadata for a particular course asset.
Arguments:
asset_key (AssetKey): key containing original asset filename
Returns:
asset metadata (AssetMetadata) -or- None if not found
"""
store
=
self
.
_get_modulestore_for_courseid
(
asset_key
.
course_key
)
return
store
.
find_asset_thumbnail_metadata
(
asset_key
)
@contract
(
course_key
=
'CourseKey'
,
start
=
int
,
maxresults
=
int
,
sort
=
'list | None'
)
def
get_all_asset_metadata
(
self
,
course_key
,
start
=
0
,
maxresults
=-
1
,
sort
=
None
):
"""
Returns a list of static assets for a course.
By default all assets are returned, but start and maxresults can be provided to limit the query.
Args:
course_key (CourseKey): course identifier
start (int): optional - start at this asset number
maxresults (int): optional - return at most this many, -1 means no limit
sort (array): optional - None means no sort
(sort_by (str), sort_order (str))
sort_by - one of 'uploadDate' or 'displayname'
sort_order - one of 'ascending' or 'descending'
Returns:
List of asset data dictionaries, which have the following keys:
asset_key (AssetKey): asset identifier
displayname: The human-readable name of the asset
uploadDate (datetime.datetime): The date and time that the file was uploaded
contentType: The mimetype string of the asset
md5: An md5 hash of the asset content
"""
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
return
store
.
get_all_asset_metadata
(
course_key
,
start
,
maxresults
,
sort
)
@contract
(
course_key
=
'CourseKey'
)
def
get_all_asset_thumbnail_metadata
(
self
,
course_key
):
"""
Returns a list of thumbnails for all course assets.
Args:
course_key (CourseKey): course identifier
Returns:
List of AssetThumbnailMetadata objects.
"""
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
return
store
.
get_all_asset_thumbnail_metadata
(
course_key
)
@contract
(
asset_key
=
'AssetKey'
)
def
delete_asset_metadata
(
self
,
asset_key
):
"""
Deletes a single asset's metadata.
Arguments:
asset_id (AssetKey): locator containing original asset filename
Returns:
Number of asset metadata entries deleted (0 or 1)
"""
store
=
self
.
_get_modulestore_for_courseid
(
asset_key
.
course_key
)
return
store
.
delete_asset_metadata
(
asset_key
)
@contract
(
asset_key
=
'AssetKey'
)
def
delete_asset_thumbnail_metadata
(
self
,
asset_key
):
"""
Deletes a single asset's metadata.
Arguments:
asset_key (AssetKey): locator containing original asset filename
Returns:
Number of asset metadata entries deleted (0 or 1)
"""
store
=
self
.
_get_modulestore_for_courseid
(
asset_key
.
course_key
)
return
store
.
delete_asset_metadata
(
asset_key
)
@contract
(
course_key
=
'CourseKey'
)
def
delete_all_asset_metadata
(
self
,
course_key
):
"""
Delete all of the assets which use this course_key as an identifier.
Arguments:
course_key (CourseKey): course_identifier
"""
store
=
self
.
_get_modulestore_for_courseid
(
course_key
)
return
store
.
delete_all_asset_metadata
(
course_key
)
@contract
(
source_course_key
=
'CourseKey'
,
dest_course_key
=
'CourseKey'
)
def
copy_all_asset_metadata
(
self
,
source_course_key
,
dest_course_key
):
"""
Copy all the course assets from source_course_key to dest_course_key.
Arguments:
source_course_key (CourseKey): identifier of course to copy from
dest_course_key (CourseKey): identifier of course to copy to
"""
# When implementing this in https://openedx.atlassian.net/browse/PLAT-78 , consider this:
# Check the modulestores of both the source and dest course_keys. If in different modulestores,
# export all asset data from one modulestore and import it into the dest one.
store
=
self
.
_get_modulestore_for_courseid
(
source_course_key
)
return
store
.
copy_all_asset_metadata
(
source_course_key
,
dest_course_key
)
@contract
(
asset_key
=
'AssetKey'
,
attr
=
str
)
def
set_asset_metadata_attr
(
self
,
asset_key
,
attr
,
value
,
user_id
):
"""
Add/set the given attr on the asset at the given location. Value can be any type which pymongo accepts.
Arguments:
asset_key (AssetKey): asset identifier
attr (str): which attribute to set
value: the value to set it to (any type pymongo accepts such as datetime, number, string)
Raises:
NotFoundError if no such item exists
AttributeError is attr is one of the build in attrs.
"""
store
=
self
.
_get_modulestore_for_courseid
(
asset_key
.
course_key
)
return
store
.
set_asset_metadata_attrs
(
asset_key
,
attr
,
value
,
user_id
)
@contract
(
asset_key
=
'AssetKey'
,
attr_dict
=
dict
)
def
set_asset_metadata_attrs
(
self
,
asset_key
,
attr_dict
,
user_id
):
"""
Add/set the given dict of attrs on the asset at the given location. Value can be any type which pymongo accepts.
Arguments:
asset_key (AssetKey): asset identifier
attr_dict (dict): attribute/value pairs to set
Raises:
NotFoundError if no such item exists
AttributeError is attr is one of the build in attrs.
"""
store
=
self
.
_get_modulestore_for_courseid
(
asset_key
.
course_key
)
return
store
.
set_asset_metadata_attrs
(
asset_key
,
attr_dict
,
user_id
)
@strip_key
@strip_key
def
get_parent_location
(
self
,
location
,
**
kwargs
):
def
get_parent_location
(
self
,
location
,
**
kwargs
):
"""
"""
...
...
common/lib/xmodule/xmodule/modulestore/mongo/base.py
View file @
b857a0ed
...
@@ -24,6 +24,7 @@ from fs.osfs import OSFS
...
@@ -24,6 +24,7 @@ from fs.osfs import OSFS
from
path
import
path
from
path
import
path
from
datetime
import
datetime
from
datetime
import
datetime
from
pytz
import
UTC
from
pytz
import
UTC
from
contracts
import
contract
,
new_contract
from
importlib
import
import_module
from
importlib
import
import_module
from
xmodule.errortracker
import
null_error_tracker
,
exc_info_to_str
from
xmodule.errortracker
import
null_error_tracker
,
exc_info_to_str
...
@@ -41,12 +42,18 @@ from xmodule.modulestore.inheritance import InheritanceMixin, inherit_metadata,
...
@@ -41,12 +42,18 @@ from xmodule.modulestore.inheritance import InheritanceMixin, inherit_metadata,
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locations
import
SlashSeparatedCourseKey
from
opaque_keys.edx.locator
import
CourseLocator
from
opaque_keys.edx.locator
import
CourseLocator
from
opaque_keys.edx.keys
import
UsageKey
,
CourseKey
from
opaque_keys.edx.keys
import
UsageKey
,
CourseKey
,
AssetKey
from
xmodule.exceptions
import
HeartbeatFailure
from
xmodule.exceptions
import
HeartbeatFailure
from
xmodule.modulestore.edit_info
import
EditInfoRuntimeMixin
from
xmodule.modulestore.edit_info
import
EditInfoRuntimeMixin
from
xmodule.assetstore
import
AssetMetadata
,
AssetThumbnailMetadata
log
=
logging
.
getLogger
(
__name__
)
log
=
logging
.
getLogger
(
__name__
)
new_contract
(
'CourseKey'
,
CourseKey
)
new_contract
(
'AssetKey'
,
AssetKey
)
new_contract
(
'AssetMetadata'
,
AssetMetadata
)
new_contract
(
'AssetThumbnailMetadata'
,
AssetThumbnailMetadata
)
# sort order that returns DRAFT items first
# sort order that returns DRAFT items first
SORT_REVISION_FAVOR_DRAFT
=
(
'_id.revision'
,
pymongo
.
DESCENDING
)
SORT_REVISION_FAVOR_DRAFT
=
(
'_id.revision'
,
pymongo
.
DESCENDING
)
...
@@ -195,7 +202,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin):
...
@@ -195,7 +202,6 @@ class CachingDescriptorSystem(MakoDescriptorSystem, EditInfoRuntimeMixin):
category
=
json_data
[
'location'
][
'category'
]
category
=
json_data
[
'location'
][
'category'
]
class_
=
self
.
load_block_type
(
category
)
class_
=
self
.
load_block_type
(
category
)
definition
=
json_data
.
get
(
'definition'
,
{})
definition
=
json_data
.
get
(
'definition'
,
{})
metadata
=
json_data
.
get
(
'metadata'
,
{})
metadata
=
json_data
.
get
(
'metadata'
,
{})
for
old_name
,
new_name
in
getattr
(
class_
,
'metadata_translations'
,
{})
.
items
():
for
old_name
,
new_name
in
getattr
(
class_
,
'metadata_translations'
,
{})
.
items
():
...
@@ -443,7 +449,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
...
@@ -443,7 +449,7 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
super
(
MongoModuleStore
,
self
)
.
__init__
(
contentstore
=
contentstore
,
**
kwargs
)
super
(
MongoModuleStore
,
self
)
.
__init__
(
contentstore
=
contentstore
,
**
kwargs
)
def
do_connection
(
def
do_connection
(
db
,
collection
,
host
,
port
=
27017
,
tz_aware
=
True
,
user
=
None
,
password
=
None
,
**
kwargs
db
,
collection
,
host
,
port
=
27017
,
tz_aware
=
True
,
user
=
None
,
password
=
None
,
asset_collection
=
None
,
**
kwargs
):
):
"""
"""
Create & open the connection, authenticate, and provide pointers to the collection
Create & open the connection, authenticate, and provide pointers to the collection
...
@@ -460,6 +466,11 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
...
@@ -460,6 +466,11 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
)
)
self
.
collection
=
self
.
database
[
collection
]
self
.
collection
=
self
.
database
[
collection
]
# Collection which stores asset metadata.
self
.
asset_collection
=
None
if
asset_collection
is
not
None
:
self
.
asset_collection
=
self
.
database
[
asset_collection
]
if
user
is
not
None
and
password
is
not
None
:
if
user
is
not
None
and
password
is
not
None
:
self
.
database
.
authenticate
(
user
,
password
)
self
.
database
.
authenticate
(
user
,
password
)
...
@@ -1436,6 +1447,147 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
...
@@ -1436,6 +1447,147 @@ class MongoModuleStore(ModuleStoreDraftAndPublished, ModuleStoreWriteBase, Mongo
field_data
=
KvsFieldData
(
kvs
)
field_data
=
KvsFieldData
(
kvs
)
return
field_data
return
field_data
def
_find_course_assets
(
self
,
course_key
):
"""
Internal; finds (or creates) course asset info about all assets for a particular course
Arguments:
course_key (CourseKey): course identifier
Returns:
Asset info for the course
"""
if
self
.
asset_collection
is
None
:
return
None
# Using the course_key, find or insert the course asset metadata document.
# A single document exists per course to store the course asset metadata.
course_assets
=
self
.
asset_collection
.
find_one
(
{
'course_id'
:
unicode
(
course_key
)},
fields
=
(
'course_id'
,
'storage'
,
'assets'
,
'thumbnails'
)
)
if
course_assets
is
None
:
# Not found, so create.
course_assets
=
{
'course_id'
:
unicode
(
course_key
),
'storage'
:
'FILLMEIN-TMP'
,
'assets'
:
[],
'thumbnails'
:
[]}
course_assets
[
'_id'
]
=
self
.
asset_collection
.
insert
(
course_assets
)
return
course_assets
@contract
(
course_key
=
'CourseKey'
,
asset_metadata
=
'AssetMetadata | AssetThumbnailMetadata'
,
user_id
=
'str | unicode'
)
def
_save_asset_info
(
self
,
course_key
,
asset_metadata
,
user_id
,
thumbnail
=
False
):
"""
Saves the info for a particular course's asset/thumbnail.
Arguments:
course_key (CourseKey): course identifier
asset_metadata (AssetMetadata/AssetThumbnailMetadata): data about the course asset/thumbnail
thumbnail (bool): True if saving thumbnail metadata, False if saving asset metadata
Returns:
True if info save was successful, else False
"""
if
self
.
asset_collection
is
None
:
return
False
course_assets
,
asset_idx
=
self
.
_find_course_asset
(
course_key
,
asset_metadata
.
asset_id
.
path
,
thumbnail
)
info
=
'thumbnails'
if
thumbnail
else
'assets'
all_assets
=
course_assets
[
info
]
# Set the edited information for assets only - not thumbnails.
if
not
thumbnail
:
asset_metadata
.
update
({
'edited_by'
:
user_id
,
'edited_on'
:
datetime
.
now
(
UTC
)})
# Translate metadata to Mongo format.
metadata_to_insert
=
asset_metadata
.
to_mongo
()
if
asset_idx
is
None
:
# Append new metadata.
# Future optimization: Insert in order & binary search to retrieve.
all_assets
.
append
(
metadata_to_insert
)
else
:
# Replace existing metadata.
all_assets
[
asset_idx
]
=
metadata_to_insert
# Update the document.
self
.
asset_collection
.
update
({
'_id'
:
course_assets
[
'_id'
]},
{
'$set'
:
{
info
:
all_assets
}})
return
True
@contract
(
asset_key
=
'AssetKey'
,
attr_dict
=
dict
)
def
set_asset_metadata_attrs
(
self
,
asset_key
,
attr_dict
,
user_id
):
"""
Add/set the given dict of attrs on the asset at the given location. Value can be any type which pymongo accepts.
Arguments:
asset_key (AssetKey): asset identifier
attr_dict (dict): attribute: value pairs to set
Raises:
ItemNotFoundError if no such item exists
AttributeError is attr is one of the build in attrs.
"""
if
self
.
asset_collection
is
None
:
return
course_assets
,
asset_idx
=
self
.
_find_course_asset
(
asset_key
.
course_key
,
asset_key
.
path
)
if
asset_idx
is
None
:
raise
ItemNotFoundError
(
asset_key
)
# Form an AssetMetadata.
all_assets
=
course_assets
[
'assets'
]
md
=
AssetMetadata
(
asset_key
,
asset_key
.
path
)
md
.
from_mongo
(
all_assets
[
asset_idx
])
md
.
update
(
attr_dict
)
md
.
update
({
'edited_by'
:
user_id
,
'edited_on'
:
datetime
.
now
(
UTC
)})
# Generate a Mongo doc from the metadata and update the course asset info.
all_assets
[
asset_idx
]
=
md
.
to_mongo
()
self
.
asset_collection
.
update
({
'_id'
:
course_assets
[
'_id'
]},
{
"$set"
:
{
'assets'
:
all_assets
}})
@contract
(
asset_key
=
'AssetKey'
)
def
_delete_asset_data
(
self
,
asset_key
,
thumbnail
=
False
):
"""
Internal; deletes a single asset's metadata -or- thumbnail.
Arguments:
asset_key (AssetKey): key containing original asset/thumbnail filename
thumbnail: True if thumbnail deletion, False if asset metadata deletion
Returns:
Number of asset metadata/thumbnail entries deleted (0 or 1)
"""
if
self
.
asset_collection
is
None
:
return
0
course_assets
,
asset_idx
=
self
.
_find_course_asset
(
asset_key
.
course_key
,
asset_key
.
path
,
get_thumbnail
=
thumbnail
)
if
asset_idx
is
None
:
return
0
info
=
'thumbnails'
if
thumbnail
else
'assets'
all_asset_info
=
course_assets
[
info
]
all_asset_info
.
pop
(
asset_idx
)
# Update the document.
self
.
asset_collection
.
update
({
'_id'
:
course_assets
[
'_id'
]},
{
'$set'
:
{
info
:
all_asset_info
}})
return
1
@contract
(
course_key
=
'CourseKey'
)
def
delete_all_asset_metadata
(
self
,
course_key
):
"""
Delete all of the assets which use this course_key as an identifier.
Arguments:
course_key (CourseKey): course_identifier
"""
if
self
.
asset_collection
is
None
:
return
# Using the course_id, find the course asset metadata document.
# A single document exists per course to store the course asset metadata.
course_assets
=
self
.
_find_course_assets
(
course_key
)
self
.
asset_collection
.
remove
(
course_assets
[
'_id'
])
def
heartbeat
(
self
):
def
heartbeat
(
self
):
"""
"""
Check that the db is reachable.
Check that the db is reachable.
...
...
common/lib/xmodule/xmodule/modulestore/split_mongo/mongo_connection.py
View file @
b857a0ed
...
@@ -99,7 +99,7 @@ class MongoConnection(object):
...
@@ -99,7 +99,7 @@ class MongoConnection(object):
Segregation of pymongo functions from the data modeling mechanisms for split modulestore.
Segregation of pymongo functions from the data modeling mechanisms for split modulestore.
"""
"""
def
__init__
(
def
__init__
(
self
,
db
,
collection
,
host
,
port
=
27017
,
tz_aware
=
True
,
user
=
None
,
password
=
None
,
**
kwargs
self
,
db
,
collection
,
host
,
port
=
27017
,
tz_aware
=
True
,
user
=
None
,
password
=
None
,
asset_collection
=
None
,
**
kwargs
):
):
"""
"""
Create & open the connection, authenticate, and provide pointers to the collections
Create & open the connection, authenticate, and provide pointers to the collections
...
@@ -114,6 +114,10 @@ class MongoConnection(object):
...
@@ -114,6 +114,10 @@ class MongoConnection(object):
db
db
)
)
# Remove when adding official Split support for asset metadata storage.
if
asset_collection
:
pass
if
user
is
not
None
and
password
is
not
None
:
if
user
is
not
None
and
password
is
not
None
:
self
.
database
.
authenticate
(
user
,
password
)
self
.
database
.
authenticate
(
user
,
password
)
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_mixed_modulestore.py
View file @
b857a0ed
...
@@ -47,6 +47,7 @@ class TestMixedModuleStore(CourseComparisonTest):
...
@@ -47,6 +47,7 @@ class TestMixedModuleStore(CourseComparisonTest):
PORT
=
MONGO_PORT_NUM
PORT
=
MONGO_PORT_NUM
DB
=
'test_mongo_
%
s'
%
uuid4
()
.
hex
[:
5
]
DB
=
'test_mongo_
%
s'
%
uuid4
()
.
hex
[:
5
]
COLLECTION
=
'modulestore'
COLLECTION
=
'modulestore'
ASSET_COLLECTION
=
'assetstore'
FS_ROOT
=
DATA_DIR
FS_ROOT
=
DATA_DIR
DEFAULT_CLASS
=
'xmodule.raw_module.RawDescriptor'
DEFAULT_CLASS
=
'xmodule.raw_module.RawDescriptor'
RENDER_TEMPLATE
=
lambda
t_n
,
d
,
ctx
=
None
,
nsp
=
'main'
:
''
RENDER_TEMPLATE
=
lambda
t_n
,
d
,
ctx
=
None
,
nsp
=
'main'
:
''
...
@@ -67,6 +68,7 @@ class TestMixedModuleStore(CourseComparisonTest):
...
@@ -67,6 +68,7 @@ class TestMixedModuleStore(CourseComparisonTest):
'port'
:
PORT
,
'port'
:
PORT
,
'db'
:
DB
,
'db'
:
DB
,
'collection'
:
COLLECTION
,
'collection'
:
COLLECTION
,
'asset_collection'
:
ASSET_COLLECTION
,
}
}
OPTIONS
=
{
OPTIONS
=
{
'mappings'
:
{
'mappings'
:
{
...
...
common/lib/xmodule/xmodule/modulestore/tests/test_mongo.py
View file @
b857a0ed
This diff is collapsed.
Click to expand it.
lms/envs/common.py
View file @
b857a0ed
...
@@ -566,6 +566,7 @@ DOC_STORE_CONFIG = {
...
@@ -566,6 +566,7 @@ DOC_STORE_CONFIG = {
'host'
:
'localhost'
,
'host'
:
'localhost'
,
'db'
:
'xmodule'
,
'db'
:
'xmodule'
,
'collection'
:
'modulestore'
,
'collection'
:
'modulestore'
,
'asset_collection'
:
'assetstore'
,
}
}
MODULESTORE
=
{
MODULESTORE
=
{
'default'
:
{
'default'
:
{
...
...
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