From 936237eceb3296b69c532751da57b4b4c72106ae Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 9 Jun 2017 13:22:12 -0400 Subject: [PATCH] config: Nicer determination of default currency for converting/signing. base defaults to USD more as an API restriction than anything else, so avoid using it as a default setting. Instead, use the user's books denomination or locale setting. --- oxrlib/config.py | 25 ++++++++++++++++++------- oxrlib_example.ini | 6 +++++- tests/test_Configuration.py | 8 ++++++-- 3 files changed, 29 insertions(+), 10 deletions(-) diff --git a/oxrlib/config.py b/oxrlib/config.py index cdb7574..4c4877d 100644 --- a/oxrlib/config.py +++ b/oxrlib/config.py @@ -5,6 +5,9 @@ import decimal import os.path import pathlib +import babel +import babel.numbers + from . import cache, loaders HOME_PATH = pathlib.Path(os.path.expanduser('~')) @@ -20,7 +23,8 @@ def currency_list(s): class Configuration: DATE_SEPS = frozenset('.-/ ') DEFAULT_CONFIG_PATH = pathlib.Path(HOME_PATH, '.config', 'oxrlib.ini') - NO_DENOMINATION = object() + LOCALE = babel.core.Locale.default() + SENTINEL = object() PREPOSITIONS = frozenset(['in', 'to', 'into']) TODAY = datetime.date.today() @@ -106,7 +110,7 @@ class Configuration: ) hist_parser.add_argument( '--no-denomination', - dest='denomination', action='store_const', const=self.NO_DENOMINATION, + dest='denomination', action='store_const', const=self.SENTINEL, help="Turn off an earlier --denomination setting", ) hist_parser.add_argument( @@ -134,7 +138,7 @@ class Configuration: hist_parser.add_argument( 'word3', nargs='?', metavar='second code', help="Convert or show rates to this currency, in three-letter code format. " - "If not specified, defaults to the base currency.", + "If not specified, defaults to the user's preferred currency.", ) hist_parser.add_argument('word4', nargs='?', help=argparse.SUPPRESS) @@ -170,20 +174,27 @@ class Configuration: errmsg.append(repr(s_value)) self.error(': '.join(errmsg)) + def _user_currency(self, default=SENTINEL): + try: + return babel.numbers.get_territory_currencies( + self.LOCALE.territory, start_date=self.TODAY)[0] + except IndexError: + return default + def _post_hook_historical(self): year = self.args.date.year if year < 100: # Don't let the user specify ambiguous dates. self.error("historical data not available from year {}".format(year)) self._read_from_conffile('base', 'Historical', 'USD', currency_code) - if self.args.denomination is self.NO_DENOMINATION: + if self.args.denomination is self.SENTINEL: self.args.denomination = None else: self._read_from_conffile('denomination', 'Historical', None, currency_code) - self._read_from_conffile('signed_currencies', 'Historical', self.args.base, + pref_currency = self.args.denomination or self._user_currency(self.args.base) + self._read_from_conffile('signed_currencies', 'Historical', pref_currency, currency_list, convert_fallback=True) self._read_from_conffile('ledger', 'Historical', False, getter='getboolean') - self.args.to_currency = self.args.base raw_words = iter(getattr(self.args, 'word' + c) for c in '1234') words = iter(word for word in raw_words if word is not None) try: @@ -202,7 +213,7 @@ class Configuration: next_word = next(words, next_word) self.args.to_currency = self._convert_or_error(currency_code, next_word) except StopIteration: - pass + self.args.to_currency = pref_currency def _build_cache_loader(self): kwargs = dict(self.conffile.items('Cache')) diff --git a/oxrlib_example.ini b/oxrlib_example.ini index 57fd7cf..e0c4496 100644 --- a/oxrlib_example.ini +++ b/oxrlib_example.ini @@ -35,5 +35,9 @@ ledger = no denomination = USD # Use signs for these currencies in Ledger output. -# If not specified, defaults to the base currency. +# If not specified, defaults to the user's preferred currency, which is +# the first setting found from: +# 1. the denomination setting above +# 2. the user's locale +# 3. the base currency (which defaults to USD) signed_currencies = USD, EUR diff --git a/tests/test_Configuration.py b/tests/test_Configuration.py index 4b34e91..96f9d05 100644 --- a/tests/test_Configuration.py +++ b/tests/test_Configuration.py @@ -2,6 +2,7 @@ import datetime import decimal import os +import babel import pytest from . import any_date, relpath @@ -56,14 +57,17 @@ def test_historical_default_base(ini_filename, expected_currency, use_switch, an (decimal.Decimal('12.34'), 'gbp', 'IN', 'eur'), ]) def test_historical_argparsing_success(amount, from_curr, preposition, to_curr, any_date): + oxrlib.config.Configuration.TODAY = datetime.date(2017, 1, 1) + # This locale's currency should not be used in any test cases above. + oxrlib.config.Configuration.LOCALE = babel.core.Locale('en', 'IN') arglist = ['historical', any_date.isoformat()] arglist.extend(str(s) for s in [amount, from_curr, preposition, to_curr] if s is not None) config = config_from(os.devnull, arglist) + expect_to_curr = 'INR' if to_curr is None else to_curr.upper() assert config.args.amount == amount assert config.args.from_currency == from_curr.upper() - if to_curr is not None: - assert config.args.to_currency == to_curr.upper() + assert config.args.to_currency == expect_to_curr @pytest.mark.parametrize('arglist', [ ['100'],