Source code for controllers.control_github

"""
This module contains the ControllerGitHub class responsible for handling GitHub-related functionalities
in the MoilApp application.

Class:
    ControllerGitHub: Handles GitHub-related functionalities such as refreshing repository information,
        changing branches, committing changes, checking for updates, etc.
"""
from subprocess import call, STDOUT
import sys
import os
from PyQt6 import QtCore, QtWidgets
import git


[docs] class ControllerGitHub: """ This class handles GitHub-related functionalities in the MoilApp application. Attributes: _controller: The controller instance. _model: The model instance. _ui_object: The UI object instance. _ctrl_main_config: The main configuration controller instance. _github_config: The GitHub configuration from the main configuration. _github_repo: The GitHub repository associated with the application's model. _show_message_box: A method to display message boxes. Methods: __init__(self, controller): Initializes the ControllerGitHub instance. _connect_event(self): Connects UI events to their respective methods. _init_setting(self): Initializes UI settings. _onclick_refresh_github_repository(self): Handles refreshing GitHub repository information. _showing_github_repository_information(self): Updates the UI with information about the GitHub repository. _show_info_update_in_github_repository(self): Fetches and displays information about commits behind the active branch. open_dialog_for_input_token(self): Displays a dialog box to prompt the user for a GitHub token. _onclick_btn_change_branch(self): Handles the click event of the "Change branch" button. _combo_box_change_branch(self): Event handler for when the value of the combo box changes. _onclick_commit_now(self): Commits changes to the Git repository. _check_for_update(self): Checks for updates in the Git repository. _check_for_new_update(self): Checks if a new software update is available and installs it if the user approves. """
[docs] def __init__(self, controller): try: self._controller = controller self._model = controller.model self._ui_object = controller.ui_object self._ctrl_main_config = controller.ctrl_main_config self._github_config = controller.model.main_config["Github_config"] self._github_repo = controller.model.github_config.github_repository self._showing_github_repository_information() self._init_setting() self._show_message_box = self._controller.ctrl_message_box.display_message_box self._connect_event() except Exception as e: self._model.activity_logger.error( f"ControllerGitHub.__init__(): Error initializing ControllerGitHub: {str(e)}")
[docs] def _connect_event(self): self._ui_object.btn_refresh_github.clicked.connect(self._onclick_refresh_github_repository) self._ui_object.comboBox.activated.connect(self._combo_box_change_branch) self._ui_object.btn_change_branch.clicked.connect(self._onclick_btn_change_branch) self._ui_object.btn_check_update.clicked.connect(self._check_for_update) self._ui_object.pushButton_commit_now.clicked.connect(self._onclick_commit_now)
[docs] def _init_setting(self): self._ui_object.frame_4.hide() self._ui_object.pushButton_update.hide() self._ui_object.textBrowser.hide()
[docs] def _onclick_refresh_github_repository(self): """ Update the GitHub repository information by refreshing the repository and showing the updated information if there are any changes. Return: None """ try: if self._model.debug_mode: self._model.activity_logger.info( "ControllerGitHub._onclick_refresh_github_repository(): Update GitHub repository MoilApp") if self._github_config["token"] is None: if (self._controller.ctrl_message_box.show_update_dialog("No token found, input your token?") != QtWidgets.QMessageBox.StandardButton.Yes): return if not self.open_dialog_for_input_token(): return current_hash = self._github_repo.head.object.hexsha branch = str(self._github_repo.active_branch) latest_hash = self._github_repo.remotes.origin.refs[branch].object.hexsha if latest_hash != current_hash: self._show_info_update_in_github_repository() else: message = "The software already up to date!" self._show_message_box(message, "information") except Exception as e: self._model.activity_logger.error(f"ControllerGitHub._onclick_refresh_github_repository(): Error: {str(e)}")
[docs] def _showing_github_repository_information(self): """ Update the UI with information about a Git repository. """ try: if self._model.debug_mode: self._model.activity_logger.info("ControllerGitHub._showing_github_repository_information(): Update " "user interface with GitHub repository MoilApp") if self._github_config["error"] is not None: self._ui_object.textBrowser_3.setText(self._github_config["error"]) else: self._ui_object.textBrowser_3.setText(self._github_config["origin_url"]) self._ui_object.label_active_branch.setText(self._github_config["active_branch"]) self._ui_object.textBrowser_token.setText(self._github_config["token"]) self._ui_object.comboBox.clear() self._ui_object.comboBox.addItems(self._github_config["list_branch"]) index = self._ui_object.comboBox.findText(self._github_config["active_branch"]) self._ui_object.comboBox.setCurrentIndex(index) if self._github_config["token"] is not None: self._show_info_update_in_github_repository() else: new = "Software can't find your token for this repository" self._ui_object.textBrowser.setText(new) except Exception as e: self._model.activity_logger.error( f"ControllerGitHub._showing_github_repository_information(): Error: {str(e)}")
[docs] def _show_info_update_in_github_repository(self): """ Fetch and display information about the GitHub repository associated with the application's model. Specifically, it retrieves a list of commits behind the currently active branch, and displays their commit message and hex SHA in the application's text browser. Return: None """ try: if self._model.debug_mode: self._model.activity_logger.info( "ControllerGitHub._show_info_update_in_github_repository(): Show to user interface with commit GitHub repository MoilApp") commits_behind = self._github_repo.iter_commits( f'{self._github_config["active_branch"]}..origin/{self._github_config["active_branch"]}') text = [f"{commit.hexsha}\n\n{commit.message}\n" for commit in commits_behind] new = '' if new.join(text) == "": message = "The repository no have commits behind\n" else: message = f'The repository have commits behind\n' \ f'here is the record commits for you \n\n' \ f'{new.join(text)}' self._controller.ui_object.textBrowser.setText(message) except Exception as e: self._model.activity_logger.error( f"ControllerGitHub._show_info_update_in_github_repository(): Error: {str(e)}")
[docs] def open_dialog_for_input_token(self): """ Display a dialog box to prompt the user for a GitHub token, and then validate and store the token in the application's model. Return: bool: True if the token was successfully validated and stored in the model, False otherwise. """ try: if self._model.debug_mode: self._model.activity_logger.info( "ControllerGitHub.open_dialog_for_input_token(): Open dialog box to get GitHub token") token, ok = QtWidgets.QInputDialog.getText(None, "Github Token!", "Write your correct token!") if not ok: return False if len(token) < 30: self._show_message_box("You didn't write the password or \n" "wrote a wrong token.", "warning!") return False self._github_config["token"] = token self._model.save_main_config_update() return True except Exception as e: self._model.activity_logger.error(f"ControllerGitHub.open_dialog_for_input_token(): Error: {str(e)}") return False
[docs] def _onclick_btn_change_branch(self): """ Handle the click event of the "Change branch" button. If the button is checked, shows a frame containing a list of available branches for the user to choose from. If the button is unchecked, hides the frame. Arg: self: An instance of the class. Return: None """ try: if self._model.debug_mode: self._model.activity_logger.info( "ControllerGitHub._onclick_btn_change_branch(): Change branch of MoilApp") if self._ui_object.btn_change_branch.isChecked(): self._ui_object.frame_4.show() else: self._ui_object.frame_4.hide() except Exception as e: self._model.activity_logger.error(f"ControllerGitHub._onclick_btn_change_branch(): Error: {str(e)}")
[docs] def _combo_box_change_branch(self): """ Event handler for when the value of the combo box changes. It checks if the selected branch name is different from the currently active branch of the GitHub repository. If it is different and a valid GitHub token exists, it attempts to change the active branch to the selected branch, and then updates the configuration and GitHub information in the application's model. It also checks if the 'docs/build/html' directory exists and removes it if it does. Finally, it displays an information message to the user, and then closes the application after a delay of 3 seconds. If the selected branch cannot be checked out, or if no valid GitHub token exists, it shows an error message to the user, and resets the combo box to the currently active branch. Return: None """ try: if self._model.debug_mode: self._model.activity_logger.info( "ControllerGitHub._combo_box_change_branch(): Combo Box Change branch of MoilApp") branch_name = str(self._ui_object.comboBox.currentText()) active_branch = self._github_repo.active_branch if str(active_branch) != branch_name: if self._github_config["token"] is not None: try: g = git.Git() g.checkout(branch_name) if os.path.isdir("../docs/build/html"): os.system("make -C ../docs clean") msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Icon.Information) msg.setWindowTitle("Information!!") msg.setText("Branches changed, Application will be closed automatically in 3 seconds!") msg.setStandardButtons(QtWidgets.QMessageBox.StandardButton.Ok) msg.show() def close_msg(): msg.done(0) sys.exit() QtCore.QTimer.singleShot(3000, close_msg) except git.exc.GitCommandError as git_error: self._show_message_box(f"Can't change branch due to Git command error: {git_error}", "information") self._ui_object.comboBox.setCurrentText(str(self._github_repo.active_branch)) self._ui_object.frame_7.show() except FileNotFoundError as file_not_found_error: self._show_message_box( f"Can't change branch due to file not found error: {file_not_found_error}", "information") self._ui_object.comboBox.setCurrentText(str(self._github_repo.active_branch)) self._ui_object.frame_7.show() else: self._show_message_box("Token cannot be found. Please add!!", "information") except Exception as e: self._model.activity_logger.error(f"ControllerGitHub._combo_box_change_branch(): Error: {str(e)}")
[docs] def _onclick_commit_now(self): """ Commit changes to the Git repository. This method commits changes to the Git repository and displays a success or error message depending on the result. """ try: if self._model.debug_mode: self._model.activity_logger.info("ControllerGitHub._onclick_commit_now(): Add commit change of MoilApp") text = self._ui_object.plainTextEdit.toPlainText() if text == "": self._show_message_box("Please write the commit message!!", "information") else: try: self._github_repo.git.add(all=True) self._github_repo.index.commit(str(text)) self._show_message_box("Successfully committed, now you can change branch!!", "information") self._ui_object.frame_7.hide() except git.exc.GitCommandError as git_error: self._show_message_box(f"Failed to commit due to Git command error: {git_error}", "warning") except FileNotFoundError as file_not_found_error: self._show_message_box(f"Failed to commit due to file not found error: {file_not_found_error}", "warning") except Exception as e: self._model.activity_logger.error(f"ControllerGitHub._onclick_commit_now(): Error: {str(e)}")
[docs] def _check_for_update(self): """ Checks for updates in the Git repository. If Git is not installed, does nothing. If a token is not set, asks the user to input one. Otherwise, calls `show_info_of_github_repository` and `check_for_new_update`. Return: None """ try: if self._model.debug_mode: self._model.activity_logger.info( "ControllerGitHub._check_for_update(): Check any update of MoilApp application from GitHub") with open(os.devnull, 'w', encoding='utf-8', errors='ignore') as devnull: if call(["git", "branch"], stderr=STDOUT, stdout=devnull) == 0: if self._github_config["token"] is None: if self._controller.ctrl_message_box.show_update_dialog( "No token found, input your token?") == QtWidgets.QMessageBox.StandardButton.Yes: if self.open_dialog_for_input_token(): self._check_for_new_update() else: self._show_info_update_in_github_repository() self._check_for_new_update() except Exception as e: self._model.activity_logger.error(f"ControllerGitHub._check_for_update(): Error: {str(e)}")
[docs] def _check_for_new_update(self): """ Check if a new software update is available and install it if user approves. Return: None """ try: if self._model.debug_mode: self._model.activity_logger.info( "ControllerGitHub._check_for_new_update(): Check any update of MoilApp and ask to install") current_hash = self._github_repo.head.object.hexsha self._github_repo.remotes.origin.fetch() branch = str(self._github_repo.active_branch) latest_hash = self._github_repo.remotes.origin.refs[branch].object.hexsha if latest_hash != current_hash: if self._controller.ctrl_message_box.show_update_dialog( "Software update available, install now?") == QtWidgets.QMessageBox.StandardButton.Yes: release_message = self._github_repo.remotes.origin.refs[branch].object.message self._github_repo.remotes.origin.pull() self._show_message_box(release_message, "information") else: self._show_message_box("The software is already up to date", "Information") except Exception as e: self._model.activity_logger.error(f"ControllerGitHub._check_for_new_update(): Error: {str(e)}")