Refactoring collection options (2)
This commit is contained in:
		
							parent
							
								
									0fdf09ea42
								
							
						
					
					
						commit
						27af9bb55e
					
				
							
								
								
									
										17
									
								
								ordigi.conf
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								ordigi.conf
									
									
									
									
									
								
							| @ -1,3 +1,11 @@ | |||||||
|  | [Filters] | ||||||
|  | exclude= ["**/.directory", "**/.DS_Store"] | ||||||
|  | 
 | ||||||
|  | [Geolocation] | ||||||
|  | geocoder=Nominatim | ||||||
|  | prefer_english_names=hjkh | ||||||
|  | timeout=1 | ||||||
|  | 
 | ||||||
| [Path] | [Path] | ||||||
| # day_begins: what hour of the day you want the day to begin (only for | # day_begins: what hour of the day you want the day to begin (only for | ||||||
| # classification purposes).  Defaults at 0 as midnight. Can be | # classification purposes).  Defaults at 0 as midnight. Can be | ||||||
| @ -5,14 +13,7 @@ | |||||||
| # be a number between 0-23') | # be a number between 0-23') | ||||||
| day_begins=4 | day_begins=4 | ||||||
| 
 | 
 | ||||||
|  | # Path format | ||||||
| dirs_path={%Y}/{%m-%b}-{city}-{folder} | dirs_path={%Y}/{%m-%b}-{city}-{folder} | ||||||
| name={%Y%m%d-%H%M%S}-%u{original_name}|%u{basename}.%l{ext} | name={%Y%m%d-%H%M%S}-%u{original_name}|%u{basename}.%l{ext} | ||||||
| 
 | 
 | ||||||
| [Exclusions] |  | ||||||
| path1=**/.directory |  | ||||||
| path2=**/.DS_Store |  | ||||||
| 
 |  | ||||||
| [Geolocation] |  | ||||||
| geocoder=Nominatim |  | ||||||
| prefer_english_names=False |  | ||||||
| # timeout=1 |  | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ import sys | |||||||
| import click | import click | ||||||
| 
 | 
 | ||||||
| from ordigi import constants, log, LOG | from ordigi import constants, log, LOG | ||||||
| from ordigi.config import Config |  | ||||||
| from ordigi.collection import Collection | from ordigi.collection import Collection | ||||||
| from ordigi.geolocation import GeoLocation | from ordigi.geolocation import GeoLocation | ||||||
| 
 | 
 | ||||||
| @ -116,13 +115,6 @@ def add_options(options): | |||||||
|     return _add_options |     return _add_options | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def _get_exclude(opt, exclude): |  | ||||||
|     # if no exclude list was passed in we check if there's a config |  | ||||||
|     if len(exclude) == 0: |  | ||||||
|         exclude = opt['exclude'] |  | ||||||
|     return set(exclude) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def _get_paths(paths, root): | def _get_paths(paths, root): | ||||||
|     root = Path(root).expanduser().absolute() |     root = Path(root).expanduser().absolute() | ||||||
|     if not paths: |     if not paths: | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ from pathlib import Path, PurePath | |||||||
| import inquirer | import inquirer | ||||||
| 
 | 
 | ||||||
| from ordigi import LOG | from ordigi import LOG | ||||||
|  | from ordigi.config import Config | ||||||
| from ordigi.database import Sqlite | from ordigi.database import Sqlite | ||||||
| from ordigi.media import Medias | from ordigi.media import Medias | ||||||
| from ordigi.images import Image, Images | from ordigi.images import Image, Images | ||||||
| @ -220,6 +221,9 @@ class FPath: | |||||||
|         Returns file path. |         Returns file path. | ||||||
|         """ |         """ | ||||||
|         path_format = self.path_format |         path_format = self.path_format | ||||||
|  | 
 | ||||||
|  |         # Each element in the list represents a folder. | ||||||
|  |         # Fallback folders are supported and are nested lists. | ||||||
|         path = [] |         path = [] | ||||||
|         path_parts = path_format.split('/') |         path_parts = path_format.split('/') | ||||||
|         for path_part in path_parts: |         for path_part in path_parts: | ||||||
| @ -706,50 +710,58 @@ class Collection(SortMedias): | |||||||
|         use_file_dates=False, |         use_file_dates=False, | ||||||
|     ): |     ): | ||||||
| 
 | 
 | ||||||
