From ee5ba1eafe65a4a1efe99be033e3dcfe0c268248 Mon Sep 17 00:00:00 2001 From: Lea Laux Date: Tue, 8 Dec 2020 16:42:42 +0100 Subject: [PATCH] Load one or all database nodes for a server With a given connection, it is now possible to specify the number of database nodes for a server: Only one node with the explicit given database can be loaded or all databases on the server. This is realized by an extra parameter for the database connection in the tree model and connection dialog. --- pygadmin/connectionstore.py | 39 +++++++++++++++++++++++++++ pygadmin/models/treemodel.py | 28 ++++++++++++++----- pygadmin/widgets/connection_dialog.py | 35 ++++++++++++++++++++---- pygadmin/widgets/tree.py | 20 ++++++++++++-- 4 files changed, 109 insertions(+), 13 deletions(-) diff --git a/pygadmin/connectionstore.py b/pygadmin/connectionstore.py index 3c79eca..9453c83 100644 --- a/pygadmin/connectionstore.py +++ b/pygadmin/connectionstore.py @@ -118,6 +118,28 @@ class ConnectionStore: if connection_parameter_dictionary["Username"] == connection_parameter_dictionary_for_check["Username"] \ and connection_parameter_dictionary["Host"] == connection_parameter_dictionary_for_check["Host"] \ and connection_parameter_dictionary["Port"] == connection_parameter_dictionary_for_check["Port"]: + + # If the two dictionaries contain a parameter for loading all databases, check them for their equality. + # If they are not equal, the given connection has changed. + if "Load All" in connection_parameter_dictionary \ + and "Load All" in connection_parameter_dictionary_for_check: + if connection_parameter_dictionary["Load All"] \ + != connection_parameter_dictionary_for_check["Load All"]: + return False + + # If only one dictionary has a key for "Load All", there is a change in the connection parameters. + elif "Load All" in connection_parameter_dictionary \ + or "Load All" in connection_parameter_dictionary_for_check: + return False + + # If load all in the check dictionary is False and if the databases are not the same, the connection has + # changed, because only one database is used. One database is not a universal enter point in this case. + if "Load All" in connection_parameter_dictionary_for_check \ + and connection_parameter_dictionary_for_check["Load All"] is False \ + and connection_parameter_dictionary["Database"] \ + != connection_parameter_dictionary_for_check["Database"]: + return False + return True # If a duplicate is not found, this else branch is active. @@ -217,5 +239,22 @@ class ConnectionStore: except IndexError: return None + def get_connection_load_all_information(self, host, database, port, username): + """ + Get the load all information of one connection defined by its host, database, port and username. + """ + + # Find in all current parameters the one with the correct host, database, port and username. + for parameters in self.connection_parameters_yaml: + if host == parameters["Host"] and database == parameters["Database"] and port == parameters["Port"] \ + and username == parameters["Username"]: + + # Try to get the parameter for loading all databases. This parameter could not exist in the dictionary. + try: + return parameters["Load All"] + + except KeyError: + return False + global_connection_store = ConnectionStore() diff --git a/pygadmin/models/treemodel.py b/pygadmin/models/treemodel.py index 99c0d73..fc76e7b 100644 --- a/pygadmin/models/treemodel.py +++ b/pygadmin/models/treemodel.py @@ -199,7 +199,7 @@ class ServerNode(AbstractBaseNode): Create a class for server nodes based on the class AbstractBaseNode. """ - def __init__(self, name, host, user, port, database="postgres", timeout=1000): + def __init__(self, name, host, user, port, database="postgres", timeout=1000, load_all_databases=True): # Use the database postgres, because this database should definitely exist on a database server and is used as # entry point here. super().__init__(name, host, user, database, port, timeout) @@ -212,8 +212,16 @@ class ServerNode(AbstractBaseNode): node_icon_path = os.path.join(os.path.dirname(pygadmin.__file__), "icons", "{}_valid.svg".format(self._node_type.lower())) + # If all databases should be loaded, this parameter is True. This is a default parameter. + if load_all_databases is True: + child = None + + # If the parameter is not True, only one database should be loaded. + else: + child = database + # Get the children with a query. - self.get_children_with_query() + self.get_children_with_query(child) else: # Use the server icon for a invalid connection. @@ -232,13 +240,21 @@ class ServerNode(AbstractBaseNode): return child - def get_children_with_query(self): + def get_children_with_query(self, child=None): """ - Get all children of the node with a database query. + Get all children of the node with a database query. As default, get all the children of the node, but use an + option for only getting one given child. """ - # Use a query to get all children, in this case, all the database nodes. - self.fetch_children(DatabaseNode, "SELECT datname FROM pg_database ORDER BY datname ASC;") + # If one explicit child is not given, get all the children of the server node. + if child is None: + # Use a query to get all children, in this case, all the database nodes. + self.fetch_children(DatabaseNode, "SELECT datname FROM pg_database ORDER BY datname ASC;") + + # If an explicit child is given, load the one explicit child. + else: + self.fetch_children(DatabaseNode, "SELECT datname FROM pg_database WHERE datname=%s ORDER BY datname ASC;", + [child]) class DatabaseNode(AbstractBaseNode): diff --git a/pygadmin/widgets/connection_dialog.py b/pygadmin/widgets/connection_dialog.py index 3758f7a..157738a 100644 --- a/pygadmin/widgets/connection_dialog.py +++ b/pygadmin/widgets/connection_dialog.py @@ -73,6 +73,10 @@ class ConnectionDialogWidget(QDialog): self.show() def init_line_edit_ui(self): + """ + Initialize the line edits. + """ + # Make a list for storing the QLabels for every connection parameter. This is necessary for using the QLabel in # the layout setting. self.connection_parameter_label_list = [] @@ -213,6 +217,11 @@ class ConnectionDialogWidget(QDialog): # Connect the state change to a function for handling a change in the checked status of the checkbox. self.use_postgres_database_checkbox.stateChanged.connect(self.set_database_edit_field_to_checkbox) + # Create a checkbox for loading all databases as a configurable parameter. + self.load_all_databases_checkbox = QCheckBox("Load all databases") + # Set the default as checked. + self.load_all_databases_checkbox.setChecked(True) + # Create a checkbox for usage in specific cases. self.open_at_start_checkbox = QCheckBox("Open always a connection dialog at start") @@ -242,8 +251,8 @@ class ConnectionDialogWidget(QDialog): # This case happens for the label of the port. else: - # The label is placed further down, so the checkbox for the default database is placed accurate. - grid_layout.addWidget(self.connection_parameter_label_list[parameter_number], parameter_number + 2, 2) + # The label is placed further down, so the checkboxes for the database are placed accurate. + grid_layout.addWidget(self.connection_parameter_label_list[parameter_number], parameter_number + 3, 2) # Use an incrementer for the next for loop. connection_parameter_edit_incrementer = 1 @@ -259,7 +268,8 @@ class ConnectionDialogWidget(QDialog): if connection_parameter == "Database": # Place the checkbox under the QLineEdit for the database. grid_layout.addWidget(self.use_postgres_database_checkbox, connection_parameter_edit_incrementer, 3) - connection_parameter_edit_incrementer += 1 + grid_layout.addWidget(self.load_all_databases_checkbox, connection_parameter_edit_incrementer + 1, 3) + connection_parameter_edit_incrementer += 2 # The port as the parameter which belongs to this checkbox is the last one in the list for the user, so this # checkbox is placed below the QLineEdit for the port. @@ -468,12 +478,14 @@ class ConnectionDialogWidget(QDialog): return - # Define the parameter in a dictionary which is required by the connection store. + # Define the parameters in a dictionary which is required by the connection store. connection_parameters = {"Host": self.connection_parameter_edit_dictionary["Host"].text(), "Username": self.connection_parameter_edit_dictionary["Username"].text(), "Database": self.connection_parameter_edit_dictionary["Database"].text(), # The port is used as integer number, not as string. - "Port": int(self.connection_parameter_edit_dictionary["Port"].text()) + "Port": int(self.connection_parameter_edit_dictionary["Port"].text()), + # Get the status for loading all databases out of the checkbox. + "Load All": self.load_all_databases_checkbox.isChecked() } return connection_parameters, connection_identifier, changed_password @@ -861,6 +873,19 @@ class ConnectionDialogWidget(QDialog): # Use the port as string, because all elements of the QLineEdit have to be strings. str(self.selected_connection_parameters_dictionary["Port"])) + # Get the status for loading all databases for the selected connection. + load_all_databases = global_connection_store.get_connection_load_all_information( + self.selected_connection_parameters_dictionary["Host"], + self.selected_connection_parameters_dictionary["Database"], + self.selected_connection_parameters_dictionary["Port"], + self.selected_connection_parameters_dictionary["Username"] + ) + + # Set the status as status of the checkbox. + self.load_all_databases_checkbox.setChecked(load_all_databases) + # Save the status in the dictionary for the selected connection. + self.selected_connection_parameters_dictionary["Load All"] = load_all_databases + # Define a password identifier for recognition in the password manager and keyring. password_identifier = self.selected_connection_identifier.split("/")[0] # Set the password as text in the QLineEdit. This is not a security flaw, because it is protected by the diff --git a/pygadmin/widgets/tree.py b/pygadmin/widgets/tree.py index 3b6f211..993d3bd 100644 --- a/pygadmin/widgets/tree.py +++ b/pygadmin/widgets/tree.py @@ -178,12 +178,20 @@ class TreeWidget(QWidget): # Create a server node for every connection dictionary. for connection_parameter in connection_parameters: + # Get the parameter for loading all databases. + try: + load_all_databases = connection_parameter["Load All"] + + except KeyError: + load_all_databases = True + new_node = ServerNode(name=connection_parameter["Host"], host=connection_parameter["Host"], user=connection_parameter["Username"], database=connection_parameter["Database"], port=connection_parameter["Port"], - timeout=connection_parameter["Timeout"]) + timeout=connection_parameter["Timeout"], + load_all_databases=load_all_databases) # Append the node to the server list. server_node_list.append(new_node) @@ -415,6 +423,13 @@ class TreeWidget(QWidget): the server node. """ + # Get the parameter for loading all databases. + try: + load_all_databases = connection_parameters_for_server_node["Load All"] + + except KeyError: + load_all_databases = True + # Try to create a server node. try: # Check for a duplicate, because only one server node is necessary for one host, user and port. @@ -424,7 +439,8 @@ class TreeWidget(QWidget): user=connection_parameters_for_server_node["Username"], database=connection_parameters_for_server_node["Database"], port=connection_parameters_for_server_node["Port"], - timeout=connection_parameters_for_server_node["Timeout"]) + timeout=connection_parameters_for_server_node["Timeout"], + load_all_databases=load_all_databases) else: # If there is a duplicate, set the server node as return value to None, because a server node was not