move history export from HistoryList to Abstract_Wallet
Moves the history export function to Abstract_wallet so it can be unittested and called from other places like the cli or QML too.
This commit is contained in:
@@ -27,7 +27,7 @@ import os
|
|||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from typing import TYPE_CHECKING, Tuple, Dict, Any, Optional
|
from typing import TYPE_CHECKING, Tuple, Dict, Any
|
||||||
import threading
|
import threading
|
||||||
import enum
|
import enum
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
@@ -43,7 +43,7 @@ from electrum.address_synchronizer import TX_HEIGHT_LOCAL
|
|||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.util import (block_explorer_URL, profiler, TxMinedInfo,
|
from electrum.util import (block_explorer_URL, profiler, TxMinedInfo,
|
||||||
OrderedDictWithIndex, timestamp_to_datetime,
|
OrderedDictWithIndex, timestamp_to_datetime,
|
||||||
Satoshis, format_time, Fiat)
|
Satoshis, format_time)
|
||||||
from electrum.logging import get_logger, Logger
|
from electrum.logging import get_logger, Logger
|
||||||
from electrum.simple_config import SimpleConfig
|
from electrum.simple_config import SimpleConfig
|
||||||
|
|
||||||
@@ -55,7 +55,6 @@ from .my_treeview import MyTreeView
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from electrum.wallet import Abstract_Wallet
|
from electrum.wallet import Abstract_Wallet
|
||||||
from electrum.exchange_rate import FxThread
|
|
||||||
from .main_window import ElectrumWindow
|
from .main_window import ElectrumWindow
|
||||||
|
|
||||||
|
|
||||||
@@ -845,8 +844,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
|
|||||||
if not filename:
|
if not filename:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.do_export_history(
|
self.wallet.export_history_to_file(
|
||||||
self.wallet,
|
|
||||||
self.main_window.fx if self.hm.should_show_fiat() else None,
|
self.main_window.fx if self.hm.should_show_fiat() else None,
|
||||||
filename,
|
filename,
|
||||||
csv_button.isChecked(),
|
csv_button.isChecked(),
|
||||||
@@ -857,78 +855,6 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
|
|||||||
return
|
return
|
||||||
self.main_window.show_message(_("Your wallet history has been successfully exported."))
|
self.main_window.show_message(_("Your wallet history has been successfully exported."))
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def do_export_history(wallet: 'Abstract_Wallet', fx: Optional['FxThread'], file_path: str, is_csv: bool):
|
|
||||||
txns = wallet.get_full_history(fx=fx)
|
|
||||||
lines = []
|
|
||||||
|
|
||||||
def get_all_fees_paid_by_item(h_item: dict) -> Tuple[int, Optional[Fiat]]:
|
|
||||||
# gets all fees paid in an item (or group), as the outer group doesn't contain the
|
|
||||||
# transaction fees paid by the children
|
|
||||||
fees_sat = 0
|
|
||||||
fees_fiat = Fiat(ccy=fx.ccy, value=Decimal()) if fx else None
|
|
||||||
for child in h_item.get('children', []):
|
|
||||||
fees_sat += child['fee_sat'] or 0 if 'fee_sat' in child \
|
|
||||||
else (child.get('fee_msat', 0) or 0) // 1000 # FIXME: loses msat precision
|
|
||||||
if fees_fiat is not None and (child_fiat_fee := child.get('fiat_fee')):
|
|
||||||
fees_fiat += child_fiat_fee
|
|
||||||
|
|
||||||
fees_sat += h_item['fee_sat'] or 0 if 'fee_sat' in h_item \
|
|
||||||
else (h_item.get('fee_msat', 0) or 0) // 1000 # FIXME: loses msat precision
|
|
||||||
if fees_fiat is not None and (h_item_fiat_fee := h_item.get('fiat_fee')):
|
|
||||||
fees_fiat += h_item_fiat_fee
|
|
||||||
|
|
||||||
fiat_value = h_item.get('fiat_value')
|
|
||||||
if fees_fiat is not None and isinstance(fiat_value, Fiat) \
|
|
||||||
and (fiat_value.value is None or fiat_value.value.is_nan()):
|
|
||||||
# ensure that str(fees_fiat) == 'No Data' if str(fiat_value) == 'No Data'
|
|
||||||
fees_fiat = Fiat(ccy=fx.ccy, value=None)
|
|
||||||
|
|
||||||
return fees_sat, fees_fiat
|
|
||||||
|
|
||||||
if is_csv:
|
|
||||||
# sort by timestamp so the generated csv is more understandable on first sight
|
|
||||||
txns = dict(sorted(txns.items(), key=lambda h_item: h_item[1]['timestamp'] or 0))
|
|
||||||
for item in txns.values():
|
|
||||||
# tx groups will are shown as single element
|
|
||||||
fees_sat, fees_fiat = get_all_fees_paid_by_item(item)
|
|
||||||
# users are sensitive to changes of these fields as they have scripts/spreadsheets
|
|
||||||
# depending on them. E.g. https://github.com/spesmilo/electrum/issues/10445
|
|
||||||
assert str(fees_fiat) == 'No Data' if str(item.get('fiat_value')) == 'No Data' else True
|
|
||||||
line = [
|
|
||||||
item.get('txid', ''),
|
|
||||||
item.get('payment_hash', ''),
|
|
||||||
item.get('label', ''),
|
|
||||||
item.get('confirmations', ''),
|
|
||||||
item['bc_value'],
|
|
||||||
item['ln_value'],
|
|
||||||
item.get('fiat_value', ''),
|
|
||||||
fees_sat,
|
|
||||||
str(fees_fiat or ''),
|
|
||||||
item['date']
|
|
||||||
]
|
|
||||||
lines.append(line)
|
|
||||||
|
|
||||||
with open(file_path, "w+", encoding='utf-8') as f:
|
|
||||||
if is_csv:
|
|
||||||
import csv
|
|
||||||
transaction = csv.writer(f, lineterminator='\n')
|
|
||||||
transaction.writerow(["oc_transaction_hash",
|
|
||||||
"ln_payment_hash",
|
|
||||||
"label",
|
|
||||||
"confirmations",
|
|
||||||
"amount_chain_bc",
|
|
||||||
"amount_lightning_bc",
|
|
||||||
"fiat_value",
|
|
||||||
"network_fee_satoshi",
|
|
||||||
"fiat_fee",
|
|
||||||
"timestamp"])
|
|
||||||
for line in lines:
|
|
||||||
transaction.writerow(line)
|
|
||||||
else:
|
|
||||||
from electrum.util import json_encode
|
|
||||||
f.write(json_encode(txns))
|
|
||||||
|
|
||||||
def get_text_from_coordinate(self, row, col):
|
def get_text_from_coordinate(self, row, col):
|
||||||
return self.get_role_data_from_coordinate(row, col, role=Qt.ItemDataRole.DisplayRole)
|
return self.get_role_data_from_coordinate(row, col, role=Qt.ItemDataRole.DisplayRole)
|
||||||
|
|
||||||
|
|||||||
@@ -3614,6 +3614,76 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
util.trigger_callback('wallet_updated', self)
|
util.trigger_callback('wallet_updated', self)
|
||||||
self.adb.set_future_tx(tx.txid(), wanted_height=wanted_height)
|
self.adb.set_future_tx(tx.txid(), wanted_height=wanted_height)
|
||||||
|
|
||||||
|
def export_history_to_file(self, fx: Optional['FxThread'], file_path: str, is_csv: bool):
|
||||||
|
txns = self.get_full_history(fx=fx)
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
def get_all_fees_paid_by_item(h_item: dict) -> Tuple[int, Optional[Fiat]]:
|
||||||
|
# gets all fees paid in an item (or group), as the outer group doesn't contain the
|
||||||
|
# transaction fees paid by the children
|
||||||
|
fees_sat = 0
|
||||||
|
fees_fiat = Fiat(ccy=fx.ccy, value=Decimal()) if fx else None
|
||||||
|
for child in h_item.get('children', []):
|
||||||
|
fees_sat += child['fee_sat'] or 0 if 'fee_sat' in child \
|
||||||
|
else (child.get('fee_msat', 0) or 0) // 1000 # FIXME: loses msat precision
|
||||||
|
if fees_fiat is not None and (child_fiat_fee := child.get('fiat_fee')):
|
||||||
|
fees_fiat += child_fiat_fee
|
||||||
|
|
||||||
|
fees_sat += h_item['fee_sat'] or 0 if 'fee_sat' in h_item \
|
||||||
|
else (h_item.get('fee_msat', 0) or 0) // 1000 # FIXME: loses msat precision
|
||||||
|
if fees_fiat is not None and (h_item_fiat_fee := h_item.get('fiat_fee')):
|
||||||
|
fees_fiat += h_item_fiat_fee
|
||||||
|
|
||||||
|
fiat_value = h_item.get('fiat_value')
|
||||||
|
if fees_fiat is not None and isinstance(fiat_value, Fiat) \
|
||||||
|
and (fiat_value.value is None or fiat_value.value.is_nan()):
|
||||||
|
# ensure that str(fees_fiat) == 'No Data' if str(fiat_value) == 'No Data'
|
||||||
|
fees_fiat = Fiat(ccy=fx.ccy, value=None)
|
||||||
|
|
||||||
|
return fees_sat, fees_fiat
|
||||||
|
|
||||||
|
if is_csv:
|
||||||
|
# sort by timestamp so the generated csv is more understandable on first sight
|
||||||
|
txns = dict(sorted(txns.items(), key=lambda h_item: h_item[1]['timestamp'] or 0))
|
||||||
|
for item in txns.values():
|
||||||
|
# tx groups will are shown as single element
|
||||||
|
fees_sat, fees_fiat = get_all_fees_paid_by_item(item)
|
||||||
|
# users are sensitive to changes of these fields as they have scripts/spreadsheets
|
||||||
|
# depending on them. E.g. https://github.com/spesmilo/electrum/issues/10445
|
||||||
|
assert str(fees_fiat) == 'No Data' if str(item.get('fiat_value')) == 'No Data' else True
|
||||||
|
line = [
|
||||||
|
item.get('txid', ''),
|
||||||
|
item.get('payment_hash', ''),
|
||||||
|
item.get('label', ''),
|
||||||
|
item.get('confirmations', ''),
|
||||||
|
item['bc_value'],
|
||||||
|
item['ln_value'],
|
||||||
|
item.get('fiat_value', ''),
|
||||||
|
fees_sat,
|
||||||
|
str(fees_fiat or ''),
|
||||||
|
item['date']
|
||||||
|
]
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
with open(file_path, "w+", encoding='utf-8') as f:
|
||||||
|
if is_csv:
|
||||||
|
import csv
|
||||||
|
transaction = csv.writer(f, lineterminator='\n')
|
||||||
|
transaction.writerow(["oc_transaction_hash",
|
||||||
|
"ln_payment_hash",
|
||||||
|
"label",
|
||||||
|
"confirmations",
|
||||||
|
"amount_chain_bc",
|
||||||
|
"amount_lightning_bc",
|
||||||
|
"fiat_value",
|
||||||
|
"network_fee_satoshi",
|
||||||
|
"fiat_fee",
|
||||||
|
"timestamp"])
|
||||||
|
for line in lines:
|
||||||
|
transaction.writerow(line)
|
||||||
|
else:
|
||||||
|
f.write(util.json_encode(txns))
|
||||||
|
|
||||||
|
|
||||||
class Simple_Wallet(Abstract_Wallet):
|
class Simple_Wallet(Abstract_Wallet):
|
||||||
# wallet with a single keystore
|
# wallet with a single keystore
|
||||||
|
|||||||
Reference in New Issue
Block a user