Added logging, cleaned up some functions

This commit is contained in:
Thomas Wilczynski 2025-09-03 13:33:47 -07:00
commit 5a04dd37f1
5 changed files with 85 additions and 43 deletions

2
.gitignore vendored
View file

@ -1,5 +1,7 @@
.vscode/
__pycache__/
venv/
venv_linux/
plan.svg
data/file_rules_custom.json
app.log

44
app.py
View file

@ -5,6 +5,7 @@ __author__ = "Gull"
import tkinter as tk
import file_manager as fm
import logging as log
import utils as ut
from tkinter import ttk
from tkinter.messagebox import showinfo
@ -18,10 +19,22 @@ def set_debug_mode(value):
else:
debug_mode = value
def setup_log():
import sys
log.basicConfig(
filename="app.log",
filemode="w",
level=log.DEBUG,
format="{asctime}: [{levelname}] {message}",
style="{")
log.getLogger().addHandler(log.StreamHandler(sys.stdout)) # Console
log.info("App started")
class App(tk.Tk):
def __init__(self, width=640, height=480):
super().__init__()
self.title("Download Utils")
self.title("Download Sorter")
self.current_mode = "all"
self.option_add('*tearOff', tk.FALSE)
self.fm = fm.Manager(True)
@ -37,9 +50,10 @@ class App(tk.Tk):
self.geometry(f"{width}x{height}+{center_x}+{center_y}")
def update_fileview(self):
def update_fileview(self, tree=True):
self.fm.set_directory(self.fm.get_directory())
self.fm.update_file_data()
log.info(f"Processed file view with {len(self.fm.filedata)} items")
while len(self.fileview.get_children()) > 0:
self.fileview.delete(self.fileview.get_children()[-1])
@ -49,17 +63,25 @@ 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)
def load_config(self, filepath="%/Temp/file_rules_custom.json"):
data = ut.load_json_file(ut.parse_dir(filepath))
self.fm.setup_file_rules(data, True)
log.info(f"Loaded custom file rules")
def save_config(self, filepath="data/file_rules_custom.json"):
ut.save_json_file(filepath, self.fm.filemodes)
def save_config(self, filepath="%/Temp/file_rules_custom.json"):
ut.save_json_file(ut.parse_dir(filepath), self.fm.filemodes)
log.info(f"Saved custom file rules")
def reset_config(self):
data = ut.load_json_file(self.fm.config_path)
self.fm.setup_file_rules(data, True)
self.prep()
log.info(f"Reloaded default file rules")
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})
log.info(f"Set properties for file mode: {key} = {value}")
def set_rules_variables(self, key):
if not key in self.fm.filemodes: return
@ -82,6 +104,7 @@ class App(tk.Tk):
if len(key) > 0:
self.current_mode = key
self.set_rules_variables(key)
log.debug(f"Selected file mode: {key}")
def prep(self):
self.update_fileview()
@ -89,11 +112,11 @@ class App(tk.Tk):
self.set_rules_variables(self.current_mode)
def run_task(self):
print("Task started")
log.info("File organization started")
self.fm.run_task()
def run_backup(self):
print("Backup started")
log.info("File backup started")
def gui(self):
self.gui_menu()
@ -159,7 +182,7 @@ class App(tk.Tk):
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")
button = ttk.Button(box, text="Reset", command=self.reset_config)
button.grid(column=1, row=0, pady=5)
box.pack(side=tk.TOP, fill=tk.X)
@ -225,5 +248,6 @@ class App(tk.Tk):
return frame
if __name__ == "__main__":
setup_log()
app = App()
app.mainloop()

View file

