]> Nutra Git (v1) - gamesguru/feather.git/commitdiff
history: import descriptions from CSV
authortobtoht <tob@featherwallet.org>
Mon, 7 Oct 2024 13:34:27 +0000 (15:34 +0200)
committertobtoht <tob@featherwallet.org>
Mon, 7 Oct 2024 13:34:27 +0000 (15:34 +0200)
src/MainWindow.cpp
src/MainWindow.h
src/MainWindow.ui
src/libwalletqt/Coins.cpp
src/libwalletqt/TransactionHistory.cpp
src/libwalletqt/TransactionHistory.h
src/libwalletqt/Wallet.cpp
src/model/TransactionHistoryModel.cpp
src/model/TransactionHistoryModel.h

index 882a061336227078ecc2df2b1900cfb51665e994..324ea902fd6179aa8f87ef36c8867d0235b55234 100644 (file)
@@ -315,6 +315,7 @@ void MainWindow::initMenu() {
 
     // [Wallet] -> [History]
     connect(ui->actionExport_CSV, &QAction::triggered, this, &MainWindow::onExportHistoryCSV);
+    connect(ui->actionImportHistoryCSV, &QAction::triggered, this, &MainWindow::onImportHistoryDescriptionsCSV);
 
     // [Wallet] -> [Contacts]
     connect(ui->actionExportContactsCSV, &QAction::triggered, this, &MainWindow::onExportContactsCSV);
@@ -575,7 +576,7 @@ void MainWindow::onWalletOpened() {
         m_wallet->history()->refresh();
     });
     // Vice versa
