gh-28 Add no-exif file for tests and add test cases
| @ -23,8 +23,6 @@ Media class for general video operations | ||||
| class Media(object): | ||||
|     # class / static variable accessible through get_valid_extensions() | ||||
|     __name__ = 'Media' | ||||
|     video_extensions = ('avi','m4v','mov','mp4','3gp') | ||||
|     photo_extensions = ('jpg', 'jpeg', 'nef', 'dng', 'gif') | ||||
| 
 | ||||
|     """ | ||||
|     @param, source, string, The fully qualified path to the video file | ||||
| @ -88,41 +86,6 @@ class Media(object): | ||||
|     def is_valid(self): | ||||
|         return False | ||||
| 
 | ||||
| 
 | ||||
|     """ | ||||
|     Get the date which the photo was taken. | ||||
|     The date value returned is defined by the min() of mtime and ctime. | ||||
| 
 | ||||
|     @returns, time object or None for non-photo files or 0 timestamp | ||||
|     """ | ||||
|     def get_date_taken(self): | ||||
|         if(not self.is_valid()): | ||||
|             return None | ||||
| 
 | ||||
|         source = self.source | ||||
|         seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source)) | ||||
|         # We need to parse a string from EXIF into a timestamp. | ||||
|         # EXIF DateTimeOriginal and EXIF DateTime are both stored in %Y:%m:%d %H:%M:%S format | ||||
|         # we use date.strptime -> .timetuple -> time.mktime to do the conversion in the local timezone | ||||
|         # EXIF DateTime is already stored as a timestamp | ||||
|         # Sourced from https://github.com/photo/frontend/blob/master/src/libraries/models/Photo.php#L500 | ||||
|         exif = self.get_exif() | ||||
|         for key in self.exif_map['date_taken']: | ||||
|             try: | ||||
|                 if(key in exif): | ||||
|                     if(re.match('\d{4}(-|:)\d{2}(-|:)\d{2}', str(exif[key].value)) is not None): | ||||
|                         seconds_since_epoch = time.mktime(exif[key].value.timetuple()) | ||||
|                         break; | ||||
|             except BaseException as e: | ||||
|                 if(constants.debug == True): | ||||
|                     print e | ||||
|                 pass | ||||
| 
 | ||||
|         if(seconds_since_epoch == 0): | ||||
|             return None | ||||
| 
 | ||||
|         return time.gmtime(seconds_since_epoch) | ||||
| 
 | ||||
|     """ | ||||
|     Read EXIF from a photo file. | ||||
|     We store the result in a member variable so we can call get_exif() often without performance degredation | ||||
|  | ||||
| @ -86,7 +86,41 @@ class Photo(Media): | ||||
|             return None | ||||
| 
 | ||||
|     """ | ||||
|     Check the file extension against valid file extensions as returned by get_valid_extensions() | ||||
|     Get the date which the photo was taken. | ||||
|     The date value returned is defined by the min() of mtime and ctime. | ||||
| 
 | ||||
|     @returns, time object or None for non-photo files or 0 timestamp | ||||
|     """ | ||||
|     def get_date_taken(self): | ||||
|         if(not self.is_valid()): | ||||
|             return None | ||||
| 
 | ||||
|         source = self.source | ||||
|         seconds_since_epoch = min(os.path.getmtime(source), os.path.getctime(source)) | ||||
|         # We need to parse a string from EXIF into a timestamp. | ||||
|         # EXIF DateTimeOriginal and EXIF DateTime are both stored in %Y:%m:%d %H:%M:%S format | ||||
|         # we use date.strptime -> .timetuple -> time.mktime to do the conversion in the local timezone | ||||
|         # EXIF DateTime is already stored as a timestamp | ||||
|         # Sourced from https://github.com/photo/frontend/blob/master/src/libraries/models/Photo.php#L500 | ||||
|         exif = self.get_exif() | ||||
|         for key in self.exif_map['date_taken']: | ||||
|             try: | ||||
|                 if(key in exif): | ||||
|                     if(re.match('\d{4}(-|:)\d{2}(-|:)\d{2}', str(exif[key].value)) is not None): | ||||
|                         seconds_since_epoch = time.mktime(exif[key].value.timetuple()) | ||||
|                         break; | ||||
|             except BaseException as e: | ||||
|                 if(constants.debug == True): | ||||
|                     print e | ||||
|                 pass | ||||
| 
 | ||||
|         if(seconds_since_epoch == 0): | ||||
|             return None | ||||
| 
 | ||||
|         return time.gmtime(seconds_since_epoch) | ||||
| 
 | ||||
