sqlshell 0.1.4__py3-none-any.whl → 0.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sqlshell might be problematic. Click here for more details.
- sqlshell/__init__.py +1 -1
- sqlshell/main.py +1189 -62
- sqlshell/sqlshell/create_test_databases.py +96 -0
- {sqlshell-0.1.4.dist-info → sqlshell-0.1.6.dist-info}/METADATA +8 -2
- sqlshell-0.1.6.dist-info/RECORD +11 -0
- sqlshell/sqlshell/main.py +0 -346
- sqlshell-0.1.4.dist-info/RECORD +0 -11
- {sqlshell-0.1.4.dist-info → sqlshell-0.1.6.dist-info}/WHEEL +0 -0
- {sqlshell-0.1.4.dist-info → sqlshell-0.1.6.dist-info}/entry_points.txt +0 -0
- {sqlshell-0.1.4.dist-info → sqlshell-0.1.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import sqlite3
|
|
3
|
+
import duckdb
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
# Define paths
|
|
7
|
+
TEST_DATA_DIR = 'test_data'
|
|
8
|
+
SQLITE_DB_PATH = os.path.join(TEST_DATA_DIR, 'test.db')
|
|
9
|
+
DUCKDB_PATH = os.path.join(TEST_DATA_DIR, 'test.duckdb')
|
|
10
|
+
|
|
11
|
+
def load_source_data():
|
|
12
|
+
"""Load the source Excel and Parquet files."""
|
|
13
|
+
sales_df = pd.read_excel(os.path.join(TEST_DATA_DIR, 'sample_sales_data.xlsx'))
|
|
14
|
+
customer_df = pd.read_parquet(os.path.join(TEST_DATA_DIR, 'customer_data.parquet'))
|
|
15
|
+
product_df = pd.read_excel(os.path.join(TEST_DATA_DIR, 'product_catalog.xlsx'))
|
|
16
|
+
return sales_df, customer_df, product_df
|
|
17
|
+
|
|
18
|
+
def create_sqlite_database():
|
|
19
|
+
"""Create SQLite database with the test data."""
|
|
20
|
+
# Remove existing database if it exists
|
|
21
|
+
if os.path.exists(SQLITE_DB_PATH):
|
|
22
|
+
os.remove(SQLITE_DB_PATH)
|
|
23
|
+
|
|
24
|
+
# Load data
|
|
25
|
+
sales_df, customer_df, product_df = load_source_data()
|
|
26
|
+
|
|
27
|
+
# Create connection and write tables
|
|
28
|
+
with sqlite3.connect(SQLITE_DB_PATH) as conn:
|
|
29
|
+
sales_df.to_sql('sales', conn, index=False)
|
|
30
|
+
customer_df.to_sql('customers', conn, index=False)
|
|
31
|
+
product_df.to_sql('products', conn, index=False)
|
|
32
|
+
|
|
33
|
+
# Create indexes for better performance
|
|
34
|
+
conn.execute('CREATE INDEX idx_sales_customer ON sales(CustomerID)')
|
|
35
|
+
conn.execute('CREATE INDEX idx_sales_product ON sales(ProductID)')
|
|
36
|
+
|
|
37
|
+
print(f"Created SQLite database at {SQLITE_DB_PATH}")
|
|
38
|
+
|
|
39
|
+
def create_duckdb_database():
|
|
40
|
+
"""Create DuckDB database with the test data."""
|
|
41
|
+
# Remove existing database if it exists
|
|
42
|
+
if os.path.exists(DUCKDB_PATH):
|
|
43
|
+
os.remove(DUCKDB_PATH)
|
|
44
|
+
|
|
45
|
+
# Load data
|
|
46
|
+
sales_df, customer_df, product_df = load_source_data()
|
|
47
|
+
|
|
48
|
+
# Create connection and write tables
|
|
49
|
+
with duckdb.connect(DUCKDB_PATH) as conn:
|
|
50
|
+
conn.execute("CREATE TABLE sales AS SELECT * FROM sales_df")
|
|
51
|
+
conn.execute("CREATE TABLE customers AS SELECT * FROM customer_df")
|
|
52
|
+
conn.execute("CREATE TABLE products AS SELECT * FROM product_df")
|
|
53
|
+
|
|
54
|
+
# Create indexes for better performance
|
|
55
|
+
conn.execute('CREATE INDEX idx_sales_customer ON sales(CustomerID)')
|
|
56
|
+
conn.execute('CREATE INDEX idx_sales_product ON sales(ProductID)')
|
|
57
|
+
|
|
58
|
+
print(f"Created DuckDB database at {DUCKDB_PATH}")
|
|
59
|
+
|
|
60
|
+
def verify_databases():
|
|
61
|
+
"""Verify the databases were created correctly by running test queries."""
|
|
62
|
+
# Test SQLite
|
|
63
|
+
with sqlite3.connect(SQLITE_DB_PATH) as conn:
|
|
64
|
+
sales_count = pd.read_sql("SELECT COUNT(*) as count FROM sales", conn).iloc[0]['count']
|
|
65
|
+
print(f"\nSQLite verification:")
|
|
66
|
+
print(f"Sales records: {sales_count}")
|
|
67
|
+
|
|
68
|
+
# Test a join query
|
|
69
|
+
sample_query = """
|
|
70
|
+
SELECT
|
|
71
|
+
p.Category,
|
|
72
|
+
COUNT(*) as NumOrders,
|
|
73
|
+
ROUND(SUM(s.TotalAmount), 2) as TotalRevenue
|
|
74
|
+
FROM sales s
|
|
75
|
+
JOIN products p ON s.ProductID = p.ProductID
|
|
76
|
+
GROUP BY p.Category
|
|
77
|
+
LIMIT 3
|
|
78
|
+
"""
|
|
79
|
+
print("\nSample SQLite query result:")
|
|
80
|
+
print(pd.read_sql(sample_query, conn))
|
|
81
|
+
|
|
82
|
+
# Test DuckDB
|
|
83
|
+
with duckdb.connect(DUCKDB_PATH) as conn:
|
|
84
|
+
sales_count = conn.execute("SELECT COUNT(*) as count FROM sales").fetchone()[0]
|
|
85
|
+
print(f"\nDuckDB verification:")
|
|
86
|
+
print(f"Sales records: {sales_count}")
|
|
87
|
+
|
|
88
|
+
# Test the same join query
|
|
89
|
+
print("\nSample DuckDB query result:")
|
|
90
|
+
print(conn.execute(sample_query).df())
|
|
91
|
+
|
|
92
|
+
if __name__ == '__main__':
|
|
93
|
+
print("Creating test databases...")
|
|
94
|
+
create_sqlite_database()
|
|
95
|
+
create_duckdb_database()
|
|
96
|
+
verify_databases()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: sqlshell
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: A powerful SQL shell with GUI interface for data analysis
|
|
5
5
|
Home-page: https://github.com/yourusername/sqlshell
|
|
6
6
|
Author: SQLShell Team
|
|
@@ -29,7 +29,7 @@ Dynamic: requires-python
|
|
|
29
29
|
|
|
30
30
|
# SQL Shell
|
|
31
31
|
|
|
32
|
-
A GUI application that provides a SQL REPL interface for querying
|
|
32
|
+
A GUI application that provides a SQL REPL interface for querying Excel and parquet files (more to come!)
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|

|
|
@@ -50,6 +50,12 @@ A GUI application that provides a SQL REPL interface for querying DuckDB databas
|
|
|
50
50
|
pip install -r requirements.txt
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
You can also do:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install sqlshell
|
|
57
|
+
```
|
|
58
|
+
|
|
53
59
|
## Usage
|
|
54
60
|
|
|
55
61
|
1. Run the application:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
sqlshell/__init__.py,sha256=OyPOfZIsEkW8eSHd0zAf6BT31-WwzDpONFBbeXV7kDc,129
|
|
2
|
+
sqlshell/main.py,sha256=DuCJqWVxcRlOnPjV1h7LwC49yXZSRFcBdMsgldFG4Fc,60536
|
|
3
|
+
sqlshell/setup.py,sha256=bAIXTpgAHhBRmPdT13Klzq16cjd4w4NOYSbyV_rxjlQ,1245
|
|
4
|
+
sqlshell/sqlshell/__init__.py,sha256=6Wp5nabfTzH5rkC-2jYo_ZjCuw8utmj21Jpy8vBuliI,100
|
|
5
|
+
sqlshell/sqlshell/create_test_data.py,sha256=sUTcf50V8-bVwYV2VNTLK65c-iHiU4wb99By67I10zM,5404
|
|
6
|
+
sqlshell/sqlshell/create_test_databases.py,sha256=oqryFJJahqLFsAjBFM4r9Fe1ea7djDcRpT9U_aBf7PU,3573
|
|
7
|
+
sqlshell-0.1.6.dist-info/METADATA,sha256=l0FXXswJr5NlZtD4YzzD345VAeYer8Mve8uhmcbana0,2559
|
|
8
|
+
sqlshell-0.1.6.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
|
9
|
+
sqlshell-0.1.6.dist-info/entry_points.txt,sha256=Kd0fOvyOW7UiTgTVY7abVOmDIH2Y2nawGTp5kVadac4,44
|
|
10
|
+
sqlshell-0.1.6.dist-info/top_level.txt,sha256=ahwsMFhvAqI97ZkT2xvHL5iZCO1p13mNiUOFkdSFwms,9
|
|
11
|
+
sqlshell-0.1.6.dist-info/RECORD,,
|
sqlshell/sqlshell/main.py
DELETED
|
@@ -1,346 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
import os
|
|
3
|
-
import duckdb
|
|
4
|
-
import pandas as pd
|
|
5
|
-
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
|
6
|
-
QHBoxLayout, QTextEdit, QPushButton, QFileDialog,
|
|
7
|
-
QLabel, QSplitter, QListWidget, QTableWidget,
|
|
8
|
-
QTableWidgetItem, QHeaderView)
|
|
9
|
-
from PyQt6.QtCore import Qt, QAbstractTableModel
|
|
10
|
-
from PyQt6.QtGui import QFont, QColor
|
|
11
|
-
import numpy as np
|
|
12
|
-
from datetime import datetime
|
|
13
|
-
from . import create_test_data # Import from the same package
|
|
14
|
-
|
|
15
|
-
class SQLShell(QMainWindow):
|
|
16
|
-
def __init__(self):
|
|
17
|
-
super().__init__()
|
|
18
|
-
self.conn = duckdb.connect('pool.db')
|
|
19
|
-
self.loaded_tables = {} # Keep track of loaded tables
|
|
20
|
-
self.init_ui()
|
|
21
|
-
|
|
22
|
-
def init_ui(self):
|
|
23
|
-
self.setWindowTitle('SQL Shell')
|
|
24
|
-
self.setGeometry(100, 100, 1400, 800)
|
|
25
|
-
|
|
26
|
-
# Create central widget and layout
|
|
27
|
-
central_widget = QWidget()
|
|
28
|
-
self.setCentralWidget(central_widget)
|
|
29
|
-
main_layout = QHBoxLayout(central_widget)
|
|
30
|
-
|
|
31
|
-
# Left panel for table list
|
|
32
|
-
left_panel = QWidget()
|
|
33
|
-
left_layout = QVBoxLayout(left_panel)
|
|
34
|
-
|
|
35
|
-
tables_label = QLabel("Loaded Tables:")
|
|
36
|
-
left_layout.addWidget(tables_label)
|
|
37
|
-
|
|
38
|
-
self.tables_list = QListWidget()
|
|
39
|
-
self.tables_list.itemClicked.connect(self.show_table_preview)
|
|
40
|
-
left_layout.addWidget(self.tables_list)
|
|
41
|
-
|
|
42
|
-
# Buttons for table management
|
|
43
|
-
table_buttons_layout = QHBoxLayout()
|
|
44
|
-
self.browse_btn = QPushButton('Load Files')
|
|
45
|
-
self.browse_btn.clicked.connect(self.browse_files)
|
|
46
|
-
self.remove_table_btn = QPushButton('Remove Selected')
|
|
47
|
-
self.remove_table_btn.clicked.connect(self.remove_selected_table)
|
|
48
|
-
self.test_btn = QPushButton('Test')
|
|
49
|
-
self.test_btn.clicked.connect(self.load_test_data)
|
|
50
|
-
|
|
51
|
-
table_buttons_layout.addWidget(self.browse_btn)
|
|
52
|
-
table_buttons_layout.addWidget(self.remove_table_btn)
|
|
53
|
-
table_buttons_layout.addWidget(self.test_btn)
|
|
54
|
-
left_layout.addLayout(table_buttons_layout)
|
|
55
|
-
|
|
56
|
-
# Right panel for query and results
|
|
57
|
-
right_panel = QWidget()
|
|
58
|
-
right_layout = QVBoxLayout(right_panel)
|
|
59
|
-
|
|
60
|
-
# Create splitter for query and results
|
|
61
|
-
splitter = QSplitter(Qt.Orientation.Vertical)
|
|
62
|
-
|
|
63
|
-
# Top part - Query section
|
|
64
|
-
query_widget = QWidget()
|
|
65
|
-
query_layout = QVBoxLayout(query_widget)
|
|
66
|
-
|
|
67
|
-
# Button row
|
|
68
|
-
button_layout = QHBoxLayout()
|
|
69
|
-
self.execute_btn = QPushButton('Execute (Ctrl+Enter)')
|
|
70
|
-
self.execute_btn.clicked.connect(self.execute_query)
|
|
71
|
-
self.clear_btn = QPushButton('Clear')
|
|
72
|
-
self.clear_btn.clicked.connect(self.clear_query)
|
|
73
|
-
|
|
74
|
-
button_layout.addWidget(self.execute_btn)
|
|
75
|
-
button_layout.addWidget(self.clear_btn)
|
|
76
|
-
button_layout.addStretch()
|
|
77
|
-
|
|
78
|
-
query_layout.addLayout(button_layout)
|
|
79
|
-
|
|
80
|
-
# Query input
|
|
81
|
-
self.query_edit = QTextEdit()
|
|
82
|
-
self.query_edit.setPlaceholderText("Enter your SQL query here...")
|
|
83
|
-
query_layout.addWidget(self.query_edit)
|
|
84
|
-
|
|
85
|
-
# Bottom part - Results section
|
|
86
|
-
results_widget = QWidget()
|
|
87
|
-
results_layout = QVBoxLayout(results_widget)
|
|
88
|
-
|
|
89
|
-
# Results header with row count
|
|
90
|
-
results_header = QWidget()
|
|
91
|
-
results_header_layout = QHBoxLayout(results_header)
|
|
92
|
-
self.results_label = QLabel("Results:")
|
|
93
|
-
self.row_count_label = QLabel("")
|
|
94
|
-
results_header_layout.addWidget(self.results_label)
|
|
95
|
-
results_header_layout.addWidget(self.row_count_label)
|
|
96
|
-
results_header_layout.addStretch()
|
|
97
|
-
results_layout.addWidget(results_header)
|
|
98
|
-
|
|
99
|
-
# Table widget for results
|
|
100
|
-
self.results_table = QTableWidget()
|
|
101
|
-
self.results_table.setSortingEnabled(True)
|
|
102
|
-
self.results_table.setAlternatingRowColors(True)
|
|
103
|
-
self.results_table.horizontalHeader().setStretchLastSection(True)
|
|
104
|
-
self.results_table.horizontalHeader().setSectionsMovable(True)
|
|
105
|
-
self.results_table.verticalHeader().setVisible(False)
|
|
106
|
-
results_layout.addWidget(self.results_table)
|
|
107
|
-
|
|
108
|
-
# Add widgets to splitter
|
|
109
|
-
splitter.addWidget(query_widget)
|
|
110
|
-
splitter.addWidget(results_widget)
|
|
111
|
-
|
|
112
|
-
# Set initial sizes for splitter
|
|
113
|
-
splitter.setSizes([300, 500])
|
|
114
|
-
|
|
115
|
-
right_layout.addWidget(splitter)
|
|
116
|
-
|
|
117
|
-
# Add panels to main layout
|
|
118
|
-
main_layout.addWidget(left_panel, 1)
|
|
119
|
-
main_layout.addWidget(right_panel, 4)
|
|
120
|
-
|
|
121
|
-
# Status bar
|
|
122
|
-
self.statusBar().showMessage('Ready')
|
|
123
|
-
|
|
124
|
-
def format_value(self, value):
|
|
125
|
-
"""Format values for display"""
|
|
126
|
-
if pd.isna(value):
|
|
127
|
-
return 'NULL'
|
|
128
|
-
elif isinstance(value, (int, np.integer)):
|
|
129
|
-
return f"{value:,}"
|
|
130
|
-
elif isinstance(value, (float, np.floating)):
|
|
131
|
-
return f"{value:,.2f}"
|
|
132
|
-
elif isinstance(value, (datetime, pd.Timestamp)):
|
|
133
|
-
return value.strftime('%Y-%m-%d %H:%M:%S')
|
|
134
|
-
return str(value)
|
|
135
|
-
|
|
136
|
-
def populate_table(self, df):
|
|
137
|
-
"""Populate the table widget with DataFrame content"""
|
|
138
|
-
if len(df) == 0:
|
|
139
|
-
self.results_table.setRowCount(0)
|
|
140
|
-
self.results_table.setColumnCount(0)
|
|
141
|
-
self.row_count_label.setText("No results")
|
|
142
|
-
return
|
|
143
|
-
|
|
144
|
-
# Set dimensions
|
|
145
|
-
self.results_table.setRowCount(len(df))
|
|
146
|
-
self.results_table.setColumnCount(len(df.columns))
|
|
147
|
-
|
|
148
|
-
# Set headers
|
|
149
|
-
self.results_table.setHorizontalHeaderLabels(df.columns)
|
|
150
|
-
|
|
151
|
-
# Populate data
|
|
152
|
-
for i, (_, row) in enumerate(df.iterrows()):
|
|
153
|
-
for j, value in enumerate(row):
|
|
154
|
-
formatted_value = self.format_value(value)
|
|
155
|
-
item = QTableWidgetItem(formatted_value)
|
|
156
|
-
|
|
157
|
-
# Set alignment based on data type
|
|
158
|
-
if isinstance(value, (int, float, np.integer, np.floating)):
|
|
159
|
-
item.setTextAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
|
160
|
-
else:
|
|
161
|
-
item.setTextAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter)
|
|
162
|
-
|
|
163
|
-
# Make cells read-only
|
|
164
|
-
item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEditable)
|
|
165
|
-
|
|
166
|
-
self.results_table.setItem(i, j, item)
|
|
167
|
-
|
|
168
|
-
# Auto-adjust column widths while ensuring minimum and maximum sizes
|
|
169
|
-
self.results_table.resizeColumnsToContents()
|
|
170
|
-
for i in range(len(df.columns)):
|
|
171
|
-
width = self.results_table.columnWidth(i)
|
|
172
|
-
self.results_table.setColumnWidth(i, min(max(width, 50), 300))
|
|
173
|
-
|
|
174
|
-
# Update row count
|
|
175
|
-
row_text = "row" if len(df) == 1 else "rows"
|
|
176
|
-
self.row_count_label.setText(f"{len(df):,} {row_text}")
|
|
177
|
-
|
|
178
|
-
def browse_files(self):
|
|
179
|
-
file_names, _ = QFileDialog.getOpenFileNames(
|
|
180
|
-
self,
|
|
181
|
-
"Open Data Files",
|
|
182
|
-
"",
|
|
183
|
-
"Data Files (*.xlsx *.xls *.csv *.parquet);;Excel Files (*.xlsx *.xls);;CSV Files (*.csv);;Parquet Files (*.parquet);;All Files (*)"
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
for file_name in file_names:
|
|
187
|
-
try:
|
|
188
|
-
if file_name.endswith(('.xlsx', '.xls')):
|
|
189
|
-
df = pd.read_excel(file_name)
|
|
190
|
-
elif file_name.endswith('.csv'):
|
|
191
|
-
df = pd.read_csv(file_name)
|
|
192
|
-
elif file_name.endswith('.parquet'):
|
|
193
|
-
df = pd.read_parquet(file_name)
|
|
194
|
-
else:
|
|
195
|
-
raise ValueError("Unsupported file format")
|
|
196
|
-
|
|
197
|
-
# Generate table name from file name
|
|
198
|
-
base_name = os.path.splitext(os.path.basename(file_name))[0]
|
|
199
|
-
table_name = self.sanitize_table_name(base_name)
|
|
200
|
-
|
|
201
|
-
# Ensure unique table name
|
|
202
|
-
original_name = table_name
|
|
203
|
-
counter = 1
|
|
204
|
-
while table_name in self.loaded_tables:
|
|
205
|
-
table_name = f"{original_name}_{counter}"
|
|
206
|
-
counter += 1
|
|
207
|
-
|
|
208
|
-
# Register table in DuckDB
|
|
209
|
-
self.conn.register(table_name, df)
|
|
210
|
-
self.loaded_tables[table_name] = file_name
|
|
211
|
-
|
|
212
|
-
# Update UI
|
|
213
|
-
self.tables_list.addItem(f"{table_name} ({os.path.basename(file_name)})")
|
|
214
|
-
self.statusBar().showMessage(f'Loaded {file_name} as table "{table_name}"')
|
|
215
|
-
|
|
216
|
-
# Show preview of loaded data
|
|
217
|
-
preview_df = df.head()
|
|
218
|
-
self.populate_table(preview_df)
|
|
219
|
-
self.results_label.setText(f"Preview of {table_name}:")
|
|
220
|
-
|
|
221
|
-
except Exception as e:
|
|
222
|
-
self.statusBar().showMessage(f'Error loading file: {str(e)}')
|
|
223
|
-
self.results_table.setRowCount(0)
|
|
224
|
-
self.results_table.setColumnCount(0)
|
|
225
|
-
self.row_count_label.setText("")
|
|
226
|
-
self.results_label.setText(f"Error loading file: {str(e)}")
|
|
227
|
-
|
|
228
|
-
def sanitize_table_name(self, name):
|
|
229
|
-
# Replace invalid characters with underscores
|
|
230
|
-
import re
|
|
231
|
-
name = re.sub(r'[^a-zA-Z0-9_]', '_', name)
|
|
232
|
-
# Ensure it starts with a letter
|
|
233
|
-
if not name[0].isalpha():
|
|
234
|
-
name = 'table_' + name
|
|
235
|
-
return name.lower()
|
|
236
|
-
|
|
237
|
-
def remove_selected_table(self):
|
|
238
|
-
current_item = self.tables_list.currentItem()
|
|
239
|
-
if current_item:
|
|
240
|
-
table_name = current_item.text().split(' (')[0]
|
|
241
|
-
if table_name in self.loaded_tables:
|
|
242
|
-
# Remove from DuckDB
|
|
243
|
-
self.conn.execute(f'DROP VIEW IF EXISTS {table_name}')
|
|
244
|
-
# Remove from our tracking
|
|
245
|
-
del self.loaded_tables[table_name]
|
|
246
|
-
# Remove from list widget
|
|
247
|
-
self.tables_list.takeItem(self.tables_list.row(current_item))
|
|
248
|
-
self.statusBar().showMessage(f'Removed table "{table_name}"')
|
|
249
|
-
self.results_table.setRowCount(0)
|
|
250
|
-
self.results_table.setColumnCount(0)
|
|
251
|
-
self.row_count_label.setText("")
|
|
252
|
-
self.results_label.setText(f"Removed table: {table_name}")
|
|
253
|
-
|
|
254
|
-
def execute_query(self):
|
|
255
|
-
query = self.query_edit.toPlainText().strip()
|
|
256
|
-
if not query:
|
|
257
|
-
return
|
|
258
|
-
|
|
259
|
-
try:
|
|
260
|
-
result = self.conn.execute(query).fetchdf()
|
|
261
|
-
self.populate_table(result)
|
|
262
|
-
self.results_label.setText("Query Results:")
|
|
263
|
-
self.statusBar().showMessage('Query executed successfully')
|
|
264
|
-
except Exception as e:
|
|
265
|
-
self.results_table.setRowCount(0)
|
|
266
|
-
self.results_table.setColumnCount(0)
|
|
267
|
-
self.row_count_label.setText("")
|
|
268
|
-
self.results_label.setText(f"Error executing query: {str(e)}")
|
|
269
|
-
self.statusBar().showMessage('Error executing query')
|
|
270
|
-
|
|
271
|
-
def clear_query(self):
|
|
272
|
-
self.query_edit.clear()
|
|
273
|
-
|
|
274
|
-
def show_table_preview(self, item):
|
|
275
|
-
"""Show a preview of the selected table"""
|
|
276
|
-
if item:
|
|
277
|
-
table_name = item.text().split(' (')[0]
|
|
278
|
-
try:
|
|
279
|
-
preview_df = self.conn.execute(f'SELECT * FROM {table_name} LIMIT 5').fetchdf()
|
|
280
|
-
self.populate_table(preview_df)
|
|
281
|
-
self.results_label.setText(f"Preview of {table_name}:")
|
|
282
|
-
self.statusBar().showMessage(f'Showing preview of table "{table_name}"')
|
|
283
|
-
except Exception as e:
|
|
284
|
-
self.results_table.setRowCount(0)
|
|
285
|
-
self.results_table.setColumnCount(0)
|
|
286
|
-
self.row_count_label.setText("")
|
|
287
|
-
self.results_label.setText(f"Error showing preview: {str(e)}")
|
|
288
|
-
self.statusBar().showMessage('Error showing table preview')
|
|
289
|
-
|
|
290
|
-
def keyPressEvent(self, event):
|
|
291
|
-
if event.key() == Qt.Key.Key_Return and event.modifiers() == Qt.KeyboardModifier.ControlModifier:
|
|
292
|
-
self.execute_query()
|
|
293
|
-
else:
|
|
294
|
-
super().keyPressEvent(event)
|
|
295
|
-
|
|
296
|
-
def load_test_data(self):
|
|
297
|
-
"""Generate and load test data"""
|
|
298
|
-
try:
|
|
299
|
-
# Create test data directory if it doesn't exist
|
|
300
|
-
os.makedirs('test_data', exist_ok=True)
|
|
301
|
-
|
|
302
|
-
# Generate test data
|
|
303
|
-
sales_df = create_test_data.create_sales_data()
|
|
304
|
-
customer_df = create_test_data.create_customer_data()
|
|
305
|
-
product_df = create_test_data.create_product_data()
|
|
306
|
-
|
|
307
|
-
# Save test data
|
|
308
|
-
sales_df.to_excel('test_data/sample_sales_data.xlsx', index=False)
|
|
309
|
-
customer_df.to_parquet('test_data/customer_data.parquet', index=False)
|
|
310
|
-
product_df.to_excel('test_data/product_catalog.xlsx', index=False)
|
|
311
|
-
|
|
312
|
-
# Load the files into DuckDB
|
|
313
|
-
self.conn.register('sample_sales_data', sales_df)
|
|
314
|
-
self.conn.register('product_catalog', product_df)
|
|
315
|
-
self.conn.register('customer_data', customer_df)
|
|
316
|
-
|
|
317
|
-
# Update loaded tables tracking
|
|
318
|
-
self.loaded_tables['sample_sales_data'] = 'test_data/sample_sales_data.xlsx'
|
|
319
|
-
self.loaded_tables['product_catalog'] = 'test_data/product_catalog.xlsx'
|
|
320
|
-
self.loaded_tables['customer_data'] = 'test_data/customer_data.parquet'
|
|
321
|
-
|
|
322
|
-
# Update UI
|
|
323
|
-
self.tables_list.clear()
|
|
324
|
-
for table_name, file_path in self.loaded_tables.items():
|
|
325
|
-
self.tables_list.addItem(f"{table_name} ({os.path.basename(file_path)})")
|
|
326
|
-
|
|
327
|
-
# Set the sample query
|
|
328
|
-
self.query_edit.setText("select * from sample_sales_data cd inner join product_catalog pc on pc.productid = cd.productid limit 3")
|
|
329
|
-
|
|
330
|
-
self.statusBar().showMessage('Test data loaded successfully')
|
|
331
|
-
|
|
332
|
-
except Exception as e:
|
|
333
|
-
self.statusBar().showMessage(f'Error loading test data: {str(e)}')
|
|
334
|
-
|
|
335
|
-
def main():
|
|
336
|
-
app = QApplication(sys.argv)
|
|
337
|
-
|
|
338
|
-
# Set application style
|
|
339
|
-
app.setStyle('Fusion')
|
|
340
|
-
|
|
341
|
-
sql_shell = SQLShell()
|
|
342
|
-
sql_shell.show()
|
|
343
|
-
sys.exit(app.exec())
|
|
344
|
-
|
|
345
|
-
if __name__ == '__main__':
|
|
346
|
-
main()
|
sqlshell-0.1.4.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
sqlshell/__init__.py,sha256=9UrXHBsLs5Z0k21he3wi2EStMvtSkECcg1awzF78Gyc,129
|
|
2
|
-
sqlshell/main.py,sha256=cwojhoG4RaU3qlN_VsqpqF9JCDdFQZESNfgHQFromRU,14187
|
|
3
|
-
sqlshell/setup.py,sha256=bAIXTpgAHhBRmPdT13Klzq16cjd4w4NOYSbyV_rxjlQ,1245
|
|
4
|
-
sqlshell/sqlshell/__init__.py,sha256=6Wp5nabfTzH5rkC-2jYo_ZjCuw8utmj21Jpy8vBuliI,100
|
|
5
|
-
sqlshell/sqlshell/create_test_data.py,sha256=sUTcf50V8-bVwYV2VNTLK65c-iHiU4wb99By67I10zM,5404
|
|
6
|
-
sqlshell/sqlshell/main.py,sha256=MlkjWh5vlAAAZ8SYE8j0ChJmQFOJ_qqUmW5iH8HiGg8,14184
|
|
7
|
-
sqlshell-0.1.4.dist-info/METADATA,sha256=Ht6sbjkZ-cuh8hh0Nq1kdh0ZreABnVI6QjOZU44TOiM,2501
|
|
8
|
-
sqlshell-0.1.4.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
|
|
9
|
-
sqlshell-0.1.4.dist-info/entry_points.txt,sha256=Kd0fOvyOW7UiTgTVY7abVOmDIH2Y2nawGTp5kVadac4,44
|
|
10
|
-
sqlshell-0.1.4.dist-info/top_level.txt,sha256=ahwsMFhvAqI97ZkT2xvHL5iZCO1p13mNiUOFkdSFwms,9
|
|
11
|
-
sqlshell-0.1.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|