forked from DIWHY/photovoter_backend
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
5.5 KiB
180 lines
5.5 KiB
from fastapi import FastAPI
|
|
from fastapi.responses import JSONResponse
|
|
from fastapi.middleware.cors import CORSMiddleware # CORS
|
|
|
|
from datetime import datetime
|
|
from uuid import uuid4
|
|
import sqlite3
|
|
|
|
|
|
# use database residing here
|
|
DB_LOCATION = (
|
|
"db/photovoter.dblite" # Q: any allowances for this being not OUR database?
|
|
)
|
|
|
|
app = FastAPI()
|
|
con = sqlite3.connect(DB_LOCATION)
|
|
con.row_factory = sqlite3.Row
|
|
cur = con.cursor() # NB! single is enough for now, we might require multiple later
|
|
|
|
origins = [ # CORS
|
|
"*",
|
|
]
|
|
|
|
app.add_middleware( # CORS
|
|
CORSMiddleware,
|
|
allow_origins=origins,
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
|
|
@app.get("/new_session", responses={503: {"description": "Unable to initiate session"}})
|
|
async def new_session():
|
|
"""Start a new session"""
|
|
# add session to the database
|
|
time = datetime.utcnow().replace(microsecond=0).isoformat()
|
|
tries = 3 # something is very wrong with our random, if we miss 3 times
|
|
for i in range(tries):
|
|
try:
|
|
# generate a cookie
|
|
cookie = uuid4().hex
|
|
cur.execute(
|
|
"""INSERT INTO sessions(cookie, time)
|
|
VALUES(:cookie, :time)
|
|
""",
|
|
{"cookie": cookie, "time": time},
|
|
)
|
|
con.commit()
|
|
except sqlite3.IntegrityError as e:
|
|
if i < tries - 1 and str(e) == "UNIQUE constraint failed: sessions.cookie":
|
|
continue
|
|
elif str(e) == "UNIQUE constraint failed: sessions.cookie":
|
|
return JSONResponse(status_code=503)
|
|
else:
|
|
raise
|
|
break
|
|
|
|
# return new session cookie
|
|
return {"cookie": cookie}
|
|
|
|
|
|
@app.get(
|
|
"/next_picture/{cookie}",
|
|
responses={
|
|
204: {"description": "All available images have been appraised"},
|
|
409: {"description": "Uninitiated session"},
|
|
},
|
|
)
|
|
async def next_picture(cookie: str):
|
|
"""Request new picture to rate."""
|
|
# check if the cookie is valid
|
|
cur.execute(
|
|
"""SELECT sessionid
|
|
FROM sessions
|
|
WHERE cookie = :cookie
|
|
LIMIT 1""",
|
|
{"cookie": cookie},
|
|
)
|
|
sessionid = cur.fetchone()
|
|
if sessionid is None:
|
|
return JSONResponse(status_code=409)
|
|
|
|
# take not rated picture from the database
|
|
# do not insert anything in the database yet
|
|
# return this picture
|
|
|
|
# SELECT all images EXCEPT images with marks from the current session ->
|
|
# -> SELECT paths for these images
|
|
# FIXME[0]: can this be done better?
|
|
cur.execute(
|
|
"""SELECT imgid, resizedpath
|
|
FROM images
|
|
WHERE imgid IN (SELECT imgid
|
|
FROM images
|
|
EXCEPT
|
|
SELECT imgid
|
|
FROM marks
|
|
WHERE sessionid = :sessionid)
|
|
LIMIT 1
|
|
""",
|
|
{"sessionid": sessionid["sessionid"]},
|
|
)
|
|
r = cur.fetchone()
|
|
if r is not None:
|
|
return {"picture_id": r["imgid"], "picture_uri": r["resizedpath"]}
|
|
else:
|
|
# All available pics have been voted for by this sessionid
|
|
return JSONResponse(status_code=204)
|
|
|
|
|
|
@app.get(
|
|
"/rate_picture/{cookie}/{picture_id}/{mark}",
|
|
responses={
|
|
406: {"description": "Already appraised"},
|
|
409: {"description": "Uninitiated session"},
|
|
},
|
|
)
|
|
async def rate_picture(cookie: str, picture_id: int, mark: int):
|
|
"""Submit a rating for the picture"""
|
|
# check if the cookie is valid
|
|
cur.execute(
|
|
"""SELECT sessionid
|
|
FROM sessions
|
|
WHERE cookie = :cookie
|
|
LIMIT 1""",
|
|
{"cookie": cookie},
|
|
)
|
|
sessionid = cur.fetchone()
|
|
if sessionid is None:
|
|
return JSONResponse(status_code=409)
|
|
|
|
# add new mark to the session table
|
|
try:
|
|
cur.execute(
|
|
"""INSERT INTO marks(imgid, sessionid, mark)
|
|
VALUES(:imgid,:sessionid,:mark)
|
|
""",
|
|
{"imgid": picture_id, "sessionid": sessionid["sessionid"], "mark": mark},
|
|
)
|
|
con.commit()
|
|
except sqlite3.IntegrityError as e:
|
|
if str(e) == "UNIQUE constraint failed: marks.imgid, marks.sessionid":
|
|
return JSONResponse(status_code=406)
|
|
|
|
return {"status": "success"}
|
|
|
|
@app.get("/photo_points")
|
|
def photo_points():
|
|
"""Get points with the url of a photo and the rate"""
|
|
return [
|
|
{
|
|
"id": 0,
|
|
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/JPEG_example_down.jpg/350px-JPEG_example_down.jpg",
|
|
"lon": 37.34542,
|
|
"lat": 55.12323,
|
|
"rate": 36 # percentage of likes
|
|
},
|
|
{
|
|
"id": 1,
|
|
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/JPEG_example_down.jpg/350px-JPEG_example_down.jpg",
|
|
"lon": 37.34342,
|
|
"lat": 55.12423,
|
|
"rate": 62 # percentage of likes
|
|
},
|
|
{
|
|
"id": 2,
|
|
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/JPEG_example_down.jpg/350px-JPEG_example_down.jpg",
|
|
"lon": 37.34642,
|
|
"lat": 55.12223,
|
|
"rate": 43 # percentage of likes
|
|
},
|
|
{
|
|
"id": 3,
|
|
"url": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/JPEG_example_down.jpg/350px-JPEG_example_down.jpg",
|
|
"lon": 37.34342,
|
|
"lat": 55.12923,
|
|
"rate": 90 # percentage of likes
|
|
}
|
|
] |