|     """ | ||||
|     Check the file extension against valid file extensions as returned by self.extensions | ||||
|      | ||||
|     @returns, boolean | ||||
|     """ | ||||
| @ -98,7 +132,6 @@ class Photo(Media): | ||||
|         if(imghdr.what(source) is None): | ||||
|             return False; | ||||
| 
 | ||||
|         # we can't use self.__get_extension else we'll endlessly recurse | ||||
|         return os.path.splitext(source)[1][1:].lower() in self.extensions | ||||
| 
 | ||||
|     """ | ||||
| @ -174,4 +207,4 @@ class Photo(Media): | ||||
|     """ | ||||
|     @classmethod | ||||
|     def get_valid_extensions(Photo): | ||||
|         return Media.photo_extensions | ||||
|         return Photo.extensions | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								elodie/tests/files/no-exif.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 222 B | 
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 13 KiB | 
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB | 
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB | 
| Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 14 KiB | 
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB | 
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 14 KiB | 
| Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 13 KiB | 
| @ -3,14 +3,10 @@ | ||||
| import os | ||||
| import sys | ||||
| 
 | ||||
| import hashlib | ||||
| import random | ||||
| import re | ||||
| import datetime | ||||
| import shutil | ||||
| import string | ||||
| import tempfile | ||||
| import time | ||||
| import datetime | ||||
| 
 | ||||
| from nose.plugins.skip import SkipTest | ||||
| 
 | ||||
| @ -25,7 +21,7 @@ os.environ['TZ'] = 'GMT' | ||||
| 
 | ||||
| def test_photo_extensions(): | ||||
|     photo = Photo() | ||||
|     extensions = photo.photo_extensions | ||||
|     extensions = photo.extensions | ||||
| 
 | ||||
|     assert 'jpg' in extensions | ||||
|     assert 'jpeg' in extensions | ||||
| @ -75,6 +71,29 @@ def test_get_coordinate_longitude(): | ||||
| 
 | ||||
|     assert coordinate == -122.033383611, coordinate | ||||
| 
 | ||||
| def test_get_coordinates_without_exif(): | ||||
|     photo = Photo(helper.get_file('no-exif.jpg')) | ||||
|     latitude = photo.get_coordinate('latitude') | ||||
|     longitude = photo.get_coordinate('longitude') | ||||
| 
 | ||||
|     assert latitude is None, latitude | ||||
|     assert longitude is None, longitude | ||||
| 
 | ||||
| def test_get_date_taken(): | ||||
|     photo = Photo(helper.get_file('plain.jpg')) | ||||
|     date_taken = photo.get_date_taken() | ||||
| 
 | ||||
|     assert date_taken == (2015, 12, 5, 0, 59, 26, 5, 339, 0), date_taken | ||||
| 
 | ||||
| def test_get_date_taken_without_exif(): | ||||
|     source = helper.get_file('no-exif.jpg') | ||||
|     photo = Photo(source) | ||||
|     date_taken = photo.get_date_taken() | ||||
| 
 | ||||
|     date_taken_from_file = time.gmtime(min(os.path.getmtime(source), os.path.getctime(source))) | ||||
| 
 | ||||
|     assert date_taken == date_taken_from_file, date_taken | ||||
| 
 | ||||
| def test_is_valid(): | ||||
|     photo = Photo(helper.get_file('with-location.jpg')) | ||||
| 
 | ||||
| @ -141,10 +160,6 @@ def test_set_title(): | ||||
|     photo = Photo(origin) | ||||
|     origin_metadata = photo.get_metadata() | ||||
| 
 | ||||
|     # Verify that original photo has no location information | ||||
|     assert origin_metadata['latitude'] is None, origin_metadata['latitude'] | ||||
|     assert origin_metadata['longitude'] is None, origin_metadata['longitude'] | ||||
| 
 | ||||
|     status = photo.set_title('my photo title') | ||||
| 
 | ||||
|     assert status == True, status | ||||
| @ -166,10 +181,6 @@ def test_set_title_non_ascii(): | ||||
|     photo = Photo(origin) | ||||
|     origin_metadata = photo.get_metadata() | ||||
| 
 | ||||
|     # Verify that original photo has no location information | ||||
|     assert origin_metadata['latitude'] is None, origin_metadata['latitude'] | ||||
|     assert origin_metadata['longitude'] is None, origin_metadata['longitude'] | ||||
| 
 | ||||
|     status = photo.set_title('形声字 / 形聲字') | ||||
| 
 | ||||
|     assert status == True, status | ||||
|  | ||||
 Jaisen Mathai
						Jaisen Mathai