|         day_begins=0 |         # TODO move to cli | ||||||
|         max_deep=None |         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, | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         # Options |  | ||||||
|         self.day_begins = day_begins |  | ||||||
|         self.log = LOG.getChild(self.__class__.__name__) |         self.log = LOG.getChild(self.__class__.__name__) | ||||||
|         self.glob = glob |  | ||||||
| 
 | 
 | ||||||
|         # Check if collection path is valid |         # Check if collection path is valid | ||||||
|         if not root.exists(): |         if not root.exists(): | ||||||
|             self.log.error(f'Collection path {root} does not exist') |             self.log.error(f'Collection path {root} does not exist') | ||||||
|             sys.exit(1) |             sys.exit(1) | ||||||
| 
 | 
 | ||||||
|         # def get_collection_config(root): |         self.root = root | ||||||
|         #     return Config(root.joinpath('.ordigi', 'ordigi.conf')) |  | ||||||
| 
 | 
 | ||||||
|         # TODO Collection options |         # Get config options | ||||||
|         # config = get_collection_config(root) |         config = self.get_config() | ||||||
|         # opt = config.get_options() |         self.opt = config.get_options() | ||||||
|         # exclude = _get_exclude(opt, kwargs['exclude']) | 
 | ||||||
|         # path_format = opt['path_format'] |         # Set client options | ||||||
|         # if kwargs['path_format']: |         for option, value in cli_options.items(): | ||||||
|         #     path_format = kwargs['path_format'] |             self.opt[option] = value | ||||||
|  |         self._set_cli_option('exclude', exclude) | ||||||
| 
 | 
 | ||||||
