Skip to content

Commit 59ee6b9

Browse files
author
Rob Fowler
committed
initial commit
0 parents  commit 59ee6b9

File tree

12 files changed

+901
-0
lines changed

12 files changed

+901
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*.pyc
2+
*.bat
3+
env/
4+
__pycache__/
5+
private/
6+

CHANGELOG.MD

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# PyBackup Change Log
2+
3+
Rob Fowler <robtf04@outlook.com>
4+
5+
## 0.0.1 - YYYY-MM-DD
6+
7+

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.MD

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Py Backup - A simple personal backup utility
2+
*v0.0.1*
3+
4+
Updated: 2023-04-17
5+
6+
## Introduction
7+
8+
**PyPackup** is a simple, no-frills, easy to use personal file backup app.
9+
10+
## Features
11+
- Backup to a timestamped *.zip file.
12+
- Save backup sources and destinations for easy re-use.
13+
14+
## License
15+
GNU General Public License 3. See the LICENSE file for details.
16+
17+
## Installation
18+
- Written in Python 3.11 on Windows 11. Not tested in any other Python versions or operating systems. I'm confident it can be made to work on Mac and Linux.
19+
- Has no requirements outside the Python standard library.
20+
- Download the repo as a zip file and extract.
21+
- ``python backup.py`` to launch.
22+
23+
## Confguration
24+
- Saved backups are contained in the file ``backups.json``.
25+
26+
## Screenshot
27+
![](screenshot.jpg)
28+
29+
## Documentation
30+
- Click the **Select Source** button to choose a directory to back up.
31+
- Click the **Select Destination** button to choose a directory to back up to. A time-stamped ZIP file will be created in this folder.
32+
- Click **Run Now** to execute the backup. A pop up will notify when the backup is completed.
33+
- Click **Save Backup** to save the backup configuration for later use. The backup will appear by name in the **Select Saved Backup** drop down list. You will need to re-start PyBackup for new backups to appear on this list.
34+
- To load a saved backup, click on the **Select Saved Backup** drop down list and select the name of the desired backup. The source and destination boxes will populate from the saved backup. You can then click **Run Now** to execute the saved backup.
35+
36+
## Credit

app.log

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
root - WARNING - This is a warning message
2+
root - ERROR - This is an error message
3+
root - CRITICAL - This is a critical message

app.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from tkinter import *
2+
from tkinter import ttk
3+
from backup import *
4+
from gui import *
5+
6+
root = Tk()
7+
PyBackup(root)
8+
root.mainloop()
9+

backup.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from pathlib import Path
2+
import configparser
3+
import json
4+
import zipfile
5+
import datetime as dt
6+
7+
def zip_backup(source,dest):
8+
source = Path(source)
9+
timestamp = str(dt.datetime.now()).replace(" ","-").replace(":","")[:17]
10+
dest = Path(dest,timestamp)
11+
12+
13+
print(f"Backing up {source} to {dest}....")
14+
with zipfile.ZipFile(dest, mode="w") as archive:
15+
for file_path in source.rglob("*"):
16+
archive.write(file_path,
17+
arcname=file_path.relative_to(source))
18+
print("Backup completed.")
19+
20+
def save_backup(source,dest,name):
21+
with open("backups.json","r",encoding="utf-8") as f:
22+
saved_backups = json.load(f)
23+
saved_backups[name] = {}
24+
saved_backups[name]["source"]=source
25+
saved_backups[name]["dest"]=dest
26+
27+
with open("backups.json","w",encoding="utf-8") as f:
28+
json.dump(saved_backups,f)
29+
30+
def load_backup(name):
31+
with open("backups.json","r",encoding="utf-8") as f:
32+
saved_backups = json.load(f)
33+
return saved_backups[name]
34+
35+
def delete_backup(name):
36+
with open("backups.json","r",encoding="utf-8") as f:
37+
saved_backups = json.load(f)
38+
n = saved_backups.pop(name)
39+
with open("backups.json","w",encoding="utf-8") as f:
40+
json.dump(saved_backups,f)
41+

backups.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"backend": {"source": "//server/Access/Backend_v2", "dest": "F:/backend"}, "documents": {"source": "C:/Users/Robf.DESKTOP-JNCQ9MB/Documents", "dest": "F/documents"}, "devnotes3": {"source": "C:/Users/Robf.DESKTOP-JNCQ9MB/Documents/DevNotes3", "dest": "F:/devnotes"}}

