-
-
Save 0x8801/9454490b22b3a5135fbccfe30e1ded08 to your computer and use it in GitHub Desktop.
// Source for Jake Archibald's idb https://github.com/jakearchibald/idb/blob/v3.0.2/build/idb.js | |
(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | |
typeof define === 'function' && define.amd ? define(['exports'], factory) : | |
(global = global || self, factory(global.idb = {})); | |
}(this, function (exports) { 'use strict'; | |
function toArray(arr) { | |
return Array.prototype.slice.call(arr); | |
} | |
function promisifyRequest(request) { | |
return new Promise(function(resolve, reject) { | |
request.onsuccess = function() { | |
resolve(request.result); | |
}; | |
request.onerror = function() { | |
reject(request.error); | |
}; | |
}); | |
} | |
function promisifyRequestCall(obj, method, args) { | |
var request; | |
var p = new Promise(function(resolve, reject) { | |
request = obj[method].apply(obj, args); | |
promisifyRequest(request).then(resolve, reject); | |
}); | |
p.request = request; | |
return p; | |
} | |
function promisifyCursorRequestCall(obj, method, args) { | |
var p = promisifyRequestCall(obj, method, args); | |
return p.then(function(value) { | |
if (!value) return; | |
return new Cursor(value, p.request); | |
}); | |
} | |
function proxyProperties(ProxyClass, targetProp, properties) { | |
properties.forEach(function(prop) { | |
Object.defineProperty(ProxyClass.prototype, prop, { | |
get: function() { | |
return this[targetProp][prop]; | |
}, | |
set: function(val) { | |
this[targetProp][prop] = val; | |
} | |
}); | |
}); | |
} | |
function proxyRequestMethods(ProxyClass, targetProp, Constructor, properties) { | |
properties.forEach(function(prop) { | |
if (!(prop in Constructor.prototype)) return; | |
ProxyClass.prototype[prop] = function() { | |
return promisifyRequestCall(this[targetProp], prop, arguments); | |
}; | |
}); | |
} | |
function proxyMethods(ProxyClass, targetProp, Constructor, properties) { | |
properties.forEach(function(prop) { | |
if (!(prop in Constructor.prototype)) return; | |
ProxyClass.prototype[prop] = function() { | |
return this[targetProp][prop].apply(this[targetProp], arguments); | |
}; | |
}); | |
} | |
function proxyCursorRequestMethods(ProxyClass, targetProp, Constructor, properties) { | |
properties.forEach(function(prop) { | |
if (!(prop in Constructor.prototype)) return; | |
ProxyClass.prototype[prop] = function() { | |
return promisifyCursorRequestCall(this[targetProp], prop, arguments); | |
}; | |
}); | |
} | |
function Index(index) { | |
this._index = index; | |
} | |
proxyProperties(Index, '_index', [ | |
'name', | |
'keyPath', | |
'multiEntry', | |
'unique' | |
]); | |
proxyRequestMethods(Index, '_index', IDBIndex, [ | |
'get', | |
'getKey', | |
'getAll', | |
'getAllKeys', | |
'count' | |
]); | |
proxyCursorRequestMethods(Index, '_index', IDBIndex, [ | |
'openCursor', | |
'openKeyCursor' | |
]); | |
function Cursor(cursor, request) { | |
this._cursor = cursor; | |
this._request = request; | |
} | |
proxyProperties(Cursor, '_cursor', [ | |
'direction', | |
'key', | |
'primaryKey', | |
'value' | |
]); | |
proxyRequestMethods(Cursor, '_cursor', IDBCursor, [ | |
'update', | |
'delete' | |
]); | |
// proxy 'next' methods | |
['advance', 'continue', 'continuePrimaryKey'].forEach(function(methodName) { | |
if (!(methodName in IDBCursor.prototype)) return; | |
Cursor.prototype[methodName] = function() { | |
var cursor = this; | |
var args = arguments; | |
return Promise.resolve().then(function() { | |
cursor._cursor[methodName].apply(cursor._cursor, args); | |
return promisifyRequest(cursor._request).then(function(value) { | |
if (!value) return; | |
return new Cursor(value, cursor._request); | |
}); | |
}); | |
}; | |
}); | |
function ObjectStore(store) { | |
this._store = store; | |
} | |
ObjectStore.prototype.createIndex = function() { | |
return new Index(this._store.createIndex.apply(this._store, arguments)); | |
}; | |
ObjectStore.prototype.index = function() { | |
return new Index(this._store.index.apply(this._store, arguments)); | |
}; | |
proxyProperties(ObjectStore, '_store', [ | |
'name', | |
'keyPath', | |
'indexNames', | |
'autoIncrement' | |
]); | |
proxyRequestMethods(ObjectStore, '_store', IDBObjectStore, [ | |
'put', | |
'add', | |
'delete', | |
'clear', | |
'get', | |
'getAll', | |
'getKey', | |
'getAllKeys', | |
'count' | |
]); | |
proxyCursorRequestMethods(ObjectStore, '_store', IDBObjectStore, [ | |
'openCursor', | |
'openKeyCursor' | |
]); | |
proxyMethods(ObjectStore, '_store', IDBObjectStore, [ | |
'deleteIndex' | |
]); | |
function Transaction(idbTransaction) { | |
this._tx = idbTransaction; | |
this.complete = new Promise(function(resolve, reject) { | |
idbTransaction.oncomplete = function() { | |
resolve(); | |
}; | |
idbTransaction.onerror = function() { | |
reject(idbTransaction.error); | |
}; | |
idbTransaction.onabort = function() { | |
reject(idbTransaction.error); | |
}; | |
}); | |
} | |
Transaction.prototype.objectStore = function() { | |
return new ObjectStore(this._tx.objectStore.apply(this._tx, arguments)); | |
}; | |
proxyProperties(Transaction, '_tx', [ | |
'objectStoreNames', | |
'mode' | |
]); | |
proxyMethods(Transaction, '_tx', IDBTransaction, [ | |
'abort' | |
]); | |
function UpgradeDB(db, oldVersion, transaction) { | |
this._db = db; | |
this.oldVersion = oldVersion; | |
this.transaction = new Transaction(transaction); | |
} | |
UpgradeDB.prototype.createObjectStore = function() { | |
return new ObjectStore(this._db.createObjectStore.apply(this._db, arguments)); | |
}; | |
proxyProperties(UpgradeDB, '_db', [ | |
'name', | |
'version', | |
'objectStoreNames' | |
]); | |
proxyMethods(UpgradeDB, '_db', IDBDatabase, [ | |
'deleteObjectStore', | |
'close' | |
]); | |
function DB(db) { | |
this._db = db; | |
} | |
DB.prototype.transaction = function() { | |
return new Transaction(this._db.transaction.apply(this._db, arguments)); | |
}; | |
proxyProperties(DB, '_db', [ | |
'name', | |
'version', | |
'objectStoreNames' | |
]); | |
proxyMethods(DB, '_db', IDBDatabase, [ | |
'close' | |
]); | |
// Add cursor iterators | |
// TODO: remove this once browsers do the right thing with promises | |
['openCursor', 'openKeyCursor'].forEach(function(funcName) { | |
[ObjectStore, Index].forEach(function(Constructor) { | |
// Don't create iterateKeyCursor if openKeyCursor doesn't exist. | |
if (!(funcName in Constructor.prototype)) return; | |
Constructor.prototype[funcName.replace('open', 'iterate')] = function() { | |
var args = toArray(arguments); | |
var callback = args[args.length - 1]; | |
var nativeObject = this._store || this._index; | |
var request = nativeObject[funcName].apply(nativeObject, args.slice(0, -1)); | |
request.onsuccess = function() { | |
callback(request.result); | |
}; | |
}; | |
}); | |
}); | |
// polyfill getAll | |
[Index, ObjectStore].forEach(function(Constructor) { | |
if (Constructor.prototype.getAll) return; | |
Constructor.prototype.getAll = function(query, count) { | |
var instance = this; | |
var items = []; | |
return new Promise(function(resolve) { | |
instance.iterateCursor(query, function(cursor) { | |
if (!cursor) { | |
resolve(items); | |
return; | |
} | |
items.push(cursor.value); | |
if (count !== undefined && items.length == count) { | |
resolve(items); | |
return; | |
} | |
cursor.continue(); | |
}); | |
}); | |
}; | |
}); | |
function openDb(name, version, upgradeCallback) { | |
var p = promisifyRequestCall(indexedDB, 'open', [name, version]); | |
var request = p.request; | |
if (request) { | |
request.onupgradeneeded = function(event) { | |
if (upgradeCallback) { | |
upgradeCallback(new UpgradeDB(request.result, event.oldVersion, request.transaction)); | |
} | |
}; | |
} | |
return p.then(function(db) { | |
return new DB(db); | |
}); | |
} | |
function deleteDb(name) { | |
return promisifyRequestCall(indexedDB, 'deleteDatabase', [name]); | |
} | |
exports.openDb = openDb; | |
exports.deleteDb = deleteDb; | |
Object.defineProperty(exports, '__esModule', { value: true }); | |
})); | |
// Get list of tables in DB | |
let dbs = await window.indexedDB.databases(), | |
db = dbs[0], // we are exporting the 1st db, change this as per your needs | |
dbPromise = await idb.openDb(db.name, db.version), | |
tableNames = [].slice.apply(dbPromise.objectStoreNames); | |
// Get content from DBs | |
let data = await Promise.all(tableNames.map(async (tableName) => { | |
return { | |
[tableName]: await dbPromise.transaction(tableName).objectStore(tableName).getAll() | |
} | |
})); | |
// wallet.budgetbakers.com records are stored here | |
let sequence = data[2]['by-sequence']; | |
// group all sequences by type of record. eg are 'account', 'category', 'record', 'currency', etc | |
// similar to _.groupBy(sequence, 'reservedModelType') | |
let groupedSequence = sequence.reduce((group, item) => { | |
if (!group[item.reservedModelType]) | |
group[item.reservedModelType] = []; | |
group[item.reservedModelType].push(item) | |
return group; | |
}, {}) | |
// Convert the category array into a map (key is category id and value is category item itself) | |
groupedSequence.categoryMap = groupedSequence.Category.reduce((group, item) => { | |
let categoryId = item._doc_id_rev.split('::')[0]; | |
group[categoryId] = item; | |
return group; | |
}, {}) | |
// Sort the records by date | |
groupedSequence.Record = groupedSequence.Record.sort((a, b) => { | |
return a.reservedCreatedAt.localeCompare(b.reservedCreatedAt) | |
}) | |
function mapRecordWithCategoryAndAccount(item) { | |
let amountSign = item.type == 0 ? 1 : -1; | |
let account = groupedSequence.Account.find(function(acc) { | |
return acc._doc_id_rev.indexOf(item.accountId) > -1; | |
}); | |
return { | |
title: item.note ? item.note : groupedSequence.categoryMap[item.categoryId].name, | |
date: item.recordDate, | |
category: groupedSequence.categoryMap[item.categoryId].name, | |
amount: amountSign * item.amount/100, | |
account: account ? account.name : 'Other' | |
} | |
} | |
let records = groupedSequence.Record.map(mapRecordWithCategoryAndAccount) | |
JSON.stringify(records) |
After copying it on the console, it shows "undefined".
Please help!
@Nehull do JSON.stringify(records)
Thank you. Is there a way I can export it as an XLS file?
@Nehull You can use some online service that can convert JSON to XLS.
some examples I just googled:
https://www.aconvert.com/document/json-to-xls/
https://json-csv.com/ (you can open csv in Excel)
@Nehull Also if you want to download the JSON as file you can use this
function downloadObjectAsJson(exportObj, exportName){
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj));
var downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", exportName + ".json");
document.body.appendChild(downloadAnchorNode); // required for firefox
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
downloadObjectAsJson(records, `budgetbaker-records-${+new Date()}`)
Function from https://stackoverflow.com/questions/19721439/download-json-object-as-a-file-from-browser
Have you guys figured out how to get the attachments out too?
works really good! but can you make it include the category as well for e.g food and dinks or transportation.
also btw if anyone is wondering why the hour of the time isn't correct, it's because the data is being exported in gmt +0 and your device shows according to your timezone
This is great code, however it needs to account for deleted rows, which are still in the database. The fix is replacing the "let records =" line at the bottom with:
function excludeDeleted(item) { return item._deleted !== true; }
let records = groupedSequence.Record.filter(excludeDeleted).map(mapRecordWithCategoryAndAccount);
Log in to web.budgetbakers.com and run this on the console.
The methods are extended from @harryi3t's gist here https://gist.github.com/harryi3t/8c439df8cee752f107824964a0d005fc