diff --git a/classquiz/db/models.py b/classquiz/db/models.py index 8ce627c..461e5aa 100644 --- a/classquiz/db/models.py +++ b/classquiz/db/models.py @@ -99,6 +99,7 @@ class QuizInput(BaseModel): public: bool = False title: str description: str + cover_image: str | None questions: list[QuizQuestion] @@ -112,6 +113,7 @@ class Quiz(ormar.Model): user_id: uuid.UUID = ormar.ForeignKey(User) questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False) imported_from_kahoot: Optional[bool] = ormar.Boolean(default=False, nullable=True) + cover_image: Optional[str] = ormar.Text(nullable=True, unique=False) class Meta: tablename = "quiz" @@ -154,6 +156,7 @@ class PlayGame(BaseModel): game_pin: str started: bool = False captcha_enabled: bool = False + cover_image: str | None class GamePlayer(BaseModel): diff --git a/classquiz/kahoot_importer/import_quiz.py b/classquiz/kahoot_importer/import_quiz.py index 3e0b5f7..4475920 100644 --- a/classquiz/kahoot_importer/import_quiz.py +++ b/classquiz/kahoot_importer/import_quiz.py @@ -55,6 +55,12 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str: image=image, ).dict() ) + cover = None + if quiz.kahoot.cover != "": + image_bytes = await _download_image(quiz.kahoot.cover) + image_name = f"{quiz_id}--{uuid.uuid4()}" + await storage.upload(file_name=image_name, file_data=image_bytes) + cover = f"{settings.root_address}/api/v1/storage/download/{image_name}" quiz_data = Quiz( id=quiz_id, public=True, @@ -65,6 +71,7 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str: user_id=user.id, questions=json.dumps(quiz_questions), imported_from_kahoot=True, + cover_image=cover, ) meilisearch.index(settings.meilisearch_index).add_documents([await get_meili_data(quiz_data)]) return await quiz_data.save() diff --git a/classquiz/routers/editor.py b/classquiz/routers/editor.py index a9cb010..4699061 100644 --- a/classquiz/routers/editor.py +++ b/classquiz/routers/editor.py @@ -157,6 +157,13 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput): mark_image_for_deletion(question.image, i, old_quiz_data) else: raise HTTPException(status_code=400, detail="Image URL(s) aren't valid!") + + if quiz_input.cover_image == "": + quiz_input.cover_image = None + + if quiz_input.cover_image is not None and not bool(re.match(server_regex, quiz_input.cover_image)): + raise HTTPException(status_code=400, detail="image url is not valid") + if session_data.edit: quiz = old_quiz_data meilisearch.index(settings.meilisearch_index).update_documents([await get_meili_data(quiz)]) @@ -169,6 +176,7 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput): quiz.description = quiz_input.description quiz.updated_at = datetime.now() quiz.questions = quiz_input.dict()["questions"] + quiz.cover_image = quiz_input.cover_image for image in images_to_delete: if image is not None: try: diff --git a/classquiz/routers/quiz.py b/classquiz/routers/quiz.py index d80f27a..b2439d4 100644 --- a/classquiz/routers/quiz.py +++ b/classquiz/routers/quiz.py @@ -42,6 +42,11 @@ async def create_quiz_lol(quiz_input: QuizInput, user: User = Depends(get_curren and not re.match(server_regex, question.image) ): raise HTTPException(status_code=400, detail="image url is not valid") + if quiz_input.cover_image == "": + quiz_input.cover_image = None + + if quiz_input.cover_image is not None and not bool(re.match(server_regex, quiz_input.cover_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") if quiz_input.public: @@ -102,6 +107,7 @@ async def start_quiz(quiz_id: str, captcha_enabled: bool = True, user: User = De title=quiz.title, description=quiz.description, captcha_enabled=captcha_enabled, + cover_image=quiz.cover_image, ) await redis.set(f"game:{str(game.game_pin)}", (game.json()), ex=18000) return {**quiz.dict(exclude={"id"}), **game.dict(exclude={"questions"})} @@ -156,6 +162,15 @@ async def update_quiz(quiz_id: str, quiz_input: QuizInput, user: User = Depends( quiz_id = uuid.UUID(quiz_id) except ValueError: raise HTTPException(status_code=400, detail="badly formed quiz id") + ## Check Cover-Image + + print(quiz_input.cover_image) + if quiz_input.cover_image == "": + quiz_input.cover_image = None + + if quiz_input.cover_image is not None and not bool(re.match(server_regex, quiz_input.cover_image)): + raise HTTPException(status_code=400, detail="image url is not valid") + 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"}) @@ -168,6 +183,7 @@ async def update_quiz(quiz_id: str, quiz_input: QuizInput, user: User = Depends( 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.cover_image = quiz_input.cover_image quiz.public = quiz_input.public quiz.description = quiz_input.description quiz.updated_at = datetime.now() diff --git a/frontend/src/lib/dashboard/main_slider.svelte b/frontend/src/lib/dashboard/main_slider.svelte index b95aef8..a8ea7c6 100644 --- a/frontend/src/lib/dashboard/main_slider.svelte +++ b/frontend/src/lib/dashboard/main_slider.svelte @@ -71,9 +71,25 @@ > {quiz.title}
-- {quiz.description} -
++ {quiz.description} +
+ {#if quiz.cover_image} +
{quiz.questions.length} {$t('words.question', { count: quiz.questions.length })}
+{quiz.description}
+ {#if quiz.cover_image} +