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.
This commit is contained in:
parent
dfac0cf853
commit
936237eceb
3 changed files with 29 additions and 10 deletions
|
@ -5,6 +5,9 @@ import decimal
|
||||||
import os.path
|
import os.path
|
||||||
import pathlib
|
import pathlib
|
||||||
|
|
||||||
|
import babel
|
||||||
|
import babel.numbers
|
||||||
|
|
||||||
from . import cache, loaders
|
from . import cache, loaders
|
||||||
|
|
||||||
HOME_PATH = pathlib.Path(os.path.expanduser('~'))
|
HOME_PATH = pathlib.Path(os.path.expanduser('~'))
|
||||||
|
@ -20,7 +23,8 @@ def currency_list(s):
|
||||||
class Configuration:
|
class Configuration:
|
||||||
DATE_SEPS = frozenset('.-/ ')
|
DATE_SEPS = frozenset('.-/ ')
|
||||||
DEFAULT_CONFIG_PATH = pathlib.Path(HOME_PATH, '.config', 'oxrlib.ini')
|
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'])
|
PREPOSITIONS = frozenset(['in', 'to', 'into'])
|
||||||
TODAY = datetime.date.today()
|
TODAY = datetime.date.today()
|
||||||
|
|
||||||
|
@ -106,7 +110,7 @@ class Configuration:
|
||||||
)
|
)
|
||||||
hist_parser.add_argument(
|
hist_parser.add_argument(
|
||||||
'--no-denomination',
|
'--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",
|
help="Turn off an earlier --denomination setting",
|
||||||
)
|
)
|
||||||
hist_parser.add_argument(
|
hist_parser.add_argument(
|
||||||
|
@ -134,7 +138,7 @@ class Configuration:
|
||||||
hist_parser.add_argument(
|
hist_parser.add_argument(
|
||||||
'word3', nargs='?', metavar='second code',
|
'word3', nargs='?', metavar='second code',
|
||||||
help="Convert or show rates to this currency, in three-letter code format. "
|
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)
|
hist_parser.add_argument('word4', nargs='?', help=argparse.SUPPRESS)
|
||||||
|
|
||||||
|
@ -170,20 +174,27 @@ class Configuration:
|
||||||
errmsg.append(repr(s_value))
|
errmsg.append(repr(s_value))
|
||||||
self.error(': '.join(errmsg))
|
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):
|
def _post_hook_historical(self):
|
||||||
year = self.args.date.year
|
year = self.args.date.year
|
||||||
if year < 100:
|
if year < 100:
|
||||||
# Don't let the user specify ambiguous dates.
|
# Don't let the user specify ambiguous dates.
|
||||||
self.error("historical data not available from year {}".format(year))
|
self.error("historical data not available from year {}".format(year))
|
||||||
self._read_from_conffile('base', 'Historical', 'USD', currency_code)
|
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
|
self.args.denomination = None
|
||||||
else:
|
else:
|
||||||
self._read_from_conffile('denomination', 'Historical', None, currency_code)
|
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)
|
currency_list, convert_fallback=True)
|
||||||
self._read_from_conffile('ledger', 'Historical', False, getter='getboolean')
|
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')
|
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)
|
words = iter(word for word in raw_words if word is not None)
|
||||||
try:
|
try:
|
||||||
|
@ -202,7 +213,7 @@ class Configuration:
|
||||||
next_word = next(words, next_word)
|
next_word = next(words, next_word)
|
||||||
self.args.to_currency = self._convert_or_error(currency_code, next_word)
|
self.args.to_currency = self._convert_or_error(currency_code, next_word)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
pass
|
self.args.to_currency = pref_currency
|
||||||
|
|
||||||
def _build_cache_loader(self):
|
def _build_cache_loader(self):
|
||||||
kwargs = dict(self.conffile.items('Cache'))
|
kwargs = dict(self.conffile.items('Cache'))
|
||||||
|
|
|
@ -35,5 +35,9 @@ ledger = no
|
||||||
denomination = USD
|
denomination = USD
|
||||||
|
|
||||||
# Use signs for these currencies in Ledger output.
|
# 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
|
signed_currencies = USD, EUR
|
||||||
|
|
|
@ -2,6 +2,7 @@ import datetime
|
||||||
import decimal
|
import decimal
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import babel
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from . import any_date, relpath
|
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'),
|
(decimal.Decimal('12.34'), 'gbp', 'IN', 'eur'),
|
||||||
])
|
])
|
||||||
def test_historical_argparsing_success(amount, from_curr, preposition, to_curr, any_date):
|
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 = ['historical', any_date.isoformat()]
|
||||||
arglist.extend(str(s) for s in [amount, from_curr, preposition, to_curr]
|
arglist.extend(str(s) for s in [amount, from_curr, preposition, to_curr]
|
||||||
if s is not None)
|
if s is not None)
|
||||||
config = config_from(os.devnull, arglist)
|
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.amount == amount
|
||||||
assert config.args.from_currency == from_curr.upper()
|
assert config.args.from_currency == from_curr.upper()
|
||||||
if to_curr is not None:
|
assert config.args.to_currency == expect_to_curr
|
||||||
assert config.args.to_currency == to_curr.upper()
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('arglist', [
|
@pytest.mark.parametrize('arglist', [
|
||||||
['100'],
|
['100'],
|
||||||
|
|
Loading…
Reference in a new issue