Applied settings; app now functions in a basic way
This commit is contained in:
parent
3d97c3b698
commit
ca2c8f2ade
5 changed files with 134 additions and 25 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
__pycache__/
|
__pycache__/
|
||||||
venv/
|
venv/
|
||||||
plan.svg
|
plan.svg
|
||||||
|
data/file_rules_custom.json
|
||||||
|
|
|
||||||
66
app.py
66
app.py
|
|
@ -19,12 +19,15 @@ def set_debug_mode(value):
|
||||||
debug_mode = value
|
debug_mode = value
|
||||||
|
|
||||||
class App(tk.Tk):
|
class App(tk.Tk):
|
||||||
def __init__(self, width=600, height=400):
|
def __init__(self, width=640, height=480):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.title("Download Utils")
|
self.title("Download Utils")
|
||||||
|
self.current_mode = "all"
|
||||||
self.option_add('*tearOff', tk.FALSE)
|
self.option_add('*tearOff', tk.FALSE)
|
||||||
self.fm = fm.Manager(True)
|
self.fm = fm.Manager(True)
|
||||||
|
self.log = list()
|
||||||
self.gui()
|
self.gui()
|
||||||
|
self.prep()
|
||||||
|
|
||||||
screen_width = self.winfo_screenwidth()
|
screen_width = self.winfo_screenwidth()
|
||||||
screen_height = self.winfo_screenheight()
|
screen_height = self.winfo_screenheight()
|
||||||
|
|
@ -46,6 +49,28 @@ class App(tk.Tk):
|
||||||
str(ut.format_bytes(v["size"])))
|
str(ut.format_bytes(v["size"])))
|
||||||
self.fileview.insert("", tk.END, k, values=values)
|
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):
|
def select_mode_from_list(self, event):
|
||||||
key = ""
|
key = ""
|
||||||
value = self.w_list.get(self.w_list.curselection())
|
value = self.w_list.get(self.w_list.curselection())
|
||||||
|
|
@ -54,13 +79,21 @@ class App(tk.Tk):
|
||||||
key = k
|
key = k
|
||||||
break
|
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]
|
def prep(self):
|
||||||
print(data)
|
self.update_fileview()
|
||||||
self.v_radio.set(data["action"])
|
if self.current_mode in self.fm.filemodes:
|
||||||
self.v_targetdir.set(data["destination"])
|
self.set_rules_variables(self.current_mode)
|
||||||
self.v_extstr.set(", ".join(data["extensions"]))
|
|
||||||
|
def run_task(self):
|
||||||
|
print("Task started")
|
||||||
|
self.fm.run_task()
|
||||||
|
|
||||||
|
def run_backup(self):
|
||||||
|
print("Backup started")
|
||||||
|
|
||||||
def gui(self):
|
def gui(self):
|
||||||
self.gui_menu()
|
self.gui_menu()
|
||||||
|
|
@ -76,7 +109,6 @@ class App(tk.Tk):
|
||||||
self.config(menu=a_menu)
|
self.config(menu=a_menu)
|
||||||
|
|
||||||
a_menu_file = tk.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_file.add_command(label="Exit", command=self.destroy)
|
||||||
|
|
||||||
a_menu.add_cascade(
|
a_menu.add_cascade(
|
||||||
|
|
@ -90,13 +122,13 @@ class App(tk.Tk):
|
||||||
|
|
||||||
box = ttk.Frame(frame)
|
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.grid(column=0, row=0, pady=5)
|
||||||
|
|
||||||
button = ttk.Button(box, text="Refresh", command=self.update_fileview)
|
button = ttk.Button(box, text="Refresh", command=self.update_fileview)
|
||||||
button.grid(column=1, row=0, pady=5)
|
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)
|
button.grid(column=2, row=0, pady=5)
|
||||||
|
|
||||||
box.pack(side=tk.TOP, fill=tk.X)
|
box.pack(side=tk.TOP, fill=tk.X)
|
||||||
|
|
@ -113,19 +145,18 @@ class App(tk.Tk):
|
||||||
command=self.fileview.yview)
|
command=self.fileview.yview)
|
||||||
self.fileview.configure(yscrollcommand=sbar.set)
|
self.fileview.configure(yscrollcommand=sbar.set)
|
||||||
|
|
||||||
self.update_fileview()
|
|
||||||
|
|
||||||
self.fileview.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
self.fileview.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
sbar.pack(side=tk.RIGHT, fill=tk.Y)
|
sbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
|
|
||||||
def gui_rules(self):
|
def gui_rules(self):
|
||||||
|
um = self.update_mode_data
|
||||||
frame = ttk.Frame(self.book)
|
frame = ttk.Frame(self.book)
|
||||||
|
|
||||||
box = ttk.Frame(frame)
|
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.grid(column=0, row=0, pady=5)
|
||||||
|
|
||||||
button = ttk.Button(box, text="Reset")
|
button = ttk.Button(box, text="Reset")
|
||||||
|
|
@ -142,7 +173,7 @@ class App(tk.Tk):
|
||||||
self.w_list = tk.Listbox(main, listvariable=self.v_modelist,
|
self.w_list = tk.Listbox(main, listvariable=self.v_modelist,
|
||||||
selectmode=tk.SINGLE)
|
selectmode=tk.SINGLE)
|
||||||
self.w_list.bind('<<ListboxSelect>>', self.select_mode_from_list)
|
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)
|
details = ttk.Frame(main)
|
||||||
|
|
||||||
|
|
@ -150,10 +181,11 @@ class App(tk.Tk):
|
||||||
|
|
||||||
self.v_radio = tk.StringVar()
|
self.v_radio = tk.StringVar()
|
||||||
self.v_actions = ("move", "copy", "delete", "ignore")
|
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:
|
for s in self.v_actions:
|
||||||
str = f"{s.capitalize()} "
|
str = f"{s.capitalize()} "
|
||||||
radio = ttk.Radiobutton(i_frame, text=str, value=s,
|
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)
|
radio.pack(side=tk.LEFT)
|
||||||
i_frame.pack(side=tk.TOP, anchor=tk.W)
|
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)
|
label.pack(side=tk.TOP, anchor=tk.W)
|
||||||
|
|
||||||
self.v_targetdir = tk.StringVar()
|
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 = ttk.Entry(i_frame, textvariable=self.v_targetdir)
|
||||||
|
entry.bind('<Return>', f_targetdir)
|
||||||
entry.pack(side=tk.TOP, fill=tk.X)
|
entry.pack(side=tk.TOP, fill=tk.X)
|
||||||
|
|
||||||
i_frame.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)
|
label.pack(side=tk.TOP, anchor=tk.W)
|
||||||
|
|
||||||
self.v_extstr = tk.StringVar()
|
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 = ttk.Entry(i_frame, textvariable=self.v_extstr)
|
||||||
|
entry.bind('<Return>', f_extstr)
|
||||||
entry.pack(side=tk.TOP, fill=tk.X)
|
entry.pack(side=tk.TOP, fill=tk.X)
|
||||||
|
|
||||||
i_frame.pack(side=tk.TOP, fill=tk.X)
|
i_frame.pack(side=tk.TOP, fill=tk.X)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,10 @@
|
||||||
__author__ = "Gull"
|
__author__ = "Gull"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import shutil
|
||||||
|
import utils as ut
|
||||||
|
|
||||||
|
debug_mode = True
|
||||||
|
|
||||||
class Manager:
|
class Manager:
|
||||||
def __init__(self, auto_config=False):
|
def __init__(self, auto_config=False):
|
||||||
|
|
@ -11,10 +14,11 @@ class Manager:
|
||||||
self.filedata = dict()
|
self.filedata = dict()
|
||||||
self.filemodes = dict() # Contains file mode data
|
self.filemodes = dict() # Contains file mode data
|
||||||
self.filetypes = dict() # Maps file extensions to modes
|
self.filetypes = dict() # Maps file extensions to modes
|
||||||
|
self.config_path = "data/file_rules_default.json"
|
||||||
|
|
||||||
if auto_config:
|
if auto_config:
|
||||||
self.set_directory(self.get_directory())
|
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"):
|
def get_directory(self, target="Downloads"):
|
||||||
dir = os.path.join(os.path.expanduser("~"), target)
|
dir = os.path.join(os.path.expanduser("~"), target)
|
||||||
|
|
@ -25,12 +29,20 @@ class Manager:
|
||||||
if os.path.exists(dir):
|
if os.path.exists(dir):
|
||||||
self.dir = dir
|
self.dir = dir
|
||||||
|
|
||||||
def setup_file_rules(self, file="data/settings_default.json", data=None):
|
def setup_file_rules(self, data, reset=False):
|
||||||
with open(file) as f:
|
if reset: self.filemodes.clear()
|
||||||
data = json.load(f)
|
if not "filemodes" in data: return
|
||||||
self.filemodes = data["filemodes"]
|
|
||||||
self.filetypes.clear()
|
filemodes = data["filemodes"]
|
||||||
for k, v in self.filemodes.items():
|
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"]:
|
for ext in v["extensions"]:
|
||||||
self.filetypes[ext] = k
|
self.filetypes[ext] = k
|
||||||
|
|
||||||
|
|
@ -46,3 +58,51 @@ class Manager:
|
||||||
st = entry.stat()
|
st = entry.stat()
|
||||||
data = {"time": st.st_mtime, "size": st.st_size}
|
data = {"time": st.st_mtime, "size": st.st_size}
|
||||||
self.filedata[entry.name] = data
|
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)
|
||||||
12
utils.py
12
utils.py
|
|
@ -3,6 +3,7 @@
|
||||||
__author__ = "Gull"
|
__author__ = "Gull"
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import json
|
||||||
|
|
||||||
KILOBYTES = 1024
|
KILOBYTES = 1024
|
||||||
|
|
||||||
|
|
@ -30,3 +31,14 @@ def format_bytes(bytes):
|
||||||
def format_date(timestamp):
|
def format_date(timestamp):
|
||||||
"""Returns a time string (ISO 8601) from the given integer 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)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue