Error handling, feature cleanup
This commit is contained in:
parent
fafa69202b
commit
94755aa78f
6 changed files with 181 additions and 107 deletions
|
|
@ -12,7 +12,7 @@ Other use cases:
|
|||
|
||||
- Copying only certain types of documents into a backup folder
|
||||
|
||||
- TODO: Grouping all of your photos into separate folders, by year
|
||||
- Moving only files that contain the name "document"
|
||||
|
||||
This is my first real Python project, after many unimportant scripts and experiments. I really needed a project like this in my portfolio, even if it's not truly amazing.
|
||||
|
||||
|
|
@ -24,9 +24,11 @@ This is my first real Python project, after many unimportant scripts and experim
|
|||
|
||||
- Set file rules for generic file types (images, documents, etc.)
|
||||
|
||||
- Save your file rules configuration for future use
|
||||
|
||||
## Usage
|
||||
|
||||
You can run either the app or the script, but the script won't use the file rules if you haven't set them up.
|
||||
You can run either the app or the script
|
||||
|
||||
## About
|
||||
|
||||
|
|
|
|||
154
app.py
154
app.py
|
|
@ -4,14 +4,8 @@ Frontend GUI that displays a list of files from a folder.
|
|||
File actions (move, copy, or delete) can be set for generic file types.
|
||||
"""
|
||||
|
||||
# TODO:
|
||||
# Add comments
|
||||
# Add some error handling
|
||||
# Enable loading custom configuration json
|
||||
# Improve user experience of rules tab
|
||||
|
||||
__name__ = "__main__"
|
||||
__version__ = "0.9"
|
||||
__version__ = "1.0"
|
||||
__author__ = "Gull"
|
||||
|
||||
import tkinter as tk
|
||||
|
|
@ -23,8 +17,8 @@ from tkinter import messagebox as mb
|
|||
from tkinter import filedialog as fd
|
||||
|
||||
APP_NAME = "Finch Filer"
|
||||
LOG_PATH = "%/Gull/app.log"
|
||||
RULES_PATH = "%/Gull/file_rules_custom.json"
|
||||
LOG_PATH = "%/Gullbase/FinchFiler/app.log"
|
||||
RULES_PATH = "%/Gullbase/FinchFiler/file_rules_custom.json"
|
||||
|
||||
def setup_log():
|
||||
"""Initializes the logging system."""
|
||||
|
|
@ -127,44 +121,73 @@ class App(tk.Tk):
|
|||
def load_config(self, filepath=""):
|
||||
"""Loads the file sorting rules from the given file path."""
|
||||
if filepath == "": filepath = self.custom_rules_path
|
||||
path = ut.parse_dir(filepath)
|
||||
data = ut.load_json_file(path)
|
||||
if data != None:
|
||||
self.fm.setup_file_rules(data, True)
|
||||
log.info(f"Loaded custom file rules: {path}")
|
||||
try:
|
||||
path = ut.parse_dir(filepath)
|
||||
except FileNotFoundError as error:
|
||||
print(error)
|
||||
else:
|
||||
try:
|
||||
data = ut.load_json_file(path)
|
||||
self.fm.setup_file_rules(data, True)
|
||||
self.prep()
|
||||
log.info(f"Loaded custom file rules: {path}")
|
||||
except Exception as error:
|
||||
print(error)
|
||||
|
||||
def save_config(self, filepath=""):
|
||||
"""Saves the file sorting rules to the given file path."""
|
||||
if filepath == "": filepath = self.custom_rules_path
|
||||
path = ut.parse_dir(filepath)
|
||||
ut.save_json_file(path, {"filemodes": self.fm.filemodes})
|
||||
log.info(f"Saved custom file rules: {path}")
|
||||
try:
|
||||
path = ut.parse_dir(filepath)
|
||||
except FileNotFoundError as error:
|
||||
print(error)
|
||||
else:
|
||||
try:
|
||||
ut.save_json_file(path, {"filemodes": self.fm.filemodes})
|
||||
log.info(f"Saved custom file rules: {path}")
|
||||
except Exception as error:
|
||||
print(error)
|
||||
|
||||
def reset_config(self):
|
||||
"""Resets the file sorting rules back to default."""
|
||||
data = ut.load_json_file(fm.Manager.RULES_PATH)
|
||||
self.fm.setup_file_rules(data, True)
|
||||
self.prep()
|
||||
log.info(f"Reloaded default file rules")
|
||||
try:
|
||||
data = ut.load_json_file(fm.Manager.RULES_PATH)
|
||||
self.fm.setup_file_rules(data, True)
|
||||
self.prep()
|
||||
log.info(f"Reloaded default file rules")
|
||||
except Exception as error:
|
||||
print(error)
|
||||
log.error("Failed to load default file rules!")
|
||||
|
||||
def update_mode_data(self, mode, key, value):
|
||||
"""Updates file mode data (key and value)."""
|
||||
if mode == "": mode = self.current_mode
|
||||
if not mode in self.fm.filemodes: return
|
||||
self.fm.filemodes[mode].update({key: value})
|
||||
if mode == "": mode = self.current_mode # Uses current mode
|
||||
if not mode in self.fm.filemodes:
|
||||
log.warning(f"File mode not found: {mode}")
|
||||
return
|
||||
data = self.fm.filemodes[mode]
|
||||
data.update({key: value})
|
||||
log.info(f"Set properties for file mode: {key} = {value}")
|
||||
|
||||
def set_rules_variables(self, key):
|
||||
if key == "name": # Refreshes mode list
|
||||
self.v_modelist.set([v["name"] for v in self.fm.filemodes.values()])
|
||||
elif key == "extensions": # Refreshes extensions cache
|
||||
value = value.replace(" ", "") # Removes all spaces
|
||||
data.update({"extensions": value.split(",")}) # Converts to list
|
||||
self.fm.match_file_types()
|
||||
self.fm.update_file_data()
|
||||
self.update_fileview()
|
||||
|
||||
def set_rules_variables(self, mode):
|
||||
"""Updates the internal variables of the file rules (key is mode)."""
|
||||
if not key in self.fm.filemodes: return
|
||||
data = self.fm.filemodes[key]
|
||||
self.v_radio.set(data["action"])
|
||||
if not mode in self.fm.filemodes:
|
||||
log.warning(f"File mode not found: {mode}")
|
||||
return
|
||||
data = self.fm.filemodes[mode]
|
||||
self.v_name.set(data["name"])
|
||||
self.v_action.set(data["action"])
|
||||
self.v_targetdir.set(data["destination"])
|
||||
# TODO: Change string parsing
|
||||
if type(data["extensions"]) == list: # Needs improvement
|
||||
self.v_extstr.set(", ".join(data["extensions"]))
|
||||
else:
|
||||
self.v_extstr.set(data["extensions"])
|
||||
self.v_extstr.set(", ".join(data["extensions"]))
|
||||
|
||||
def select_mode_from_list(self, event):
|
||||
"""Selects the current file mode to use; also updates variables."""
|
||||
|
|
@ -188,8 +211,9 @@ class App(tk.Tk):
|
|||
data = self.fm.filemodes[self.current_mode]
|
||||
dir = fd.askdirectory(initialdir = ut.parse_dir(data["destination"]),
|
||||
title = "Select target directory")
|
||||
self.v_targetdir.set(dir)
|
||||
self.update_mode_data("", "destination", dir)
|
||||
if len(dir) > 0:
|
||||
self.v_targetdir.set(dir)
|
||||
self.update_mode_data("", "destination", dir)
|
||||
|
||||
def set_directory(self, dir):
|
||||
"""Sets the file directory for the app to use."""
|
||||
|
|
@ -201,7 +225,8 @@ class App(tk.Tk):
|
|||
"""Prompts which file directory for the app to use."""
|
||||
dir = fd.askdirectory(initialdir = ut.parse_dir(self.fm.dir),
|
||||
title = "Select directory")
|
||||
self.set_directory(dir)
|
||||
if len(dir) > 0:
|
||||
self.set_directory(dir)
|
||||
|
||||
def ask_load_rules(self):
|
||||
"""Prompts which file rules config for the app to use."""
|
||||
|
|
@ -210,27 +235,38 @@ class App(tk.Tk):
|
|||
dir = fd.askopenfilename(initialdir = initdir,
|
||||
filetypes = filetypes,
|
||||
title = "Load config file")
|
||||
self.load_config(dir)
|
||||
if len(dir) > 0:
|
||||
self.custom_rules_path = dir
|
||||
self.load_config(dir)
|
||||
|
||||
def ask_save_rules(self):
|
||||
"""Prompts which file name to use to save the file rules config."""
|
||||
initdir = ut.parse_dir(self.custom_rules_path)
|
||||
filetypes = (("Config files", "*.json"))
|
||||
initdir = ""
|
||||
if self.custom_rules_path == RULES_PATH:
|
||||
initdir = ut.parse_dir("~/") # Switch to home directory
|
||||
else:
|
||||
initdir = ut.parse_dir(self.custom_rules_path)
|
||||
|
||||
filetypes = (("Config files", "*.json"), ("All files", "*.*"))
|
||||
dir = fd.asksaveasfilename(initialdir = initdir,
|
||||
filetypes = filetypes,
|
||||
title = "Save config file")
|
||||
self.custom_rules_path = dir
|
||||
self.save_config(dir)
|
||||
if len(dir) > 0:
|
||||
if ".json" not in dir: dir += ".json"
|
||||
self.custom_rules_path = dir
|
||||
self.save_config(dir)
|
||||
|
||||
def prep(self):
|
||||
"""Makes the app ready to use after initialization."""
|
||||
self.update_fileview()
|
||||
self.v_modelist.set([v["name"] for v in self.fm.filemodes.values()])
|
||||
if self.current_mode in self.fm.filemodes:
|
||||
self.set_rules_variables(self.current_mode)
|
||||
|
||||
def run_task(self):
|
||||
"""Runs the main file sorting task."""
|
||||
result = mb.askyesno("Task", "Ready to sort all files?")
|
||||
result = mb.askyesno("Task", f"Ready? {len(self.fm.filedata)} files "
|
||||
"will be affected.")
|
||||
if not result: return
|
||||
|
||||
log.info("File organization started")
|
||||
|
|
@ -299,15 +335,8 @@ class App(tk.Tk):
|
|||
"""Files tab of the app."""
|
||||
frame = ttk.Frame(self.book)
|
||||
|
||||
box = ttk.Frame(frame)
|
||||
|
||||
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)
|
||||
|
||||
box.pack(side=tk.TOP, fill=tk.X)
|
||||
button = ttk.Button(frame, text="Start Task", command=self.run_task)
|
||||
button.pack(side=tk.TOP, anchor=tk.W, pady=5)
|
||||
|
||||
self.fileview_columns = ("Last Modified", "Size")
|
||||
self.fileview = ttk.Treeview(frame, columns=self.fileview_columns)
|
||||
|
|
@ -353,22 +382,29 @@ class App(tk.Tk):
|
|||
|
||||
details = ttk.Frame(main)
|
||||
|
||||
i_frame = ttk.Labelframe(details, text="Name")
|
||||
|
||||
self.v_name = tk.StringVar()
|
||||
f_name = lambda e: um("", "name", self.v_name.get())
|
||||
entry = ttk.Entry(i_frame, textvariable=self.v_name)
|
||||
entry.bind('<Return>', f_name)
|
||||
entry.pack(side=tk.LEFT)
|
||||
i_frame.pack(side=tk.TOP, anchor=tk.W, pady=5)
|
||||
|
||||
i_frame = ttk.Labelframe(details, text="Action")
|
||||
|
||||
self.v_radio = tk.StringVar()
|
||||
self.v_action = tk.StringVar()
|
||||
self.v_actions = ("move", "copy", "delete", "ignore")
|
||||
f_radio = lambda: um("", "action", self.v_radio.get())
|
||||
f_action = lambda: um("", "action", self.v_action.get())
|
||||
for s in self.v_actions:
|
||||
str = f"{s.capitalize()} "
|
||||
radio = ttk.Radiobutton(i_frame, text=str, value=s,
|
||||
variable=self.v_radio, command = f_radio)
|
||||
variable=self.v_action, command = f_action)
|
||||
radio.pack(side=tk.LEFT)
|
||||
i_frame.pack(side=tk.TOP, anchor=tk.W, pady=5)
|
||||
|
||||
i_frame = ttk.Labelframe(details, text="Target Directory")
|
||||
|
||||
# text="Start with \"~/\" for your home (user) directory.")
|
||||
|
||||
self.v_targetdir = tk.StringVar()
|
||||
f_targetdir = lambda e: um("", "destination", self.v_targetdir.get())
|
||||
entry = ttk.Entry(i_frame, textvariable=self.v_targetdir)
|
||||
|
|
@ -381,11 +417,7 @@ class App(tk.Tk):
|
|||
|
||||
i_frame.pack(side=tk.TOP, fill=tk.X, pady=5)
|
||||
|
||||
i_frame = ttk.Labelframe(details, text="File Extensions")
|
||||
|
||||
label = ttk.Label(i_frame,
|
||||
text="Separate extensions with a comma and space.")
|
||||
label.pack(side=tk.TOP, anchor=tk.W)
|
||||
i_frame = ttk.Labelframe(details, text="File Filters")
|
||||
|
||||
self.v_extstr = tk.StringVar()
|
||||
f_extstr = lambda e: um("", "extensions", self.v_extstr.get())
|
||||
|
|
|
|||
|
|
@ -3,57 +3,57 @@
|
|||
"all": {
|
||||
"action": "ignore",
|
||||
"active": true,
|
||||
"destination": "~/Downloads/FinchFiler/",
|
||||
"extensions": ["*"],
|
||||
"destination": "~/",
|
||||
"extensions": ["*.*"],
|
||||
"name": "All Files"
|
||||
},
|
||||
"image": {
|
||||
"action": "ignore",
|
||||
"active": true,
|
||||
"destination": "~/Pictures/",
|
||||
"extensions": ["jpg", "jpeg", "png", "gif", "bmp", "psd", "raw", "webp"],
|
||||
"extensions": ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.bmp", "*.psd", "*.raw", "*.webp"],
|
||||
"name": "Images"
|
||||
},
|
||||
"audio": {
|
||||
"action": "ignore",
|
||||
"active": true,
|
||||
"destination": "~/Music/",
|
||||
"extensions": ["wav", "mp3", "ogg", "flac", "wma", "aiff", "aac"],
|
||||
"extensions": ["*.wav", "*.mp3", "*.ogg", "*.flac", "*.wma", "*.aiff", "*.aac"],
|
||||
"name": "Audio"
|
||||
},
|
||||
"video": {
|
||||
"action": "ignore",
|
||||
"active": true,
|
||||
"destination": "~/Videos/",
|
||||
"extensions": ["avi", "mpeg", "mp4", "mov", "mkv", "ogv", "webm"],
|
||||
"extensions": ["*.avi", "*.mpeg", "*.mp4", "*.mov", "*.mkv", "*.ogv", "*.webm"],
|
||||
"name": "Video"
|
||||
},
|
||||
"document": {
|
||||
"action": "ignore",
|
||||
"active": true,
|
||||
"destination": "~/Documents/",
|
||||
"extensions": ["txt", "doc", "docx", "pdf", "rtf"],
|
||||
"extensions": ["*.txt", "*.doc", "*.docx", "*.pdf", "*.rtf"],
|
||||
"name": "Documents"
|
||||
},
|
||||
"data": {
|
||||
"action": "ignore",
|
||||
"active": true,
|
||||
"destination": "~/Documents/data/",
|
||||
"extensions": ["json", "csv", "db"],
|
||||
"extensions": ["*.json", "*.csv", "*.db"],
|
||||
"name": "Data"
|
||||
},
|
||||
"program": {
|
||||
"action": "ignore",
|
||||
"active": true,
|
||||
"destination": "~/Downloads/",
|
||||
"extensions": ["exe", "msi", "elf"],
|
||||
"extensions": ["*.exe", "*.msi", "*.elf"],
|
||||
"name": "Programs"
|
||||
},
|
||||
"archive": {
|
||||
"action": "ignore",
|
||||
"active": true,
|
||||
"destination": "~/Downloads/",
|
||||
"extensions": ["zip", "rar", "tar", "iso", "gz", "lz", "rz", "7z", "dmg"],
|
||||
"extensions": ["*.zip", "*.rar", "*.tar", "*.iso", "*.gz", "*.lz", "*.rz", "*.7z", "*.dmg"],
|
||||
"name": "Archives"
|
||||
},
|
||||
"other": {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import logging as log
|
|||
import utils as ut
|
||||
import send2trash as s2t
|
||||
|
||||
debug_mode = True
|
||||
debug_mode = False
|
||||
|
||||
class Manager:
|
||||
"""File manager and organizer system."""
|
||||
|
|
@ -37,11 +37,15 @@ class Manager:
|
|||
"""Sets the working directory of the file manager."""
|
||||
if os.path.exists(dir):
|
||||
self.dir = dir
|
||||
else:
|
||||
raise FileNotFoundError(f"Directory path not found: {dir}")
|
||||
|
||||
def set_config_path(self, dir):
|
||||
"""Sets the working config path of the file manager."""
|
||||
if os.path.exists(dir):
|
||||
self.config_path = dir
|
||||
else:
|
||||
raise FileNotFoundError(f"Config path not found: {dir}")
|
||||
|
||||
def setup_file_rules(self, data, reset=False):
|
||||
"""Sets or resets file rules from the given data."""
|
||||
|
|
@ -70,10 +74,21 @@ class Manager:
|
|||
def get_file_properties(self, short_filename):
|
||||
"""Returns the time, size, mode, and full path of a single file."""
|
||||
fullpath = os.path.join(self.dir, short_filename)
|
||||
# Maybe check if fullpath exists (os.path.exists(fullpath))
|
||||
try:
|
||||
os.stat(fullpath)
|
||||
except Exception as error:
|
||||
raise error
|
||||
|
||||
_, ext = os.path.splitext(fullpath)
|
||||
ext = ext[1:].lower()
|
||||
mode = self.filetypes[ext] if ext in self.filetypes else "other"
|
||||
|
||||
mode = "other"
|
||||
for k, v in self.filetypes.items():
|
||||
if ext in k: mode = v # Matched extension
|
||||
for k, v in self.filetypes.items():
|
||||
if k in short_filename: mode = v # Matched file name
|
||||
# File name has priority over extension
|
||||
|
||||
stat = os.stat(fullpath)
|
||||
return {"time": stat.st_mtime, "size": stat.st_size, "mode": mode,
|
||||
"fullpath": fullpath}
|
||||
|
|
@ -123,12 +138,16 @@ class Manager:
|
|||
if action in w_stats: w_stats[action] += 1
|
||||
if action != "ignore": w_stats["total"] += 1
|
||||
|
||||
log.info(f"Successfully processed {w_stats['total']} files in total")
|
||||
if debug_mode:
|
||||
log.info(f"Simulated {w_stats['total']} files in total")
|
||||
else:
|
||||
log.info(f"Processed {w_stats['total']} files in total")
|
||||
log.info(f"Moved {w_stats['move']} files")
|
||||
log.info(f"Copied {w_stats['copy']} files")
|
||||
log.info(f"Deleted {w_stats['delete']} files")
|
||||
|
||||
def set_debug_mode(value):
|
||||
"""Enables or disables the file manager debug mode."""
|
||||
global debug_mode
|
||||
if type(value) == None:
|
||||
debug_mode = not debug_mode # Toggles the mode
|
||||
|
|
@ -136,25 +155,34 @@ def set_debug_mode(value):
|
|||
debug_mode = value
|
||||
|
||||
def move(src, dst):
|
||||
"""Moves a file from a source to a destination."""
|
||||
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)
|
||||
try:
|
||||
shutil.move(src, dst)
|
||||
except Exception as error:
|
||||
print(error)
|
||||
return src, dst
|
||||
|
||||
def copy(src, dst):
|
||||
"""Copies a file from a source to a destination."""
|
||||
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)
|
||||
try:
|
||||
shutil.copy2(src, dst)
|
||||
except Exception as error:
|
||||
print(error)
|
||||
return src, dst
|
||||
|
||||
def delete(src):
|
||||
"""Moves a file to the OS trash equivalent."""
|
||||
src = ut.parse_dir(src)
|
||||
log.info(f"Deleted file: {src}")
|
||||
if not debug_mode:
|
||||
try:
|
||||
s2t.send2trash(src)
|
||||
except s2t.TrashPermissionError:
|
||||
pass
|
||||
except s2t.TrashPermissionError as error:
|
||||
print(error)
|
||||
return src
|
||||
28
script.py
28
script.py
|
|
@ -1,4 +1,4 @@
|
|||
"""Script."""
|
||||
"""Script mode for Finch Filer."""
|
||||
|
||||
__author__ = "Gull"
|
||||
|
||||
|
|
@ -8,9 +8,10 @@ import utils as ut
|
|||
import file_manager as fm
|
||||
from argparse import ArgumentParser as ap
|
||||
|
||||
LOG_PATH = "%/Gull/script.log"
|
||||
LOG_PATH = "%/Gullbase/FinchFiler/script.log"
|
||||
SOURCE_PATH = "~/Downloads"
|
||||
RULES_PATH = "%/Gull/file_rules_custom.json"
|
||||
RULES_PATH = "%/Gullbase/FinchFiler/file_rules_custom.json"
|
||||
RULES_PATH_ALT = "data/file_rules_default.json"
|
||||
|
||||
def setup_log():
|
||||
log.basicConfig(**ut.get_log_config(LOG_PATH, log.DEBUG))
|
||||
|
|
@ -40,14 +41,25 @@ def run():
|
|||
|
||||
setup_log()
|
||||
|
||||
fm.set_debug_mode(True) # Temporary
|
||||
|
||||
mgr = fm.Manager()
|
||||
mgr.set_directory(source)
|
||||
mgr.setup_file_rules(ut.load_json_file(rules))
|
||||
try:
|
||||
mgr.set_directory(source)
|
||||
except FileNotFoundError as error:
|
||||
print(error)
|
||||
return
|
||||
|
||||
try:
|
||||
mgr.setup_file_rules(ut.load_json_file(rules))
|
||||
except FileNotFoundError as error:
|
||||
try:
|
||||
rules = ut.parse_dir(RULES_PATH_ALT, True)
|
||||
mgr.setup_file_rules(ut.load_json_file(rules))
|
||||
except FileNotFoundError as error:
|
||||
print(error)
|
||||
return
|
||||
|
||||
if mode != "default" and "all" in mgr.filemodes:
|
||||
mgr.filemodes["all"]["action"] = mode
|
||||
# TODO: Error handling
|
||||
|
||||
mgr.update_file_data()
|
||||
mgr.run_task()
|
||||
|
|
|
|||
38
utils.py
38
utils.py
|
|
@ -37,42 +37,42 @@ def format_date(timestamp):
|
|||
|
||||
def load_json_file(filepath):
|
||||
"""Opens a JSON file, and returns JSON data as a dict."""
|
||||
try: # Needs improvement!
|
||||
try:
|
||||
with open(filepath, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
return data
|
||||
except:
|
||||
return None
|
||||
except Exception as error:
|
||||
raise error
|
||||
|
||||
def save_json_file(filepath, data, raw=False):
|
||||
"""Saves a JSON file, given a dict."""
|
||||
try:
|
||||
with open(filepath, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, sort_keys=True, indent=None if raw else 2)
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
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)
|
||||
except Exception as error:
|
||||
raise error
|
||||
|
||||
def parse_dir(path="", ignore_mkdir=False):
|
||||
"""Converts and creates a directory (can be full file path)."""
|
||||
if path[0] == "~": # Denotes the current user directory
|
||||
path = get_user_dir(path.lstrip("~/"))
|
||||
path = pathlib.Path(path).expanduser()
|
||||
elif path[0] == "%": # Denotes the local appdata directory
|
||||
path = get_appdata_dir(path.lstrip("%/"))
|
||||
path = pathlib.Path(ad.user_data_dir(None, False)) / path.lstrip("%/")
|
||||
else:
|
||||
path = pathlib.Path(path)
|
||||
|
||||
if not ignore_mkdir:
|
||||
pathlib.Path(os.path.split(path)[0]).mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
# This seems sloppy
|
||||
dir = str(path)
|
||||
_, ext = os.path.splitext(dir)
|
||||
if len(ext) > 0: dir = os.path.dirname(dir) #Is file
|
||||
pathlib.Path(dir).mkdir(parents=True, exist_ok=True)
|
||||
except Exception as error:
|
||||
raise error
|
||||
|
||||
return path
|
||||
# Returns as a string
|
||||
return str(path)
|
||||
|
||||
def get_log_config(path="~/", level=None):
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue