Make a Format dataclass, add --quantize option.

This commit is contained in:
Chris Berkhout 2021-04-26 13:43:05 +02:00
parent ec7df02391
commit 58d37463c6
7 changed files with 83 additions and 47 deletions

View file

@ -3,7 +3,7 @@ from datetime import datetime, timedelta
from pricehist import outputs, sources
from pricehist import __version__
from pricehist.formatinfo import FormatInfo
from pricehist.format import Format
def cli(args=None):
@ -62,17 +62,24 @@ def cmd_fetch(args):
for p in prices
]
default = FormatInfo()
default = Format()
fi = FormatInfo(
time=(args.renametime or default.time),
decimal=(args.formatdecimal or default.decimal),
thousands=(args.formatthousands or default.thousands),
symbol=(args.formatsymbol or default.symbol),
datesep=(args.formatdatesep or default.datesep),
def if_not_none(value, default):
if value is None:
return default
else:
return value
fmt = Format(
time=if_not_none(args.renametime, default.time),
decimal=if_not_none(args.formatdecimal, default.decimal),
thousands=if_not_none(args.formatthousands, default.thousands),
symbol=if_not_none(args.formatsymbol, default.symbol),
datesep=if_not_none(args.formatdatesep, default.datesep),
decimal_places=if_not_none(args.quantize, default.decimal_places),
)
print(output.format(prices, format_info=fi), end="")
print(output.format(prices, fmt=fmt), end="")
def build_parser():
@ -122,7 +129,10 @@ def build_parser():
usage=(
"pricehist fetch SOURCE PAIR "
"[-h] (-s DATE | -sx DATE) [-e DATE] [-o FMT] "
"[--rename-base SYM] [--rename-quote SYM] [--rename-time TIME]"
"[--rename-base SYM] [--rename-quote SYM] [--rename-time TIME] "
"[--format-decimal CHAR] [--format-thousands CHAR] "
"[--format-symbol rightspace|right|leftspace|left] [--format-datesep CHAR] "
"[--quantize INT]"
),
)
fetch_parser.add_argument(
@ -229,5 +239,12 @@ def build_parser():
type=str,
help="date separator",
)
fetch_parser.add_argument(
"--quantize",
dest="quantize",
metavar="INT",
type=int,
help="quantize to given number of decimal places",
)
return parser

27
src/pricehist/format.py Normal file
View file

@ -0,0 +1,27 @@
from dataclasses import dataclass
from decimal import Decimal, getcontext
@dataclass(frozen=True)
class Format:
time: str = "00:00:00"
decimal: str = "."
thousands: str = ""
symbol: str = "rightspace"
datesep: str = "-"
decimal_places: int = None
def quantize(self, num):
if self.decimal_places is None:
return num
else:
prec = getcontext().prec
digits = len(num.as_tuple().digits)
exponent = num.as_tuple().exponent
fractional_digits = -exponent
whole_digits = digits - fractional_digits
max_decimal_places = prec - whole_digits
chosen_decimal_places = min(self.decimal_places, max_decimal_places)
return num.quantize(Decimal("0." + ("0" * chosen_decimal_places)))

View file

@ -1,7 +0,0 @@
from collections import namedtuple
FormatInfo = namedtuple(
"FormatInfo",
["time", "decimal", "thousands", "symbol", "datesep"],
defaults=["00:00:00", ".", "", "rightspace", "-"],
)

View file

@ -1,23 +1,23 @@
from pricehist.formatinfo import FormatInfo
from pricehist.format import Format
class Beancount:
def format(self, prices, format_info=FormatInfo()):
def format(self, prices, fmt=Format()):
lines = []
for price in prices:
amount_parts = f"{price.amount:,}".split(".")
amount_parts[0] = amount_parts[0].replace(",", format_info.thousands)
amount_parts = f"{fmt.quantize(price.amount):,}".split(".")
amount_parts[0] = amount_parts[0].replace(",", fmt.thousands)
amount = ".".join(amount_parts)
qa_parts = [amount]
if format_info.symbol == "right":
if fmt.symbol == "right":
qa_parts = qa_parts + [price.quote]
else:
qa_parts = qa_parts + [" ", price.quote]
quote_amount = "".join(qa_parts)
date = str(price.date).replace("-", format_info.datesep)
date = str(price.date).replace("-", fmt.datesep)
lines.append(f"{date} price {price.base} {quote_amount}")
return "\n".join(lines) + "\n"

