Allow changing of language preference from profile page

This commit is contained in:
Renzo Lucioni
2014-09-26 17:31:46 -04:00
parent c5530b758d
commit 254a0ce6c8
15 changed files with 426 additions and 83 deletions

View File

@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
""" Python API for language and translation management. """
from collections import namedtuple
from django.conf import settings
from django.utils.translation import get_language
from dark_lang.models import DarkLangConfig
# Named tuples can be referenced using object-like variable
# deferencing, making the use of tuples more readable by
# eliminating the need to see the context of the tuple packing.
Language = namedtuple('Language', 'code name')
def released_languages():
"""Retrieve the list of released languages.
Constructs a list of Language tuples by intersecting the
list of valid language tuples with the list of released
language codes.
Returns:
list of Language: Languages in which full translations are available.
Example:
>>> print released_languages()
[Language(code='en', name=u'English'), Language(code='fr', name=u'Français')]
"""
released_language_codes = DarkLangConfig.current().released_languages_list
default_language_code = settings.LANGUAGE_CODE
if default_language_code not in released_language_codes:
released_language_codes.append(default_language_code)
released_language_codes.sort()
# Intersect the list of valid language tuples with the list
# of release language codes
released_languages = [
Language(tuple[0], tuple[1])
for tuple in settings.LANGUAGES
if tuple[0] in released_language_codes
]
return released_languages
def preferred_language(preferred_language_code):
"""Retrieve the name of the user's preferred language.
Note:
The preferred_language_code may be None. If this is the case,
the if/else block will handle it by returning either the active
language or the default language.
Args:
preferred_language_code (str): The ISO 639 code corresponding
to the user's preferred language.
Returns:
unicode: The name of the user's preferred language.
"""
active_language_code = get_language()
if preferred_language_code in settings.LANGUAGE_DICT:
# If the user has indicated a preference for a valid
# language, record their preferred language
preferred_language = settings.LANGUAGE_DICT[preferred_language_code]
elif active_language_code in settings.LANGUAGE_DICT:
# Otherwise, set the language used in the current thread
# as the preferred language
preferred_language = settings.LANGUAGE_DICT[active_language_code]
else:
# Otherwise, use the default language
preferred_language = settings.LANGUAGE_DICT[settings.LANGUAGE_CODE]
return preferred_language

View File

@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
""" Tests for the language API. """
from django.test import TestCase
import ddt
from lang_pref import api as language_api
@ddt.ddt
class LanguageApiTest(TestCase):
INVALID_LANGUAGE_CODES = ['', 'foo']
def test_released_languages(self):
released_languages = language_api.released_languages()
self.assertGreaterEqual(len(released_languages), 1)
def test_preferred_language(self):
preferred_language = language_api.preferred_language('fr')
self.assertEqual(preferred_language, u'Français')
@ddt.data(*INVALID_LANGUAGE_CODES)
def test_invalid_preferred_language(self, language_code):
preferred_language = language_api.preferred_language(language_code)
self.assertEqual(preferred_language, u'English')
def test_no_preferred_language(self):
preferred_language = language_api.preferred_language(None)
self.assertEqual(preferred_language, u'English')

View File

@@ -414,4 +414,3 @@ def _validate_email(email):
raise AccountEmailInvalid(
u"Email '{email}' format is not valid".format(email=email)
)

View File

@@ -5,7 +5,8 @@ but does NOT include basic account information such as username, password, and
email address.
"""
from user_api.models import UserProfile
from user_api.models import User, UserProfile, UserPreference
from user_api.helpers import intercept_errors
@@ -43,13 +44,13 @@ FULL_NAME_MAX_LENGTH = 255
@intercept_errors(ProfileInternalError, ignore_errors=[ProfileRequestError])
def profile_info(username):
"""Retrieve a user's profile information
"""Retrieve a user's profile information.
Searches either by username or email.
At least one of the keyword args must be provided.
Arguments:
Args:
username (unicode): The username of the account to retrieve.
Returns:
@@ -78,7 +79,7 @@ def update_profile(username, full_name=None):
Args:
username (unicode): The username associated with the account.
Keyword Arguments:
Keyword Args:
full_name (unicode): If provided, set the user's full name to this value.
Returns:
@@ -102,31 +103,48 @@ def update_profile(username, full_name=None):
@intercept_errors(ProfileInternalError, ignore_errors=[ProfileRequestError])
def preference_info(username, preference_name):
def preference_info(username):
"""Retrieve information about a user's preferences.
Arguments:
Args:
username (unicode): The username of the account to retrieve.
preference_name (unicode): The name of the preference to retrieve.
Returns:
The JSON-deserialized value.
dict: Empty if there is no user
"""
pass
preferences = UserPreference.objects.filter(user__username=username)
preferences_dict = {}
for preference in preferences:
preferences_dict[preference.key] = preference.value
return preferences_dict
@intercept_errors(ProfileInternalError, ignore_errors=[ProfileRequestError])
def update_preference(username, preference_name, preference_value):
"""Update a user's preference.
def update_preferences(username, **kwargs):
"""Update a user's preferences.
Arguments:
Sets the provided preferences for the given user.
Args:
username (unicode): The username of the account to retrieve.
preference_name (unicode): The name of the preference to set.
preference_value (JSON-serializable): The new value for the preference.
Keyword Args:
**kwargs (unicode): Arbitrary key-value preference pairs
Returns:
None
Raises:
ProfileUserNotFound
"""
pass
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise ProfileUserNotFound
else:
for key, value in kwargs.iteritems():
UserPreference.set_preference(user, key, value)

