From 00fa691116315ba093e1e2a7143f8b13e519ca41 Mon Sep 17 00:00:00 2001 From: Paul S Date: Wed, 5 May 2021 17:54:06 +0200 Subject: [PATCH 01/21] Renamed "changelog.MD" to "CHANGELOG.md" --- changelog.MD | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 changelog.MD diff --git a/changelog.MD b/changelog.MD deleted file mode 100644 index 51721aa..0000000 --- a/changelog.MD +++ /dev/null @@ -1,21 +0,0 @@ -# Changelog Backuppy - -## [1.01.001] - 2021-05-04 -### Changed -- Use E-MAIL constant - -## [1.01.000] - 2021-05-04 -### Added -- Graphical installer based on Python3 with PySide2 GUI framework (Qt-bindings for Python) -- Changelog.MD - -### Changed -- Directory ".languages" renamed to "languages" due to python module import syntax constraints -- Files "english.txt" and "german.txt" renamed to .-py due to follow the python filename schema - -# Changelog Backuppy - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). From e3bccb0715c5b34688c6fb6210d8b659824752bc Mon Sep 17 00:00:00 2001 From: PhotoLinux Date: Wed, 5 May 2021 19:15:25 +0200 Subject: [PATCH 02/21] deleted backupp.py deleted backup.py because we don't need it anymore. --- backup.py | 268 ------------------------------------------------------ 1 file changed, 268 deletions(-) delete mode 100644 backup.py diff --git a/backup.py b/backup.py deleted file mode 100644 index 266dc56..0000000 --- a/backup.py +++ /dev/null @@ -1,268 +0,0 @@ -#!/usr/bin/env python3 -""" -project: Backuppy -version: 1.01.001 -file: backup.py -summary: main entry python file -""" - -# Standard library imports -import os - -# Third party imports -from PySide2 import QtCore -from PySide2 import QtGui -from PySide2 import QtCore, QtWidgets - -# local imports -from languages import english -from languages import german - -# local globals -VERSION: str = "1.01.001" -EMAIL: str = "fotocoder@joschu.ch" -LANG_EN: str = "English" -LANG_DE: str = "German" -LANGUAGE: str = LANG_EN -MYDIR: str = os.getcwd() -SOURCEDIR: str = None -EXCLUDE: bool = True -TARGETDIR: str = None - -def get_lang_text(search_str: str): - return_str: str = eval("english." + search_str) - global LANGUAGE - if LANGUAGE == LANG_DE: - return_str = eval("german." + search_str) - return return_str - - -class BackuppyWizard(QtWidgets.QWizard): - def __init__(self, parent=None): - super().__init__(parent) - - 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) - -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("English", LANG_DE) - self.comboBox.addItem("German", LANG_EN) - - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label) - layout.addWidget(self.comboBox) - self.setLayout(layout) - - def validatePage(self): - global LANGUAGE - if self.comboBox.currentText() == LANG_DE: - LANGUAGE = LANG_DE - else: - LANGUAGE = LANG_EN - 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.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.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): - global EXCLUDE - if self.radio1.isChecked(): - EXCLUDE = True - else: - EXCLUDE = False - return True - -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")) - self.label2.setText(SOURCEDIR) - 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")) - self.label2.setText(TARGETDIR) - self.label3.setText(get_lang_text("targetdir2_2")) - - -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 SOURCEDIR, TARGETDIR - self.label2.setText(f"rsync -aqp --exclude-from={MYDIR}/exclude.txt {SOURCEDIR} {TARGETDIR}") - - -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) - - -if __name__ == '__main__': - print(f"Starting backup.py v{VERSION}") - app = QtWidgets.QApplication() - wizard = BackuppyWizard() - wizard.show() - app.exec_() - print("Ending backup.py") From 1dfe37a97086c80878219405e13c4bc33fc2ccd7 Mon Sep 17 00:00:00 2001 From: Paul S Date: Thu, 6 May 2021 12:40:12 +0200 Subject: [PATCH 03/21] Ported shell script "install.sh" to Python --- install_cli.py | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 install_cli.py diff --git a/install_cli.py b/install_cli.py new file mode 100644 index 0000000..c3215e3 --- /dev/null +++ b/install_cli.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +# Dependencies +import os +import time + +# Variables +MYDIR = os.getcwd() +SHELL = os.environ.get("SHELL") +HOME = os.environ.get("HOME") + +# Intro +# language + +language = input("Hello, first of all, which language do you prefer: German [DE] or English [EN]?\n> ") +if language.upper() == "DE": + from languages.german import * + print("Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen!\n") +else: + from languages.english import * + print("Perfect, the English language package is now activated. Welcome!.\n") + +time.sleep(1) + +print("\n" + intromsg1 + "\n") +time.sleep(1) + +print("\n" + intromsg2 + "\n") +time.sleep(1) + +# Installer + +# creates the file 'Backuppy.sh' +fBackuppy = open("Backuppy.sh", "w") +fBackuppy.write("#!/bin/bash") +os.chmod("Backuppy.sh", 0o777) # make file executable + +# which Rsync options are available and which one you want to use +print(rsyncopt + "\n") +time.sleep(1) + +# asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes +exclude = input(excludefile1 + "\n> ") +if exclude.upper() in ("J", "Y"): + print(excludefile2 + "\n") + fExclude = open("exclude.txt", "w") + fExclude.close() +else: + print(excludefile3 + "\n") +time.sleep(1) + +# Asks for the source directory which should be saved +print(srcdir1) +time.sleep(1) +sourcedir = input(srcdir2 + "\n> ") + +print(f"{srcdir3_1} {sourcedir} {srcdir3_2}") +time.sleep(1) + +# asks for the destination directory in which the backup should be saved +targetdir = input(targetdir1 + "\n> ") +print(f"{targetdir2_1} {targetdir} {targetdir2_2}") +time.sleep(1) + +# alias entry in .bashrc or .zshrc +print(SHELL) + +# .zshrc case1 and case2 +if SHELL.upper().find("ZSH") >0: + # Appending to bash config file + fRc = open(os.path.join(HOME, ".zshrc"), "a") # append mode + fRc.write("\n" + f"alias backuppy='sudo {MYDIR}/Backuppy.sh'" + "\n") + fRc.close() + +# .bashrc case1 and case2 +elif SHELL.upper().find("BASH") >0: + # Appending to zsh config file + fRc = open(os.path.join(HOME, ".bashrc"), "a") # append mode + fRc.write("\n" + f"alias backuppy='sudo {MYDIR}/Backuppy.sh'" + "\n") + fRc.close() + +# collects all the information needed to execute the rsync command and creates it. +print(collect + "\n") +time.sleep(1) +print(f"rsync -aqp --exclude-from={MYDIR}/exclude.txt {sourcedir} {targetdir}\n") +time.sleep(1) + +# enter the rsync command in Backuppy.sh +fBackuppy.write("\n" + f"rsync -aqp --exclude-from={MYDIR}/exclude.txt {sourcedir} {targetdir}" + "\n") +fBackuppy.close() + +# Outro +print(outro1) +time.sleep(2) +print(outro2 + " fotocoder@joschu.ch") From 5d367fa1a89376e265fd3616c57cb28232794284 Mon Sep 17 00:00:00 2001 From: Paul S Date: Thu, 6 May 2021 15:14:50 +0200 Subject: [PATCH 04/21] Write alias only if not already existing --- CHANGELOG.md | 10 +++++++--- install.py | 35 +++++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e975aa4..7e5aa15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog Backuppy -## [1.02.000] - 2021-05-05 +## [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 ### Added - Now fully functional as the shell version @@ -11,11 +15,11 @@ ### Fixed - Fixed problem with the Bash/ZSH config -## [1.01.001] - 2021-05-04 +## [0.5.1] - 2021-05-04 ### Changed - Use E-MAIL constant -## [1.01.000] - 2021-05-04 +## [0.5] - 2021-05-04 ### Added - Graphical installer based on Python3 with PySide2 GUI framework (Qt-bindings for Python) - Changelog.MD diff --git a/install.py b/install.py index e99284f..a586b0b 100644 --- a/install.py +++ b/install.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 1.02.000 +version: 0.6 file: install.py summary: main entry python file """ @@ -10,16 +10,14 @@ summary: main entry python file import os # Third party imports -from PySide2 import QtCore -from PySide2 import QtGui -from PySide2 import QtCore, QtWidgets +from PySide2 import QtWidgets # local imports from languages import english from languages import german # local globals -VERSION: str = "1.02.000" +VERSION: str = "0.6" EMAIL: str = "fotocoder@joschu.ch" TARGET_SCRIPT: str = "Backuppy.sh" EXCLUDE_FILE: str = "exclude.txt" @@ -44,7 +42,7 @@ def get_lang_text(search_str: str): def trace(message_txt: str): """ Print a formatted message to std out. """ - print(f"[ OK ]", message_txt) + print("[ OK ]" + message_txt) class BackuppyWizard(QtWidgets.QWizard): def __init__(self, parent=None): @@ -295,20 +293,29 @@ def create_shell_script(command_str: str): 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 + # Check for installed ZSH + if current_shell.upper().find("ZSH") > 0: 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 + # Check for installed BASH + if current_shell.upper().find("BASH") > 0: rc_filepath = os.path.join(user_home, ".bashrc") + # Append our alias if not already existing if os.path.isfile(rc_filepath): - with open(rc_filepath, 'a') as file1: + 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}'.") - file1.write("\n# Following line was created by Backuppy\n" + ALIAS_STR + "\n") + 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() ## STEP 2: Create the exclude script if desired if EXCLUDE: From a8199debc99c4fa11fa27296361a753be3b7dbe7 Mon Sep 17 00:00:00 2001 From: Paul S Date: Thu, 6 May 2021 15:47:58 +0200 Subject: [PATCH 05/21] Added TO-DO check user-input for validity --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index fae01e5..e5f4308 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ More infos: see constants and README.md - add a log-file for the rsync errors +- check user-input for validity + # Dependencies - rsync (because Backuppy makes its backups with rsync) From a1c5fbf7eaa33dd9998f3331d10110fc9af7d1f7 Mon Sep 17 00:00:00 2001 From: Paul S Date: Thu, 6 May 2021 19:40:07 +0200 Subject: [PATCH 06/21] Reworked "install.sh" to call "install.py" - Reworked "install.sh" to call "install.py" - All the functionality in "install.py" - Launches graphical installer when called with "--gui" option: "install.sh --gui" --- CHANGELOG.md | 5 + install.py | 401 +++++++++++++++---------------------------------- install.sh | 109 +------------- install_cli.py | 95 ------------ install_gui.py | 260 ++++++++++++++++++++++++++++++++ 5 files changed, 390 insertions(+), 480 deletions(-) delete mode 100644 install_cli.py create mode 100644 install_gui.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e5aa15..2167c76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog Backuppy +## [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 diff --git a/install.py b/install.py index a586b0b..ac7ace4 100644 --- a/install.py +++ b/install.py @@ -1,343 +1,176 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 0.6 +version: 0.7 file: install.py summary: main entry python file """ # Standard library imports +import sys import os - -# Third party imports -from PySide2 import QtWidgets +import time # local imports from languages import english from languages import german # local globals -VERSION: str = "0.6" -EMAIL: str = "fotocoder@joschu.ch" -TARGET_SCRIPT: str = "Backuppy.sh" -EXCLUDE_FILE: str = "exclude.txt" +# ---------------------- +VERSION: str = "0.7" +# ---------------------- +MYDIR = os.getcwd() +EXCLUDE_FILE = "exclude.txt" +BACKUPPY_SCRIPT = "Backuppy.sh" +SHELL = os.environ.get("SHELL") +HOME = os.environ.get("HOME") 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 +LANGUAGE: str = None RSYNC_CMD: str = None +EMAIL: str = "fotocoder@joschu.ch" + +def set_language(language): + global LANGUAGE + LANGUAGE = language + +def trace(message_txt): + """ Print a formatted message to std out. """ + print("[ OK ] " + message_txt) def get_lang_text(search_str: str): + global LANGUAGE """ 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 trace(message_txt: str): - """ Print a formatted message to std out. """ - print("[ OK ]" + message_txt) +def install_cli_main(): + language = input("Hello, first of all, which language do you prefer: German [DE] or English [EN]?\n> ") + if language.upper() == "DE": + set_language(LANG_DE) + print("Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen!\n") + else: + print("Perfect, the English language package is now activated. Welcome!.\n") -class BackuppyWizard(QtWidgets.QWizard): - def __init__(self, parent=None): - super().__init__(parent) + time.sleep(1) - 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) + print("\n" + get_lang_text("intromsg1") + "\n") + time.sleep(1) -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) + print("\n" + get_lang_text("intromsg2") + "\n") + time.sleep(1) - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label) - layout.addWidget(self.comboBox) - self.setLayout(layout) + # which Rsync options are available and which one you want to use + print(get_lang_text("rsyncopt") + "\n") + time.sleep(1) - def validatePage(self): - global LANGUAGE - if self.comboBox.currentText() == LANG_DE: - LANGUAGE = LANG_DE - else: - LANGUAGE = LANG_EN - return True + # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes + exclude = input(get_lang_text("excludefile1") + "\n> ") + if exclude.upper() in ("J", "Y"): + print(get_lang_text("excludefile2") + "\n") + os.environ["BUPY_CREATE_EXCLUDE"] = "True" + else: + print(get_lang_text("excludefile3") + "\n") + time.sleep(1) + # Asks for the source directory which should be saved + print(get_lang_text("srcdir1")) + time.sleep(1) + sourcedir = input(get_lang_text("srcdir2") + "\n> ") -class Page02(QtWidgets.QWizardPage): - def __init__(self, parent=None): - super().__init__(parent) - self.label1 = QtWidgets.QLabel() - self.label2 = QtWidgets.QLabel() + print(f"{get_lang_text('srcdir3_1')} {sourcedir} {get_lang_text('srcdir3_2')}") + time.sleep(1) - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label1) - layout.addWidget(self.label2) - self.setLayout(layout) + # asks for the destination directory in which the backup should be saved + targetdir = input(get_lang_text("targetdir1") + "\n> ") + print(f"{get_lang_text('targetdir2_1')} {targetdir} {get_lang_text('targetdir2_2')}") + time.sleep(1) - def initializePage(self): - self.label1.setText(get_lang_text("languagepack")) - self.label2.setText(get_lang_text("intromsg2")) + # collects all the information needed to execute the rsync command and creates it. + print(get_lang_text("collect") + "\n") + time.sleep(1) + exclude_file = os.path.join(MYDIR, EXCLUDE_FILE) + + RSYNC_CMD = f"rsync -aqp --exclude-from={exclude_file} {sourcedir} {targetdir}" + os.environ["BUPY_RSYNC_CMD"] = RSYNC_CMD + print(f"{RSYNC_CMD}") + time.sleep(1) + # Outro + print(get_lang_text("outro1")) + time.sleep(2) + print(get_lang_text("outro2") + " " + EMAIL) -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() +def create_exclude_file(directory, exclude_file): + exclude_file = os.path.join(directory, exclude_file) + with open(exclude_file, "w") as fExclude: + trace(f"creating exclude-file '{exclude_file}'.") + fExclude.write("\n") - vbox = QtWidgets.QVBoxLayout() - vbox.addWidget(self.radio1) - vbox.addWidget(self.radio2) - vbox.addStretch(1) - self.groupbox.setLayout(vbox) + return exclude_file - layout = QtWidgets.QVBoxLayout() - layout.addWidget(self.label) - layout.addWidget(self.groupbox) - self.setLayout(layout) +def create_alias(directory, backuppy_script): + # alias entry in .bashrc or .zshrc + backuppy_script = os.path.join(directory, backuppy_script) + alias_str = f"alias backuppy='sudo {backuppy_script}'" - 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): - global EXCLUDE - if self.radio1.isChecked(): - EXCLUDE = True - else: - EXCLUDE = False - return True - -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 # Check for installed ZSH - if current_shell.upper().find("ZSH") > 0: - rc_filepath = os.path.join(user_home, ".zshrc") - + if SHELL.upper().find("ZSH") > 0: + rc_filepath = os.path.join(HOME, ".zshrc") # Check for installed BASH - if current_shell.upper().find("BASH") > 0: - rc_filepath = os.path.join(user_home, ".bashrc") - + if SHELL.upper().find("BASH") > 0: + rc_filepath = os.path.join(HOME, ".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: + 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}'.") + 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.write("\n# Following line was created by Backuppy\n" + alias_str + "\n") fileRc.close() - ## 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 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 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") +def do_the_install(): + """ Does the things with our environment variables. """ + is_exclude = os.environ.get("BUPY_CREATE_EXCLUDE") + rsync_cmd = os.environ.get("BUPY_RSYNC_CMD") + + if is_exclude == "True": + exclude_file = create_exclude_file(MYDIR, EXCLUDE_FILE) + create_alias(MYDIR, BACKUPPY_SCRIPT) + create_backuppy_script(MYDIR, BACKUPPY_SCRIPT, rsync_cmd) -if __name__ == '__main__': +def main(argv): trace(f"Starting Backuppy install.py v{VERSION}") - app = QtWidgets.QApplication() - wizard = BackuppyWizard() - wizard.show() - app.exec_() + if argv and argv[0] == "--gui": + import install_gui + trace("Starting GUI-version.\n") + install_gui.main() # collect user input via GUI and store in env. variables + else: + trace("Starting CLI-version.\n") + install_cli_main() # collect user input via CLI and store in env. variables + + do_the_install() trace("Ending Backuppy install.py") + +if __name__ == '__main__': + # sys.argv.append("--gui") # TODO: disable for production + sys.exit(main(sys.argv[1:])) diff --git a/install.sh b/install.sh index ba8f1da..9711dd6 100755 --- a/install.sh +++ b/install.sh @@ -1,107 +1,14 @@ #!/bin/bash -# Variables -mydir=$PWD -langDE="./languages/german.py" -langEN="./languages/english.py" - # Check if graphical installer should be executed if [ "$1" == "--gui" ]; then - python3 -B install.py - exit 0 + if ! command -v pip3> /dev/null + then + echo "Please install PIP on your system for the graphical version of Backuppy!" + exit + fi + pip3 install pyside2 + python3 -B install.py "$1" 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 + python3 -B install.py fi diff --git a/install_cli.py b/install_cli.py deleted file mode 100644 index c3215e3..0000000 --- a/install_cli.py +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env python3 - -# Dependencies -import os -import time - -# Variables -MYDIR = os.getcwd() -SHELL = os.environ.get("SHELL") -HOME = os.environ.get("HOME") - -# Intro -# language - -language = input("Hello, first of all, which language do you prefer: German [DE] or English [EN]?\n> ") -if language.upper() == "DE": - from languages.german import * - print("Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen!\n") -else: - from languages.english import * - print("Perfect, the English language package is now activated. Welcome!.\n") - -time.sleep(1) - -print("\n" + intromsg1 + "\n") -time.sleep(1) - -print("\n" + intromsg2 + "\n") -time.sleep(1) - -# Installer - -# creates the file 'Backuppy.sh' -fBackuppy = open("Backuppy.sh", "w") -fBackuppy.write("#!/bin/bash") -os.chmod("Backuppy.sh", 0o777) # make file executable - -# which Rsync options are available and which one you want to use -print(rsyncopt + "\n") -time.sleep(1) - -# asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes -exclude = input(excludefile1 + "\n> ") -if exclude.upper() in ("J", "Y"): - print(excludefile2 + "\n") - fExclude = open("exclude.txt", "w") - fExclude.close() -else: - print(excludefile3 + "\n") -time.sleep(1) - -# Asks for the source directory which should be saved -print(srcdir1) -time.sleep(1) -sourcedir = input(srcdir2 + "\n> ") - -print(f"{srcdir3_1} {sourcedir} {srcdir3_2}") -time.sleep(1) - -# asks for the destination directory in which the backup should be saved -targetdir = input(targetdir1 + "\n> ") -print(f"{targetdir2_1} {targetdir} {targetdir2_2}") -time.sleep(1) - -# alias entry in .bashrc or .zshrc -print(SHELL) - -# .zshrc case1 and case2 -if SHELL.upper().find("ZSH") >0: - # Appending to bash config file - fRc = open(os.path.join(HOME, ".zshrc"), "a") # append mode - fRc.write("\n" + f"alias backuppy='sudo {MYDIR}/Backuppy.sh'" + "\n") - fRc.close() - -# .bashrc case1 and case2 -elif SHELL.upper().find("BASH") >0: - # Appending to zsh config file - fRc = open(os.path.join(HOME, ".bashrc"), "a") # append mode - fRc.write("\n" + f"alias backuppy='sudo {MYDIR}/Backuppy.sh'" + "\n") - fRc.close() - -# collects all the information needed to execute the rsync command and creates it. -print(collect + "\n") -time.sleep(1) -print(f"rsync -aqp --exclude-from={MYDIR}/exclude.txt {sourcedir} {targetdir}\n") -time.sleep(1) - -# enter the rsync command in Backuppy.sh -fBackuppy.write("\n" + f"rsync -aqp --exclude-from={MYDIR}/exclude.txt {sourcedir} {targetdir}" + "\n") -fBackuppy.close() - -# Outro -print(outro1) -time.sleep(2) -print(outro2 + " fotocoder@joschu.ch") diff --git a/install_gui.py b/install_gui.py new file mode 100644 index 0000000..88ab993 --- /dev/null +++ b/install_gui.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python3 +""" +project: Backuppy +version: 0.7 +file: install_gui.py +summary: main entry python file +""" + +# Standard library imports +import os + +# Third party imports +from PySide2 import QtWidgets + +# local imports +from install import * + +class BackuppyWizard(QtWidgets.QWizard): + def __init__(self, parent=None): + super().__init__(parent) + + 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) + +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) + + layout = QtWidgets.QVBoxLayout() + layout.addWidget(self.label) + layout.addWidget(self.comboBox) + self.setLayout(layout) + + def validatePage(self): + if self.comboBox.currentText() == LANG_DE: + set_language(LANG_DE) + + 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.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.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): + global EXCLUDE + if self.radio1.isChecked(): + EXCLUDE = True + else: + EXCLUDE = False + return True + +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")) + os.environ["BUPY_CREATE_EXCLUDE"] = "True" + 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 + exclude_file = os.path.join(MYDIR, EXCLUDE_FILE) + + RSYNC_CMD = f"rsync -aqp --exclude-from={exclude_file} {SOURCEDIR} {TARGETDIR}" + os.environ["BUPY_RSYNC_CMD"] = RSYNC_CMD + + 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 main(): + app = QtWidgets.QApplication() + wizard = BackuppyWizard() + wizard.show() + + app.exec_() + +if __name__ == '__main__': + main() From 845b7e9c9a339d8a6d6c37f9999d692614778391 Mon Sep 17 00:00:00 2001 From: Paul S Date: Fri, 7 May 2021 12:08:33 +0200 Subject: [PATCH 07/21] "Browse"-button on directory-selection dialogs --- CHANGELOG.md | 4 +++ install.py | 78 +++++++++++++++++++++++++++++--------------------- install.sh | 16 ++++++++++- install_gui.py | 68 +++++++++++++++++++++++++++++++------------ 4 files changed, 115 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2167c76..dc9236c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog Backuppy +## [0.8] - 2021-05-07 +### Added +- Introduce "Browse"-button on directory-selection dialogs + ## [0.7] - 2021-05-06 ### Added - Reworked "install.sh" to call "install.py" diff --git a/install.py b/install.py index ac7ace4..40bde02 100644 --- a/install.py +++ b/install.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 0.7 +version: 0.8 file: install.py -summary: main entry python file +summary: python installer-script in CLI-mode """ # Standard library imports @@ -17,23 +17,28 @@ from languages import german # local globals # ---------------------- -VERSION: str = "0.7" -# ---------------------- -MYDIR = os.getcwd() +VERSION: str = "0.8" +EMAIL = "fotocoder@joschu.ch" EXCLUDE_FILE = "exclude.txt" BACKUPPY_SCRIPT = "Backuppy.sh" +# ---------------------- +MYDIR = os.getcwd() +EXCLUDE: bool = False SHELL = os.environ.get("SHELL") HOME = os.environ.get("HOME") -LANG_EN: str = "English" -LANG_DE: str = "German" -LANGUAGE: str = None +LANG_EN = "English" +LANG_DE = "German" +LANGUAGE = LANG_EN RSYNC_CMD: str = None -EMAIL: str = "fotocoder@joschu.ch" def set_language(language): global LANGUAGE LANGUAGE = language +def set_exclude(exclude_flag): + global EXCLUDE + EXCLUDE = exclude_flag + def trace(message_txt): """ Print a formatted message to std out. """ print("[ OK ] " + message_txt) @@ -46,7 +51,7 @@ def get_lang_text(search_str: str): return_str = eval("german." + search_str) return return_str -def install_cli_main(): +def main_install_cli(): language = input("Hello, first of all, which language do you prefer: German [DE] or English [EN]?\n> ") if language.upper() == "DE": set_language(LANG_DE) @@ -69,9 +74,10 @@ def install_cli_main(): # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes exclude = input(get_lang_text("excludefile1") + "\n> ") if exclude.upper() in ("J", "Y"): + set_exclude(True) print(get_lang_text("excludefile2") + "\n") - os.environ["BUPY_CREATE_EXCLUDE"] = "True" else: + set_exclude(False) print(get_lang_text("excludefile3") + "\n") time.sleep(1) @@ -94,7 +100,7 @@ def install_cli_main(): exclude_file = os.path.join(MYDIR, EXCLUDE_FILE) RSYNC_CMD = f"rsync -aqp --exclude-from={exclude_file} {sourcedir} {targetdir}" - os.environ["BUPY_RSYNC_CMD"] = RSYNC_CMD + print(f"{RSYNC_CMD}") time.sleep(1) @@ -103,25 +109,25 @@ def install_cli_main(): time.sleep(2) print(get_lang_text("outro2") + " " + EMAIL) + return True, EXCLUDE, RSYNC_CMD + def create_exclude_file(directory, exclude_file): exclude_file = os.path.join(directory, exclude_file) with open(exclude_file, "w") as fExclude: trace(f"creating exclude-file '{exclude_file}'.") fExclude.write("\n") - return exclude_file - -def create_alias(directory, backuppy_script): +def create_alias(shell, home_dir, directory, backuppy_script): # alias entry in .bashrc or .zshrc backuppy_script = os.path.join(directory, backuppy_script) alias_str = f"alias backuppy='sudo {backuppy_script}'" # Check for installed ZSH - if SHELL.upper().find("ZSH") > 0: - rc_filepath = os.path.join(HOME, ".zshrc") + 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, ".bashrc") + 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 @@ -145,29 +151,37 @@ def create_backuppy_script(directory, backuppy_script, rsync_cmd): os.chmod(backuppy_file, 0o777) # make file executable -def do_the_install(): - """ Does the things with our environment variables. """ +def do_the_install(is_exclude: bool, rsync_cmd: str): + """ Creates scripts and entries based on environment variables. """ - is_exclude = os.environ.get("BUPY_CREATE_EXCLUDE") - rsync_cmd = os.environ.get("BUPY_RSYNC_CMD") - - if is_exclude == "True": - exclude_file = create_exclude_file(MYDIR, EXCLUDE_FILE) - create_alias(MYDIR, BACKUPPY_SCRIPT) - create_backuppy_script(MYDIR, BACKUPPY_SCRIPT, rsync_cmd) + if is_exclude: + create_exclude_file(MYDIR, EXCLUDE_FILE) + + if rsync_cmd: + create_backuppy_script(MYDIR, BACKUPPY_SCRIPT, rsync_cmd) + create_alias(SHELL, HOME, MYDIR, BACKUPPY_SCRIPT) def main(argv): trace(f"Starting Backuppy install.py v{VERSION}") + is_finalized = False if argv and argv[0] == "--gui": - import install_gui + from install_gui import main_install_gui trace("Starting GUI-version.\n") - install_gui.main() # collect user input via GUI and store in env. variables + is_finalized, is_exclude, rsync_cmd = main_install_gui() # collect user input via GUI and store in env. variables + else: trace("Starting CLI-version.\n") - install_cli_main() # collect user input via CLI and store in env. variables + is_finalized, is_exclude, rsync_cmd = main_install_cli() # collect user input via CLI and store in env. variables + if is_finalized: + print("CLI finalized.") + if is_exclude: + print("exclude is true.") + if rsync_cmd: + print("rsync command returned: " + rsync_cmd) - do_the_install() + if is_finalized: + do_the_install(is_exclude, rsync_cmd) trace("Ending Backuppy install.py") diff --git a/install.sh b/install.sh index 9711dd6..08fe784 100755 --- a/install.sh +++ b/install.sh @@ -1,14 +1,28 @@ #!/bin/bash +# """ +# project: Backuppy +# version: 0.8 +# file: install.sh +# summary: main entry shell script +# """ # 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 - pip3 install pyside2 + # Check if PIP module "PySide2" is installed + if ! pip list | grep PySide2> /dev/null + then + # Install PySide2 + 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 fi diff --git a/install_gui.py b/install_gui.py index 88ab993..538fe1c 100644 --- a/install_gui.py +++ b/install_gui.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 0.7 +version: 0.8 file: install_gui.py -summary: main entry python file +summary: python installer-script in GUI-mode (needs PySide2) """ # Standard library imports @@ -19,6 +19,8 @@ class BackuppyWizard(QtWidgets.QWizard): def __init__(self, parent=None): super().__init__(parent) + self.setWindowTitle(get_lang_text("intromsg1")) + self.addPage(Page01(self)) self.addPage(Page02(self)) self.addPage(Page03(self)) @@ -29,15 +31,15 @@ class BackuppyWizard(QtWidgets.QWizard): self.addPage(Page08(self)) self.addPage(Page09(self)) self.addPage(Page10(self)) - - self.setWindowTitle(english.intromsg1) + self.resize(640, 480) + 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) @@ -66,6 +68,8 @@ class Page02(QtWidgets.QWizardPage): 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")) @@ -97,13 +101,13 @@ class Page03(QtWidgets.QWizardPage): self.radio1.setChecked(True) def validatePage(self): - global EXCLUDE if self.radio1.isChecked(): - EXCLUDE = True + set_exclude(True) else: - EXCLUDE = False + set_exclude(False) return True + class Page04(QtWidgets.QWizardPage): def __init__(self, parent=None): super().__init__(parent) @@ -116,7 +120,6 @@ class Page04(QtWidgets.QWizardPage): global EXCLUDE if EXCLUDE: self.label.setText(get_lang_text("excludefile2")) - os.environ["BUPY_CREATE_EXCLUDE"] = "True" else: self.label.setText(get_lang_text("excludefile3")) @@ -125,20 +128,33 @@ class Page05(QtWidgets.QWizardPage): super().__init__(parent) self.label1 = QtWidgets.QLabel() self.label2 = QtWidgets.QLabel() - self.edit = QtWidgets.QLineEdit() + 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) - layout.addWidget(self.edit) + + 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): global SOURCEDIR - SOURCEDIR = self.edit.text() + SOURCEDIR = self.efSourceDir.text() return True @@ -166,18 +182,31 @@ class Page07(QtWidgets.QWizardPage): def __init__(self, parent=None): super().__init__(parent) self.label1 = QtWidgets.QLabel() - self.edit = QtWidgets.QLineEdit() + self.efTargetDir = 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.edit) + + 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): global TARGETDIR - TARGETDIR = self.edit.text() + TARGETDIR = self.efTargetDir.text() return True @@ -205,7 +234,6 @@ class Page08(QtWidgets.QWizardPage): exclude_file = os.path.join(MYDIR, EXCLUDE_FILE) RSYNC_CMD = f"rsync -aqp --exclude-from={exclude_file} {SOURCEDIR} {TARGETDIR}" - os.environ["BUPY_RSYNC_CMD"] = RSYNC_CMD return True @@ -239,6 +267,7 @@ class Page10(QtWidgets.QWizardPage): layout.addWidget(self.label2) layout.addWidget(self.label3) self.setLayout(layout) + self.setFinalPage(True) def initializePage(self): @@ -248,8 +277,11 @@ class Page10(QtWidgets.QWizardPage): self.label3.setText(urlLink) self.label3.setOpenExternalLinks(True) + def validatePage(self): + return True, EXCLUDE, RSYNC_CMD -def main(): + +def main_install_gui(): app = QtWidgets.QApplication() wizard = BackuppyWizard() wizard.show() @@ -257,4 +289,4 @@ def main(): app.exec_() if __name__ == '__main__': - main() + main_install_gui() From 8e7afcedcee747cd1a93ff578ddef010a3ea4ba1 Mon Sep 17 00:00:00 2001 From: Paul S Date: Fri, 7 May 2021 13:15:20 +0200 Subject: [PATCH 08/21] Return values for main-function --- install_gui.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/install_gui.py b/install_gui.py index 538fe1c..6a3dfae 100644 --- a/install_gui.py +++ b/install_gui.py @@ -101,10 +101,11 @@ class Page03(QtWidgets.QWizardPage): self.radio1.setChecked(True) def validatePage(self): + global EXCLUDE if self.radio1.isChecked(): - set_exclude(True) + EXCLUDE = True else: - set_exclude(False) + EXCLUDE = False return True @@ -278,7 +279,7 @@ class Page10(QtWidgets.QWizardPage): self.label3.setOpenExternalLinks(True) def validatePage(self): - return True, EXCLUDE, RSYNC_CMD + return True def main_install_gui(): @@ -288,5 +289,7 @@ def main_install_gui(): app.exec_() + return True, EXCLUDE, RSYNC_CMD + if __name__ == '__main__': - main_install_gui() + is_finalized, is_exclude, rsync_cmd = main_install_gui() From 5852ac6b625e15518737ce70686c91b1a93dfb1b Mon Sep 17 00:00:00 2001 From: Paul S Date: Fri, 7 May 2021 13:16:13 +0200 Subject: [PATCH 09/21] Installing necessary PySide2 package for GUI-mode --- CHANGELOG.md | 1 + install.py | 11 ++++------- install.sh | 1 + 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc9236c..6c33f16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [0.8] - 2021-05-07 ### Added - Introduce "Browse"-button on directory-selection dialogs +- Installing necessary PySide2 package for GUI-mode if not existing ## [0.7] - 2021-05-06 ### Added diff --git a/install.py b/install.py index 40bde02..d6468be 100644 --- a/install.py +++ b/install.py @@ -35,10 +35,6 @@ def set_language(language): global LANGUAGE LANGUAGE = language -def set_exclude(exclude_flag): - global EXCLUDE - EXCLUDE = exclude_flag - def trace(message_txt): """ Print a formatted message to std out. """ print("[ OK ] " + message_txt) @@ -73,11 +69,12 @@ def main_install_cli(): # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes exclude = input(get_lang_text("excludefile1") + "\n> ") + global EXCLUDE if exclude.upper() in ("J", "Y"): - set_exclude(True) + EXCLUDE = True print(get_lang_text("excludefile2") + "\n") else: - set_exclude(False) + EXCLUDE = False print(get_lang_text("excludefile3") + "\n") time.sleep(1) @@ -167,7 +164,7 @@ def main(argv): if argv and argv[0] == "--gui": from install_gui import main_install_gui - trace("Starting GUI-version.\n") + trace("Starting GUI-version.") is_finalized, is_exclude, rsync_cmd = main_install_gui() # collect user input via GUI and store in env. variables else: diff --git a/install.sh b/install.sh index 08fe784..f1e9ffd 100755 --- a/install.sh +++ b/install.sh @@ -18,6 +18,7 @@ if [ "$1" == "--gui" ]; then 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 From 3423502e68dfccb94ef8377956e75a026039d1e0 Mon Sep 17 00:00:00 2001 From: Paul S Date: Fri, 7 May 2021 13:38:08 +0200 Subject: [PATCH 10/21] Fixed typos, simplified dependencies-section --- README.md | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e5f4308..95cacf2 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,7 +16,7 @@ Author: Joël Schurter Licence: GPL3 -More infos: see constants and README.md +More infos: See README.md and CHANGELOG.md # ToDo @@ -38,12 +38,6 @@ More infos: see constants and README.md execute `sudo pacman -S pip` on Arch/Manjaro -- pip package 'PySide2' - - 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. +You have to remove the "backuppy" alias in your .bashrc or .zshrc before reinstalling Backuppy. From a336776904091f17b4a8cdf239dd80316c5ef43d Mon Sep 17 00:00:00 2001 From: Paul S Date: Fri, 7 May 2021 16:12:42 +0200 Subject: [PATCH 11/21] Removed note about removing alias --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 95cacf2..cf272a9 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,3 @@ More infos: See README.md and CHANGELOG.md execute `sudo apt install python3-pip` on Debian/Ubuntu execute `sudo pacman -S pip` on Arch/Manjaro - -# IMPORTANT - -You have to remove the "backuppy" alias in your .bashrc or .zshrc before reinstalling Backuppy. From d8e953bf057c4b2bd12833265864e18235bfa610 Mon Sep 17 00:00:00 2001 From: Paul S Date: Fri, 7 May 2021 16:26:33 +0200 Subject: [PATCH 12/21] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c33f16..66e0b1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## [0.8] - 2021-05-07 ### Added -- Introduce "Browse"-button on directory-selection dialogs -- Installing necessary PySide2 package for GUI-mode if not existing +- GUI-mode: Introduce "Browse"-button on directory-selection dialogs +- GUI-mode: Installing necessary PySide2 package if not existing ## [0.7] - 2021-05-06 ### Added From 21f6b4b3ac41aab6d09e0626d3eb162424e7c3b7 Mon Sep 17 00:00:00 2001 From: Paul S Date: Fri, 7 May 2021 17:35:08 +0200 Subject: [PATCH 13/21] Added install instructions for Fedora and openSUSE --- README.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cf272a9..6ee459c 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,21 @@ More infos: See README.md and CHANGELOG.md # Dependencies - rsync (because Backuppy makes its backups with rsync) - execute `sudo apt install rsync` on Debian/Ubuntu - 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 + # 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 + + execute `sudo dnf install pip` on Fedora + + execute `sudo zypper install python3-pip` on openSUSE From b24dabf2036b2f8170fbcbeb26f26ef9fa1ed7f7 Mon Sep 17 00:00:00 2001 From: Paul S Date: Mon, 10 May 2021 11:29:40 +0200 Subject: [PATCH 14/21] Show message on Ctrl+C - CLI-mode: Show text "Programm interrupted by user." instead of error message when Ctrl+C was pressed. - CLI-mode: Removed obsolete trace messages --- CHANGELOG.md | 5 +++ install.py | 118 +++++++++++++++++++++++++++------------------------ 2 files changed, 68 insertions(+), 55 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66e0b1d..7031763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog Backuppy +## [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 diff --git a/install.py b/install.py index d6468be..34b04ac 100644 --- a/install.py +++ b/install.py @@ -35,8 +35,16 @@ def set_language(language): global LANGUAGE LANGUAGE = language -def trace(message_txt): - """ Print a formatted message to std out. """ +def trace(message_txt, prefix_crlf=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") print("[ OK ] " + message_txt) def get_lang_text(search_str: str): @@ -48,65 +56,71 @@ def get_lang_text(search_str: str): return return_str def main_install_cli(): - language = input("Hello, first of all, which language do you prefer: German [DE] or English [EN]?\n> ") - if language.upper() == "DE": - set_language(LANG_DE) - print("Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen!\n") - else: - print("Perfect, the English language package is now activated. Welcome!.\n") + try: + language = input("Hello, first of all, which language do you prefer: German [DE] or English [EN]?\n> ") + if language.upper() == "DE": + set_language(LANG_DE) + print("Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen!\n") + else: + print("Perfect, the English language package is now activated. Welcome!.\n") - time.sleep(1) + time.sleep(1) - print("\n" + get_lang_text("intromsg1") + "\n") - time.sleep(1) + print("\n" + get_lang_text("intromsg1") + "\n") + time.sleep(1) - print("\n" + get_lang_text("intromsg2") + "\n") - time.sleep(1) + print("\n" + get_lang_text("intromsg2") + "\n") + time.sleep(1) - # which Rsync options are available and which one you want to use - print(get_lang_text("rsyncopt") + "\n") - time.sleep(1) + # which Rsync options are available and which one you want to use + print(get_lang_text("rsyncopt") + "\n") + time.sleep(1) - # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes - exclude = input(get_lang_text("excludefile1") + "\n> ") - global EXCLUDE - if exclude.upper() in ("J", "Y"): - EXCLUDE = True - print(get_lang_text("excludefile2") + "\n") - else: - EXCLUDE = False - print(get_lang_text("excludefile3") + "\n") - time.sleep(1) + # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes + exclude = input(get_lang_text("excludefile1") + "\n> ") + global EXCLUDE + if exclude.upper() in ("J", "Y"): + EXCLUDE = True + print(get_lang_text("excludefile2") + "\n") + else: + EXCLUDE = False + print(get_lang_text("excludefile3") + "\n") + time.sleep(1) - # Asks for the source directory which should be saved - print(get_lang_text("srcdir1")) - time.sleep(1) - sourcedir = input(get_lang_text("srcdir2") + "\n> ") + # Asks for the source directory which should be saved + print(get_lang_text("srcdir1")) + time.sleep(1) + sourcedir = input(get_lang_text("srcdir2") + "\n> ") - print(f"{get_lang_text('srcdir3_1')} {sourcedir} {get_lang_text('srcdir3_2')}") - time.sleep(1) + print(f"{get_lang_text('srcdir3_1')} {sourcedir} {get_lang_text('srcdir3_2')}") + time.sleep(1) - # asks for the destination directory in which the backup should be saved - targetdir = input(get_lang_text("targetdir1") + "\n> ") - print(f"{get_lang_text('targetdir2_1')} {targetdir} {get_lang_text('targetdir2_2')}") - time.sleep(1) + # asks for the destination directory in which the backup should be saved + targetdir = input(get_lang_text("targetdir1") + "\n> ") + print(f"{get_lang_text('targetdir2_1')} {targetdir} {get_lang_text('targetdir2_2')}") + time.sleep(1) - # collects all the information needed to execute the rsync command and creates it. - print(get_lang_text("collect") + "\n") - time.sleep(1) - exclude_file = os.path.join(MYDIR, EXCLUDE_FILE) - - RSYNC_CMD = f"rsync -aqp --exclude-from={exclude_file} {sourcedir} {targetdir}" + # collects all the information needed to execute the rsync command and creates it. + print(get_lang_text("collect") + "\n") + time.sleep(1) + exclude_file = os.path.join(MYDIR, EXCLUDE_FILE) + + RSYNC_CMD = f"rsync -aqp --exclude-from={exclude_file} {sourcedir} {targetdir}" - print(f"{RSYNC_CMD}") - time.sleep(1) + print(f"{RSYNC_CMD}") + time.sleep(1) - # Outro - print(get_lang_text("outro1")) - time.sleep(2) - print(get_lang_text("outro2") + " " + EMAIL) + # Outro + print(get_lang_text("outro1")) + time.sleep(2) + print(get_lang_text("outro2") + " " + EMAIL) + + return True, EXCLUDE, RSYNC_CMD + + except KeyboardInterrupt: + trace("Programm interrupted by user.", prefix_crlf=True) + return False, None, None - return True, EXCLUDE, RSYNC_CMD def create_exclude_file(directory, exclude_file): exclude_file = os.path.join(directory, exclude_file) @@ -170,12 +184,6 @@ def main(argv): else: trace("Starting CLI-version.\n") is_finalized, is_exclude, rsync_cmd = main_install_cli() # collect user input via CLI and store in env. variables - if is_finalized: - print("CLI finalized.") - if is_exclude: - print("exclude is true.") - if rsync_cmd: - print("rsync command returned: " + rsync_cmd) if is_finalized: do_the_install(is_exclude, rsync_cmd) From 45535d7ff5d631b97c709abe8bf768b7941b84d4 Mon Sep 17 00:00:00 2001 From: Paul S Date: Mon, 10 May 2021 14:00:27 +0200 Subject: [PATCH 15/21] Set version to 0.8.1 --- install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.py b/install.py index 34b04ac..ef672f6 100644 --- a/install.py +++ b/install.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 0.8 +version: 0.8.1 file: install.py summary: python installer-script in CLI-mode """ @@ -17,7 +17,7 @@ from languages import german # local globals # ---------------------- -VERSION: str = "0.8" +VERSION: str = "0.8.1" EMAIL = "fotocoder@joschu.ch" EXCLUDE_FILE = "exclude.txt" BACKUPPY_SCRIPT = "Backuppy.sh" From edb10d9a54204ad149d8005f479b72877e1f7b7d Mon Sep 17 00:00:00 2001 From: Paul S Date: Mon, 10 May 2021 18:47:31 +0200 Subject: [PATCH 16/21] Fix typos --- languages/english.py | 4 +++- languages/german.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/languages/english.py b/languages/english.py index e04cfe2..367e77a 100644 --- a/languages/english.py +++ b/languages/english.py @@ -9,6 +9,8 @@ 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!" @@ -33,7 +35,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 07508c9..8836f19 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." From 87ee1f2064dd864c5ca58e665cf878fc5f960701 Mon Sep 17 00:00:00 2001 From: Paul S Date: Mon, 10 May 2021 18:48:11 +0200 Subject: [PATCH 17/21] Created helper scripts "utils.py" --- utils.py | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 utils.py diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..ce6df85 --- /dev/null +++ b/utils.py @@ -0,0 +1,80 @@ +#!/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 From c7180d586368c98b6de7e6a4cf144695f9abaa96 Mon Sep 17 00:00:00 2001 From: Paul S Date: Mon, 10 May 2021 18:49:30 +0200 Subject: [PATCH 18/21] Refactoring, color-mode, :q and ESC key ## [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) --- CHANGELOG.md | 9 +++ install.py | 178 +++++++++++++++++++------------------------------ install_gui.py | 81 ++++++++++++++-------- 3 files changed, 130 insertions(+), 138 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7031763..e3bd4f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # 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. diff --git a/install.py b/install.py index ef672f6..4cc80fb 100644 --- a/install.py +++ b/install.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 0.8.1 +version: 0.9 file: install.py summary: python installer-script in CLI-mode """ @@ -9,128 +9,87 @@ summary: python installer-script in CLI-mode # Standard library imports import sys import os -import time # local imports -from languages import english -from languages import german +from utils import set_language, query, _print, trace # local globals -# ---------------------- -VERSION: str = "0.8.1" +VERSION: str = "0.9" EMAIL = "fotocoder@joschu.ch" EXCLUDE_FILE = "exclude.txt" BACKUPPY_SCRIPT = "Backuppy.sh" -# ---------------------- -MYDIR = os.getcwd() -EXCLUDE: bool = False -SHELL = os.environ.get("SHELL") -HOME = os.environ.get("HOME") -LANG_EN = "English" -LANG_DE = "German" -LANGUAGE = LANG_EN -RSYNC_CMD: str = None -def set_language(language): - global LANGUAGE - LANGUAGE = language +def main_install_cli(mydir, exclude_file): + language = query("welcome") + if not language: + return False, None, None + set_language(language) -def trace(message_txt, prefix_crlf=False): - """ Print a formatted message to std out. + _print("languagepack") - :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. - - """ + _print("intromsg1") - if prefix_crlf: - print("\n") - print("[ OK ] " + message_txt) + _print("intromsg2") -def get_lang_text(search_str: str): - global LANGUAGE - """ Returns a string from the appropriate language file. """ - return_str: str = eval("english." + search_str) - if LANGUAGE == LANG_DE: - return_str = eval("german." + search_str) - return return_str + # which Rsync options are available and which one you want to use + _print("rsyncopt") -def main_install_cli(): - try: - language = input("Hello, first of all, which language do you prefer: German [DE] or English [EN]?\n> ") - if language.upper() == "DE": - set_language(LANG_DE) - print("Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen!\n") - else: - print("Perfect, the English language package is now activated. Welcome!.\n") + # 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 - time.sleep(1) - - print("\n" + get_lang_text("intromsg1") + "\n") - time.sleep(1) - - print("\n" + get_lang_text("intromsg2") + "\n") - time.sleep(1) - - # which Rsync options are available and which one you want to use - print(get_lang_text("rsyncopt") + "\n") - time.sleep(1) - - # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes - exclude = input(get_lang_text("excludefile1") + "\n> ") - global EXCLUDE - if exclude.upper() in ("J", "Y"): - EXCLUDE = True - print(get_lang_text("excludefile2") + "\n") - else: - EXCLUDE = False - print(get_lang_text("excludefile3") + "\n") - time.sleep(1) - - # Asks for the source directory which should be saved - print(get_lang_text("srcdir1")) - time.sleep(1) - sourcedir = input(get_lang_text("srcdir2") + "\n> ") - - print(f"{get_lang_text('srcdir3_1')} {sourcedir} {get_lang_text('srcdir3_2')}") - time.sleep(1) - - # asks for the destination directory in which the backup should be saved - targetdir = input(get_lang_text("targetdir1") + "\n> ") - print(f"{get_lang_text('targetdir2_1')} {targetdir} {get_lang_text('targetdir2_2')}") - time.sleep(1) - - # collects all the information needed to execute the rsync command and creates it. - print(get_lang_text("collect") + "\n") - time.sleep(1) - exclude_file = os.path.join(MYDIR, EXCLUDE_FILE) - - RSYNC_CMD = f"rsync -aqp --exclude-from={exclude_file} {sourcedir} {targetdir}" - - print(f"{RSYNC_CMD}") - time.sleep(1) - - # Outro - print(get_lang_text("outro1")) - time.sleep(2) - print(get_lang_text("outro2") + " " + EMAIL) - - return True, EXCLUDE, RSYNC_CMD - - except KeyboardInterrupt: - trace("Programm interrupted by user.", prefix_crlf=True) + # Asks for the source directory which should be saved + _print("srcdir1") + sourcedir = query("srcdir2") + if not sourcedir: return False, None, None + _print("srcdir3_1") + print(sourcedir) + _print("srcdir3_2") -def create_exclude_file(directory, exclude_file): - exclude_file = os.path.join(directory, exclude_file) + # asks for the destination directory in which the backup should be saved + targetdir = query("targetdir1") + if not targetdir: + return False, None, None + + _print("targetdir2_1") + print(targetdir) + _print("targetdir2_2") + + # collects all the information needed to execute the rsync command and creates it. + _print("collect") + + rsync_cmd = f"rsync -aqp --exclude-from={os.path.join(mydir, exclude_file)} {sourcedir} {targetdir}" + + print(f"{rsync_cmd}") + + # Outro + _print("outro1") + + _print("outro2") + print(EMAIL) + + return True, exclude, rsync_cmd + +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 create_alias(shell, home_dir, directory, backuppy_script): +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(directory, backuppy_script) + backuppy_script = os.path.join(mydir, backuppy_script) alias_str = f"alias backuppy='sudo {backuppy_script}'" # Check for installed ZSH @@ -162,34 +121,35 @@ def create_backuppy_script(directory, backuppy_script, rsync_cmd): os.chmod(backuppy_file, 0o777) # make file executable -def do_the_install(is_exclude: bool, rsync_cmd: str): +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. """ if is_exclude: - create_exclude_file(MYDIR, EXCLUDE_FILE) + create_exclude_file(mydir, exclude_file) if rsync_cmd: - create_backuppy_script(MYDIR, BACKUPPY_SCRIPT, rsync_cmd) - create_alias(SHELL, HOME, MYDIR, BACKUPPY_SCRIPT) + 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() # collect user input via GUI and store in env. variables + 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() # collect user input via CLI and store in env. variables + 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(is_exclude, rsync_cmd) + 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.argv.append("--gui") # TODO: disable for production sys.exit(main(sys.argv[1:])) diff --git a/install_gui.py b/install_gui.py index 6a3dfae..6d9e1fd 100644 --- a/install_gui.py +++ b/install_gui.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 0.8 +version: 0.9 file: install_gui.py summary: python installer-script in GUI-mode (needs PySide2) """ @@ -13,12 +13,21 @@ import os from PySide2 import QtWidgets # local imports -from install import * +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): + 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)) @@ -38,11 +47,11 @@ class BackuppyWizard(QtWidgets.QWizard): 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.label = QtWidgets.QLabel(get_lang_text("welcome")) self.comboBox = QtWidgets.QComboBox(self) - self.comboBox.addItem(LANG_EN, LANG_EN) - self.comboBox.addItem(LANG_DE, LANG_DE) + self.comboBox.addItem(LANG_EN) + self.comboBox.addItem(LANG_DE) layout = QtWidgets.QVBoxLayout() layout.addWidget(self.label) @@ -50,8 +59,7 @@ class Page01(QtWidgets.QWizardPage): self.setLayout(layout) def validatePage(self): - if self.comboBox.currentText() == LANG_DE: - set_language(LANG_DE) + set_language(self.comboBox.currentText()) return True @@ -77,6 +85,9 @@ class Page02(QtWidgets.QWizardPage): 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) @@ -101,25 +112,26 @@ class Page03(QtWidgets.QWizardPage): self.radio1.setChecked(True) def validatePage(self): - global EXCLUDE if self.radio1.isChecked(): - EXCLUDE = True + self.parent.exclude = True else: - EXCLUDE = False + 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): - global EXCLUDE - if EXCLUDE: + if self.parent.exclude: self.label.setText(get_lang_text("excludefile2")) else: self.label.setText(get_lang_text("excludefile3")) @@ -127,6 +139,9 @@ class Page04(QtWidgets.QWizardPage): 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() @@ -154,8 +169,7 @@ class Page05(QtWidgets.QWizardPage): self.efSourceDir.setText(dirName) def validatePage(self): - global SOURCEDIR - SOURCEDIR = self.efSourceDir.text() + self.parent.sourcedir = self.efSourceDir.text() return True @@ -163,6 +177,9 @@ class Page05(QtWidgets.QWizardPage): 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() @@ -174,7 +191,7 @@ class Page06(QtWidgets.QWizardPage): def initializePage(self): self.label1.setText(get_lang_text("srcdir3_1")) - bold_text = f"

