Make script flake8 compabile

Fix almost all flake8 warnings for coding style
- long lines are currently ignored
- the Google lookup has been simplified
- the if and for loop are not yet simplified
This commit is contained in:
2018-03-12 17:08:48 +09:00
parent c7491c0b31
commit b3ac5051d0

View File

@@ -9,18 +9,26 @@
# * all data is translated into English with long vowl system (aka ou or oo is ō) # * all data is translated into English with long vowl system (aka ou or oo is ō)
# MUST HAVE: Python XMP Toolkit (http://python-xmp-toolkit.readthedocs.io/) # MUST HAVE: Python XMP Toolkit (http://python-xmp-toolkit.readthedocs.io/)
import argparse, sqlite3, requests, configparser, textwrap import argparse
import glob, os, sys, re import sqlite3
import requests
import configparser
# import textwrap
import glob
import os
import sys
import re
# Note XMPFiles does not work with sidecar files, need to read via XMPMeta # Note XMPFiles does not work with sidecar files, need to read via XMPMeta
from libxmp import XMPMeta, XMPError, consts from libxmp import XMPMeta, consts
from shutil import copyfile, get_terminal_size from shutil import copyfile, get_terminal_size
from math import ceil from math import ceil
############################################################## ##############################################################
### FUNCTIONS # FUNCTIONS
############################################################## ##############################################################
### ARGPARSE HELPERS
# ARGPARSE HELPERS
# call: writable_dir_folder # call: writable_dir_folder
# checks if this is a writeable folder OR file # checks if this is a writeable folder OR file
@@ -43,6 +51,7 @@ class writable_dir_folder(argparse.Action):
else: else:
raise argparse.ArgumentTypeError("writable_dir_folder: {0} is not a writable dir".format(prospective_dir)) raise argparse.ArgumentTypeError("writable_dir_folder: {0} is not a writable dir".format(prospective_dir))
# call: readable_dir # call: readable_dir
# custom define to check if it is a valid directory # custom define to check if it is a valid directory
class readable_dir(argparse.Action): class readable_dir(argparse.Action):
@@ -55,7 +64,8 @@ class readable_dir(argparse.Action):
else: else:
raise argparse.ArgumentTypeError("readable_dir:{0} is not a readable dir".format(prospective_dir)) raise argparse.ArgumentTypeError("readable_dir:{0} is not a readable dir".format(prospective_dir))
### MAIN FUNCTIONS
# MAIN FUNCTIONS
# METHOD: reverseGeolocate # METHOD: reverseGeolocate
# PARAMS: latitude, longitude, map search target (google or openstreetmap) # PARAMS: latitude, longitude, map search target (google or openstreetmap)
@@ -80,6 +90,7 @@ def reverseGeolocate(longitude, latitude, map_type):
'error': 'Map type not valid' 'error': 'Map type not valid'
} }
# METHOD: reverseGeolocateInit # METHOD: reverseGeolocateInit
# PARAMS: longitude, latitude # PARAMS: longitude, latitude
# RETURN: empty geolocation dictionary, or error flag if lat/long is not valid # RETURN: empty geolocation dictionary, or error flag if lat/long is not valid
@@ -104,6 +115,7 @@ def reverseGeolocateInit(longitude, latitude):
geolocation['error_message'] = 'Latitude {} or Longitude {} are not valid'.format(latitude, longitude) geolocation['error_message'] = 'Latitude {} or Longitude {} are not valid'.format(latitude, longitude)
return geolocation return geolocation
# METHOD: reverseGeolocateOpenStreetMap # METHOD: reverseGeolocateOpenStreetMap
# PARAMS: latitude, longitude # PARAMS: latitude, longitude
# RETURN: OpenStreetMap reverse lookcation lookup # RETURN: OpenStreetMap reverse lookcation lookup
@@ -163,6 +175,7 @@ def reverseGeolocateOpenStreetMap(longitude, latitude):
# return # return
return geolocation return geolocation
# METHOD: reverseGeolocateGoogle # METHOD: reverseGeolocateGoogle
# PARAMS: latitude, longitude # PARAMS: latitude, longitude
# RETURN: Google Maps reverse location lookup # RETURN: Google Maps reverse location lookup
@@ -196,12 +209,26 @@ def reverseGeolocateGoogle(longitude, latitude):
print("Google search for Lat: {}, Long: {} with {}".format(longitude, latitude, response.url)) print("Google search for Lat: {}, Long: {} with {}".format(longitude, latitude, response.url))
if args.debug and args.verbose >= 1: if args.debug and args.verbose >= 1:
print("Google response: {} => JSON: {}".format(response, response.json())) print("Google response: {} => JSON: {}".format(response, response.json()))
# type map
# For automated return of correct data into set to return
type_map = {
'CountryCode': ['country'],
'Country': ['country'],
'State': ['administrative_area_level_1', 'administrative_area_level_2'],
'City': ['locality'],
'Location': ['sublocality_level_1', 'sublocality_level_2', 'route'],
}
# print("Error: {}".format(response.json()['status'])) # print("Error: {}".format(response.json()['status']))
if response.json()['status'] == 'OK': if response.json()['status'] == 'OK':
# first entry for type = premise # first entry for type = premise
for entry in response.json()['results']: for entry in response.json()['results']:
for sub_entry in entry: for sub_entry in entry:
if sub_entry == 'types' and ('premise' in entry[sub_entry] or 'route' in entry[sub_entry] or 'street_address' in entry[sub_entry] or 'sublocality' in entry[sub_entry]): if sub_entry == 'types' and (
'premise' in entry[sub_entry] or
'route' in entry[sub_entry] or
'street_address' in entry[sub_entry] or
'sublocality' in entry[sub_entry]
):
# print("Entry {}: {}".format(sub_entry, entry[sub_entry])) # print("Entry {}: {}".format(sub_entry, entry[sub_entry]))
# print("Address {}".format(entry['address_components'])) # print("Address {}".format(entry['address_components']))
# type # type
@@ -210,30 +237,17 @@ def reverseGeolocateGoogle(longitude, latitude):
# -> locality, # -> locality,
# -> sublocality (_level_1 or 2 first found, then route) # -> sublocality (_level_1 or 2 first found, then route)
# so we get the data in the correct order # so we get the data in the correct order
for index in ['country', 'administrative_area_level_1', 'administrative_area_level_2', 'locality', 'sublocality_level_1', 'sublocality_level_2', 'route']: for loc_index in type_map:
# loop through the entries in the returned json and find matching for index in type_map[loc_index]:
# this is an array, so we need to loop through each
for addr in entry['address_components']: for addr in entry['address_components']:
# print("Addr: {}".format(addr)) # in types check that index is in there and the location is not yet set
# country code + country if index in addr['types'] and not geolocation[loc_index]:
if index == 'country' and index in addr['types'] and not geolocation['CountryCode']: # for country code we need to use short name, else we use long name
geolocation['CountryCode'] = addr['short_name'] if loc_index == 'CountryCode':
geolocation['Country'] = addr['long_name'] geolocation[loc_index] = addr['short_name']
# state else:
if index == 'administrative_area_level_1' and index in addr['types'] and not geolocation['State']: geolocation[loc_index] = addr['long_name']
geolocation['State'] = addr['long_name']
if index == 'administrative_area_level_2' and index in addr['types'] and not geolocation['State']:
geolocation['State'] = addr['long_name']
# city
if index == 'locality' and index in addr['types'] and not geolocation['City']:
geolocation['City'] = addr['long_name']
# location
if index == 'sublocality_level_1' and index in addr['types'] and not geolocation['Location']:
geolocation['Location'] = addr['long_name']
if index == 'sublocality_level_2' and index in addr['types'] and not geolocation['Location']:
geolocation['Location'] = addr['long_name']
# if all failes try route
if index == 'route' and index in addr['types'] and not geolocation['Location']:
geolocation['Location'] = addr['long_name']
# write OK status # write OK status
geolocation['status'] = response.json()['status'] geolocation['status'] = response.json()['status']
else: else:
@@ -244,6 +258,7 @@ def reverseGeolocateGoogle(longitude, latitude):
# return # return
return geolocation return geolocation
# METHOD: convertLatLongToDMS # METHOD: convertLatLongToDMS
# PARAMS: latLong in (-)N.N format, lat or long flag (else we can't set N/S) # PARAMS: latLong in (-)N.N format, lat or long flag (else we can't set N/S)
# RETURN: Deg,Min.Sec(NESW) format # RETURN: Deg,Min.Sec(NESW) format
@@ -252,20 +267,25 @@ def convertLatLongToDMS(lat_long, is_latitude = False, is_longitude = False):
# minus part before . and then multiply rest by 60 # minus part before . and then multiply rest by 60
degree = int(abs(lat_long)) degree = int(abs(lat_long))
minutes = round((float(abs(lat_long)) - int(abs(lat_long))) * 60, 10) minutes = round((float(abs(lat_long)) - int(abs(lat_long))) * 60, 10)
if is_latitude == True: if is_latitude is True:
direction = 'S' if int(lat_long) < 0 else 'N' direction = 'S' if int(lat_long) < 0 else 'N'
elif is_longitude == True: elif is_longitude is True:
direction = 'W' if int(lat_long) < 0 else 'E' direction = 'W' if int(lat_long) < 0 else 'E'
else: else:
direction = '(INVALID)' direction = '(INVALID)'
return "{},{}{}".format(degree, minutes, direction) return "{},{}{}".format(degree, minutes, direction)
# wrapper functions for Long/Lat calls
# wrapper functions for Long/Lat calls: latitude
def convertLatToDMS(lat_long): def convertLatToDMS(lat_long):
return convertLatLongToDMS(lat_long, is_latitude=True) return convertLatLongToDMS(lat_long, is_latitude=True)
# wrapper for Long/Lat call: longitute
def convertLongToDMS(lat_long): def convertLongToDMS(lat_long):
return convertLatLongToDMS(lat_long, is_longitude=True) return convertLatLongToDMS(lat_long, is_longitude=True)
# METHOD: longLatReg # METHOD: longLatReg
# PARAMS: latitude in (n,n.nNSEW format), longitude # PARAMS: latitude in (n,n.nNSEW format), longitude
# RETURN: dict with converted lat/long # RETURN: dict with converted lat/long
@@ -291,12 +311,17 @@ def longLatReg(longitude, latitude):
lat_long[element] *= -1 lat_long[element] *= -1
return lat_long return lat_long
# wrapper calls for DMS to Lat/Long
# wrapper calls for DMS to Lat/Long: latitude
def convertDMStoLat(lat_long): def convertDMStoLat(lat_long):
return longLatReg('0,0.0N', lat_long)['latitude'] return longLatReg('0,0.0N', lat_long)['latitude']
# # wrapper calls for DMS to Lat/Long: longitude
def convertDMStoLong(lat_long): def convertDMStoLong(lat_long):
return longLatReg(lat_long, '0,0.0N')['longitude'] return longLatReg(lat_long, '0,0.0N')['longitude']
# METHOD: checkOverwrite # METHOD: checkOverwrite
# PARAMS: data: value field, key: XMP key, field_controls: array from args # PARAMS: data: value field, key: XMP key, field_controls: array from args
# RETURN: true/false # RETURN: true/false
@@ -329,13 +354,14 @@ def checkOverwrite(data, key, field_controls):
)) ))
return status return status
# METHOD: shortenPath # METHOD: shortenPath
# PARAMS: path = string, length = int, file_only = true/false, path_only = true/false # PARAMS: path = string, length = int, file_only = true/false, path_only = true/false
# RETURN: shortend path with ... in front # RETURN: shortend path with ... in front
# DESC : shortes a path from the left so it fits into lenght # DESC : shortes a path from the left so it fits into lenght
# if file only is set to true, it will split the file, if path only is set, only the path # if file only is set to true, it will split the file, if path only is set, only the path
def shortenPath(path, length=30, file_only=False, path_only=False): def shortenPath(path, length=30, file_only=False, path_only=False):
length = length - 3; length = length - 3
# I assume the XMP file name has no CJK characters inside, so I strip out the path # I assume the XMP file name has no CJK characters inside, so I strip out the path
# The reason is that if there are CJK characters inside it will screw up the formatting # The reason is that if there are CJK characters inside it will screw up the formatting
if file_only: if file_only:
@@ -344,7 +370,8 @@ def shortenPath(path, length = 30, file_only = False, path_only = False):
path = os.path.split(path)[0] path = os.path.split(path)[0]
if len(path) > length: if len(path) > length:
path = "{} {}".format("..", path[len(path) - length:]) path = "{} {}".format("..", path[len(path) - length:])
return path; return path
# METHOD: shortenString # METHOD: shortenString
# PARAMS: string, shorten width, override shorten placeholder # PARAMS: string, shorten width, override shorten placeholder
@@ -357,6 +384,7 @@ def shortenString(string, width, placeholder = '..'):
else: else:
return str(string) return str(string)
# METHOD: printHeader # METHOD: printHeader
# PARAMS: header string, line counter, print header counter trigger # PARAMS: header string, line counter, print header counter trigger
# RETURN: line counter +1 # RETURN: line counter +1
@@ -372,6 +400,7 @@ def printHeader(header, lines = 0, header_line = 0):
lines += 1 lines += 1
return lines return lines
# METHOD: fileSortNumber # METHOD: fileSortNumber
# PARAMS: file name # PARAMS: file name
# RETURN: number found in the BK string or 0 for none # RETURN: number found in the BK string or 0 for none
@@ -381,9 +410,10 @@ def fileSortNumber(file):
return int(m.group(1)) if m is not None else 0 return int(m.group(1)) if m is not None else 0
############################################################## ##############################################################
### ARGUMENT PARSNING # ARGUMENT PARSNING
############################################################## ##############################################################
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Reverse Geoencoding based on set Latitude/Longitude data in XMP files', description='Reverse Geoencoding based on set Latitude/Longitude data in XMP files',
# formatter_class=argparse.RawDescriptionHelpFormatter, # formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -436,7 +466,10 @@ parser.add_argument('-f', '--field',
choices=['overwrite', 'location', 'city', 'state', 'country', 'countrycode'], choices=['overwrite', 'location', 'city', 'state', 'country', 'countrycode'],
dest='field_controls', dest='field_controls',
metavar='<overwrite, location, city, state, country, countrycode>', metavar='<overwrite, location, city, state, country, countrycode>',
help = 'On default only set fields that are not set yet. Options are: Overwrite (write all new), Location, City, State, Country, CountryCode. Multiple can be given for combination overwrite certain fields only or set only certain fields. If with overwrite the field will be overwritten if already set, else it will be always skipped.' help='On default only set fields that are not set yet. Options are: '\
'Overwrite (write all new), Location, City, State, Country, CountryCode. '\
'Multiple can be given for combination overwrite certain fields only or set only certain fields. '\
'If with overwrite the field will be overwritten if already set, else it will be always skipped.'
) )
# Google Maps API key to overcome restrictions # Google Maps API key to overcome restrictions
@@ -518,7 +551,7 @@ parser.add_argument('--test', action = 'store_true', dest = 'test', help = 'Do n
args = parser.parse_args() args = parser.parse_args()
############################################################## ##############################################################
### MAIN CODE # MAIN CODE
############################################################## ##############################################################
# init verbose to 0 if not set # init verbose to 0 if not set
@@ -677,7 +710,10 @@ count = {
# do lightroom stuff only if we have the lightroom folder # do lightroom stuff only if we have the lightroom folder
if args.lightroom_folder: if args.lightroom_folder:
# query string for lightroom DB check # query string for lightroom DB check
query = 'SELECT Adobe_images.id_local, AgLibraryFile.baseName, AgLibraryRootFolder.absolutePath, AgLibraryRootFolder.name as realtivePath, AgLibraryFolder.pathFromRoot, AgLibraryFile.originalFilename, AgHarvestedExifMetadata.gpsLatitude, AgHarvestedExifMetadata.gpsLongitude, AgHarvestedIptcMetadata.locationDataOrigination, AgInternedIptcLocation.value as Location, AgInternedIptcCity.value as City, AgInternedIptcState.value as State, AgInternedIptcCountry.value as Country, AgInternedIptcIsoCountryCode.value as CountryCode ' query = 'SELECT Adobe_images.id_local, AgLibraryFile.baseName, AgLibraryRootFolder.absolutePath, AgLibraryRootFolder.name as realtivePath, AgLibraryFolder.pathFromRoot, AgLibraryFile.originalFilename, '
query += 'AgHarvestedExifMetadata.gpsLatitude, AgHarvestedExifMetadata.gpsLongitude, '
query += 'AgHarvestedIptcMetadata.locationDataOrigination, AgInternedIptcLocation.value as Location, AgInternedIptcCity.value as City, '
query += 'AgInternedIptcState.value as State, AgInternedIptcCountry.value as Country, AgInternedIptcIsoCountryCode.value as CountryCode '
query += 'FROM AgLibraryFile, AgHarvestedExifMetadata, AgLibraryFolder, AgLibraryRootFolder, Adobe_images ' query += 'FROM AgLibraryFile, AgHarvestedExifMetadata, AgLibraryFolder, AgLibraryRootFolder, Adobe_images '
query += 'LEFT JOIN AgHarvestedIptcMetadata ON Adobe_images.id_local = AgHarvestedIptcMetadata.image ' query += 'LEFT JOIN AgHarvestedIptcMetadata ON Adobe_images.id_local = AgHarvestedIptcMetadata.image '
query += 'LEFT JOIN AgInternedIptcLocation ON AgHarvestedIptcMetadata.locationRef = AgInternedIptcLocation.id_local ' query += 'LEFT JOIN AgInternedIptcLocation ON AgHarvestedIptcMetadata.locationRef = AgInternedIptcLocation.id_local '
@@ -817,7 +853,7 @@ if args.read_only:
print("[!!!] Screen layout might be skewed. Increase Terminal width") print("[!!!] Screen layout might be skewed. Increase Terminal width")
# after how many lines do we reprint the header # after how many lines do we reprint the header
header_repeat = 50; header_repeat = 50
# how many pages will we have # how many pages will we have
page_all = ceil(len(work_files) / header_repeat) page_all = ceil(len(work_files) / header_repeat)
# current page number # current page number
@@ -1003,7 +1039,7 @@ for xmp_file in work_files:
# if not the same (to original data) and passes overwrite check # if not the same (to original data) and passes overwrite check
if data_set[key] != data_set_original[key] and checkOverwrite(data_set_original[key], key, args.field_controls): if data_set[key] != data_set_original[key] and checkOverwrite(data_set_original[key], key, args.field_controls):
xmp.set_property(xmp_fields[key], key, data_set[key]) xmp.set_property(xmp_fields[key], key, data_set[key])
write_file = True; write_file = True
if write_file: if write_file:
count['lightroom'] += 1 count['lightroom'] += 1
# if we have the write flag set, write data # if we have the write flag set, write data