From fd3280c2547e7d54e6d28bd65342512eea51ee67 Mon Sep 17 00:00:00 2001 From: dsc Date: Sun, 20 Jan 2019 16:18:27 +0100 Subject: [PATCH] CSV export functionality for transaction history --- main.cpp | 5 ++ pages/History.qml | 47 +++++++++++++++++- src/libwalletqt/TransactionHistory.cpp | 66 ++++++++++++++++++++++++++ src/libwalletqt/TransactionHistory.h | 1 + 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/main.cpp b/main.cpp index 82229c23..43b1f5a6 100644 --- a/main.cpp +++ b/main.cpp @@ -268,6 +268,11 @@ int main(int argc, char *argv[]) engine.rootContext()->setContextProperty("scaleRatio", 1); #endif +#ifndef Q_OS_IOS + const QString desktopFolder = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + if (!desktopFolder.isEmpty()) + engine.rootContext()->setContextProperty("desktopFolder", desktopFolder); +#endif if (!moneroAccountsRootDir.empty()) { diff --git a/pages/History.qml b/pages/History.qml index 0f9d61a7..1c53692c 100644 --- a/pages/History.qml +++ b/pages/History.qml @@ -130,7 +130,7 @@ Rectangle { anchors.top: parent.top anchors.right: parent.right - spacing: 0 + spacing: 10 * scaleRatio GridLayout { property int column_width: { @@ -147,6 +147,15 @@ Rectangle { RowLayout { visible: !isMobile Layout.preferredWidth: parent.column_width + + StandardButton { + visible: !isIOS + small: true + text: qsTr("Export") + translationManager.emptyString + onClicked: { + writeCSVFileDialog.open(); + } + } } RowLayout { @@ -363,6 +372,42 @@ Rectangle { } } + FileDialog { + id: writeCSVFileDialog + title: "Please choose a folder" + selectFolder: true + onRejected: { + console.log("csv write canceled") + } + onAccepted: { + var dataDir = walletManager.urlToLocalPath(writeCSVFileDialog.fileUrl); + var written = currentWallet.history.writeCSV(currentWallet.currentSubaddressAccount, dataDir); + + if(written !== ""){ + confirmationDialog.title = qsTr("Success") + translationManager.emptyString; + var text = qsTr("CSV file written to: %1").arg(written) + "\n\n" + text += qsTr("Tip: Use your favorite spreadsheet software to sort on blockheight.") + "\n\n" + translationManager.emptyString; + confirmationDialog.text = text; + confirmationDialog.icon = StandardIcon.Information; + } else { + confirmationDialog.title = qsTr("Error") + translationManager.emptyString; + confirmationDialog.text = qsTr("Error exporting transaction data.") + "\n\n" + translationManager.emptyString; + confirmationDialog.icon = StandardIcon.Critical; + } + confirmationDialog.open() + } + Component.onCompleted: { + var _folder = 'file://' + moneroAccountsDir; + try { + _folder = 'file://' + desktopFolder; + } + catch(err) {} + finally { + writeCSVFileDialog.folder = _folder; + } + } + } + function onPageCompleted() { if(currentWallet != null && typeof currentWallet.history !== "undefined" ) { currentWallet.history.refresh(currentWallet.currentSubaddressAccount) diff --git a/src/libwalletqt/TransactionHistory.cpp b/src/libwalletqt/TransactionHistory.cpp index 243db642..86e9c4c7 100644 --- a/src/libwalletqt/TransactionHistory.cpp +++ b/src/libwalletqt/TransactionHistory.cpp @@ -2,6 +2,7 @@ #include "TransactionInfo.h" #include +#include #include @@ -113,3 +114,68 @@ TransactionHistory::TransactionHistory(Monero::TransactionHistory *pimpl, QObjec m_firstDateTime = QDateTime(QDate(2014, 4, 18)); // the genesis block m_lastDateTime = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones) } + +QString TransactionHistory::writeCSV(quint32 accountIndex, QString out) +{ + QList history = this->getAll(accountIndex); + if(history.count() < 1){ + return QString(""); + } + + // construct filename + qint64 now = QDateTime::currentDateTime().currentMSecsSinceEpoch(); + QString fn = QString(QString("%1/monero-txs_%2.csv").arg(out, QString::number(now / 1000))); + + // open file + QFile data(fn); + if(!data.open(QFile::WriteOnly | QFile::Truncate)){ + return QString(""); + } + + // write header + QTextStream output(&data); + output << "blockHeight,epoch,date,direction,amount,atomicAmount,fee,txid,label,subaddrAccount,paymentId\n"; + + foreach(const TransactionInfo *info, history) + { + // collect column data + double amount = info->amount(); + quint64 atomicAmount = info->atomicAmount(); + quint32 subaddrAccount = info->subaddrAccount(); + QString fee = info->fee(); + QString direction = QString(""); + TransactionInfo::Direction _direction = info->direction(); + if(_direction == TransactionInfo::Direction_In) + { + direction = QString("in"); + } + else if(_direction == TransactionInfo::Direction_Out){ + direction = QString("out"); + } + else { + continue; // skip TransactionInfo::Direction_Both + } + QString label = info->label(); + label.remove(QChar('"')); // reserved + quint64 blockHeight = info->blockHeight(); + QDateTime timeStamp = info->timestamp(); + QString date = info->date() + " " + info->time(); + uint epoch = timeStamp.toTime_t(); + QString displayAmount = info->displayAmount(); + QString paymentId = info->paymentId(); + if(paymentId == "0000000000000000"){ + paymentId = ""; + } + + // format and write + QString line = QString("%1,%2,%3,%4,%5,%6,%7,%8,\"%9\",%10,%11\n") + .arg(QString::number(blockHeight), QString::number(epoch), date) + .arg(direction, QString::number(amount), QString::number(atomicAmount)) + .arg(info->fee(), info->hash(), label, QString::number(subaddrAccount)) + .arg(paymentId); + output << line; + } + + data.close(); + return fn; +} diff --git a/src/libwalletqt/TransactionHistory.h b/src/libwalletqt/TransactionHistory.h index ac0c4400..cff62ef8 100644 --- a/src/libwalletqt/TransactionHistory.h +++ b/src/libwalletqt/TransactionHistory.h @@ -25,6 +25,7 @@ public: // Q_INVOKABLE TransactionInfo * transaction(const QString &id); Q_INVOKABLE QList getAll(quint32 accountIndex) const; Q_INVOKABLE void refresh(quint32 accountIndex); + Q_INVOKABLE QString writeCSV(quint32 accountIndex, QString out); quint64 count() const; QDateTime firstDateTime() const; QDateTime lastDateTime() const;