api upload framework

This commit is contained in:
dave 2019-06-29 12:43:13 -07:00
parent 2c1ebea31c
commit 1b310f0c4a
1 changed files with 116 additions and 12 deletions

View File

@ -15,6 +15,98 @@ from photoapp.utils import mime2ext, auth, require_auth, photo_auth_filter, slug
from photoapp.dbutils import db
class StorageAdapter(object):
"""
Abstract interface for working with photo file storage. All paths are relative to the storage adapter's root parameter.
"""
def file_exists(self, path):
# TODO return true/false if the file path exists
raise NotImplementedError()
def open(self, path, mode):
# TODO return a handle to the path
# TODO this should work as a context manager
raise NotImplementedError()
def delete(self, path):
# TODO erase the path
raise NotImplementedError()
class FilesystemAdapter(StorageAdapter):
def file_exists(self, path):
# TODO return true/false if the file path exists
raise NotImplementedError()
def open(self, path, mode):
# TODO return a handle to the path. this should work as a context manager
raise NotImplementedError()
def delete(self, path):
# TODO erase the path
raise NotImplementedError()
class S3Adapter(StorageAdapter):
def file_exists(self, path):
# TODO return true/false if the file path exists
raise NotImplementedError()
def open(self, path, mode):
# TODO return a handle to the path. this should work as a context manager
raise NotImplementedError()
def delete(self, path):
# TODO erase the path
raise NotImplementedError()
class GfapiAdapter(StorageAdapter):
pass
#This is largely duplicated from library.py, but written with intent for later refactoring to support abstract storage.
class LibraryManager(object):
def __init__(self):
pass
def add_photoset(self, photoset):
"""
Commit a populated photoset object to the library. The paths in the photoset's file list entries will be updated
as the file is moved to the library path.
"""
# Create target directory
path = os.path.join(self.path, self.get_datedir_path(photoset.date))
os.makedirs(path, exist_ok=True)
moves = [] # Track files moved. If the sql transaction files, we'll undo these
for file in photoset.files:
dest = os.path.join(path, os.path.basename(file.path))
# Check if the name is already in use, rename new file if needed
dupe_rename = 1
while os.path.exists(dest):
fname = os.path.basename(file.path).split(".")
fname[-2] += "_{}".format(dupe_rename)
dest = os.path.join(path, '.'.join(fname))
dupe_rename += 1
os.rename(file.path, dest)
moves.append((file.path, dest))
file.path = dest.lstrip(self.path)
s = self.session()
s.add(photoset)
try:
s.commit()
except IntegrityError:
# Commit failed, undo the moves
for move in moves:
os.rename(move[1], move[0])
raise
class PhotosApi(object):
def __init__(self):
self.v1 = PhotosApiV1()
@ -22,7 +114,7 @@ class PhotosApi(object):
class PhotosApiV1(object):
def __init__(self):
pass
self.lib = LibraryManager()
@cherrypy.expose
def index(self):
@ -41,23 +133,35 @@ class PhotosApiV1(object):
for file in files:
print("File name:", file.filename)
import hashlib
sha = hashlib.sha256()
total = 0
while True:
b = file.file.read(1024)
if not b:
break
sha.update(b)
total += len(b)
print("Read length:", total)
print("Read sha256:", sha.hexdigest())
# import hashlib
# sha = hashlib.sha256()
# total = 0
# while True:
# b = file.file.read(1024)
# if not b:
# break
# sha.update(b)
# total += len(b)
# print("Read length:", total)
# print("Read sha256:", sha.hexdigest())
if str(file.filename) not in meta["files"].keys():
raise cherrypy.HTTPError(400, f"no mdatadata provided for filename '{file.filename}'")
print("we have metadata for this file:", meta["files"][file.filename])
# create database objects based on the request
# self.lib.add_photoset(set_, photos)
# build file path (yyyy/mm/dd/yyyy-mm_hh.MM.ss_x.jpg) (incrmenting X if the key already exists etc)
# copy to storage
# check if sha256 exists already
# delete if dupe, raise error
# (see file rewind code in ingest.py)
# create records
# commit
# respond with list of uuids of the sets
print("____")
@cherrypy.expose