From 883adeaaff4bc47f5701589ed04ea11aadc2cbd0 Mon Sep 17 00:00:00 2001 From: Lea Laux Date: Wed, 24 Feb 2021 13:03:59 +0100 Subject: [PATCH] CSV export improvement: Full table Export the full table to a CSV file in the context menu for a table node in the tree model. Use the csv exporter as refactored tool. The exporter can work with database parameters including the name of the table or the resulting data list. TODO: Documentation --- pygadmin/csv_exporter.py | 145 +++++++++++++++++++++++++++++++++++++ pygadmin/widgets/editor.py | 59 ++------------- pygadmin/widgets/tree.py | 21 ++++++ 3 files changed, 172 insertions(+), 53 deletions(-) create mode 100644 pygadmin/csv_exporter.py diff --git a/pygadmin/csv_exporter.py b/pygadmin/csv_exporter.py new file mode 100644 index 0000000..57a8712 --- /dev/null +++ b/pygadmin/csv_exporter.py @@ -0,0 +1,145 @@ +import logging + +from PyQt5.QtWidgets import QFileDialog, QMessageBox + +from pygadmin.database_query_executor import DatabaseQueryExecutor +from pygadmin.connectionfactory import global_connection_factory + + +class CSVExporter: + """ + Create a class for exporting the current results to a CSV file. TODO: Docu + """ + + def __init__(self, parent, data_list, database_connection_parameters=None, table_name=None): + """ + Get the parent for assigning the file dialog to a widget and the data list for the data, which will be saved. + """ + + # If the data list is None and the database connection parameters are None, there is no way to get the required + # data for the csv file. + if data_list is None and database_connection_parameters is None: + # Show the error to the user and end the initialization. + QMessageBox.critical(parent, "Data Error", "A data source for exporting to CSV is not given.") + return + + # Define the parent, the data list and the file name as attributes. + self.parent = parent + self.data_list = data_list + self.file_name = None + + # If the data list is None, the required data needs to be collected. + if self.data_list is None: + # Prepare the data list with building the query and preparing the database query executor. + self.prepare_data_list(database_connection_parameters, table_name) + # Set the success attribute to False, because the csv export is still in process and not successful at this + # point in the program. + self.success = False + + def prepare_data_list(self, database_connection_parameters, table_name): + """ + Prepare the relevant parts for getting the data list with the database connection, the database query executor + and the database query. + """ + + database_connection = self.get_database_connection(database_connection_parameters) + + if database_connection is None: + return + + query = "SELECT * FROM {};".format(table_name) + + self.database_query_executor = DatabaseQueryExecutor() + self.database_query_executor.result_data.connect(self.get_result_data_list) + self.database_query_executor.database_connection = database_connection + self.database_query_executor.database_query = query + + def get_database_connection(self, database_connection_parameters): + database_connection = global_connection_factory.get_database_connection(database_connection_parameters["host"], + database_connection_parameters["user"], + database_connection_parameters[ + "database"], + database_connection_parameters["port"]) + + if database_connection is None or database_connection is False: + QMessageBox.critical(self.parent, "Database Connection Fail", "The connection to the database failed, " + "please check the log for further " + "information.") + + return None + + return database_connection + + def get_result_data_list(self, data_list): + if data_list: + self.data_list = data_list + self.success = True + self.export_and_save_csv_data() + + def get_file_name_by_file_dialog(self): + """ + Create a file dialog for activating the csv export. + """ + + # Get a csv file with the default name result.csv and the file type csv. + file_dialog_with_name_and_type = QFileDialog.getSaveFileName(self.parent, "Save File", "result.csv", + "CSV (*.csv)") + + # Get the file name. + file_name = file_dialog_with_name_and_type[0] + + # If the file name is not an empty string, return the file name, because in this case, the user has entered one. + if file_name != "": + return file_name + + # Inform the user in the log about the abort. + else: + logging.info("The current file saving process was aborted by the user, so the current result as csv file" + " is not saved.") + + # Return False, because there is no file name. + return False + + def export_result_to_csv(self, file_name): + """ + Export the result data to csv with the file name. Get the data out of the table model. + """ + + # Open the given file in write mode. + with open(file_name, "w") as file_to_save: + # Get through every data row in the data list. + for data_row in self.data_list: + # Get through every value in the data row. + for data_value_counter in range(len(data_row)): + # Write every value. + file_to_save.write(str(data_row[data_value_counter])) + + # If the value is not the last one, append a comma for comma separated value. + if data_value_counter != len(data_row) - 1: + file_to_save.write(",") + + # Write a newline at the end of a data row. + file_to_save.write("\n") + + def export_and_save_csv_data(self): + """ + Activate the export and save of the csv data. + """ + + if self.file_name is None: + # Get the file name with a file dialog. + self.file_name = self.get_file_name_by_file_dialog() + + # If the file name is False, the process of saving the file has been aborted. + if self.file_name is False: + # End the function with a return. + return + + if self.data_list is None: + self.database_query_executor.submit_and_execute_query() + return + + # Save the current query result data. + self.export_result_to_csv(self.file_name) + + return True diff --git a/pygadmin/widgets/editor.py b/pygadmin/widgets/editor.py index 07dc9fa..8dcc444 100644 --- a/pygadmin/widgets/editor.py +++ b/pygadmin/widgets/editor.py @@ -10,6 +10,7 @@ from PyQt5.QtWidgets import QWidget, QGridLayout, QPushButton, QTableView, QMess QCheckBox, QLabel, qApp from pygadmin.connectionfactory import global_connection_factory +from pygadmin.csv_exporter import CSVExporter from pygadmin.models.tablemodel import TableModel from pygadmin.configurator import global_app_configurator from pygadmin.models.lexer import SQLLexer @@ -1074,60 +1075,12 @@ class EditorWidget(QWidget, SearchReplaceParent, metaclass=MetaEditor): Activate the export and save of the csv data. """ - # Get the file name with a file dialog. - file_name = self.activate_file_dialog_for_csv_export() + # TODO: Docu + csv_exporter = CSVExporter(self, self.table_model.data_list) + success = csv_exporter.export_and_save_csv_data() - # If the file name is None, the process of saving the file has been aborted. - if file_name is None: - # End the function with a return. - return - - # Save the current query result data. - self.export_result_to_csv(file_name) - - def activate_file_dialog_for_csv_export(self): - """ - Create a file dialog for activating the csv export. - """ - - # Get a csv file with the default name result.csv and the file type csv. - file_dialog_with_name_and_type = QFileDialog.getSaveFileName(self, "Save File", "result", "CSV (*.csv)") - - # Get the file name. - file_name = file_dialog_with_name_and_type[0] - - # If the file name is not an empty string, return the file name, because in this case, the user has entered one. - if file_name != "": - return file_name - - # Inform the user in the log about the abort. - else: - logging.info("The current file saving process was aborted by the user, so the current result as csv file" - " is not saved.") - - # Return None, because there is no file name. - return None - - def export_result_to_csv(self, file_name): - """ - Export the result data to csv with the file name. Get the data out of the table model. - """ - - # Open the given file in write mode. - with open(file_name, "w") as file_to_save: - # Get through every data row in the data list. - for data_row in self.table_model.data_list: - # Get through every value in the data row. - for data_value_counter in range(len(data_row)): - # Write every value. - file_to_save.write(str(data_row[data_value_counter])) - - # If the value is not the last one, append a comma for comma separated value. - if data_value_counter != len(data_row) - 1: - file_to_save.write(",") - - # Write a newline at the end of a data row. - file_to_save.write("\n") + if success: + QMessageBox.information(self, "Export Success", "The csv export was successful.") def get_explain_analyze_query(self): """ diff --git a/pygadmin/widgets/tree.py b/pygadmin/widgets/tree.py index 4463cba..0f47f53 100644 --- a/pygadmin/widgets/tree.py +++ b/pygadmin/widgets/tree.py @@ -8,6 +8,7 @@ from PyQt5.QtWidgets import QWidget, QTreeView, QAbstractItemView, QMessageBox, from pygadmin.configurator import global_app_configurator from pygadmin.connectionstore import global_connection_store +from pygadmin.csv_exporter import CSVExporter from pygadmin.models.treemodel import ServerNode, TablesNode, ViewsNode, SchemaNode, AbstractBaseNode, DatabaseNode, \ TableNode, ViewNode from pygadmin.widgets.materialized_view_information import MaterializedViewInformationDialog @@ -311,6 +312,8 @@ class TreeWidget(QWidget): self.context_menu.addAction(show_permission_information_action) edit_single_values_action = QAction("Edit Single Values", self) self.context_menu.addAction(edit_single_values_action) + export_full_table_to_csv_action = QAction("Export Full Table As CSV", self) + self.context_menu.addAction(export_full_table_to_csv_action) # Get the action at the current position of the triggering event. position_action = self.context_menu.exec_(self.tree_view.viewport().mapToGlobal(position)) @@ -334,6 +337,9 @@ class TreeWidget(QWidget): elif position_action == edit_single_values_action: self.show_edit_singles_values_dialog(current_selected_node) + elif position_action == export_full_table_to_csv_action: + self.get_full_data_of_current_table_for_csv_export(current_selected_node) + def append_new_connection_parameters_and_node(self): """ Get new parameters out of the .yaml file, where all connections are stored, because this function should be @@ -978,3 +984,18 @@ class TreeWidget(QWidget): """ self.materialized_view_information_dialog = MaterializedViewInformationDialog(current_node) + + def get_full_data_of_current_table_for_csv_export(self, current_node): + # TODO: Docu + self.csv_exporter = CSVExporter(self, None, {"host": current_node.database_connection_parameters["host"], + "user": current_node.database_connection_parameters["user"], + "database": + current_node.database_connection_parameters["database"], + "port": current_node.database_connection_parameters["port"]}, + current_node.name) + + self.csv_exporter.export_and_save_csv_data() + self.csv_exporter.database_query_executor.result_data.connect(lambda: + QMessageBox.information(self, "Export Success", + "The csv export was " + "successful."))