Add support for a %custom placeholder for more complex folder names than %location or %date provide #279 (#283)
This commit is contained in:
parent
91bf181575
commit
69937ca1a3
|
@ -9,3 +9,4 @@ build/**
|
|||
**/*.dng
|
||||
**/*.nef
|
||||
**/*.rw2
|
||||
env/**
|
||||
|
|
|
@ -194,6 +194,12 @@ month=%m
|
|||
year=%Y
|
||||
full_path=%year-%month/%location
|
||||
# -> 2015-12/Sunnyvale, California
|
||||
|
||||
date=%Y
|
||||
location=%city, %state
|
||||
custom=%date %album
|
||||
full_path=%location/%custom
|
||||
# -> Sunnyvale, California/2015 Birthday Party
|
||||
```
|
||||
|
||||
#### Using fallback folders
|
||||
|
@ -239,6 +245,7 @@ In addition to my built-in and date placeholders you can combine them into a sin
|
|||
|
||||
* `%location` can be used to combine multiple values of `%city`, `%state` and `%country`. For example, `location=%city, %state` would result in folder names like `Sunnyvale, California`.
|
||||
* `%date` can be used to combine multiple values from [the standard Python time directives](https://docs.python.org/2/library/datetime.html#strftime-and-strptime-behavior). For example, `date=%Y-%m` would result in folder names like `2015-12`.
|
||||
* `%custom` can be used to combine multiple values from anything else. Think of it as a catch-all when `%location` and `%date` don't meet your needs.
|
||||
|
||||
### Reorganize by changing location and dates
|
||||
|
||||
|
|
|
@ -218,7 +218,7 @@ class FileSystem(object):
|
|||
def get_folder_path(self, metadata):
|
||||
"""Given a media's metadata this function returns the folder path as a string.
|
||||
|
||||
:param metadata dict: Metadata dictionary.
|
||||
:param dict metadata: Metadata dictionary.
|
||||
:returns: str
|
||||
"""
|
||||
path_parts = self.get_folder_path_definition()
|
||||
|
@ -232,34 +232,70 @@ class FileSystem(object):
|
|||
# Unknown Location - when neither an album nor location exist
|
||||
for this_part in path_part:
|
||||
part, mask = this_part
|
||||
if part in ('date', 'day', 'month', 'year'):
|
||||
path.append(
|
||||
time.strftime(mask, metadata['date_taken'])
|
||||
)
|
||||
this_path = self.get_dynamic_path(part, mask, metadata)
|
||||
if this_path:
|
||||
path.append(this_path.strip())
|
||||
# We break as soon as we have a value to append
|
||||
# Else we continue for fallbacks
|
||||
break
|
||||
elif part in ('location', 'city', 'state', 'country'):
|
||||
place_name = geolocation.place_name(
|
||||
metadata['latitude'],
|
||||
metadata['longitude']
|
||||
)
|
||||
|
||||
location_parts = re.findall('(%[^%]+)', mask)
|
||||
parsed_folder_name = self.parse_mask_for_location(
|
||||
mask,
|
||||
location_parts,
|
||||
place_name,
|
||||
)
|
||||
path.append(parsed_folder_name)
|
||||
break
|
||||
elif part in ('album', 'camera_make', 'camera_model'):
|
||||
if metadata[part]:
|
||||
path.append(metadata[part])
|
||||
break
|
||||
elif part.startswith('"') and part.endswith('"'):
|
||||
path.append(part[1:-1])
|
||||
|
||||
return os.path.join(*path)
|
||||
|
||||
def get_dynamic_path(self, part, mask, metadata):
|
||||
"""Parse a specific folder's name given a mask and metadata.
|
||||
|
||||
:param part: Name of the part as defined in the path (i.e. date from %date)
|
||||
:param mask: Mask representing the template for the path (i.e. %city %state
|
||||
:param metadata: Metadata dictionary.
|
||||
:returns: str
|
||||
"""
|
||||
|
||||
# Each part has its own custom logic and we evaluate a single part and return
|
||||
# the evaluated string.
|
||||
if part in ('custom'):
|
||||
custom_parts = re.findall('(%[a-z_]+)', mask)
|
||||
folder = mask
|
||||
for i in custom_parts:
|
||||
folder = folder.replace(
|
||||
i,
|
||||
self.get_dynamic_path(i[1:], i, metadata)
|
||||
)
|
||||
return folder
|
||||
elif part in ('date'):
|
||||
config = load_config()
|
||||
# If Directory is in the config we assume full_path and its
|
||||
# corresponding values (date, location) are also present
|
||||
config_directory = self.default_folder_path_definition
|
||||
if('Directory' in config):
|
||||
config_directory = config['Directory']
|
||||
date_mask = ''
|
||||
if 'date' in config_directory:
|
||||
date_mask = config_directory['date']
|
||||
return time.strftime(date_mask, metadata['date_taken'])
|
||||
elif part in ('day', 'month', 'year'):
|
||||
return time.strftime(mask, metadata['date_taken'])
|
||||
elif part in ('location', 'city', 'state', 'country'):
|
||||
place_name = geolocation.place_name(
|
||||
metadata['latitude'],
|
||||
metadata['longitude']
|
||||
)
|
||||
|
||||
location_parts = re.findall('(%[^%]+)', mask)
|
||||
parsed_folder_name = self.parse_mask_for_location(
|
||||
mask,
|
||||
location_parts,
|
||||
place_name,
|
||||
)
|
||||
return parsed_folder_name
|
||||
elif part in ('album', 'camera_make', 'camera_model'):
|
||||
if metadata[part]:
|
||||
return metadata[part]
|
||||
elif part.startswith('"') and part.endswith('"'):
|
||||
# Fallback string
|
||||
return part[1:-1]
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def parse_mask_for_location(self, mask, location_parts, place_name):
|
||||
"""Takes a mask for a location and interpolates the actual place names.
|
||||
|
||||
|
|
|
@ -279,6 +279,54 @@ full_path=%date
|
|||
|
||||
assert path == os.path.join('2015'), path
|
||||
|
||||
@mock.patch('elodie.config.config_file', '%s/config.ini-combined-date-and-album' % gettempdir())
|
||||
def test_get_folder_path_with_combined_date_and_album():
|
||||
# gh-239
|
||||
with open('%s/config.ini-combined-date-and-album' % gettempdir(), 'w') as f:
|
||||
f.write("""
|
||||
[Directory]
|
||||
date=%Y-%m-%b
|
||||
custom=%date %album
|
||||
full_path=%custom
|
||||
""")
|
||||
if hasattr(load_config, 'config'):
|
||||
del load_config.config
|
||||
filesystem = FileSystem()
|
||||
media = Photo(helper.get_file('with-album.jpg'))
|
||||
path = filesystem.get_folder_path(media.get_metadata())
|
||||
if hasattr(load_config, 'config'):
|
||||
del load_config.config
|
||||
|
||||
assert path == '2015-12-Dec Test Album', path
|
||||
|
||||
@mock.patch('elodie.config.config_file', '%s/config.ini-combined-date-album-location-fallback' % gettempdir())
|
||||
def test_get_folder_path_with_album_and_location_fallback():
|
||||
# gh-279
|
||||
with open('%s/config.ini-combined-date-album-location-fallback' % gettempdir(), 'w') as f:
|
||||
f.write("""
|
||||
[Directory]
|
||||
date=%Y-%m-%b
|
||||
custom=%album
|
||||
full_path=%custom|%city
|
||||
""")
|
||||
if hasattr(load_config, 'config'):
|
||||
del load_config.config
|
||||
filesystem = FileSystem()
|
||||
|
||||
# Test with no location
|
||||
media = Photo(helper.get_file('plain.jpg'))
|
||||
path_plain = filesystem.get_folder_path(media.get_metadata())
|
||||
|
||||
# Test with City
|
||||
media = Photo(helper.get_file('with-location.jpg'))
|
||||
path_city = filesystem.get_folder_path(media.get_metadata())
|
||||
if hasattr(load_config, 'config'):
|
||||
del load_config.config
|
||||
|
||||
assert path_plain == 'Unknown Location', path_plain
|
||||
assert path_city == 'Sunnyvale', path_city
|
||||
|
||||
|
||||
def test_get_folder_path_with_int_in_source_path():
|
||||
# gh-239
|
||||
filesystem = FileSystem()
|
||||
|
|
Loading…
Reference in New Issue