gui.py

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
from tkinter import *
2+
from tkinter import ttk
3+
from tkinter import filedialog
4+
from tkinter import messagebox
5+
from pathlib import Path
6+
from backup import *
7+
8+
import json
9+
10+
11+
class PyBackup:
12+
def __init__(self,root):
13+
root.title("PyBackup")
14+
mnuMain = MainMenu(root)
15+
root.config(menu=mnuMain)
16+
Main = MainFrame(root)
17+
18+
class MainMenu(Menu):
19+
def __init__(self,root):
20+
Menu.__init__ (self,root)
21+
22+
mnuFile = Menu(self,tearoff=0)
23+
self.add_cascade(label="File",menu=mnuFile)
24+
mnuFile.add_command(label="Quit",command=root.quit)
25+
26+
mnuEdit = Menu(self,tearoff=0)
27+
self.add_cascade(label="Edit",menu=mnuEdit)
28+
mnuEdit.add_command(label="Preferences")
29+
30+
mnuHelp = Menu(self,tearoff=0)
31+
self.add_cascade(label="Help",menu=mnuHelp)
32+
mnuHelp.add_command(label="About")
33+
34+
class MainFrame(Frame):
35+
def __init__(self,parent):
36+
Frame.__init__ (self,parent)
37+
38+
global varSaveBackup
39+
40+
# Load list of saved backups
41+
with open("backups.json","r",encoding="utf-8") as f:
42+
saved_backups = json.load(f)
43+
44+
backups_list = list(saved_backups.keys())
45+
46+
def SelectSource():
47+
varSource.set(filedialog.askdirectory(title = "Select Source Folder"))
48+
49+
def SelectDestination():
50+
varDest.set(filedialog.askdirectory(title = "Select Destination Folder"))
51+
52+
def RunNow():
53+
zip_backup(varSource.get(), varDest.get())
54+
messagebox.showinfo("PyBackup","Backup complete.")
55+
56+
def SaveBackupPopup():
57+
Popup = Toplevel(parent)
58+
59+
def SaveBackupName():
60+
varSaveBackup = txtValue.get()
61+
save_backup(varSource.get(),varDest.get(),varSaveBackup)
62+
Popup.destroy()
63+
64+
def Cancel():
65+
Popup.destroy()
66+
67+
lblEntry = Label(Popup,text="Enter a name for this backup:",font=("Helvetica",10))
68+
txtValue = Entry(Popup, font=("Helvetica",10))
69+
btnOK = Button(Popup,text="OK",width=10,command=SaveBackupName)
70+
btnCancel= Button(Popup,text="Cancel",width=10,command=Cancel)
71+
72+
lblEntry.grid(row=0,column=0,columnspan=2,padx=10,pady=5)
73+
txtValue.grid(row=1,column=0,columnspan=2,padx=5,pady=5)
74+
btnOK.grid(row=2,column=0,sticky=(W,E),padx=5,pady=5)
75+
btnCancel.grid(row=2,column=1,sticky=(W,E),padx=5,pady=5)
76+
77+
78+
79+
def LoadBackup(e):
80+
backup = load_backup(varSavedBackup.get())
81+
varSource.set(backup['source'])
82+
varDest.set(backup['dest'])
83+
84+
85+
# create widgets
86+
varSource=StringVar()
87+
btnSource = Button(self,text="Select Source",command=SelectSource)
88+
txtSource = Entry(self, textvariable=varSource,font=("Helvetica",10),width=30)
89+
90+
varDest=StringVar()
91+
btnDest = Button(self,text="Select Destination",command=SelectDestination)
92+
txtDest = Entry(self, textvariable=varDest,font=("Helvetica",10),width=50)
93+
94+
btnRunNow = Button(self,text="Run Now",command=RunNow)
95+
btnSaveBackup = Button(self,text="Save Backup",command=SaveBackupPopup)
96+
97+
lblSelectSaved = Label(self,text="Select Saved Backup",font=("Helvetica",10))
98+
99+
varSavedBackup = StringVar()
100+
cboSelectSaved = ttk.Combobox(self,textvariable=varSavedBackup,font=("Helvetica",10))
101+
cboSelectSaved['values'] = backups_list
102+
cboSelectSaved.bind("<<ComboboxSelected>>",LoadBackup)
103+
104+
# grid the widgets
105+
btnSource.grid(row=0,column=0,sticky=(W),padx=5,pady=5)
106+
txtSource.grid(row=1,column=0,sticky=(W,E),padx=5,pady=5,columnspan=2)
107+
btnDest.grid(row=2,column=0,sticky=(W),padx=5,pady=5)
108+
txtDest.grid(row=3,column=0,sticky=(W,E),padx=5,pady=5,columnspan=2)
109+
btnRunNow.grid(row=4,column=0,padx=5,pady=5)
110+
btnSaveBackup.grid(row=4,column=1,padx=5,pady=5)
111+
lblSelectSaved.grid(row=5,column=0,padx=5,pady=5,columnspan=2)
112+
cboSelectSaved.grid(row=6,column=0,padx=5,pady=5,columnspan=2)
113+
114+
self.grid(row=0,column=0,sticky=(W,E))

logtest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import logging
2+
3+
4+
logging.basicConfig(filename='app.log', filemode='w', format='%(name)s - %(levelname)s - %(message)s')
5+
6+
logging.debug('This is a debug message')
7+
logging.info('This is an info message')
8+
logging.warning('This is a warning message')
9+
logging.error('This is an error message')
10+
logging.critical('This is a critical message')

0 commit comments

Comments
 (0)