141 lines
4.4 KiB
Python
141 lines
4.4 KiB
Python
"""
|
|
Bookmarks service.
|
|
"""
|
|
from __future__ import absolute_import
|
|
|
|
import logging
|
|
|
|
import six
|
|
from django.core.exceptions import ObjectDoesNotExist
|
|
from edx_django_utils.cache import DEFAULT_REQUEST_CACHE
|
|
|
|
from xmodule.modulestore.django import modulestore
|
|
from xmodule.modulestore.exceptions import ItemNotFoundError
|
|
|
|
from . import DEFAULT_FIELDS, api
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
CACHE_KEY_TEMPLATE = u"bookmarks.list.{}.{}"
|
|
|
|
|
|
class BookmarksService(object):
|
|
"""
|
|
A service that provides access to the bookmarks API.
|
|
|
|
When bookmarks() or is_bookmarked() is called for the
|
|
first time, the service fetches and caches all the bookmarks
|
|
of the user for the relevant course. So multiple calls to
|
|
get bookmark status during a request (for, example when
|
|
rendering courseware and getting bookmarks status for search
|
|
results) will not cause repeated queries to the database.
|
|
"""
|
|
|
|
def __init__(self, user, **kwargs):
|
|
super(BookmarksService, self).__init__(**kwargs)
|
|
self._user = user
|
|
|
|
def _bookmarks_cache(self, course_key, fetch=False):
|
|
"""
|
|
Return the user's bookmarks cache for a particular course.
|
|
|
|
Arguments:
|
|
course_key (CourseKey): course_key of the course whose bookmarks cache should be returned.
|
|
fetch (Bool): if the bookmarks should be fetched and cached if they already aren't.
|
|
"""
|
|
store = modulestore()
|
|
course_key = store.fill_in_run(course_key)
|
|
if course_key.run is None:
|
|
return []
|
|
cache_key = CACHE_KEY_TEMPLATE.format(self._user.id, course_key)
|
|
|
|
bookmarks_cache = DEFAULT_REQUEST_CACHE.data.get(cache_key, None)
|
|
if bookmarks_cache is None and fetch is True:
|
|
bookmarks_cache = api.get_bookmarks(
|
|
self._user, course_key=course_key, fields=DEFAULT_FIELDS
|
|
)
|
|
DEFAULT_REQUEST_CACHE.data[cache_key] = bookmarks_cache
|
|
|
|
return bookmarks_cache
|
|
|
|
def bookmarks(self, course_key):
|
|
"""
|
|
Return a list of bookmarks for the course for the current user.
|
|
|
|
Arguments:
|
|
course_key: CourseKey of the course for which to retrieve the user's bookmarks for.
|
|
|
|
Returns:
|
|
list of dict:
|
|
"""
|
|
return self._bookmarks_cache(course_key, fetch=True)
|
|
|
|
def is_bookmarked(self, usage_key):
|
|
"""
|
|
Return whether the block has been bookmarked by the user.
|
|
|
|
Arguments:
|
|
usage_key: UsageKey of the block.
|
|
|
|
Returns:
|
|
Bool
|
|
"""
|
|
usage_id = six.text_type(usage_key)
|
|
bookmarks_cache = self._bookmarks_cache(usage_key.course_key, fetch=True)
|
|
for bookmark in bookmarks_cache:
|
|
if bookmark['usage_id'] == usage_id:
|
|
return True
|
|
|
|
return False
|
|
|
|
def set_bookmarked(self, usage_key):
|
|
"""
|
|
Adds a bookmark for the block.
|
|
|
|
Arguments:
|
|
usage_key: UsageKey of the block.
|
|
|
|
Returns:
|
|
Bool indicating whether the bookmark was added.
|
|
"""
|
|
try:
|
|
bookmark = api.create_bookmark(user=self._user, usage_key=usage_key)
|
|
except ItemNotFoundError:
|
|
log.error(u'Block with usage_id: %s not found.', usage_key)
|
|
return False
|
|
|
|
bookmarks_cache = self._bookmarks_cache(usage_key.course_key)
|
|
if bookmarks_cache is not None:
|
|
bookmarks_cache.append(bookmark)
|
|
|
|
return True
|
|
|
|
def unset_bookmarked(self, usage_key):
|
|
"""
|
|
Removes the bookmark for the block.
|
|
|
|
Arguments:
|
|
usage_key: UsageKey of the block.
|
|
|
|
Returns:
|
|
Bool indicating whether the bookmark was removed.
|
|
"""
|
|
try:
|
|
api.delete_bookmark(self._user, usage_key=usage_key)
|
|
except ObjectDoesNotExist:
|
|
log.error(u'Bookmark with usage_id: %s does not exist.', usage_key)
|
|
return False
|
|
|
|
bookmarks_cache = self._bookmarks_cache(usage_key.course_key)
|
|
if bookmarks_cache is not None:
|
|
deleted_bookmark_index = None
|
|
usage_id = six.text_type(usage_key)
|
|
for index, bookmark in enumerate(bookmarks_cache):
|
|
if bookmark['usage_id'] == usage_id:
|
|
deleted_bookmark_index = index
|
|
break
|
|
if deleted_bookmark_index is not None:
|
|
bookmarks_cache.pop(deleted_bookmark_index)
|
|
|
|
return True
|