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
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -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,11 +232,47 @@ 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
 | 
			
		||||
        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'],
 | 
			
		||||
@ -249,16 +285,16 @@ class FileSystem(object):
 | 
			
		||||
                location_parts,
 | 
			
		||||
                place_name,
 | 
			
		||||
            )
 | 
			
		||||
                    path.append(parsed_folder_name)
 | 
			
		||||
                    break
 | 
			
		||||
            return parsed_folder_name
 | 
			
		||||
        elif part in ('album', 'camera_make', 'camera_model'):
 | 
			
		||||
            if metadata[part]:
 | 
			
		||||
                        path.append(metadata[part])
 | 
			
		||||
                        break
 | 
			
		||||
                return metadata[part]
 | 
			
		||||
        elif part.startswith('"') and part.endswith('"'):
 | 
			
		||||
                    path.append(part[1:-1])
 | 
			
		||||
            # Fallback string
 | 
			
		||||
            return part[1:-1]
 | 
			
		||||
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
        return os.path.join(*path)
 | 
			
		||||
 | 
			
		||||
    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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user