Made timezone-aware (configurable)

Added optional dry-run modifier
Made timezone-aware for timesources without timezone
Added optional parameter to override timezone to local timezone, defaults to localtime
This commit is contained in:
Marcus Scholz 2020-03-07 21:29:25 +01:00
parent aef69f81a1
commit 5b4811b081
2 changed files with 26 additions and 17 deletions

View File

@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
- Added GPX parser - Added GPX parser
- Added optional dry-run modifier - Added optional dry-run modifier
- Made timezone-aware for timesources without timezone
- Added optional parameter to override timezone to local timezone, defaults to localtime
## [0.2] - 2020-02-05 ## [0.2] - 2020-02-05
### Added ### Added

View File

@ -6,11 +6,12 @@ DateTimeOriginal from Exif tag to DateTime in a csv log
of a GeigerMuellerCounter and writes its value to the UserComment of a GeigerMuellerCounter and writes its value to the UserComment
Exif tag in µS/h""" Exif tag in µS/h"""
from datetime import datetime from datetime import datetime, timedelta
import os import os
import shutil import shutil
import csv import csv
import argparse import argparse
import pytz
import pyexiv2 import pyexiv2
import gpxpy import gpxpy
@ -30,6 +31,9 @@ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFo
them into the Exif tags of given photos.''') them into the Exif tags of given photos.''')
parser.add_argument('-si', '--sifactor', type=float, default=0.0065, parser.add_argument('-si', '--sifactor', type=float, default=0.0065,
help='Factor to multiply recorded CPM with.') help='Factor to multiply recorded CPM with.')
parser.add_argument('-tz', '--timezone', type=str, metavar='Timezone',
help='''Manually set timezone of CSV / and Photo timestamp,
defaults to localtime if omitted.''')
parser.add_argument('-d', '--dry', action='store_true', parser.add_argument('-d', '--dry', action='store_true',
help='Dry-run, do not actually write anything.') help='Dry-run, do not actually write anything.')
parser.add_argument('csv', metavar='CSV', type=str, parser.add_argument('csv', metavar='CSV', type=str,
@ -43,13 +47,16 @@ parser.add_argument('-o', '--outdir', type=str, default='.',
args = parser.parse_args() args = parser.parse_args()
# Create timezone datetime object
localtz = pytz.timezone(args.timezone)
# Inform the user about what is going to happen # Inform the user about what is going to happen
if args.outdir == ".": if args.outdir == ".":
print('Modifying photos in place (overwrite)') print('Modifying photos in place (overwrite)')
else: else:
print('Modifying photos in', str(args.outdir), '(copy)') print('Modifying photos in', str(args.outdir), '(copy)')
# Print table header # Print table header
print('{:<15} {:<20} {:<22}'.format('filename', 'date / time', 'Exif UserComment')) print('{:<15} {:<25} {:<22}'.format('filename', 'date / time', 'Exif UserComment'))
for srcphoto in args.photos: for srcphoto in args.photos:
# Get image file name out of path # Get image file name out of path
@ -62,6 +69,7 @@ for srcphoto in args.photos:
else: else:
# be os aware and use the correct directory delimiter for destfile # be os aware and use the correct directory delimiter for destfile
dstphoto = os.path.join(args.outdir, photo_basename) dstphoto = os.path.join(args.outdir, photo_basename)
# Don't copy image if in dry-run mode
if args.dry == 'True': if args.dry == 'True':
shutil.copy(srcphoto, dstphoto) shutil.copy(srcphoto, dstphoto)
photo = dstphoto photo = dstphoto
@ -73,15 +81,19 @@ for srcphoto in args.photos:
metadata.read() metadata.read()
tag = metadata['Exif.Photo.DateTimeOriginal'] tag = metadata['Exif.Photo.DateTimeOriginal']
# tag.value creates datetime object in pictime # tag.value creates datetime object in pictime
pictime = tag.value picnaivetime = tag.value
# Set timezone
pictime = picnaivetime.astimezone(localtz)
# Import GeigerCounter log # Import GeigerCounter log
with open(args.csv, "r") as f: with open(args.csv, "r") as f:
csvreader = csv.reader(filter(lambda row: row[0] != '#', f), csvreader = csv.reader(filter(lambda row: row[0] != '#', f),
delimiter=',', skipinitialspace=True) delimiter=',', skipinitialspace=True)
for _, csvrawtime, csvrawcpm, _ in csvreader: for _, csvrawtime, csvrawcpm, _ in csvreader:
csvtime = datetime.fromisoformat(csvrawtime) csvnaivetime = datetime.fromisoformat(csvrawtime)
# Set timezone
csvtime = csvnaivetime.astimezone(localtz)
# Process image if its timestamp is found in CSV log (compares 2 datetime objects) # Process image if its timestamp is found in CSV log (compares 2 datetime objects)
if csvtime == pictime: if csvtime == pictime:
rad = round(float(csvrawcpm) * args.sifactor, 2) rad = round(float(csvrawcpm) * args.sifactor, 2)
@ -92,13 +104,13 @@ for srcphoto in args.photos:
metadata[key] = pyexiv2.ExifTag(key, new_comment) metadata[key] = pyexiv2.ExifTag(key, new_comment)
# print found radiation levels # print found radiation levels
print('{:<15} {:<20} {:<22}'.format(photo_basename, str(pictime), new_comment)) print('{:<15} {:<25} {:<22}'.format(photo_basename, str(pictime), new_comment))
# Write Exif tags to file # Write Exif tags to file, if not in dry-run mode
if args.dry == 'True': if args.dry == 'True':
metadata.write() metadata.write()
break break
else: else:
print('{:<15} {:<20} {:<22}'.format(photo_basename, str(pictime), 'NOT FOUND!')) print('{:<15} {:<25} {:<22}'.format(photo_basename, str(pictime), 'NOT FOUND!'))
# close CSV file # close CSV file
f.close() f.close()
@ -111,13 +123,8 @@ for srcphoto in args.photos:
for track in gpxreader.tracks: for track in gpxreader.tracks:
for segment in track.segments: for segment in track.segments:
for point in segment.points: for point in segment.points:
#if pictime == point.time: # datetimes match with 1 minute precision
print(type(point.time)) delta = timedelta(minutes=1)
print(type(pictime)) if abs(point.time - pictime) < delta:
print(pictime, 'vs.', point.time) print(pictime, 'vs.', point.time, 'Delta:', pictime - point.time)
# print('Point at ({0},{1}) -> {2}'.format(point.latitude, point.longitude, point.time)) # print('Point at ({0},{1}) -> {2}'.format(point.latitude, point.longitude, point.time))
'''<class 'datetime.datetime'>
2018-08-04 17:50:16 vs. 2018-08-04 17:50:03.535000+00:00
<class 'datetime.datetime'>
2018-08-04 17:50:16 vs. 2018-08-04 17:50:23.327000+00:00'''