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

Commit 2fea134

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

File tree

2 files changed

+171
-0
lines changed

2 files changed

+171
-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: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
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+
os.chdir(DATA_FOLDER)
79+
80+
# Parse table list
81+
tableList = tableList.replace(' ', '').split(',')
82+
tablesAndColumns = []
83+
for table in tableList:
84+
if table in _TABLES:
85+
tablesAndColumns.append((table, _TABLES[table]))
86+
else:
87+
return 'ERROR, Table not found: {0}'.format(table)
88+
89+
# Remove existing database files and re-make them
90+
if removePrevious and os.path.exists('backup'):
91+
shutil.rmtree('backup')
92+
if not os.path.exists('backup'):
93+
os.makedirs('backup')
94+
_exportDatabaseToCsv(tablesAndColumns)
95+
return 'Success'
96+
97+
def _importCsvToTable(fileName, deleteDataFirst=False):
98+
"""Imports given CSV file to the database."""
99+
tableName = re.search(r'table_(\w+).csv', fileName).group(1)
100+
dbFile = _getDatabase()
101+
with lite.connect(dbFile) as dbConnection:
102+
dbConnection.text_factory = str
103+
cursor = dbConnection.cursor()
104+
if deleteDataFirst:
105+
cursor.execute('DELETE FROM {0}'.format(tableName))
106+
with open(fileName, 'rb') as f:
107+
reader = csv.reader(f)
108+
header = True
109+
for row in reader:
110+
if header:
111+
header = False
112+
columns = ', '.join(['?' for _ in row])
113+
insertsql = 'INSERT INTO {0} VALUES ({1})'.format(tableName, columns)
114+
rowlen = len(row)
115+
else:
116+
if len(row) == rowlen:
117+
cursor.execute(insertsql, row)
118+
119+
120+
def restoreFiles(inputFile, removePreviousDatabaseFiles=False):
121+
"""Restores files of given archive to OpenBazaar folder."""
122+
if not inputFile:
123+
return 'Input path is needed'
124+
os.chdir(DATA_FOLDER)
125+
126+
# Remove existing database files if any
127+
if removePreviousDatabaseFiles and os.path.exists('backup'):
128+
shutil.rmtree('backup')
129+
130+
# Unarchive files
131+
with tarfile.open(inputFile, 'r:gz') as tar:
132+
tar.extractall()
133+
134+
return 'Success'
135+
136+
if __name__ == '__main__':
137+
print 'Backup tool works as a library.'
138+
139+
def importDatabase(deletePreviousData=False):
140+
"""Imports table files from the OpenBazaar folder."""
141+
os.chdir(DATA_FOLDER)
142+
143+
# Restore database files to the database
144+
if os.path.exists('backup'):
145+
files = ['backup/{0}'.format(f) for f in os.listdir('backup')]
146+
for f in files:
147+
_importCsvToTable(f, deletePreviousData)
148+
return 'Success'

0 commit comments

Comments
 (0)