|         self.db = CollectionDb(root) |         self.db = CollectionDb(root) | ||||||
|         self.fileio = FileIO(dry_run) |         self.fileio = FileIO(self.opt['dry_run']) | ||||||
|         self.paths = Paths( |         self.paths = Paths( | ||||||
|             exclude, |             self.opt['exclude'], | ||||||
|             extensions, |             self.opt['extensions'], | ||||||
|             glob, |             self.opt['glob'], | ||||||
|             interactive, |             self.opt['interactive'], | ||||||
|             max_deep, |             self.opt['max_deep'], | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         self.medias = Medias( |         self.medias = Medias( | ||||||
|             self.paths, |             self.paths, | ||||||
|             root, |             root, | ||||||
|             album_from_folder, |             self.opt['album_from_folder'], | ||||||
|             cache, |             self.opt['cache'], | ||||||
|             self.db, |             self.db, | ||||||
|             interactive, |             self.opt['interactive'], | ||||||
|             ignore_tags, |             self.opt['ignore_tags'], | ||||||
|             use_date_filename, |             self.opt['use_date_filename'], | ||||||
|             use_file_dates, |             self.opt['use_file_dates'], | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         # Features |         # Features | ||||||
| @ -758,21 +770,30 @@ class Collection(SortMedias): | |||||||
|             self.medias, |             self.medias, | ||||||
|             root, |             root, | ||||||
|             self.db, |             self.db, | ||||||
|             dry_run, |             self.opt['dry_run'], | ||||||
|             interactive, |             self.opt['interactive'], | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         # Attributes |         # Attributes | ||||||
|         self.summary = Summary(self.root) |         self.summary = Summary(self.root) | ||||||
|         self.theme = request.load_theme() |         self.theme = request.load_theme() | ||||||
| 
 | 
 | ||||||
|  |     def get_config(self): | ||||||
|  |         """Get collection config""" | ||||||
|  |         return Config(self.root.joinpath('.ordigi', 'ordigi.conf')) | ||||||
|  | 
 | ||||||
|  |     def _set_cli_option(self, option, cli_option): | ||||||
|  |         """if client option is set overwrite collection option value""" | ||||||
|  |         if cli_option: | ||||||
|  |             self.opt['option'] = cli_option | ||||||
|  | 
 | ||||||
|     def get_collection_files(self, exclude=True): |     def get_collection_files(self, exclude=True): | ||||||
|         if exclude: |         if exclude: | ||||||
|             exclude = self.paths.exclude |             exclude = self.paths.exclude | ||||||
| 
 | 
 | ||||||
|         paths = Paths( |         paths = Paths( | ||||||
|             exclude, |             exclude, | ||||||
|             interactive=self.interactive, |             interactive=self.opt['interactive'], | ||||||
|         ) |         ) | ||||||
|         for file_path in paths.get_files(self.root): |         for file_path in paths.get_files(self.root): | ||||||
|             yield file_path |             yield file_path | ||||||
| @ -932,7 +953,7 @@ class Collection(SortMedias): | |||||||
|         files = os.listdir(directory) |         files = os.listdir(directory) | ||||||
|         if len(files) == 0 and remove_root: |         if len(files) == 0 and remove_root: | ||||||
|             self.log.info(f"Removing empty folder: {directory}") |             self.log.info(f"Removing empty folder: {directory}") | ||||||
|             if not self.dry_run: |             if not self.opt['dry_run']: | ||||||
|                 os.rmdir(directory) |                 os.rmdir(directory) | ||||||
|             self.summary.append('remove', True, directory) |             self.summary.append('remove', True, directory) | ||||||
| 
 | 
 | ||||||
| @ -948,11 +969,14 @@ class Collection(SortMedias): | |||||||
|         # Check db |         # Check db | ||||||
|         self._init_check_db(loc) |         self._init_check_db(loc) | ||||||
| 
 | 
 | ||||||
|  |         # if path format client option is set overwrite it | ||||||
|  |         self._set_cli_option('path_format', path_format) | ||||||
|  | 
 | ||||||
|         # Get medias data |         # Get medias data | ||||||
|         subdirs = set() |         subdirs = set() | ||||||
|         for src_path, metadata in self.medias.get_metadatas(src_dirs, imp=imp, loc=loc): |         for src_path, metadata in self.medias.get_metadatas(src_dirs, imp=imp, loc=loc): | ||||||
|             # Get the destination path according to metadata |             # Get the destination path according to metadata | ||||||
|             fpath = FPath(path_format, self.day_begins) |             fpath = FPath(path_format, self.opt['day_begins']) | ||||||
|             metadata['file_path'] = fpath.get_path(metadata) |             metadata['file_path'] = fpath.get_path(metadata) | ||||||
|             subdirs.add(src_path.parent) |             subdirs.add(src_path.parent) | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										191
									
								
								ordigi/config.py
									
									
									
									
									
								
							
							
						
						
									
										191
									
								
								ordigi/config.py
									
									
									
									
									
								
							| @ -1,15 +1,80 @@ | |||||||
|  | import json | ||||||
|  | import re | ||||||
|  | 
 | ||||||
| from configparser import RawConfigParser | from configparser import RawConfigParser | ||||||
| from os import path | from os import path | ||||||
| from ordigi import constants | from ordigi import constants | ||||||
| from geopy.geocoders import options as gopt | from geopy.geocoders import options as gopt | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # TODO make one method??? | ||||||
|  | def check_option(getoption): | ||||||
|  |     """Check option type int or boolean""" | ||||||
|  |     try: | ||||||
|  |         getoption | ||||||
|  |     except ValueError as e: | ||||||
|  |         # TODO | ||||||
|  |         return None | ||||||
|  |     else: | ||||||
|  |         return getoption | ||||||
|  | 
 | ||||||
|  | def check_json(getoption): | ||||||
|  |     """Check if json string is valid""" | ||||||
|  |     try: | ||||||
|  |         getoption | ||||||
|  |     except json.JSONDecodeError as e: | ||||||
|  |         # TODO | ||||||
|  |         return None | ||||||
|  |     else: | ||||||
|  |         return getoption | ||||||
|  | 
 | ||||||
|  | def check_re(getoption): | ||||||
|  |     """Check if regex string is valid""" | ||||||
|  |     try: | ||||||
|  |         getoption | ||||||
|  |     except re.error as e: | ||||||
|  |         # TODO | ||||||
|  |         return None | ||||||
|  |     else: | ||||||
|  |         return getoption | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class Config: | class Config: | ||||||
|     """Manage config file""" |     """Manage config file""" | ||||||
| 
 | 
 | ||||||
|     def __init__(self, conf_path=constants.CONFIG_FILE, conf={}): |     # Initialize with default options | ||||||
|  |     options: dict = { | ||||||
|  |         'Console': { | ||||||
|  |             'dry_run': False, | ||||||
|  |             'interactive': False, | ||||||
|  |         }, | ||||||
|  |         'Database': { | ||||||
|  |             'cache': False, | ||||||
|  |             'album_from_folder': False, | ||||||
|  |             'ignore_tags': None, | ||||||
|  |             'use_date_filename': False, | ||||||
|  |             'use_file_dates': False, | ||||||
|  |         }, | ||||||
|  |         'Filters': { | ||||||
|  |             'exclude': None, | ||||||
|  |             'extensions': None, | ||||||
|  |             'glob': '**/*', | ||||||
|  |             'max_deep': None, | ||||||
|  |         }, | ||||||
|  |         'Geolocation': { | ||||||
|  |             'geocoder': constants.DEFAULT_GEOCODER, | ||||||
|  |             'prefer_english_names': False, | ||||||
|  |             'timeout': gopt.default_timeout, | ||||||
|  |         }, | ||||||
|  |         'Path': { | ||||||
|  |             'day_begins': 0, | ||||||
|  |             'path_format': constants.DEFAULT_PATH_FORMAT, | ||||||
|  |         }, | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     def __init__(self, conf_path=constants.CONFIG_FILE, conf=None): | ||||||
|         self.conf_path = conf_path |         self.conf_path = conf_path | ||||||
|         if conf == {}: |         if conf is None: | ||||||
|             self.conf = self.load_config() |             self.conf = self.load_config() | ||||||
|             if self.conf == {}: |             if self.conf == {}: | ||||||
|                 # Fallback to default config |                 # Fallback to default config | ||||||
| @ -33,66 +98,94 @@ class Config: | |||||||
|         conf.read(self.conf_path) |         conf.read(self.conf_path) | ||||||
|         return conf |         return conf | ||||||
| 
 | 
 | ||||||
|     def get_option(self, option, section): |     def is_option(self, section, option): | ||||||
| 
 |         """Get ConfigParser options""" | ||||||
|         if section in self.conf and option in self.conf[section]: |         if section in self.conf and option in self.conf[section]: | ||||||
|             return self.conf[section][option] |             return True | ||||||
| 
 | 
 | ||||||
|         return False |         return False | ||||||
| 
 | 
 | ||||||
|     def get_path_definition(self): |     @check_option | ||||||
|         """Returns a list of folder definitions. |     def _getboolean(self, section, option): | ||||||
|  |         return self.conf.getboolean(section, option) | ||||||
|  |     getboolean = check_option(_getboolean) | ||||||
| 
 | 
 | ||||||
|         Each element in the list represents a folder. |     @check_option | ||||||
|         Fallback folders are supported and are nested lists. |     def _getint(self, section, option): | ||||||
|  |         return self.conf.getint(section, option) | ||||||
|  |     getint = check_option(_getint) | ||||||
| 
 | 
 | ||||||
|         :returns: string |     @check_json | ||||||
|         """ |     def _getjson(self, section, option): | ||||||
|  |         return json.loads(self.conf.get(section, option)) | ||||||
|  |     getjson = check_json(_getjson) | ||||||
| 
 | 
 | ||||||
|         if 'Path' in self.conf: |     @check_re | ||||||
|             if 'format' in self.conf['Path']: |     def _getre(self, section, option): | ||||||
|                 return self.conf['Path']['format'] |         return re.compile(self.conf.get(section, option)) | ||||||
|             elif 'dirs_path' and 'name' in self.conf['Path']: |     getre = check_re(_getre) | ||||||
|                 return self.conf['Path']['dirs_path'] + '/' + self.conf['Path']['name'] |  | ||||||
| 
 | 
 | ||||||
|         return constants.DEFAULT_PATH_FORMAT |     def get_option(self, section, option): | ||||||
|  |         bool_options = { | ||||||
|  |             'cache', | ||||||
|  |             'dry_run', | ||||||
|  |             'prefer_english_names', | ||||||
|  |             'album_from_folder', | ||||||
|  |             'interactive', | ||||||
|  |             'use_date_filename', | ||||||
|  |             'use_file_dates', | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|     def get_options(self): |         int_options = { | ||||||
|         """Get config options |             'day_begins', | ||||||
|         :returns: dict |             'max_deep', | ||||||
|         """ |             'timeout', | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         options = {} |         string_options = { | ||||||
|         geocoder = self.get_option('geocoder', 'Geolocation') |             'glob', | ||||||
|         if geocoder and geocoder in ('Nominatim',): |             'geocoder', | ||||||
|             options['geocoder'] = geocoder |         } | ||||||
|         else: |  | ||||||
|             options['geocoder'] = constants.DEFAULT_GEOCODER |  | ||||||
| 
 | 
 | ||||||
|         prefer_english_names = self.get_option('prefer_english_names', 'Geolocation') |         multi_options = { | ||||||
|         if prefer_english_names: |             'exclude', | ||||||
|             options['prefer_english_names'] = bool(prefer_english_names) |             'extensions', | ||||||
|         else: |             'ignore_tags', | ||||||
|             options['prefer_english_names'] = False |         } | ||||||
| 
 | 
 | ||||||
|         timeout = self.get_option('timeout', 'Geolocation') |         value = self.options[section][option] | ||||||
|         if timeout: |         if self.is_option(section, option): | ||||||
|             options['timeout'] = timeout |             if option in bool_options: | ||||||
|         else: |                 return self.getboolean(section, option) | ||||||
|             options['timeout'] = gopt.default_timeout |             if option in int_options: | ||||||
|  |                 return self.getint(section, option) | ||||||
|  |             if option == 'geocoder' and value in ('Nominatim',): | ||||||
|  |                 return self.conf[section][option] | ||||||
|  |             if option == 'glob': | ||||||
|  |                 return self.conf[section][option] | ||||||
|  |             if option == 'path_format': | ||||||
|  |                 return self.getre(section, option) | ||||||
|  |             if option in multi_options: | ||||||
|  |                 return set(self.getjson(section, option)) | ||||||
| 
 | 
 | ||||||
|         options['path_format'] = self.get_path_definition() |             return value | ||||||
| 
 | 
 | ||||||
|         options['day_begins'] = 0 |         if self.is_option('Path', 'name') and self.is_option('dirs_path', option): | ||||||
|         options['max_deep'] = None |             # Path format is split in two parts | ||||||
|         if 'Path' in self.conf: |             value = self.conf['Path']['dirs_path'] + '/' + self.conf['Path']['name'] | ||||||
|             if 'day_begins' in self.conf['Path']: |  | ||||||
|                 options['day_begins'] = int(self.conf['Path']['day_begins']) |  | ||||||
|             if 'max_deep' in self.conf['Path']: |  | ||||||
|                 options['max_deep'] = int(self.conf['Path']['max_deep']) |  | ||||||
| 
 | 
 | ||||||
|         options['exclude'] = [] |         return value | ||||||
|         if 'Exclusions' in self.conf: |  | ||||||
|             options['exclude'] = [value for key, value in self.conf.items('Exclusions')] |  | ||||||
| 
 | 
 | ||||||
|         return options |     def get_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 | ||||||
|  |                 self.options[section][option] = value | ||||||
|  | 
 | ||||||
|  |         return old_options | ||||||
|  | |||||||
| @ -50,11 +50,16 @@ class TestConfig: | |||||||
|             config = Config(conf_path) |             config = Config(conf_path) | ||||||
|         assert e.typename == 'MissingSectionHeaderError' |         assert e.typename == 'MissingSectionHeaderError' | ||||||
| 
 | 
 | ||||||
|     def test_get_path_definition(self, conf): |     # def test_get_path_definition(self, conf): | ||||||
|         """ |     #     """ | ||||||
|         Get path definition from config |     #     Get path definition from config | ||||||
|         """ |     #     """ | ||||||
|         config = Config(conf=conf) |     #     config = Config(conf=conf) | ||||||
|         path = config.get_path_definition() |     #     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}' |     #     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): | ||||||
|  |         config = Config(conf=conf) | ||||||
|  |         options = config.get_options() | ||||||
|  |         assert isinstance(options, dict) | ||||||
|  |         # assert isinstance(options['Path'], dict) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user