Skip to content

Commit 0da923e

Browse files
committed
Sort token conversions, totals by conversion ratios
1 parent 70767a7 commit 0da923e

File tree

2 files changed

+94
-67
lines changed

2 files changed

+94
-67
lines changed

slip39/invoice/artifact.py

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ def conversions_table( conversions, symbols=None, greater=None, tablefmt=None, p
129129
"""Output a tabulation of conversion ratios, optionally >= a value (or of a pair of ratios), to
130130
the specified precision (default, 8 decimal points).
131131
132+
Sort by the column with the highest density of conversion ratios.
133+
132134
"""
133135
if symbols is None:
134136
symbols = sorted( set( sum( conversions.keys(), () )))
@@ -145,6 +147,7 @@ def fmt( v, d ):
145147
return round( v, d )
146148
return v
147149

150+
headers_raw = [ 'Coin' ] + [ f"in {s}" for s in symbols ]
148151
convers_raw = [
149152
[ r ] + list(
150153
(
@@ -161,14 +164,23 @@ def fmt( v, d ):
161164
# worthless (or very low valued) currencies, esp. w/ a small 'greater'. Transpose the
162165
# conversions (so each column is a row), and elide any w/ empty conversions rows. Finally,
163166
# re-transpose back to columns.
164-
headers_raw = [ 'Coin' ] + [ f"in {s}" for s in symbols ]
165167
convers_txp = list( zip( *convers_raw ))
166-
headers_use,convers_use_txp = zip( *[
167-
(hdr,col)
168-
for hdr,col in zip( headers_raw, convers_txp )
169-
if any( c != '' for c in col )
170-
])
171-
convers_use = list( zip( *convers_use_txp ))
168+
import json
169+
headers_use,convers_use_txp = zip( *sorted(
170+
[
171+
(hdr,col)
172+
for hdr,col in zip( headers_raw, convers_txp )
173+
if any( c != '' for c in col )
174+
],
175+
key = lambda hdr_col: sum( map( bool, hdr_col[1] )), # number of occupied entries in column
176+
reverse = True,
177+
))
178+
# Finally sort by the 2nd (first non-label) column, which has the most occupied values; consider None/'' entries as zero
179+
convers_use = sorted(
180+
zip( *convers_use_txp ),
181+
key = lambda row: row[1] or 0, # by 1st numeric column's conversion ratio
182+
reverse = True,
183+
)
172184

173185
return tabulate_nopad(
174186
convers_use,
@@ -746,8 +758,9 @@ def toti( c ):
746758
def taxi( c ):
747759
return headers_can.index( can( f'_Taxes {c}' ))
748760

749-
subtotal = tabulate_nopad(
750-
# And the per-currency Sub-totals (for each page)
761+
# The per-currency Sub-totals (for each page), sorted in ascending order. Stabilizes
762+
# the sort by also sorting the currencies.
763+
subtotal_rows = sorted(
751764
[
752765
[
753766
str( self.currencies_account[c] ),
@@ -758,29 +771,37 @@ def taxi( c ):
758771
]
759772
for c in sorted( self.currencies )
760773
],
761-
headers = (
762-
'Account',
763-
'Taxes' if final else f'Taxes {p+1}/{len( pages )}',
764-
'Subtotal' if final else f'Subtotal {p+1}/{len( pages )}',
765-
'Coin',
766-
'Currency',
767-
),
774+
key = lambda r: r[2]
775+
)
776+
subtotal_headers = (
777+
'Account',
778+
'Taxes' if final else f'Taxes {p+1}/{len( pages )}',
779+
'Subtotal' if final else f'Subtotal {p+1}/{len( pages )}',
780+
'Coin',
781+
'Currency',
782+
)
783+
subtotal = tabulate_nopad(
784+
subtotal_rows,
785+
headers = subtotal_headers,
768786
intfmt = ',',
769787
floatfmt = ',.15g',
770788
tablefmt = tablefmt or INVOICE_FORMAT,
771789
)
772790

773791
# And the per-currency Totals (up to current page)
774-
total_rows = [
792+
total_rows = sorted(
775793
[
776-
str( self.currencies_account[c] ),
777-
round( page[-1][taxi( c )], deci( c )),
778-
round( page[-1][toti( c )], deci( c )),
779-
c,
780-
self.currencies_account[c].name if c == self.currencies_account[c].symbol else self.currencies_proxy[c].name,
781-
]
782-
for c in sorted( self.currencies )
783-
]
794+
[
795+
str( self.currencies_account[c] ),
796+
round( page[-1][taxi( c )], deci( c )),
797+
round( page[-1][toti( c )], deci( c )),
798+
c,
799+
self.currencies_account[c].name if c == self.currencies_account[c].symbol else self.currencies_proxy[c].name,
800+
]
801+
for c in sorted( self.currencies )
802+
],
803+
key = lambda r: r[2]
804+
)
784805
total_headers = (
785806
'Account',
786807
'Taxes' if final else f'Taxes {p+1}/{len( pages )}',
@@ -1287,7 +1308,7 @@ def produce_invoice(
12871308
colalign=( 'right', 'left' ), tablefmt='plain'
12881309
)
12891310

1290-
exch = conversions_table( invoice.conversions, greater=1 )
1311+
exch = conversions_table( invoice.conversions )
12911312
exch_date = invoice.resolved.strftime( INVOICE_STRFTIME )
12921313
inv_table = (dets, tbl,)
12931314
if final:

slip39/invoice/artifact_test.py

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,11 @@ def test_conversions():
4141
c1_tbl = conversions_table( c1, tablefmt='orgtbl' )
4242
print( '\n' + c1_tbl )
4343
assert c1_tbl == """\
44-
| Coin | in ETH | in USD |
45-
|------+--------+-----------|
46-
| BTC | ? | 23,456.78 |
47-
| ETH | | 1,234.56 |
48-
| USD | | |"""
44+
| Coin | in USD | in ETH |
45+
|------+-----------+--------|
46+
| BTC | 23,456.78 | ? |
47+
| ETH | 1,234.56 | |
48+
| USD | | |"""
4949

5050
c_simple = dict( c1 )
5151
c_simple_i = 0
@@ -55,11 +55,11 @@ def test_conversions():
5555
c_simple_tbl = conversions_table( c_simple, tablefmt='orgtbl' )
5656
print( c_simple_tbl )
5757
assert c_simple_tbl == """\
58-
| Coin | in BTC | in ETH | in USD |
59-
|------+------------+-----------+-----------|
60-
| BTC | | 19.000113 | 23,456.78 |
61-
| ETH | 0.05263126 | | 1,234.56 |
62-
| USD | | | |"""
58+
| Coin | in USD | in BTC | in ETH |
59+
|------+-----------+------------+-----------|
60+
| BTC | 23,456.78 | | 19.000113 |
61+
| ETH | 1,234.56 | 0.05263126 | |
62+
| USD | | | |"""
6363

6464
c_w_doge = dict( c1, ) | { ('DOGE','BTC'): .00000385, ('DOGE','USD'): None }
6565
c_w_doge_i = 0
@@ -83,22 +83,22 @@ def test_conversions():
8383
c_w_doge_tbl = conversions_table( c_w_doge, tablefmt='orgtbl' )
8484
print( c_w_doge_tbl )
8585
assert c_w_doge_tbl == """\
86-
| Coin | in BTC | in DOGE | in ETH | in USD |
87-
|------+------------+----------------+-----------+----------------|
88-
| BTC | | 259,740.26 | 19.000113 | 23,456.78 |
89-
| DOGE | | | | 0.0903086 |
90-
| ETH | 0.05263126 | 13,670.458 | | 1,234.56 |
91-
| USD | | 11.073142 | | |"""
86+
| Coin | in DOGE | in USD | in BTC | in ETH |
87+
|------+----------------+----------------+------------+-----------|
88+
| BTC | 259,740.26 | 23,456.78 | | 19.000113 |
89+
| ETH | 13,670.458 | 1,234.56 | 0.05263126 | |
90+
| USD | 11.073142 | | | |
91+
| DOGE | | 0.0903086 | | |"""
9292

9393
c_w_doge_all = conversions_table( c_w_doge, greater=False, tablefmt='orgtbl' )
9494
print( c_w_doge_all )
9595
assert c_w_doge_all == """\
9696
| Coin | in BTC | in DOGE | in ETH | in USD |
9797
|------+------------+----------------+-------------+----------------|
98-
| BTC | | 259,740.26 | 19.000113 | 23,456.78 |
99-
| DOGE | 3.85e-06 | | 7.315e-05 | 0.0903086 |
10098
| ETH | 0.05263126 | 13,670.458 | | 1,234.56 |
101-
| USD | 4.263e-05 | 11.073142 | 0.00081001 | |"""
99+
| USD | 4.263e-05 | 11.073142 | 0.00081001 | |
100+
| DOGE | 3.85e-06 | | 7.315e-05 | 0.0903086 |
101+
| BTC | | 259,740.26 | 19.000113 | 23,456.78 |"""
102102

103103
c_bad = dict( c1, ) | { ('DOGE','USD'): None }
104104
with pytest.raises( Exception ) as c_bad_exc:
@@ -136,13 +136,13 @@ def test_conversions():
136136
c_zero_tbl = conversions_table( c_zero, tablefmt='orgtbl' )
137137
print( c_zero_tbl )
138138
assert c_zero_tbl == """\
139-
| Coin | in BTC | in ETH | in USD | in WEENUS | in ZEENUS |
140-
|--------+------------+-----------+-----------+-----------+-----------|
141-
| BTC | | 19.000113 | 23,456.78 | | |
142-
| ETH | 0.05263126 | | 1,234.56 | | |
143-
| USD | | | | | |
144-
| WEENUS | 0 | 0 | 0 | | 1 |
145-
| ZEENUS | 0 | 0 | 0 | 1 | |"""
139+
| Coin | in USD | in BTC | in ETH | in WEENUS | in ZEENUS |
140+
|--------+-----------+------------+-----------+-----------+-----------|
141+
| BTC | 23,456.78 | | 19.000113 | | |
142+
| ETH | 1,234.56 | 0.05263126 | | | |
143+
| USD | | | | | |
144+
| WEENUS | 0 | 0 | 0 | | 1 |
145+
| ZEENUS | 0 | 0 | 0 | 1 | |"""
146146

147147

148148
line_amounts = [
@@ -356,28 +356,28 @@ def test_tabulate( tmp_path ):
356356
2 | Simple | 1 | USD | USDC | 12,345.0001 | no tax | 0 | 12,345 | 12,762.88 | 0.56723907
357357
---+-------------------------------------------------+-----+---------------+--------+-------------+--------+------------+-----------------+------------+------------
358358
| BTC: bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 | | Bitcoin | BTC | | | 0.0008844 | 0.56723907 | |
359-
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | Ethereum | ETH | | | 0.013266 | 8.508586 | |
360-
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | USD Coin | USDC | | | 19.9 | 12,762.88 | |
361359
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | Wrapped BTC | WBTC | | | 0.0008844 | 0.56723907 | |
360+
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | Ethereum | ETH | | | 0.013266 | 8.508586 | |
362361
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | Wrapped Ether | WETH | | | 0.013266 | 8.508586 | |
362+
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | USD Coin | USDC | | | 19.9 | 12,762.88 | |
363363
| XRP: rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV | | Ripple | XRP | | | 53.06 | 34,034.34 | |
364364
365365
Account | Taxes | Subtotal | Coin | Currency
366366
-------------------------------------------------+------------+-----------------+------+---------------
367367
BTC: bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 | 0.0008844 | 0.56723907 | BTC | Bitcoin
368-
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.013266 | 8.508586 | ETH | Ethereum
369-
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 19.9 | 12,762.88 | USDC | USD Coin
370368
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.0008844 | 0.56723907 | WBTC | Wrapped BTC
369+
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.013266 | 8.508586 | ETH | Ethereum
371370
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.013266 | 8.508586 | WETH | Wrapped Ether
371+
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 19.9 | 12,762.88 | USDC | USD Coin
372372
XRP: rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV | 53.06 | 34,034.34 | XRP | Ripple
373373
374374
Account | Taxes | Total | Coin | Currency
375375
-------------------------------------------------+------------+-----------------+------+---------------
376376
BTC: bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 | 0.0008844 | 0.56723907 | BTC | Bitcoin
377-
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.013266 | 8.508586 | ETH | Ethereum
378-
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 19.9 | 12,762.88 | USDC | USD Coin
379377
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.0008844 | 0.56723907 | WBTC | Wrapped BTC
378+
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.013266 | 8.508586 | ETH | Ethereum
380379
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.013266 | 8.508586 | WETH | Wrapped Ether
380+
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 19.9 | 12,762.88 | USDC | USD Coin
381381
XRP: rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV | 53.06 | 34,034.34 | XRP | Ripple""" # noqa: E501
382382

383383
# Test precision of columns related to Bitcoin; should be 8 decimals throughout, even if WBTC
@@ -417,29 +417,29 @@ def test_tabulate( tmp_path ):
417417
1 | Couchant bogy | 45 | BTC | BTC | 9e-05 | 3.5% inc | 0.00013696 | 0.00405 | 4,613.62 | 218.44 | 0.20505 | 0.00970839 | 0.20505 | 0.00970839
418418
---+-------------------------------------------------+-----+---------------+------+---------+----------+--------------+--------------+------------+------------+-----------+------------+------------+------------
419419
| BTC: bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 | | Bitcoin | BTC | | | 0.00970839 | 0.20505 | | | | | |
420-
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | Ethereum | ETH | | | 0.145626 | 3.07575 | | | | | |
421-
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | USD Coin | USDC | | | 218.44 | 4,613.62 | | | | | |
422420
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | Wrapped BTC | WBTC | | | 0.00970839 | 0.20505 | | | | | |
421+
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | Ethereum | ETH | | | 0.145626 | 3.07575 | | | | | |
423422
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | Wrapped Ether | WETH | | | 0.145626 | 3.07575 | | | | | |
423+
| ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | | USD Coin | USDC | | | 218.44 | 4,613.62 | | | | | |
424424
| XRP: rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV | | Ripple | XRP | | | 582.5 | 12,303 | | | | | |
425425
426426
Account | Taxes | Subtotal | Coin | Currency
427427
-------------------------------------------------+--------------+--------------+------+---------------
428428
BTC: bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 | 0.00970839 | 0.20505 | BTC | Bitcoin
429-
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.145626 | 3.07575 | ETH | Ethereum
430-
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 218.44 | 4,613.62 | USDC | USD Coin
431429
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.00970839 | 0.20505 | WBTC | Wrapped BTC
430+
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.145626 | 3.07575 | ETH | Ethereum
432431
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.145626 | 3.07575 | WETH | Wrapped Ether
432+
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 218.44 | 4,613.62 | USDC | USD Coin
433433
XRP: rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV | 582.5 | 12,303 | XRP | Ripple
434434
435435
Account | Taxes | Total | Coin | Currency
436436
-------------------------------------------------+--------------+--------------+------+---------------
437437
BTC: bc1qk0a9hr7wjfxeenz9nwenw9flhq0tmsf6vsgnn2 | 0.00970839 | 0.20505 | BTC | Bitcoin
438-
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.145626 | 3.07575 | ETH | Ethereum
439-
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 218.44 | 4,613.62 | USDC | USD Coin
440438
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.00970839 | 0.20505 | WBTC | Wrapped BTC
439+
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.145626 | 3.07575 | ETH | Ethereum
441440
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 0.145626 | 3.07575 | WETH | Wrapped Ether
442-
XRP: rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV | 582.5 | 12,303 | XRP | Ripple""" # noqa: E501
441+
ETH: 0xfc2077CA7F403cBECA41B1B0F62D91B5EA631B5E | 218.44 | 4,613.62 | USDC | USD Coin
442+
XRP: rUPzi4ZwoYxi7peKCqUkzqEuSrzSRyLguV | 582.5 | 12,303 | XRP | Ripple""" # noqa: E501
443443

444444
# Output some invoices
445445
this = Path( __file__ ).resolve()
@@ -512,12 +512,18 @@ def test_tabulate( tmp_path ):
512512
"DAI",
513513
None,
514514
"USDC",
515-
"USDT",
516515
"US Dollar",
516+
"USD Coin",
517+
"US Dollar",
518+
"USDT",
519+
"Pax Dollar",
520+
"Ripple",
517521
"XRP",
522+
"HoloToken",
518523
"HOT",
519524
"Shiba Inu",
520-
"ZEENUS", # Worthless; will cause Invoice to fail if chosen as one of payment 'currencies'
525+
"WEENUS", # Worthless; will cause Invoice to fail if chosen as one of payment 'currencies'
526+
"ZEENUS",
521527
]
522528

523529

0 commit comments

Comments
 (0)