"""
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 _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 _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)}")