Files get deleted, if quiz gets deleted

This commit is contained in:
Mawoka
2022-04-02 20:51:17 +02:00
parent 0bad308c9c
commit e649b5ac46
7 changed files with 44 additions and 21 deletions
+3
View File
@@ -1,6 +1,7 @@
import redis.asyncio as redis_lib import redis.asyncio as redis_lib
from functools import lru_cache from functools import lru_cache
from pydantic import BaseSettings, RedisDsn, PostgresDsn from pydantic import BaseSettings, RedisDsn, PostgresDsn
from classquiz.storage import Storage
class Settings(BaseSettings): class Settings(BaseSettings):
@@ -43,3 +44,5 @@ def settings() -> Settings:
redis: redis_lib.client.Redis = redis_lib.Redis().from_url(settings().redis) redis: redis_lib.client.Redis = redis_lib.Redis().from_url(settings().redis)
storage: Storage = Storage(backend=settings().storage_backend, deta_key=settings().deta_project_key,
deta_id=settings().deta_project_id, storage_path=settings().storage_path)
+2 -4
View File
@@ -6,8 +6,7 @@ from aiohttp import ClientSession, FormData
import html import html
from classquiz.db.models import Quiz, QuizAnswer, QuizQuestion, User from classquiz.db.models import Quiz, QuizAnswer, QuizQuestion, User
from classquiz.kahoot_importer.get import get as get_quiz from classquiz.kahoot_importer.get import get as get_quiz
from classquiz.config import settings from classquiz.config import settings, storage
from classquiz.storage import Storage
settings = settings() settings = settings()
@@ -65,8 +64,7 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
return "quiz not found" return "quiz not found"
quiz_questions: list[dict] = [] quiz_questions: list[dict] = []
quiz_id = uuid.uuid4() quiz_id = uuid.uuid4()
storage = Storage(backend=settings.storage_backend, deta_key=settings.deta_project_key,
deta_id=settings.deta_project_id, storage_path=settings.storage_path)
for q in quiz.kahoot.questions: for q in quiz.kahoot.questions:
answers: list[QuizAnswer] = [] answers: list[QuizAnswer] = []
+13 -2
View File
@@ -9,8 +9,9 @@ from fastapi.responses import JSONResponse
import re import re
from classquiz.auth import get_current_user, get_current_user_optional from classquiz.auth import get_current_user, get_current_user_optional
from classquiz.config import redis, settings from classquiz.config import redis, settings, storage
from classquiz.db.models import Quiz, QuizInput, User, PlayGame from classquiz.db.models import Quiz, QuizInput, User, PlayGame
settings = settings() settings = settings()
router = APIRouter() router = APIRouter()
@@ -108,7 +109,17 @@ async def delete_quiz(quiz_id: str, user: User = Depends(get_current_user)):
except ValueError: except ValueError:
raise HTTPException(status_code=400, detail="badly formed quiz id") raise HTTPException(status_code=400, detail="badly formed quiz id")
quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id) quiz = await Quiz.objects.get_or_none(id=quiz_id, user_id=user.id)
if quiz is None: if quiz is None:
return JSONResponse(status_code=404, content={"detail": "quiz not found"}) return JSONResponse(status_code=404, content={"detail": "quiz not found"})
else: 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
await storage.delete(pics_to_delete)
return await quiz.delete() return await quiz.delete()
+1 -4
View File
@@ -1,8 +1,7 @@
from fastapi import APIRouter, HTTPException from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from classquiz.config import settings from classquiz.config import settings, storage
import re import re
from classquiz.storage import Storage
settings = settings() settings = settings()
@@ -13,8 +12,6 @@ file_regex = r"^[a-z0-9]{8}-[a-z0-9-]{27}--[a-z0-9-]{36}$"
@router.get('/download/{file_name}') @router.get('/download/{file_name}')
async def download_file(file_name: str): async def download_file(file_name: str):
storage = Storage(backend=settings.storage_backend, deta_key=settings.deta_project_key,
deta_id=settings.deta_project_id, storage_path=settings.storage_path)
if not re.match(file_regex, file_name): if not re.match(file_regex, file_name):
raise HTTPException(status_code=400, detail="Invalid file name") raise HTTPException(status_code=400, detail="Invalid file name")
+7 -1
View File
@@ -8,7 +8,7 @@ class Storage:
self.backend = backend self.backend = backend
self.deta_key = deta_key self.deta_key = deta_key
self.deta_id = deta_id self.deta_id = deta_id
self.deta_base_url = f"https://drive.deta.sh/v1/{deta_id}/classquiz" self.deta_base_url = f"https://drive.deta.sh/v1/{deta_id}/classquiz1"
self.deta_instance = DetaStorage(deta_base_url=self.deta_base_url, deta_key=self.deta_key, deta_id=self.deta_id) self.deta_instance = DetaStorage(deta_base_url=self.deta_base_url, deta_key=self.deta_key, deta_id=self.deta_id)
self.local_instance = LocalStorage(base_path=storage_path) self.local_instance = LocalStorage(base_path=storage_path)
if backend == "deta": if backend == "deta":
@@ -32,3 +32,9 @@ class Storage:
return await self.deta_instance.upload(file=file_data, file_name=file_name) return await self.deta_instance.upload(file=file_data, file_name=file_name)
elif self.backend == "local": elif self.backend == "local":
return await self.local_instance.write_file(file_name=file_name, data=file_data) return await self.local_instance.write_file(file_name=file_name, data=file_data)
async def delete(self, file_names: [str]) -> None:
if self.backend == "deta":
return await self.deta_instance.delete(file_names=file_names)
elif self.backend == "local":
return await self.local_instance.delete_file(file_names=file_names)
+2 -5
View File
@@ -1,9 +1,6 @@
from classquiz.config import settings
from aiohttp import ClientSession from aiohttp import ClientSession
from io import BytesIO from io import BytesIO
settings = settings()
class DetaStorage: class DetaStorage:
def __init__(self, deta_base_url: str, deta_id: str, deta_key: str): def __init__(self, deta_base_url: str, deta_id: str, deta_key: str):
@@ -42,9 +39,9 @@ class DetaStorage:
else: else:
raise Exception("Upload failed") raise Exception("Upload failed")
async def delete(self, file_name: [str]) -> None: async def delete(self, file_names: [str]) -> None:
async with ClientSession(headers=self.headers) as session: async with ClientSession(headers=self.headers) as session:
async with session.delete(f"{self.deta_url}/files/delete", data={"names": file_name}) as response: async with session.delete(f"{self.deta_url}/files", json={"names": file_names}) as response:
if response.status == 200: if response.status == 200:
return None return None
else: else:
+12 -1
View File
@@ -1,17 +1,28 @@
import io import io
import os import os
import aiofiles import aiofiles
import aiofiles.os
class LocalStorage: class LocalStorage:
def __init__(self, base_path: str): def __init__(self, base_path: str):
self.base_path = base_path self.base_path = base_path
async def get_file(self, file_name: str) -> io.BytesIO: async def get_file(self, file_name: str) -> io.BytesIO | None:
try:
async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode='rb') as f: async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode='rb') as f:
return io.BytesIO(await f.read()) return io.BytesIO(await f.read())
except FileNotFoundError:
return None
async def write_file(self, file_name: str, data: bytes) -> None: async def write_file(self, file_name: str, data: bytes) -> None:
async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode='wb') as f: async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode='wb') as f:
await f.write(data) await f.write(data)
async def delete_file(self, file_names: [str]) -> None:
for i in file_names:
try:
await aiofiles.os.remove(os.path.join(self.base_path, i))
except FileNotFoundError:
pass
return None