From eeeabbbbf9d25dc315a875d5973b14c9050aa57d Mon Sep 17 00:00:00 2001
From: Paul S
Date: Wed, 5 May 2021 17:20:04 +0200
Subject: [PATCH 1/3] Updates language-files to be used in GUI-installer
---
languages/english.py | 12 ++++++------
languages/german.py | 12 ++++++------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/languages/english.py b/languages/english.py
index 891ef03..e04cfe2 100644
--- a/languages/english.py
+++ b/languages/english.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
-"""
-project: Backuppy
-file: languages/english.py
-summary: english language file
-"""
+# """
+# project: Backuppy
+# file: languages/english.py
+# summary: english language file
+# """
Yes="Yes"
@@ -17,7 +17,7 @@ intromsg2="The installer will now ask you a few things to adapt your backup scri
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'."
-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?"
+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."
diff --git a/languages/german.py b/languages/german.py
index bf4081e..07508c9 100644
--- a/languages/german.py
+++ b/languages/german.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
-"""
-project: Backuppy
-file: languages/german.py
-summary: german language file
-"""
+# """
+# project: Backuppy
+# file: languages/german.py
+# summary: german language file
+# """
Yes="Ja"
@@ -17,7 +17,7 @@ intromsg2="Der Installer wird dich nun einige Dinge abfragen, um dein Backup-Skr
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."
-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?"
+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."
From 67a03607270e6262efc8ce19f4cd7602411b9a19 Mon Sep 17 00:00:00 2001
From: Paul S
Date: Wed, 5 May 2021 17:21:06 +0200
Subject: [PATCH 2/3] Updated CHANGELOG.md
---
CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 CHANGELOG.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..e975aa4
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,32 @@
+# Changelog Backuppy
+
+## [1.02.000] - 2021-05-05
+### Added
+- Now fully functional as the shell version
+
+### Changed
+- Updated "README.md" concerning installation and usage
+- Changed filename "Changelog.MD" to "CHANGELOG.md"
+
+### Fixed
+- Fixed problem with the Bash/ZSH config
+
+## [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 d4327b64dbaa8d01c06b14b513f4a3c191601995 Mon Sep 17 00:00:00 2001
From: Paul S
Date: Wed, 5 May 2021 17:21:54 +0200
Subject: [PATCH 3/3] Implemented same functionality as in install.sh
---
install.py | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 336 insertions(+)
create mode 100644 install.py
diff --git a/install.py b/install.py
new file mode 100644
index 0000000..e99284f
--- /dev/null
+++ b/install.py
@@ -0,0 +1,336 @@
+#!/usr/bin/env python3
+"""
+project: Backuppy
+version: 1.02.000
+file: install.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.02.000"
+EMAIL: str = "fotocoder@joschu.ch"
+TARGET_SCRIPT: str = "Backuppy.sh"
+EXCLUDE_FILE: str = "exclude.txt"
+LANG_EN: str = "English"
+LANG_DE: str = "German"
+LANGUAGE: str = LANG_EN
+MYDIR: str = os.environ.get("mydir")
+if not MYDIR:
+ MYDIR = os.getcwd()
+SOURCEDIR: str = None
+EXCLUDE: bool = True
+TARGETDIR: str = None
+RSYNC_CMD: str = None
+
+def 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 trace(message_txt: str):
+ """ Print a formatted message to std out. """
+ print(f"[ OK ]", message_txt)
+
+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):
+ 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"))
+ bold_text = f"{SOURCEDIR}
"
+ self.label2.setText(bold_text)
+ self.label3.setText(get_lang_text("srcdir3_2"))
+
+
+class Page07(QtWidgets.QWizardPage):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.label1 = QtWidgets.QLabel()
+ self.edit = QtWidgets.QLineEdit()
+ layout = QtWidgets.QVBoxLayout()
+ layout.addWidget(self.label1)
+ layout.addWidget(self.edit)
+ self.setLayout(layout)
+
+ def initializePage(self):
+ self.label1.setText(get_lang_text("targetdir1"))
+
+ def validatePage(self):
+ global TARGETDIR
+ TARGETDIR = self.edit.text()
+
+ return True
+
+
+class Page08(QtWidgets.QWizardPage):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.label1 = QtWidgets.QLabel()
+ self.label2 = QtWidgets.QLabel()
+ self.label3 = QtWidgets.QLabel()
+ layout = QtWidgets.QVBoxLayout()
+ layout.addWidget(self.label1)
+ layout.addWidget(self.label2)
+ layout.addWidget(self.label3)
+ self.setLayout(layout)
+
+ def initializePage(self):
+ self.label1.setText(get_lang_text("targetdir2_1"))
+ bold_text = f"{TARGETDIR}
"
+ self.label2.setText(bold_text)
+ self.label3.setText(get_lang_text("targetdir2_2"))
+
+ def validatePage(self):
+ global RSYNC_CMD, MYDIR, SOURCEDIR, TARGETDIR
+ RSYNC_CMD = f"rsync -aqp --exclude-from={MYDIR}/exclude.txt {SOURCEDIR} {TARGETDIR}"
+
+ return True
+
+
+class Page09(QtWidgets.QWizardPage):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.label1 = QtWidgets.QLabel()
+ self.label2 = QtWidgets.QLabel()
+ layout = QtWidgets.QVBoxLayout()
+ layout.addWidget(self.label1)
+ layout.addWidget(self.label2)
+ self.setLayout(layout)
+
+ def initializePage(self):
+ self.label1.setText(get_lang_text("collect"))
+ global RSYNC_CMD
+ #self.label2.setText(f"{RSYNC_CMD}")
+ bold_text = f"{RSYNC_CMD}
"
+ self.label2.setText(bold_text)
+
+
+class Page10(QtWidgets.QWizardPage):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.label1 = QtWidgets.QLabel()
+ self.label2 = QtWidgets.QLabel()
+ self.label3 = QtWidgets.QLabel()
+ layout = QtWidgets.QVBoxLayout()
+ layout.addWidget(self.label1)
+ layout.addWidget(self.label2)
+ layout.addWidget(self.label3)
+ self.setLayout(layout)
+ self.setFinalPage(True)
+
+ def initializePage(self):
+ self.label1.setText(get_lang_text("outro1"))
+ self.label2.setText(get_lang_text("outro2"))
+ urlLink = f"{EMAIL}"
+ self.label3.setText(urlLink)
+ self.label3.setOpenExternalLinks(True)
+
+ def validatePage(self):
+ # create shell script with rsync command
+ create_shell_script(RSYNC_CMD)
+
+ return True
+
+
+def create_shell_script(command_str: str):
+ rc_filepath: str = None
+
+ global MYDIR, EXCLUDE, TARGET_SCRIPT
+
+ TARGET_SCRIPT = os.path.join(MYDIR, TARGET_SCRIPT)
+ ALIAS_STR = f"alias backuppy='sudo {TARGET_SCRIPT}'"
+ current_shell = os.environ.get("SHELL")
+ user_home = os.environ.get("HOME")
+
+ ## STEP 1: update shell config scripts with alias for backuppy
+ # .zshrc case1 and case2
+ if current_shell in ("/usr/bin/zsh", "/bin/zsh"):
+ # Appending to bash config file
+ rc_filepath = os.path.join(user_home, ".zshrc")
+
+ # .bashrc case1 and case2
+ elif current_shell in ("/usr/bin/bash", "/bin/bash"):
+ # Appending to zsh config file
+ rc_filepath = os.path.join(user_home, ".bashrc")
+
+ if os.path.isfile(rc_filepath):
+ with open(rc_filepath, 'a') as file1:
+ trace(f"Writing {ALIAS_STR} to config file '{rc_filepath}'.")
+ file1.write("\n# Following line was created by Backuppy\n" + ALIAS_STR + "\n")
+
+ ## 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")
+
+ ## STEP 3: enter the rsync command in Backuppy.sh
+ with open(TARGET_SCRIPT, "w") as file3:
+ trace(f"Writing rsync-command to file '{TARGET_SCRIPT}':\n {command_str}")
+ file3.writelines("#!/bin/bash" + "\n")
+ file3.writelines(command_str + "\n")
+
+
+if __name__ == '__main__':
+ trace(f"Starting Backuppy install.py v{VERSION}")
+ app = QtWidgets.QApplication()
+ wizard = BackuppyWizard()
+ wizard.show()
+
+ app.exec_()
+
+ trace("Ending Backuppy install.py")