View File

@@ -5,6 +5,7 @@ from django.test import TestCase
import ddt
from nose.tools import raises
from dateutil.parser import parse as parse_datetime
from user_api.api import account as account_api
from user_api.api import profile as profile_api
from user_api.models import UserProfile
@@ -13,9 +14,9 @@ from user_api.models import UserProfile
@ddt.ddt
class ProfileApiTest(TestCase):
USERNAME = u"frank-underwood"
PASSWORD = u"ṕáśśẃőŕd"
EMAIL = u"frank+underwood@example.com"
USERNAME = u'frank-underwood'
PASSWORD = u'ṕáśśẃőŕd'
EMAIL = u'frank+underwood@example.com'
def test_create_profile(self):
# Create a new account, which should have an empty profile by default.
@@ -31,9 +32,9 @@ class ProfileApiTest(TestCase):
def test_update_full_name(self):
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
profile_api.update_profile(self.USERNAME, full_name=u"ȻħȺɍłɇs")
profile = profile_api.profile_info(username=self.USERNAME)
self.assertEqual(profile['full_name'], u"ȻħȺɍłɇs")
profile_api.update_profile(self.USERNAME, full_name=u'ȻħȺɍłɇs')
profile = profile_api.profile_info(self.USERNAME)
self.assertEqual(profile['full_name'], u'ȻħȺɍłɇs')
@raises(profile_api.ProfileInvalidField)
@ddt.data('', 'a' * profile_api.FULL_NAME_MAX_LENGTH + 'a')
@@ -43,10 +44,10 @@ class ProfileApiTest(TestCase):
@raises(profile_api.ProfileUserNotFound)
def test_update_profile_no_user(self):
profile_api.update_profile(self.USERNAME, full_name="test")
profile_api.update_profile(self.USERNAME, full_name='test')
def test_retrieve_profile_no_user(self):
profile = profile_api.profile_info("does not exist")
profile = profile_api.profile_info('does not exist')
self.assertIs(profile, None)
def test_record_name_change_history(self):
@@ -55,30 +56,53 @@ class ProfileApiTest(TestCase):
# Change the name once
# Since the original name was an empty string, expect that the list
# of old names is empty
profile_api.update_profile(self.USERNAME, full_name="new name")
profile_api.update_profile(self.USERNAME, full_name='new name')
meta = UserProfile.objects.get(user__username=self.USERNAME).get_meta()
self.assertEqual(meta, {})
# Change the name again and expect the new name is stored in the history
profile_api.update_profile(self.USERNAME, full_name="another new name")
profile_api.update_profile(self.USERNAME, full_name='another new name')
meta = UserProfile.objects.get(user__username=self.USERNAME).get_meta()
self.assertEqual(len(meta['old_names']), 1)
name, rationale, timestamp = meta['old_names'][0]
self.assertEqual(name, "new name")
self.assertEqual(rationale, u"")
self.assertEqual(name, 'new name')
self.assertEqual(rationale, u'')
self._assert_is_datetime(timestamp)
# Change the name a third time and expect both names are stored in the history
profile_api.update_profile(self.USERNAME, full_name="yet another new name")
profile_api.update_profile(self.USERNAME, full_name='yet another new name')
meta = UserProfile.objects.get(user__username=self.USERNAME).get_meta()
self.assertEqual(len(meta['old_names']), 2)
name, rationale, timestamp = meta['old_names'][1]
self.assertEqual(name, "another new name")
self.assertEqual(rationale, u"")
self.assertEqual(name, 'another new name')
self.assertEqual(rationale, u'')
self._assert_is_datetime(timestamp)
def test_update_and_retrieve_preference_info(self):
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
profile_api.update_preferences(self.USERNAME, preference_key='preference_value')
preferences = profile_api.preference_info(self.USERNAME)
self.assertEqual(preferences['preference_key'], 'preference_value')
@raises(profile_api.ProfileUserNotFound)
def test_retrieve_and_update_preference_info_no_user(self):
preferences = profile_api.preference_info(self.USERNAME)
self.assertEqual(preferences, {})
profile_api.update_preferences(self.USERNAME, preference_key='preference_value')
def test_update_and_retrieve_preference_info_unicode(self):
account_api.create_account(self.USERNAME, self.PASSWORD, self.EMAIL)
profile_api.update_preferences(self.USERNAME, **{u'ⓟⓡⓔⓕⓔⓡⓔⓝⓒⓔ_ⓚⓔⓨ': u'ǝnןɐʌ_ǝɔuǝɹǝɟǝɹd'})
preferences = profile_api.preference_info(self.USERNAME)
self.assertEqual(preferences[u'ⓟⓡⓔⓕⓔⓡⓔⓝⓒⓔ_ⓚⓔⓨ'], u'ǝnןɐʌ_ǝɔuǝɹǝɟǝɹd')
def _assert_is_datetime(self, timestamp):
if not timestamp:
return False