From ec7df02391c18ee6408e211e9422a329350b2b4c Mon Sep 17 00:00:00 2001 From: Chris Berkhout Date: Mon, 26 Apr 2021 10:57:21 +0200 Subject: [PATCH] Add --format-decimal --format-thousands --format-symbol and --format-datesep. --- src/pricehist/cli.py | 43 +++++++++++++++++++++++++++-- src/pricehist/formatinfo.py | 7 +++++ src/pricehist/outputs/beancount.py | 25 ++++++++++++++--- src/pricehist/outputs/csv.py | 11 ++++++-- src/pricehist/outputs/gnucashsql.py | 6 ++-- src/pricehist/outputs/ledger.py | 26 +++++++++++++++-- 6 files changed, 105 insertions(+), 13 deletions(-) create mode 100644 src/pricehist/formatinfo.py diff --git a/src/pricehist/cli.py b/src/pricehist/cli.py index cc5d60b..31aaa28 100644 --- a/src/pricehist/cli.py +++ b/src/pricehist/cli.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta from pricehist import outputs, sources from pricehist import __version__ +from pricehist.formatinfo import FormatInfo def cli(args=None): @@ -61,8 +62,17 @@ def cmd_fetch(args): for p in prices ] - time = args.renametime or "00:00:00" - print(output.format(prices, time=time), end="") + default = FormatInfo() + + 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), + ) + + print(output.format(prices, format_info=fi), end="") def build_parser(): @@ -190,5 +200,34 @@ def build_parser(): type=str, help="set a particular time of day, e.g. 23:59:59", ) + fetch_parser.add_argument( + "--format-decimal", + dest="formatdecimal", + metavar="CHAR", + type=str, + help="decimal point", + ) + fetch_parser.add_argument( + "--format-thousands", + dest="formatthousands", + metavar="CHAR", + type=str, + help="thousands separator", + ) + fetch_parser.add_argument( + "--format-symbol", + dest="formatsymbol", + metavar="CHAR", + type=str, + choices=["rightspace", "right", "leftspace", "left"], + help="placement of the quote's commodity symbol relative to its amount", + ) + fetch_parser.add_argument( + "--format-datesep", + dest="formatdatesep", + metavar="CHAR", + type=str, + help="date separator", + ) return parser diff --git a/src/pricehist/formatinfo.py b/src/pricehist/formatinfo.py new file mode 100644 index 0000000..735189c --- /dev/null +++ b/src/pricehist/formatinfo.py @@ -0,0 +1,7 @@ +from collections import namedtuple + +FormatInfo = namedtuple( + "FormatInfo", + ["time", "decimal", "thousands", "symbol", "datesep"], + defaults=["00:00:00", ".", "", "rightspace", "-"], +) diff --git a/src/pricehist/outputs/beancount.py b/src/pricehist/outputs/beancount.py index 3ba535e..4a9776c 100644 --- a/src/pricehist/outputs/beancount.py +++ b/src/pricehist/outputs/beancount.py @@ -1,13 +1,30 @@ +from pricehist.formatinfo import FormatInfo + + class Beancount: - def format(self, prices, time=None): + def format(self, prices, format_info=FormatInfo()): lines = [] for price in prices: - lines.append( - f"{price.date} price {price.base} {price.amount} {price.quote}" - ) + + amount_parts = f"{price.amount:,}".split(".") + amount_parts[0] = amount_parts[0].replace(",", format_info.thousands) + amount = ".".join(amount_parts) + + qa_parts = [amount] + if format_info.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) + lines.append(f"{date} price {price.base} {quote_amount}") return "\n".join(lines) + "\n" +# NOTE: Beancount always has commodity to the right. It seems to be possible to +# skip the space, according to https://plaintextaccounting.org/quickref/#h.n4b87oz9ku6t + # https://beancount.github.io/docs/fetching_prices_in_beancount.html # https://beancount.github.io/docs/beancount_language_syntax.html#commodities-currencies # https://beancount.github.io/docs/beancount_language_syntax.html#comments diff --git a/src/pricehist/outputs/csv.py b/src/pricehist/outputs/csv.py index ef2856c..330bfe4 100644 --- a/src/pricehist/outputs/csv.py +++ b/src/pricehist/outputs/csv.py @@ -1,7 +1,14 @@ +from pricehist.formatinfo import FormatInfo + + class CSV: - def format(self, prices, time=None): + def format(self, prices, format_info=FormatInfo()): lines = ["date,base,quote,amount"] for price in prices: - line = ",".join([price.date, price.base, price.quote, str(price.amount)]) + 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) + line = ",".join([date, price.base, price.quote, amount]) lines.append(line) return "\n".join(lines) + "\n" diff --git a/src/pricehist/outputs/gnucashsql.py b/src/pricehist/outputs/gnucashsql.py index 0118566..fa672a3 100644 --- a/src/pricehist/outputs/gnucashsql.py +++ b/src/pricehist/outputs/gnucashsql.py @@ -3,16 +3,18 @@ from datetime import datetime from importlib.resources import read_text from pricehist import __version__ +from pricehist.formatinfo import FormatInfo class GnuCashSQL: - def format(self, prices, time=None): + def format(self, prices, format_info=FormatInfo()): + fi = format_info source = "pricehist" typ = "unknown" values_parts = [] for price in prices: - date = f"{price.date} {time}" + date = f"{price.date} {fi.time}" m = hashlib.sha256() m.update( "".join( diff --git a/src/pricehist/outputs/ledger.py b/src/pricehist/outputs/ledger.py index 869c4fa..3b0c0af 100644 --- a/src/pricehist/outputs/ledger.py +++ b/src/pricehist/outputs/ledger.py @@ -1,9 +1,29 @@ +from pricehist.formatinfo import FormatInfo + + class Ledger: - def format(self, prices, time=None): + def format(self, prices, format_info=FormatInfo()): + fi = format_info lines = [] for price in prices: - date = str(price.date).translate(str.maketrans("-", "/")) - lines.append(f"P {date} {time} {price.base} {price.amount} {price.quote}") + date = str(price.date).replace("-", fi.datesep) + + amount_parts = f"{price.amount:,}".split(".") + amount_parts[0] = amount_parts[0].replace(",", format_info.thousands) + amount = format_info.decimal.join(amount_parts) + + qa_parts = [amount] + if format_info.symbol == "left": + qa_parts = [price.quote] + qa_parts + elif format_info.symbol == "leftspace": + qa_parts = [price.quote, " "] + qa_parts + elif format_info.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}") return "\n".join(lines) + "\n" # TODO support additional details of the format: