summaryrefslogtreecommitdiff
path: root/comp/work/14/sql/database.py
blob: 32d485cdb2a4a7faebf7ee77be842226febc10b7 (plain)
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
#### database_tool.py

# Import libraries:
from tkinter import *
from tkinter.scrolledtext import *
import sqlite3
import os # needed to obtain OS specific methods for navigating file structures

# Global Variables
current_database = ''
databases = []
textbox_colour = 'linen'

#### Utility Functions
def pretty_tables(info, body):
    '''Called to handle SQL queries beginning with SELECT.
       It then formats the output from sqlite3 into easy tables'''
    column_widths = []
    headings = []
    
    # work hard to get headings and put them into a list
    # set column widths to length of heading strings
    for row in info:
        headings.append(row[0])
        column_widths.append(len(row[0]))

    # update column widths from body text
    for row in body:
        counter = 0
        for item in row:
            length = len(str(item))
            if length > column_widths[counter]:
                column_widths[counter] = length
            counter = counter+1
        
    # format results with a lot of string manipulation
    top_row = '+'
    heading_text = '| '
    body_text = '| '
    for width in column_widths:
        top_row = top_row + '-'*(width+3)
    top_row = top_row[:-1] + '+\n'
    
    counter = 0
    for item in headings:
        padding = ' '*(column_widths[counter] - len(str(item)))
        heading_text = heading_text + item + padding + ' | '
        counter = counter+1
    heading_text = heading_text + '\n'

    for row in body:
        counter = 0
        for item in row:
            padding = ' '*(column_widths[counter] - len(str(item)))
            body_text = body_text + str(item) + padding + ' | '
            counter = counter+1
        body_text = body_text + '\n| '
    body_text = body_text[:-2]
        
    text = top_row + heading_text + top_row + body_text + top_row
    return text

def update_databases():
    '''Looks through the folder where this script is
       found looking for database files'''
    number_of_dbs = 0
    files = os.listdir()
    for file in files:
        # select files where the last three characters are .db
        if file[-3:] == '.db':
            databases.append(file)

#### Button functions
def build_database():
    '''Creates a new database and rebuilds the dropdown menu'''
    global var
    global current_database
    global db_dropdown
    db_name = db_name_textbox.get() + '.db'
    if db_name == '.db':
        output_textbox.insert(END, 'ERROR: Please enter a name for your new database.\n\n')

    elif db_name in databases:
        output_textbox.insert(END, 'ERROR: Database already exists.\n\n')
    else:
        try:
            with sqlite3.connect(db_name) as db:
                cursor = db.cursor()
        except:
            output_textbox.insert(END, 'ERROR: Please try again.\n\n')
        else:
            # If all OK add new name to databases list
            databases.append(db_name)

            # Now destroy current dropdown menu and rebuild it with new list.
            db_dropdown.destroy()

            var = StringVar()
            var.set('Choose database:')
            db_dropdown = OptionMenu(frame_buttons, var, *databases, command=choose_database)
            db_dropdown.grid(row=0, column=0, sticky=NW)
 
def choose_database(value):
    '''Takes the appropriate database passed from the dropdown menu
       selection and updates the current_database global variable'''
    global current_database
    current_database = value
    
def clear_output():
    '''Empies the text in the Output textbox'''
    output_textbox.delete(0.0, END)  # clear output text box

def get_tables():
    '''Finds a list of tables in the database'''
    query = '''SELECT name FROM sqlite_master
             WHERE type='table'
             ORDER BY name;'''
    run_sql(query)

def quit_tool():
    '''closes the window and then quits'''
    window.withdraw()
    window.quit()

def run_query():
    '''Collects the query string from the SQL entry textbox and runs the query'''
    # Check database selected:
    if current_database == '':
        output_textbox.insert(END, 'ERROR: Please choose a database\n\n')
    else:
        # Fetch users SQL
        query = sql_textbox.get(0.0, END)
        run_sql(query)
        sql_textbox.delete(0.0, END)  # clear output text box
        print(query)

def run_sql(sql):
    '''Executes any SQL code passed to it and displays the output in the
       Output textbox'''
    # Connect to the database and run the SQL passed to this function
    with sqlite3.connect(current_database) as db:
        cursor = db.cursor()
        # the cursor is required to navigate the database
        cursor.execute(sql)
        db.commit()
        
        # use fetchall() to collect result of queries
        result = cursor.fetchall()

        # if a SELECT query, process result into a table
        if sql[:6].upper() == 'SELECT':
            table_info = cursor.description
            result = pretty_tables(table_info, result)
        else:
            result = str(result)
            
        result = result + '\n\n'
        # Display the value held in the variable result in the output textbox
        if len(result) > 50:  
            clear_output()
        output_textbox.insert(END, result)
   
##### main:
window = Tk()
window.title('Python Database Tool')

update_databases()

# Import images
python_logo = PhotoImage(file='python-logo.gif')
sqlite_logo = PhotoImage(file='sqlite-logo.gif')

# Create frames
frame_new_db = Frame(window)
frame_new_db.grid(row=3, column=0, pady=20, sticky=SW)

frame_buttons = Frame(window)
frame_buttons.grid(row=0, column=0)

frame_query = Frame(window)
frame_query.grid(row=2, column=0, sticky=NW)

frame_output = Frame(window)
frame_output.grid(row=2, column=1, rowspan=2)

# Add labels
Label(frame_new_db, text='Create a new database:').grid(row=0, column=0, columnspan=2, sticky=NW)
Label(frame_new_db, text='Choose a name: ').grid(row=1, column=0, sticky=NW)
Label(frame_query, text='Add SQL:').grid(row=0, column=0, sticky=NW)
Label(frame_output, text='Output: ').grid(row=0, column=0, sticky=NW)

# Create database name box
db_name_textbox = Entry(frame_new_db, width=10)
db_name_textbox.grid(row=1, column=1, sticky=NW)

# Create sql entry box
sql_textbox = Text(frame_query, width=50, height=10, background=textbox_colour)
sql_textbox.grid(row=1, column=0, sticky=NW)

# Create scrolling textbox
output_textbox = ScrolledText(frame_output, width=65, height=20, background=textbox_colour)
output_textbox.grid(row=1, column=0, sticky=NW)

# Add images in label widgets
Label(window, image=python_logo).grid(row=1, column=0, sticky=NW)
Label(window, image=sqlite_logo).grid(row=1, column=1, sticky=NW)

# Add buttons
button_quit = Button(frame_buttons, text="Quit", width=10, command=quit_tool)
button_quit.grid(row=0, column=2, sticky=NE)
button_clear = Button(frame_output, text="Clear result box", command=clear_output)
button_clear.grid(row=2, column=0, sticky=NE)
button_run = Button(frame_query, text="Run SQL", width=10, command=run_query)
button_run.grid(row=2, column=0, sticky=NW)
button_tables = Button(frame_buttons, text="List Tables", width=15, command=get_tables)
button_tables.grid(row=0, column=1, sticky=NE)
button_submit = Button(frame_new_db, text="Build database", command=build_database)
button_submit.grid(row=2, column=0, sticky=NW)

# Add dropdown menu
var = StringVar()
var.set('Choose database:')
# builds dropdown from databases list but if empty (first use situation) handles this.
db_dropdown = OptionMenu(frame_buttons, var, *databases if databases else ['empty'], command=choose_database)
db_dropdown.grid(row=0, column=0, sticky=NW)

##### Run mainloop
window.mainloop()