Merge pull request #123 from zserg/py3_1

Python 3 compatibility added
This commit is contained in:
Jaisen Mathai 2016-08-22 11:35:59 -07:00 committed by GitHub
commit 09872c82a5
21 changed files with 106 additions and 80 deletions

View File

@ -2,14 +2,15 @@ language: python
dist: trusty
python:
- "2.7"
- "3.4"
virtualenv:
system_site_packages: true
before_install:
- "sudo apt-get update -qq"
- "sudo apt-get install python-dev python-pip libimage-exiftool-perl -y"
install:
- "sudo pip install -r elodie/tests/requirements.txt"
- "sudo pip install coveralls"
- "pip install -r elodie/tests/requirements.txt"
- "pip install coveralls"
# command to run tests
# test mapquest key
before_script:
@ -25,5 +26,5 @@ before_script:
- "cd ${ELODIE_DIR}"
after_success:
- "coveralls"
script:
script:
- "nosetests --with-coverage --cover-package=elodie -w elodie/tests"

View File

@ -1,5 +1,6 @@
#!/usr/bin/env python
from __future__ import print_function
import os
import re
import sys
@ -35,16 +36,16 @@ def import_file(_file, destination, album_from_folder, trash):
"""
if not os.path.exists(_file):
if constants.debug:
print 'Could not find %s' % _file
print '{"source":"%s", "error_msg":"Could not find %s"}' % \
(_file, _file)
print('Could not find %s' % _file)
print('{"source":"%s", "error_msg":"Could not find %s"}' % \
(_file, _file))
return
media = Media.get_class_by_file(_file, [Text, Audio, Photo, Video])
if not media:
if constants.debug:
print 'Not a supported file (%s)' % _file
print '{"source":"%s", "error_msg":"Not a supported file"}' % _file
print('Not a supported file (%s)' % _file)
print('{"source":"%s", "error_msg":"Not a supported file"}' % _file)
return
if media.__name__ == 'Video':
@ -56,7 +57,7 @@ def import_file(_file, destination, album_from_folder, trash):
dest_path = FILESYSTEM.process_file(_file, destination,
media, allowDuplicate=False, move=False)
if dest_path:
print '%s -> %s' % (_file, dest_path)
print('%s -> %s' % (_file, dest_path))
if trash:
send2trash(_file)
@ -109,9 +110,9 @@ def update_location(media, file_path, location_name):
'latitude'], location_coords['longitude'])
if not location_status:
if constants.debug:
print 'Failed to update location'
print ('{"source":"%s",' % file_path,
'"error_msg":"Failed to update location"}')
print('Failed to update location')
print(('{"source":"%s",' % file_path,
'"error_msg":"Failed to update location"}'))
sys.exit(1)
return True
@ -125,8 +126,8 @@ def update_time(media, file_path, time_string):
elif re.match(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}\d{2}$', time_string):
msg = ('Invalid time format. Use YYYY-mm-dd hh:ii:ss or YYYY-mm-dd')
if constants.debug:
print msg
print '{"source":"%s", "error_msg":"%s"}' % (file_path, msg)
print(msg)
print('{"source":"%s", "error_msg":"%s"}' % (file_path, msg))
sys.exit(1)
time = datetime.strptime(time_string, time_format)
@ -150,9 +151,9 @@ def _update(album, location, time, title, files):
for file_path in files:
if not os.path.exists(file_path):
if constants.debug:
print 'Could not find %s' % file_path
print '{"source":"%s", "error_msg":"Could not find %s"}' % \
(file_path, file_path)
print('Could not find %s' % file_path)
print('{"source":"%s", "error_msg":"Could not find %s"}' % \
(file_path, file_path))
continue
file_path = os.path.expanduser(file_path)
@ -209,9 +210,9 @@ def _update(album, location, time, title, files):
dest_path = FILESYSTEM.process_file(file_path, destination,
updated_media, move=True, allowDuplicate=True)
if constants.debug:
print u'%s -> %s' % (file_path, dest_path)
print '{"source":"%s", "destination":"%s"}' % (file_path,
dest_path)
print(u'%s -> %s' % (file_path, dest_path))
print('{"source":"%s", "destination":"%s"}' % (file_path,
dest_path))
# If the folder we moved the file out of or its parent are empty
# we delete it.
FILESYSTEM.delete_directory_if_empty(os.path.dirname(file_path))

View File

@ -1,6 +1,7 @@
"""
Command line argument parsing for helper scripts.
"""
from __future__ import print_function
import getopt
import sys
@ -20,13 +21,13 @@ def parse(argv, options, long_options, usage):
try:
opts, args = getopt.getopt(argv, options, long_options)
except getopt.GetoptError:
print usage
print(usage)
sys.exit(2)
return_arguments = {}
for opt, arg in opts:
if opt == '-h':
print usage
print(usage)
sys.exit()
else:
return_arguments[sub('^-+', '', opt)] = arg

View File

@ -2,6 +2,7 @@
Helpers for checking for an interacting with external dependencies. These are
things that Elodie requires, but aren't installed automatically for the user.
"""
from __future__ import print_function
import os
import sys
@ -43,7 +44,7 @@ def verify_dependencies():
"""
exiftool = get_exiftool()
if exiftool is None:
print >>sys.stderr, EXIFTOOL_ERROR
print(EXIFTOOL_ERROR, file=sys.stderr)
return False
return True

View File

@ -273,8 +273,7 @@ class ExifTool(object):
"""
if not self.running:
raise ValueError("ExifTool instance not running.")
cmd_txt = b"\n".join(params + (b"-execute\n",))
self._process.stdin.write(cmd_txt.encode("utf-8"))
self._process.stdin.write(b"\n".join(params + (b"-execute\n",)))
self._process.stdin.flush()
output = b""
fd = self._process.stdout.fileno()
@ -403,11 +402,13 @@ class ExifTool(object):
"an iterable of strings")
params = []
params_utf8 = []
for tag, value in tags.items():
params.append(u'-%s=%s' % (tag, value))
params.extend(filenames)
return self.execute(*params)
params_utf8 = [x.encode('utf-8') for x in params]
return self.execute(*params_utf8)
def set_tags(self, tags, filename):
"""Writes the values of the specified tags for the given file.

View File

@ -3,6 +3,8 @@ General file system methods.
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import print_function
from builtins import object
import os
import re
@ -176,7 +178,7 @@ class FileSystem(object):
allow_duplicate = kwargs['allowDuplicate']
if(not media.is_valid()):
print '%s is not a valid media file. Skipping...' % _file
print('%s is not a valid media file. Skipping...' % _file)
return
metadata = media.get_metadata()
@ -191,17 +193,17 @@ class FileSystem(object):
checksum = db.checksum(_file)
if(checksum is None):
if(constants.debug is True):
print 'Could not get checksum for %s. Skipping...' % _file
print('Could not get checksum for %s. Skipping...' % _file)
return
# If duplicates are not allowed and this hash exists in the db then we
# return
if(allow_duplicate is False and db.check_hash(checksum) is True):
if(constants.debug is True):
print '%s already exists at %s. Skipping...' % (
print('%s already exists at %s. Skipping...' % (
_file,
db.get_hash(checksum)
)
))
return
self.create_directory(dest_directory)

View File

@ -1,32 +1,23 @@
"""Look up geolocation information for media objects."""
from __future__ import print_function
from __future__ import division
from future import standard_library
from past.utils import old_div
from os import path
from ConfigParser import ConfigParser
import fractions
from configparser import ConfigParser
standard_library.install_aliases() # noqa
import requests
import urllib
import urllib.request
import urllib.parse
import urllib.error
from elodie import constants
from elodie.localstorage import Db
class Fraction(fractions.Fraction):
"""Only create Fractions from floats.
Should be compatible with Python 2.6, though untested.
>>> Fraction(0.3)
Fraction(3, 10)
>>> Fraction(1.1)
Fraction(11, 10)
"""
def __new__(cls, value, ignore=None):
return fractions.Fraction.from_float(value).limit_denominator(99999)
def coordinates_by_name(name):
# Try to get cached location first
db = Db()
@ -88,7 +79,8 @@ def dms_to_decimal(degrees, minutes, seconds, direction=' '):
if(direction[0] in 'WSws'):
sign = -1
return (
float(degrees) + float(minutes) / 60 + float(seconds) / 3600
float(degrees) + old_div(float(minutes), 60) +
old_div(float(seconds), 3600)
) * sign
@ -159,17 +151,17 @@ def reverse_lookup(lat, lon):
headers = {"Accept-Language": constants.accepted_language}
r = requests.get(
'http://open.mapquestapi.com/nominatim/v1/reverse.php?%s' %
urllib.urlencode(params), headers=headers
urllib.parse.urlencode(params), headers=headers
)
return r.json()
except requests.exceptions.RequestException as e:
if(constants.debug is True):
print e
print(e)
return None
except ValueError as e:
if(constants.debug is True):
print r.text
print e
print(r.text)
print(e)
return None
@ -182,18 +174,18 @@ def lookup(name):
try:
params = {'format': 'json', 'key': key, 'location': name}
if(constants.debug is True):
print 'http://open.mapquestapi.com/geocoding/v1/address?%s' % urllib.urlencode(params) # noqa
print('http://open.mapquestapi.com/geocoding/v1/address?%s' % urllib.parse.urlencode(params)) # noqa
r = requests.get(
'http://open.mapquestapi.com/geocoding/v1/address?%s' %
urllib.urlencode(params)
urllib.parse.urlencode(params)
)
return r.json()
except requests.exceptions.RequestException as e:
if(constants.debug is True):
print e
print(e)
return None
except ValueError as e:
if(constants.debug is True):
print r.text
print e
print(r.text)
print(e)
return None

View File

@ -1,6 +1,8 @@
"""
Methods for interacting with information Elodie caches about stored media.
"""
from builtins import map
from builtins import object
import hashlib
import json
@ -141,17 +143,17 @@ class Db(object):
the given latitude and longitude.
:returns: str, or None if a matching location couldn't be found.
"""
last_d = sys.maxint
last_d = sys.maxsize
name = None
for data in self.location_db:
# As threshold is quite small use simple math
# From http://stackoverflow.com/questions/15736995/how-can-i-quickly-estimate-the-distance-between-two-latitude-longitude-points # noqa
# convert decimal degrees to radians
lon1, lat1, lon2, lat2 = map(
lon1, lat1, lon2, lat2 = list(map(
radians,
[longitude, latitude, data['long'], data['lat']]
)
))
r = 6371000 # radius of the earth in m
x = (lon2 - lon1) * cos(0.5 * (lat2 + lat1))

View File

@ -5,8 +5,9 @@ class.
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import absolute_import
from video import Video
from .video import Video
class Audio(Video):

View File

@ -13,6 +13,11 @@ are used to represent the actual files.
import mimetypes
import os
try: # Py3k compatibility
basestring
except NameError:
basestring = (bytes, str)
class Base(object):

View File

@ -8,6 +8,7 @@ are used to represent the actual files.
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import print_function
# load modules
from elodie import constants

View File

@ -4,6 +4,8 @@ image objects (JPG, DNG, etc.).
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import print_function
from __future__ import absolute_import
import imghdr
import os
@ -15,7 +17,7 @@ from re import compile
from elodie import constants
from media import Media
from .media import Media
class Photo(Media):
@ -95,7 +97,7 @@ class Photo(Media):
break
except BaseException as e:
if(constants.debug is True):
print e
print(e)
pass
if(seconds_since_epoch == 0):

View File

@ -123,7 +123,7 @@ class Text(Base):
self.metadata_line = parsed_json
except ValueError:
if(constants.debug is True):
print 'Could not parse JSON from first line: %s' % first_line
print('Could not parse JSON from first line: %s' % first_line)
pass
def write_metadata(self, **kwargs):

View File

@ -4,6 +4,9 @@ objects (AVI, MOV, etc.).
.. moduleauthor:: Jaisen Mathai <jaisen@jmathai.com>
"""
from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
# load modules
from distutils.spawn import find_executable
@ -13,7 +16,7 @@ import os
import re
import time
from media import Media
from .media import Media
class Video(Media):

View File

@ -1,3 +1,4 @@
from __future__ import absolute_import
# Project imports
import os
import re
@ -11,7 +12,7 @@ import mock
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))
import helper
from . import helper
from elodie.filesystem import FileSystem
from elodie.media.media import Media
from elodie.media.photo import Photo

View File

@ -1,3 +1,7 @@
from __future__ import absolute_import
from __future__ import division
from builtins import range
from past.utils import old_div
# Project imports
import os
import random
@ -6,7 +10,7 @@ import sys
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))
import helper
from . import helper
from elodie import geolocation
os.environ['TZ'] = 'GMT'
@ -18,7 +22,7 @@ def test_decimal_to_dms():
target_decimal_value = random.uniform(0.0, 180.0)
if(x % 2 == 1):
target_decimal_value = target_decimal_value * -1
dms = geolocation.decimal_to_dms(target_decimal_value)
check_value = (dms[0] + dms[1] / 60 + dms[2] / 3600) * dms[3]
@ -34,7 +38,7 @@ def test_dms_string_latitude():
target_decimal_value = random.uniform(0.0, 180.0)
if(x % 2 == 1):
target_decimal_value = target_decimal_value * -1
dms = geolocation.decimal_to_dms(target_decimal_value)
dms_string = geolocation.dms_string(target_decimal_value, 'latitude')
@ -49,7 +53,7 @@ def test_dms_string_longitude():
target_decimal_value = random.uniform(0.0, 180.0)
if(x % 2 == 1):
target_decimal_value = target_decimal_value * -1
dms = geolocation.decimal_to_dms(target_decimal_value)
dms_string = geolocation.dms_string(target_decimal_value, 'longitude')

View File

@ -1,3 +1,7 @@
from __future__ import division
from __future__ import unicode_literals
from builtins import range
from past.utils import old_div
import hashlib
import os
import random
@ -12,7 +16,7 @@ from datetime import timedelta
def checksum(file_path, blocksize=65536):
hasher = hashlib.sha256()
with open(file_path, 'r') as f:
with open(file_path, 'rb') as f:
buf = f.read(blocksize)
while len(buf) > 0:
@ -73,7 +77,7 @@ def random_decimal():
def random_coordinate(coordinate, precision):
# Here we add to the decimal section of the coordinate by a given precision
return coordinate + ((10.0 / (10.0**precision)) * random_decimal())
return coordinate + ((old_div(10.0, (10.0**precision))) * random_decimal())
def temp_dir():
return tempfile.gettempdir()
@ -91,8 +95,8 @@ def is_windows():
def path_tz_fix(file_name):
if is_windows():
# Calculate the offset between UTC and local time
tz_shift = (datetime.fromtimestamp(0) -
datetime.utcfromtimestamp(0)).seconds/3600
tz_shift = old_div((datetime.fromtimestamp(0) -
datetime.utcfromtimestamp(0)).seconds,3600)
# replace timestamp in file_name
m = re.search('(\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2})',file_name)
t_date = datetime.fromtimestamp(time.mktime(time.strptime(m.group(0), '%Y-%m-%d_%H-%M-%S')))

View File

@ -1,10 +1,12 @@
from __future__ import print_function
from __future__ import absolute_import
# Project imports
import os
import sys
sys.path.insert(0, os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))))
import helper
from . import helper
from elodie.localstorage import Db
from elodie import constants
@ -154,10 +156,10 @@ def test_get_location_name_within_threshold():
latitude, longitude, name = helper.get_test_location()
db.add_location(latitude, longitude, name)
print latitude
print(latitude)
new_latitude = helper.random_coordinate(latitude, 4)
new_longitude = helper.random_coordinate(longitude, 4)
print new_latitude
print(new_latitude)
# 10 miles
retrieved_name = db.get_location_name(new_latitude, new_longitude, 1600*10)

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8
# Project imports
from __future__ import print_function
import os
import sys
@ -56,7 +57,6 @@ def test_get_date_taken():
audio = Audio(helper.get_file('audio.m4a'))
date_taken = audio.get_date_taken()
print '%r' % date_taken
assert date_taken == (2016, 1, 4, 5, 24, 15, 0, 19, 0), date_taken
def test_get_exiftool_attributes():

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8
# Project imports
from __future__ import unicode_literals
import os
import sys

View File

@ -1,3 +1,4 @@
click>=6.2,<7.0
requests>=2.9.1,<3.0
send2trash>=1.3.0,<2.0
future