Files
classquiz-ai/classquiz/routers/quiz.py
T
2022-04-14 21:33:16 +02:00

180 lines
7.2 KiB
Python

import json
import re
import uuid
from datetime import datetime
from random import randint
from classquiz.helpers import get_meili_data
from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse
from pydantic import ValidationError
import bleach
from classquiz.auth import get_current_user
from classquiz.config import redis, settings, storage, meilisearch
from classquiz.db.models import Quiz, QuizInput, User, PlayGame
from classquiz.kahoot_importer.import_quiz import import_quiz
settings = settings()
router = APIRouter()
@router.post("/create")
async def create_quiz_lol(quiz_input: QuizInput, user: User = Depends(get_current_user)):
imgur_regex = r"^https://i\.imgur\.com\/.{7}.(jpg|png|gif)$"
server_regex = rf"^{settings.root_address}/api/v1/storage/download/.{36}--.{36}$"
quiz_input.title = bleach.clean(quiz_input.title, tags=[], strip=True)
quiz_input.description = bleach.clean(quiz_input.description, tags=[], strip=True)
for question in quiz_input.questions:
if question.image == "":
question.image = None
if question.image is not None:
if not re.match(imgur_regex, question.image) and not re.match(server_regex, question.image):
raise HTTPException(status_code=400, detail="image url is not valid")
quiz = Quiz(**quiz_input.dict(), user_id=user.id, id=uuid.uuid4())
await redis.delete("global_quiz_count")
meilisearch.index(settings.meilisearch_index).add_documents([await get_meili_data(quiz)])
return await quiz.save()
@router.get("/get/{quiz_id}")
async def get_quiz_from_id(quiz_id: str, user: User | None = Depends(get_current_user)):
try:
quiz_id = uuid.UUID(quiz_id)
except ValueError:
raise HTTPException(status_code=400, detail="badly formed quiz id")
if user is None:
quiz = await Quiz.objects.get_or_none(id=quiz_id, public=True)
else:
quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id)
if quiz is None:
public_quiz = await Quiz.objects.get_or_none(id=quiz_id, public=True)
if public_quiz is None:
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
else:
return public_quiz
else:
return quiz
@router.get("/get/public/{quiz_id}", response_model=Quiz)
async def get_public_quiz(quiz_id: str):
try:
quiz_id = uuid.UUID(quiz_id)
except ValueError:
raise HTTPException(status_code=400, detail="badly formed quiz id")
quiz = await Quiz.objects.get_or_none(id=quiz_id, public=True)
if quiz is None:
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
else:
return quiz
@router.post("/start/{quiz_id}")
async def start_quiz(quiz_id: str, user: User = Depends(get_current_user)):
try:
quiz_id = uuid.UUID(quiz_id)
except ValueError:
raise HTTPException(status_code=400, detail="badly formed quiz id")
quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id)
if quiz is None:
quiz = await Quiz.objects.get_or_none(id=quiz_id, public=True)
if quiz is None:
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
game_pin = randint(10000000, 99999999)
game = PlayGame(
quiz_id=quiz_id,
game_pin=str(game_pin),
questions=quiz.questions,
game_id=uuid.uuid4(),
title=quiz.title,
description=quiz.description,
)
await redis.set(f"game:{str(game.game_pin)}", (game.json()), ex=18000)
return {**quiz.dict(exclude={"id"}), **game.dict(exclude={"questions"})}
@router.get("/join/{game_pin}")
async def get_game_id(game_pin: str):
redis_res = (await redis.get(f"game:{game_pin}")).decode()
if redis_res is None:
raise HTTPException(status_code=404, detail="game not found")
else:
return json.loads(redis_res)["game_id"]
@router.get("/list")
async def get_quiz_list(user: User = Depends(get_current_user)):
return await Quiz.objects.filter(user_id=user.id).all()
@router.put("/update/{quiz_id}")
async def update_quiz(quiz_id: str, quiz_input: QuizInput, user: User = Depends(get_current_user)):
imgur_regex = r"^https://i\.imgur\.com\/.{7}.(jpg|png|gif)$"
server_regex = rf"^{settings.root_address}/api/v1/storage/download/.{{36}}--.{{36}}$"
for question in quiz_input.questions:
if question.image == "":
question.image = None
if question.image is not None:
if not bool(re.match(server_regex, question.image)) and not bool(re.match(imgur_regex, question.image)):
print("not valid", question.image)
raise HTTPException(status_code=400, detail="image url is not valid")
try:
quiz_id = uuid.UUID(quiz_id)
except ValueError:
raise HTTPException(status_code=400, detail="badly formed quiz id")
quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id)
if quiz is None:
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
else:
# print(quiz_input)
# print(quiz)
quiz_input.description = bleach.clean(quiz_input.description, tags=[], strip=True)
quiz_input.title = bleach.clean(quiz_input.title, tags=[], strip=True)
meilisearch.index(settings.meilisearch_index).update_documents([await get_meili_data(quiz)])
if quiz.public and not quiz_input.public:
print("removing from meilisearch")
meilisearch.index(settings.meilisearch_index).delete_document(str(quiz.id))
if not quiz.public and quiz_input.public:
meilisearch.index(settings.meilisearch_index).add_documents([await get_meili_data(quiz)])
quiz.title = quiz_input.title
quiz.public = quiz_input.public
quiz.description = quiz_input.description
quiz.updated_at = datetime.now()
quiz.questions = quiz_input.dict()["questions"]
return await quiz.update()
@router.post("/import/{quiz_id}")
async def import_quiz_route(quiz_id: str, user: User = Depends(get_current_user)):
try:
return await import_quiz(quiz_id, user)
except ValidationError:
raise HTTPException(status_code=400, detail="THis quiz in't (yet) supported")
@router.delete("/delete/{quiz_id}")
async def delete_quiz(quiz_id: str, user: User = Depends(get_current_user)):
try:
quiz_id = uuid.UUID(quiz_id)
except ValueError:
raise HTTPException(status_code=400, detail="badly formed quiz id")
quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id)
if quiz is None:
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
pics_to_delete = []
pic_name_regex = re.compile("^.*/(.{36}--.{36})$")
for question in quiz.questions:
try:
if question["image"] is not None:
if not str(question["image"]).startswith("https://i.imgur.com/"):
pics_to_delete.append(pic_name_regex.match(question["image"]).group(1))
except KeyError:
pass
if len(pics_to_delete) != 0:
await storage.delete(pics_to_delete)
meilisearch.index(settings.meilisearch_index).delete_document(str(quiz.id))
return await quiz.delete()