Files
@ 992c91fc90de
Branch filter:
Location: NPO-Accounting/oxrlib/oxrlib/commands/historical.py - annotation
992c91fc90de
4.4 KiB
text/x-python
config: Nicer implementation of date parsing.
Keeps less local state through the function, to reduce the risk of that
state getting inconsistent.
Keeps less local state through the function, to reduce the risk of that
state getting inconsistent.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 ec3b9e83f865 f9c045a63efc 667c214e9191 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 ec3b9e83f865 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 667c214e9191 667c214e9191 667c214e9191 667c214e9191 667c214e9191 f9c045a63efc 3b5a563ef64b 3b5a563ef64b 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 667c214e9191 667c214e9191 9be9b07a8d54 9be9b07a8d54 667c214e9191 9be9b07a8d54 9be9b07a8d54 667c214e9191 9be9b07a8d54 9be9b07a8d54 9be9b07a8d54 667c214e9191 | import decimal
import itertools
import operator
import babel.numbers
from .. import rate as oxrrate
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
else:
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):
to_amt = self.rate.convert(from_amt, from_curr, to_curr)
return "{} = {}".format(
self.format_currency(from_amt, from_curr),
self.format_currency(to_amt, to_curr),
)
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)
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(
from_amt, from_curr, self.format_ledger_rate(to_amt, to_curr))
def format_conversion(self, from_amt, from_curr, to_curr):
to_rate = self.rate.convert(1, from_curr, to_curr)
to_amt = self.rate.convert(from_amt, from_curr, to_curr)
return "{} {}\n{}".format(
self.format_currency(from_amt, from_curr),
self.format_ledger_rate(to_rate, to_curr),
self.format_currency(to_amt, to_curr),
)
def run(config, stdout, stderr):
loaders = config.get_loaders()
with loaders.historical(config.args.date, config.args.base) as rate_json:
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)
else:
formatter = Formatter(rate)
if not config.args.from_currency:
for from_curr in sorted(rate.rates):
print(formatter.format_rate_pair_bidir(from_curr, config.args.to_currency),
file=stdout)
elif config.args.amount is None:
print(formatter.format_rate_pair_bidir(config.args.from_currency, config.args.to_currency),
file=stdout)
else:
print(formatter.format_conversion(config.args.amount,
config.args.from_currency,
config.args.to_currency),
file=stdout)
|