Skip to content
This repository was archived by the owner on May 16, 2019. It is now read-only.

Commit d5fa98f

Browse files
committed
Added a backup tool to import/export store data
1 parent 7d2814b commit d5fa98f

File tree

2 files changed

+167
-0
lines changed

2 files changed

+167
-0
lines changed

api/restapi.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
__author__ = 'chris'
2+
import backupTool
23
import json
34
import os
45
from txrestapi.resource import APIResource
@@ -601,3 +602,25 @@ def upload_image(self, request):
601602
request.write(json.dumps({"success": False, "reason": e.message}, indent=4))
602603
request.finish()
603604
return server.NOT_DONE_YET
605+
606+
@POST('^/api/v1/backup_files')
607+
def backup_files(self, request):
608+
output = request.args["output"][0]
609+
return backupTool.backupFiles(output)
610+
611+
@POST('^/api/v1/export_database')
612+
def export_database(self, request):
613+
tables_and_columns = request.args["tables_and_columns"][0]
614+
remove_previous = request.args["remove_previous"][0]
615+
return backupTool.exportDatabase(tables_and_columns, remove_previous)
616+
617+
@POST('^/api/v1/restore_files')
618+
def restore_files(self, request):
619+
input = request.args["input"][0]
620+
remove_previous_database_files = request.args["remove_previous_database_files"][0]
621+
return backupTool.restoreFiles(input, remove_previous_database_files)
622+
623+
@POST('^/api/v1/import_database')
624+
def import_database(self, request):
625+
remove_previous = request.args["remove_previous"][0]
626+
return backupTool.importDatabase(remove_previous)

