Grouptabs – Export as CSV

One advantage of using the same software again and again is that the process and toolsets are already established and proven. Grouptabs uses CouchDB as its backend, a database that I have been using for various purposes for a long time. I have toolset and process for CouchDB.

In this case, I wanted a way to download or export all the transactions in a tab/group such as CSV. Our flow here involves utilizing CouchDB's REST APIs to retrieve the data, formatting it in Javascript, and then downloading it as a CSV. We do this using Greasemonkey/Violentmonkey—yet another familiar tool.

We use AJAX to call CouchDBs _all_docs API endpoint, and we will get the complete document.

function grouptabs() {
//    GM.notification("grouptabs", "start");
    var url = document.URL;
    var extractedValue = url.split('/').pop();
//    GM.notification("grouptabs",extractedValue);
    var db_path ="https://backend.grouptabs.net/tab%2F"+extractedValue+"/_all_docs?include_docs=true"
    var csv_file = extractedValue +".csv"
    var xhr = new XMLHttpRequest();
    xhr.open("GET",db_path, true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            var jsonResponse = JSON.parse(xhr.responseText);
            var csvData = jsonToCsv(jsonResponse.rows);
            download(csvData,csv_file, "text/csv");
        }
    };
    xhr.send();
}

Grouptabs has two types of documents defined by an attribute type; it can have two values: info or transaction. We are interested in transaction types of documents. Then, we convert the participants into rows (see getParticipantsData function) and create a CSV.

function getParticipantColumns(rows) {
    const participantColumns = new Set();
    rows.forEach(row => {
        row.doc.participants.forEach(p => participantColumns.add(p.participant));
    });
    return Array.from(participantColumns);
}

  function getParticipantsData(participants, participantColumns) {
    const participantData = {};
    participantColumns.forEach(col => {
        participantData[col] = 0; // Initialize all columns with 0
    });
    participants.forEach(p => {
        if (participantData.hasOwnProperty(p.participant)) {
            participantData[p.participant] = p.amount;
        }
    });
    return participantColumns.map(col => participantData[col]);
}

function jsonToCsv(rows) {
      // Filter only "transaction" objects
      rows = rows.filter(row => row.doc.type === "transaction");

      // Extract columns from the first row
      const columns = ["_id", "type", "timestamp", "transactionType", "description", "date"];
      const csvRows = [];

      const participantColumns = getParticipantColumns(rows);

      // Add header
      columns.push(...participantColumns);
      csvRows.push(columns.join(","));

      // Add rows
      rows.forEach(row => {
          const doc = row.doc;
          const participantsData = getParticipantsData(doc.participants, participantColumns);
          const values = [
              doc._id,
              doc.type,
              doc.timestamp,
              doc.transactionType,
              doc.description,
              doc.date,
              ...participantsData
          ];
          csvRows.push(values.join(","));
      });

      return csvRows.join("\n");
}

Then, we form a blob and download it.

function download(data, filename, type) {
    var file = new Blob([data], { type: type });
    var a = document.createElement("a"),
        url = URL.createObjectURL(file);
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    setTimeout(function() {
        document.body.removeChild(a);
        window.URL.revokeObjectURL(url);
    }, 0);
}

All of this is enabled only for the Grouptabs URL using Greasemonkey @match. And that's it. Our user script is ready to be used. You can import from here and use it.


You can read this blog using RSS Feed. But if you are the person who loves getting emails, then you can join my readers by signing up.

Join 2,237 other subscribers

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.