@ -11,28 +11,28 @@
"action": "ignore",
"active": true,
"destination": "~/Pictures/",
"extensions": ["jpg", "jpeg", "png", "gif", "webp"],
"extensions": ["jpg", "jpeg", "png", "gif", "bmp", "psd", "raw", "webp"],
"name": "Images"
},
"audio": {
"action": "ignore",
"active": true,
"destination": "~/Music/",
"extensions": ["wav", "mp3", "ogg"],
"extensions": ["wav", "mp3", "ogg", "flac", "wma", "aiff", "aac"],
"name": "Audio"
},
"video": {
"action": "ignore",
"active": true,
"destination": "~/Videos/",
"extensions": ["avi", "mpeg", "mp4", "webm"],
"extensions": ["avi", "mpeg", "mp4", "mov", "mkv", "ogv", "webm"],
"name": "Video"
},
"document": {
"action": "ignore",
"active": true,
"destination": "~/Documents/",
"extensions": ["txt", "doc", "docx", "pdf"],
"extensions": ["txt", "doc", "docx", "pdf", "rtf"],
"name": "Documents"
},
"data": {
@ -46,14 +46,14 @@
"action": "ignore",
"active": true,
"destination": "~/Downloads/",
"extensions": ["exe", "msi"],
"extensions": ["exe", "msi", "elf"],
"name": "Programs"
},
"archive": {
"action": "ignore",
"active": true,
"destination": "~/Downloads/",
"extensions": ["zip", "rar", "gz"],
"extensions": ["zip", "rar", "tar", "iso", "gz", "lz", "rz", "7z", "dmg"],
"name": "Archives"
},
"other": {
@ -63,6 +63,5 @@
"extensions": [],
"name": "Other"
}
},
"version": 1
}
}

View file

@ -4,7 +4,9 @@ __author__ = "Gull"
import os
import shutil
import logging as log
import utils as ut
import send2trash as s2t
debug_mode = True
@ -61,6 +63,9 @@ class Manager:
def run_task(self):
self.match_file_types() # Updates file types dict
log.info(f"Now processing {len(self.filedata)} files")
if debug_mode:
log.warning("Debug mode is enabled; file actions will be ignored")
for k, v in self.filedata.items():
fullpath = os.path.join(self.dir, k)
if os.path.exists(fullpath):
@ -79,30 +84,23 @@ class Manager:
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:
src, dst = ut.parse_dir(src), ut.parse_dir(dst)
log.info(f"Moved file: {src} > {dst}")
if not debug_mode:
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:
src, dst = ut.parse_dir(src), ut.parse_dir(dst)
log.info(f"Copied file: {src} > {dst}")
if not debug_mode:
shutil.copy2(src, dst)
def delete(src):
src = parse_dir(src)
if debug_mode:
print(f"Delete: {src}")
else:
os.remove(src)
src = ut.parse_dir(src)
log.info(f"Deleted file: {src}")
if not debug_mode:
try:
s2t.send2trash(src)
except s2t.TrashPermissionError:
pass

View file

@ -2,8 +2,10 @@
__author__ = "Gull"
import os
import time
import json
import appdirs as ad
KILOBYTES = 1024
@ -20,13 +22,13 @@ def format_bytes(bytes):
if bytes < units["KB"]:
return f"{bytes} B"
elif units["KB"] <= bytes < units["MB"]:
return f"{(bytes / units["KB"]):.2f} KB"
return f"{(bytes / units['KB']):.2f} KB"
elif units["MB"] <= bytes < units["GB"]:
return f"{(bytes / units["MB"]):.2f} MB"
return f"{(bytes / units['MB']):.2f} MB"
elif units["GB"] <= bytes < units["TB"]:
return f"{(bytes / units["GB"]):.2f} GB"
return f"{(bytes / units['GB']):.2f} GB"
elif units["TB"] <= bytes:
return f"{(bytes / units["TB"]):.2f} TB"
return f"{(bytes / units['TB']):.2f} TB"
def format_date(timestamp):
"""Returns a time string (ISO 8601) from the given integer timestamp."""
@ -41,4 +43,21 @@ def load_json_file(filepath):
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)
json.dump(data, f, sort_keys=True, indent=None if raw else 2)
def get_user_dir(dir=""):
"""Returns the joined user directory."""
return os.path.join(os.path.expanduser("~"), dir)
def get_appdata_dir(dir=""):
"""Returns the joined appdata directory."""
return os.path.join(ad.user_data_dir(None, False), dir)
def parse_dir(dir=""):
"""Checks if a directory exists, and if it is special."""
if dir[0] == "~": # Denotes the current user directory
dir = get_user_dir(dir.lstrip("~/"))
elif dir[0] == "%": # Denotes the local appdata directory
dir = get_appdata_dir(dir.lstrip("%/"))
return dir