Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ Common: Allow instructors to input complicated expressions as answers to
`NumericalResponse`s. Prior to the change only numbers were allowed, now any
answer from '1/3' to 'sqrt(12)*(1-1/3^2+1/5/3^2)' are valid.

Studio/LMS: Allow for 'preview' and 'published' in a single LMS instance. Use
middlware components to retain the incoming Django request and put in thread
local storage. It is recommended that all developers define a 'preview.localhost'
which maps to the same IP address as localhost in his/her HOSTS file.

LMS: Enable beta instructor dashboard. The beta dashboard is a rearchitecture
of the existing instructor dashboard and is available by clicking a link at
the top right of the existing dashboard.
Expand Down
12 changes: 12 additions & 0 deletions cms/envs/dev_shared_preview.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
This configuration is have localdev use a preview.localhost hostname for the preview LMS so that we can share
the same process between preview and published
"""

# We intentionally define lots of variables that aren't used, and
# want to import all variables from base settings files
# pylint: disable=W0401, W0614

from .dev import *

MITX_FEATURES['PREVIEW_LMS_BASE'] = "preview.localhost:8000"
35 changes: 34 additions & 1 deletion common/lib/xmodule/xmodule/modulestore/django.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
from __future__ import absolute_import
from importlib import import_module

import re

from django.conf import settings
from django.core.cache import get_cache, InvalidCacheBackendError
from django.dispatch import Signal
from xmodule.modulestore.loc_mapper_store import LocMapperStore
from xmodule.util.django import get_current_request_hostname

# We may not always have the request_cache module available
try:
Expand Down Expand Up @@ -67,11 +70,41 @@ def create_modulestore_instance(engine, options):
)


def modulestore(name='default'):
def get_default_store_name_for_current_request():
"""
This method will return the appropriate default store mapping for the current Django request,
else 'default' which is the system default
"""
store_name = 'default'

# see what request we are currently processing - if any at all - and get hostname for the request
hostname = get_current_request_hostname()

# get mapping information which is defined in configurations
mappings = getattr(settings, 'HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS', None)

# compare hostname against the regex expressions set of mappings
# which will tell us which store name to use
if hostname and mappings:
for key in mappings.keys():
if re.match(key, hostname):
store_name = mappings[key]
return store_name

return store_name


def modulestore(name=None):
"""
This returns an instance of a modulestore of given name. This will wither return an existing
modulestore or create a new one
"""

if not name:
# If caller did not specify name then we should
# determine what should be the default
name = get_default_store_name_for_current_request()

if name not in _MODULESTORES:
_MODULESTORES[name] = create_modulestore_instance(settings.MODULESTORE[name]['ENGINE'],
settings.MODULESTORE[name]['OPTIONS'])
Expand Down
20 changes: 20 additions & 0 deletions common/lib/xmodule/xmodule/tests/test_utils_django.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Tests for methods defined in util/django.py"""
from xmodule.util.django import get_current_request, get_current_request_hostname
from nose.tools import assert_is_none
from unittest import TestCase

class UtilDjangoTests(TestCase):
"""
Tests for methods exposed in util/django
"""
def test_get_current_request(self):
"""
Since we are running outside of Django assert that get_current_request returns None
"""
assert_is_none(get_current_request())

def test_get_current_request_hostname(self):
"""
Since we are running outside of Django assert that get_current_request_hostname returns None
"""
assert_is_none(get_current_request_hostname())
20 changes: 20 additions & 0 deletions common/lib/xmodule/xmodule/util/django.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
Exposes Django utilities for consumption in the xmodule library
NOTE: This file should only be imported into 'django-safe' code, i.e. known that this code runs int the Django
runtime environment with the djangoapps in common configured to load
"""

# NOTE: we are importing this method so that any module that imports us has access to get_current_request
from crum import get_current_request


def get_current_request_hostname():
"""
This method will return the hostname that was used in the current Django request
"""
hostname = None
request = get_current_request()
if request:
hostname = request.META.get('HTTP_HOST')

return hostname
14 changes: 14 additions & 0 deletions lms/djangoapps/courseware/tests/test_courses.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# -*- coding: utf-8 -*-
import mock

from django.test import TestCase
from django.http import Http404
from django.test.utils import override_settings
from courseware.courses import get_course_by_id, get_cms_course_link_by_id
from xmodule.modulestore.django import get_default_store_name_for_current_request

CMS_BASE_TEST = 'testcms'

Expand All @@ -26,3 +29,14 @@ def test_get_cms_course_link_by_id(self):
self.assertEqual("//{}/".format(CMS_BASE_TEST), get_cms_course_link_by_id("blah_bad_course_id"))
self.assertEqual("//{}/".format(CMS_BASE_TEST), get_cms_course_link_by_id("too/too/many/slashes"))
self.assertEqual("//{}/org/num/course/name".format(CMS_BASE_TEST), get_cms_course_link_by_id('org/num/name'))


@mock.patch('xmodule.modulestore.django.get_current_request_hostname', mock.Mock(return_value='preview.localhost'))
@override_settings(HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS={'preview\.': 'draft'})
def test_default_modulestore_preview_mapping(self):
self.assertEqual(get_default_store_name_for_current_request(), 'draft')

@mock.patch('xmodule.modulestore.django.get_current_request_hostname', mock.Mock(return_value='localhost'))
@override_settings(HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS={'preview\.': 'draft'})
def test_default_modulestore_published_mapping(self):
self.assertEqual(get_default_store_name_for_current_request(), 'default')
2 changes: 2 additions & 0 deletions lms/envs/cms/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@
CMS_AUTH_TOKENS = json.load(auth_file)

MODULESTORE = CMS_AUTH_TOKENS['MODULESTORE']

HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS = ENVS_TOKENS.get('HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS',{})
11 changes: 11 additions & 0 deletions lms/envs/cms/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
'ENGINE': 'xmodule.modulestore.mongo.MongoModuleStore',
'OPTIONS': modulestore_options
},
'draft': {
'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore',
'OPTIONS': modulestore_options
},
}

CONTENTSTORE = {
Expand All @@ -58,3 +62,10 @@
DEBUG_TOOLBAR_PANELS += (
'debug_toolbar_mongo.panel.MongoDebugPanel',
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps some documentation here explaining what this does?

# HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS defines, as dictionary of regex's, a set of mappings of HTTP request hostnames to
# what the 'default' modulestore to use while processing the request
# for example 'preview.edx.org' should use the draft modulestore
HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS = {
'preview\.': 'draft'
}
1 change: 1 addition & 0 deletions lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@
#'django.contrib.auth.middleware.AuthenticationMiddleware',
'cache_toolbox.middleware.CacheBackedAuthenticationMiddleware',
'contentserver.middleware.StaticContentServer',
'crum.CurrentRequestUserMiddleware',

'django.contrib.messages.middleware.MessageMiddleware',
'track.middleware.TrackMiddleware',
Expand Down
1 change: 1 addition & 0 deletions requirements/edx/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ django_debug_toolbar
django-debug-toolbar-mongo
nose-ignore-docstring
nose-exclude
django-crum==0.5

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jtauber can I get your signoff here?

I forwarded in email a different 3rd party package. This package does the same thing, but I preferred it. It's also BSD, I believe.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, no problem with BSD.


git+https://github.com/mfogel/django-settings-context-processor.git

Expand Down