Commit 3deb3a3c by Calen Pennington Committed by GitHub

Merge pull request #14977 from cpennington/cale/fix-learner-542

Cale/fix learner 542
parents 09593998 2ca367a1
......@@ -221,6 +221,8 @@ GIT_REPO_EXPORT_DIR = ENV_TOKENS.get('GIT_REPO_EXPORT_DIR', '/edx/var/edxapp/exp
# Translation overrides
LANGUAGES = ENV_TOKENS.get('LANGUAGES', LANGUAGES)
LANGUAGE_CODE = ENV_TOKENS.get('LANGUAGE_CODE', LANGUAGE_CODE)
LANGUAGE_COOKIE = ENV_TOKENS.get('LANGUAGE_COOKIE', LANGUAGE_COOKIE)
USE_I18N = ENV_TOKENS.get('USE_I18N', USE_I18N)
ENV_FEATURES = ENV_TOKENS.get('FEATURES', {})
......
......@@ -547,6 +547,8 @@ TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_
LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGES_BIDI = lms.envs.common.LANGUAGES_BIDI
LANGUAGE_COOKIE = lms.envs.common.LANGUAGE_COOKIE
LANGUAGES = lms.envs.common.LANGUAGES
LANGUAGE_DICT = dict(LANGUAGES)
......
......@@ -17,7 +17,7 @@ from student.models import CourseEnrollment
CREATE_LOGON_COOKIE = Signal(providing_args=['user', 'response'])
def _get_cookie_settings(request):
def standard_cookie_settings(request):
""" Returns the common cookie settings (e.g. expiration time). """
if request.session.get_expire_at_browser_close():
......@@ -73,7 +73,7 @@ def set_logged_in_cookies(request, response, user):
HttpResponse
"""
cookie_settings = _get_cookie_settings(request)
cookie_settings = standard_cookie_settings(request)
# Backwards compatibility: set the cookie indicating that the user
# is logged in. This is just a boolean value, so it's not very useful.
......@@ -96,7 +96,7 @@ def set_logged_in_cookies(request, response, user):
def set_user_info_cookie(response, request):
""" Sets the user info cookie on the response. """
cookie_settings = _get_cookie_settings(request)
cookie_settings = standard_cookie_settings(request)
# In production, TLS should be enabled so that this cookie is encrypted
# when we send it. We also need to set "secure" to True so that the browser
......
......@@ -231,18 +231,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase):
# # of sql queries to default,
# # of mongo queries,
# )
('no_overrides', 1, True, False): (25, 1),
('no_overrides', 2, True, False): (25, 1),
('no_overrides', 3, True, False): (25, 1),
('ccx', 1, True, False): (25, 1),
('ccx', 2, True, False): (25, 1),
('ccx', 3, True, False): (25, 1),
('no_overrides', 1, False, False): (25, 1),
('no_overrides', 2, False, False): (25, 1),
('no_overrides', 3, False, False): (25, 1),
('ccx', 1, False, False): (25, 1),
('ccx', 2, False, False): (25, 1),
('ccx', 3, False, False): (25, 1),
('no_overrides', 1, True, False): (23, 1),
('no_overrides', 2, True, False): (23, 1),
('no_overrides', 3, True, False): (23, 1),
('ccx', 1, True, False): (23, 1),
('ccx', 2, True, False): (23, 1),
('ccx', 3, True, False): (23, 1),
('no_overrides', 1, False, False): (23, 1),
('no_overrides', 2, False, False): (23, 1),
('no_overrides', 3, False, False): (23, 1),
('ccx', 1, False, False): (23, 1),
('ccx', 2, False, False): (23, 1),
('ccx', 3, False, False): (23, 1),
}
......@@ -254,19 +254,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase):
__test__ = True
TEST_DATA = {
('no_overrides', 1, True, False): (25, 3),
('no_overrides', 2, True, False): (25, 3),
('no_overrides', 3, True, False): (25, 3),
('ccx', 1, True, False): (25, 3),
('ccx', 2, True, False): (25, 3),
('ccx', 3, True, False): (25, 3),
('ccx', 1, True, True): (26, 3),
('ccx', 2, True, True): (26, 3),
('ccx', 3, True, True): (26, 3),
('no_overrides', 1, False, False): (25, 3),
('no_overrides', 2, False, False): (25, 3),
('no_overrides', 3, False, False): (25, 3),
('ccx', 1, False, False): (25, 3),
('ccx', 2, False, False): (25, 3),
('ccx', 3, False, False): (25, 3),
('no_overrides', 1, True, False): (23, 3),
('no_overrides', 2, True, False): (23, 3),
('no_overrides', 3, True, False): (23, 3),
('ccx', 1, True, False): (23, 3),
('ccx', 2, True, False): (23, 3),
('ccx', 3, True, False): (23, 3),
('ccx', 1, True, True): (24, 3),
('ccx', 2, True, True): (24, 3),
('ccx', 3, True, True): (24, 3),
('no_overrides', 1, False, False): (23, 3),
('no_overrides', 2, False, False): (23, 3),
('no_overrides', 3, False, False): (23, 3),
('ccx', 1, False, False): (23, 3),
('ccx', 2, False, False): (23, 3),
('ccx', 3, False, False): (23, 3),
}
......@@ -367,7 +367,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest
self.assertEqual(resp.status_code, 200)
def test_num_queries_instructor_paced(self):
self.fetch_course_info_with_queries(self.instructor_paced_course, 21, 4)
self.fetch_course_info_with_queries(self.instructor_paced_course, 18, 4)
def test_num_queries_self_paced(self):
self.fetch_course_info_with_queries(self.self_paced_course, 21, 4)
self.fetch_course_info_with_queries(self.self_paced_course, 18, 4)
......@@ -2,6 +2,7 @@
Tests i18n in courseware
"""
import json
import re
from django.conf import settings
......@@ -14,8 +15,6 @@ from nose.plugins.attrib import attr
from openedx.core.djangoapps.dark_lang.models import DarkLangConfig
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from student.models import UserProfile
from student.tests.factories import UserFactory
......@@ -61,8 +60,7 @@ class BaseI18nTestCase(TestCase):
"""
# Create one user and save it to the database
email = 'test@edx.org'
self.user = UserFactory.build(username='test', email=email, password=self.pwd)
self.user.save()
self.user = UserFactory.create(username='test', email=email, password=self.pwd)
def user_login(self):
"""
......@@ -119,7 +117,6 @@ class I18nRegressionTests(BaseI18nTestCase):
def test_unreleased_lang_resolution(self):
# Regression test; LOC-85
UserProfile.objects.create(user=self.user)
self.release_languages('fa')
self.user_login()
......@@ -136,7 +133,6 @@ class I18nRegressionTests(BaseI18nTestCase):
self.assert_tag_has_attr(response.content, "html", "lang", "fa-ir")
def test_preview_lang(self):
UserProfile.objects.create(user=self.user)
self.user_login()
# Regression test; LOC-87
......@@ -172,9 +168,17 @@ class I18nLangPrefTests(BaseI18nTestCase):
"""
def setUp(self):
super(I18nLangPrefTests, self).setUp()
UserProfile.objects.create(user=self.user)
self.user_login()
def set_lang_preference(self, language):
"""Sets the user's language preference, allowing the LangPref middleware to operate to set the preference cookie."""
response = self.client.patch(
reverse('preferences_api', args=[self.user.username]),
json.dumps({LANGUAGE_KEY: language}),
content_type="application/merge-patch+json"
)
self.assertEqual(response.status_code, 204)
def test_lang_preference(self):
# Regression test; LOC-87
self.release_languages('ar, es-419')
......@@ -184,13 +188,13 @@ class I18nLangPrefTests(BaseI18nTestCase):
self.assert_tag_has_attr(response.content, "html", "lang", self.site_lang)
# Set user language preference
set_user_preference(self.user, LANGUAGE_KEY, 'ar')
self.set_lang_preference('ar')
# and verify we now get an ar response
response = self.client.get(self.url)
self.assert_tag_has_attr(response.content, "html", "lang", 'ar')
# Verify that switching language preference gives the right language
set_user_preference(self.user, LANGUAGE_KEY, 'es-419')
self.set_lang_preference('es-419')
response = self.client.get(self.url)
self.assert_tag_has_attr(response.content, "html", "lang", 'es-419')
......@@ -199,7 +203,7 @@ class I18nLangPrefTests(BaseI18nTestCase):
self.release_languages('ar, es-419')
# Set user language preference
set_user_preference(self.user, LANGUAGE_KEY, 'ar')
self.set_lang_preference('ar')
# Verify preview-lang takes precedence
self.client.post(self.preview_language_url, {'preview_lang': 'eo', 'set_language': 'set_language'})
response = self.client.get(self.url)
......
......@@ -206,8 +206,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
NUM_PROBLEMS = 20
@ddt.data(
(ModuleStoreEnum.Type.mongo, 10, 147),
(ModuleStoreEnum.Type.split, 4, 147),
(ModuleStoreEnum.Type.mongo, 10, 141),
(ModuleStoreEnum.Type.split, 4, 141),
)
@ddt.unpack
def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
......@@ -1408,12 +1408,12 @@ class ProgressPageTests(ModuleStoreTestCase):
"""Test that query counts remain the same for self-paced and instructor-paced courses."""
SelfPacedConfiguration(enabled=self_paced_enabled).save()
self.setup_course(self_paced=self_paced)
with self.assertNumQueries(42), check_mongo_calls(1):
with self.assertNumQueries(39), check_mongo_calls(1):
self._get_progress_page()
@ddt.data(
(False, 42, 28),
(True, 35, 24)
(False, 39, 25),
(True, 32, 21)
)
@ddt.unpack
def test_progress_queries(self, enable_waffle, initial, subsequent):
......
......@@ -377,8 +377,8 @@ class ViewsQueryCountTestCase(
return inner
@ddt.data(
(ModuleStoreEnum.Type.mongo, 3, 4, 31),
(ModuleStoreEnum.Type.split, 3, 13, 31),
(ModuleStoreEnum.Type.mongo, 3, 4, 30),
(ModuleStoreEnum.Type.split, 3, 13, 30),
)
@ddt.unpack
@count_queries
......@@ -386,8 +386,8 @@ class ViewsQueryCountTestCase(
self.create_thread_helper(mock_request)
@ddt.data(
(ModuleStoreEnum.Type.mongo, 3, 3, 27),
(ModuleStoreEnum.Type.split, 3, 10, 27),
(ModuleStoreEnum.Type.mongo, 3, 3, 26),
(ModuleStoreEnum.Type.split, 3, 10, 26),
)
@ddt.unpack
@count_queries
......
......@@ -315,6 +315,8 @@ TIME_ZONE = ENV_TOKENS.get('TIME_ZONE', TIME_ZONE)
LANGUAGES = ENV_TOKENS.get('LANGUAGES', LANGUAGES)
LANGUAGE_DICT = dict(LANGUAGES)
LANGUAGE_CODE = ENV_TOKENS.get('LANGUAGE_CODE', LANGUAGE_CODE)
LANGUAGE_COOKIE = ENV_TOKENS.get('LANGUAGE_COOKIE', LANGUAGE_COOKIE)
USE_I18N = ENV_TOKENS.get('USE_I18N', USE_I18N)
# Additional installed apps
......
......@@ -863,6 +863,8 @@ LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html
# these languages display right to left
LANGUAGES_BIDI = ("he", "ar", "fa", "ur", "fa-ir", "rtl")
LANGUAGE_COOKIE = "openedx-language-preference"
# Sourced from http://www.localeplanet.com/icu/ and wikipedia
LANGUAGES = (
('en', u'English'),
......
......@@ -30,6 +30,7 @@ FEATURES.update({
'ENABLE_COURSEWARE_SEARCH': False,
'ENABLE_COURSE_DISCOVERY': False,
'ENABLE_DASHBOARD_SEARCH': False,
'SHOW_LANGUAGE_SELECTOR': True
})
ENABLE_MKTG_SITE = os.environ.get('ENABLE_MARKETING_SITE', False)
......
......@@ -268,7 +268,7 @@ class BookmarksListViewTests(BookmarksViewsTestsBase):
self.assertEqual(response.data['developer_message'], u'Parameter usage_id not provided.')
# Send empty data dictionary.
with self.assertNumQueries(8): # No queries for bookmark table.
with self.assertNumQueries(7): # No queries for bookmark table.
response = self.send_post(
client=self.client,
url=reverse('bookmarks'),
......
......@@ -4,3 +4,7 @@ Useful information for setting the language preference
# this is the UserPreference key for the user's preferred language
LANGUAGE_KEY = 'pref-lang'
LANGUAGE_HEADER = 'HTTP_ACCEPT_LANGUAGE'
COOKIE_DURATION = 14 * 24 * 60 * 60 # 14 days in seconds
......@@ -2,12 +2,17 @@
Middleware for Language Preferences
"""
from django.conf import settings
from django.utils.translation import LANGUAGE_SESSION_KEY
from django.utils.translation.trans_real import parse_accept_lang_header
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.lang_pref.api import released_languages
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference, delete_user_preference
from openedx.core.djangoapps.lang_pref import (
LANGUAGE_KEY, LANGUAGE_HEADER, COOKIE_DURATION
)
from openedx.core.djangoapps.user_api.preferences.api import (
get_user_preference, delete_user_preference, set_user_preference
)
from openedx.core.djangoapps.user_api.errors import UserAPIInternalError, UserAPIRequestError
class LanguagePreferenceMiddleware(object):
......@@ -21,17 +26,46 @@ class LanguagePreferenceMiddleware(object):
def process_request(self, request):
"""
If a user's UserPreference contains a language preference, use the user's preference.
Save the current language preference cookie as the user's preferred language.
"""
languages = released_languages()
system_released_languages = [seq[0] for seq in languages]
cookie_lang = request.COOKIES.get(settings.LANGUAGE_COOKIE, None)
if cookie_lang:
if request.user.is_authenticated():
set_user_preference(request.user, LANGUAGE_KEY, cookie_lang)
accept_header = request.META.get(LANGUAGE_HEADER, None)
if accept_header:
current_langs = parse_accept_lang_header(accept_header)
# Promote the cookie_lang over any language currently in the accept header
current_langs = [(lang, qvalue) for (lang, qvalue) in current_langs if lang != cookie_lang]
current_langs.insert(0, (cookie_lang, 1))
accept_header = ",".join("{};q={}".format(lang, qvalue) for (lang, qvalue) in current_langs)
else:
accept_header = cookie_lang
request.META[LANGUAGE_HEADER] = accept_header
def process_response(self, request, response):
# If the user is logged in, check for their language preference
if request.user.is_authenticated():
if getattr(request, 'user', None) and request.user.is_authenticated():
# Get the user's language preference
user_pref = get_user_preference(request.user, LANGUAGE_KEY)
# Set it to the LANGUAGE_SESSION_KEY (Django-specific session setting governing language pref)
if user_pref:
if user_pref in system_released_languages:
request.session[LANGUAGE_SESSION_KEY] = user_pref
try:
user_pref = get_user_preference(request.user, LANGUAGE_KEY)
except (UserAPIRequestError, UserAPIInternalError):
# If we can't find the user preferences, then don't modify the cookie
pass
else:
# Set it in the LANGUAGE_COOKIE
if user_pref:
response.set_cookie(
settings.LANGUAGE_COOKIE,
value=user_pref,
domain=settings.SESSION_COOKIE_DOMAIN,
max_age=COOKIE_DURATION,
)
else:
delete_user_preference(request.user, LANGUAGE_KEY)
response.delete_cookie(
settings.LANGUAGE_COOKIE,
domain=settings.SESSION_COOKIE_DOMAIN
)
return response
......@@ -9,7 +9,7 @@ from django.http import HttpResponse
from django.views.decorators.csrf import ensure_csrf_cookie
from django.utils.translation import LANGUAGE_SESSION_KEY
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY, COOKIE_DURATION
@ensure_csrf_cookie
......@@ -17,9 +17,16 @@ def update_session_language(request):
"""
Update the language session key.
"""
response = HttpResponse(200)
if request.method == 'PATCH':
data = json.loads(request.body)
language = data.get(LANGUAGE_KEY, settings.LANGUAGE_CODE)
if request.session.get(LANGUAGE_SESSION_KEY, None) != language:
request.session[LANGUAGE_SESSION_KEY] = unicode(language)
return HttpResponse(200)
response.set_cookie(
settings.LANGUAGE_COOKIE,
language,
domain=settings.SESSION_COOKIE_DOMAIN,
max_age=COOKIE_DURATION
)
return response
......@@ -174,7 +174,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
Test that a client (logged in) can get her own username.
"""
self.client.login(username=self.user.username, password=TEST_PASSWORD)
self._verify_get_own_username(15)
self._verify_get_own_username(14)
def test_get_username_inactive(self):
"""
......@@ -184,7 +184,7 @@ class TestOwnUsernameAPI(CacheIsolationTestCase, UserAPITestCase):
self.client.login(username=self.user.username, password=TEST_PASSWORD)
self.user.is_active = False
self.user.save()
self._verify_get_own_username(15)
self._verify_get_own_username(14)
def test_get_username_not_logged_in(self):
"""
......@@ -305,7 +305,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
"""
self.different_client.login(username=self.different_user.username, password=TEST_PASSWORD)
self.create_mock_profile(self.user)
with self.assertNumQueries(19):
with self.assertNumQueries(18):
response = self.send_get(self.different_client)
self._verify_full_shareable_account_response(response, account_privacy=ALL_USERS_VISIBILITY)
......@@ -320,7 +320,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
"""
self.different_client.login(username=self.different_user.username, password=TEST_PASSWORD)
self.create_mock_profile(self.user)
with self.assertNumQueries(19):
with self.assertNumQueries(18):
response = self.send_get(self.different_client)
self._verify_private_account_response(response, account_privacy=PRIVATE_VISIBILITY)
......@@ -395,12 +395,12 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
self.assertEqual(False, data["accomplishments_shared"])
self.client.login(username=self.user.username, password=TEST_PASSWORD)
verify_get_own_information(17)
verify_get_own_information(16)
# Now make sure that the user can get the same information, even if not active
self.user.is_active = False
self.user.save()
verify_get_own_information(11)
verify_get_own_information(10)
def test_get_account_empty_string(self):
"""
......@@ -414,7 +414,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase):
legacy_profile.save()
self.client.login(username=self.user.username, password=TEST_PASSWORD)
with self.assertNumQueries(17):
with self.assertNumQueries(16):
response = self.send_get(self.client)
for empty_field in ("level_of_education", "gender", "country", "bio"):
self.assertIsNone(response.data[empty_field])
......
......@@ -83,6 +83,7 @@ class PreferenceValidationError(PreferenceRequestError):
"""
def __init__(self, preference_errors):
self.preference_errors = preference_errors
super(PreferenceValidationError, self).__init__(preference_errors)
class PreferenceUpdateError(PreferenceRequestError):
......@@ -93,6 +94,7 @@ class PreferenceUpdateError(PreferenceRequestError):
def __init__(self, developer_message, user_message=None):
self.developer_message = developer_message
self.user_message = user_message
super(PreferenceUpdateError, self).__init__(developer_message)
class CountryCodeError(ValueError):
......
......@@ -6,6 +6,7 @@ from collections import defaultdict
from functools import wraps
import logging
import json
import traceback
from django import forms
from django.core.serializers.json import DjangoJSONEncoder
......@@ -65,16 +66,19 @@ def intercept_errors(api_error, ignore_errors=None):
LOGGER.warning(msg)
raise
caller = traceback.format_stack(limit=2)[0]
# Otherwise, log the error and raise the API-specific error
msg = (
u"An unexpected error occurred when calling '{func_name}' "
u"with arguments '{args}' and keyword arguments '{kwargs}': "
u"with arguments '{args}' and keyword arguments '{kwargs}' from {caller}: "
u"{exception}"
).format(
func_name=func.func_name,
args=args,
kwargs=kwargs,
exception=ex.developer_message if hasattr(ex, 'developer_message') else repr(ex)
exception=ex.developer_message if hasattr(ex, 'developer_message') else repr(ex),
caller=caller.strip(),
)
LOGGER.exception(msg)
raise api_error(msg)
......@@ -502,3 +506,13 @@ def shim_student_view(view_func, check_logged_in=False):
return response
return _inner
def serializer_is_dirty(preference_serializer):
"""
Return True if saving the supplied (Raw)UserPreferenceSerializer would change the database.
"""
return (
preference_serializer.instance is None or
preference_serializer.instance.value != preference_serializer.validated_data['value']
)
......@@ -20,7 +20,7 @@ from ..errors import (
UserAPIInternalError, UserAPIRequestError, UserNotFound, UserNotAuthorized,
PreferenceValidationError, PreferenceUpdateError, CountryCodeError
)
from ..helpers import intercept_errors
from ..helpers import intercept_errors, serializer_is_dirty
from ..models import UserOrgTag, UserPreference
from ..serializers import UserSerializer, RawUserPreferenceSerializer
......@@ -142,7 +142,9 @@ def update_user_preferences(requesting_user, update, user=None):
if preference_value is not None:
try:
serializer = serializers[preference_key]
serializer.save()
if serializer_is_dirty(serializer):
serializer.save()
except Exception as error:
raise _create_preference_update_error(preference_key, preference_value, error)
else:
......@@ -177,10 +179,12 @@ def set_user_preference(requesting_user, preference_key, preference_value, usern
existing_user = _get_authorized_user(requesting_user, username)
serializer = create_user_preference_serializer(existing_user, preference_key, preference_value)
validate_user_preference_serializer(serializer, preference_key, preference_value)
try:
serializer.save()
except Exception as error:
raise _create_preference_update_error(preference_key, preference_value, error)
if serializer_is_dirty(serializer):
try:
serializer.save()
except Exception as error:
raise _create_preference_update_error(preference_key, preference_value, error)
@intercept_errors(UserAPIInternalError, ignore_errors=[UserAPIRequestError])
......@@ -310,7 +314,13 @@ def _get_authorized_user(requesting_user, username=None, allow_staff=False):
If username is not provided, requesting_user.username is assumed.
"""
if username is None:
username = requesting_user.username
# If the user is one that has already been stored to the database, use that
if requesting_user.pk:
return requesting_user
else:
# Otherwise, treat this as a request against a separate user
username = requesting_user.username
try:
existing_user = User.objects.get(username=username)
except ObjectDoesNotExist:
......@@ -347,13 +357,13 @@ def create_user_preference_serializer(user, preference_key, preference_value):
except ObjectDoesNotExist:
existing_user_preference = None
new_data = {
"user": user.id,
"key": preference_key,
"value": preference_value,
}
if existing_user_preference:
serializer = RawUserPreferenceSerializer(existing_user_preference, data=new_data)
serializer = RawUserPreferenceSerializer(existing_user_preference, data=new_data, partial=True)
else:
new_data['user'] = user.id
serializer = RawUserPreferenceSerializer(data=new_data)
return serializer
......
......@@ -53,9 +53,8 @@ class TestPreferenceAPI(CacheIsolationTestCase):
super(TestPreferenceAPI, self).setUp()
self.user = UserFactory.create(password=self.password)
self.different_user = UserFactory.create(password=self.password)
self.staff_user = UserFactory(is_staff=True, password=self.password)
self.no_such_user = UserFactory.create(password=self.password)
self.no_such_user.username = "no_such_user"
self.staff_user = UserFactory.create(is_staff=True, password=self.password)
self.no_such_user = UserFactory.build(password=self.password, username="no_such_user")
self.test_preference_key = "test_key"
self.test_preference_value = "test_value"
set_user_preference(self.user, self.test_preference_key, self.test_preference_value)
......
......@@ -22,12 +22,7 @@ class UserSerializer(serializers.HyperlinkedModelSerializer):
"""
Return the name attribute from the user profile object if profile exists else none
"""
try:
profile = UserProfile.objects.get(user=user)
except UserProfile.DoesNotExist:
return None
return profile.name
return user.profile.name
def get_preferences(self, user):
"""
......
......@@ -2,6 +2,7 @@
Tests for helper functions.
"""
import json
import re
import mock
import ddt
from django import forms
......@@ -55,19 +56,31 @@ class InterceptErrorsTest(TestCase):
exception = 'openedx.core.djangoapps.user_api.tests.test_helpers.FakeInputException'
expected_log_msg = (
u"An unexpected error occurred when calling 'intercepted_function' with arguments '()' and "
u"keyword arguments '{'raise_error': <class '" + exception + u"'>}': FakeInputException()"
)
u"keyword arguments '{{'raise_error': <class '{}'>}}' "
u"from File \"{}\", line XXX, in test_logs_errors\n"
u" intercepted_function(raise_error=FakeInputException): FakeInputException()"
).format(exception, __file__)
# Verify that the raised exception has the error message
try:
intercepted_function(raise_error=FakeInputException)
except FakeOutputException as ex:
self.assertEqual(ex.message, expected_log_msg)
actual_message = re.sub(r'line \d+', 'line XXX', ex.message, flags=re.MULTILINE)
self.assertEqual(actual_message, expected_log_msg)
# Verify that the error logger is called
# This will include the stack trace for the original exception
# because it's called with log level "ERROR"
mock_logger.exception.assert_called_once_with(expected_log_msg)
calls = mock_logger.exception.mock_calls
self.assertEqual(len(calls), 1)
name, args, kwargs = calls[0]
self.assertEqual(name, '')
self.assertEqual(len(args), 1)
self.assertEqual(kwargs, {})
actual_message = re.sub(r'line \d+', 'line XXX', args[0], flags=re.MULTILINE)
self.assertEqual(actual_message, expected_log_msg)
class FormDescriptionTest(TestCase):
......
......@@ -1002,7 +1002,7 @@ class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (ApiKeyHeaderPermission,)
queryset = User.objects.all().prefetch_related("preferences")
queryset = User.objects.all().prefetch_related("preferences").select_related("profile")
serializer_class = UserSerializer
paginate_by = 10
paginate_by_param = "page_size"
......@@ -1028,7 +1028,7 @@ class ForumRoleUsersListView(generics.ListAPIView):
raise ParseError('course_id must be specified')
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id_string)
role = Role.objects.get_or_create(course_id=course_id, name=name)[0]
users = role.users.all()
users = role.users.prefetch_related("preferences").select_related("profile").all()
return users
......@@ -1057,7 +1057,7 @@ class PreferenceUsersListView(generics.ListAPIView):
paginate_by_param = "page_size"
def get_queryset(self):
return User.objects.filter(preferences__key=self.kwargs["pref_key"]).prefetch_related("preferences")
return User.objects.filter(preferences__key=self.kwargs["pref_key"]).prefetch_related("preferences").select_related("profile")
class UpdateEmailOptInPreference(APIView):
......
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