Compare commits
No commits in common. "main" and "v0.3" have entirely different histories.
9 changed files with 72 additions and 759 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +0,0 @@
|
||||||
__pycache__/
|
|
60
CHANGELOG.md
60
CHANGELOG.md
|
@ -1,60 +0,0 @@
|
||||||
# Changelog Backuppy
|
|
||||||
|
|
||||||
## [0.9] - 2021-05-10
|
|
||||||
### Added
|
|
||||||
- CLI-mode: It's now possible to end the program anytime with ":q" (or ESC-key) and pressing ENTER.
|
|
||||||
- CLI-mode: Colored output on traces
|
|
||||||
- New helper script "utils.py"
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Code refactoring (get rid of global variables and constants, instead pass as arguments)
|
|
||||||
|
|
||||||
## [0.8.1] - 2021-05-10
|
|
||||||
### Fixed
|
|
||||||
- CLI-mode: Show text "Programm interrupted by user." instead of error message when Ctrl+C was pressed.
|
|
||||||
- CLI-mode: Removed obsolete trace messages
|
|
||||||
|
|
||||||
## [0.8] - 2021-05-07
|
|
||||||
### Added
|
|
||||||
- GUI-mode: Introduce "Browse"-button on directory-selection dialogs
|
|
||||||
- GUI-mode: Installing necessary PySide2 package if not existing
|
|
||||||
|
|
||||||
## [0.7] - 2021-05-06
|
|
||||||
### Added
|
|
||||||
- Reworked "install.sh" to call "install.py"
|
|
||||||
- Launches graphical installer when called with "--gui" option: "install.sh --gui"
|
|
||||||
|
|
||||||
## [0.6] - 2021-05-06
|
|
||||||
### Added
|
|
||||||
- Write alias to Backuppy.sh into .bashrc/.zshrc only if not already existing
|
|
||||||
|
|
||||||
## [0.5.2] - 2021-05-05
|
|
||||||
### 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
|
|
||||||
|
|
||||||
## [0.5.1] - 2021-05-04
|
|
||||||
### Changed
|
|
||||||
- Use E-MAIL constant
|
|
||||||
|
|
||||||
## [0.5] - 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).
|
|
38
README.md
38
README.md
|
@ -3,45 +3,17 @@
|
||||||
No backup - no pity. So that this is not the case, you make backups. Backuppy tries to simplify this.
|
No backup - no pity. So that this is not the case, you make backups. Backuppy tries to simplify this.
|
||||||
|
|
||||||
Name: Backuppy
|
Name: Backuppy
|
||||||
|
|
||||||
Description: Make daily backups with Backuppy to avoid losing your data.
|
Description: Make daily backups with Backuppy to avoid losing your data.
|
||||||
|
Usage: start it with: 'backuppy'
|
||||||
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
|
Author: Joël Schurter
|
||||||
|
|
||||||
Licence: GPL3
|
Licence: GPL3
|
||||||
|
More infos: see constants and readme.md
|
||||||
More infos: See README.md and CHANGELOG.md
|
|
||||||
|
|
||||||
# ToDo
|
# ToDo
|
||||||
|
|
||||||
- add a log-file for the rsync errors
|
- translate Backuppy to English
|
||||||
|
- make a graphical installer & Tool
|
||||||
- check user-input for validity
|
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
- rsync (because Backuppy makes its backups with rsync)
|
|
||||||
|
|
||||||
execute `sudo pacman -S rsync` on Arch/Manjaro
|
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 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 dnf install pip` on Fedora
|
|
||||||
|
|
||||||
execute `sudo zypper install python3-pip` on openSUSE
|
|
||||||
|
|
155
install.py
155
install.py
|
@ -1,155 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
project: Backuppy
|
|
||||||
version: 0.9
|
|
||||||
file: install.py
|
|
||||||
summary: python installer-script in CLI-mode
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Standard library imports
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
# local imports
|
|
||||||
from utils import set_language, query, _print, trace
|
|
||||||
|
|
||||||
# local globals
|
|
||||||
VERSION: str = "0.9"
|
|
||||||
EMAIL = "fotocoder@joschu.ch"
|
|
||||||
EXCLUDE_FILE = "exclude.txt"
|
|
||||||
BACKUPPY_SCRIPT = "Backuppy.sh"
|
|
||||||
|
|
||||||
def main_install_cli(mydir, exclude_file):
|
|
||||||
language = query("welcome")
|
|
||||||
if not language:
|
|
||||||
return False, None, None
|
|
||||||
set_language(language)
|
|
||||||
|
|
||||||
_print("languagepack")
|
|
||||||
|
|
||||||
_print("intromsg1")
|
|
||||||
|
|
||||||
_print("intromsg2")
|
|
||||||
|
|
||||||
# which Rsync options are available and which one you want to use
|
|
||||||
_print("rsyncopt")
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# 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")
|
|
||||||
|
|
||||||
# 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(mydir, backuppy_script):
|
|
||||||
shell = os.environ.get("SHELL")
|
|
||||||
home_dir = os.environ.get("HOME")
|
|
||||||
# alias entry in .bashrc or .zshrc
|
|
||||||
backuppy_script = os.path.join(mydir, backuppy_script)
|
|
||||||
alias_str = f"alias backuppy='sudo {backuppy_script}'"
|
|
||||||
|
|
||||||
# Check for installed ZSH
|
|
||||||
if shell.upper().find("ZSH") > 0:
|
|
||||||
rc_filepath = os.path.join(home_dir, ".zshrc")
|
|
||||||
# Check for installed BASH
|
|
||||||
if shell.upper().find("BASH") > 0:
|
|
||||||
rc_filepath = os.path.join(home_dir, ".bashrc")
|
|
||||||
# Append our alias if not already existing
|
|
||||||
if os.path.isfile(rc_filepath):
|
|
||||||
fileRc = open(rc_filepath, "r") # open file in read mode
|
|
||||||
backuppy_entry_exists = False
|
|
||||||
for line in fileRc:
|
|
||||||
if "alias backuppy=" in line:
|
|
||||||
backuppy_entry_exists = True
|
|
||||||
break
|
|
||||||
if not backuppy_entry_exists:
|
|
||||||
trace(f"Writing {alias_str} to config file '{rc_filepath}'.")
|
|
||||||
fileRc = open(rc_filepath, "a") # open file in append mode
|
|
||||||
fileRc.write("\n# Following line was created by Backuppy\n" + alias_str + "\n")
|
|
||||||
fileRc.close()
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
if rsync_cmd:
|
|
||||||
create_backuppy_script(mydir, backuppy_script, rsync_cmd)
|
|
||||||
create_alias(mydir, backuppy_script)
|
|
||||||
|
|
||||||
def main(argv):
|
|
||||||
trace(f"Starting Backuppy install.py v{VERSION}")
|
|
||||||
is_finalized = False
|
|
||||||
mydir = os.getcwd()
|
|
||||||
|
|
||||||
if argv and argv[0] == "--gui":
|
|
||||||
from install_gui import main_install_gui
|
|
||||||
trace("Starting GUI-version.")
|
|
||||||
is_finalized, is_exclude, rsync_cmd = main_install_gui(mydir, EXCLUDE_FILE) # collect user input via GUI and store in env. variables
|
|
||||||
|
|
||||||
else:
|
|
||||||
trace("Starting CLI-version.\n")
|
|
||||||
is_finalized, is_exclude, rsync_cmd = main_install_cli(mydir, EXCLUDE_FILE) # collect user input via CLI and store in env. variables
|
|
||||||
|
|
||||||
if is_finalized:
|
|
||||||
do_the_install(mydir, EXCLUDE_FILE, is_exclude, BACKUPPY_SCRIPT, rsync_cmd)
|
|
||||||
|
|
||||||
trace("Ending Backuppy install.py")
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
# sys.argv.append("--gui") # TODO: disable for production
|
|
||||||
sys.exit(main(sys.argv[1:]))
|
|
89
install.sh
89
install.sh
|
@ -1,29 +1,70 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# """
|
|
||||||
# project: Backuppy
|
|
||||||
# version: 0.8
|
|
||||||
# file: install.sh
|
|
||||||
# summary: main entry shell script
|
|
||||||
# """
|
|
||||||
|
|
||||||
# Check if graphical installer should be executed
|
# Variables
|
||||||
if [ "$1" == "--gui" ]; then
|
mydir=$PWD
|
||||||
# Check if PIP ist installed
|
|
||||||
if ! command -v pip3> /dev/null
|
# Intro
|
||||||
then
|
echo -e "\n Danke, dass du Backuppy nutzt, um deine Backups zu erstellen! \n"
|
||||||
echo "Please install PIP on your system for the graphical version of Backuppy!"
|
sleep 2
|
||||||
exit
|
echo -e "Der Installer wird dich nun einige Dinge abfragen, um dein Backup-Skript an deine Anforderungen anzupassen. \n"
|
||||||
|
sleep 2
|
||||||
|
# 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 "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
|
||||||
|
|
||||||
|
# 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]"
|
||||||
|
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"
|
||||||
|
touch exclude.txt
|
||||||
|
sleep 2
|
||||||
fi
|
fi
|
||||||
# Check if PIP module "PySide2" is installed
|
if [ $exclude = "N" ]; then
|
||||||
if ! pip list | grep PySide2> /dev/null
|
echo -e "Gut, dann erstelle ich die Datei 'exclude.txt' gar nicht erst, da du sie ja nicht brauchst. \n"
|
||||||
then
|
rm exclude.txt
|
||||||
# Install PySide2
|
sleep 2
|
||||||
echo -e "Installing necessary PySide2 package for GUI-mode."
|
|
||||||
pip3 install PySide2
|
|
||||||
fi
|
fi
|
||||||
# Launch python installer in GUI mode
|
|
||||||
python3 -B install.py "$1"
|
# Asks for the source directory which should be saved
|
||||||
else
|
echo -e "Nun kommen wir zu einem der wichtigesten Teile der Installation von Backuppy:"
|
||||||
# Launch python installer in CLI mode
|
sleep 1
|
||||||
python3 -B install.py
|
echo -e "Welches Verzeichnis möchtest du sichern (z.B. das Homeverzeichnis)? Bitte gib einen absoluten Pfad (z.B. '/home/username/') an."
|
||||||
|
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
|
||||||
|
|
||||||
|
# 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."
|
||||||
|
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
|
||||||
|
|
||||||
|
# alias entry in .bashrc or .zshrc
|
||||||
|
#zshrc
|
||||||
|
echo "$SHELL"
|
||||||
|
if [ $SHELL = "/usr/bin/zsh" ]; then
|
||||||
|
echo "alias backuppy='sudo $mydir/Backuppy.sh'" >> ~/.zshrc
|
||||||
fi
|
fi
|
||||||
|
#bashrc
|
||||||
|
if [ $SHELL = "/usr/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 "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 "rsync -aqp --exclude-from=$mydir/exclude.txt $sourcedir $targetdir \n"
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# 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."
|
||||||
|
|
318
install_gui.py
318
install_gui.py
|
@ -1,318 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
project: Backuppy
|
|
||||||
version: 0.9
|
|
||||||
file: install_gui.py
|
|
||||||
summary: python installer-script in GUI-mode (needs PySide2)
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Standard library imports
|
|
||||||
import os
|
|
||||||
|
|
||||||
# Third party imports
|
|
||||||
from PySide2 import QtWidgets
|
|
||||||
|
|
||||||
# local imports
|
|
||||||
from install import EXCLUDE_FILE, EMAIL
|
|
||||||
from utils import set_language, get_lang_text, LANG_EN, LANG_DE
|
|
||||||
|
|
||||||
class BackuppyWizard(QtWidgets.QWizard):
|
|
||||||
def __init__(self, parent=None, mydir=os.getcwd(), exclude_file=EXCLUDE_FILE):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
# init instance variables
|
|
||||||
self.mydir = mydir
|
|
||||||
self.exclude_file = exclude_file
|
|
||||||
self.exclude = None
|
|
||||||
self.sourcedir = None
|
|
||||||
self.targetdir = None
|
|
||||||
self.rsync_cmd = None
|
|
||||||
|
|
||||||
self.setWindowTitle(get_lang_text("intromsg1"))
|
|
||||||
|
|
||||||
self.addPage(Page01(self))
|
|
||||||
self.addPage(Page02(self))
|
|
||||||
self.addPage(Page03(self))
|
|
||||||
self.addPage(Page04(self))
|
|
||||||
self.addPage(Page05(self))
|
|
||||||
self.addPage(Page06(self))
|
|
||||||
self.addPage(Page07(self))
|
|
||||||
self.addPage(Page08(self))
|
|
||||||
self.addPage(Page09(self))
|
|
||||||
self.addPage(Page10(self))
|
|
||||||
|
|
||||||
self.resize(640, 480)
|
|
||||||
|
|
||||||
|
|
||||||
class Page01(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.label = QtWidgets.QLabel(get_lang_text("welcome"))
|
|
||||||
|
|
||||||
self.comboBox = QtWidgets.QComboBox(self)
|
|
||||||
self.comboBox.addItem(LANG_EN)
|
|
||||||
self.comboBox.addItem(LANG_DE)
|
|
||||||
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label)
|
|
||||||
layout.addWidget(self.comboBox)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def validatePage(self):
|
|
||||||
set_language(self.comboBox.currentText())
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Page02(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.label1 = QtWidgets.QLabel()
|
|
||||||
self.label2 = QtWidgets.QLabel()
|
|
||||||
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label1)
|
|
||||||
layout.addWidget(self.label2)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
self.setWindowTitle(get_lang_text("intromsg1"))
|
|
||||||
|
|
||||||
self.label1.setText(get_lang_text("languagepack"))
|
|
||||||
self.label2.setText(get_lang_text("intromsg2"))
|
|
||||||
|
|
||||||
|
|
||||||
class Page03(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
self.label = QtWidgets.QLabel()
|
|
||||||
self.groupbox = QtWidgets.QGroupBox()
|
|
||||||
self.groupbox.setFlat(True)
|
|
||||||
self.radio1 = QtWidgets.QRadioButton()
|
|
||||||
self.radio2 = QtWidgets.QRadioButton()
|
|
||||||
|
|
||||||
vbox = QtWidgets.QVBoxLayout()
|
|
||||||
vbox.addWidget(self.radio1)
|
|
||||||
vbox.addWidget(self.radio2)
|
|
||||||
vbox.addStretch(1)
|
|
||||||
self.groupbox.setLayout(vbox)
|
|
||||||
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label)
|
|
||||||
layout.addWidget(self.groupbox)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
self.label.setText(get_lang_text("excludefile1"))
|
|
||||||
self.radio1.setText(get_lang_text("Yes"))
|
|
||||||
self.radio2.setText(get_lang_text("No"))
|
|
||||||
self.radio1.setChecked(True)
|
|
||||||
|
|
||||||
def validatePage(self):
|
|
||||||
if self.radio1.isChecked():
|
|
||||||
self.parent.exclude = True
|
|
||||||
else:
|
|
||||||
self.parent.exclude = False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Page04(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
self.label = QtWidgets.QLabel()
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
if self.parent.exclude:
|
|
||||||
self.label.setText(get_lang_text("excludefile2"))
|
|
||||||
else:
|
|
||||||
self.label.setText(get_lang_text("excludefile3"))
|
|
||||||
|
|
||||||
class Page05(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
self.label1 = QtWidgets.QLabel()
|
|
||||||
self.label2 = QtWidgets.QLabel()
|
|
||||||
self.efSourceDir = QtWidgets.QLineEdit()
|
|
||||||
self.pbBrowse = QtWidgets.QPushButton("Browse")
|
|
||||||
self.pbBrowse.clicked.connect(self.on_pbBrowse_clicked)
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label1)
|
|
||||||
layout.addWidget(self.label2)
|
|
||||||
|
|
||||||
hLayout = QtWidgets.QHBoxLayout()
|
|
||||||
hLayout.addWidget(self.efSourceDir)
|
|
||||||
hLayout.addWidget(self.pbBrowse)
|
|
||||||
|
|
||||||
layout.addLayout(hLayout)
|
|
||||||
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
self.label1.setText(get_lang_text("srcdir1"))
|
|
||||||
self.label2.setText(get_lang_text("srcdir2"))
|
|
||||||
|
|
||||||
def on_pbBrowse_clicked(self):
|
|
||||||
options = QtWidgets.QFileDialog.Options() | QtWidgets.QFileDialog.ShowDirsOnly
|
|
||||||
dirName = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", None, options)
|
|
||||||
self.efSourceDir.setText(dirName)
|
|
||||||
|
|
||||||
def validatePage(self):
|
|
||||||
self.parent.sourcedir = self.efSourceDir.text()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Page06(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
self.label1 = QtWidgets.QLabel()
|
|
||||||
self.label2 = QtWidgets.QLabel()
|
|
||||||
self.label3 = QtWidgets.QLabel()
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label1)
|
|
||||||
layout.addWidget(self.label2)
|
|
||||||
layout.addWidget(self.label3)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
self.label1.setText(get_lang_text("srcdir3_1"))
|
|
||||||
bold_text = f"<p><strong>{self.parent.sourcedir}</strong></p>"
|
|
||||||
self.label2.setText(bold_text)
|
|
||||||
self.label3.setText(get_lang_text("srcdir3_2"))
|
|
||||||
|
|
||||||
|
|
||||||
class Page07(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
self.label1 = QtWidgets.QLabel()
|
|
||||||
self.efTargetDir = QtWidgets.QLineEdit()
|
|
||||||
self.pbBrowse = QtWidgets.QPushButton("Browse")
|
|
||||||
self.pbBrowse.clicked.connect(self.on_pbBrowse_clicked)
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label1)
|
|
||||||
|
|
||||||
hLayout = QtWidgets.QHBoxLayout()
|
|
||||||
hLayout.addWidget(self.efTargetDir)
|
|
||||||
hLayout.addWidget(self.pbBrowse)
|
|
||||||
|
|
||||||
layout.addLayout(hLayout)
|
|
||||||
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
self.label1.setText(get_lang_text("targetdir1"))
|
|
||||||
|
|
||||||
def on_pbBrowse_clicked(self):
|
|
||||||
options = QtWidgets.QFileDialog.Options() | QtWidgets.QFileDialog.ShowDirsOnly
|
|
||||||
dirName = QtWidgets.QFileDialog.getExistingDirectory(self, "Select Directory", None, options)
|
|
||||||
self.efTargetDir.setText(dirName)
|
|
||||||
|
|
||||||
def validatePage(self):
|
|
||||||
self.parent.targetdir = self.efTargetDir.text()
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Page08(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
self.label1 = QtWidgets.QLabel()
|
|
||||||
self.label2 = QtWidgets.QLabel()
|
|
||||||
self.label3 = QtWidgets.QLabel()
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label1)
|
|
||||||
layout.addWidget(self.label2)
|
|
||||||
layout.addWidget(self.label3)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
self.label1.setText(get_lang_text("targetdir2_1"))
|
|
||||||
bold_text = f"<p><strong>{self.parent.targetdir}</strong></p>"
|
|
||||||
self.label2.setText(bold_text)
|
|
||||||
self.label3.setText(get_lang_text("targetdir2_2"))
|
|
||||||
|
|
||||||
def validatePage(self):
|
|
||||||
exclude_file = os.path.join(self.parent.mydir, self.parent.exclude_file)
|
|
||||||
|
|
||||||
self.parent.rsync_cmd = f"rsync -aqp --exclude-from={exclude_file} {self.parent.sourcedir} {self.parent.targetdir}"
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Page09(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
|
|
||||||
self.parent = parent
|
|
||||||
|
|
||||||
self.label1 = QtWidgets.QLabel()
|
|
||||||
self.label2 = QtWidgets.QLabel()
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label1)
|
|
||||||
layout.addWidget(self.label2)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
self.label1.setText(get_lang_text("collect"))
|
|
||||||
bold_text = f"<p><strong>{self.parent.rsync_cmd}</strong></p>"
|
|
||||||
self.label2.setText(bold_text)
|
|
||||||
|
|
||||||
|
|
||||||
class Page10(QtWidgets.QWizardPage):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent)
|
|
||||||
self.label1 = QtWidgets.QLabel()
|
|
||||||
self.label2 = QtWidgets.QLabel()
|
|
||||||
self.label3 = QtWidgets.QLabel()
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
|
||||||
layout.addWidget(self.label1)
|
|
||||||
layout.addWidget(self.label2)
|
|
||||||
layout.addWidget(self.label3)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
self.setFinalPage(True)
|
|
||||||
|
|
||||||
def initializePage(self):
|
|
||||||
self.label1.setText(get_lang_text("outro1"))
|
|
||||||
self.label2.setText(get_lang_text("outro2"))
|
|
||||||
global EMAIL
|
|
||||||
urlLink = f"<a href='mailto: {EMAIL}'>{EMAIL}</a>"
|
|
||||||
self.label3.setText(urlLink)
|
|
||||||
self.label3.setOpenExternalLinks(True)
|
|
||||||
|
|
||||||
def validatePage(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def main_install_gui(mydir, exclude_file):
|
|
||||||
app = QtWidgets.QApplication()
|
|
||||||
wizard = BackuppyWizard(parent=None, mydir=mydir, exclude_file=exclude_file)
|
|
||||||
wizard.show()
|
|
||||||
|
|
||||||
app.exec_()
|
|
||||||
|
|
||||||
return True, wizard.exclude, wizard.rsync_cmd
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
is_finalized, is_exclude, rsync_cmd = main_install_gui(mydir=os.getcwd(), exclude_file=EXCLUDE_FILE)
|
|
|
@ -1,44 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
# """
|
|
||||||
# project: Backuppy
|
|
||||||
# file: languages/english.py
|
|
||||||
# summary: english language file
|
|
||||||
# """
|
|
||||||
|
|
||||||
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!"
|
|
||||||
|
|
||||||
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.\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."
|
|
||||||
|
|
||||||
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.\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."
|
|
|
@ -1,42 +0,0 @@
|
||||||
#!/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.\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."
|
|
||||||
|
|
||||||
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.\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\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.\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."
|
|
80
utils.py
80
utils.py
|
@ -1,80 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
project: Backuppy
|
|
||||||
version: 0.9
|
|
||||||
file: utils.py
|
|
||||||
summary: utilities script
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Standard library imports
|
|
||||||
import time
|
|
||||||
|
|
||||||
# local imports
|
|
||||||
from languages import english
|
|
||||||
from languages import german
|
|
||||||
|
|
||||||
# local globals
|
|
||||||
LANG_EN = "EN"
|
|
||||||
LANG_DE = "DE"
|
|
||||||
LANGUAGE = LANG_EN
|
|
||||||
class bcolors:
|
|
||||||
""" Define colors for the std. output to console. """
|
|
||||||
|
|
||||||
OK = '\033[92m' #GREEN
|
|
||||||
WARNING = '\033[93m' #YELLOW
|
|
||||||
ERROR = '\033[91m' #RED
|
|
||||||
RESET = '\033[0m' #RESET COLOR
|
|
||||||
|
|
||||||
|
|
||||||
def trace(message_txt, prefix_crlf=False, err=False):
|
|
||||||
""" Print a formatted message to std out.
|
|
||||||
|
|
||||||
:param "message_txt" [in] The message text that should be displayed.
|
|
||||||
:param "prefix_crlf" [in] If True, a carriage return/line feed will be done prior to the message text.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
if prefix_crlf:
|
|
||||||
print("\n")
|
|
||||||
if err:
|
|
||||||
print("[ " + bcolors.ERROR + "ERR" + bcolors.RESET + " ] " + message_txt)
|
|
||||||
else:
|
|
||||||
print("[ " + bcolors.OK + "OK" + bcolors.RESET + " ] " + message_txt)
|
|
||||||
|
|
||||||
def get_lang_text(search_str: str):
|
|
||||||
""" Returns a string from the appropriate language file. """
|
|
||||||
return_str: str = eval("english." + search_str)
|
|
||||||
global LANGUAGE
|
|
||||||
if LANGUAGE == LANG_DE:
|
|
||||||
return_str = eval("german." + search_str)
|
|
||||||
return return_str
|
|
||||||
|
|
||||||
def _print(message_txt: str):
|
|
||||||
print("\n" + get_lang_text(message_txt) + "\n")
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
def set_language(language):
|
|
||||||
""" Set global constant "LANGUAGE" to given language ("EN"/"DE"). """
|
|
||||||
global LANGUAGE, LANG_DE, LANG_EN
|
|
||||||
|
|
||||||
if str(language) and language.upper() == LANG_DE:
|
|
||||||
LANGUAGE = LANG_DE
|
|
||||||
else:
|
|
||||||
LANGUAGE = LANG_EN
|
|
||||||
|
|
||||||
def query(question_txt: str):
|
|
||||||
try:
|
|
||||||
answer_txt = input(get_lang_text(question_txt) + "\n> ")
|
|
||||||
|
|
||||||
if not answer_txt:
|
|
||||||
answer_txt = "''"
|
|
||||||
|
|
||||||
if answer_txt.upper() == ":Q" or answer_txt == chr(27):
|
|
||||||
trace("Program exited by user-input.")
|
|
||||||
return None
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
trace("Program interrupted by user.", prefix_crlf=True, err=True)
|
|
||||||
return None
|
|
||||||
|
|
||||||
return answer_txt
|
|
Loading…
Reference in a new issue