Refactoring collection options (3)

This commit is contained in:
Cédric Leporcq 2021-11-19 18:24:35 +01:00
parent 27af9bb55e
commit 7688d0e7c4
6 changed files with 149 additions and 163 deletions

View File

@ -184,7 +184,6 @@ def _check(**kwargs):
def _clean(**kwargs):
"""Remove empty folders"""
dry_run = kwargs['dry_run']
folders = kwargs['folders']
log_level = log.get_level(kwargs['verbose'])
log.console(LOG, level=log_level)
@ -195,10 +194,12 @@ def _clean(**kwargs):
collection = Collection(
root,
dry_run=dry_run,
exclude=kwargs['exclude'],
extensions=kwargs['ext'],
glob=kwargs['glob'],
{
"dry_run": kwargs['dry_run'],
"exclude": kwargs['exclude'],
"extensions": kwargs['ext'],
"glob": kwargs['glob'],
},
)
# TODO
@ -253,7 +254,6 @@ def _compare(**kwargs):
Sort similar images in directories
"""
dry_run = kwargs['dry_run']
subdirs = kwargs['subdirs']
root = kwargs['collection']
@ -263,10 +263,12 @@ def _compare(**kwargs):
collection = Collection(
root,
exclude=kwargs['exclude'],
extensions=kwargs['ext'],
glob=kwargs['glob'],
dry_run=dry_run,
{
"exclude": kwargs['exclude'],
"extensions": kwargs['ext'],
"glob": kwargs['glob'],
"dry_run": kwargs['dry_run'],
},
)
for path in paths:
@ -338,16 +340,18 @@ def _import(**kwargs):
collection = Collection(
root,
kwargs['album_from_folder'],
False,
kwargs['dry_run'],
kwargs['exclude'],
kwargs['ext'],
kwargs['glob'],
kwargs['interactive'],
kwargs['ignore_tags'],
kwargs['use_date_filename'],
kwargs['use_file_dates'],
{
'album_from_folder': kwargs['album_from_folder'],
'cache': False,
'ignore_tags': kwargs['ignore_tags'],
'use_date_filename': kwargs['use_date_filename'],
'use_file_dates': kwargs['use_file_dates'],
'exclude': kwargs['exclude'],
'extensions': kwargs['ext'],
'glob': kwargs['glob'],
'dry_run': kwargs['dry_run'],
'interactive': kwargs['interactive'],
}
)
# TODO retrieve collection.opt
@ -396,16 +400,18 @@ def _sort(**kwargs):
collection = Collection(
root,
kwargs['album_from_folder'],
cache,
kwargs['dry_run'],
kwargs['exclude'],
kwargs['ext'],
kwargs['glob'],
kwargs['interactive'],
kwargs['ignore_tags'],
kwargs['use_date_filename'],
kwargs['use_file_dates'],
{
'album_from_folder': kwargs['album_from_folder'],
'cache': cache,
'ignore_tags': kwargs['ignore_tags'],
'use_date_filename': kwargs['use_date_filename'],
'use_file_dates': kwargs['use_file_dates'],
'exclude': kwargs['exclude'],
'extensions': kwargs['ext'],
'glob': kwargs['glob'],
'dry_run': kwargs['dry_run'],
'interactive': kwargs['interactive'],
}
)
# TODO retrieve collection.opt

View File

@ -308,28 +308,21 @@ class FileIO:
class Paths:
"""Get filtered files paths"""
def __init__(
self,
exclude=None,
extensions=None,
glob='**/*',
interactive=False,
max_deep=None,
):
def __init__(self, filters, interactive=False):
# Options
self.exclude = exclude
self.filters = filters
if extensions and '%media' in extensions:
extensions.remove('%media')
self.extensions = extensions.union(Medias.extensions)
else:
self.extensions = extensions
self.extensions = self.filters['extensions']
if not self.extensions:
self.extensions = set()
elif '%media' in self.extensions:
self.extensions.remove('%media')
self.extensions = self.extensions.union(Medias.extensions)
self.glob = self.filters['glob']
self.glob = glob
self.interactive = interactive
self.log = LOG.getChild(self.__class__.__name__)
self.max_deep = max_deep
self.paths_list = []
# Attributes
@ -382,10 +375,11 @@ class Paths:
if path / '.ordigi' in file_path.parents:
continue
if self.max_deep is not None:
if level > self.max_deep:
if self.filters['max_deep'] is not None:
if level > self.filters['max_deep']:
continue
self.exclude = self.filters['exclude']
if self.exclude:
matched = False
for exclude in self.exclude:
@ -695,34 +689,10 @@ class SortMedias:
class Collection(SortMedias):
"""Class of the media collection."""
def __init__(
self,
root,
album_from_folder=False,
cache=False,
dry_run=False,
exclude=None,
extensions=None,
glob='**/*',
interactive=False,
ignore_tags=None,
use_date_filename=False,
use_file_dates=False,
):
def __init__(self, root, cli_options=None):
# TODO move to cli
cli_options = {
'album_from_folder': album_from_folder,
'cache': cache,
'dry_run': dry_run,
'exclude': exclude,
'extensions': extensions,
'glob': '**/*',
'interactive': interactive,
'ignore_tags': ignore_tags,
'use_date_filename': use_date_filename,
'use_file_dates': use_file_dates,
}
if not cli_options:
cli_options = {}
self.log = LOG.getChild(self.__class__.__name__)
@ -734,34 +704,30 @@ class Collection(SortMedias):
self.root = root
# Get config options
config = self.get_config()
self.opt = config.get_options()
self.opt = self.get_config_options()
# Set client options
for option, value in cli_options.items():
self.opt[option] = value
self._set_cli_option('exclude', exclude)
for section in self.opt:
self.opt[section][option] = value
self.exclude = self.opt['Filters']['exclude']
if not self.exclude:
self.exclude = set()
self.db = CollectionDb(root)
self.fileio = FileIO(self.opt['dry_run'])
self.fileio = FileIO(self.opt['Terminal']['dry_run'])
self.paths = Paths(
self.opt['exclude'],
self.opt['extensions'],
self.opt['glob'],
self.opt['interactive'],
self.opt['max_deep'],
self.opt['Filters'],
interactive=self.opt['Terminal']['interactive'],
)
self.medias = Medias(
self.paths,
root,
self.opt['album_from_folder'],
self.opt['cache'],
self.opt['Exif'],
self.db,
self.opt['interactive'],
self.opt['ignore_tags'],
self.opt['use_date_filename'],
self.opt['use_file_dates'],
self.opt['Terminal']['interactive'],
)
# Features
@ -770,30 +736,37 @@ class Collection(SortMedias):
self.medias,
root,
self.db,
self.opt['dry_run'],
self.opt['interactive'],
self.opt['Terminal']['dry_run'],
self.opt['Terminal']['interactive'],
)
# Attributes
self.summary = Summary(self.root)
self.theme = request.load_theme()
def get_config(self):
def get_config_options(self):
"""Get collection config"""
return Config(self.root.joinpath('.ordigi', 'ordigi.conf'))
config = Config(self.root.joinpath('.ordigi', 'ordigi.conf'))
def _set_cli_option(self, option, cli_option):
return config.get_config_options()
def _set_option(self, section, option, cli_option):
"""if client option is set overwrite collection option value"""
if cli_option:
self.opt['option'] = cli_option
self.opt[section][option] = cli_option
def get_collection_files(self, exclude=True):
if exclude:
exclude = self.paths.exclude
exclude = self.exclude
paths = Paths(
exclude,
interactive=self.opt['interactive'],
filters = {
'exclude': exclude,
'extensions': None,
'glob': '**/*',
'max_deep': None,
},
interactive=self.opt['Terminal']['interactive'],
)
for file_path in paths.get_files(self.root):
yield file_path
@ -905,7 +878,7 @@ class Collection(SortMedias):
"""Remove excluded files in collection"""
# get all files
for file_path in self.get_collection_files(exclude=False):
for exclude in self.paths.exclude:
for exclude in self.exclude:
if fnmatch(file_path, exclude):
self.fileio.remove(file_path)
self.summary.append('remove', True, file_path)
@ -953,7 +926,7 @@ class Collection(SortMedias):
files = os.listdir(directory)
if len(files) == 0 and remove_root:
self.log.info(f"Removing empty folder: {directory}")
if not self.opt['dry_run']:
if not self.opt['Terminal']['dry_run']:
os.rmdir(directory)
self.summary.append('remove', True, directory)
@ -970,13 +943,13 @@ class Collection(SortMedias):
self._init_check_db(loc)
# if path format client option is set overwrite it
self._set_cli_option('path_format', path_format)
self._set_option('Path', 'path_format', path_format)
# Get medias data
subdirs = set()
for src_path, metadata in self.medias.get_metadatas(src_dirs, imp=imp, loc=loc):
# Get the destination path according to metadata
fpath = FPath(path_format, self.opt['day_begins'])
fpath = FPath(path_format, self.opt['Path']['day_begins'])
metadata['file_path'] = fpath.get_path(metadata)
subdirs.add(src_path.parent)

View File

@ -42,15 +42,25 @@ def check_re(getoption):
class Config:
"""Manage config file"""
def __init__(self, conf_path=constants.CONFIG_FILE, conf=None):
self.conf_path = conf_path
if conf is None:
self.conf = self.load_config()
if self.conf == {}:
# Fallback to default config
self.conf_path = constants.CONFIG_FILE
self.conf = self.load_config()
else:
self.conf = conf
self.options = self.set_default_options()
def set_default_options(self) -> dict:
# Initialize with default options
options: dict = {
'Console': {
'dry_run': False,
'interactive': False,
},
'Database': {
'cache': False,
return {
'Exif': {
'album_from_folder': False,
'cache': False,
'ignore_tags': None,
'use_date_filename': False,
'use_file_dates': False,
@ -70,19 +80,12 @@ class Config:
'day_begins': 0,
'path_format': constants.DEFAULT_PATH_FORMAT,
},
'Terminal': {
'dry_run': False,
'interactive': False,
},
}
def __init__(self, conf_path=constants.CONFIG_FILE, conf=None):
self.conf_path = conf_path
if conf is None:
self.conf = self.load_config()
if self.conf == {}:
# Fallback to default config
self.conf_path = constants.CONFIG_FILE
self.conf = self.load_config()
else:
self.conf = conf
def write(self, conf):
with open(self.conf_path, 'w') as conf_file:
conf.write(conf_file)
@ -125,7 +128,7 @@ class Config:
return re.compile(self.conf.get(section, option))
getre = check_re(_getre)
def get_option(self, section, option):
def get_config_option(self, section, option):
bool_options = {
'cache',
'dry_run',
@ -176,16 +179,13 @@ class Config:
return value
def get_options(self) -> dict:
def get_config_options(self) -> dict:
"""Get config options"""
old_options = {}
for section in self.options:
for option in self.options[section]:
# Option is in section
# TODO make a function
value = self.get_option(section, option)
old_options[option] = value
value = self.get_config_option(section, option)
self.options[section][option] = value
return old_options
return self.options

View File

@ -611,13 +611,9 @@ class Medias:
self,
paths,
root,
album_from_folder=False,
cache=False,
exif_options,
db=None,
interactive=False,
ignore_tags=None,
use_date_filename=False,
use_file_dates=False,
):
# Modules
@ -628,13 +624,11 @@ class Medias:
self.root = root
# Options
self.cache = cache
self.album_from_folder = album_from_folder
self.ignore_tags = ignore_tags
self.exif_opt = exif_options
self.album_from_folder = self.exif_opt['album_from_folder']
self.ignore_tags = self.exif_opt['ignore_tags']
self.interactive = interactive
self.log = LOG.getChild(self.__class__.__name__)
self.use_date_filename = use_date_filename
self.use_file_dates = use_file_dates
# Attributes
# List to store medias datas
@ -648,16 +642,17 @@ class Medias:
self.album_from_folder,
self.ignore_tags,
self.interactive,
self.use_date_filename,
self.use_file_dates,
self.exif_opt['use_date_filename'],
self.exif_opt['use_file_dates'],
)
return media
def get_metadata(self, file_path, src_dir, loc=None):
media = self.get_media(file_path, src_dir)
media.get_metadata(self.root, loc, self.db.sqlite,
self.cache)
media.get_metadata(
self.root, loc, self.db.sqlite, self.exif_opt['cache']
)
return media.metadata

View File

@ -137,7 +137,8 @@ class TestCollection:
assert summary.success_table.sum('sort') == nb
def test_sort_files(self, tmp_path):
collection = Collection(tmp_path, album_from_folder=True)
cli_options = {'album_from_folder': True}
collection = Collection(tmp_path, cli_options=cli_options)
loc = GeoLocation()
summary = collection.sort_files([self.src_path],
self.path_format, loc, imp='copy')
@ -150,14 +151,20 @@ class TestCollection:
assert not summary.errors
# check if album value are set
paths = Paths(glob='**/*').get_files(tmp_path)
filters = {
'exclude': None,
'extensions': None,
'glob': '**/*',
'max_deep': None,
}
paths = Paths(filters).get_files(tmp_path)
for file_path in paths:
if '.db' not in str(file_path):
media = Media(file_path, tmp_path, album_from_folder=True)
for value in ReadExif(file_path).get_key_values('album'):
assert value != '' or None
collection = Collection(tmp_path, album_from_folder=True)
collection = Collection(tmp_path, cli_options=cli_options)
# Try to change path format and sort files again
path = '{city}/{%Y}-{name}.%l{ext}'
summary = collection.sort_files([tmp_path],
@ -222,8 +229,13 @@ class TestCollection:
shutil.copyfile(dest_path, src_path)
def test_get_files(self):
exclude={'**/*.dng',}
paths = Paths(exclude=exclude, max_deep=1)
filters = {
'exclude': {'**/*.dng',},
'extensions': None,
'glob': '**/*',
'max_deep': 1,
}
paths = Paths(filters)
paths = list(paths.get_files(self.src_path))
assert len(paths) == 9
assert Path(self.src_path, 'test_exif/photo.dng') not in paths

View File

@ -58,8 +58,8 @@ class TestConfig:
# path = config.get_path_definition()
# assert path == '%u{%Y-%m}/{city}|{city}-{%Y}/{folders[:1]}/{folder}/{%Y-%m-%b-%H-%M-%S}-{basename}.%l{ext}'
def test_get_options(self, conf):
def test_get_config_options(self, conf):
config = Config(conf=conf)
options = config.get_options()
options = config.get_config_options()
assert isinstance(options, dict)
# assert isinstance(options['Path'], dict)