-    connect(m_wallet->history(), &TransactionHistory::txNoteChanged, [this] {
+    connect(m_wallet->transactionHistoryModel(), &TransactionHistoryModel::transactionDescriptionChanged, [this] {
         m_wallet->coins()->refresh();
     });
 
@@ -1713,6 +1714,21 @@ void MainWindow::onExportHistoryCSV() {
     Utils::showInfo(this, "CSV export", QString("Transaction history exported to %1").arg(fn));
 }
 
+void MainWindow::onImportHistoryDescriptionsCSV() {
+    const QString fileName = QFileDialog::getOpenFileName(this, "Import CSV file", QDir::homePath(), "CSV Files (*.csv)");
+    if (fileName.isEmpty()) {
+        return;
+    }
+
+    QString error = m_wallet->history()->importLabelsFromCSV(fileName);
+    if (!error.isEmpty()) {
+        Utils::showError(this, "Unable to import transaction descriptions from CSV", error);
+    }
+    else {
+        Utils::showInfo(this, "Successfully imported transaction descriptions from CSV");
+    }
+}
+
 void MainWindow::onExportContactsCSV() {
     auto *model = m_wallet->addressBookModel();
     if (model->rowCount() <= 0){
index 9e15b0c113c160de89a69bee05e3ab0561522d57..84cbe410700e034a5037480693bc546e6c21e88e 100644 (file)
@@ -113,6 +113,7 @@ private slots:
     void menuToggleTabVisible(const QString &key);
     void menuClearHistoryClicked();
     void onExportHistoryCSV();
+    void onImportHistoryDescriptionsCSV();
     void onExportContactsCSV();
     void onCreateDesktopEntry();
     void onShowDocumentation();
index 7cde5195a88c8c6e18130df2e87027f86a06f68e..1133416c74b1bbe925edc9efcd1394440fa080e7 100644 (file)
       <string>History</string>
      </property>
      <addaction name="actionExport_CSV"/>
+     <addaction name="actionImportHistoryCSV"/>
     </widget>
     <widget class="QMenu" name="menuContacts">
      <property name="title">
     <string>Tx pool viewer</string>
    </property>
   </action>
+  <action name="actionImportHistoryCSV">
+   <property name="text">
+    <string>Import descriptions from CSV</string>
+   </property>
+  </action>
  </widget>
  <layoutdefault spacing="6" margin="11"/>
  <customwidgets>
index 7ab8a4519bf87d69213c8f1cd51d661e2c4d2a4c..c5de0aaf79832d861d3f2312addd91376160f510 100644 (file)
@@ -34,6 +34,8 @@ CoinsInfo* Coins::coin(int index)
 
 void Coins::refresh()
 {
+    qDebug() << Q_FUNC_INFO;
+
     emit refreshStarted();
 
     boost::shared_lock<boost::shared_mutex> transfers_lock(m_wallet2->m_transfers_mutex);
index 064352d0d17cc3f760f1c3ab0e39021db9be859b..7102f3ee56ad4670e9fcb6f871882ce16378789f 100644 (file)
@@ -64,6 +64,8 @@ TransactionRow* TransactionHistory::transaction(int index)
 
 void TransactionHistory::refresh()
 {
+    qDebug() << Q_FUNC_INFO;
+
     QDateTime firstDateTime = QDate(2014, 4, 18).startOfDay();
     QDateTime lastDateTime  = QDateTime::currentDateTime().addDays(1); // tomorrow (guard against jitter and timezones)
 
@@ -281,12 +283,14 @@ void TransactionHistory::refresh()
 void TransactionHistory::setTxNote(const QString &txid, const QString &note)
 {
     cryptonote::blobdata txid_data;
-    if(!epee::string_tools::parse_hexstr_to_binbuff(txid.toStdString(), txid_data) || txid_data.size() != sizeof(crypto::hash))
+    if (!epee::string_tools::parse_hexstr_to_binbuff(txid.toStdString(), txid_data) || txid_data.size() != sizeof(crypto::hash)) {
+        qDebug() << Q_FUNC_INFO << "invalid txid";
         return;
+    }
+
     const crypto::hash htxid = *reinterpret_cast<const crypto::hash*>(txid_data.data());
 
     m_wallet2->set_tx_note(htxid, note.toStdString());
-    refresh();
     emit txNoteChanged();
 }
 
@@ -401,4 +405,102 @@ bool TransactionHistory::writeCSV(const QString &path) {
 
     data = QString("blockHeight,timestamp,date,accountIndex,direction,balanceDelta,amount,fee,txid,description,paymentId,fiatAmount,fiatCurrency%1").arg(data);
     return Utils::fileWrite(path, data);
-}
\ No newline at end of file
+}
+
+QStringList parseCSVLine(const QString &line) {
+    QStringList result;
+    QString currentField;
+    bool inQuotes = false;
+
+    for (int i = 0; i < line.length(); ++i) {
+        QChar currentChar = line[i];
+
+        if (currentChar == '"') {
+            if (inQuotes && i + 1 < line.length() && line[i + 1] == '"') {
+                currentField.append('"');
+                ++i;
+            } else {
+                inQuotes = !inQuotes;
+            }
+        } else if (currentChar == ',' && !inQuotes) {
+            result.append(currentField.trimmed());
+            currentField.clear();
+        } else {
+            currentField.append(currentChar);
+        }
+    }
+
+    result.append(currentField.trimmed());
+    return result;
+}
+
+QString TransactionHistory::importLabelsFromCSV(const QString &fileName) {
+    QFile file(fileName);
+
+    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        return QString("Could not open file: %1").arg(fileName);
+    }
+
+    QTextStream in(&file);
+
+    QList<QStringList> fields;
+    while (!in.atEnd()) {
+        QString line = in.readLine();
+        fields.append(parseCSVLine(line));
+    }
+
+    if (fields.empty()) {
+        return "CSV file appears to be empty";
+    }
+
+    qint64 txidField = -1;
+    qint64 descriptionField = -1;
+
+    QStringList header = fields[0];
+    for (int i = 0; i < header.length(); i++) {
+        if (header[i] == "txid") {
+            txidField = i;
+            continue;
+        }
+        if (header[i] == "description") {
+            descriptionField = i;
+        }
+    }
+
+    if (txidField < 0) {
+        return "'txid' field not found in CSV header";
+    }
+    if (descriptionField < 0) {
+        return "'description' field not found in CSV header";
+    }
+    qint64 maxIndex = std::max(txidField, descriptionField);
+
+    QList<QPair<QString, QString>> descriptions;
+
+    for (int i = 1; i < fields.length(); i++) {
+        const auto& row = fields[i];
+        if (maxIndex >= row.length()) {
+            qDebug() << "Row with invalid length in CSV";
+            continue;
+        }
+
+        if (row[txidField].isEmpty()) {
+            continue;
+        }
+
+        if (row[descriptionField].isEmpty()) {
+            continue;
+        }
+
+        descriptions.push_back({row[txidField], row[descriptionField]});
+    }
+
+    for (const auto& description : descriptions) {
+        qDebug() << "Setting note for tx:" << description.first << "description:" << description.second;
+        this->setTxNote(description.first, description.second);
+    }
+
+    this->refresh();
+
+    return {};
+}
index ce10b8e7dd7452d21edbcc9889e5b4c1b1e651ce..7b0d5b4fd505b5df92c6b4b50097aa29ffb1e126 100644 (file)
@@ -34,7 +34,6 @@ public:
     TransactionRow* transaction(int index);
     void refresh();
     void setTxNote(const QString &txid, const QString &note);
-    bool writeCSV(const QString &path);
     quint64 count() const;
     QDateTime firstDateTime() const;
     QDateTime lastDateTime() const;
@@ -42,6 +41,9 @@ public:
     bool locked() const;
     void clearRows();
 
+    bool writeCSV(const QString &path);
+    QString importLabelsFromCSV(const QString &fileName);
+
 signals:
     void refreshStarted() const;
     void refreshFinished() const;
index 770718a083d66d812d6fe2c290c014f040c06572..ce5f4946b294e0aab703abc13278d0d3d9800a77 100644 (file)
@@ -74,10 +74,6 @@ Wallet::Wallet(Monero::Wallet *wallet, QObject *parent)
         this->updateBalance();
     }
 
-    connect(this->history(), &TransactionHistory::txNoteChanged, [this]{
-        this->history()->refresh();
-    });
-
     connect(this, &Wallet::refreshed, this, &Wallet::onRefreshed);
     connect(this, &Wallet::newBlock, this, &Wallet::onNewBlock);
     connect(this, &Wallet::updated, this, &Wallet::onUpdated);
index 932b43d3d9fadd14dcdae0362a0d777c08e9feef..5e87563124fbad439af113bb69af0ae75e372afd 100644 (file)
@@ -236,6 +236,8 @@ bool TransactionHistoryModel::setData(const QModelIndex &index, const QVariant &
                     hash = tInfo.hash();
                 });
                 m_transactionHistory->setTxNote(hash, value.toString());
+                m_transactionHistory->refresh();
+                emit transactionDescriptionChanged();
                 break;
             }
             default:
index d84a6d75cfbd98553fa21283b44b13d733c6a7e2..38f467d24bca521d1b2f1c7592eef4d79e607f75 100644 (file)
@@ -45,6 +45,7 @@ public:
 
 signals:
     void transactionHistoryChanged();
+    void transactionDescriptionChanged();
 
 private:
     QVariant parseTransactionInfo(const TransactionRow &tInfo, int column, int role) const;