Commit 136f3d34 by Bill DeRusha

Merge pull request #12552 from edx/release

Merge Release 2016-05-24 to Master
parents 025f74c8 14c02628
...@@ -8,7 +8,7 @@ from opaque_keys import InvalidKeyError ...@@ -8,7 +8,7 @@ from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore.django import modulestore from xmodule.modulestore.django import modulestore
from ...api import get_course_in_cache from ...api import get_course_in_cache, update_course_in_cache
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -39,6 +39,12 @@ class Command(BaseCommand): ...@@ -39,6 +39,12 @@ class Command(BaseCommand):
action='store_true', action='store_true',
default=False, default=False,
) )
parser.add_argument(
'--force',
help='Force update of the course blocks for the requested courses.',
action='store_true',
default=False,
)
def handle(self, *args, **options): def handle(self, *args, **options):
...@@ -57,7 +63,10 @@ class Command(BaseCommand): ...@@ -57,7 +63,10 @@ class Command(BaseCommand):
for course_key in course_keys: for course_key in course_keys:
try: try:
block_structure = get_course_in_cache(course_key) if options.get('force'):
block_structure = update_course_in_cache(course_key)
else:
block_structure = get_course_in_cache(course_key)
if options.get('dags'): if options.get('dags'):
self._find_and_log_dags(block_structure, course_key) self._find_and_log_dags(block_structure, course_key)
except Exception as ex: # pylint: disable=broad-except except Exception as ex: # pylint: disable=broad-except
......
...@@ -44,6 +44,21 @@ class TestGenerateCourseBlocks(ModuleStoreTestCase): ...@@ -44,6 +44,21 @@ class TestGenerateCourseBlocks(ModuleStoreTestCase):
self._assert_courses_not_in_block_cache(self.course_1.id, self.course_2.id) self._assert_courses_not_in_block_cache(self.course_1.id, self.course_2.id)
self.command.handle(all=True) self.command.handle(all=True)
self._assert_courses_in_block_cache(self.course_1.id, self.course_2.id) self._assert_courses_in_block_cache(self.course_1.id, self.course_2.id)
with patch(
'openedx.core.lib.block_structure.factory.BlockStructureFactory.create_from_modulestore'
) as mock_update_from_store:
self.command.handle(all=True)
mock_update_from_store.assert_not_called()
def test_generate_force(self):
self._assert_courses_not_in_block_cache(self.course_1.id, self.course_2.id)
self.command.handle(all=True)
self._assert_courses_in_block_cache(self.course_1.id, self.course_2.id)
with patch(
'openedx.core.lib.block_structure.factory.BlockStructureFactory.create_from_modulestore'
) as mock_update_from_store:
self.command.handle(all=True, force=True)
mock_update_from_store.assert_called()
def test_generate_one(self): def test_generate_one(self):
self._assert_courses_not_in_block_cache(self.course_1.id, self.course_2.id) self._assert_courses_not_in_block_cache(self.course_1.id, self.course_2.id)
......
...@@ -111,7 +111,7 @@ class DateSummary(object): ...@@ -111,7 +111,7 @@ class DateSummary(object):
date_format = _(u"{relative} ago - {absolute}") if date_has_passed else _(u"in {relative} - {absolute}") date_format = _(u"{relative} ago - {absolute}") if date_has_passed else _(u"in {relative} - {absolute}")
return date_format.format( return date_format.format(
relative=relative_date, relative=relative_date,
absolute=self.date.strftime(self.date_format), absolute=self.date.strftime(self.date_format.encode('utf-8')).decode('utf-8'),
) )
@property @property
...@@ -157,7 +157,9 @@ class TodaysDate(DateSummary): ...@@ -157,7 +157,9 @@ class TodaysDate(DateSummary):
@property @property
def title(self): def title(self):
return _(u'Today is {date}').format(date=datetime.now(pytz.UTC).strftime(self.date_format)) return _(u'Today is {date}').format(
date=datetime.now(pytz.UTC).strftime(self.date_format.encode('utf-8')).decode('utf-8')
)
class CourseStartDate(DateSummary): class CourseStartDate(DateSummary):
......
""" Course Discovery API Service. """ """ Course Discovery API Service. """
from django.conf import settings import datetime
from django.conf import settings
from edx_rest_api_client.client import EdxRestApiClient from edx_rest_api_client.client import EdxRestApiClient
import jwt
from openedx.core.djangoapps.theming import helpers from openedx.core.djangoapps.theming import helpers
from openedx.core.lib.token_utils import get_id_token
from provider.oauth2.models import Client from provider.oauth2.models import Client
from student.models import UserProfile, anonymous_id_for_user
CLIENT_NAME = 'course-discovery' CLIENT_NAME = 'course-discovery'
def get_id_token(user):
"""
Return a JWT for `user`, suitable for use with the course discovery service.
Arguments:
user (User): User for whom to generate the JWT.
Returns:
str: The JWT.
"""
try:
# Service users may not have user profiles.
full_name = UserProfile.objects.get(user=user).name
except UserProfile.DoesNotExist:
full_name = None
now = datetime.datetime.utcnow()
expires_in = getattr(settings, 'OAUTH_ID_TOKEN_EXPIRATION', 30)
payload = {
'preferred_username': user.username,
'name': full_name,
'email': user.email,
'administrator': user.is_staff,
'iss': helpers.get_value('OAUTH_OIDC_ISSUER', settings.OAUTH_OIDC_ISSUER),
'exp': now + datetime.timedelta(seconds=expires_in),
'iat': now,
'aud': helpers.get_value('JWT_AUTH', settings.JWT_AUTH)['JWT_AUDIENCE'],
'sub': anonymous_id_for_user(user, None),
}
secret_key = helpers.get_value('JWT_AUTH', settings.JWT_AUTH)['JWT_SECRET_KEY']
return jwt.encode(payload, secret_key)
def course_discovery_api_client(user): def course_discovery_api_client(user):
""" Returns a Course Discovery API client setup with authentication for the specified user. """ """ Returns a Course Discovery API client setup with authentication for the specified user. """
course_discovery_client = Client.objects.get(name=CLIENT_NAME) course_discovery_client = Client.objects.get(name=CLIENT_NAME)
secret_key = helpers.get_value('JWT_AUTH', settings.JWT_AUTH)['JWT_SECRET_KEY'] return EdxRestApiClient(course_discovery_client.url, jwt=get_id_token(user))
return EdxRestApiClient(
course_discovery_client.url,
jwt=get_id_token(user, CLIENT_NAME, secret_key=secret_key)
)
...@@ -96,3 +96,13 @@ def update_course_structure(course_key): ...@@ -96,3 +96,13 @@ def update_course_structure(course_key):
structure_model.structure_json = structure_json structure_model.structure_json = structure_json
structure_model.discussion_id_map_json = discussion_id_map_json structure_model.discussion_id_map_json = discussion_id_map_json
structure_model.save() structure_model.save()
# TODO (TNL-4630) For temporary hotfix to update the block_structure cache.
# Should be moved to proper location.
from django.core.cache import cache
from openedx.core.lib.block_structure.manager import BlockStructureManager
store = modulestore()
course_usage_key = store.make_course_usage_key(course_key)
block_structure_manager = BlockStructureManager(course_usage_key, store, cache)
block_structure_manager.update_collected()
...@@ -45,13 +45,13 @@ class BlockStructureCache(object): ...@@ -45,13 +45,13 @@ class BlockStructureCache(object):
) )
zp_data_to_cache = zpickle(data_to_cache) zp_data_to_cache = zpickle(data_to_cache)
# Set the timeout value for the cache to None. This caches the # Set the timeout value for the cache to 1 day as a fail-safe
# value forever. The expectation is that the caller will delete # in case the signal to invalidate the cache doesn't come through.
# the cached value once it is outdated. timeout_in_seconds = 60 * 60 * 24
self._cache.set( self._cache.set(
self._encode_root_cache_key(block_structure.root_block_usage_key), self._encode_root_cache_key(block_structure.root_block_usage_key),
zp_data_to_cache, zp_data_to_cache,
timeout=None, timeout=timeout_in_seconds,
) )
logger.info( logger.info(
......
...@@ -36,7 +36,7 @@ class TestBlockStructureCache(ChildrenMapTestMixin, TestCase): ...@@ -36,7 +36,7 @@ class TestBlockStructureCache(ChildrenMapTestMixin, TestCase):
self.add_transformers() self.add_transformers()
self.block_structure_cache.add(self.block_structure) self.block_structure_cache.add(self.block_structure)
self.assertEquals(self.mock_cache.timeout_from_last_call, None) self.assertEquals(self.mock_cache.timeout_from_last_call, 60 * 60 * 24)
cached_value = self.block_structure_cache.get(self.block_structure.root_block_usage_key) cached_value = self.block_structure_cache.get(self.block_structure.root_block_usage_key)
self.assertIsNotNone(cached_value) self.assertIsNotNone(cached_value)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment