diff --git a/classquiz/routers/editor.py b/classquiz/routers/editor.py index cf167d7..5828674 100644 --- a/classquiz/routers/editor.py +++ b/classquiz/routers/editor.py @@ -11,15 +11,27 @@ import asyncpg.exceptions import bleach from fastapi import APIRouter, HTTPException, Depends from pydantic import BaseModel +import html -from classquiz.config import settings, redis, storage, meilisearch, ALLOWED_TAGS_FOR_QUIZ, arq +from classquiz.config import ( + settings, + redis, + storage, + meilisearch, + ALLOWED_TAGS_FOR_QUIZ, + arq, +) from classquiz.db.models import Quiz, QuizInput, User, QuizQuestionType, StorageItem from classquiz.auth import get_current_user import os from datetime import datetime from uuid import UUID -from classquiz.helpers import get_meili_data, check_image_string, extract_image_ids_from_quiz +from classquiz.helpers import ( + get_meili_data, + check_image_string, + extract_image_ids_from_quiz, +) from classquiz.storage.errors import DeletionFailedError settings = settings() @@ -79,15 +91,15 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput): quiz_input.background_color = bleach.clean(quiz_input.background_color, tags=[], strip=True) for i, question in enumerate(quiz_input.questions): - if question.type == QuizQuestionType.ABCD: + if question.type == QuizQuestionType.ABCD or question.type == QuizQuestionType.VOTING: for i2, answer in enumerate(question.answers): if answer.color is not None: quiz_input.questions[i].answers[i2].color = bleach.clean(answer.color, tags=[], strip=True) if answer.answer == "": quiz_input.questions[i].answers[i2].answer = None if answer.answer is not None: - quiz_input.questions[i].answers[i2].answer = bleach.clean( - answer.answer, tags=ALLOWED_TAGS_FOR_QUIZ, strip=True + quiz_input.questions[i].answers[i2].answer = html.unescape( + bleach.clean(answer.answer, tags=ALLOWED_TAGS_FOR_QUIZ, strip=True) ) images_to_delete = [] diff --git a/classquiz/socket_server/__init__.py b/classquiz/socket_server/__init__.py index 73301a1..30a23b2 100644 --- a/classquiz/socket_server/__init__.py +++ b/classquiz/socket_server/__init__.py @@ -25,7 +25,11 @@ from classquiz.db.models import ( from pydantic import BaseModel, ValidationError from datetime import datetime -from classquiz.socket_server.helpers import check_answer, check_captcha +from classquiz.socket_server.helpers import ( + check_answer, + check_captcha, + has_already_answered, +) from .models import ( RejoinGameData, JoinGameData, @@ -286,7 +290,12 @@ async def submit_answer(sid: str, data: dict): return data.answer = str(data.answer) session = await get_session(sid, sio) + question_index = int(float(data.question_index)) game_data = await PlayGame.get_from_redis(session["game_pin"]) + already_answered = await has_already_answered(session["game_pin"], question_index, session["username"]) + if already_answered: + await sio.emit("already_replied", room=sid) + return (answer_right, answer) = check_answer(game_data, data) latency = int(float(session["ping"])) time_q_started = datetime.fromisoformat(await redis.get(f"game:{session['game_pin']}:current_time")) @@ -295,7 +304,7 @@ async def submit_answer(sid: str, data: dict): if answer_right: score = calculate_score( abs(diff) - latency, - int(float(game_data.questions[int(float(data.question_index))].time)), + int(float(game_data.questions[question_index].time)), ) if score > 1000: score = 1000 diff --git a/classquiz/socket_server/helpers.py b/classquiz/socket_server/helpers.py index e79fbdf..4f19985 100644 --- a/classquiz/socket_server/helpers.py +++ b/classquiz/socket_server/helpers.py @@ -10,6 +10,7 @@ from classquiz.db.models import ( ABCDQuizAnswer, VotingQuizAnswer, RangeQuizAnswer, + AnswerDataList, ) from classquiz.socket_server.models import SubmitAnswerData from .models import SubmitAnswerDataOrderType @@ -131,3 +132,12 @@ def check_check_question(answer: str, answers: list[ABCDQuizAnswer]) -> bool: if a.right: correct_string += str(i) return bool(correct_string == answer) + + +async def has_already_answered(game_pin: str, q_index: int, username: str) -> bool: + answers = await AnswerDataList.get_redis_or_empty(game_pin, q_index) + if answers is None: + return False + else: + answers = list(filter(lambda a: a.username == username, answers.root)) + return len(answers) > 0 diff --git a/frontend/src/lib/play/question.svelte b/frontend/src/lib/play/question.svelte index 768e232..17a8568 100644 --- a/frontend/src/lib/play/question.svelte +++ b/frontend/src/lib/play/question.svelte @@ -82,6 +82,9 @@ SPDX-License-Identifier: MPL-2.0 }); const selectAnswer = (answer: string) => { + if (selected_answer !== undefined) { + return; + } selected_answer = answer; //timer_res = '0'; socket.emit('submit_answer', { @@ -262,7 +265,7 @@ SPDX-License-Identifier: MPL-2.0 @@ -271,7 +274,7 @@ SPDX-License-Identifier: MPL-2.0