From 002404d82cb41489e3a051dcc6bc79b0b960b03a Mon Sep 17 00:00:00 2001 From: Cedric Leporcq Date: Mon, 18 Apr 2022 08:57:47 +0200 Subject: [PATCH] Change path format syntax --- README.md | 6 ++--- ordigi.conf | 4 +-- ordigi/collection.py | 53 +++++++++++++++++++++------------------- ordigi/constants.py | 4 +-- tests/conftest.py | 4 +-- tests/test_collection.py | 38 ++++++++++++++-------------- tests/test_config.py | 6 ++--- 7 files changed, 59 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 2339cd1..e07ae7c 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,8 @@ pattern. Default folder structure: ``` -dirs_path={%Y}/{%m-%b}-{city}-{folder} -name={%Y%m%d-%H%M%S}-%u{original_name}.%l{ext}|{%Y%m%d-%H%M%S}-%u{basename}.%l{ext} +dirs_path=<%Y>/<%m-%b>-- +name=<%Y%m%d-%H%M%S>-<%u|%u>.%l ``` Example folder structure: @@ -115,7 +115,7 @@ Example folder structure: │ └── 20150927_014138-_dsc8705.nef ``` -The folder structure use standard unix path separator (`/`). Fallback folder part can be optionally specified using a pipe (`|`) separator. +The folder structure use standard unix path separator (`/`). Fallback folder part can be optionally specified using a pipe separator and brackets (`<.*|.*>`). Valid keywords are: diff --git a/ordigi.conf b/ordigi.conf index 26b8db5..3b7ee2d 100644 --- a/ordigi.conf +++ b/ordigi.conf @@ -14,5 +14,5 @@ timeout=1 day_begins=4 # Path format -dirs_path={%Y}/{%m-%b}-{city}-{folder} -name={%Y%m%d-%H%M%S}-(%u{original_name}|%u{name}).%l{ext} +dirs_path=<%Y>/<%m-%b>-- +name=<%Y%m%d-%H%M%S>-<%u|%u>.%l diff --git a/ordigi/collection.py b/ordigi/collection.py index 6ffdef5..b6ba782 100644 --- a/ordigi/collection.py +++ b/ordigi/collection.py @@ -38,22 +38,22 @@ class FPath: def get_items(self): """Return features items of Fpath class""" return { - 'album': '{album}', - 'stem': '{stem}', - 'camera_make': '{camera_make}', - 'camera_model': '{camera_model}', - 'city': '{city}', - 'custom': r'{".*"}', - 'country': '{country}', - 'date': r'{(%[a-zA-Z][^a-zA-Z]*){1,8}}', # search for date format string - 'ext': '{ext}', - 'folder': '{folder}', - 'folders': r'{folders(\[[0-9:]{0,3}\])?}', - 'location': '{location}', - 'name': '{name}', - 'original_name': '{original_name}', - 'state': '{state}', - 'title': '{title}', + 'album': '', + 'stem': '', + 'camera_make': '', + 'camera_model': '', + 'city': '', + 'custom': r'<".*">', + 'country': '', + 'date': r'<(%[a-zA-Z][^a-zA-Z]*){1,8}>', # search for date format string + 'ext': '', + 'folder': '', + 'folders': r'', + 'location': '', + 'name': '', + 'original_name': '', + 'state': '', + 'title': '', } def get_early_morning_photos_date(self, date, mask): @@ -140,6 +140,9 @@ class FPath: part = stem for regex in utils.get_date_regex().values(): part = re.sub(regex, '', part) + # Delete separator + if re.search('^[-_ .]', part): + part = part[1:] elif item == 'date': date = metadata['date_media'] # early morning photos can be grouped with previous day @@ -194,7 +197,7 @@ class FPath: for item, regex in self.items.items(): matched = re.search(regex, this_part) if matched: - self.log.debug(f'item: {item}, mask: {matched.group()[1:-1]}') + self.log.debug(f'item: {item}, mask: <matched.group()[1:-1]>') part = self.get_part(item, matched.group()[1:-1], metadata) self.log.debug(f'part: {part}') @@ -213,16 +216,16 @@ class FPath: this_part = self._substitute(regex, part, this_part) # remove alternate parts inside bracket separated by | - regex = r'[-_ .]?\(\|\)' + regex = r'[-_ .]?\<\|\>' if re.search(regex, this_part): # Delete substitute part and separator if empty this_part = re.sub(regex, '', this_part) - elif re.search(r'\(.*\)', this_part): - regex = r'\(\|' + elif re.search(r'\<.*\>', this_part): + regex = r'\<\|' this_part = re.sub(regex, '', this_part) - regex = r'\|.*\)' + regex = r'\|.*\>' this_part = re.sub(regex, '', this_part) - regex = r'\)' + regex = r'\>' this_part = re.sub(regex, '', this_part) # Delete separator char at the begining of the string if any: @@ -238,7 +241,7 @@ class FPath: def get_path(self, metadata: dict) -> list: """ - path_format: {%Y-%d-%m}/%u{city}/{album} + path_format: <%Y-%d-%m>/%u<city>/<album> Returns file path. """ path_format = self.path_format @@ -252,9 +255,9 @@ class FPath: if part != '': # Check if all masks are substituted - if True in [c in part for c in '{}']: + if True in [c in part for c in '<>']: self.log.error( - f"Format path part invalid: {this_part}" + f"Format path part invalid: {part}" ) sys.exit(1) diff --git a/ordigi/constants.py b/ordigi/constants.py index 8f9392e..fbc277a 100644 --- a/ordigi/constants.py +++ b/ordigi/constants.py @@ -23,8 +23,8 @@ def get_config_dir(name): APPLICATION_DIRECTORY = get_config_dir('ordigi') -DEFAULT_PATH = '{%Y-%m-%b}/{album}|{city}' -DEFAULT_NAME = '{%Y-%m-%d_%H-%M-%S}-{name}-{title}.%l{ext}' +DEFAULT_PATH = '<%Y-%m-%b>/<album>|<city>' +DEFAULT_NAME = '<%Y-%m-%d_%H-%M-%S>-<name>-<title>.%l<ext>' DEFAULT_PATH_FORMAT = DEFAULT_PATH + '/' + DEFAULT_NAME DEFAULT_GEOCODER = 'Nominatim' diff --git a/tests/conftest.py b/tests/conftest.py index 06af8db..9226fe5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -60,8 +60,8 @@ def conf_path(): conf = RawConfigParser() conf['Path'] = { 'day_begins': '4', - 'dirs_path':'%u{%Y-%m}/{city}|{city}-{%Y}/{folders[:1]}/{folder}', - 'name':'{%Y-%m-%b-%H-%M-%S}-{basename}.%l{ext}' + 'dirs_path':'%u<%Y-%m>/<city>|<city>-<%Y>/<folders[:1]>/<folder>', + 'name':'<%Y-%m-%b-%H-%M-%S>-<basename>.%l<ext>' } conf['Geolocation'] = { 'geocoder': 'Nominatium' diff --git a/tests/test_collection.py b/tests/test_collection.py index 0d3b6ef..bbb35ab 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -33,24 +33,24 @@ class TestFPath: # Item to search for: items = fpath.get_items() masks = [ - '{album}', - '{basename}', - '{camera_make}', - '{camera_model}', - '{city}', - '{"custom"}', - '{country}', - '{ext}', - '{folder}', - '{folders[1:3]}', - '{location}', - '{name}', - '{original_name}', - '{state}', - '{title}', - '{%Y-%m-%d}', - '{%Y-%m-%d_%H-%M-%S}', - '{%Y-%m-%b}' + '<album>', + '<basename>', + '<camera_make>', + '<camera_model>', + '<city>', + '<"custom">', + '<country>', + '<ext>', + '<folder>', + '<folders[1:3]>', + '<location>', + '<name>', + '<original_name>', + '<state>', + '<title>', + '<%Y-%m-%d>', + '<%Y-%m-%d_%H-%M-%S>', + '<%Y-%m-%b>' ] for file_path in self.file_paths: @@ -165,7 +165,7 @@ class TestCollection: collection = Collection(tmp_path, cli_options=cli_options) # Try to change path format and sort files again - path_format = 'test_exif/{city}/{%Y}-{name}.%l{ext}' + path_format = 'test_exif/<city>/<%Y>-<name>.%l<ext>' summary = collection.sort_files([tmp_path], path_format, loc) self.assert_sort(summary, 27) diff --git a/tests/test_config.py b/tests/test_config.py index ce3c801..c9415e0 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -32,8 +32,8 @@ class TestConfig: Read files from config and return variables """ # test valid config file - assert conf['Path']['dirs_path'] == '%u{%Y-%m}/{city}|{city}-{%Y}/{folders[:1]}/{folder}' - assert conf['Path']['name'] == '{%Y-%m-%b-%H-%M-%S}-{basename}.%l{ext}' + assert conf['Path']['dirs_path'] == '%u<%Y-%m>/<city>|<city>-<%Y>/<folders[:1]>/<folder>' + assert conf['Path']['name'] == '<%Y-%m-%b-%H-%M-%S>-<basename>.%l<ext>' assert conf['Path']['day_begins'] == '4' assert conf['Geolocation']['geocoder'] == 'Nominatium' @@ -56,7 +56,7 @@ class TestConfig: # """ # config = Config(conf=conf) # 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_config_options(self, conf): config = Config(conf=conf)