sqlshell 0.4.4__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.
- sqlshell/__init__.py +84 -0
- sqlshell/__main__.py +4926 -0
- sqlshell/ai_autocomplete.py +392 -0
- sqlshell/ai_settings_dialog.py +337 -0
- sqlshell/context_suggester.py +768 -0
- sqlshell/create_test_data.py +152 -0
- sqlshell/data/create_test_data.py +137 -0
- sqlshell/db/__init__.py +6 -0
- sqlshell/db/database_manager.py +1318 -0
- sqlshell/db/export_manager.py +188 -0
- sqlshell/editor.py +1166 -0
- sqlshell/editor_integration.py +127 -0
- sqlshell/execution_handler.py +421 -0
- sqlshell/menus.py +262 -0
- sqlshell/notification_manager.py +370 -0
- sqlshell/query_tab.py +904 -0
- sqlshell/resources/__init__.py +1 -0
- sqlshell/resources/icon.png +0 -0
- sqlshell/resources/logo_large.png +0 -0
- sqlshell/resources/logo_medium.png +0 -0
- sqlshell/resources/logo_small.png +0 -0
- sqlshell/resources/splash_screen.gif +0 -0
- sqlshell/space_invaders.py +501 -0
- sqlshell/splash_screen.py +405 -0
- sqlshell/sqlshell/__init__.py +5 -0
- sqlshell/sqlshell/create_test_data.py +118 -0
- sqlshell/sqlshell/create_test_databases.py +96 -0
- sqlshell/sqlshell_demo.png +0 -0
- sqlshell/styles.py +257 -0
- sqlshell/suggester_integration.py +330 -0
- sqlshell/syntax_highlighter.py +124 -0
- sqlshell/table_list.py +996 -0
- sqlshell/ui/__init__.py +6 -0
- sqlshell/ui/bar_chart_delegate.py +49 -0
- sqlshell/ui/filter_header.py +469 -0
- sqlshell/utils/__init__.py +16 -0
- sqlshell/utils/profile_cn2.py +1661 -0
- sqlshell/utils/profile_column.py +2635 -0
- sqlshell/utils/profile_distributions.py +616 -0
- sqlshell/utils/profile_entropy.py +347 -0
- sqlshell/utils/profile_foreign_keys.py +779 -0
- sqlshell/utils/profile_keys.py +2834 -0
- sqlshell/utils/profile_ohe.py +934 -0
- sqlshell/utils/profile_ohe_advanced.py +754 -0
- sqlshell/utils/profile_ohe_comparison.py +237 -0
- sqlshell/utils/profile_prediction.py +926 -0
- sqlshell/utils/profile_similarity.py +876 -0
- sqlshell/utils/search_in_df.py +90 -0
- sqlshell/widgets.py +400 -0
- sqlshell-0.4.4.dist-info/METADATA +441 -0
- sqlshell-0.4.4.dist-info/RECORD +54 -0
- sqlshell-0.4.4.dist-info/WHEEL +5 -0
- sqlshell-0.4.4.dist-info/entry_points.txt +2 -0
- sqlshell-0.4.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"""Export functionality for SQLShell application."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import numpy as np
|
|
6
|
+
from typing import Optional, Tuple, Dict, Any
|
|
7
|
+
|
|
8
|
+
class ExportManager:
|
|
9
|
+
"""Manages data export functionality for SQLShell."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, db_manager):
|
|
12
|
+
"""Initialize the export manager.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
db_manager: The database manager instance to use for table registration
|
|
16
|
+
"""
|
|
17
|
+
self.db_manager = db_manager
|
|
18
|
+
|
|
19
|
+
def export_to_excel(self, df: pd.DataFrame, file_name: str) -> Tuple[str, Dict[str, Any]]:
|
|
20
|
+
"""Export data to Excel format.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
df: The DataFrame to export
|
|
24
|
+
file_name: The target file path
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Tuple containing:
|
|
28
|
+
- The generated table name
|
|
29
|
+
- Dictionary with export metadata
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
# Export to Excel
|
|
33
|
+
df.to_excel(file_name, index=False)
|
|
34
|
+
|
|
35
|
+
# Generate table name from file name
|
|
36
|
+
base_name = os.path.splitext(os.path.basename(file_name))[0]
|
|
37
|
+
table_name = self.db_manager.sanitize_table_name(base_name)
|
|
38
|
+
|
|
39
|
+
# Ensure unique table name
|
|
40
|
+
original_name = table_name
|
|
41
|
+
counter = 1
|
|
42
|
+
while table_name in self.db_manager.loaded_tables:
|
|
43
|
+
table_name = f"{original_name}_{counter}"
|
|
44
|
+
counter += 1
|
|
45
|
+
|
|
46
|
+
# Register the table in the database manager
|
|
47
|
+
self.db_manager.register_dataframe(df, table_name, file_name)
|
|
48
|
+
|
|
49
|
+
# Update tracking
|
|
50
|
+
self.db_manager.loaded_tables[table_name] = file_name
|
|
51
|
+
self.db_manager.table_columns[table_name] = [str(col) for col in df.columns.tolist()]
|
|
52
|
+
|
|
53
|
+
return table_name, {
|
|
54
|
+
'file_path': file_name,
|
|
55
|
+
'columns': df.columns.tolist(),
|
|
56
|
+
'row_count': len(df)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
except Exception as e:
|
|
60
|
+
raise Exception(f"Failed to export to Excel: {str(e)}")
|
|
61
|
+
|
|
62
|
+
def export_to_parquet(self, df: pd.DataFrame, file_name: str) -> Tuple[str, Dict[str, Any]]:
|
|
63
|
+
"""Export data to Parquet format.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
df: The DataFrame to export
|
|
67
|
+
file_name: The target file path
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Tuple containing:
|
|
71
|
+
- The generated table name
|
|
72
|
+
- Dictionary with export metadata
|
|
73
|
+
"""
|
|
74
|
+
try:
|
|
75
|
+
# Export to Parquet using fastparquet engine (lighter than pyarrow - saves 147MB in builds)
|
|
76
|
+
df.to_parquet(file_name, index=False, engine='fastparquet')
|
|
77
|
+
|
|
78
|
+
# Generate table name from file name
|
|
79
|
+
base_name = os.path.splitext(os.path.basename(file_name))[0]
|
|
80
|
+
table_name = self.db_manager.sanitize_table_name(base_name)
|
|
81
|
+
|
|
82
|
+
# Ensure unique table name
|
|
83
|
+
original_name = table_name
|
|
84
|
+
counter = 1
|
|
85
|
+
while table_name in self.db_manager.loaded_tables:
|
|
86
|
+
table_name = f"{original_name}_{counter}"
|
|
87
|
+
counter += 1
|
|
88
|
+
|
|
89
|
+
# Register the table in the database manager
|
|
90
|
+
self.db_manager.register_dataframe(df, table_name, file_name)
|
|
91
|
+
|
|
92
|
+
# Update tracking
|
|
93
|
+
self.db_manager.loaded_tables[table_name] = file_name
|
|
94
|
+
self.db_manager.table_columns[table_name] = [str(col) for col in df.columns.tolist()]
|
|
95
|
+
|
|
96
|
+
return table_name, {
|
|
97
|
+
'file_path': file_name,
|
|
98
|
+
'columns': df.columns.tolist(),
|
|
99
|
+
'row_count': len(df)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
except Exception as e:
|
|
103
|
+
raise Exception(f"Failed to export to Parquet: {str(e)}")
|
|
104
|
+
|
|
105
|
+
def convert_table_to_dataframe(self, table_widget) -> Optional[pd.DataFrame]:
|
|
106
|
+
"""Convert a QTableWidget to a pandas DataFrame with proper data types.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
table_widget: The QTableWidget containing the data
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
DataFrame with properly typed data, or None if conversion fails
|
|
113
|
+
"""
|
|
114
|
+
if not table_widget or table_widget.rowCount() == 0:
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
# Get headers
|
|
118
|
+
headers = [table_widget.horizontalHeaderItem(i).text()
|
|
119
|
+
for i in range(table_widget.columnCount())]
|
|
120
|
+
|
|
121
|
+
# Get data
|
|
122
|
+
data = []
|
|
123
|
+
for row in range(table_widget.rowCount()):
|
|
124
|
+
row_data = []
|
|
125
|
+
for column in range(table_widget.columnCount()):
|
|
126
|
+
item = table_widget.item(row, column)
|
|
127
|
+
row_data.append(item.text() if item else '')
|
|
128
|
+
data.append(row_data)
|
|
129
|
+
|
|
130
|
+
# Create DataFrame from raw string data
|
|
131
|
+
df_raw = pd.DataFrame(data, columns=headers)
|
|
132
|
+
|
|
133
|
+
# Try to use the original dataframe's dtypes if available
|
|
134
|
+
if hasattr(table_widget, 'current_df') and table_widget.current_df is not None:
|
|
135
|
+
original_df = table_widget.current_df
|
|
136
|
+
|
|
137
|
+
# Create a new DataFrame with appropriate types
|
|
138
|
+
df_typed = pd.DataFrame()
|
|
139
|
+
|
|
140
|
+
for col in df_raw.columns:
|
|
141
|
+
if col in original_df.columns:
|
|
142
|
+
# Get the original column type
|
|
143
|
+
orig_type = original_df[col].dtype
|
|
144
|
+
|
|
145
|
+
# Special handling for different data types
|
|
146
|
+
if pd.api.types.is_numeric_dtype(orig_type):
|
|
147
|
+
try:
|
|
148
|
+
numeric_col = pd.to_numeric(
|
|
149
|
+
df_raw[col].str.replace(',', '').replace('NULL', np.nan)
|
|
150
|
+
)
|
|
151
|
+
df_typed[col] = numeric_col
|
|
152
|
+
except:
|
|
153
|
+
df_typed[col] = df_raw[col]
|
|
154
|
+
elif pd.api.types.is_datetime64_dtype(orig_type):
|
|
155
|
+
try:
|
|
156
|
+
df_typed[col] = pd.to_datetime(df_raw[col].replace('NULL', np.nan))
|
|
157
|
+
except:
|
|
158
|
+
df_typed[col] = df_raw[col]
|
|
159
|
+
elif pd.api.types.is_bool_dtype(orig_type):
|
|
160
|
+
try:
|
|
161
|
+
df_typed[col] = df_raw[col].map({'True': True, 'False': False}).replace('NULL', np.nan)
|
|
162
|
+
except:
|
|
163
|
+
df_typed[col] = df_raw[col]
|
|
164
|
+
else:
|
|
165
|
+
df_typed[col] = df_raw[col]
|
|
166
|
+
else:
|
|
167
|
+
df_typed[col] = df_raw[col]
|
|
168
|
+
|
|
169
|
+
return df_typed
|
|
170
|
+
|
|
171
|
+
else:
|
|
172
|
+
# If we don't have the original dataframe, try to infer types
|
|
173
|
+
df_raw.replace('NULL', np.nan, inplace=True)
|
|
174
|
+
|
|
175
|
+
for col in df_raw.columns:
|
|
176
|
+
try:
|
|
177
|
+
df_raw[col] = pd.to_numeric(df_raw[col].str.replace(',', ''))
|
|
178
|
+
except:
|
|
179
|
+
try:
|
|
180
|
+
df_raw[col] = pd.to_datetime(df_raw[col])
|
|
181
|
+
except:
|
|
182
|
+
try:
|
|
183
|
+
if df_raw[col].dropna().isin(['True', 'False']).all():
|
|
184
|
+
df_raw[col] = df_raw[col].map({'True': True, 'False': False})
|
|
185
|
+
except:
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
return df_raw
|