Insert and Create/Insert in csv importer
Inserting the data in the csv import dialog is now possible. In addition, create/insert in one action is possible. The building process for the activation in the main window has started.
This commit is contained in:
parent
ef9d6e6a20
commit
5b4ff89abb
@ -30,6 +30,10 @@ class CSVImporter:
|
|||||||
self.database_query_executor = DatabaseQueryExecutor()
|
self.database_query_executor = DatabaseQueryExecutor()
|
||||||
# Use the given database connection for further execution of database queries on the given database.
|
# Use the given database connection for further execution of database queries on the given database.
|
||||||
self.database_query_executor.database_connection = database_connection
|
self.database_query_executor.database_connection = database_connection
|
||||||
|
# Define a chunk size for splitting the data in separate chunks for inserting later. 5000 is an acceptable value
|
||||||
|
# between the two extreme cases (inserting all data in one INSERT or inserting every row/list with an own
|
||||||
|
# INSERT). A class attribute is chosen for potential (read) access.
|
||||||
|
self.chunk_size = 5000
|
||||||
|
|
||||||
def check_existence_csv_file(self):
|
def check_existence_csv_file(self):
|
||||||
"""
|
"""
|
||||||
@ -217,31 +221,32 @@ class CSVImporter:
|
|||||||
# Check the name of the table.
|
# Check the name of the table.
|
||||||
self.table_name = self.check_ddl_parameter(self.table_name)
|
self.table_name = self.check_ddl_parameter(self.table_name)
|
||||||
|
|
||||||
def create_and_execute_insert_queries(self):
|
def create_and_execute_insert_queries(self, table_name=None):
|
||||||
"""
|
"""
|
||||||
Create the necessary queries for inserting the data in the table based on splitting the data in sub lists for
|
Create the necessary queries for inserting the data in the table based on splitting the data in sub lists for
|
||||||
improving the performance of the insert. The attribute of the class for the csv data should not be harmed, so
|
improving the performance of the insert. The attribute of the class for the csv data should not be harmed, so
|
||||||
a copy is used for the working process. Execute the queries after their creation.
|
a copy is used for the working process. The default for the table name is None, so the attribute of the class
|
||||||
|
for the table name is used. Execute the queries after their creation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Use the table name defined by the class/csv file as table name.
|
||||||
|
if table_name is None:
|
||||||
|
table_name = self.table_name
|
||||||
|
|
||||||
# Copy the data list, so the work data list can be used and modified.
|
# Copy the data list, so the work data list can be used and modified.
|
||||||
work_data_list = copy.copy(self.csv_data)
|
work_data_list = copy.copy(self.csv_data)
|
||||||
# Delete the header, because the header does not have to be inserted.
|
# Delete the header, because the header does not have to be inserted.
|
||||||
del work_data_list[0]
|
del work_data_list[0]
|
||||||
|
|
||||||
# Define a chunk size for separation the work data list in those chunks. 5000 is an acceptable value between the
|
|
||||||
# two extreme cases (inserting all data in one INSERT or inserting every row/list with an own INSERT).
|
|
||||||
chunk_size = 5000
|
|
||||||
|
|
||||||
# Split the work data list in lists with the given chunk size.
|
# Split the work data list in lists with the given chunk size.
|
||||||
work_data_list = [work_data_list[i * chunk_size:(i+1) * chunk_size]
|
work_data_list = [work_data_list[i * self.chunk_size:(i+1) * self.chunk_size]
|
||||||
for i in range((len(work_data_list) + chunk_size - 1) // chunk_size)]
|
for i in range((len(work_data_list) + self.chunk_size - 1) // self.chunk_size)]
|
||||||
|
|
||||||
# Iterate over every sub list in the work data list. Those sub lists contain their separate chunk of data for
|
# Iterate over every sub list in the work data list. Those sub lists contain their separate chunk of data for
|
||||||
# inserting.
|
# inserting.
|
||||||
for sub_data_list in work_data_list:
|
for sub_data_list in work_data_list:
|
||||||
# Get the begin of an insert query.
|
# Get the begin of an insert query with the given table name.
|
||||||
insert_query = self.create_insert_query_begin()
|
insert_query = self.create_insert_query_begin(table_name)
|
||||||
# Define a list for the parameters, because the data is used as parameter in the query.
|
# Define a list for the parameters, because the data is used as parameter in the query.
|
||||||
parameter_list = []
|
parameter_list = []
|
||||||
|
|
||||||
@ -292,13 +297,17 @@ class CSVImporter:
|
|||||||
# Execute the insert query.
|
# Execute the insert query.
|
||||||
self.execute_query(insert_query, parameter_list)
|
self.execute_query(insert_query, parameter_list)
|
||||||
|
|
||||||
def create_insert_query_begin(self):
|
def create_insert_query_begin(self, table_name=None):
|
||||||
"""
|
"""
|
||||||
Create the begin of an insert query.
|
Create the begin of an insert query. The default table name is None, so the name of the csv file is used.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Get the default name, the name of the csv file.
|
||||||
|
if table_name is None:
|
||||||
|
table_name = self.table_name
|
||||||
|
|
||||||
# Begin with the INSERT INTO and the checked table name, so an formatted input is okay.
|
# Begin with the INSERT INTO and the checked table name, so an formatted input is okay.
|
||||||
insert_query = "INSERT INTO {} (".format(self.table_name)
|
insert_query = "INSERT INTO {} (".format(table_name)
|
||||||
|
|
||||||
# Get the header for the column names.
|
# Get the header for the column names.
|
||||||
header = self.csv_data[0]
|
header = self.csv_data[0]
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import sys
|
from PyQt5.QtWidgets import QDialog, QGridLayout, QLabel, QPushButton, QMessageBox, QLineEdit, \
|
||||||
import time
|
QInputDialog
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QDialog, QGridLayout, QLabel, QApplication, QPushButton, QMessageBox, QLineEdit, \
|
from pygadmin.csv_importer import CSVImporter
|
||||||
|
from pygadmin.widgets.widget_icon_adder import IconAdder
|
||||||
|
from PyQt5.QtWidgets import QDialog, QGridLayout, QLabel, QPushButton, QMessageBox, QLineEdit, \
|
||||||
QInputDialog
|
QInputDialog
|
||||||
|
|
||||||
from pygadmin.connectionfactory import global_connection_factory
|
|
||||||
from pygadmin.csv_importer import CSVImporter
|
from pygadmin.csv_importer import CSVImporter
|
||||||
from pygadmin.widgets.widget_icon_adder import IconAdder
|
from pygadmin.widgets.widget_icon_adder import IconAdder
|
||||||
|
|
||||||
@ -56,7 +57,6 @@ class CSVImportDialog(QDialog):
|
|||||||
self.init_ui()
|
self.init_ui()
|
||||||
# Initialize the grid layout.
|
# Initialize the grid layout.
|
||||||
self.init_grid()
|
self.init_grid()
|
||||||
# TODO: Better functions for connecting for the signals of the database query executor.
|
|
||||||
|
|
||||||
# Show an error for a non existing file.
|
# Show an error for a non existing file.
|
||||||
else:
|
else:
|
||||||
@ -145,16 +145,29 @@ class CSVImportDialog(QDialog):
|
|||||||
|
|
||||||
# Create a drop button for dropping the table.
|
# Create a drop button for dropping the table.
|
||||||
self.drop_button = QPushButton("Drop Table")
|
self.drop_button = QPushButton("Drop Table")
|
||||||
# Connect the button with the signal for actually dropping the table.
|
# Connect the button with the function for actually dropping the table.
|
||||||
self.drop_button.clicked.connect(self.drop_table)
|
self.drop_button.clicked.connect(self.drop_table)
|
||||||
|
|
||||||
# Connect the signals for the result data and the error with functions for processing.
|
# Create an insert button for inserting the data.
|
||||||
self.csv_importer.database_query_executor.result_data.connect(self.process_create_drop_success)
|
self.insert_button = QPushButton("Insert Data")
|
||||||
self.csv_importer.database_query_executor.error.connect(self.process_create_drop_error)
|
# Connect the button with the function for inserting the data.
|
||||||
|
|
||||||
# Under construction TODO
|
|
||||||
self.insert_button = QPushButton("Insert")
|
|
||||||
self.insert_button.clicked.connect(self.insert_data)
|
self.insert_button.clicked.connect(self.insert_data)
|
||||||
|
|
||||||
|
self.create_and_insert_button = QPushButton("Create Table and Insert Data")
|
||||||
|
self.create_and_insert_button.clicked.connect(self.create_table_and_insert_data)
|
||||||
|
|
||||||
|
# Define two parameters/attributes for controlling the number of expected inserts.
|
||||||
|
self.number_of_inserts = 0
|
||||||
|
self.executed_inserts = 0
|
||||||
|
|
||||||
|
# Define an attribute for the usage after a create statement: If the attribute is True, the data is inserted
|
||||||
|
# right after the creation of the table.
|
||||||
|
self.proceed_with_insert = False
|
||||||
|
|
||||||
|
# Connect the signals for the result data and the error with functions for processing.
|
||||||
|
self.csv_importer.database_query_executor.result_data.connect(self.process_sql_success)
|
||||||
|
self.csv_importer.database_query_executor.error.connect(self.process_sql_error)
|
||||||
|
|
||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def init_grid(self):
|
def init_grid(self):
|
||||||
@ -185,10 +198,13 @@ class CSVImportDialog(QDialog):
|
|||||||
|
|
||||||
# Place the close label at the end of CREATE.
|
# Place the close label at the end of CREATE.
|
||||||
grid_layout.addWidget(self.close_label, line_edit_count, 0)
|
grid_layout.addWidget(self.close_label, line_edit_count, 0)
|
||||||
# Set the create button at the end of the statement, created by the line edits.
|
# Set the create and insert button at the end of the statement, created by the line edits.
|
||||||
grid_layout.addWidget(self.create_button, line_edit_count, 1)
|
grid_layout.addWidget(self.create_and_insert_button, line_edit_count, 1)
|
||||||
# Set the drop button on the right of the widget.
|
# Set the drop button on the right of the create and insert button.
|
||||||
grid_layout.addWidget(self.drop_button, 0, 4)
|
grid_layout.addWidget(self.drop_button, line_edit_count, 2)
|
||||||
|
# Set the insert and the create button under the row with the create and insert button.
|
||||||
|
grid_layout.addWidget(self.insert_button, line_edit_count+1, 0)
|
||||||
|
grid_layout.addWidget(self.create_button, line_edit_count+1, 1)
|
||||||
|
|
||||||
# Set the spacing of the grid.
|
# Set the spacing of the grid.
|
||||||
grid_layout.setSpacing(10)
|
grid_layout.setSpacing(10)
|
||||||
@ -218,8 +234,6 @@ class CSVImportDialog(QDialog):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
create_statement = self.get_user_create_statement()
|
create_statement = self.get_user_create_statement()
|
||||||
self.csv_importer.database_query_executor.result_data.connect(self.process_create_drop_success)
|
|
||||||
self.csv_importer.database_query_executor.error.connect(self.process_create_drop_error)
|
|
||||||
self.csv_importer.create_table_for_csv_data(create_statement)
|
self.csv_importer.create_table_for_csv_data(create_statement)
|
||||||
|
|
||||||
def get_user_create_statement(self):
|
def get_user_create_statement(self):
|
||||||
@ -267,32 +281,92 @@ class CSVImportDialog(QDialog):
|
|||||||
table_name = self.table_name_line_edit.text()
|
table_name = self.table_name_line_edit.text()
|
||||||
self.csv_importer.drop_table(table_name)
|
self.csv_importer.drop_table(table_name)
|
||||||
|
|
||||||
def process_create_drop_success(self, result):
|
def process_sql_success(self, result):
|
||||||
"""
|
"""
|
||||||
Process the result of the table creation or dropping.
|
Process the result of the table creation or dropping.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The parameter result contains a list. If the list is not empty, a success can be processed.
|
# The parameter result contains a list. If the list is not empty, a success can be processed.
|
||||||
if result:
|
if result:
|
||||||
QMessageBox.information(self, "Create Success", "The result is {}".format(result))
|
# Get the title of the result list for further usage as QMessageBox title.
|
||||||
|
title = result[0][0]
|
||||||
|
# A data definition query has been executed.
|
||||||
|
if self.number_of_inserts == 0 and self.executed_inserts == 0:
|
||||||
|
# Get the message.
|
||||||
|
message = result[1][0]
|
||||||
|
# Show the title and the message to the user.
|
||||||
|
QMessageBox.information(self, title, message)
|
||||||
|
|
||||||
def process_create_drop_error(self, result):
|
# Check the attribute for proceeding with the insert of the data.
|
||||||
|
if self.proceed_with_insert is True:
|
||||||
|
# Insert the data of the csv file.
|
||||||
|
self.insert_data()
|
||||||
|
|
||||||
|
# The inserts are not done yet.
|
||||||
|
elif self.number_of_inserts != self.executed_inserts:
|
||||||
|
# Increment the insert counter for executed inserts.
|
||||||
|
self.executed_inserts += 1
|
||||||
|
|
||||||
|
# The inserts are done.
|
||||||
|
elif self.number_of_inserts == self.executed_inserts:
|
||||||
|
# Show the success to the user.
|
||||||
|
QMessageBox.information(self, title, "The data has been inserted successfully.")
|
||||||
|
# Reset the number of excepted inserts and the number of executed inserts back to 0.
|
||||||
|
self.number_of_inserts = 0
|
||||||
|
self.executed_inserts = 0
|
||||||
|
|
||||||
|
def process_sql_error(self, result):
|
||||||
"""
|
"""
|
||||||
Process the error during the create or drop process.
|
Process the SQL error after the try to execute a query.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
QMessageBox.critical(self, "Create Error", "An error occurred during the create process: {}".format(result))
|
# Get the title of the error message.
|
||||||
|
title = result[0]
|
||||||
|
# Get the error message.
|
||||||
|
message = result[1]
|
||||||
|
|
||||||
|
# This function defines the error case, so if a table should have been created and the data inserted, set the
|
||||||
|
# attribute for proceeding with an insert to False, which is the default value for this attribute.
|
||||||
|
if self.proceed_with_insert is True:
|
||||||
|
self.proceed_with_insert = False
|
||||||
|
|
||||||
|
# A data definition query should have been executed.
|
||||||
|
if self.number_of_inserts == 0 and self.executed_inserts == 0:
|
||||||
|
QMessageBox.critical(self, title, message)
|
||||||
|
|
||||||
|
# The inserts are not done yet.
|
||||||
|
elif self.number_of_inserts != self.executed_inserts:
|
||||||
|
# Increment the counter for one processed insert.
|
||||||
|
self.executed_inserts += 1
|
||||||
|
|
||||||
|
# The inserts are done.
|
||||||
|
elif self.number_of_inserts == self.executed_inserts:
|
||||||
|
# Show the insert error
|
||||||
|
QMessageBox.critical(self, "Insert Error", "An error occurred during the insert: {}".format(message))
|
||||||
|
# Reset the number of excepted inserts and the number of executed inserts back to 0.
|
||||||
|
self.number_of_inserts = 0
|
||||||
|
self.executed_inserts = 0
|
||||||
|
|
||||||
# under construction: TODO
|
|
||||||
def insert_data(self):
|
def insert_data(self):
|
||||||
begin = time.time()
|
"""
|
||||||
self.csv_importer.create_and_execute_insert_queries()
|
Insert the data of the csv file into the given table.
|
||||||
end = time.time()
|
"""
|
||||||
print("Runtime: {}".format(end - begin))
|
|
||||||
|
|
||||||
|
self.proceed_with_insert = False
|
||||||
|
|
||||||
if __name__ == "__main__":
|
# Get the table name out of the line edit.
|
||||||
app = QApplication(sys.argv)
|
table_name = self.table_name_line_edit.text()
|
||||||
csv_import = CSVImportDialog(global_connection_factory.get_database_connection("localhost", "testuser", "testdb"),
|
# Calculate the expected number of inserts by the chunk size of the csv importer.
|
||||||
"/home/sqlea/test.csv")
|
self.number_of_inserts = (len(self.csv_importer.csv_data) - 2) // self.csv_importer.chunk_size
|
||||||
sys.exit(app.exec())
|
# Start the insert process.
|
||||||
|
self.csv_importer.create_and_execute_insert_queries(table_name)
|
||||||
|
|
||||||
|
def create_table_and_insert_data(self):
|
||||||
|
"""
|
||||||
|
Create the table and insert the data.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Set the attribute for inserting the data to True.
|
||||||
|
self.proceed_with_insert = True
|
||||||
|
# Create the table. The insert will be triggered, if the create statement is successful.
|
||||||
|
self.create_table()
|
||||||
|
@ -3,7 +3,7 @@ import logging
|
|||||||
|
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
from PyQt5.QtGui import QIcon, QPixmap
|
from PyQt5.QtGui import QIcon, QPixmap
|
||||||
from PyQt5.QtWidgets import QMainWindow, QAction, QToolBar, QMessageBox, QMenu
|
from PyQt5.QtWidgets import QMainWindow, QAction, QToolBar, QMessageBox, QMenu, QFileDialog
|
||||||
from PyQt5.QtCore import Qt, pyqtSlot
|
from PyQt5.QtCore import Qt, pyqtSlot
|
||||||
|
|
||||||
import pygadmin
|
import pygadmin
|
||||||
@ -140,6 +140,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.add_action_to_menu_bar("Change Database Connections", self.activate_new_connection_dialog)
|
self.add_action_to_menu_bar("Change Database Connections", self.activate_new_connection_dialog)
|
||||||
# Create an action for showing the current history.
|
# Create an action for showing the current history.
|
||||||
self.add_action_to_menu_bar("Show History", self.activate_command_history_dialog)
|
self.add_action_to_menu_bar("Show History", self.activate_command_history_dialog)
|
||||||
|
self.add_action_to_menu_bar("Import CSV", self.activate_csv_import)
|
||||||
# Create a sub menu for settings.
|
# Create a sub menu for settings.
|
||||||
settings_menu = QMenu("Settings", self)
|
settings_menu = QMenu("Settings", self)
|
||||||
# Add the sub menu to the edit menu point.
|
# Add the sub menu to the edit menu point.
|
||||||
@ -465,6 +466,20 @@ class MainWindow(QMainWindow):
|
|||||||
# Set the text to the query input editor of the editor widget.
|
# Set the text to the query input editor of the editor widget.
|
||||||
empty_editor_widget.query_input_editor.setText(command)
|
empty_editor_widget.query_input_editor.setText(command)
|
||||||
|
|
||||||
|
def activate_csv_import(self):
|
||||||
|
"""
|
||||||
|
Activate the necessary steps for starting the csv import dialog. This process includes getting the csv file by
|
||||||
|
a file dialog and getting the current database connection. TODO: Think about a better place for the importer or
|
||||||
|
error for missing database connection.
|
||||||
|
"""
|
||||||
|
|
||||||
|
file_name_and_type = QFileDialog.getOpenFileName(self, "Open CSV", "", "CSV (*.csv)")
|
||||||
|
file_name = file_name_and_type[0]
|
||||||
|
|
||||||
|
# The user has aborted the process, so the file name is an empty string, which is useless.
|
||||||
|
if file_name == "":
|
||||||
|
return
|
||||||
|
|
||||||
@pyqtSlot(str)
|
@pyqtSlot(str)
|
||||||
def show_status_bar_message(self, message):
|
def show_status_bar_message(self, message):
|
||||||
"""
|
"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user