from babel.numbers import format_currency
import decimal
import itertools
import operator
import babel.numbers
from .. import rate as oxrrate
CURRENCY_FMT = '#,##0.### ¤¤'
RATE_FMT = '{from_amt:g} {from_curr} = {to_amt:g} {to_curr}'
class Formatter:
def __init__(self, rate, signed_currencies=(), base_fmt='#,##0.###'):
self.rate = rate
self.base_fmt = base_fmt
self.base_fmt_noprec = base_fmt.rsplit('.', 1)[0]
self.signed_currencies = set(code for code in signed_currencies
if self.can_sign_currency(code))
def can_sign_currency(self, code):
return len(babel.numbers.get_currency_symbol(code)) == 1
def format_currency(self, amount, code, currency_digits=True):
if currency_digits:
fmt = self.base_fmt
else:
fmt = '{}.{}'.format(self.base_fmt_noprec, '#' * -amount.as_tuple().exponent)
if code in self.signed_currencies:
fmt = '¤' + fmt
fmt = fmt + ' ¤¤'
return babel.numbers.format_currency(amount, code, fmt, currency_digits=currency_digits)
def format_rate(self, rate):
return "{:g}".format(rate)
def format_rate_pair(self, from_curr, to_curr):
from_amt = 1
to_amt = self.rate.convert(from_amt, from_curr, to_curr)
return "{} {} = {} {}".format(
self.format_rate(from_amt), from_curr,
self.format_rate(to_amt), to_curr,
)
def format_rate_pair_bidir(self, from_curr, to_curr, sep='\n'):
return "{}{}{}".format(
self.format_rate_pair(from_curr, to_curr),
sep,
self.format_rate_pair(to_curr, from_curr),
def format_conversion(self, from_amt, from_curr, to_curr):
return "{} = {}".format(
self.format_currency(from_amt, from_curr),
self.format_currency(to_amt, to_curr),
def format_conversion(rate, from_amt, from_curr, to_curr):
to_amt = rate.convert(from_amt, from_curr, to_curr)
format_currency(from_amt, from_curr, CURRENCY_FMT),
format_currency(to_amt, to_curr, CURRENCY_FMT),
class LedgerFormatter(Formatter):
RATE_PREC = 5
def normalize_rate(self, rate, prec=None):
# Return prec nonzero digits of precision, if available.
if prec is None:
prec = self.RATE_PREC
_, digits, exponent = rate.normalize().as_tuple()
prec -= min(0, exponent + len(digits))
quant_to = '1.{}'.format('0' * prec)
try:
qrate = rate.quantize(decimal.Decimal(quant_to))
except decimal.InvalidOperation:
# The original rate doesn't have that much precision, so use it raw.
qrate = rate
return qrate.normalize()
def format_rate(self, rate, prec=None):
return str(self.normalize_rate(rate, prec))
def format_ledger_rate(self, rate, curr, prec=None):
nrate = self.normalize_rate(rate, prec)
rate_s = self.format_currency(nrate, curr, currency_digits=False)
return "{{={0}}} @ {0}".format(rate_s)
return "{} {} {}".format(
from_amt, from_curr, self.format_ledger_rate(to_amt, to_curr))
to_rate = self.rate.convert(1, from_curr, to_curr)
return "{} {}\n{}".format(
self.format_ledger_rate(to_rate, to_curr),
def format_rate_pair(rate, from_curr, to_curr):
amt = rate.convert(1, from_curr, to_curr)
yield RATE_FMT.format(from_amt=1, from_curr=from_curr, to_amt=amt, to_curr=to_curr)
amt = rate.convert(1, to_curr, from_curr)
yield RATE_FMT.format(from_amt=1, from_curr=to_curr, to_amt=amt, to_curr=from_curr)
def run(config, stdout, stderr):
loaders = config.get_loaders()
@@ -24,14 +101,19 @@ def run(config, stdout, stderr):
rate = oxrrate.Rate.from_json_file(rate_json)
if loaders.should_cache():
config.cache.save_rate(rate)
if config.args.ledger:
formatter = LedgerFormatter(rate, config.args.signed_currencies)
formatter = Formatter(rate)
if not config.args.from_currency:
for from_curr in sorted(rate.rates):
print(*format_rate_pair(rate, from_curr, config.args.to_currency),
sep='\n', file=stdout)
print(formatter.format_rate_pair_bidir(from_curr, config.args.to_currency),
file=stdout)
elif config.args.amount is None:
print(*format_rate_pair(rate, config.args.from_currency, config.args.to_currency),
print(formatter.format_rate_pair_bidir(config.args.from_currency, config.args.to_currency),
print(format_conversion(rate, config.args.amount,
config.args.from_currency, config.args.to_currency),
print(formatter.format_conversion(config.args.amount,
config.args.from_currency,
config.args.to_currency),