add: accept and extract zip photo archives

pull/5/head
rrr-marble 4 years ago
parent 7df1ff3503
commit 36cd3297af

@ -1,4 +1,4 @@
from fastapi import FastAPI, File, UploadFile, Depends
from fastapi import FastAPI, File, UploadFile, Depends, BackgroundTasks
from fastapi.responses import JSONResponse
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi.middleware.cors import CORSMiddleware # CORS
@ -7,12 +7,14 @@ from secrets import compare_digest
from datetime import datetime
from uuid import uuid4
import sqlite3
import zipfile
# use database residing here
DB_LOCATION = (
"../testbox/photovoter.dblite" # Q: any allowances for this being not OUR database?
)
DATA_LOCATION = "/tmp/123"
app = FastAPI()
security = HTTPBasic()
@ -178,24 +180,66 @@ async def photo_points():
@app.post(
"/upload_pictures/",
responses={
202: {"description": "Archive accepted into processing"},
401: {"description": "Authentication is required to access this resource"},
415: {"description": "Cannot process uploaded archive"},
},
)
async def upload_pictures(
credentials: HTTPBasicCredentials = Depends(security), file: UploadFile = File(...)
background_tasks: BackgroundTasks,
credentials: HTTPBasicCredentials = Depends(security),
file: UploadFile = File(...),
):
"""Интерфейс для загрузки фотографий"""
"""Условно кладём в браузер zip с фотографиями и он их потихоньку ест.
Доступ к этому интерфейсу, наверное, лучше ограничить паролем или как-нибудь ещё.
Пока исходим из предположения, что только я буду загружать фотографии."""
Пока исходим из предположения, что только я буду загружать фотографии.
"""
# check authenticity
correct_username = compare_digest(credentials.username, "1")
correct_password = compare_digest(credentials.password, "1")
if not (correct_username and correct_password):
return JSONResponse(status_code=401)
# slurp the zip
# *detach from the interface, if possible
if not zipfile.is_zipfile(file.file):
return JSONResponse(status_code=415)
# detach from the interface
# unpack zip
tasks = BackgroundTasks()
tasks.add_task(
unpack_pictures_zip,
file=file,
time=datetime.utcnow().replace(microsecond=0).isoformat(),
)
# feed the pictures to util/import_photos.py
return {"filename": file.filename}
return JSONResponse("Accepted", background=tasks)
def unpack_pictures_zip(file: UploadFile, time):
"""
Unpack and process zip archived photo
Extract pictures in the DATA_LOCATION/processing
#TODO: and feed them to util/import_photos.py
#TODO: Walk the nested DATA_LOCATION/processing ourselves
Uses: DATA_LOCATION
"""
# we only call this function sporadically, so import here
import os
print(f"Accepted {file.filename} at {time} into processing")
os.chdir(DATA_LOCATION)
os.mkdir("processing")
os.chdir("processing")
# using private ._file field is a dirty hack, but
# SpooledTemporaryFile does not implement seekable
# required by zipfile 'r' mode
# https://bugs.python.org/issue26175
with zipfile.ZipFile(file.file._file) as photo_zip:
problem_files = photo_zip.testzip()
if problem_files is not None:
print(f"Errors in {file.filename} accepted at {time}: {problem_files}")
photo_zip.extractall()
photo_zip.close()
print(f"Succesfully processed {file.filename} accepted at {time}")

Loading…
Cancel
Save