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. :returns: str """ path_parts = self.get_folder_path_definition() path = [] for path_part in path_parts: # We support fallback values so that # 'album|city|"Unknown Location" # %album|%city|"Unknown Location" results in # My Album - when an album exists # Sunnyvale - when no album exists but a city exists # 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'])) 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 == 'dbcomplex': maskT, maskL = mask.split('~') tcomp = time.strftime(maskT, metadata['date_taken']) place_name = geolocation.place_name( metadata['latitude'], metadata['longitude']) location_parts = re.findall('(%[^%]+)', maskL) parsed_folder_name = self.parse_mask_for_location( maskL, location_parts, place_name, ) path.append(tcomp + ' ' + parsed_folder_name) break elif part in ('album'): if metadata['album']: path.append(metadata['album']) break elif part.startswith('"') and part.endswith('"'): path.append(part[1:-1]) return os.path.join(*path)
def test_place_name_no_default(): # See gh-160 for backwards compatability needed when a string is stored instead of a dict helper.reset_dbs() place_name = geolocation.place_name(123456.000, 123456.000) helper.restore_dbs() assert place_name['default'] == 'Unknown Location', place_name
def get_folder_path(self, metadata): """Get folder path by various parameters. :param time time_obj: Time object to be used to determine folder name. :returns: str """ path = [] if(metadata['date_taken'] is not None): path.append(time.strftime('%Y-%m-%b', metadata['date_taken'])) if(metadata['album'] is not None): path.append(metadata['album']) elif( metadata['latitude'] is not None and metadata['longitude'] is not None ): place_name = geolocation.place_name( metadata['latitude'], metadata['longitude'] ) if(place_name is not None): path.append(place_name) # if we don't have a 2nd level directory we use 'Unknown Location' if(len(path) < 2): path.append('Unknown Location') # return '/'.join(path[::-1]) return os.path.join(*path)
def get_folder_path(self, metadata): """Get folder path by various parameters. :param time time_obj: Time object to be used to determine folder name. :returns: str """ path = [] if (metadata['date_taken'] is not None): path.append(time.strftime('%Y-%m-%b', metadata['date_taken'])) if (metadata['album'] is not None): path.append(metadata['album']) elif (metadata['latitude'] is not None and metadata['longitude'] is not None): place_name = geolocation.place_name(metadata['latitude'], metadata['longitude']) if (place_name is not None): path.append(place_name) # if we don't have a 2nd level directory we use 'Unknown Location' if (len(path) < 2): path.append('Unknown Location') # return '/'.join(path[::-1]) return os.path.join(*path)
def main(argv): args = arguments.parse( argv, None, ['file=', 'type='], './import.py --type=<photo or video> --file=<path to file>') if ('file' not in args): print 'No file specified' sys.exit(1) media = Media.get_class_by_file(args['file'], [Photo, Video]) if (media is None): print 'Not a valid file' sys.exit(1) metadata = media.get_metadata() place_name = geolocation.place_name(metadata['latitude'], metadata['longitude']) output = { 'latitude': metadata['latitude'], 'longitude': metadata['longitude'], 'place_name': place_name } print '%r' % output
def get_folder_path(self, metadata, path_parts=None): """Given a media's metadata this function returns the folder path as a string. :param metadata dict: Metadata dictionary. :optional param path_parts list of tuples: Pre-defined path definition (for unit test) :returns: str """ # Stitch in the extended folder_path handler config = Destination_path_pattern() config_ini = os.path.join(os.getcwd(), 'elodie', 'tests', 'files', 'config_extended_1.ini') config.read_config_file(config_file) raw_full_path = config.get_raw_full_path() if raw_full_path != '': fpconfig = Destination_actual_path( raw_full_path, '') # Don't need the filepath as we'll fake the metadata fpconfig._set_metadata(metadata) full_path = fpconfig.get_full_path() return full_path # else extended folder_path handler is not specified in config.ini if not path_parts: path_parts = self.get_folder_path_definition() path = [] for path_part in path_parts: # We support fallback values so that # 'album|city|"Unknown Location" # %album|%city|"Unknown Location" results in # My Album - when an album exists # Sunnyvale - when no album exists but a city exists # 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'])) break elif part in ('location', 'hamlet', 'village', 'town', 'city', 'state', 'country'): place_name = geolocation.place_name( metadata['latitude'], metadata['longitude']) # TBD _x = self._parseFallbacks(metadata, mask) 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 test_place_name_cached(): helper.reset_dbs() with open('%s/location.json-cached' % gettempdir(), 'w') as f: f.write(""" [{"lat": 37.3667027222222, "long": -122.033383611111, "name": {"city": "UNITTEST"}}] """) place_name = geolocation.place_name(37.3667027222222, -122.033383611111) helper.restore_dbs() assert place_name['city'] == 'UNITTEST', place_name
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 test_place_name_deprecated_string_cached(): # See gh-160 for backwards compatability needed when a string is stored instead of a dict helper.reset_dbs() with open('%s/location.json-cached' % gettempdir(), 'w') as f: f.write(""" [{"lat": 37.3667027222222, "long": -122.033383611111, "name": "OLDVALUE"}] """) place_name = geolocation.place_name(37.3667027222222, -122.033383611111) helper.restore_dbs() assert place_name['city'] == 'Sunnyvale', place_name
def get_folder_path(self, metadata): path = [] if(metadata['date_taken'] is not None): path.append(time.strftime('%Y-%m-%b', metadata['date_taken'])) if(metadata['album'] is not None): path.append(metadata['album']) elif(metadata['latitude'] is not None and metadata['longitude'] is not None): place_name = geolocation.place_name(metadata['latitude'], metadata['longitude']) if(place_name is not None): path.append(place_name) # if we don't have a 2nd level directory we use 'Unknown Location' if(len(path) < 2): path.append('Unknown Location') #return '/'.join(path[::-1]) return '/'.join(path)
def main(argv): args = arguments.parse(argv, None, ['file=','type='], './import.py --type=<photo or video> --file=<path to file>') if('file' not in args): print 'No file specified' sys.exit(1) media = Media.get_class_by_file(args['file'], [Photo, Video]) if(media is None): print 'Not a valid file' sys.exit(1) metadata = media.get_metadata() place_name = geolocation.place_name(metadata['latitude'], metadata['longitude']) output = {'latitude': metadata['latitude'], 'longitude': metadata['longitude'], 'place_name': place_name} print '%r' % output
def get_folder_path(self, metadata): """Get folder path by various parameters. :param metadata dict: Metadata dictionary. :returns: str """ path_parts = self.get_folder_path_definition() path = [] for path_part in path_parts: part, mask = path_part if part == 'date': path.append(time.strftime(mask, metadata['date_taken'])) elif part == 'location': if (metadata['latitude'] is not None and metadata['longitude'] is not None): place_name = geolocation.place_name( metadata['latitude'], metadata['longitude']) if (place_name is not None): location_parts = re.findall('(%[^%]+)', mask) parsed_folder_name = self.parse_mask_for_location( mask, location_parts, place_name, ) path.append(parsed_folder_name) # For now we always make the leaf folder an album if it's in the EXIF. # This is to preserve backwards compatability until we figure out how # to include %album in the config.ini syntax. if (metadata['album'] is not None): if (len(path) == 1): path.append(metadata['album']) elif (len(path) == 2): path[1] = metadata['album'] # if we don't have a 2nd level directory we use 'Unknown Location' if (len(path) < 2): path.append('Unknown Location') # return '/'.join(path[::-1]) return os.path.join(*path)
def get_file_name(self, metadata): """Generate file name for a photo or video using its metadata. Originally we hardcoded the file name to include an ISO date format. We use an ISO8601-like format for the file name prefix. Instead of colons as the separator for hours, minutes and seconds we use a hyphen. https://en.wikipedia.org/wiki/ISO_8601#General_principles PR #225 made the file name customizable and fixed issues #107 #110 #111. https://github.com/jmathai/elodie/pull/225 :param media: A Photo or Video instance :type media: :class:`~elodie.media.photo.Photo` or :class:`~elodie.media.video.Video` :returns: str or None for non-photo or non-videos """ if(metadata is None): return None # Get the name template and definition. # Name template is in the form %date-%original_name-%title.%extension # Definition is in the form # [ # [('date', '%Y-%m-%d_%H-%M-%S')], # [('original_name', '')], [('title', '')], // contains a fallback # [('extension', '')] # ] name_template, definition = self.get_file_name_definition() name = name_template for parts in definition: this_value = None for this_part in parts: part, mask = this_part if part in ('date', 'day', 'month', 'year'): this_value = time.strftime(mask, metadata['date_taken']) break elif part in ('location', 'city', 'state', 'country'): place_name = geolocation.place_name( metadata['latitude'], metadata['longitude'] ) location_parts = re.findall('(%[^%]+)', mask) this_value = self.parse_mask_for_location( mask, location_parts, place_name, ) break elif part in ('album', 'extension', 'title'): if metadata[part]: this_value = re.sub(self.whitespace_regex, '-', metadata[part].strip()) break elif part in ('original_name'): # First we check if we have metadata['original_name']. # We have to do this for backwards compatibility because # we original did not store this back into EXIF. if metadata[part]: this_value = os.path.splitext(metadata['original_name'])[0] else: # We didn't always store original_name so this is # for backwards compatability. # We want to remove the hardcoded date prefix we used # to add to the name. # This helps when re-running the program on file # which were already processed. this_value = re.sub( '^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-', '', metadata['base_name'] ) if(len(this_value) == 0): this_value = metadata['base_name'] # Lastly we want to sanitize the name this_value = re.sub(self.whitespace_regex, '-', this_value.strip()) elif part.startswith('"') and part.endswith('"'): this_value = part[1:-1] break # Here we replace the placeholder with it's corresponding value. # Check if this_value was not set so that the placeholder # can be removed completely. # For example, %title- will be replaced with '' # Else replace the placeholder (i.e. %title) with the value. if this_value is None: name = re.sub( #'[^a-z_]+%{}'.format(part), '[^a-zA-Z0-9_]+%{}'.format(part), '', name, ) else: name = re.sub( '%{}'.format(part), this_value, name, ) config = load_config() if('File' in config and 'capitalization' in config['File'] and config['File']['capitalization'] == 'upper'): return name.upper() else: return name.lower()
def get_file_name(self, media): """Generate file name for a photo or video using its metadata. We use an ISO8601-like format for the file name prefix. Instead of colons as the separator for hours, minutes and seconds we use a hyphen. https://en.wikipedia.org/wiki/ISO_8601#General_principles :param media: A Photo or Video instance :type media: :class:`~elodie.media.photo.Photo` or :class:`~elodie.media.video.Video` :returns: str or None for non-photo or non-videos """ if (not media.is_valid()): return None metadata = media.get_metadata() if (metadata is None): return None name_template, definition = self.get_file_name_definition() name = name_template for parts in definition: this_value = None for this_part in parts: part, mask = this_part if part in ('date', 'day', 'month', 'year'): this_value = time.strftime(mask, metadata['date_taken']) break elif part in ('location', 'city', 'state', 'country'): place_name = geolocation.place_name( metadata['latitude'], metadata['longitude']) location_parts = re.findall('(%[^%]+)', mask) this_value = self.parse_mask_for_location( mask, location_parts, place_name, ) break elif part in ('album', 'base_name', 'extension', 'original_name', 'title'): if metadata[part]: this_value = metadata[part] break elif part.startswith('"') and part.endswith('"'): this_value = part[1:-1] break if this_value is None: name = re.sub( '[^a-z_]+%{}'.format(part), '', name, ) else: name = re.sub( '%{}'.format(part), this_value, name, ) return name # DELETE ME .. # First we check if we have metadata['original_name']. # We have to do this for backwards compatibility because # we original did not store this back into EXIF. if ('original_name' in metadata and metadata['original_name']): base_name = os.path.splitext(metadata['original_name'])[0] else: # If the file has EXIF title we use that in the file name # (i.e. my-favorite-photo-img_1234.jpg) # We want to remove the date prefix we add to the name. # This helps when re-running the program on file which were already # processed. base_name = re.sub('^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}-', '', metadata['base_name']) if (len(base_name) == 0): base_name = metadata['base_name'] if ('title' in metadata and metadata['title'] is not None and len(metadata['title']) > 0): title_sanitized = re.sub('\W+', '-', metadata['title'].strip()) base_name = base_name.replace('-%s' % title_sanitized, '') base_name = '%s-%s' % (base_name, title_sanitized) file_name = '%s-%s.%s' % (time.strftime( '%Y-%m-%d_%H-%M-%S', metadata['date_taken']), base_name, metadata['extension']) return file_name.lower()