Applied settings; app now functions in a basic way

This commit is contained in:
Thomas Wilczynski 2025-08-20 16:47:20 -07:00
commit ca2c8f2ade
5 changed files with 134 additions and 25 deletions

3
.gitignore vendored
View file

@ -1,4 +1,5 @@
.vscode/
__pycache__/
venv/
plan.svg
plan.svg
data/file_rules_custom.json

66
app.py
View file

@ -19,12 +19,15 @@ def set_debug_mode(value):
debug_mode = value
class App(tk.Tk):
def __init__(self, width=600, height=400):
def __init__(self, width=640, height=480):
super().__init__()
self.title("Download Utils")
self.current_mode = "all"
self.option_add('*tearOff', tk.FALSE)
self.fm = fm.Manager(True)
self.log = list()
self.gui()
self.prep()
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
@ -46,6 +49,28 @@ class App(tk.Tk):
str(ut.format_bytes(v["size"])))
self.fileview.insert("", tk.END, k, values=values)
def load_config(self, filepath="data/file_rules_custom.json"):
data = ut.load_json_file(filepath)
self.fm.setup_file_rules(data, True)
def save_config(self, filepath="data/file_rules_custom.json"):
ut.save_json_file(filepath, self.fm.filemodes)
def update_mode_data(self, mode, key, value):
print(f"{key} : {value}")
if not mode in self.fm.filemodes: return
self.fm.filemodes[mode].update({key: value})
def set_rules_variables(self, key):
if not key in self.fm.filemodes: return
data = self.fm.filemodes[key]
self.v_radio.set(data["action"])
self.v_targetdir.set(data["destination"])
if type(data["extensions"]) == list: # Needs improvement
self.v_extstr.set(", ".join(data["extensions"]))
else:
self.v_extstr.set(data["extensions"])
def select_mode_from_list(self, event):
key = ""
value = self.w_list.get(self.w_list.curselection())
@ -54,13 +79,21 @@ class App(tk.Tk):
key = k
break
if len(key) == 0: return # Do error
if len(key) > 0:
self.current_mode = key
self.set_rules_variables(key)
data = self.fm.filemodes[key]
print(data)
self.v_radio.set(data["action"])
self.v_targetdir.set(data["destination"])
self.v_extstr.set(", ".join(data["extensions"]))
def prep(self):
self.update_fileview()
if self.current_mode in self.fm.filemodes:
self.set_rules_variables(self.current_mode)
def run_task(self):
print("Task started")
self.fm.run_task()
def run_backup(self):
print("Backup started")
def gui(self):
self.gui_menu()
@ -76,7 +109,6 @@ class App(tk.Tk):
self.config(menu=a_menu)
a_menu_file = tk.Menu(a_menu)
a_menu_file.add_command(label="Open")
a_menu_file.add_command(label="Exit", command=self.destroy)
a_menu.add_cascade(
@ -90,13 +122,13 @@ class App(tk.Tk):
box = ttk.Frame(frame)
button = ttk.Button(box, text="Start Task")
button = ttk.Button(box, text="Start Task", command=self.run_task)
button.grid(column=0, row=0, pady=5)
button = ttk.Button(box, text="Refresh", command=self.update_fileview)
button.grid(column=1, row=0, pady=5)
button = ttk.Button(box, text="Backup")
button = ttk.Button(box, text="Backup", command=self.run_backup)
button.grid(column=2, row=0, pady=5)
box.pack(side=tk.TOP, fill=tk.X)
@ -113,19 +145,18 @@ class App(tk.Tk):
command=self.fileview.yview)
self.fileview.configure(yscrollcommand=sbar.set)
self.update_fileview()
self.fileview.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
sbar.pack(side=tk.RIGHT, fill=tk.Y)
return frame
def gui_rules(self):
um = self.update_mode_data
frame = ttk.Frame(self.book)
box = ttk.Frame(frame)
button = ttk.Button(box, text="Save Config")
button = ttk.Button(box, text="Save Config", command=self.save_config)
button.grid(column=0, row=0, pady=5)
button = ttk.Button(box, text="Reset")
@ -142,7 +173,7 @@ class App(tk.Tk):
self.w_list = tk.Listbox(main, listvariable=self.v_modelist,
selectmode=tk.SINGLE)
self.w_list.bind('<<ListboxSelect>>', self.select_mode_from_list)
self.w_list.grid(row=0, column=0, sticky=tk.NS)
self.w_list.grid(row=0, column=0, padx=5, sticky=tk.NSEW)
details = ttk.Frame(main)
@ -150,10 +181,11 @@ class App(tk.Tk):
self.v_radio = tk.StringVar()
self.v_actions = ("move", "copy", "delete", "ignore")
f_radio = lambda: um(self.current_mode, "action", self.v_radio.get())
for s in self.v_actions:
str = f"{s.capitalize()} "
radio = ttk.Radiobutton(i_frame, text=str, value=s,
variable=self.v_radio)
variable=self.v_radio, command = f_radio)
radio.pack(side=tk.LEFT)
i_frame.pack(side=tk.TOP, anchor=tk.W)
@ -165,7 +197,9 @@ class App(tk.Tk):
label.pack(side=tk.TOP, anchor=tk.W)
self.v_targetdir = tk.StringVar()
f_targetdir = lambda e: um(self.current_mode, "destination", self.v_targetdir.get())
entry = ttk.Entry(i_frame, textvariable=self.v_targetdir)
entry.bind('<Return>', f_targetdir)
entry.pack(side=tk.TOP, fill=tk.X)
i_frame.pack(side=tk.TOP, fill=tk.X)
@ -177,7 +211,9 @@ class App(tk.Tk):
label.pack(side=tk.TOP, anchor=tk.W)
self.v_extstr = tk.StringVar()
f_extstr = lambda e: um(self.current_mode, "extensions", self.v_extstr.get())
entry = ttk.Entry(i_frame, textvariable=self.v_extstr)
entry.bind('<Return>', f_extstr)
entry.pack(side=tk.TOP, fill=tk.X)
i_frame.pack(side=tk.TOP, fill=tk.X)

