Skip to content

Instantly share code, notes, and snippets.

@d-gubert
Created February 16, 2016 00:08
Show Gist options
  • Save d-gubert/4f54d8c8a155c1fedf51 to your computer and use it in GitHub Desktop.
Save d-gubert/4f54d8c8a155c1fedf51 to your computer and use it in GitHub Desktop.
A script to extract your data from NuBank to a .csv file
(function() {
'use strict';
class NuBankExtractor {
constructor() {
this.transactionsByDate = new Map();
this.maxDate = new Date();
}
getDataFrom(link, promiseMethods, method) {
let xhr = new XMLHttpRequest;
if (method === undefined) method = 'GET';
xhr.open(method, link, true);
xhr.setRequestHeader('Authorization', "Bearer %bearer_token%");
if (typeof promiseMethods === 'object' && typeof promiseMethods.resolve === 'function' && typeof promiseMethods.reject === 'function') {
xhr.onload = function() {
if (xhr.status === 200 && xhr.response.length > 0) {
promiseMethods.resolve(JSON.parse(xhr.response));
} else {
promiseMethods.reject('Unexpected error');
}
}
xhr.onerror = function() {
promiseMethods.reject('Network error');
}
}
xhr.send();
return xhr;
}
requestCustomerId() {
console.info("Querying customer information...");
// Some problems with the strict mode's scope made me use this closure to pass variables around...
return new Promise((function(self) {return function(resolve, reject) {
self.getDataFrom('https://prod-customers.nubank.com.br/api/customers', {resolve: resolve, reject: reject});
}})(this));
}
requestEventList(customer) {
console.info("Querying events...");
// Some problems with the strict mode's scope made me use this closure to pass variables around...
return new Promise((function(self, customerId) {return function(resolve, reject) {
self.getDataFrom('https://prod-notification.nubank.com.br/api/contacts/'+customerId+'/feed', {resolve: resolve, reject: reject});
}})(this, customer.id));
}
ensureTransactionDateExists(date) {
if (!this.transactionsByDate.has(date.getTime())) this.transactionsByDate.set(date.getTime(), []);
}
processEventList(eventList) {
console.info("Processing events...");
for (var event of eventList) {
if (event.category !== 'transaction') continue;
let transactionDate = new Date(event.time.substring(0,10)); //Gets only the date portion
if (event.details.charges === undefined) {
this.processSinglePurchase(event, transactionDate);
} else {
this.processInstallmentPurchase(event, transactionDate);
}
}
console.log(this.transactionsByDate);
this.generateReport();
}
processInstallmentPurchase(transaction, transactionDate) {
transaction.amount = transaction.details.charges.amount;
for (let i = 1, installmentDate = new Date(transactionDate), transactionDescription = transaction.description;
i <= transaction.details.charges.count; i++) {
installmentDate.setMonth(transactionDate.getMonth() + i - 1);
if (installmentDate > this.maxDate) break;
transaction.description = transactionDescription + " (Installment #" + i + ")";
this.processSinglePurchase(transaction, installmentDate);
}
}
processSinglePurchase(transaction, transactionDate) {
this.ensureTransactionDateExists(transactionDate);
this.transactionsByDate.get(transactionDate.getTime()).push({
description: transaction.description,
value: transaction.amount / 100,
category: transaction.title,
datetime: new Date(transaction.time),
nubankId: transaction.id
});
}
generateReport() {
let report = 'nubankId,description,value,category,datetime';
this.transactionsByDate.forEach(function(transactionList) {
for (let transaction of transactionList) {
report += "\n" +
transaction.nubankId + "," +
'"' + transaction.description + '",' +
transaction.value + "," +
transaction.category + "," +
'"' + transaction.datetime.toLocaleString() + '"';
}
});
this.downloadReport(report);
}
downloadReport(report) {
let anchor = document.createElement('a');
anchor.setAttribute("href", "data:attachment/csv," + encodeURIComponent(report));
anchor.setAttribute("download", "extrato_nubank_" + (new Date()).toLocaleDateString().replace(/\//g,'-') + ".csv");
anchor.click();
}
run() {
// Some problems with the strict mode's scope made me use this closure to pass variables around...
this.requestCustomerId().then((function(self) {
return function(response) {
console.log(response.customer);
return self.requestEventList(response.customer);
}
})(this)).then((function(self) {
return function(response) {
return self.processEventList(response.events);
}
})(this));
}
};
let app = new NuBankExtractor;
app.run();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment