refactor with configmanager
This commit is contained in:
parent
8eb54555f5
commit
217a034fbf
@ -1,100 +1,165 @@
|
|||||||
from os import path, getcwd, listdir, mkdir, makedirs, rename
|
import enum
|
||||||
|
from os import EX_CANTCREAT, path, getcwd, listdir, rename
|
||||||
|
from sys import exit
|
||||||
import re
|
import re
|
||||||
|
|
||||||
def read_config(filepath, root_directory="."):
|
from .globals import version, settings_map
|
||||||
if not path.isfile(filepath): return False
|
from .globals import warning, error, user_error, info, create_dir
|
||||||
|
|
||||||
file = open(filepath, 'r')
|
class ConfigManager():
|
||||||
keys = {}
|
"""Manage config files for imgsort"""
|
||||||
for line in file.readlines():
|
def __init__(self, config_dir: str):
|
||||||
line = line.replace("\n", "")
|
"""TODO: to be defined.
|
||||||
match = re.match(r". = /?([a-z-A-ZöÖäÄüÜ0-9/: _-]+/)*[a-zA-ZöÖäÄüÜ0-9/: _-]+/?", line)
|
|
||||||
if match:
|
|
||||||
key, value = line.split(" = ")
|
|
||||||
keys[key] = root_directory + "/" + value
|
|
||||||
return keys
|
|
||||||
|
|
||||||
def write_config(filepath, keys):
|
@param config_path TODO
|
||||||
file = open(filepath, 'w')
|
|
||||||
file.write("Config written by imgsort.\n")
|
"""
|
||||||
|
self._config_dir = config_dir
|
||||||
|
if not path.isdir(self._config_dir):
|
||||||
|
if path.exists(self._config_dir):
|
||||||
|
error(f"Config '{self._config_dir}' exists but is not a directory.")
|
||||||
|
info(f"Creating config dir '{self._config_dir}' since it does not exist")
|
||||||
|
try:
|
||||||
|
create_dir(self._config_dir)
|
||||||
|
except PermissionError as e:
|
||||||
|
error(f"Could not create '{self._config_dir}': PermissionError: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
error(f"Could not create '{self._config_dir}': {e}")
|
||||||
|
|
||||||
|
self._configs = [ e for e in listdir(self._config_dir) if path.isfile(path.normpath(self._config_dir + "/" + e)) and e.endswith(".conf") ]
|
||||||
|
self._configs.sort()
|
||||||
|
|
||||||
|
def present_config_selection(self):
|
||||||
|
"""
|
||||||
|
Returns to path to an existing config or False if a new config should be created
|
||||||
|
"""
|
||||||
|
# get configs
|
||||||
|
if len(self._configs) == 0:
|
||||||
|
info(f"No config valid file found in '{self._config_dir}'")
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("0: create new configuration")
|
||||||
|
for i, c in enumerate(self._configs):
|
||||||
|
print(f"{i+1:2}: {c}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
choice = input("Please select a config: ")
|
||||||
|
try:
|
||||||
|
choice = int(choice)
|
||||||
|
except ValueError:
|
||||||
|
user_error(f"Invalid choice: '{choice}'. Choice must be a number between 0 and {len(self._configs)}")
|
||||||
|
continue
|
||||||
|
if not 0 <= choice <= len(self._configs):
|
||||||
|
user_error(f"Invalid choice: '{choice}'. Choice must be a number between 0 and {len(self._configs)}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if choice == 0:
|
||||||
|
return False
|
||||||
|
return self._configs[choice-1]
|
||||||
|
|
||||||
|
|
||||||
|
def _make_name(self, config_name: str):
|
||||||
|
return path.normpath(self._config_dir + "/" + config_name.removesuffix(".conf") + ".conf")
|
||||||
|
|
||||||
|
|
||||||
|
def write_config(self, config_name: str, keys: dict[str,str]):
|
||||||
|
file = open(path.normpath(self._config_dir + "/" + config_name), 'w')
|
||||||
|
file.write(f"# Config written by imgsort {version}.\n")
|
||||||
for k, v in keys.items() :
|
for k, v in keys.items() :
|
||||||
file.write(f"{k} = {v}\n")
|
file.write(f"{k} = {v}\n")
|
||||||
|
|
||||||
def create_config():
|
|
||||||
keys = {}
|
def read_config(self, config_name: str, root_directory="."):
|
||||||
print(
|
|
||||||
"""
|
"""
|
||||||
|
@param root_directory Make all relative paths relative to this one
|
||||||
|
"""
|
||||||
|
if type(config_name) != str:
|
||||||
|
error(f"load config got wrong type: '{type(config_name)}'")
|
||||||
|
config_file = self._make_name(config_name)
|
||||||
|
if not path.isfile(config_file):
|
||||||
|
error(f"File '{config_file}' does not exist")
|
||||||
|
try:
|
||||||
|
file = open(config_file, 'r')
|
||||||
|
except Exception as e:
|
||||||
|
error(f"Could not open file '{config_file}': {e}")
|
||||||
|
|
||||||
|
keys: dict[str, str] = {}
|
||||||
|
for i, line in enumerate(file.readlines()):
|
||||||
|
line = line.replace("\n", "")
|
||||||
|
match = re.fullmatch(r". = [^*?<>|]+", line)
|
||||||
|
if match:
|
||||||
|
key, value = line.split(" = ")
|
||||||
|
keys[key] = path.normpath(root_directory + "/" + value)
|
||||||
|
elif not line[0] == "#":
|
||||||
|
warning(f"In config file '{config_file}': Invalid line ({i+1}): '{line}'")
|
||||||
|
self.validate_dirs(keys)
|
||||||
|
return keys
|
||||||
|
|
||||||
|
def create_config(self):
|
||||||
|
keys: dict[str, str] = {}
|
||||||
|
print(
|
||||||
|
f"""
|
||||||
===================================================================================================
|
===================================================================================================
|
||||||
Creating a new config
|
Creating a new config
|
||||||
Please enter at least one key and one directory.
|
You can now map keys to directories.
|
||||||
The key must be one single letter, a single digit number or some other keyboard key like .-#+&/ ...
|
The key must be one single letter, a single digit number or some other keyboard key like .-#+&/ ...
|
||||||
The key can not be 'q', 's', 'o' or 'u'.
|
The key can not be one of {' '.join(settings_map.keys())}.
|
||||||
The directory must be a valid path to a directory, but is does not have to exist.
|
The directory must be a valid path to a directory, but is does not have to exist.
|
||||||
You can use an absolute path (starting with '/', not '~') or a relative path (from here).
|
You can use an absolute path (starting with '/', not '~') or a relative path (from here).
|
||||||
===================================================================================================
|
===================================================================================================
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
done = False
|
while True:
|
||||||
while not done:
|
|
||||||
|
|
||||||
# ask for key
|
# ask for key
|
||||||
key = input("Please enter a key or 'q' when you are done: ")
|
key = input("Please enter a key or 'q' when you are done: ")
|
||||||
if (len(key) != 1):
|
if (len(key) != 1):
|
||||||
print("Invalid key: " + key)
|
user_error(f"Invalid key: '{key}' has a length other than 1")
|
||||||
continue
|
continue
|
||||||
# if done
|
# if done
|
||||||
elif key == 'q':
|
elif key == 'q':
|
||||||
save = input("\nDo you want to save the config to ~/.config/imgsort/<name>.conf?\nType a name to save the config or type 'q' to not save the config: ")
|
if len(keys) == 0:
|
||||||
if not save == 'q':
|
warning(f"No keys were mapped - exiting")
|
||||||
config_path = path.expanduser("~") + "/.config/imgsort"
|
exit(0)
|
||||||
if not path.isdir(config_path):
|
save = input(f"\nDo you want to save the config to {self._config_dir}/<name>.conf?\nType a name to save the config or type 'q' to not save the config: ")
|
||||||
mkdir(config_path)
|
if save != 'q':
|
||||||
|
self.write_config(save + ".conf", keys)
|
||||||
write_config(path.normpath(config_path + "/" + save + ".conf"), keys)
|
break
|
||||||
done = True
|
elif key in settings_map.keys():
|
||||||
|
user_error(f"Invalid key: '{key}' is reserved and can not be mapped")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# ask for directory
|
# ask for directory
|
||||||
directory = input("Please enter the directory path: ")
|
directory = input("Please enter the directory/path: ")
|
||||||
match = re.match(r"/?([a-z-A-ZöÖäÄüÜ0-9/: _\-]+/)*[a-z-A-ZöÖäÄüÜ0-9/: _\-]+/?", directory)
|
# match = re.match(r"/?([a-z-A-ZöÖäÄüÜ0-9/: _\-]+/)*[a-z-A-ZöÖäÄüÜ0-9/: _\-]+/?", directory)
|
||||||
if not match:
|
INVALID_PATH_CHARS = r":*?<>|"
|
||||||
print("Invalid directory path: " + directory)
|
if any(c in INVALID_PATH_CHARS for c in directory):
|
||||||
|
user_error(f"Invalid directory path: '{directory}' contains at least one if '{INVALID_PATH_CHARS}'")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
keys[key] = directory
|
keys[key] = directory
|
||||||
print(f"Added: ({key}: {directory})\n")
|
print(f"Added: {key}: '{directory}'\n")
|
||||||
|
|
||||||
|
self.validate_dirs(keys)
|
||||||
|
|
||||||
return keys
|
return keys
|
||||||
|
|
||||||
|
|
||||||
def select_config():
|
def validate_dirs(self, keys):
|
||||||
"""
|
"""
|
||||||
Returns to path to an existing config or False if a new config should be created
|
Create the directories that dont exist.
|
||||||
"""
|
"""
|
||||||
# get configs
|
missing = []
|
||||||
config_path = path.expanduser("~") + "/.config/imgsort"
|
for d in keys.values():
|
||||||
if not path.isdir(config_path) or len(listdir(config_path)) == 0:
|
if not path.isdir(d):
|
||||||
return False
|
missing.append(d)
|
||||||
|
if len(missing) == 0: return
|
||||||
configs = {}
|
print(f"The following directories do not exist:")
|
||||||
|
for d in missing: print(f"\t{d}")
|
||||||
i = 1
|
decision = input(f"Create the ({len(missing)}) missing directories? y/*: ")
|
||||||
for file in listdir(config_path):
|
if (decision == "y"):
|
||||||
if not re.match(r"[a-zA-ZöÖäÄüÜ0-9_\- ]+\.conf", file): continue
|
for d in missing:
|
||||||
|
create_dir(d)
|
||||||
configs[str(i)] = file
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
# print configs
|
|
||||||
print("0: Create new config")
|
|
||||||
for n, conf in configs.items():
|
|
||||||
print(f"{n}: {conf}")
|
|
||||||
|
|
||||||
choice = input("Please select a config: ")
|
|
||||||
if choice == "0": return None
|
|
||||||
elif choice in configs:
|
|
||||||
return path.normpath(config_path + "/" + configs[choice])
|
|
||||||
else:
|
else:
|
||||||
print("Invalid choice - creating new config")
|
error("Exiting - can not use non-existing directories.")
|
||||||
return None
|
|
||||||
|
|
||||||
|
@ -13,16 +13,12 @@ if __name__ == "__main__": # make relative imports work as described here: http
|
|||||||
filepath = path.realpath(path.abspath(__file__))
|
filepath = path.realpath(path.abspath(__file__))
|
||||||
sys.path.insert(0, path.dirname(path.dirname(filepath)))
|
sys.path.insert(0, path.dirname(path.dirname(filepath)))
|
||||||
|
|
||||||
from .configs import read_config, select_config
|
|
||||||
import ueberzug.lib.v0 as uz
|
import ueberzug.lib.v0 as uz
|
||||||
|
|
||||||
|
from .configs import ConfigManager
|
||||||
settings = {
|
from .ueberzug import UeberzugLayer
|
||||||
"q": "quit",
|
from .globals import version, settings_map
|
||||||
"s": "skip",
|
from .globals import warning, error, user_error, info, create_dir
|
||||||
"u": "undo",
|
|
||||||
"o": "open"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Size settings
|
# Size settings
|
||||||
FOOTER_LEFT = 0
|
FOOTER_LEFT = 0
|
||||||
@ -48,9 +44,7 @@ class Sorter:
|
|||||||
|
|
||||||
self.keys = config
|
self.keys = config
|
||||||
|
|
||||||
self.settings = settings
|
self.settings = settings_map
|
||||||
|
|
||||||
self.validate_dirs()
|
|
||||||
|
|
||||||
# info about last action
|
# info about last action
|
||||||
self.last_dir = ""
|
self.last_dir = ""
|
||||||
@ -77,21 +71,6 @@ class Sorter:
|
|||||||
self.placement.width = self.win_x - SIDEBAR_WIDTH - 1
|
self.placement.width = self.win_x - SIDEBAR_WIDTH - 1
|
||||||
self.placement.height = self.win_y - FOOTER_HEIGHT - 2
|
self.placement.height = self.win_y - FOOTER_HEIGHT - 2
|
||||||
|
|
||||||
def validate_dirs(self):
|
|
||||||
"""
|
|
||||||
Create the directories that dont exist.
|
|
||||||
"""
|
|
||||||
for d in self.keys.values():
|
|
||||||
if not path.isdir(d):
|
|
||||||
print(f"Directory '{d}' does not exist.")
|
|
||||||
decision = input(f"Create directory '{path.abspath(d)}'? y/n: ")
|
|
||||||
if (decision == "y"):
|
|
||||||
makedirs(d)
|
|
||||||
else:
|
|
||||||
print("Exiting - can not use non-existing directory.")
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def get_images(self):
|
def get_images(self):
|
||||||
"""
|
"""
|
||||||
Put all image-paths from wd in images dictionary.
|
Put all image-paths from wd in images dictionary.
|
||||||
@ -133,7 +112,7 @@ class Sorter:
|
|||||||
self.image_iter += 1
|
self.image_iter += 1
|
||||||
self.message = "Skipped image"
|
self.message = "Skipped image"
|
||||||
continue
|
continue
|
||||||
elif settings[self.pressed_key] == "undo":
|
elif settings_map[self.pressed_key] == "undo":
|
||||||
if self.image_iter > 0:
|
if self.image_iter > 0:
|
||||||
self.image_iter -= 1 # using last image
|
self.image_iter -= 1 # using last image
|
||||||
rename(self.images_new[self.image_iter], self.images[self.image_iter])
|
rename(self.images_new[self.image_iter], self.images[self.image_iter])
|
||||||
@ -143,7 +122,7 @@ class Sorter:
|
|||||||
else:
|
else:
|
||||||
self.message = "Nothing to undo!"
|
self.message = "Nothing to undo!"
|
||||||
continue
|
continue
|
||||||
elif settings[self.pressed_key] == "open":
|
elif settings_map[self.pressed_key] == "open":
|
||||||
try:
|
try:
|
||||||
subprocess.run(['xdg-open', self.image], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
subprocess.run(['xdg-open', self.image], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
|
||||||
self.message = "Opening with xdg-open"
|
self.message = "Opening with xdg-open"
|
||||||
@ -263,7 +242,7 @@ Image Sorter
|
|||||||
if 'IMGSOSRT_CONFIG_DIR' in os.environ: config_dir = os.environ['IMGSORT_CONFIG_DIR']
|
if 'IMGSOSRT_CONFIG_DIR' in os.environ: config_dir = os.environ['IMGSORT_CONFIG_DIR']
|
||||||
|
|
||||||
parser = argparse.ArgumentParser("imgsort")
|
parser = argparse.ArgumentParser("imgsort")
|
||||||
parser.add_argument("-c", "--config", action="store", help="name of the config file in ($IMGSORT_CONFIG_DIR > $XDG_CONFIG_HOME/imgsort > ~/.config/imgsort)")
|
parser.add_argument("-c", "--config", action="store", help="name of the config file in ($IMGSORT_CONFIG_DIR > $XDG_CONFIG_HOME/imgsort > ~/.config/imgsort)", default=None)
|
||||||
parser.add_argument("-i", "--sort-dir", action="store", help="the directory where the folders from the config will be created")
|
parser.add_argument("-i", "--sort-dir", action="store", help="the directory where the folders from the config will be created")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@ -274,23 +253,18 @@ Image Sorter
|
|||||||
else:
|
else:
|
||||||
args.sort_dir = getcwd()
|
args.sort_dir = getcwd()
|
||||||
|
|
||||||
# configuration
|
confman = ConfigManager(config_dir)
|
||||||
if args.config:
|
|
||||||
config_path = args.config_file
|
|
||||||
# search locally
|
|
||||||
if not path.isfile(config_path):
|
|
||||||
config_path = path.join(config_dir, args.config)
|
|
||||||
if not path.isfile(config_path):
|
|
||||||
parser.error(f"invalid configuration path/name:'{config_path}'/'{args.config}'")
|
|
||||||
else:
|
|
||||||
args.config = select_config()
|
|
||||||
|
|
||||||
config = read_config(args.config, root_directory=args.sort_dir)
|
# configuration
|
||||||
if not config:
|
if not args.config:
|
||||||
print("Error reading the config:")
|
args.config = confman.present_config_selection()
|
||||||
print(" Config Name:", args.config)
|
# if create config was selected
|
||||||
print(" Config:", config)
|
if args.config is False:
|
||||||
exit(1)
|
config = confman.create_config()
|
||||||
|
else:
|
||||||
|
if type(args.config) != str:
|
||||||
|
error(f"Could not determine condig file to load ('{args.config}' is of type '{type(args.config)}' not ")
|
||||||
|
config = confman.read_config(args.config, args.sort_dir)
|
||||||
|
|
||||||
with uz.Canvas() as canvas:
|
with uz.Canvas() as canvas:
|
||||||
sorter = Sorter(wd, canvas, config)
|
sorter = Sorter(wd, canvas, config)
|
||||||
|
Loading…
Reference in New Issue
Block a user