{SOURCEDIR}

" + bold_text = f"

{self.parent.sourcedir}

" self.label2.setText(bold_text) self.label3.setText(get_lang_text("srcdir3_2")) @@ -182,6 +199,9 @@ class Page06(QtWidgets.QWizardPage): 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") @@ -206,8 +226,7 @@ class Page07(QtWidgets.QWizardPage): self.efTargetDir.setText(dirName) def validatePage(self): - global TARGETDIR - TARGETDIR = self.efTargetDir.text() + self.parent.targetdir = self.efTargetDir.text() return True @@ -215,6 +234,9 @@ class Page07(QtWidgets.QWizardPage): 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() @@ -226,15 +248,14 @@ class Page08(QtWidgets.QWizardPage): def initializePage(self): self.label1.setText(get_lang_text("targetdir2_1")) - bold_text = f"

{TARGETDIR}

" + bold_text = f"

{self.parent.targetdir}

" self.label2.setText(bold_text) self.label3.setText(get_lang_text("targetdir2_2")) def validatePage(self): - global RSYNC_CMD, MYDIR, SOURCEDIR, TARGETDIR - exclude_file = os.path.join(MYDIR, EXCLUDE_FILE) + exclude_file = os.path.join(self.parent.mydir, self.parent.exclude_file) - RSYNC_CMD = f"rsync -aqp --exclude-from={exclude_file} {SOURCEDIR} {TARGETDIR}" + self.parent.rsync_cmd = f"rsync -aqp --exclude-from={exclude_file} {self.parent.sourcedir} {self.parent.targetdir}" return True @@ -242,6 +263,9 @@ class Page08(QtWidgets.QWizardPage): 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() @@ -251,9 +275,7 @@ class Page09(QtWidgets.QWizardPage): def initializePage(self): self.label1.setText(get_lang_text("collect")) - global RSYNC_CMD - #self.label2.setText(f"{RSYNC_CMD}") - bold_text = f"

{RSYNC_CMD}

" + bold_text = f"

{self.parent.rsync_cmd}

" self.label2.setText(bold_text) @@ -274,6 +296,7 @@ class Page10(QtWidgets.QWizardPage): 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) @@ -282,14 +305,14 @@ class Page10(QtWidgets.QWizardPage): return True -def main_install_gui(): +def main_install_gui(mydir, exclude_file): app = QtWidgets.QApplication() - wizard = BackuppyWizard() + wizard = BackuppyWizard(parent=None, mydir=mydir, exclude_file=exclude_file) wizard.show() app.exec_() - return True, EXCLUDE, RSYNC_CMD + return True, wizard.exclude, wizard.rsync_cmd if __name__ == '__main__': - is_finalized, is_exclude, rsync_cmd = main_install_gui() + is_finalized, is_exclude, rsync_cmd = main_install_gui(mydir=os.getcwd(), exclude_file=EXCLUDE_FILE) From 543a0e59f15cc2cfb00208ad44efb327ff2f3c03 Mon Sep 17 00:00:00 2001 From: Paul S Date: Mon, 10 May 2021 18:53:34 +0200 Subject: [PATCH 19/21] Disable always gui mode for production release --- install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.py b/install.py index 4cc80fb..490916b 100644 --- a/install.py +++ b/install.py @@ -151,5 +151,5 @@ def main(argv): trace("Ending Backuppy install.py") if __name__ == '__main__': - sys.argv.append("--gui") # TODO: disable for production + # sys.argv.append("--gui") # TODO: disable for production sys.exit(main(sys.argv[1:])) From f9dc51340cbccf2ab7f215167555462894526a0d Mon Sep 17 00:00:00 2001 From: Paul S Date: Tue, 11 May 2021 19:07:21 +0200 Subject: [PATCH 20/21] check if user paths from user-input exists and differ - CLI-mode: check if user paths from user-input exists - CLI-mode: prevent identical source and target path --- CHANGELOG.md | 5 +++++ install.sh | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3bd4f3..8912add 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog Backuppy +## [0.10] - 2021-05-11 +### Added +- CLI-mode: check if user paths from user-input exists +- CLI-mode: prevent identical source and target path + ## [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. diff --git a/install.sh b/install.sh index f1e9ffd..7a8c145 100755 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ #!/bin/bash # """ # project: Backuppy -# version: 0.8 +# version: 0.10 # file: install.sh # summary: main entry shell script # """ From eb8e536617e916fd801e02db79c79a910066bf58 Mon Sep 17 00:00:00 2001 From: Paul S Date: Tue, 11 May 2021 19:08:56 +0200 Subject: [PATCH 21/21] check if user paths from user-input exists and differs - CLI-mode: check if user paths from user-input exists - CLI-mode: prevent identical source and target path --- install.py | 29 ++++++++++++++++------------- languages/english.py | 14 +++++++++----- languages/german.py | 12 ++++++++---- utils.py | 20 +++++++++++++++++--- 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/install.py b/install.py index 490916b..9113aba 100644 --- a/install.py +++ b/install.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 0.9 +version: 0.10 file: install.py summary: python installer-script in CLI-mode """ @@ -11,10 +11,10 @@ import sys import os # local imports -from utils import set_language, query, _print, trace +from utils import set_language, query, query_path, _print, trace, paths_are_identical # local globals -VERSION: str = "0.9" +VERSION: str = "0.10" EMAIL = "fotocoder@joschu.ch" EXCLUDE_FILE = "exclude.txt" BACKUPPY_SCRIPT = "Backuppy.sh" @@ -32,7 +32,7 @@ def main_install_cli(mydir, exclude_file): _print("intromsg2") # which Rsync options are available and which one you want to use - _print("rsyncopt") + _print("rsyncopt", wait=2) # asks if you want to exclude files/directories from backup and creates an exclude file in case of Yes exclude = query("excludefile1") @@ -47,32 +47,35 @@ def main_install_cli(mydir, exclude_file): # Asks for the source directory which should be saved _print("srcdir1") - sourcedir = query("srcdir2") + sourcedir = query_path("srcdir2") if not sourcedir: return False, None, None - _print("srcdir3_1") - print(sourcedir) + #_print("srcdir3_1") + #print(sourcedir) _print("srcdir3_2") # asks for the destination directory in which the backup should be saved - targetdir = query("targetdir1") + targetdir = query_path("targetdir1") + while paths_are_identical(sourcedir, targetdir): + _print("paths_must_differ") + targetdir = query_path("targetdir1") if not targetdir: - return False, None, None + return False, None, None - _print("targetdir2_1") - print(targetdir) + #_print("targetdir2_1") + #print(targetdir) _print("targetdir2_2") # collects all the information needed to execute the rsync command and creates it. - _print("collect") + _print("collect", wait=2) rsync_cmd = f"rsync -aqp --exclude-from={os.path.join(mydir, exclude_file)} {sourcedir} {targetdir}" print(f"{rsync_cmd}") # Outro - _print("outro1") + _print("outro1", wait=2) _print("outro2") print(EMAIL) diff --git a/languages/english.py b/languages/english.py index 367e77a..d903678 100644 --- a/languages/english.py +++ b/languages/english.py @@ -9,7 +9,11 @@ Yes="Yes" No="No" -welcome="Hello, first of all, which language do you prefer: German [DE] or English [EN]?" +welcome="Hello, first of all, which language do you prefer: German or English [de/EN]?" + +path_not_existing="Specified directory does not exist!" + +paths_must_differ="Source dir and target dir must differ!" languagepack="Perfect, the English language package is now activated. Welcome!" @@ -17,9 +21,9 @@ intromsg1="Thanks for using Backuppy to make your backups!" intromsg2="The installer will now ask you a few things to adapt your backup script to your requirements." -rsyncopt="rsync offers various options, but to simplify the installation process, I have activated the options -a, -q and -p. \n If you want to set more options, you can do thjat in the file 'Backuppy.sh'." +rsyncopt="rsync offers various options, but to simplify the installation process, I have activated the options -a, -q and -p.\nIf you want to set more options, you can do that in the file 'Backuppy.sh'." -excludefile1="Now I need to know if you want to exclude one or more files/directories from your backups.\nThen you can adjust this in the 'exclude.txt'.\nThere you can exclude directories and files in the format '/directory' '/file.txt'.\nDo you want to exclude files/directories or not? [Y/N]" +excludefile1="Now I need to know if you want to exclude one or more files/directories from your backups.\nThen you can adjust this in the 'exclude.txt'.\nThere you can exclude directories and files in the format '/directory' '/file.txt'.\nDo you want to exclude files/directories or not? [y/N]" excludefile2="Perfect, then you can enter your files/directories to be excluded in the file 'exclude.txt' after completing the installation of Backuppy." @@ -30,12 +34,12 @@ srcdir1="Now we come to one of the most important parts of installing Backuppy:" srcdir2="Which directory do you want to save (e.g. the home directory)? Please enter an absolute path (e.g. '/home/username/')." srcdir3_1="you have typed in the following source path: " -srcdir3_2="if this path is not correct, adjust it in the file 'Backuppy.sh'." +srcdir3_2="(You may adjust this any time 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="(You may adjust this any time 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..acf978e 100644 --- a/languages/german.py +++ b/languages/german.py @@ -9,15 +9,19 @@ Yes="Ja" No="Nein" +path_not_existing="Der angegebene Pfad existiert nicht!" + +paths_must_differ="Quell- and Zielpfad dürfen nicht identisch sein!" + languagepack="Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen!" intromsg1="Danke, dass du Backuppy nutzt, um deine Backups zu erstellen!" intromsg2="Der Installer wird dich nun einige Dinge abfragen, um dein Backup-Skript an deine Anforderungen anzupassen." -rsyncopt="rsync bietet verschiedene Optionen an, um das Ganze jedoch zu vereinfachen, habe ich die Optionen -a, -q und -p aktiviert. \n Wenn du mehr einstellen willst, kannst du das in der Datei 'Backuppy.sh' machen." +rsyncopt="rsync bietet verschiedene Optionen an, um das Ganze jedoch zu vereinfachen, habe ich die Optionen -a, -q und -p aktiviert.\nWenn du mehr einstellen willst, kannst du das in der Datei 'Backuppy.sh' machen." -excludefile1="Nun muss ich noch wissen, ob du ein oder mehrere Dateien/Verzeichnisse vom Backup ausschliessen möchtest.\nDann kannst du das in der 'exclude.txt' anpassen.\nDort kannst du dann im Format '/Verzeichnis' '/Datei.txt' Verzeichnisse und Dateien ausschliessen.\nMöchtest du Dateien/Verzeichnisse ausschliessen oder nicht? [J/N]" +excludefile1="Nun muss ich noch wissen, ob du ein oder mehrere Dateien/Verzeichnisse vom Backup ausschliessen möchtest.\nDann kannst du das in der 'exclude.txt' anpassen.\nDort kannst du dann im Format '/Verzeichnis' '/Datei.txt' Verzeichnisse und Dateien ausschliessen.\nMöchtest du Dateien/Verzeichnisse ausschliessen oder nicht? [j/N]" excludefile2="Perfekt, dann kannst du nach der Fertigstellung der Installation von Backuppy deine auszuschliessenden\nDateien/Verzeichnisse in der Datei 'exclude.txt eintragen." @@ -28,12 +32,12 @@ srcdir1="Nun kommen wir zu einem der wichtigesten Teile der Installation von Bac srcdir2="Welches Verzeichnis möchtest du sichern (z.B. das Homeverzeichnis)? Bitte gib einen absoluten Pfad (z.B. '/home/username/') an." srcdir3_1="du hast folgenden Quellpfad eingetippt: " -srcdir3_2="wenn dieser Pfad nicht stimmen sollte, dann passe ihn in der Datei 'Backuppy.sh' an." +srcdir3_2="(Der Pfad kann jederzeit in der Datei 'Backuppy.sh' angepasst werden.)" 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="(Der Pfad kann jederzeit in der Datei 'Backuppy.sh' angepasst werden.)" 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 index ce6df85..4a8dea6 100644 --- a/utils.py +++ b/utils.py @@ -8,6 +8,7 @@ summary: utilities script # Standard library imports import time +import os # local imports from languages import english @@ -25,7 +26,6 @@ class bcolors: 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. @@ -49,9 +49,9 @@ def get_lang_text(search_str: str): return_str = eval("german." + search_str) return return_str -def _print(message_txt: str): +def _print(message_txt: str, wait: int=1): print("\n" + get_lang_text(message_txt) + "\n") - time.sleep(1) + time.sleep(wait) def set_language(language): """ Set global constant "LANGUAGE" to given language ("EN"/"DE"). """ @@ -62,6 +62,20 @@ def set_language(language): else: LANGUAGE = LANG_EN +def paths_are_identical(path1: str, path2: str): + if os.path.isdir(path1): + if path1 == path2: + return True + return False + +def query_path(question_txt: str): + path = query(question_txt) + while not os.path.isdir(path): + _print("path_not_existing") + #path = input("Please retype an existing directory.\n> ") + path = query(question_txt) + return path + def query(question_txt: str): try: answer_txt = input(get_lang_text(question_txt) + "\n> ")