sqlshell 0.2.2__py3-none-any.whl → 0.3.0__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.

@@ -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] = 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
76
+ df.to_parquet(file_name, index=False)
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] = 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
@@ -0,0 +1,127 @@
1
+ """
2
+ Integration module to add F5/F9 execution functionality to SQLEditor.
3
+ This module provides a clean way to enhance the editor without modifying the original code.
4
+ """
5
+
6
+ from PyQt6.QtCore import Qt
7
+ from PyQt6.QtWidgets import QPlainTextEdit
8
+ from .execution_handler import SQLExecutionHandler, ExecutionKeyHandler
9
+
10
+
11
+ class EditorExecutionIntegration:
12
+ """
13
+ Integration class to add F5/F9 execution functionality to SQLEditor.
14
+ """
15
+
16
+ def __init__(self, editor: QPlainTextEdit, execute_callback=None):
17
+ """
18
+ Initialize the integration.
19
+
20
+ Args:
21
+ editor: The SQLEditor instance
22
+ execute_callback: Function to call to execute queries
23
+ """
24
+ self.editor = editor
25
+ self.execution_handler = SQLExecutionHandler(execute_callback)
26
+ self.key_handler = ExecutionKeyHandler(self.execution_handler)
27
+
28
+ # Store original keyPressEvent to preserve existing functionality
29
+ self.original_key_press_event = editor.keyPressEvent
30
+
31
+ # Replace keyPressEvent with our enhanced version
32
+ editor.keyPressEvent = self.enhanced_key_press_event
33
+
34
+ def set_execute_callback(self, callback):
35
+ """Set the execution callback function."""
36
+ self.execution_handler.set_execute_callback(callback)
37
+
38
+ def enhanced_key_press_event(self, event):
39
+ """Enhanced keyPressEvent that handles F5/F9 while preserving original functionality."""
40
+
41
+ # First, try to handle F5/F9 keys
42
+ if self.key_handler.handle_key_press(self.editor, event.key(), event.modifiers()):
43
+ # Key was handled by our execution handler
44
+ return
45
+
46
+ # If not F5/F9, use the original keyPressEvent
47
+ self.original_key_press_event(event)
48
+
49
+ def execute_all_statements(self):
50
+ """Execute all statements in the editor (F5 functionality)."""
51
+ try:
52
+ return self.execution_handler.execute_from_editor(self.editor, "all")
53
+ except Exception as e:
54
+ print(f"Error executing all statements: {e}")
55
+ return None
56
+
57
+ def execute_current_statement(self):
58
+ """Execute the current statement (F9 functionality)."""
59
+ try:
60
+ return self.execution_handler.execute_from_editor(self.editor, "current")
61
+ except Exception as e:
62
+ print(f"Error executing current statement: {e}")
63
+ return None
64
+
65
+ def get_current_statement_info(self):
66
+ """Get information about the current statement at cursor position."""
67
+ text = self.editor.toPlainText()
68
+ cursor = self.editor.textCursor()
69
+ cursor_position = cursor.position()
70
+
71
+ current_stmt = self.execution_handler.get_current_statement(text, cursor_position)
72
+ if current_stmt:
73
+ stmt_text, start_pos, end_pos = current_stmt
74
+ return {
75
+ 'text': stmt_text,
76
+ 'start': start_pos,
77
+ 'end': end_pos,
78
+ 'cursor_position': cursor_position
79
+ }
80
+ return None
81
+
82
+ def get_all_statements_info(self):
83
+ """Get information about all statements in the editor."""
84
+ text = self.editor.toPlainText()
85
+ statements = self.execution_handler.parse_sql_statements(text)
86
+
87
+ return [
88
+ {
89
+ 'text': stmt_text,
90
+ 'start': start_pos,
91
+ 'end': end_pos,
92
+ 'index': i
93
+ }
94
+ for i, (stmt_text, start_pos, end_pos) in enumerate(statements)
95
+ ]
96
+
97
+
98
+ def integrate_execution_functionality(editor: QPlainTextEdit, execute_callback=None):
99
+ """
100
+ Convenience function to integrate F5/F9 execution functionality into an editor.
101
+
102
+ Args:
103
+ editor: The SQLEditor instance
104
+ execute_callback: Function to call to execute queries
105
+
106
+ Returns:
107
+ EditorExecutionIntegration instance for further customization
108
+ """
109
+ integration = EditorExecutionIntegration(editor, execute_callback)
110
+
111
+ # Store the integration instance on the editor for later access
112
+ editor._execution_integration = integration
113
+
114
+ return integration
115
+
116
+
117
+ def get_execution_integration(editor: QPlainTextEdit):
118
+ """
119
+ Get the execution integration instance from an editor.
120
+
121
+ Args:
122
+ editor: The SQLEditor instance
123
+
124
+ Returns:
125
+ EditorExecutionIntegration instance or None if not integrated
126
+ """
127
+ return getattr(editor, '_execution_integration', None)