api upload framework
This commit is contained in:
parent
2c1ebea31c
commit
1b310f0c4a
128
photoapp/api.py
128
photoapp/api.py
|
@ -15,6 +15,98 @@ from photoapp.utils import mime2ext, auth, require_auth, photo_auth_filter, slug
|
||||||
from photoapp.dbutils import db
|
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):
|
class PhotosApi(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.v1 = PhotosApiV1()
|
self.v1 = PhotosApiV1()
|
||||||
|
@ -22,7 +114,7 @@ class PhotosApi(object):
|
||||||
|
|
||||||
class PhotosApiV1(object):
|
class PhotosApiV1(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
self.lib = LibraryManager()
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def index(self):
|
def index(self):
|
||||||
|
@ -41,23 +133,35 @@ class PhotosApiV1(object):
|
||||||
|
|
||||||
for file in files:
|
for file in files:
|
||||||
print("File name:", file.filename)
|
print("File name:", file.filename)
|
||||||
import hashlib
|
|
||||||
sha = hashlib.sha256()
|
|
||||||
|
|
||||||
total = 0
|
# import hashlib
|
||||||
while True:
|
# sha = hashlib.sha256()
|
||||||
b = file.file.read(1024)
|
# total = 0
|
||||||
if not b:
|
# while True:
|
||||||
break
|
# b = file.file.read(1024)
|
||||||
sha.update(b)
|
# if not b:
|
||||||
total += len(b)
|
# break
|
||||||
print("Read length:", total)
|
# sha.update(b)
|
||||||
print("Read sha256:", sha.hexdigest())
|
# total += len(b)
|
||||||
|
# print("Read length:", total)
|
||||||
|
# print("Read sha256:", sha.hexdigest())
|
||||||
|
|
||||||
if str(file.filename) not in meta["files"].keys():
|
if str(file.filename) not in meta["files"].keys():
|
||||||
raise cherrypy.HTTPError(400, f"no mdatadata provided for filename '{file.filename}'")
|
raise cherrypy.HTTPError(400, f"no mdatadata provided for filename '{file.filename}'")
|
||||||
print("we have metadata for this file:", meta["files"][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("____")
|
print("____")
|
||||||
|
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
|
|
Loading…
Reference in New Issue