upload endpoint #5
Merged
w2
merged 14 commits from w2/photovoter_backend:upload into main 4 years ago
@ -0,0 +1,15 @@
|
|||||||||||||
|
# use database residing here
|
||||||||||||
|
DB_LOCATION = (
|
||||||||||||
|
"testbox/photovoter.dblite" # Q: any allowances for this being not OUR database?
|
||||||||||||
|
)
|
||||||||||||
|
|
||||||||||||
|
DATA_LOCATION = "/tmp/123"
|
||||||||||||
|
|
|||||||||||||
|
|
||||||||||||
|
# place compressed images here (needs to exist)
|
||||||||||||
|
DEST_SHRUNK = "image/"
|
||||||||||||
|
# move originals here (needs to exist)
|
||||||||||||
|
DEST_ORIGINAL = "original/"
|
||||||||||||
|
|
||||||||||||
|
# upload interface credentials
|
||||||||||||
|
CRED_USERNAME = "changeme"
|
||||||||||||
|
CRED_PASSWORD = "CHANGEME"
|
||||||||||||
@ -8,32 +8,17 @@ from sys import argv, stderr
|
|||||||||||||
from shutil import move
|
from shutil import move
|
||||||||||||
import sqlite3
|
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_SHRUNK = "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():
|
def process_pictures(source: str, dest_shrunk: str, dest_original: str):
|
||||||||||||
"""Process images from the base directory in the first command line argument.
|
"""Process images from the base directory in the first command line argument.
|
||||||||||||
Place the resized copies to DEST_SHRUNK and
|
Place the resized copies to dest_shrunk and
|
||||||||||||
move the originals to DEST_ORIGINAL.
|
move the originals to dest_original.
|
||||||||||||
Return a dict for each image processed for database collection.
|
Return a dict for each image processed for database collection.
|
||||||||||||
Uses: DEST_SHRUNK, DEST_ORIGINAL
|
|
||||||||||||
"""
|
"""
|
||||||||||||
# walk every pic
|
# walk every pic
|
||||||||||||
# We only care about files in the root of the path
|
# We only care about files in the root of the path
|
||||||||||||
# Ignore any nested directories
|
# Ignore any nested directories
|
||||||||||||
(root, _, filenames) = next(walk(argv[1], topdown=True), (None, None, []))
|
(root, _, filenames) = next(walk(source, topdown=True), (None, None, []))
|
||||||||||||
for filename in filenames:
|
for filename in filenames:
|
||||||||||||
# FIXME[0]:what if picture with the same name already exists?
|
# FIXME[0]:what if picture with the same name already exists?
|
||||||||||||
# skip any non-image files
|
# skip any non-image files
|
||||||||||||
@ -50,34 +35,37 @@ def process_pictures():
|
|||||||||||||
cloned.strip() # Q: may damage icc, do we allow that or use smh else?
|
cloned.strip() # Q: may damage icc, do we allow that or use smh else?
|
||||||||||||
cloned.transform(resize="50%") # Q: what do we want here?
|
cloned.transform(resize="50%") # Q: what do we want here?
|
||||||||||||
|
g
commented 4 years ago
Review
I think width=1000px with preserved aspect ratio would be enough.
w2
commented 4 years ago
Review
Independent of this PR. I filed #8. |
|||||||||||||
# move them to the processed folder
|
# move them to the processed folder
|
||||||||||||
cloned.save(filename=path.join(DEST_SHRUNK, filename))
|
cloned.save(filename=path.join(dest_shrunk, filename))
|
||||||||||||
|
|
||||||||||||
# move the originals out of the working directory
|
# move the originals out of the working directory
|
||||||||||||
# Q: do we strip exif from originals?
|
# Q: do we strip exif from originals?
|
||||||||||||
move(path.join(root, filename), DEST_ORIGINAL)
|
move(path.join(root, filename), dest_original)
|
||||||||||||
|
|
||||||||||||
|
try:
|
||||||||||||
# return the freshly processed picture info
|
# return the freshly processed picture info
|
||||||||||||
yield {
|
yield {
|
||||||||||||
"ResizedImage": path.join(DEST_SHRUNK, filename),
|
"ResizedImage": path.join(dest_shrunk, filename),
|
||||||||||||
"OriginalImage": path.join(DEST_ORIGINAL, filename),
|
"OriginalImage": path.join(dest_original, filename),
|
||||||||||||
"DateTimeOriginal": exif["DateTimeOriginal"], # Q: normalize it?
|
"DateTimeOriginal": exif["DateTimeOriginal"], # Q: normalize it?
|
||||||||||||
"GPSLatitude": exif["GPSLatitude"],
|
"GPSLatitude": exif["GPSLatitude"],
|
||||||||||||
"GPSLatitudeRef": exif["GPSLatitudeRef"],
|
"GPSLatitudeRef": exif["GPSLatitudeRef"],
|
||||||||||||
"GPSLongitude": exif["GPSLongitude"],
|
"GPSLongitude": exif["GPSLongitude"],
|
||||||||||||
"GPSLongitudeRef": exif["GPSLongitudeRef"],
|
"GPSLongitudeRef": exif["GPSLongitudeRef"],
|
||||||||||||
}
|
}
|
||||||||||||
|
except KeyError as e:
|
||||||||||||
|
print(f"Image '{filename}' has no valid exif")
|
||||||||||||
|
continue
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
def update_database(pic_info: dict):
|
def update_database(pic_info: dict, db_location: str):
|
||||||||||||
"""Append new image information to the existing database
|
"""Append new image information to the existing database
|
||||||||||||
or create a new one, if it does not exist yet
|
or create a new one, if it does not exist yet
|
||||||||||||
Uses: DB_LOCATION
|
|
||||||||||||
"""
|
"""
|
||||||||||||
# make sure the database exists
|
# make sure the database exists
|
||||||||||||
check_database(DB_LOCATION)
|
check_database(db_location)
|
||||||||||||
|
|
||||||||||||
# FIXME[1]: closure it, so we open it only once?
|
# FIXME[1]: closure it, so we open it only once?
|
||||||||||||
con = sqlite3.connect(DB_LOCATION)
|
con = sqlite3.connect(db_location)
|
||||||||||||
cur = con.cursor()
|
cur = con.cursor()
|
||||||||||||
# insert new pictures to the image table
|
# insert new pictures to the image table
|
||||||||||||
cur.execute(
|
cur.execute(
|
||||||||||||
@ -114,7 +102,7 @@ def check_database(database_path: str):
|
|||||||||||||
return
|
return
|
||||||||||||
# make one
|
# make one
|
||||||||||||
else:
|
else:
|
||||||||||||
print("No DB, creating", database_path)
|
print("No DB, creating", path.abspath(database_path))
|
||||||||||||
|
|
||||||||||||
con = sqlite3.connect(database_path)
|
con = sqlite3.connect(database_path)
|
||||||||||||
cur = con.cursor()
|
cur = con.cursor()
|
||||||||||||
@ -169,23 +157,46 @@ def check_database(database_path: str):
|
|||||||||||||
con.close()
|
con.close()
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
def main():
|
def run(db_location: str, source: str, dest_shrunk: str, dest_original: str):
|
||||||||||||
if len(argv) != 2:
|
"""Core program logic"""
|
||||||||||||
usage()
|
|
||||||||||||
exit(1)
|
|
||||||||||||
|
|
||||||||||||
pics_processed = 0
|
pics_processed = 0
|
||||||||||||
# process each pic and add it to the database
|
# process each pic and add it to the database
|
||||||||||||
for pic in process_pictures():
|
for pic in process_pictures(source, dest_shrunk, dest_original):
|
||||||||||||
update_database(pic)
|
update_database(pic, db_location)
|
||||||||||||
pics_processed += 1
|
pics_processed += 1
|
||||||||||||
|
|
||||||||||||
if pics_processed == 0:
|
if pics_processed == 0:
|
||||||||||||
print("No more pictures processed from", argv[1])
|
print("No pictures processed from", source)
|
||||||||||||
print("Do we have enough permissions?")
|
print("Do we have enough permissions?")
|
||||||||||||
else:
|
else:
|
||||||||||||
print("Pictures processed:", pics_processed)
|
print("Pictures processed:", pics_processed)
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
|
def usage():
|
||||||||||||
|
"""Brief usage explanation"""
|
||||||||||||
|
print("USAGE: python {name} /path/to/images".format(name=argv[0]), file=stderr)
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
|
def main():
|
||||||||||||
|
if len(argv) != 2:
|
||||||||||||
|
usage()
|
||||||||||||
|
exit(1)
|
||||||||||||
|
|
||||||||||||
|
import sys
|
||||||||||||
|
import os
|
||||||||||||
|
|
||||||||||||
|
# append root directory to sys.path
|
||||||||||||
|
# to allow import globals from ../config.py
|
||||||||||||
|
sys.path.append(os.path.dirname(__file__) + "/..")
|
||||||||||||
|
import config as cfg
|
||||||||||||
|
|
||||||||||||
|
run(
|
||||||||||||
|
cfg.DB_LOCATION,
|
||||||||||||
|
argv[1],
|
||||||||||||
|
path.normcase(cfg.DEST_SHRUNK),
|
||||||||||||
|
path.normcase(cfg.DEST_ORIGINAL),
|
||||||||||||
|
)
|
||||||||||||
|
|
||||||||||||
|
|
||||||||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||||||||
main()
|
main()
|
||||||||||||
|
|||||||||||||
Loading…
Reference in new issue
Add comment that
DATA_LOCATIONshould end with a slash liketest/. Else in Windows a path stored in a database looks like thistest\image/img.jpg. It could cause problems in a frontend.Better yet, we can just normalize it. Can be done right in the config.py (since we made it .py) but it's only used in a handful of places so let's not clutter it with code just yet.
Fixed now.
Some say Python on Windows will automatically handle forward slashes (/) properly.
You probably shouldn't mix and match DBs from different systems, tho. So, if upload to windows, serve from windows too.