From 360fadb35a3f064af29a3c8532603ecc8e127b69 Mon Sep 17 00:00:00 2001 From: FotoCoder Date: Thu, 8 Apr 2021 18:11:54 +0200 Subject: [PATCH 01/35] install.sh aktualisiert --- install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install.sh b/install.sh index db400e4..6c92375 100755 --- a/install.sh +++ b/install.sh @@ -68,3 +68,4 @@ echo "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir" >> Bac # Outro echo -e "Perfekt, jetzt sind wir fertig. Nun kannst du Backuppy im Terminal mit dem Befehl 'backuppy' starten. Möglicherweise musst du dann noch dein Passwort eintippen, damit Backuppy ordnungsgemäss arbeiten kann." +sleep 2 From 6edc54be087bf6f9706aad56dd895c71a973609a Mon Sep 17 00:00:00 2001 From: FotoCoder Date: Thu, 8 Apr 2021 18:32:50 +0200 Subject: [PATCH 02/35] added a farewell text at the end of the installer --- install.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install.sh b/install.sh index 6c92375..0c7f46c 100755 --- a/install.sh +++ b/install.sh @@ -69,3 +69,4 @@ echo "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir" >> Bac # Outro echo -e "Perfekt, jetzt sind wir fertig. Nun kannst du Backuppy im Terminal mit dem Befehl 'backuppy' starten. Möglicherweise musst du dann noch dein Passwort eintippen, damit Backuppy ordnungsgemäss arbeiten kann." sleep 2 +echo "Backuppy ist nun installiert, viel Spass damit! Bei Fragen schreib mir einfach: fotocoder@joschu.ch" From 7a8cf88830f3c296c9e94580b3304c862b02af48 Mon Sep 17 00:00:00 2001 From: FotoCoder Date: Sat, 10 Apr 2021 10:36:31 +0200 Subject: [PATCH 03/35] Added English language In this version I have managed it: now English is supported as well as German! --- .languages/english.txt | 29 ++++++++++++++++ .languages/german.txt | 29 ++++++++++++++++ install.sh | 75 ++++++++++++++++++++++++++---------------- 3 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 .languages/english.txt create mode 100644 .languages/german.txt diff --git a/.languages/english.txt b/.languages/english.txt new file mode 100644 index 0000000..29c8869 --- /dev/null +++ b/.languages/english.txt @@ -0,0 +1,29 @@ +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'." + +excludefile1="Now I need to know if you want to exclude one or more files/directories from your backups. Then you can adjust this in the 'exclude.txt'. \There you can exclude directories and files in the format '/directory' '/file.txt'.\n Do 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." + +excludefile3="Good, then I won't even create the file 'exclude.txt', because you don't need it." + +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'." + +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'." + +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. \n Note: I recommend that you download Backuppy completely again in this case." + +outro1="Now we have reached the end. Now you can start Backuppy in the terminal with the command 'backuppy'. You may then have to type in your password so that Backuppy can work properly." + +outro2="Backuppy is now installed, have fun with it! If you have any questions, just write to me: fotocoder@joschu.ch" diff --git a/.languages/german.txt b/.languages/german.txt new file mode 100644 index 0000000..d185c03 --- /dev/null +++ b/.languages/german.txt @@ -0,0 +1,29 @@ +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." + +excludefile1="Nun muss ich noch wissen, ob du ein oder mehrere Dateien/Verzeichnisse vom Backup ausschliessen möchtest. Dann kannst du das in der 'exclude.txt' anpassen. \n Dort kannst du dann im Format '/Verzeichnis' '/Datei.txt' Verzeichnisse und Dateien ausschliessen.\n Möchtest du Dateien/Verzeichnisse ausschliessen oder nicht? [J/N]" + +excludefile2="Perfekt, dann kannst du nach der Fertigstellung der Installation von Backuppy deine auszuschliessenden Dateien/Verzeichnisse in der Datei 'exclude.txt eintragen." + +excludefile3="Gut, dann erstelle ich die Datei 'exclude.txt' gar nicht erst, da du sie ja nicht brauchst." + +srcdir1="Nun kommen wir zu einem der wichtigesten Teile der Installation von Backuppy:" + +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." + +targetdir1="Nun muss ich noch wissen, wo Backuppy dein Backup ablegen soll, das Zielverzeichnis also. \n Bitte 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." + +collect="Nun sind wir fast am Ende des Installers angelangt. Ich erstelle nun den rsync-Befehl für dich und zeige ihn dir nachher nochmal. \n Wenn dir da etwas auffallen sollte, brich den Installer einfach ab und fange nochmal von Vorne an. \n Achtung: ich empfehle dir, Backuppy in diesem Fall nochmal komplett neu zu installieren." + +outro1="Perfekt, jetzt sind wir fertig. Nun kannst du Backuppy im Terminal mit dem Befehl 'backuppy' starten. Möglicherweise musst du dann noch dein Passwort eintippen, damit Backuppy ordnungsgemäss arbeiten kann." + +outro2="Backuppy ist nun installiert, viel Spass damit! Bei Fragen schreib mir einfach: fotocoder@joschu.ch" diff --git a/install.sh b/install.sh index 0c7f46c..db467a2 100755 --- a/install.sh +++ b/install.sh @@ -2,12 +2,28 @@ # Variables mydir=$PWD +langDE=".languages/german.txt" +langEN=".languages/english.txt" # Intro -echo -e "\n Danke, dass du Backuppy nutzt, um deine Backups zu erstellen! \n" -sleep 2 -echo -e "Der Installer wird dich nun einige Dinge abfragen, um dein Backup-Skript an deine Anforderungen anzupassen. \n" -sleep 2 +# 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' @@ -15,58 +31,61 @@ 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 "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. \n" -sleep 2 +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 "Nun muss ich noch wissen, ob du ein oder mehrere Dateien/Verzeichnisse vom Backup ausschliessen möchtest. Dann kannst du das in der 'exclude.txt' anpassen. \n Dort kannst du dann im Format '/Verzeichnis' '/Datei.txt' Verzeichnisse und Dateien ausschliessen.\n Möchtest du Dateien/Verzeichnisse ausschliessen oder nicht? [J/N]" +echo -e "$excludefile1" read exclude if [ $exclude = "J" ]; then - echo -e "Perfekt, dann kannst du nach der Fertigstellung der Installation von Backuppy deine auszuschliessenden Dateien/Verzeichnisse in der Datei 'exclude.txt eintragen. \n" + echo -e "$excludefile2 \n" touch exclude.txt - sleep 2 + sleep 1 +elif [ $exclude = "Y" ]; then + echo -e "$excludefile2 \n" + touch exclude.txt + sleep 1 fi if [ $exclude = "N" ]; then - echo -e "Gut, dann erstelle ich die Datei 'exclude.txt' gar nicht erst, da du sie ja nicht brauchst. \n" - rm exclude.txt - sleep 2 + echo -e "$excludefile3 \n" + sleep 1 fi # Asks for the source directory which should be saved -echo -e "Nun kommen wir zu einem der wichtigesten Teile der Installation von Backuppy:" +echo -e "$srcdir1" sleep 1 -echo -e "Welches Verzeichnis möchtest du sichern (z.B. das Homeverzeichnis)? Bitte gib einen absoluten Pfad (z.B. '/home/username/') an." +echo -e "$srcdir2" read sourcedir -echo -e "du hast folgenden Quellpfad eingetippt: $sourcedir wenn dieser Pfad nicht stimmen sollte, dann passe ihn in der Datei 'backuppy.sh' an" -sleep 2 +echo -e "$srcdir3_1 $sourcedir $srcdir3_2" +sleep 1 -# fasks for the destination directory in which the backup should be saved -echo -e "Nun muss ich noch wissen, wo Backuppy dein Backup ablegen soll, das Zielverzeichnis also. \n Bitte tippe dieses gewissenhaft und auf die Weise wie beim Quellverzeichnis ein." +# asks for the destination directory in which the backup should be saved +echo -e "$targetdir1" read targetdir -echo -e "du hast folgenden Zielpfad eingetippt: $targetdir wenn dieser Pfad nicht stimmen sollte, dann passe ihn in der Datei 'backuppy.sh' an" -sleep 2 +echo -e "$targetdir2_1 $targetdir $targetdir2_2" +sleep 1 # alias entry in .bashrc or .zshrc #zshrc -echo "$SHELL" +echo $SHELL if [ $SHELL = "/usr/bin/zsh" ]; then - echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> test1.txt fi #bashrc if [ $SHELL = "/usr/bin/bash" ]; then - echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.bashrc + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> test2.txt fi # collects all the information needed to execute the rsync command and creates it. -echo -e "Nun sind wir fast am Ende des Installers angelangt. Ich erstelle nun den rsync-Befehl für dich und zeige ihn dir nachher nochmal. \n Wenn dir da etwas auffallen sollte, brich den Installer einfach ab und fange nochmal von Vorne an. \n Achtung: ich empfehle dir, Backuppy in diesem Fall nochmal komplett neu zu installieren. \n" -sleep 2 +echo -e "$collect \n" +sleep 1 echo -e "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir \n" -sleep 2 +sleep 1 # enter the rsync command in Backuppy.sh echo "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir" >> Backuppy.sh # Outro -echo -e "Perfekt, jetzt sind wir fertig. Nun kannst du Backuppy im Terminal mit dem Befehl 'backuppy' starten. Möglicherweise musst du dann noch dein Passwort eintippen, damit Backuppy ordnungsgemäss arbeiten kann." +echo -e "$outro1" sleep 2 -echo "Backuppy ist nun installiert, viel Spass damit! Bei Fragen schreib mir einfach: fotocoder@joschu.ch" +echo "$outro2" From 13ee4cff74b7cc7c9c719b3420014adeba4505f9 Mon Sep 17 00:00:00 2001 From: FotoCoder Date: Sat, 10 Apr 2021 10:39:24 +0200 Subject: [PATCH 04/35] Corrected a mistake I had to correct a mistake with the .zshrc and .bashrc --- install.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index db467a2..f28fc48 100755 --- a/install.sh +++ b/install.sh @@ -69,11 +69,11 @@ sleep 1 #zshrc echo $SHELL if [ $SHELL = "/usr/bin/zsh" ]; then - echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> test1.txt + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc fi #bashrc if [ $SHELL = "/usr/bin/bash" ]; then - echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> test2.txt + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.bashrc fi # collects all the information needed to execute the rsync command and creates it. From 556ffc6027fe5de6065d3712440a0e03d4f84a6d Mon Sep 17 00:00:00 2001 From: FotoCoder Date: Sat, 10 Apr 2021 08:46:17 +0000 Subject: [PATCH 05/35] Updated README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 901646d..1450c02 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,13 @@ More infos: see constants and readme.md # ToDo -- translate Backuppy to English - make a graphical installer & Tool +- add a log-file for the rsync errors # Dependencies rsync - 'sudo apt install' rsync or 'sudo pacman -S rsync' # because Backuppy makes its bakups with rsync + +# IMPORTANT + +You have to remove the "backuppy" alias in your .bashrc or .zshrc before reinstall Backuppy. \ No newline at end of file From 64a01a9985769bb534bbe64d2a86f1afb24b35f8 Mon Sep 17 00:00:00 2001 From: FotoCoder Date: Wed, 14 Apr 2021 04:29:51 +0000 Subject: [PATCH 06/35] readme.md angepasst --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 1450c02..c1c7e67 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,15 @@ No backup - no pity. So that this is not the case, you make backups. Backuppy tries to simplify this. Name: Backuppy + Description: Make daily backups with Backuppy to avoid losing your data. + Usage: start it with: 'backuppy' + Author: Joël Schurter + Licence: GPL3 + More infos: see constants and readme.md # ToDo From 9cf4ca47a1c59e7a60edac91d41a61c1f6592152 Mon Sep 17 00:00:00 2001 From: Paul S Date: Tue, 4 May 2021 18:10:32 +0200 Subject: [PATCH 07/35] initial version of graphical installer - Graphical installer based on Python3 with PySide2 GUI framework (Qt-bindings for Python) --- .gitignore | 1 + README.md | 25 +- backup.py | 268 ++++++++++++++++++ changelog.MD | 16 ++ install.sh | 4 +- .../english.txt => languages/english.py | 19 +- .languages/german.txt => languages/german.py | 25 +- 7 files changed, 343 insertions(+), 15 deletions(-) create mode 100644 .gitignore create mode 100644 backup.py create mode 100644 changelog.MD rename .languages/english.txt => languages/english.py (76%) rename .languages/german.txt => languages/german.py (55%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/README.md b/README.md index c1c7e67..5f3d97f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ Name: Backuppy Description: Make daily backups with Backuppy to avoid losing your data. -Usage: start it with: 'backuppy' +Usage: +- start it with: `backuppy` +- start gui version with: `python backup.py` (install pip package 'PySide2' first) Author: Joël Schurter @@ -16,13 +18,28 @@ More infos: see constants and readme.md # ToDo -- make a graphical installer & Tool - add a log-file for the rsync errors # Dependencies +- rsync (because Backuppy makes its backups with rsync) -rsync - 'sudo apt install' rsync or 'sudo pacman -S rsync' # because Backuppy makes its bakups with rsync + execute `sudo apt install rsync` on Debian/Ubuntu + + execute `sudo pacman -S rsync` on Arch/Manjaro + +# Dependencies for the graphical installer: +- pip (the python package manager) + + execute `sudo apt install python3-pip` on Debian/Ubuntu + + 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. \ No newline at end of file +You have to remove the "backuppy" alias in your .bashrc or .zshrc before reinstall Backuppy. diff --git a/backup.py b/backup.py new file mode 100644 index 0000000..d2f6938 --- /dev/null +++ b/backup.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +""" +project: Backuppy +version: 1.01.000 +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.000" +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 = "fotocoder@joschu.ch" + 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") diff --git a/changelog.MD b/changelog.MD new file mode 100644 index 0000000..0b581e8 --- /dev/null +++ b/changelog.MD @@ -0,0 +1,16 @@ +# Changelog Backuppy + +## [1.01.000] - 2021-05-04 +### Added + - Graphical installer based on Python3 with PySide2 GUI framework (Qt-bindings for Python) + + ### 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). diff --git a/install.sh b/install.sh index f28fc48..ab6e3fc 100755 --- a/install.sh +++ b/install.sh @@ -2,8 +2,8 @@ # Variables mydir=$PWD -langDE=".languages/german.txt" -langEN=".languages/english.txt" +langDE="languages/german.py" +langEN="languages/english.py" # Intro # language diff --git a/.languages/english.txt b/languages/english.py similarity index 76% rename from .languages/english.txt rename to languages/english.py index 29c8869..891ef03 100644 --- a/.languages/english.txt +++ b/languages/english.py @@ -1,10 +1,23 @@ +#!/usr/bin/env python3 +""" +project: Backuppy +file: languages/english.py +summary: english language file +""" + +Yes="Yes" + +No="No" + +languagepack="Perfect, the English language package is now activated. Welcome!" + 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'." -excludefile1="Now I need to know if you want to exclude one or more files/directories from your backups. Then you can adjust this in the 'exclude.txt'. \There you can exclude directories and files in the format '/directory' '/file.txt'.\n Do 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?" excludefile2="Perfect, then you can enter your files/directories to be excluded in the file 'exclude.txt' after completing the installation of Backuppy." @@ -22,8 +35,8 @@ targetdir1="Now I need to know where Backuppy should save your backups, i.e. the 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'." -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. \n Note: I recommend that you download Backuppy completely again in this case." +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." outro1="Now we have reached the end. Now you can start Backuppy in the terminal with the command 'backuppy'. You may then have to type in your password so that Backuppy can work properly." -outro2="Backuppy is now installed, have fun with it! If you have any questions, just write to me: fotocoder@joschu.ch" +outro2="Backuppy is now installed, have fun with it! If you have any questions, just write to me." diff --git a/.languages/german.txt b/languages/german.py similarity index 55% rename from .languages/german.txt rename to languages/german.py index d185c03..bf4081e 100644 --- a/.languages/german.txt +++ b/languages/german.py @@ -1,12 +1,25 @@ +#!/usr/bin/env python3 +""" +project: Backuppy +file: languages/german.py +summary: german language file +""" + +Yes="Ja" + +No="Nein" + +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." -excludefile1="Nun muss ich noch wissen, ob du ein oder mehrere Dateien/Verzeichnisse vom Backup ausschliessen möchtest. Dann kannst du das in der 'exclude.txt' anpassen. \n Dort kannst du dann im Format '/Verzeichnis' '/Datei.txt' Verzeichnisse und Dateien ausschliessen.\n Mö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?" -excludefile2="Perfekt, dann kannst du nach der Fertigstellung der Installation von Backuppy deine auszuschliessenden Dateien/Verzeichnisse in der Datei 'exclude.txt eintragen." +excludefile2="Perfekt, dann kannst du nach der Fertigstellung der Installation von Backuppy deine auszuschliessenden\nDateien/Verzeichnisse in der Datei 'exclude.txt eintragen." excludefile3="Gut, dann erstelle ich die Datei 'exclude.txt' gar nicht erst, da du sie ja nicht brauchst." @@ -17,13 +30,13 @@ srcdir2="Welches Verzeichnis möchtest du sichern (z.B. das Homeverzeichnis)? Bi srcdir3_1="du hast folgenden Quellpfad eingetippt: " srcdir3_2="wenn dieser Pfad nicht stimmen sollte, dann passe ihn in der Datei 'Backuppy.sh' an." -targetdir1="Nun muss ich noch wissen, wo Backuppy dein Backup ablegen soll, das Zielverzeichnis also. \n Bitte tippe dieses gewissenhaft und auf die Weise wie beim Quellverzeichnis ein." +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." -collect="Nun sind wir fast am Ende des Installers angelangt. Ich erstelle nun den rsync-Befehl für dich und zeige ihn dir nachher nochmal. \n Wenn dir da etwas auffallen sollte, brich den Installer einfach ab und fange nochmal von Vorne an. \n Achtung: ich empfehle dir, Backuppy in diesem Fall nochmal komplett neu zu installieren." +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." -outro1="Perfekt, jetzt sind wir fertig. Nun kannst du Backuppy im Terminal mit dem Befehl 'backuppy' starten. Möglicherweise musst du dann noch dein Passwort eintippen, damit Backuppy ordnungsgemäss arbeiten kann." +outro1="Perfekt, jetzt sind wir fertig. Nun kannst du Backuppy im Terminal mit dem Befehl 'backuppy' starten.\nMöglicherweise musst du dann noch dein Passwort eintippen, damit Backuppy ordnungsgemäss arbeiten kann." -outro2="Backuppy ist nun installiert, viel Spass damit! Bei Fragen schreib mir einfach: fotocoder@joschu.ch" +outro2="Backuppy ist nun installiert, viel Spass damit! Bei Fragen schreib mir einfach." From 5d5477e3e8cc181538457bcc64f14af45aacde3d Mon Sep 17 00:00:00 2001 From: Paul S Date: Tue, 4 May 2021 18:14:22 +0200 Subject: [PATCH 08/35] Update changelog.MD --- changelog.MD | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.MD b/changelog.MD index 0b581e8..61fc7fc 100644 --- a/changelog.MD +++ b/changelog.MD @@ -3,6 +3,7 @@ ## [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 From c281a0a588c37ac4874e7255b177424c160054d6 Mon Sep 17 00:00:00 2001 From: Paul S Date: Tue, 4 May 2021 18:25:28 +0200 Subject: [PATCH 09/35] Use defined E-MAIL constant --- backup.py | 6 +++--- changelog.MD | 14 +++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/backup.py b/backup.py index d2f6938..266dc56 100644 --- a/backup.py +++ b/backup.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 """ project: Backuppy -version: 1.01.000 +version: 1.01.001 file: backup.py summary: main entry python file """ @@ -19,7 +19,7 @@ from languages import english from languages import german # local globals -VERSION: str = "1.01.000" +VERSION: str = "1.01.001" EMAIL: str = "fotocoder@joschu.ch" LANG_EN: str = "English" LANG_DE: str = "German" @@ -254,7 +254,7 @@ class Page10(QtWidgets.QWizardPage): def initializePage(self): self.label1.setText(get_lang_text("outro1")) self.label2.setText(get_lang_text("outro2")) - urlLink = "fotocoder@joschu.ch" + urlLink = f"{EMAIL}" self.label3.setText(urlLink) self.label3.setOpenExternalLinks(True) diff --git a/changelog.MD b/changelog.MD index 61fc7fc..51721aa 100644 --- a/changelog.MD +++ b/changelog.MD @@ -1,13 +1,17 @@ # 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 +- 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 +### 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 From 453a6219e46443de664c75047be413729479b0df Mon Sep 17 00:00:00 2001 From: PhotoLinux Date: Tue, 4 May 2021 19:07:16 +0200 Subject: [PATCH 10/35] fixed problem with the Bash/ZSH config Fixed a problem with the .bashrc and the .zshrc file. --- install.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index f28fc48..2bef750 100755 --- a/install.sh +++ b/install.sh @@ -66,15 +66,24 @@ echo -e "$targetdir2_1 $targetdir $targetdir2_2" sleep 1 # alias entry in .bashrc or .zshrc -#zshrc +# .zshrc case 1 echo $SHELL if [ $SHELL = "/usr/bin/zsh" ]; then echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc fi -#bashrc +# 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" From 898f4b5809371c4ea5016d4be280e80471d7b666 Mon Sep 17 00:00:00 2001 From: Paul S Date: Wed, 5 May 2021 12:33:17 +0200 Subject: [PATCH 11/35] fixed problem with the Bash/ZSH config --- install.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index ab6e3fc..2852df2 100755 --- a/install.sh +++ b/install.sh @@ -66,15 +66,24 @@ echo -e "$targetdir2_1 $targetdir $targetdir2_2" sleep 1 # alias entry in .bashrc or .zshrc -#zshrc +# .zshrc case 1 echo $SHELL if [ $SHELL = "/usr/bin/zsh" ]; then echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc fi -#bashrc +# 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" From e2c03559b6a1a5faa53652addc34adcd3c2e0f19 Mon Sep 17 00:00:00 2001 From: Paul S Date: Wed, 5 May 2021 17:18:36 +0200 Subject: [PATCH 12/35] Updated README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5f3d97f..fae01e5 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,17 @@ Name: Backuppy Description: Make daily backups with Backuppy to avoid losing your data. -Usage: -- start it with: `backuppy` -- start gui version with: `python backup.py` (install pip package 'PySide2' first) +Installation: execute `chmod +x install.sh && .\install.sh` + +Install. GUI: execute `chmod +x install.sh && .\install.sh --gui` + +Usage: execute `backuppy` Author: Joël Schurter Licence: GPL3 -More infos: see constants and readme.md +More infos: see constants and README.md # ToDo From eeeabbbbf9d25dc315a875d5973b14c9050aa57d Mon Sep 17 00:00:00 2001 From: Paul S Date: Wed, 5 May 2021 17:20:04 +0200 Subject: [PATCH 13/35] 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 14/35] 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 15/35] 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") From 4bc87b96423dd84d8ed484e2ae11f8e1ac8f9648 Mon Sep 17 00:00:00 2001 From: Paul S Date: Wed, 5 May 2021 17:22:57 +0200 Subject: [PATCH 16/35] Implemented option "--gui" for graphical installer --- install.sh | 183 +++++++++++++++++++++++++++-------------------------- 1 file changed, 95 insertions(+), 88 deletions(-) diff --git a/install.sh b/install.sh index 2852df2..ba8f1da 100755 --- a/install.sh +++ b/install.sh @@ -2,99 +2,106 @@ # Variables mydir=$PWD -langDE="languages/german.py" -langEN="languages/english.py" +langDE="./languages/german.py" +langEN="./languages/english.py" -# 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 +# Check if graphical installer should be executed +if [ "$1" == "--gui" ]; then + python3 -B install.py + exit 0 +else + # Intro + # language + echo -e "Hello, first of all, which language do you prefer: German [DE] or English [EN]?" + read language + if [ $language = "DE" ]; then + echo -e "Perfekt, nun ist das deutsche Sprachpaket aktiviert. Willkommen! \n" + . $langDE + sleep 1 + fi + if [ $language = "EN" ]; then + echo -e "Perfect, the English language package is now activated. Welcome!. \n" + . $langEN + sleep 1 + fi + + echo -e "\n$intromsg1 \n" sleep 1 -fi -if [ $language = "EN" ]; then - echo -e "Perfect, the English language package is now activated. Welcome!. \n" - . $langEN + echo -e " \n$intromsg2\n" sleep 1 -fi + # Installer -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 + # 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 -elif [ $exclude = "Y" ]; then - echo -e "$excludefile2 \n" - touch exclude.txt + + # 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 -fi -if [ $exclude = "N" ]; then - echo -e "$excludefile3 \n" + echo -e "$srcdir2" + read sourcedir + echo -e "$srcdir3_1 $sourcedir $srcdir3_2" sleep 1 + + # asks for the destination directory in which the backup should be saved + echo -e "$targetdir1" + read targetdir + echo -e "$targetdir2_1 $targetdir $targetdir2_2" + sleep 1 + + # alias entry in .bashrc or .zshrc + # .zshrc case 1 + echo $SHELL + if [ $SHELL = "/usr/bin/zsh" ]; then + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc + fi + # zshrc case 2 + echo $SHELL + if [ $SHELL = "/bin/zsh" ]; then + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc + fi + #bashrc case 1 + if [ $SHELL = "/usr/bin/bash" ]; then + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.bashrc + fi + # bashrc case 2 + if [ $SHELL = "/bin/bash" ]; then + echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.bashrc + fi + + # collects all the information needed to execute the rsync command and creates it. + echo -e "$collect \n" + sleep 1 + echo -e "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir \n" + sleep 1 + + # enter the rsync command in Backuppy.sh + echo "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir" >> Backuppy.sh + + # Outro + echo -e "$outro1" + sleep 2 + echo "$outro2" + exit 0 fi - -# 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" From 00fa691116315ba093e1e2a7143f8b13e519ca41 Mon Sep 17 00:00:00 2001 From: Paul S Date: Wed, 5 May 2021 17:54:06 +0200 Subject: [PATCH 17/35] 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 18/35] 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 19/35] 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 20/35] 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 21/35] 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 22/35] 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 23/35] "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 24/35] 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 25/35] 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 26/35] 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 27/35] 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 28/35] 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 29/35] 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 30/35] 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 31/35] 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 32/35] 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 33/35] 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 34/35] 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 35/35] 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:]))