From: tobtoht Date: Wed, 5 Mar 2025 12:21:57 +0000 (+0100) Subject: history: rework csv export X-Git-Url: https://git.nutra.tk/v1?a=commitdiff_plain;h=055b898c85e96871e762d6aca6bf2b6af9511ed4;p=gamesguru%2Ffeather.git history: rework csv export --- diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index aea3a43a..bac74d46 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -13,6 +13,7 @@ #include "dialog/AddressCheckerIndexDialog.h" #include "dialog/BalanceDialog.h" #include "dialog/DebugInfoDialog.h" +#include "dialog/HistoryExportDialog.h" #include "dialog/PasswordDialog.h" #include "dialog/TxBroadcastDialog.h" #include "dialog/TxConfAdvDialog.h" @@ -1714,26 +1715,8 @@ void MainWindow::onTxPoolBacklog(const QVector &backlog, quint64 origin } void MainWindow::onExportHistoryCSV() { - QString filePath = QFileDialog::getSaveFileName(this, "Save CSV file", QDir::homePath(), "CSV (*.csv)"); - if (filePath.isEmpty()) - return; - if (!filePath.endsWith(".csv")) - filePath += ".csv"; - QFileInfo fileInfo(filePath); - QDir dir = fileInfo.absoluteDir(); - - if (!dir.exists()) { - Utils::showError(this, "Unable to export transaction history", QString("Path does not exist: %1").arg(dir.absolutePath())); - return; - } - - bool success = m_wallet->history()->writeCSV(filePath); - if (!success) { - Utils::showError(this, "Unable to export transaction history", QString("No permission to write to: %1").arg(filePath)); - return; - } - - Utils::showInfo(this, "CSV export", QString("Transaction history exported to %1").arg(filePath)); + HistoryExportDialog dialog{m_wallet, this}; + dialog.exec(); } void MainWindow::onImportHistoryDescriptionsCSV() { diff --git a/src/dialog/HistoryExportDialog.cpp b/src/dialog/HistoryExportDialog.cpp new file mode 100644 index 00000000..0849ced5 --- /dev/null +++ b/src/dialog/HistoryExportDialog.cpp @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#include "HistoryExportDialog.h" +#include "ui_HistoryExportDialog.h" + +#include + +#include "Utils.h" +#include "WalletManager.h" +#include "libwalletqt/Wallet.h" +#include "TransactionHistory.h" +#include "utils/AppData.h" + +HistoryExportDialog::HistoryExportDialog(Wallet *wallet, QWidget *parent) + : WindowModalDialog(parent) + , ui(new Ui::HistoryExportDialog) + , m_wallet(wallet) +{ + ui->setupUi(this); + + connect(ui->btn_export, &QPushButton::clicked, this, &HistoryExportDialog::exportHistory); + + connect(ui->radio_everything, &QRadioButton::toggled, [this](bool toggled) { + if (!toggled) return; + this->setEverything(); + }); + + connect(ui->radio_YTD, &QRadioButton::toggled, [this](bool toggled) { + if (!toggled) return; + ui->date_min->setDate(QDate(QDate::currentDate().year(), 1, 1)); + ui->date_max->setDate(QDate::currentDate()); + }); + + connect(ui->radio_lastDays, &QRadioButton::toggled, [this](bool toggled) { + ui->spin_days->setEnabled(toggled); + if (!toggled) return; + ui->date_min->setDate(QDate::currentDate().addDays(-ui->spin_days->value() + 1)); + ui->date_max->setDate(QDate::currentDate()); + }); + + connect(ui->spin_days, &QSpinBox::valueChanged, [this] { + ui->date_min->setDate(QDate::currentDate().addDays(-ui->spin_days->value() + 1)); + ui->date_max->setDate(QDate::currentDate()); + }); + + connect(ui->radio_lastMonth, &QRadioButton::toggled, [this](bool toggled) { + if (!toggled) return; + ui->date_min->setDate(QDate(QDate::currentDate().year(), QDate::currentDate().month(), 1).addMonths(-1)); + ui->date_max->setDate(QDate(QDate::currentDate().year(), QDate::currentDate().month(), 1).addDays(-1)); + }); + + connect(ui->radio_lastYear, &QRadioButton::toggled, [this](bool toggled) { + if (!toggled) return; + ui->date_min->setDate(QDate(QDate::currentDate().year() - 1, 1, 1)); + ui->date_max->setDate(QDate(QDate::currentDate().year(), 1, 1).addDays(-1)); + }); + + connect(ui->radio_customRange, &QRadioButton::toggled, [this](bool toggled) { + ui->date_min->setEnabled(toggled); + ui->date_max->setEnabled(toggled); + }); + + this->setEverything(); + + this->adjustSize(); +} + +void HistoryExportDialog::setEverything() +{ + ui->date_min->setDate(QDate(2014,4,18)); + ui->date_max->setDate(QDate::currentDate()); +} + +void HistoryExportDialog::exportHistory() +{ + QString wallet_name = m_wallet->walletName(); + QString csv_file_name = QString("/history_export_%1.csv").arg(wallet_name); + + QString filePath = QFileDialog::getSaveFileName(this, "Save CSV file", QDir::homePath() + csv_file_name, "CSV (*.csv)"); + if (filePath.isEmpty()) + return; + if (!filePath.endsWith(".csv")) + filePath += ".csv"; + QFileInfo fileInfo(filePath); + QDir dir = fileInfo.absoluteDir(); + + if (!dir.exists()) { + Utils::showError(this, "Unable to export transaction history", QString("Path does not exist: %1").arg(dir.absolutePath())); + return; + } + + auto num_transactions = m_wallet->history()->count(); + + QList> csvData; + + for (int i = 0; i < num_transactions; i++) { + TransactionRow* tx = m_wallet->history()->transaction(i); + + QDate minimumDate = ui->date_min->date(); + if (tx->timestamp().date() < minimumDate) { + continue; + } + + QDate maximumDate = ui->date_max->date(); + if (tx->timestamp().date() > maximumDate) { + continue; + } + + if (ui->check_excludePending->isChecked() && tx->isPending()) { + continue; + } + + if (ui->check_excludeFailed->isChecked() && tx->isFailed()) { + continue; + } + + if (ui->radio_incomingTransactions->isChecked() && tx->direction() != TransactionRow::Direction_In) { + continue; + } + + if (ui->radio_outgoingTransactions->isChecked() && tx->direction() != TransactionRow::Direction_Out) { + continue; + } + + if (ui->radio_coinbaseTransactions->isChecked() && !tx->isCoinbase()) { + continue; + } + + QString date = QString("%1T%2Z").arg(tx->date(), tx->time()); + + QString direction = QString(""); + if (tx->direction() == TransactionRow::Direction_In) + direction = QString("in"); + else if (tx->direction() == TransactionRow::Direction_Out) + direction = QString("out"); + else + continue; // skip TransactionInfo::Direction_Both + + QString balanceDelta = WalletManager::displayAmount(abs(tx->balanceDelta())); + if (tx->direction() == TransactionRow::Direction_Out) { + balanceDelta = "-" + balanceDelta; + } + + QString paymentId = tx->paymentId(); + if (paymentId == "0000000000000000") { + paymentId = ""; + } + + const double usd_price = appData()->txFiatHistory->get(tx->timestamp().toString("yyyyMMdd")); + double fiat_price = usd_price * tx->amount(); + QString fiatAmount = (usd_price > 0) ? QString::number(fiat_price, 'f', 2) : "?"; + + QString line = QString(R"(%1,%2,"%3",%4,"%5",%6,%7,%8,"%9","%10","%11","%12","%13")") + .arg(QString::number(tx->blockHeight()), + QString::number(tx->timestamp().toSecsSinceEpoch()), + date, + QString::number(tx->subaddrAccount()), + direction, + balanceDelta, + tx->displayAmount(), + tx->fee(), + tx->hash(), + tx->description(), + paymentId, + fiatAmount, + "USD"); + csvData.append({tx->blockHeight(), line}); + } + + std::sort(csvData.begin(), csvData.end(), [](const QPair &tx1, const QPair &tx2){ + return tx1.first < tx2.first; + }); + + QString csvString = "blockHeight,timestamp,date,accountIndex,direction,balanceDelta,amount,fee,txid,description,paymentId,fiatAmount,fiatCurrency"; + for (const auto& data : csvData) { + csvString += "\n" + data.second; + } + + bool success = Utils::fileWrite(filePath, csvString); + + if (!success) { + Utils::showError(this, "Unable to export transaction history", QString("No permission to write to: %1").arg(filePath)); + return; + } + + Utils::showInfo(this, "CSV export", QString("Transaction history exported to:\n\n%1").arg(filePath)); +} + +HistoryExportDialog::~HistoryExportDialog() = default; diff --git a/src/dialog/HistoryExportDialog.h b/src/dialog/HistoryExportDialog.h new file mode 100644 index 00000000..8ec7bd6f --- /dev/null +++ b/src/dialog/HistoryExportDialog.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-3-Clause +// SPDX-FileCopyrightText: The Monero Project + +#ifndef HISTORYEXPORTDIALOG_H +#define HISTORYEXPORTDIALOG_H + +#include "components.h" + +namespace Ui { +class HistoryExportDialog; +} + +class Wallet; +class HistoryExportDialog : public WindowModalDialog +{ + Q_OBJECT + +public: + explicit HistoryExportDialog(Wallet *wallet, QWidget *parent); + ~HistoryExportDialog() override; + +private: + void setEverything(); + void exportHistory(); + + QScopedPointer ui; + Wallet *m_wallet; +}; + + +#endif //HISTORYEXPORTDIALOG_H diff --git a/src/dialog/HistoryExportDialog.ui b/src/dialog/HistoryExportDialog.ui new file mode 100644 index 00000000..b65737cf --- /dev/null +++ b/src/dialog/HistoryExportDialog.ui @@ -0,0 +1,302 @@ + + + HistoryExportDialog + + + + 0 + 0 + 351 + 539 + + + + History Export + + + + + + Export range + + + + + + Everything + + + true + + + + + + + Year to Date + + + + + + + + + Last + + + + + + + false + + + 1 + + + 99999 + + + 90 + + + + + + + days + + + + + + + Qt::Orientation::Horizontal + + + + 0 + 20 + + + + + + + + + + Last month + + + + + + + Last year + + + + + + + Custom range + + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Fixed + + + + 30 + 0 + + + + + + + + From + + + + + + + false + + + yyyy-MM-dd + + + true + + + + + + + To + + + + + + + false + + + yyyy-MM-dd + + + true + + + + + + + + + + + + + 0 + 0 + + + + What to export + + + + + + All transactions + + + true + + + + + + + Incoming transactions + + + + + + + Outgoing transactions + + + + + + + Coinbase (miner) transactions + + + + + + + Qt::Orientation::Horizontal + + + + + + + Exclude pending transactions + + + true + + + + + + + Exclude failed transactions + + + true + + + + + + + + + + + + Export + + + + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Close + + + + + + + + + + + buttonBox + accepted() + HistoryExportDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + HistoryExportDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp index cfc7fd21..14b0a468 100644 --- a/src/libwalletqt/TransactionHistory.cpp +++ b/src/libwalletqt/TransactionHistory.cpp @@ -342,68 +342,6 @@ void TransactionHistory::clearRows() { m_rows.clear(); } -bool TransactionHistory::writeCSV(const QString &path) { - QString data; - QReadLocker locker(&m_lock); - - auto transactions = m_rows; - - std::sort(transactions.begin(), transactions.end(), [](const TransactionRow *info1, const TransactionRow *info2){ - return info1->blockHeight() < info2->blockHeight(); - }); - - for (const auto &info : transactions) { - // collect column data - QDateTime timeStamp = info->timestamp(); - double amount = info->amount(); - - // calc historical fiat price - QString fiatAmount; - QString preferredFiatSymbol = conf()->get(Config::preferredFiatCurrency).toString(); - const double usd_price = appData()->txFiatHistory->get(timeStamp.toString("yyyyMMdd")); - double fiat_price = usd_price * amount; - - if (preferredFiatSymbol != "USD") - fiat_price = appData()->prices.convert("USD", preferredFiatSymbol, fiat_price); - double fiat_rounded = ceil(Utils::roundSignificant(fiat_price, 3) * 100.0) / 100.0; - if (usd_price != 0) - fiatAmount = QString::number(fiat_rounded); - else - fiatAmount = "\"?\""; - - QString direction = QString(""); - if (info->direction() == TransactionRow::Direction_In) - direction = QString("in"); - else if (info->direction() == TransactionRow::Direction_Out) - direction = QString("out"); - else - continue; // skip TransactionInfo::Direction_Both - - QString displayAmount = info->displayAmount(); - QString paymentId = info->paymentId(); - if (paymentId == "0000000000000000") { - paymentId = ""; - } - - QString date = QString("%1T%2Z").arg(info->date(), info->time()); // ISO 8601 - - QString balanceDelta = WalletManager::displayAmount(info->balanceDelta()); - if (info->direction() == TransactionRow::Direction_Out) { - balanceDelta = "-" + balanceDelta; - } - - // format and write - QString line = QString("\n%1,%2,\"%3\",%4,\"%5\",%6,%7,%8,\"%9\",\"%10\",\"%11\",%12,\"%13\"") - .arg(QString::number(info->blockHeight()), QString::number(timeStamp.toSecsSinceEpoch()), date, - QString::number(info->subaddrAccount()), direction, balanceDelta, info->displayAmount(), - info->fee(), info->hash(), info->description(), paymentId, fiatAmount, preferredFiatSymbol); - data += line; - } - - data = QString("blockHeight,timestamp,date,accountIndex,direction,balanceDelta,amount,fee,txid,description,paymentId,fiatAmount,fiatCurrency%1").arg(data); - return Utils::fileWrite(path, data); -} - QStringList parseCSVLine(const QString &line) { QStringList result; QString currentField; diff --git a/src/libwalletqt/TransactionHistory.h b/src/libwalletqt/TransactionHistory.h index ba8dc38a..cd665315 100644 --- a/src/libwalletqt/TransactionHistory.h +++ b/src/libwalletqt/TransactionHistory.h @@ -41,7 +41,6 @@ public: bool locked() const; void clearRows(); - bool writeCSV(const QString &path); QString importLabelsFromCSV(const QString &fileName); signals: