Add list data only flag

With the -r flag data will only be read from the XMP files and printed
out and no other action (LR lookup, Maps lookup) is performed
This commit is contained in:
2018-03-05 15:52:07 +09:00
parent a4bd574e48
commit 41bae7eb5d
2 changed files with 213 additions and 116 deletions

View File

@@ -26,7 +26,7 @@ reverse_geolocate.py [-h] -x
[-l LIGHTROOM FOLDER] [-s] [-l LIGHTROOM FOLDER] [-s]
[-f <overwrite, location, city, state, country, countrycode>] [-f <overwrite, location, city, state, country, countrycode>]
[-g GOOGLE API KEY] [-o] [-e EMIL ADDRESS] [-w] [-g GOOGLE API KEY] [-o] [-e EMIL ADDRESS] [-w]
[-n] [-v] [--debug] [--test] [-r] [-n] [-v] [--debug] [--test]
### Arguments ### Arguments
@@ -40,6 +40,7 @@ Argument | Argument Value | Description
-o, --openstreetmap | | Use OpenStreetMap instead of the default google maps -o, --openstreetmap | | Use OpenStreetMap instead of the default google maps
-e, --email | email address | For OpenStreetMap with a large number of access -e, --email | email address | For OpenStreetMap with a large number of access
-g, --google | Google Maps API Key | If available, to avoid the access limitations to the reverse location lookup -g, --google | Google Maps API Key | If available, to avoid the access limitations to the reverse location lookup
-r, --read-only | | Read only data from the XMP files and print them out. No LR DB connection is done or any map lookups
-w, --write-settings | | Write the Google API key or the OpenStreetMap email into the settings file -w, --write-settings | | Write the Google API key or the OpenStreetMap email into the settings file
-v, --verbose | | More verbose output. Currently not used -v, --verbose | | More verbose output. Currently not used
--debug | | Full detailed debug output. Will print out alot of data --debug | | Full detailed debug output. Will print out alot of data

View File

@@ -9,14 +9,11 @@
# * 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 import argparse, sqlite3, requests, configparser, textwrap
import os, sys, re import os, sys, 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, XMPError, consts
import sqlite3
import requests
from shutil import copyfile from shutil import copyfile
import configparser
############################################################## ##############################################################
### FUNCTIONS ### FUNCTIONS
@@ -293,6 +290,12 @@ 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
def convertDMStoLat(lat_long):
return longLatReg('0,0.0N', lat_long)['latitude']
def convertDMStoLong(lat_long):
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
@@ -325,6 +328,32 @@ def checkOverwrite(data, key, field_controls):
)) ))
return status return status
# METHOD: shortenPath
# PARAMS: path = string, length = int
# RETURN: shortend path with ... in front
# DESC : shortes a path from the left so it fits into lenght
def shortenPath(path, length = 30):
length = length - 3;
if len(path) > length:
path = "{} {}".format("..", path[len(path) - length:])
return path;
# METHOD: printHeader
# PARAMS: header string, header seperator, line counter, print header counter trigger
# RETURN: line counter +1
# DESC : prints header line and header seperator line
def printHeader(header, header_seperator, lines = 0, header_line = 0):
if lines == header_line:
# blank line, might be used for page numbers, or other info
print("{}".format(''))
# print header
print("{}".format(header))
# print line with length of header string
# print("{}".format('-' * len(header)))
print("{}".format(header_seperator))
lines += 1
return lines
############################################################## ##############################################################
### ARGUMENT PARSNING ### ARGUMENT PARSNING
############################################################## ##############################################################
@@ -398,12 +427,19 @@ parser.add_argument('-e', '--email',
) )
# write api/email settings to config file # write api/email settings to config file
parser.add_argument('-w', '--write-seettings', parser.add_argument('-w', '--write-settings',
dest = 'config_write', dest = 'config_write',
action = 'store_true', action = 'store_true',
help = 'Write Google API or OpenStreetMap email to config file' help = 'Write Google API or OpenStreetMap email to config file'
) )
# only read data and print on screen, do not write anything
parser.add_argument('-r', '--read-only',
dest = 'read_only',
action = 'store_true',
help = 'Read current values from the XMP file only, do not read from LR or lookup any data and write back'
)
# Do not create backup files # Do not create backup files
parser.add_argument('-n', '--nobackup', parser.add_argument('-n', '--nobackup',
dest = 'no_xmp_backup', dest = 'no_xmp_backup',
@@ -559,6 +595,7 @@ cur = ''
# count variables # count variables
count = { count = {
'all': 0, 'all': 0,
'read': 0,
'map': 0, 'map': 0,
'cache': 0, 'cache': 0,
'lightroom': 0, 'lightroom': 0,
@@ -632,15 +669,57 @@ for xmp_file_source in args.xmp_sources:
if args.debug: if args.debug:
print("### Work Files {}".format(work_files)) print("### Work Files {}".format(work_files))
# if we have read only we print list format style
if args.read_only:
# various string lengths
format_length ={
'filename': 40,
'latitude': 18,
'longitude': 18,
'code': 4,
'country': 20,
'state': 20,
'city': 25,
'location': 30
}
# after how many lines do we reprint the header
header_repeat = 40;
# the formatted line for the output
format_line = " {filename:<" + str(format_length['filename']) + "} | {latitude:>" + str(format_length['latitude']) + "} | {longitude:>" + str(format_length['longitude']) + "} | {code:<" + str(format_length['code']) + "} | {country:<" + str(format_length['country']) + "} | {state:<" + str(format_length['state']) + "} | {city:<" + str(format_length['city']) + "} | {location:<" + str(format_length['location']) + "} "
# header line
header = format_line.format(
filename = "File",
latitude = "Latitude",
longitude = "Longitude",
code = 'Code',
country = 'Country',
state = 'State',
city = 'City',
location = 'Location'
)
# header seperator line
header_seperator = "{}+{}+{}+{}+{}+{}+{}+{}".format(
'-' * (format_length['filename'] + 2),
'-' * (format_length['latitude'] + 2),
'-' * (format_length['longitude'] + 2),
'-' * (format_length['code'] + 2),
'-' * (format_length['country'] + 2),
'-' * (format_length['state'] + 2),
'-' * (format_length['city'] + 2),
'-' * (format_length['location'] + 2)
)
# print header
printHeader(header, header_seperator)
# now we just loop through each file and work on them # now we just loop through each file and work on them
for xmp_file in work_files: for xmp_file in work_files:
if not args.read_only:
print("---> {}: ".format(xmp_file), end = '') print("---> {}: ".format(xmp_file), end = '')
#### ACTION FLAGs #### ACTION FLAGs
write_file = False write_file = False
lightroom_data_ok = True lightroom_data_ok = True
#### LIGHTROOM DB READING #### LIGHTROOM DB READING
# read in data from DB if we uave lightroom folder # read in data from DB if we uave lightroom folder
if use_lightroom: if use_lightroom and not args.read_only:
# get the base file name, we need this for lightroom # get the base file name, we need this for lightroom
xmp_file_basename = os.path.splitext(os.path.split(xmp_file)[1])[0] xmp_file_basename = os.path.splitext(os.path.split(xmp_file)[1])[0]
# for strict check we need to get the full path, and add / as the LR stores the last folder with / # for strict check we need to get the full path, and add / as the LR stores the last folder with /
@@ -676,9 +755,24 @@ for xmp_file in work_files:
data_set[xmp_field] = xmp.get_property(xmp_fields[xmp_field], xmp_field) data_set[xmp_field] = xmp.get_property(xmp_fields[xmp_field], xmp_field)
if args.debug: if args.debug:
print("### => XMP: {}:{} => {}".format(xmp_fields[xmp_field], xmp_field, data_set[xmp_field])) print("### => XMP: {}:{} => {}".format(xmp_fields[xmp_field], xmp_field, data_set[xmp_field]))
if args.read_only:
# for read only we print out the data formatted
# headline check, do we need to print that
count['read'] = printHeader(header, header_seperator, count['read'], header_repeat)
# the data content
print(format_line.format(
filename = shortenPath(xmp_file, format_length['filename']), # shorten from the left
latitude = str(convertDMStoLat(data_set['GPSLatitude']))[:format_length['latitude']], # cut off from the right
longitude = str(convertDMStoLong(data_set['GPSLongitude']))[:format_length['longitude']],
code = data_set['CountryCode'][:2].center(4), # is only 2 chars
country = textwrap.shorten(data_set['Country'], width = format_length['country'], placeholder = '..'), # shorten from the right
state = textwrap.shorten(data_set['State'], width = format_length['state'], placeholder = '..'),
city = textwrap.shorten(data_set['City'], width = format_length['city'], placeholder = '..'),
location = textwrap.shorten(data_set['Location'], width = format_length['location'], placeholder = '..')
))
else:
# create a duplicate copy for later checking if something changed # create a duplicate copy for later checking if something changed
data_set_original = data_set.copy() data_set_original = data_set.copy()
# check if LR exists and use this to compare to XMP data # check if LR exists and use this to compare to XMP data
# is LR GPS and no XMP GPS => use LR and set XMP # is LR GPS and no XMP GPS => use LR and set XMP
# same for location names # same for location names
@@ -771,23 +865,25 @@ for xmp_file in work_files:
count['skipped'] += 1 count['skipped'] += 1
# close DB connection # close DB connection
lrdb.close()
# end stats
print("{}".format('=' * 37))
print("XMP Files found : {:7,}".format(count['all']))
print("Updated : {:7,}".format(count['changed']))
print("Skipped : {:7,}".format(count['skipped']))
print("New GeoLocation from Map : {:7,}".format(count['map']))
print("GeoLocation from Cache : {:7,}".format(count['cache']))
print("Failed reverse GeoLocate : {:7,}".format(count['failed']))
if use_lightroom: if use_lightroom:
print("GeoLocaction from Lightroom : {:7,}".format(count['lightroom'])) lrdb.close()
print("No Lightroom data found : {:7,}".format(count['not_found']))
print("More than one found in LR : {:7,}".format(count['many_found'])) # end stats only if we write
# if we have failed data print("{}".format('=' * 39))
if len(failed_files) > 0: print("XMP Files found : {:9,}".format(count['all']))
print("{}".format('-' * 37)) if not args.read_only:
print("Updated : {:9,}".format(count['changed']))
print("Skipped : {:9,}".format(count['skipped']))
print("New GeoLocation from Map : {:9,}".format(count['map']))
print("GeoLocation from Cache : {:9,}".format(count['cache']))
print("Failed reverse GeoLocate : {:9,}".format(count['failed']))
if use_lightroom:
print("GeoLocaction from Lightroom : {:9,}".format(count['lightroom']))
print("No Lightroom data found : {:9,}".format(count['not_found']))
print("More than one found in LR : {:9,}".format(count['many_found']))
# if we have failed data
if len(failed_files) > 0:
print("{}".format('-' * 39))
print("Files that failed to update:") print("Files that failed to update:")
print("{}".format(', '.join(failed_files))) print("{}".format(', '.join(failed_files)))