# 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 = "db/photovoter.dblite" # Q: any allowances for this being not OUR database? # place compressed images here (needs to exist) DEST_STRUNK = "db/image/" # move originals here (needs to exist) DEST_ORIGINAL = "db/original/" def usage(): """Brief usage explanation""" print("USAGE: python {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() # create the rest of the tables, while we're at it cur.execute( """CREATE TABLE sessions ( sessionid INTEGER PRIMARY KEY, cookie TEXT UNIQUE NOT NULL, time TEXT, description TEXT )""" ) con.commit() cur.execute( """CREATE TABLE marks ( imgid INTEGER, sessionid INTEGER, mark INTEGER, PRIMARY KEY (imgid, sessionid), FOREIGN KEY (imgid) REFERENCES images (imgid) ON DELETE NO ACTION ON UPDATE NO ACTION, FOREIGN KEY (sessionid) REFERENCES sessions (sessionid) ON DELETE NO ACTION ON UPDATE NO ACTION )""" ) 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()