# Third-party from wand.image import Image import filetype # Built-in from os import path, walk from sys import argv, stderr from shutil import move import sqlite3 # update database residing here DB_LOCATION = "../../testbox/photovoter.dblite" # Q: any allowances for this being not OUR database? # place compressed images here (needs to exist) DEST_STRUNK = "../../testbox/image/" # move originals here (needs to exist) DEST_ORIGINAL = "../../testbox/original/" def usage(): """Brief usage explanation""" print("USAGE: ./{name} /path/to/images".format(name=argv[0]), file=stderr) def process_pictures(): """Process images from the base directory in the first command line argument. Place the resized copies to DEST_STRUNK and move the originals to DEST_ORIGINAL. Return a dict for each image processed for database collection. Uses: DEST_STRUNK, DEST_ORIGINAL """ # walk every pic # We only care about files in the root of the path # Ignore any nested directories (root, _, filenames) = next(walk(argv[1], topdown=True), (None, None, [])) for filename in filenames: # FIXME[0]:what if picture with the same name already exists? # skip any non-image files if not filetype.image_match(path.join(root, filename)): continue # process pictures with wand lib, imagemagick wrapper exif = {} with Image(filename=path.join(root, filename)) as image: exif.update( (k[5:], v) for k, v in image.metadata.items() if k.startswith("exif:") ) with image.clone() as cloned: cloned.strip() # Q: may damage icc, do we allow that or use smh else? cloned.transform(resize="50%") # Q: what do we want here? # move them to the processed folder cloned.save(filename=path.join(DEST_STRUNK, filename)) # move the originals out of the working directory # Q: do we strip exif from originals? move(path.join(root, filename), DEST_ORIGINAL) # return the freshly processed picture info yield { "ResizedImage": path.join(DEST_STRUNK, filename), "OriginalImage": path.join(DEST_ORIGINAL, filename), "DateTimeOriginal": exif["DateTimeOriginal"], # Q: normalize it? "GPSLatitude": exif["GPSLatitude"], "GPSLatitudeRef": exif["GPSLatitudeRef"], "GPSLongitude": exif["GPSLongitude"], "GPSLongitudeRef": exif["GPSLongitudeRef"], } def update_database(pic_info: dict): """Append new image information to the existing database or create a new one, if it does not exist yet Uses: DB_LOCATION """ # make sure the database exists check_database(DB_LOCATION) # FIXME[1]: closure it, so we open it only once? con = sqlite3.connect(DB_LOCATION) cur = con.cursor() # insert new pictures to the image table cur.execute( """INSERT INTO images(resizedpath, origpath, date, GPSLatitude, GPSLatitudeRef, GPSLongitude, GPSLongitudeRef) VALUES (:ResizedImage, :OriginalImage, :DateTimeOriginal, :GPSLatitude, :GPSLatitudeRef, :GPSLongitude, :GPSLongitudeRef) """, pic_info, ) con.commit() # FIXME[1] con.close() def check_database(database_path: str): """Check if there is a database at DB_LOCATION. Just return if there is. If not, create a new one. """ # db exists? if path.exists(database_path): return # make one else: print("No DB, creating", database_path) con = sqlite3.connect(database_path) cur = con.cursor() # Create table cur.execute( """CREATE TABLE images ( imgid INTEGER PRIMARY KEY, resizedpath TEXT NOT NULL, origpath TEXT NOT NULL, date TEXT, GPSLatitude TEXT, GPSLatitudeRef TEXT, GPSLongitude TEXT, GPSLongitudeRef TEXT )""" ) con.commit() # we only use this occasionaly with new databases, # so don't bother with transfer logic, just close and reopen it later con.close() def main(): if len(argv) != 2: usage() exit(1) pics_processed = 0 # process each pic and add it to the database for pic in process_pictures(): update_database(pic) pics_processed += 1 if pics_processed == 0: print("No more pictures processed from", argv[1]) print("Do we have enough permissions?") else: print("Pictures processed:", pics_processed) if __name__ == "__main__": main()