backupTool.py

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
__author__ = 'marc'
2+
from constants import DATA_FOLDER
3+
import csv
4+
import db.datastore as db
5+
import errno
6+
import os
7+
import re
8+
import shutil
9+
import sqlite3 as lite
10+
import tarfile
11+
import time
12+
13+
_TABLES = {
14+
'hashmap': ['hash', 'filepath'],
15+
'profile': ['id', 'serializedUserInfo'],
16+
'listings': ['id', 'serializedListings'],
17+
'keys': ['type', 'privkey', 'pubkey'],
18+
'followers': ['id', 'serializedFollowers'],
19+
'following': ['id', 'serializedFollowing'],
20+
'messages': ['guid', 'handle', 'signed_pubkey', 'encryption_pubkey', 'subject', 'message_type', 'message', 'timestamp', 'avatar_hash', 'signature', 'outgoing'],
21+
'notifications': ['guid', 'handle', 'message', 'timestamp', 'avatar_hash'],
22+
'vendors': ['guid', 'ip', 'port', 'signedPubkey'],
23+
'moderators': ['guid', 'signedPubkey', 'encryptionKey', 'encryptionSignature', 'bitcoinKey', 'bitcoinSignature', 'handle'],
24+
'purchases': ['id', 'title', 'timestamp', 'btc', 'address', 'status', 'thumbnail', 'seller', 'proofSig'],
25+
'sales': ['id', 'title', 'timestamp', 'btc', 'address', 'status', 'thumbnail', 'seller'],
26+
'dht': ['keyword', 'id', 'value', 'birthday']
27+
}
28+
29+
def _getDatabase():
30+
"""Retrieves the OpenBazaar database file."""
31+
Database = db.Database()
32+
return Database.DATABASE
33+
34+
def silentRemove(filename):
35+
"""Silently removes a file if it exists."""
36+
try:
37+
os.remove(filename)
38+
except OSError as e:
39+
if e.errno != errno.ENOENT: # ENOENT: no such file or directory
40+
raise
41+
42+
def _exportDatabaseToCsv(tablesAndColumns):
43+
"""Reads the database for all given tables and stores them as CSV files."""
44+
dbFile = _getDatabase()
45+
result = None
46+
with lite.connect(dbFile) as dbConnection:
47+
dbConnection.text_factory = str
48+
cursor = dbConnection.cursor()
49+
for table in tablesAndColumns:
50+
table_name = table[0]
51+
table_columns = ', '.join(table[1])
52+
data = cursor.execute("SELECT {0} FROM {1}".format(table_columns, table_name))
53+
fileName = 'table_{0}.csv'.format(table_name)
54+
filePath = os.path.join('backup', fileName)
55+
with open(filePath, 'wb') as f:
56+
writer = csv.writer(f)
57+
writer.writerow(table[1])
58+
writer.writerows(data)
59+
return result
60+
61+
def backupFiles(output=None):
62+
"""Archives OpenBazaar files in a single tar archive."""
63+
os.chdir(DATA_FOLDER)
64+
65+
# Archive files
66+
files = os.listdir(DATA_FOLDER)
67+
if not output:
68+
output = 'backup_{0}.tar.gz'.format(time.strftime('%Y-%m-%d'))
69+
silentRemove(output)
70+
with tarfile.open(output, 'w:gz') as tar:
71+
for f in files:
72+
tar.add(f)
73+
tar.close()
74+
return 'Success'
75+
76+
def exportDatabase(tableList, removePrevious=False):
77+
"""Exports given tables to the OpenBazaar folder."""
78+
# Parse table list
79+
tableList = tableList.replace(' ', '').split(',')
80+
tablesAndColumns = []
81+
for table in tableList:
82+
if table in _TABLES:
83+
tablesAndColumns.append((table, _TABLES[table]))
84+
else:
85+
return 'ERROR, Table not found: {0}'.format(table)
86+
87+
# Remove existing database files and re-make them
88+
if removePrevious and os.path.exists('backup'):
89+
shutil.rmtree('backup')
90+
if not os.path.exists('backup'):
91+
os.makedirs('backup')
92+
_exportDatabaseToCsv(tablesAndColumns)
93+
return 'Success'
94+
95+
def _importCsvToTable(fileName, deleteDataFirst=False):
96+
"""Imports given CSV file to the database."""
97+
tableName = re.search('table_(\w+).csv', fileName).group(1)
98+
dbFile = _getDatabase()
99+
with lite.connect(dbFile) as dbConnection:
100+
dbConnection.text_factory = str
101+
cursor = dbConnection.cursor()
102+
if deleteDataFirst:
103+
cursor.execute('DELETE FROM {0}'.format(tableName))
104+
with open(fileName, 'rb') as f:
105+
reader = csv.reader(f)
106+
header = True
107+
for row in reader:
108+
if header:
109+
header = False
110+
columns = ', '.join(['?' for column in row])
111+
insertsql = 'INSERT INTO {0} VALUES ({1})'.format(tableName, columns)
112+
rowlen = len(row)
113+
else:
114+
if len(row) == rowlen:
115+
cursor.execute(insertsql, row)
116+
117+
118+
def restoreFiles(input, removePreviousDatabaseFiles=False):
119+
"""Restores files of given archive to OpenBazaar folder."""
120+
if not input:
121+
return 'Input path is needed'
122+
os.chdir(DATA_FOLDER)
123+
124+
# Remove existing database files if any
125+
if removePreviousDatabaseFiles and os.path.exists('backup'):
126+
shutil.rmtree('backup')
127+
128+
# Unarchive files
129+
with tarfile.open(input, 'r:gz') as tar:
130+
tar.extractall()
131+
132+
return 'Success'
133+
134+
if __name__ == '__main__':
135+
print 'Backup tool works as a library.'
136+
137+
def importDatabase(deletePreviousData=False):
138+
"""Imports table files from the OpenBazaar folder."""
139+
# Restore database files to the database
140+
if os.path.exists('backup'):
141+
files = ['backup/{0}'.format(f) for f in os.listdir('backup')]
142+
for f in files:
143+
_importCsvToTable(f, deletePreviousData)
144+
return 'Success'

0 commit comments

Comments
 (0)