View file

@ -1,14 +1,14 @@
from pricehist.formatinfo import FormatInfo
from pricehist.format import Format
class CSV:
def format(self, prices, format_info=FormatInfo()):
def format(self, prices, fmt=Format()):
lines = ["date,base,quote,amount"]
for price in prices:
date = str(price.date).replace("-", format_info.datesep)
amount_parts = f"{price.amount:,}".split(".")
amount_parts[0] = amount_parts[0].replace(",", format_info.thousands)
amount = format_info.decimal.join(amount_parts)
date = str(price.date).replace("-", fmt.datesep)
amount_parts = f"{fmt.quantize(price.amount):,}".split(".")
amount_parts[0] = amount_parts[0].replace(",", fmt.thousands)
amount = fmt.decimal.join(amount_parts)
line = ",".join([date, price.base, price.quote, amount])
lines.append(line)
return "\n".join(lines) + "\n"

View file

@ -3,27 +3,27 @@ from datetime import datetime
from importlib.resources import read_text
from pricehist import __version__
from pricehist.formatinfo import FormatInfo
from pricehist.format import Format
class GnuCashSQL:
def format(self, prices, format_info=FormatInfo()):
fi = format_info
def format(self, prices, fmt=Format()):
source = "pricehist"
typ = "unknown"
values_parts = []
for price in prices:
date = f"{price.date} {fi.time}"
date = f"{price.date} {fmt.time}"
amount = fmt.quantize(price.amount)
m = hashlib.sha256()
m.update(
"".join(
[date, price.base, price.quote, source, typ, str(price.amount)]
[date, price.base, price.quote, source, typ, str(amount)]
).encode("utf-8")
)
guid = m.hexdigest()[0:32]
value_num = str(price.amount).replace(".", "")
value_denom = 10 ** len(f"{price.amount}.".split(".")[1])
value_num = str(amount).replace(".", "")
value_denom = 10 ** len(f"{amount}.".split(".")[1])
v = (
"("
f"'{guid}', "

View file

@ -1,29 +1,28 @@
from pricehist.formatinfo import FormatInfo
from pricehist.format import Format
class Ledger:
def format(self, prices, format_info=FormatInfo()):
fi = format_info
def format(self, prices, fmt=Format()):
lines = []
for price in prices:
date = str(price.date).replace("-", fi.datesep)
date = str(price.date).replace("-", fmt.datesep)
amount_parts = f"{price.amount:,}".split(".")
amount_parts[0] = amount_parts[0].replace(",", format_info.thousands)
amount = format_info.decimal.join(amount_parts)
amount_parts = f"{fmt.quantize(price.amount):,}".split(".")
amount_parts[0] = amount_parts[0].replace(",", fmt.thousands)
amount = fmt.decimal.join(amount_parts)
qa_parts = [amount]
if format_info.symbol == "left":
if fmt.symbol == "left":
qa_parts = [price.quote] + qa_parts
elif format_info.symbol == "leftspace":
elif fmt.symbol == "leftspace":
qa_parts = [price.quote, " "] + qa_parts
elif format_info.symbol == "right":
elif fmt.symbol == "right":
qa_parts = qa_parts + [price.quote]
else:
qa_parts = qa_parts + [" ", price.quote]
quote_amount = "".join(qa_parts)
lines.append(f"P {date} {fi.time} {price.base} {quote_amount}")
lines.append(f"P {date} {fmt.time} {price.base} {quote_amount}")
return "\n".join(lines) + "\n"
# TODO support additional details of the format: