Compare commits

...

3 commits

4 changed files with 380 additions and 12 deletions

32
CHANGELOG.md Normal file
View file

@ -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).

336
install.py Normal file
View file

@ -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"<p><strong>{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.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"<p><strong>{TARGETDIR}</strong></p>"
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"<p><strong>{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"))
urlLink = f"<a href='mailto: {EMAIL}'>{EMAIL}</a>"
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")

View file

@ -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."

View file

@ -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."