-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDBConverter.py
More file actions
317 lines (250 loc) · 10.1 KB
/
DBConverter.py
File metadata and controls
317 lines (250 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
###################
# VERSION 1.03.04 #
###################
Version = "1.03.04"
upDate = "16 Apr 2026"
versionString = "Version " + Version + " - Last Updated " + upDate
###################
# LIBRARY IMPORTS #
###################
import os
from os import path
import sys
import sqlite3
import csv
from tkinter import *
from tkinter import filedialog
from tkinter import font
########################
# VARIABLE DEFINITIONS #
########################
extensionsAllowable = [".DB", ".db", ".db3", ".sqlite", ".sqlite3"] #List of common SQLite3 file extensions
filePath = FALSE
outputPath = FALSE
checkboxVars = []
########################
# FUNCTION DEFINITIONS #
########################
def resource_path(relative_path):
base_path = getattr(sys, '_MEIPASS', path.dirname(path.abspath(__file__)))
return path.join(base_path, relative_path)
#WRITES SELECTED DATABASE TABLE TO A CSV FILE
def subConvertDB(fullpath, dbFPath, tableName):
con = sqlite3.connect(dbFPath)
cur = con.cursor()
cols = cur.execute("PRAGMA table_info('%s')" % tableName).fetchall()
fileOpen = open(fullpath, 'w', newline='', encoding='utf-8')
writer = csv.writer(fileOpen, dialect=csv.excel, quoting=csv.QUOTE_ALL)
fieldNameRow = []
#DETERMINING FIELD TITLES
for col in cols:
col_name = col[1]
fieldNameRow.append(col_name)
writer.writerow(fieldNameRow)
#FETCHING DATA FROM DB TABLE
cur.execute("SELECT * FROM " + tableName + ";")
rows = cur.fetchall()
#WRITING TO CSV
for row in rows:
writer.writerow(row) # write data row
fileOpen.closed
#CHECKS WHICH TABLES ARE SELECTED AND LOOPS TO RUN CSV WRITE FUNCTION
def ConvertDB(dbFile, savePath):
#CHECKS STATE OF TABLE CHECKLIST CHECKBOXES
readStates()
#QUERIES SQLITE3 DATABASE
con = sqlite3.connect(filePath)
cur = con.cursor()
cur.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = cur.fetchall()
tableNames = [table[0] for table in tables]
#LOOPS subConvertDB FOR EACH SELECTED TABLE
count = 0
for table in tables:
tableName = tableNames[count]
path_f = os.path.join(savePath, tableName + ".csv")
if tickStates[count] == True:
subConvertDB(path_f, dbFile, tableName)
count += 1
#OPENS FILE EXPLORER DIALOGUE TO SELECT AN SQLITE3 DATABASE
def openFile():
#OPENING FILE DIALOG
global filePath
filePath = filedialog.askopenfilename(title="Open an SQLite3 Database file",
filetypes=(("SQLite3 Database File", tuple(extensionsAllowable)), ("all files", "*.*")))
#CLEARING EXISTING FILE SELECTION
mainTextInput.configure(state="normal")
mainTextInput.delete(1.0, END)
con = sqlite3.connect(filePath)
cur = con.cursor()
#CHECKS VALIDITY OF SELECTED DATABASE
validDatabase = True
try:
cur.execute("PRAGMA schema_version")
except sqlite3.DatabaseError as e:
validDatabase = False
mainLabelError.configure(text="Selected file is not a valid SQLite3 database.", fg='red')
#IF VALID SQLITE3 DATABASE QUERY TABLE AND CREATE CHECKLIST OF DATABASE TABLES. IF INVALID CLEAR CHECKLIST.
if validDatabase:
mainLabelError.configure(text="")
mainTextInput.insert(END, filePath)
#QUERYING DATABASE
cur.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = cur.fetchall()
global tableNames
tableNames=[table[0] for table in tables]
#CLEARING EXISTING CHECKBOXES AND VARIABLES
global checkboxVars
checkboxVars = []
rowPos = 2
for widget in checkFrame.winfo_children():
if isinstance(widget, Checkbutton):
widget.destroy()
#GENERATING CHECKLIST AND VARIABLES
for table in tables:
var = BooleanVar()
checkboxVars.append(var)
Checkbutton(checkFrame, text=table, variable = var).grid(row=rowPos, column=1, sticky="W")
rowPos += 1
updateScrollRegion()
checkButtonSelect.configure(state="normal")
else:
checkboxVars=[]
for widget in checkFrame.winfo_children():
if isinstance(widget, Checkbutton):
widget.destroy()
checkButtonSelect.configure(text="Select All", state="disabled")
mainTextInput.configure(state="disabled")
#OUTPUT DIRECTORY SELECTION DIALOG.
def saveLocation():
global outputPath
outputPath = filedialog.askdirectory(mustexist=TRUE)
#CLEARING ANY EXISTING DIRECTORY SELECTION
mainTextOutput.configure(state="normal")
mainTextOutput.delete(1.0, END)
#PRINTING SELECTED DIRECTORY IN TEXT BOX
if outputPath:
mainTextOutput.insert(END, outputPath)
mainTextOutput.configure(state="disabled")
#DETERMINES IF THE SELECTED FILE IS SUITABLE, RUNS CONVERSION, OUTPUTS COMPLETION OR ERROR MESSAGE
def conversionWindow():
mainLabelError.configure(text="")
if outputPath and filePath:
ConvertDB(filePath, outputPath)
mainLabelError.configure(text="The selected SQLite3 database table(s) have been successfully converted into CSV file(s)!",
fg='Dark Green')
mainButtonCancel.configure(text="Exit")
else:
mainLabelError.configure(text = "Please ensure a suitable file and directory have been selected.", fg='Red')
#IMMEDIATELY EXITS THE PROGRAM
def cancelProcess():
mainWindow.destroy()
#READS THE STATES OF THE TABLE CHECKLIST BUTTONS WRITES TO GLOBAL VARIABLE
def readStates():
global tickStates
tickStates = []
tickStates = [var.get() for var in checkboxVars]
#SETS ALL TABLE CHECKLIST BUTTONS TO TRUE, TOGGLES SELECT ALL TO UNSELECT ALL
def selectAll():
if checkboxVars:
[var.set(True) for var in checkboxVars]
checkButtonSelect.configure(text="Unselect All", command=unselectAll)
#SETS ALL TABLE CHECKLIST BUTTONS TO FALSE. TOGGLES UNSELECT ALL TO SELECT ALL
def unselectAll():
if checkboxVars:
[var.set(False) for var in checkboxVars]
checkButtonSelect.configure(text="Select All", command = selectAll)
#UPDATES SCROLLABLE REGION IN checkWindow
def updateScrollRegion():
checkContainer.update_idletasks()
checkContainer.config(scrollregion=checkFrame.bbox())
#CREATES A SCROLLABLE FRAME FOR checkWindow
def createScrollableContainer():
checkContainer.config(yscrollcommand=checkScrollbar.set, highlightthickness=0)
checkScrollbar.config(orient=VERTICAL, command=checkContainer.yview)
checkScrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
checkContainer.pack(fill=BOTH, side=LEFT, expand=TRUE)
checkContainer.create_window(0, 0, window=checkFrame, anchor=NW)
##################
# GUI GEOMETRIES #
##################
#DISPLAY DIMENSIONS
root = Tk()
screenWidth = root.winfo_screenwidth()
screenHeight = root.winfo_screenheight()
root.destroy()
#WINDOW GEOMETRIES
mainWidth = 1200
mainHeight = 300
checkWidth = 300
checkHeight = mainHeight
#WINDOW POSITIONING
mainCornerX = (screenWidth - mainWidth - checkWidth) / 2
mainCornerY = (screenHeight - mainHeight) / 2
checkCornerX = mainCornerX + mainWidth
checkCornerY = mainCornerY
#############
# MAIN MENU #
#############
#INITIALIZING AND CONFIGURING
mainWindow = Tk()
mainWindow.geometry('%dx%d+%d+%d' % (mainWidth, mainHeight, mainCornerX, mainCornerY))
mainWindow.resizable(False, False)
mainWindow.title("SQLite3 Database Conversion Tool")
icon = PhotoImage(file=resource_path('Icon.png'))
mainWindow.iconphoto(False, icon)
#DATABASE SELECTION WIDGETS
mainLabelInput = Label(mainWindow, text="Press the button below to select an SQLite3 database file")
mainTextInput = Text(mainWindow, width=135, height=1)
mainTextInput.configure(state="disabled")
mainButtonInput = Button(text="Open Database", command=openFile)
#OUTPUT DIRECTORY SELECTION WIDGETS
mainLabelOutput = Label(mainWindow, text="Press the button below to select a directory to save the *.csv outputs to")
mainTextOutput = Text(mainWindow, width=135, height=1)
mainTextOutput.configure(state="disabled")
mainButtonOutput = Button(text="Select Directory", command=saveLocation)
#CONFIRMATION AND CANCEL WIDGETS
mainButtonConfirm = Button(text="Confirm Selections", command=conversionWindow)
mainButtonCancel = Button(text="Cancel", command=cancelProcess, width=10)
mainLabelError=Label(mainWindow, text='', fg='black')
#VERSION LABEL
mainLabelVersion = Label(mainWindow, text=versionString, fg='gray')
#PACKING MAIN MENU
mainLabelInput.pack(pady=(5, 0))
mainTextInput.pack(pady=(5, 0))
mainButtonInput.pack(pady=(5, 0))
mainLabelOutput.pack(pady=(20, 0))
mainTextOutput.pack(pady=(5, 0))
mainButtonOutput.pack(pady=(5,0))
mainButtonConfirm.pack(pady=(15, 0))
mainButtonCancel.pack(pady=(5, 0))
mainLabelError.pack(pady=(10,0))
mainLabelVersion.pack(anchor='sw', padx = 5)
########################
# TABLE SELECTION MENU #
########################
#INITIALIZING AND CONFIGURING
checkWindow = Toplevel(mainWindow)
checkWindow.title("Select tables to export")
checkWindow.geometry('%dx%d+%d+%d' % (checkWidth, checkHeight, checkCornerX, checkCornerY))
checkWindow.resizable(False, False)
checkWindow.iconphoto(False, icon)
checkWindow.protocol("WM_DELETE_WINDOW", cancelProcess)
#WINDOW AND SCROLLBAR CONFIGURATION
checkContainer = Canvas(checkWindow)
checkFrame = Frame(checkContainer)
checkScrollbar = Scrollbar(checkWindow)
#SELECT ALL BUTTON
checkButtonSelect = Button(checkFrame, text="Select All", command=selectAll)
checkButtonSelect.configure(width=2, height=1)
checkButtonSelect.grid(row=1, column=1, ipadx=120, padx=10)
checkButtonSelect.configure(state="disabled")
#CONFIGURING FONT
defaultFont = font.nametofont("TkDefaultFont")
defaultFont.configure(family="Tahoma", size=9)
##############
# MAIN LOOPS #
##############
createScrollableContainer()
checkWindow.mainloop()
mainWindow.mainloop()