View file

@ -3,7 +3,10 @@
__author__ = "Gull"
import os
import json
import shutil
import utils as ut
debug_mode = True
class Manager:
def __init__(self, auto_config=False):
@ -11,10 +14,11 @@ class Manager:
self.filedata = dict()
self.filemodes = dict() # Contains file mode data
self.filetypes = dict() # Maps file extensions to modes
self.config_path = "data/file_rules_default.json"
if auto_config:
self.set_directory(self.get_directory())
self.setup_file_rules()
self.setup_file_rules(ut.load_json_file(self.config_path))
def get_directory(self, target="Downloads"):
dir = os.path.join(os.path.expanduser("~"), target)
@ -25,12 +29,20 @@ class Manager:
if os.path.exists(dir):
self.dir = dir
def setup_file_rules(self, file="data/settings_default.json", data=None):
with open(file) as f:
data = json.load(f)
self.filemodes = data["filemodes"]
self.filetypes.clear()
for k, v in self.filemodes.items():
def setup_file_rules(self, data, reset=False):
if reset: self.filemodes.clear()
if not "filemodes" in data: return
filemodes = data["filemodes"]
for k, v in filemodes.items():
self.filemodes[k] = v
self.match_file_types()
def match_file_types(self):
self.filetypes.clear()
for k, v in self.filemodes.items():
if "extensions" in v:
for ext in v["extensions"]:
self.filetypes[ext] = k
@ -46,3 +58,51 @@ class Manager:
st = entry.stat()
data = {"time": st.st_mtime, "size": st.st_size}
self.filedata[entry.name] = data
def run_task(self):
self.match_file_types() # Updates file types dict
for k, v in self.filedata.items():
fullpath = os.path.join(self.dir, k)
if os.path.exists(fullpath):
_, ext = os.path.splitext(fullpath)
ext = ext[1:].lower()
mode = self.filetypes[ext] if ext in self.filetypes else "other"
v["mode"] = mode
v["fullpath"] = fullpath
for k, v in self.filedata.items():
rule = self.filemodes[v["mode"]]
if rule["action"] == "move":
move(v["fullpath"], rule["destination"])
elif rule["action"] == "copy":
copy(v["fullpath"], rule["destination"])
elif rule["action"] == "delete":
delete(v["fullpath"])
def parse_dir(dir):
"""Checks if a directory exists, and if it is for the user."""
if dir[0] == "~":
dir = dir.lstrip("~/")
dir = os.path.join(os.path.expanduser("~"), dir)
return dir
def move(src, dst):
src, dst = parse_dir(src), parse_dir(dst)
if debug_mode:
print(f"Move: {src} > {dst}")
else:
shutil.move(src, dst)
def copy(src, dst):
src, dst = parse_dir(src), parse_dir(dst)
if debug_mode:
print(f"Copy: {src} > {dst}")
else:
shutil.copy2(src, dst)
def delete(src):
src = parse_dir(src)
if debug_mode:
print(f"Delete: {src}")
else:
os.remove(src)

View file

@ -3,6 +3,7 @@
__author__ = "Gull"
import time
import json
KILOBYTES = 1024
@ -29,4 +30,15 @@ def format_bytes(bytes):
def format_date(timestamp):
"""Returns a time string (ISO 8601) from the given integer timestamp."""
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(timestamp))
def load_json_file(filepath):
"""Opens a JSON file, and returns JSON data as a dict."""
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
return data
def save_json_file(filepath, data, raw=False):
"""Saves a JSON file, given a dict."""
with open(filepath, "w", encoding="utf-8") as f:
json.dump(data, f, sort_keys=True, indent=None if raw else 2)