From 2fc0aaf625151081feaf382c289d68620a720b61 Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Tue, 08 Oct 2013 05:58:01 +0000 Subject: Specify secondary languages on client side; use comma as a separtor --- diff --git a/sugar_network/client/__init__.py b/sugar_network/client/__init__.py index d9e68fd..6a620f9 100644 --- a/sugar_network/client/__init__.py +++ b/sugar_network/client/__init__.py @@ -95,7 +95,7 @@ hub_root = Option( default='/usr/share/sugar-network/hub') layers = Option( - 'space separated list of layers to restrict Sugar Network content by', + 'comma separated list of layers to restrict Sugar Network content by', default=[], type_cast=Option.list_cast, type_repr=Option.list_repr, name='layers') @@ -116,7 +116,9 @@ anonymous = Option( name='anonymous') accept_language = Option( - 'specify HTTP Accept-Language header field value manually', + 'comma separated list to specify HTTP Accept-Language ' + 'header field value manually', + default=[], type_cast=Option.list_cast, type_repr=Option.list_repr, name='accept-language', short_option='-l') cache_limit = Option( diff --git a/sugar_network/node/__init__.py b/sugar_network/node/__init__.py index 6b1588f..68f3e2a 100644 --- a/sugar_network/node/__init__.py +++ b/sugar_network/node/__init__.py @@ -61,7 +61,7 @@ pull_timeout = Option( default=30, type_cast=int) sync_layers = Option( - 'space separated list of layers to restrict Sugar Network ' + 'comma separated list of layers to restrict Sugar Network ' 'synchronization content', default=['pilot'], type_cast=Option.list_cast, type_repr=Option.list_repr, name='sync-layers') diff --git a/sugar_network/node/stats_node.py b/sugar_network/node/stats_node.py index a2a2827..6c24278 100644 --- a/sugar_network/node/stats_node.py +++ b/sugar_network/node/stats_node.py @@ -30,7 +30,7 @@ stats_node_step = Option( default=60 * 5, type_cast=int) stats_node_rras = Option( - 'space separated list of RRAs for node RRD databases', + 'comma separated list of RRAs for node RRD databases', default=[ 'RRA:AVERAGE:0.5:1:288', # one day with 5min step 'RRA:AVERAGE:0.5:3:672', # one week with 15min step diff --git a/sugar_network/node/stats_user.py b/sugar_network/node/stats_user.py index 9265502..0b96449 100644 --- a/sugar_network/node/stats_user.py +++ b/sugar_network/node/stats_user.py @@ -31,7 +31,7 @@ stats_user_step = Option( default=60, type_cast=int) stats_user_rras = Option( - 'space separated list of RRAs for users\' RRD databases', + 'comma separated list of RRAs for users\' RRD databases', default=[ 'RRA:AVERAGE:0.5:1:4320', # one day with 60s step 'RRA:AVERAGE:0.5:5:2016', # one week with 5min step diff --git a/sugar_network/toolkit/__init__.py b/sugar_network/toolkit/__init__.py index f7e59ea..c805b06 100644 --- a/sugar_network/toolkit/__init__.py +++ b/sugar_network/toolkit/__init__.py @@ -137,6 +137,25 @@ def default_lang(): return _default_lang +def default_langs(): + """Default languages list, i.e., including all secondory languages. + + :returns: + list of strings in format of HTTP's Accept-Language, e.g., `ru,en-gb`. + + """ + global _default_langs + + if _default_langs is None: + langs = os.environ.get('LANGUAGE') + if langs: + _default_langs = langs.split(':') + else: + _default_langs = [default_lang()] + + return _default_langs + + def gettext(value, accept_language=None): if not value: return '' @@ -862,3 +881,4 @@ def _nb_read(stream): _default_lang = None +_default_langs = None diff --git a/sugar_network/toolkit/http.py b/sugar_network/toolkit/http.py index cadf64c..31f9e5a 100644 --- a/sugar_network/toolkit/http.py +++ b/sugar_network/toolkit/http.py @@ -271,8 +271,8 @@ class Connection(object): headers['content-length'] = str(len(request.content)) for env_key, key, value in ( ('HTTP_IF_MODIFIED_SINCE', 'if-modified-since', None), - ('HTTP_ACCEPT_LANGUAGE', 'accept-language', - client.accept_language.value), + ('HTTP_ACCEPT_LANGUAGE', 'accept-language', ','.join( + client.accept_language.value or toolkit.default_langs())), ('HTTP_ACCEPT_ENCODING', 'accept-encoding', None), ): if value is None: diff --git a/sugar_network/toolkit/options.py b/sugar_network/toolkit/options.py index da945af..5f10525 100644 --- a/sugar_network/toolkit/options.py +++ b/sugar_network/toolkit/options.py @@ -16,10 +16,15 @@ """Command-line options parsing utilities.""" import os +import re import sys from os.path import exists, expanduser, isdir, join +# TODO Replace by ',' +_LIST_SEPARATOR = re.compile(r'[\s,]+') + + class Option(object): """Configuration option. @@ -256,7 +261,7 @@ class Option(object): @staticmethod def list_cast(x): if isinstance(x, basestring): - return [i for i in x.strip().split() if i] + return [i for i in _LIST_SEPARATOR.split(x.strip()) if i] else: return x @@ -289,8 +294,6 @@ class Option(object): @staticmethod def _bind(parser, config_files, notice): - import re - if config_files: Option.config = Option() Option.config.name = 'config' @@ -298,10 +301,8 @@ class Option(object): Option.config.description = \ 'colon separated list of paths to configuration file(s)' Option.config.short_option = '-c' - Option.config.type_cast = \ - lambda x: [i for i in re.split('[\\s:;,]+', x) if i] - Option.config.type_repr = \ - lambda x: ':'.join(x) + Option.config.type_cast = Option.paths_cast + Option.config.type_repr = Option.paths_repr Option.config.value = ':'.join(config_files) for prop in [Option.config] + Option.items.values(): diff --git a/sugar_network/toolkit/spec.py b/sugar_network/toolkit/spec.py index 9dec4b8..6de2b36 100644 --- a/sugar_network/toolkit/spec.py +++ b/sugar_network/toolkit/spec.py @@ -43,7 +43,7 @@ _FIELDS = { _ARCHES = ['all', 'any'] _STABILITIES = ('insecure', 'buggy', 'developer', 'testing', 'stable') _POLICY_URL = 'http://wiki.sugarlabs.org/go/Sugar_Network/Policy' -_LIST_SEPARATOR = ';' +_LIST_SEPARATOR = ',' _RESTRICTION_RE = re.compile('(<|<=|=|>|>=)\\s*([0-9.]+)') @@ -537,7 +537,8 @@ def _parse_list(str_list): i = 0 while i < len(str_list): - if not max(brackets.values()) and str_list[i] == _LIST_SEPARATOR: + if not max(brackets.values()) and \ + str_list[i] in (_LIST_SEPARATOR, ';'): parts.append(str_list[:i]) str_list = str_list[i + 1:] i = 0 diff --git a/tests/units/client/routes.py b/tests/units/client/routes.py index 7514c96..6b0d6e5 100755 --- a/tests/units/client/routes.py +++ b/tests/units/client/routes.py @@ -10,7 +10,7 @@ from os.path import exists from __init__ import tests -from sugar_network import db, client, model +from sugar_network import db, client, model, toolkit from sugar_network.client import journal, IPCConnection, cache_limit, cache_lifetime from sugar_network.client.routes import ClientRoutes, CachedClientRoutes from sugar_network.model.user import User @@ -396,6 +396,96 @@ class RoutesTest(tests.Test): assert home_volume['implementation'].exists(guid) assert exists('client/implementation/%s/%s' % (guid[:2], guid)) + def test_LanguagesFallbackInRequests(self): + self.start_online_client() + ipc = IPCConnection() + + guid1 = self.node_volume['context'].create({ + 'type': 'activity', + 'title': {'en': '1', 'ru': '2', 'es': '3'}, + 'summary': '', + 'description': '', + }) + guid2 = self.node_volume['context'].create({ + 'type': 'activity', + 'title': {'en': '1', 'ru': '2'}, + 'summary': '', + 'description': '', + }) + guid3 = self.node_volume['context'].create({ + 'type': 'activity', + 'title': {'en': '1'}, + 'summary': '', + 'description': '', + }) + + client.accept_language.value = ['es', 'ru', 'en'] + self.assertEqual('3', ipc.get(['context', guid1, 'title'])) + self.assertEqual('2', ipc.get(['context', guid2, 'title'])) + self.assertEqual('1', ipc.get(['context', guid3, 'title'])) + + client.accept_language.value = ['ru', 'en'] + self.assertEqual('2', ipc.get(['context', guid1, 'title'])) + self.assertEqual('2', ipc.get(['context', guid2, 'title'])) + self.assertEqual('1', ipc.get(['context', guid3, 'title'])) + + client.accept_language.value = ['en'] + self.assertEqual('1', ipc.get(['context', guid1, 'title'])) + self.assertEqual('1', ipc.get(['context', guid2, 'title'])) + self.assertEqual('1', ipc.get(['context', guid3, 'title'])) + + client.accept_language.value = ['foo'] + self.assertEqual('1', ipc.get(['context', guid1, 'title'])) + self.assertEqual('1', ipc.get(['context', guid2, 'title'])) + self.assertEqual('1', ipc.get(['context', guid3, 'title'])) + + def test_DefaultLanguagesFallbackInRequests(self): + self.start_online_client() + ipc = IPCConnection() + + guid1 = self.node_volume['context'].create({ + 'type': 'activity', + 'title': {'en': '1', 'ru': '2', 'es': '3'}, + 'summary': '', + 'description': '', + }) + guid2 = self.node_volume['context'].create({ + 'type': 'activity', + 'title': {'en': '1', 'ru': '2'}, + 'summary': '', + 'description': '', + }) + guid3 = self.node_volume['context'].create({ + 'type': 'activity', + 'title': {'en': '1'}, + 'summary': '', + 'description': '', + }) + + toolkit._default_langs = None + os.environ['LANGUAGE'] = 'es:ru:en' + self.assertEqual('3', ipc.get(['context', guid1, 'title'])) + self.assertEqual('2', ipc.get(['context', guid2, 'title'])) + self.assertEqual('1', ipc.get(['context', guid3, 'title'])) + + toolkit._default_langs = None + os.environ['LANGUAGE'] = 'ru:en' + self.assertEqual('2', ipc.get(['context', guid1, 'title'])) + self.assertEqual('2', ipc.get(['context', guid2, 'title'])) + self.assertEqual('1', ipc.get(['context', guid3, 'title'])) + + toolkit._default_langs = None + os.environ['LANGUAGE'] = 'en' + self.assertEqual('1', ipc.get(['context', guid1, 'title'])) + self.assertEqual('1', ipc.get(['context', guid2, 'title'])) + self.assertEqual('1', ipc.get(['context', guid3, 'title'])) + + toolkit._default_langs = None + os.environ['LANGUAGE'] = 'foo' + self.assertEqual('1', ipc.get(['context', guid1, 'title'])) + self.assertEqual('1', ipc.get(['context', guid2, 'title'])) + self.assertEqual('1', ipc.get(['context', guid3, 'title'])) + def call(routes, request): router = Router(routes) -- cgit v0.9.1