diff --git a/CHANGELOG.md b/CHANGELOG.md index e3bd4f3..e975aa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,34 +1,6 @@ # Changelog Backuppy -## [0.9] - 2021-05-10 -### Added -- CLI-mode: It's now possible to end the program anytime with ":q" (or ESC-key) and pressing ENTER. -- CLI-mode: Colored output on traces -- New helper script "utils.py" - -### Changed -- Code refactoring (get rid of global variables and constants, instead pass as arguments) - -## [0.8.1] - 2021-05-10 -### Fixed -- CLI-mode: Show text "Programm interrupted by user." instead of error message when Ctrl+C was pressed. -- CLI-mode: Removed obsolete trace messages - -## [0.8] - 2021-05-07 -### Added -- GUI-mode: Introduce "Browse"-button on directory-selection dialogs -- GUI-mode: Installing necessary PySide2 package if not existing - -## [0.7] - 2021-05-06 -### Added -- Reworked "install.sh" to call "install.py" -- Launches graphical installer when called with "--gui" option: "install.sh --gui" - -## [0.6] - 2021-05-06 -### Added -- Write alias to Backuppy.sh into .bashrc/.zshrc only if not already existing - -## [0.5.2] - 2021-05-05 +## [1.02.000] - 2021-05-05 ### Added - Now fully functional as the shell version @@ -39,11 +11,11 @@ ### Fixed - Fixed problem with the Bash/ZSH config -## [0.5.1] - 2021-05-04 +## [1.01.001] - 2021-05-04 ### Changed - Use E-MAIL constant -## [0.5] - 2021-05-04 +## [1.01.000] - 2021-05-04 ### Added - Graphical installer based on Python3 with PySide2 GUI framework (Qt-bindings for Python) - Changelog.MD diff --git a/README.md b/README.md index 6ee459c..fae01e5 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ Name: Backuppy Description: Make daily backups with Backuppy to avoid losing your data. -Installation: execute `chmod +x install.sh && ./install.sh` +Installation: execute `chmod +x install.sh && .\install.sh` -Install. GUI: execute `chmod +x install.sh && ./install.sh --gui` +Install. GUI: execute `chmod +x install.sh && .\install.sh --gui` Usage: execute `backuppy` @@ -16,32 +16,32 @@ Author: Joël Schurter Licence: GPL3 -More infos: See README.md and CHANGELOG.md +More infos: see constants and README.md # ToDo - add a log-file for the rsync errors -- check user-input for validity - # Dependencies - rsync (because Backuppy makes its backups with rsync) - execute `sudo pacman -S rsync` on Arch/Manjaro - execute `sudo apt install rsync` on Debian/Ubuntu - - execute `sudo dnf install rsync` on Fedora - - execute `sudo zypper install rsync` on openSUSE + + execute `sudo pacman -S rsync` on Arch/Manjaro # Dependencies for the graphical installer: - pip (the python package manager) - execute `sudo pacman -S pip` on Arch/Manjaro - execute `sudo apt install python3-pip` on Debian/Ubuntu + + execute `sudo pacman -S pip` on Arch/Manjaro + +- pip package 'PySide2' - execute `sudo dnf install pip` on Fedora - - execute `sudo zypper install python3-pip` on openSUSE + execute `pip3 install pyside2` on Debian/Ubuntu + + execute `pip install pyside2` on Arch/Manjaro + +# IMPORTANT + +You have to remove the "backuppy" alias in your .bashrc or .zshrc before reinstall Backuppy. diff --git a/install.py b/install.py index 490916b..e99284f 100644 --- a/install.py +++ b/install.py @@ -1,155 +1,336 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 0.9 +version: 1.02.000 file: install.py -summary: python installer-script in CLI-mode +summary: main entry python file """ # Standard library imports -import sys import os +# Third party imports +from PySide2 import QtCore +from PySide2 import QtGui +from PySide2 import QtCore, QtWidgets + # local imports -from utils import set_language, query, _print, trace +from languages import english +from languages import german # local globals -VERSION: str = "0.9" -EMAIL = "fotocoder@joschu.ch" -EXCLUDE_FILE = "exclude.txt" -BACKUPPY_SCRIPT = "Backuppy.sh" +VERSION: str = "1.02.000" +EMAIL: str = "fotocoder@joschu.ch" +TARGET_SCRIPT: str = "Backuppy.sh" +EXCLUDE_FILE: str = "exclude.txt" +LANG_EN: str = "English" +LANG_DE: str = "German" +LANGUAGE: str = LANG_EN +MYDIR: str = os.environ.get("mydir") +if not MYDIR: + MYDIR = os.getcwd() +SOURCEDIR: str = None +EXCLUDE: bool = True +TARGETDIR: str = None +RSYNC_CMD: str = None -def main_install_cli(mydir, exclude_file): - language = query("welcome") - if not language: - return False, None, None - set_language(language) +def get_lang_text(search_str: str): + """ Returns a string from the appropriate language file. """ + return_str: str = eval("english." + search_str) + global LANGUAGE + if LANGUAGE == LANG_DE: + return_str = eval("german." + search_str) + return return_str - _print("languagepack") +def trace(message_txt: str): + """ Print a formatted message to std out. """ + print(f"[ OK ]", message_txt) - _print("intromsg1") +class BackuppyWizard(QtWidgets.QWizard): + def __init__(self, parent=None): + super().__init__(parent) - _print("intromsg2") + self.addPage(Page01(self)) + self.addPage(Page02(self)) + self.addPage(Page03(self)) + self.addPage(Page04(self)) + self.addPage(Page05(self)) + self.addPage(Page06(self)) + self.addPage(Page07(self)) + self.addPage(Page08(self)) + self.addPage(Page09(self)) + self.addPage(Page10(self)) + + self.setWindowTitle(english.intromsg1) + self.resize(640, 480) - # which Rsync options are available and which one you want to use - _print("rsyncopt") +class Page01(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label = QtWidgets.QLabel("Hello, first of all, which language do you prefer: English or German?") + # self.comboBox = QIComboBox(self) + self.comboBox = QtWidgets.QComboBox(self) + self.comboBox.addItem(LANG_EN, LANG_EN) + self.comboBox.addItem(LANG_DE, LANG_DE) - # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes - exclude = query("excludefile1") - if not exclude: - return False, None, None - elif exclude.upper() in ("J", "Y"): - _print("excludefile2") - exclude = True - else: - _print("excludefile3") - exclude = False + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label) + layout.addWidget(self.comboBox) + self.setLayout(layout) - # Asks for the source directory which should be saved - _print("srcdir1") - sourcedir = query("srcdir2") - if not sourcedir: - return False, None, None + def validatePage(self): + global LANGUAGE + if self.comboBox.currentText() == LANG_DE: + LANGUAGE = LANG_DE + else: + LANGUAGE = LANG_EN + return True - _print("srcdir3_1") - print(sourcedir) - _print("srcdir3_2") - # asks for the destination directory in which the backup should be saved - targetdir = query("targetdir1") - if not targetdir: - return False, None, None +class Page02(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label1 = QtWidgets.QLabel() + self.label2 = QtWidgets.QLabel() - _print("targetdir2_1") - print(targetdir) - _print("targetdir2_2") + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label1) + layout.addWidget(self.label2) + self.setLayout(layout) - # collects all the information needed to execute the rsync command and creates it. - _print("collect") + def initializePage(self): + self.label1.setText(get_lang_text("languagepack")) + self.label2.setText(get_lang_text("intromsg2")) - rsync_cmd = f"rsync -aqp --exclude-from={os.path.join(mydir, exclude_file)} {sourcedir} {targetdir}" - print(f"{rsync_cmd}") +class Page03(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label = QtWidgets.QLabel() + self.groupbox = QtWidgets.QGroupBox() + self.groupbox.setFlat(True) + self.radio1 = QtWidgets.QRadioButton() + self.radio2 = QtWidgets.QRadioButton() - # Outro - _print("outro1") + vbox = QtWidgets.QVBoxLayout() + vbox.addWidget(self.radio1) + vbox.addWidget(self.radio2) + vbox.addStretch(1) + self.groupbox.setLayout(vbox) - _print("outro2") - print(EMAIL) + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label) + layout.addWidget(self.groupbox) + self.setLayout(layout) - return True, exclude, rsync_cmd + def initializePage(self): + self.label.setText(get_lang_text("excludefile1")) + self.radio1.setText(get_lang_text("Yes")) + self.radio2.setText(get_lang_text("No")) + self.radio1.setChecked(True) -def create_exclude_file(mydir, exclude_file): - exclude_file = os.path.join(mydir, exclude_file) - with open(exclude_file, "w") as fExclude: - trace(f"creating exclude-file '{exclude_file}'.") - fExclude.write("\n") + def validatePage(self): + global EXCLUDE + if self.radio1.isChecked(): + EXCLUDE = True + else: + EXCLUDE = False + return True -def create_alias(mydir, backuppy_script): - shell = os.environ.get("SHELL") - home_dir = os.environ.get("HOME") - # alias entry in .bashrc or .zshrc - backuppy_script = os.path.join(mydir, backuppy_script) - alias_str = f"alias backuppy='sudo {backuppy_script}'" +class Page04(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label = QtWidgets.QLabel() + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label) + self.setLayout(layout) + + def initializePage(self): + global EXCLUDE + if EXCLUDE: + self.label.setText(get_lang_text("excludefile2")) + else: + self.label.setText(get_lang_text("excludefile3")) + +class Page05(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label1 = QtWidgets.QLabel() + self.label2 = QtWidgets.QLabel() + self.edit = QtWidgets.QLineEdit() + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label1) + layout.addWidget(self.label2) + layout.addWidget(self.edit) + self.setLayout(layout) + + def initializePage(self): + self.label1.setText(get_lang_text("srcdir1")) + self.label2.setText(get_lang_text("srcdir2")) + + def validatePage(self): + global SOURCEDIR + SOURCEDIR = self.edit.text() + + return True + + +class Page06(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label1 = QtWidgets.QLabel() + self.label2 = QtWidgets.QLabel() + self.label3 = QtWidgets.QLabel() + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label1) + layout.addWidget(self.label2) + layout.addWidget(self.label3) + self.setLayout(layout) + + def initializePage(self): + self.label1.setText(get_lang_text("srcdir3_1")) + bold_text = f"
{SOURCEDIR}
" + self.label2.setText(bold_text) + self.label3.setText(get_lang_text("srcdir3_2")) + + +class Page07(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label1 = QtWidgets.QLabel() + self.edit = QtWidgets.QLineEdit() + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label1) + layout.addWidget(self.edit) + self.setLayout(layout) + + def initializePage(self): + self.label1.setText(get_lang_text("targetdir1")) + + def validatePage(self): + global TARGETDIR + TARGETDIR = self.edit.text() + + return True + + +class Page08(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label1 = QtWidgets.QLabel() + self.label2 = QtWidgets.QLabel() + self.label3 = QtWidgets.QLabel() + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label1) + layout.addWidget(self.label2) + layout.addWidget(self.label3) + self.setLayout(layout) + + def initializePage(self): + self.label1.setText(get_lang_text("targetdir2_1")) + bold_text = f"{TARGETDIR}
" + self.label2.setText(bold_text) + self.label3.setText(get_lang_text("targetdir2_2")) + + def validatePage(self): + global RSYNC_CMD, MYDIR, SOURCEDIR, TARGETDIR + RSYNC_CMD = f"rsync -aqp --exclude-from={MYDIR}/exclude.txt {SOURCEDIR} {TARGETDIR}" + + return True + + +class Page09(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label1 = QtWidgets.QLabel() + self.label2 = QtWidgets.QLabel() + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label1) + layout.addWidget(self.label2) + self.setLayout(layout) + + def initializePage(self): + self.label1.setText(get_lang_text("collect")) + global RSYNC_CMD + #self.label2.setText(f"{RSYNC_CMD}") + bold_text = f"{RSYNC_CMD}
" + self.label2.setText(bold_text) + + +class Page10(QtWidgets.QWizardPage): + def __init__(self, parent=None): + super().__init__(parent) + self.label1 = QtWidgets.QLabel() + self.label2 = QtWidgets.QLabel() + self.label3 = QtWidgets.QLabel() + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label1) + layout.addWidget(self.label2) + layout.addWidget(self.label3) + self.setLayout(layout) + self.setFinalPage(True) + + def initializePage(self): + self.label1.setText(get_lang_text("outro1")) + self.label2.setText(get_lang_text("outro2")) + urlLink = f"{EMAIL}" + self.label3.setText(urlLink) + self.label3.setOpenExternalLinks(True) + + def validatePage(self): + # create shell script with rsync command + create_shell_script(RSYNC_CMD) + + return True + + +def create_shell_script(command_str: str): + rc_filepath: str = None + + global MYDIR, EXCLUDE, TARGET_SCRIPT + + TARGET_SCRIPT = os.path.join(MYDIR, TARGET_SCRIPT) + ALIAS_STR = f"alias backuppy='sudo {TARGET_SCRIPT}'" + current_shell = os.environ.get("SHELL") + user_home = os.environ.get("HOME") + + ## STEP 1: update shell config scripts with alias for backuppy + # .zshrc case1 and case2 + if current_shell in ("/usr/bin/zsh", "/bin/zsh"): + # Appending to bash config file + rc_filepath = os.path.join(user_home, ".zshrc") + + # .bashrc case1 and case2 + elif current_shell in ("/usr/bin/bash", "/bin/bash"): + # Appending to zsh config file + rc_filepath = os.path.join(user_home, ".bashrc") - # Check for installed ZSH - if shell.upper().find("ZSH") > 0: - rc_filepath = os.path.join(home_dir, ".zshrc") - # Check for installed BASH - if shell.upper().find("BASH") > 0: - rc_filepath = os.path.join(home_dir, ".bashrc") - # Append our alias if not already existing if os.path.isfile(rc_filepath): - fileRc = open(rc_filepath, "r") # open file in read mode - backuppy_entry_exists = False - for line in fileRc: - if "alias backuppy=" in line: - backuppy_entry_exists = True - break - if not backuppy_entry_exists: - trace(f"Writing {alias_str} to config file '{rc_filepath}'.") - fileRc = open(rc_filepath, "a") # open file in append mode - fileRc.write("\n# Following line was created by Backuppy\n" + alias_str + "\n") - fileRc.close() + with open(rc_filepath, 'a') as file1: + trace(f"Writing {ALIAS_STR} to config file '{rc_filepath}'.") + file1.write("\n# Following line was created by Backuppy\n" + ALIAS_STR + "\n") -def create_backuppy_script(directory, backuppy_script, rsync_cmd): - # creates the file 'Backuppy.sh' - backuppy_file = os.path.join(directory, backuppy_script) - with open(backuppy_file, "w") as fBackuppy: - trace(f"creating backuppy-file '{backuppy_file}'.") - fBackuppy.write("#!/bin/bash\n" + rsync_cmd + "\n") - - os.chmod(backuppy_file, 0o777) # make file executable + ## STEP 2: Create the exclude script if desired + if EXCLUDE: + global EXCLUDE_FILE + EXCLUDE_FILE = os.path.join(MYDIR, EXCLUDE_FILE) + with open(EXCLUDE_FILE, "w") as file2: + trace(f"creating exclude-file '{EXCLUDE_FILE}'.") + file2.writelines("\n") -def do_the_install(mydir: str, exclude_file, is_exclude: bool, backuppy_script: str, rsync_cmd: str): - """ Creates scripts and entries based on environment variables. """ + ## STEP 3: enter the rsync command in Backuppy.sh + with open(TARGET_SCRIPT, "w") as file3: + trace(f"Writing rsync-command to file '{TARGET_SCRIPT}':\n {command_str}") + file3.writelines("#!/bin/bash" + "\n") + file3.writelines(command_str + "\n") - if is_exclude: - create_exclude_file(mydir, exclude_file) - - if rsync_cmd: - create_backuppy_script(mydir, backuppy_script, rsync_cmd) - create_alias(mydir, backuppy_script) - -def main(argv): - trace(f"Starting Backuppy install.py v{VERSION}") - is_finalized = False - mydir = os.getcwd() - - if argv and argv[0] == "--gui": - from install_gui import main_install_gui - trace("Starting GUI-version.") - is_finalized, is_exclude, rsync_cmd = main_install_gui(mydir, EXCLUDE_FILE) # collect user input via GUI and store in env. variables - - else: - trace("Starting CLI-version.\n") - is_finalized, is_exclude, rsync_cmd = main_install_cli(mydir, EXCLUDE_FILE) # collect user input via CLI and store in env. variables - - if is_finalized: - do_the_install(mydir, EXCLUDE_FILE, is_exclude, BACKUPPY_SCRIPT, rsync_cmd) - - trace("Ending Backuppy install.py") if __name__ == '__main__': - # sys.argv.append("--gui") # TODO: disable for production - sys.exit(main(sys.argv[1:])) + trace(f"Starting Backuppy install.py v{VERSION}") + app = QtWidgets.QApplication() + wizard = BackuppyWizard() + wizard.show() + + app.exec_() + + trace("Ending Backuppy install.py") diff --git a/install.sh b/install.sh index f1e9ffd..ba8f1da 100755 --- a/install.sh +++ b/install.sh @@ -1,29 +1,107 @@ #!/bin/bash -# """ -# project: Backuppy -# version: 0.8 -# file: install.sh -# summary: main entry shell script -# """ + +# Variables +mydir=$PWD +langDE="./languages/german.py" +langEN="./languages/english.py" # Check if graphical installer should be executed if [ "$1" == "--gui" ]; then - # Check if PIP ist installed - if ! command -v pip3> /dev/null - then - echo "Please install PIP on your system for the graphical version of Backuppy!" - exit - fi - # Check if PIP module "PySide2" is installed - if ! pip list | grep PySide2> /dev/null - then - # Install PySide2 - echo -e "Installing necessary PySide2 package for GUI-mode." - pip3 install PySide2 - fi - # Launch python installer in GUI mode - python3 -B install.py "$1" -else - # Launch python installer in CLI mode python3 -B install.py + exit 0 +else + # Intro + # language + echo -e "Hello, first of all, which language do you prefer: German [DE] or English [EN]?" + read language + if [ $language = "DE" ]; then + echo -e "Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen! \n" + . $langDE + sleep 1 + fi + if [ $language = "EN" ]; then + echo -e "Perfect, the English language package is now activated. Welcome!. \n" + . $langEN + sleep 1 + fi + + echo -e "\n$intromsg1 \n" + sleep 1 + echo -e " \n$intromsg2\n" + sleep 1 + # Installer + + # creates the file 'Backuppy.sh' + touch Backuppy.sh + echo "#!/bin/bash" >> Backuppy.sh + chmod +x Backuppy.sh + # which Rsync options are available and which one you want to use + echo -e "$rsyncopt \n" + sleep 1 + + # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes + echo -e "$excludefile1" + read exclude + if [ $exclude = "J" ]; then + echo -e "$excludefile2 \n" + touch exclude.txt + sleep 1 + elif [ $exclude = "Y" ]; then + echo -e "$excludefile2 \n" + touch exclude.txt + sleep 1 + fi + if [ $exclude = "N" ]; then + echo -e "$excludefile3 \n" + sleep 1 + fi + + # Asks for the source directory which should be saved + echo -e "$srcdir1" + sleep 1 + echo -e "$srcdir2" + read sourcedir + echo -e "$srcdir3_1 $sourcedir $srcdir3_2" + sleep 1 + + # asks for the destination directory in which the backup should be saved + echo -e "$targetdir1" + read targetdir + echo -e "$targetdir2_1 $targetdir $targetdir2_2" + sleep 1 + + # alias entry in .bashrc or .zshrc + # .zshrc case 1 + echo $SHELL + if [ $SHELL = "/usr/bin/zsh" ]; then + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc + fi + # zshrc case 2 + echo $SHELL + if [ $SHELL = "/bin/zsh" ]; then + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc + fi + #bashrc case 1 + if [ $SHELL = "/usr/bin/bash" ]; then + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.bashrc + fi + # bashrc case 2 + if [ $SHELL = "/bin/bash" ]; then + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.bashrc + fi + + # collects all the information needed to execute the rsync command and creates it. + echo -e "$collect \n" + sleep 1 + echo -e "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir \n" + sleep 1 + + # enter the rsync command in Backuppy.sh + echo "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir" >> Backuppy.sh + + # Outro + echo -e "$outro1" + sleep 2 + echo "$outro2" + exit 0 fi diff --git a/install_gui.py b/install_gui.py deleted file mode 100644 index 6d9e1fd..0000000 --- a/install_gui.py +++ /dev/null @@ -1,318 +0,0 @@ -#!/usr/bin/env python3 -""" -project: Backuppy -version: 0.9 -file: install_gui.py -summary: python installer-script in GUI-mode (needs PySide2) -""" - -# Standard library imports -import os - -# Third party imports -from PySide2 import QtWidgets - -# local imports -from install import EXCLUDE_FILE, EMAIL -from utils import set_language, get_lang_text, LANG_EN, LANG_DE - -class BackuppyWizard(QtWidgets.QWizard): - def __init__(self, parent=None, mydir=os.getcwd(), exclude_file=EXCLUDE_FILE): - super().__init__(parent) - - # init instance variables - self.mydir = mydir - self.exclude_file = exclude_file - self.exclude = None - self.sourcedir = None - self.targetdir = None - self.rsync_cmd = None - - self.setWindowTitle(get_lang_text("intromsg1")) - - self.addPage(Page01(self)) - self.addPage(Page02(self)) - self.addPage(Page03(self)) - self.addPage(Page04(self)) - self.addPage(Page05(self)) - self.addPage(Page06(self)) - self.addPage(Page07(self)) - self.addPage(Page08(self)) - self.addPage(Page09(self)) - self.addPage(Page10(self)) - - self.resize(640, 480) - - -class Page01(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - self.label = QtWidgets.QLabel(get_lang_text("welcome")) - - self.comboBox = QtWidgets.QComboBox(self) - self.comboBox.addItem(LANG_EN) - self.comboBox.addItem(LANG_DE) - - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label) - layout.addWidget(self.comboBox) - self.setLayout(layout) - - def validatePage(self): - set_language(self.comboBox.currentText()) - - return True - - -class Page02(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - self.label1 = QtWidgets.QLabel() - self.label2 = QtWidgets.QLabel() - - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label1) - layout.addWidget(self.label2) - self.setLayout(layout) - - def initializePage(self): - self.setWindowTitle(get_lang_text("intromsg1")) - - self.label1.setText(get_lang_text("languagepack")) - self.label2.setText(get_lang_text("intromsg2")) - - -class Page03(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - - self.parent = parent - - self.label = QtWidgets.QLabel() - self.groupbox = QtWidgets.QGroupBox() - self.groupbox.setFlat(True) - self.radio1 = QtWidgets.QRadioButton() - self.radio2 = QtWidgets.QRadioButton() - - vbox = QtWidgets.QVBoxLayout() - vbox.addWidget(self.radio1) - vbox.addWidget(self.radio2) - vbox.addStretch(1) - self.groupbox.setLayout(vbox) - - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label) - layout.addWidget(self.groupbox) - self.setLayout(layout) - - def initializePage(self): - self.label.setText(get_lang_text("excludefile1")) - self.radio1.setText(get_lang_text("Yes")) - self.radio2.setText(get_lang_text("No")) - self.radio1.setChecked(True) - - def validatePage(self): - if self.radio1.isChecked(): - self.parent.exclude = True - else: - self.parent.exclude = False - return True - - -class Page04(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - - self.parent = parent - - self.label = QtWidgets.QLabel() - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label) - self.setLayout(layout) - - def initializePage(self): - if self.parent.exclude: - self.label.setText(get_lang_text("excludefile2")) - else: - self.label.setText(get_lang_text("excludefile3")) - -class Page05(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - - self.parent = parent - - self.label1 = QtWidgets.QLabel() - self.label2 = QtWidgets.QLabel() - self.efSourceDir = QtWidgets.QLineEdit() - self.pbBrowse = QtWidgets.QPushButton("Browse") - self.pbBrowse.clicked.connect(self.on_pbBrowse_clicked) - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label1) - layout.addWidget(self.label2) - - hLayout = QtWidgets.QHBoxLayout() - hLayout.addWidget(self.efSourceDir) - hLayout.addWidget(self.pbBrowse) - - layout.addLayout(hLayout) - - self.setLayout(layout) - - def initializePage(self): - self.label1.setText(get_lang_text("srcdir1")) - self.label2.setText(get_lang_text("srcdir2")) - - def on_pbBrowse_clicked(self): - options = QtWidgets.QFileDialog.Options() | QtWidgets.QFileDialog.ShowDirsOnly - dirName = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", None, options) - self.efSourceDir.setText(dirName) - - def validatePage(self): - self.parent.sourcedir = self.efSourceDir.text() - - return True - - -class Page06(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - - self.parent = parent - - self.label1 = QtWidgets.QLabel() - self.label2 = QtWidgets.QLabel() - self.label3 = QtWidgets.QLabel() - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label1) - layout.addWidget(self.label2) - layout.addWidget(self.label3) - self.setLayout(layout) - - def initializePage(self): - self.label1.setText(get_lang_text("srcdir3_1")) - bold_text = f"{self.parent.sourcedir}
" - self.label2.setText(bold_text) - self.label3.setText(get_lang_text("srcdir3_2")) - - -class Page07(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - - self.parent = parent - - self.label1 = QtWidgets.QLabel() - self.efTargetDir = QtWidgets.QLineEdit() - self.pbBrowse = QtWidgets.QPushButton("Browse") - self.pbBrowse.clicked.connect(self.on_pbBrowse_clicked) - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label1) - - hLayout = QtWidgets.QHBoxLayout() - hLayout.addWidget(self.efTargetDir) - hLayout.addWidget(self.pbBrowse) - - layout.addLayout(hLayout) - - self.setLayout(layout) - - def initializePage(self): - self.label1.setText(get_lang_text("targetdir1")) - - def on_pbBrowse_clicked(self): - options = QtWidgets.QFileDialog.Options() | QtWidgets.QFileDialog.ShowDirsOnly - dirName = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", None, options) - self.efTargetDir.setText(dirName) - - def validatePage(self): - self.parent.targetdir = self.efTargetDir.text() - - return True - - -class Page08(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - - self.parent = parent - - self.label1 = QtWidgets.QLabel() - self.label2 = QtWidgets.QLabel() - self.label3 = QtWidgets.QLabel() - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label1) - layout.addWidget(self.label2) - layout.addWidget(self.label3) - self.setLayout(layout) - - def initializePage(self): - self.label1.setText(get_lang_text("targetdir2_1")) - bold_text = f"{self.parent.targetdir}
" - self.label2.setText(bold_text) - self.label3.setText(get_lang_text("targetdir2_2")) - - def validatePage(self): - exclude_file = os.path.join(self.parent.mydir, self.parent.exclude_file) - - self.parent.rsync_cmd = f"rsync -aqp --exclude-from={exclude_file} {self.parent.sourcedir} {self.parent.targetdir}" - - return True - - -class Page09(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - - self.parent = parent - - self.label1 = QtWidgets.QLabel() - self.label2 = QtWidgets.QLabel() - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label1) - layout.addWidget(self.label2) - self.setLayout(layout) - - def initializePage(self): - self.label1.setText(get_lang_text("collect")) - bold_text = f"{self.parent.rsync_cmd}
" - self.label2.setText(bold_text) - - -class Page10(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - self.label1 = QtWidgets.QLabel() - self.label2 = QtWidgets.QLabel() - self.label3 = QtWidgets.QLabel() - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label1) - layout.addWidget(self.label2) - layout.addWidget(self.label3) - self.setLayout(layout) - - self.setFinalPage(True) - - def initializePage(self): - self.label1.setText(get_lang_text("outro1")) - self.label2.setText(get_lang_text("outro2")) - global EMAIL - urlLink = f"{EMAIL}" - self.label3.setText(urlLink) - self.label3.setOpenExternalLinks(True) - - def validatePage(self): - return True - - -def main_install_gui(mydir, exclude_file): - app = QtWidgets.QApplication() - wizard = BackuppyWizard(parent=None, mydir=mydir, exclude_file=exclude_file) - wizard.show() - - app.exec_() - - return True, wizard.exclude, wizard.rsync_cmd - -if __name__ == '__main__': - is_finalized, is_exclude, rsync_cmd = main_install_gui(mydir=os.getcwd(), exclude_file=EXCLUDE_FILE) diff --git a/languages/english.py b/languages/english.py index 367e77a..e04cfe2 100644 --- a/languages/english.py +++ b/languages/english.py @@ -9,8 +9,6 @@ Yes="Yes" No="No" -welcome="Hello, first of all, which language do you prefer: German [DE] or English [EN]?" - languagepack="Perfect, the English language package is now activated. Welcome!" intromsg1="Thanks for using Backuppy to make your backups!" @@ -35,7 +33,7 @@ srcdir3_2="if this path is not correct, adjust it in the file 'Backuppy.sh'." targetdir1="Now I need to know where Backuppy should save your backups, i.e. the target directory. \n Please enter this carefully and in the same way as for the source directory." targetdir2_1="you have typed in the following destination path:" -targetdir2_2="if this path is not correct, adjust it in the file 'Backuppy.sh'." +targetdir2_2="if this path is not correct, adjust it in the file 'backuppy.sh'." collect="Now we have almost reached the end of the installer. I will now create the rsync command for you and show it to you.\n If you notice a mistake, just cancel the installer and start again from the beginning.\nNote: I recommend that you download Backuppy completely again in this case." diff --git a/languages/german.py b/languages/german.py index 8836f19..07508c9 100644 --- a/languages/german.py +++ b/languages/german.py @@ -33,7 +33,7 @@ srcdir3_2="wenn dieser Pfad nicht stimmen sollte, dann passe ihn in der Datei 'B targetdir1="Nun muss ich noch wissen, wo Backuppy dein Backup ablegen soll, das Zielverzeichnis also.\nBitte tippe dieses gewissenhaft und auf die Weise wie beim Quellverzeichnis ein." targetdir2_1="du hast folgenden Zielpfad eingetippt:" -targetdir2_2="wenn dieser Pfad nicht stimmen sollte, dann passe ihn in der Datei 'Backuppy.sh' an." +targetdir2_2="wenn dieser Pfad nicht stimmen sollte, dann passe ihn in der Datei 'backuppy.sh' an." collect="Nun sind wir fast am Ende des Installers angelangt. Ich erstelle nun den rsync-Befehl für dich und zeige ihn\ndir nachher nochmal.\nWenn dir da etwas auffallen sollte, brich den Installer einfach ab und fange nochmal von Vorne an.\nAchtung: ich empfehle dir, Backuppy in diesem Fall nochmal komplett neu zu installieren." diff --git a/utils.py b/utils.py deleted file mode 100644 index ce6df85..0000000 --- a/utils.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 -""" -project: Backuppy -version: 0.9 -file: utils.py -summary: utilities script -""" - -# Standard library imports -import time - -# local imports -from languages import english -from languages import german - -# local globals -LANG_EN = "EN" -LANG_DE = "DE" -LANGUAGE = LANG_EN -class bcolors: - """ Define colors for the std. output to console. """ - - OK = '\033[92m' #GREEN - WARNING = '\033[93m' #YELLOW - ERROR = '\033[91m' #RED - RESET = '\033[0m' #RESET COLOR - - -def trace(message_txt, prefix_crlf=False, err=False): - """ Print a formatted message to std out. - - :param "message_txt" [in] The message text that should be displayed. - :param "prefix_crlf" [in] If True, a carriage return/line feed will be done prior to the message text. - - """ - - if prefix_crlf: - print("\n") - if err: - print("[ " + bcolors.ERROR + "ERR" + bcolors.RESET + " ] " + message_txt) - else: - print("[ " + bcolors.OK + "OK" + bcolors.RESET + " ] " + message_txt) - -def get_lang_text(search_str: str): - """ Returns a string from the appropriate language file. """ - return_str: str = eval("english." + search_str) - global LANGUAGE - if LANGUAGE == LANG_DE: - return_str = eval("german." + search_str) - return return_str - -def _print(message_txt: str): - print("\n" + get_lang_text(message_txt) + "\n") - time.sleep(1) - -def set_language(language): - """ Set global constant "LANGUAGE" to given language ("EN"/"DE"). """ - global LANGUAGE, LANG_DE, LANG_EN - - if str(language) and language.upper() == LANG_DE: - LANGUAGE = LANG_DE - else: - LANGUAGE = LANG_EN - -def query(question_txt: str): - try: - answer_txt = input(get_lang_text(question_txt) + "\n> ") - - if not answer_txt: - answer_txt = "''" - - if answer_txt.upper() == ":Q" or answer_txt == chr(27): - trace("Program exited by user-input.") - return None - - except KeyboardInterrupt: - trace("Program interrupted by user.", prefix_crlf=True, err=True) - return None - - return answer_txt