Most dependencies updated, mostly tested
This commit is contained in:
Generated
+1200
-972
File diff suppressed because it is too large
Load Diff
+25
-12
@@ -10,7 +10,7 @@ from typing import Optional
|
|||||||
|
|
||||||
import ormar
|
import ormar
|
||||||
from ormar import ReferentialAction
|
from ormar import ReferentialAction
|
||||||
from pydantic import BaseModel, Json, field_validator, ConfigDict, RootModel
|
from pydantic import BaseModel, Json, field_validator, ConfigDict, RootModel, ValidationInfo
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from . import metadata, database
|
from . import metadata, database
|
||||||
from .quiztivity import QuizTivityPage
|
from .quiztivity import QuizTivityPage
|
||||||
@@ -102,7 +102,7 @@ class UserSession(ormar.Model):
|
|||||||
class ABCDQuizAnswer(BaseModel):
|
class ABCDQuizAnswer(BaseModel):
|
||||||
right: bool
|
right: bool
|
||||||
answer: str
|
answer: str
|
||||||
color: str | None
|
color: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class RangeQuizAnswer(BaseModel):
|
class RangeQuizAnswer(BaseModel):
|
||||||
@@ -115,7 +115,7 @@ class RangeQuizAnswer(BaseModel):
|
|||||||
class VotingQuizAnswer(BaseModel):
|
class VotingQuizAnswer(BaseModel):
|
||||||
answer: str
|
answer: str
|
||||||
image: str | None = None
|
image: str | None = None
|
||||||
color: str | None
|
color: str | None = None
|
||||||
|
|
||||||
|
|
||||||
class QuizQuestionType(str, Enum):
|
class QuizQuestionType(str, Enum):
|
||||||
@@ -141,20 +141,20 @@ class QuizQuestion(BaseModel):
|
|||||||
image: str | None = None
|
image: str | None = None
|
||||||
|
|
||||||
@field_validator("answers")
|
@field_validator("answers")
|
||||||
def answers_not_none_if_abcd_type(cls, v, values):
|
def answers_not_none_if_abcd_type(cls, v, info: ValidationInfo):
|
||||||
if values["type"] == QuizQuestionType.ABCD and not isinstance(v[0], ABCDQuizAnswer):
|
if info.data["type"] == QuizQuestionType.ABCD and not isinstance(v[0], ABCDQuizAnswer):
|
||||||
raise ValueError("Answers can't be none if type is ABCD")
|
raise ValueError("Answers can't be none if type is ABCD")
|
||||||
if values["type"] == QuizQuestionType.RANGE and not isinstance(v, RangeQuizAnswer):
|
if info.data["type"] == QuizQuestionType.RANGE and not isinstance(v, RangeQuizAnswer):
|
||||||
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
|
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
|
||||||
if values["type"] == QuizQuestionType.VOTING and not isinstance(v[0], VotingQuizAnswer):
|
if info.data["type"] == QuizQuestionType.VOTING and not isinstance(v[0], VotingQuizAnswer):
|
||||||
raise ValueError("Answer must be from type VotingQuizAnswer if type is VOTING")
|
raise ValueError("Answer must be from type VotingQuizAnswer if type is VOTING")
|
||||||
if values["type"] == QuizQuestionType.TEXT and not isinstance(v[0], TextQuizAnswer):
|
if info.data["type"] == QuizQuestionType.TEXT and not isinstance(v[0], TextQuizAnswer):
|
||||||
raise ValueError("Answer must be from type TextQuizAnswer if type is TEXT")
|
raise ValueError("Answer must be from type TextQuizAnswer if type is TEXT")
|
||||||
if values["type"] == QuizQuestionType.ORDER and not isinstance(v[0], VotingQuizAnswer):
|
if info.data["type"] == QuizQuestionType.ORDER and not isinstance(v[0], VotingQuizAnswer):
|
||||||
raise ValueError("Answer must be from type VotingQuizAnswer if type is ORDER")
|
raise ValueError("Answer must be from type VotingQuizAnswer if type is ORDER")
|
||||||
if values["type"] == QuizQuestionType.SLIDE and not isinstance(v, str):
|
if info.data["type"] == QuizQuestionType.SLIDE and not isinstance(v, str):
|
||||||
raise ValueError("Answer must be from type SlideElement if type is SLIDE")
|
raise ValueError("Answer must be from type SlideElement if type is SLIDE")
|
||||||
if values["type"] == QuizQuestionType.CHECK and not isinstance(v[0], ABCDQuizAnswer):
|
if info.data["type"] == QuizQuestionType.CHECK and not isinstance(v[0], ABCDQuizAnswer):
|
||||||
raise ValueError("Answers can't be none if type is CHECK")
|
raise ValueError("Answers can't be none if type is CHECK")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@@ -278,7 +278,20 @@ class AnswerData(BaseModel):
|
|||||||
score: int
|
score: int
|
||||||
|
|
||||||
|
|
||||||
AnswerDataList = RootModel[list[AnswerData]]
|
class AnswerDataList(RootModel):
|
||||||
|
root: list[AnswerData]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self.root)
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.root(item)
|
||||||
|
|
||||||
|
def append(self, item):
|
||||||
|
self.root.append(item)
|
||||||
|
|
||||||
|
def __len__(self) -> int:
|
||||||
|
return len(self.root)
|
||||||
|
|
||||||
|
|
||||||
class GameInLobby(BaseModel):
|
class GameInLobby(BaseModel):
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ class _LastEdit(BaseModel):
|
|||||||
|
|
||||||
class _ImageMetadata(BaseModel):
|
class _ImageMetadata(BaseModel):
|
||||||
id: UUID | None = None
|
id: UUID | None = None
|
||||||
content_type: Optional[str]
|
content_type: Optional[str] = None
|
||||||
width: Optional[int]
|
width: Optional[int] = None
|
||||||
height: Optional[int]
|
height: Optional[int] = None
|
||||||
resources: Optional[str]
|
resources: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class _SampleQuestion(BaseModel):
|
class _SampleQuestion(BaseModel):
|
||||||
@@ -130,8 +130,8 @@ class _Video(BaseModel):
|
|||||||
startTime: float
|
startTime: float
|
||||||
endTime: float
|
endTime: float
|
||||||
service: str
|
service: str
|
||||||
full_url: Optional[str]
|
full_url: Optional[str] = None
|
||||||
id: Optional[str]
|
id: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class _Question(BaseModel):
|
class _Question(BaseModel):
|
||||||
@@ -143,7 +143,7 @@ class _Question(BaseModel):
|
|||||||
choices: List[_Choice]
|
choices: List[_Choice]
|
||||||
image: str | None = None
|
image: str | None = None
|
||||||
imageMetadata: _ImageMetadata | None = None
|
imageMetadata: _ImageMetadata | None = None
|
||||||
resources: Optional[str]
|
resources: Optional[str] = None
|
||||||
video: _Video
|
video: _Video
|
||||||
questionFormat: int
|
questionFormat: int
|
||||||
languageInfo: _LanguageInfo | None = None
|
languageInfo: _LanguageInfo | None = None
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ async def submit_answer_fn(data_answer: int, game_pin: str, player_id: str, now:
|
|||||||
answers = await set_answer(answers, game_pin=game_pin, data=answer_data, q_index=game.current_question)
|
answers = await set_answer(answers, game_pin=game_pin, data=answer_data, q_index=game.current_question)
|
||||||
player_count = await redis.scard(f"game_session:{game_pin}:players")
|
player_count = await redis.scard(f"game_session:{game_pin}:players")
|
||||||
await sio.emit("player_answer", {})
|
await sio.emit("player_answer", {})
|
||||||
if answers is not None and len(answers.__root__) == player_count:
|
if answers is not None and len(answers) == player_count:
|
||||||
await sio.emit("everyone_answered", {})
|
await sio.emit("everyone_answered", {})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ async def export_quiz(quiz_id: uuid.UUID, user: User = Depends(get_current_user)
|
|||||||
question["image"] = i
|
question["image"] = i
|
||||||
if quiz.cover_image is not None:
|
if quiz.cover_image is not None:
|
||||||
image_urls[-1] = quiz.cover_image
|
image_urls[-1] = quiz.cover_image
|
||||||
quiz.cover_image = -1
|
quiz.cover_image = "-1"
|
||||||
quiz_dict = quiz.model_dump()
|
quiz_dict = quiz.model_dump()
|
||||||
del quiz_dict["user_id"], quiz_dict["id"]
|
del quiz_dict["user_id"], quiz_dict["id"]
|
||||||
quiz_dict["created_at"] = quiz_dict["created_at"].isoformat()
|
quiz_dict["created_at"] = quiz_dict["created_at"].isoformat()
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ async def voting_results(game_pin: str, api_key: str, as_array: bool = False):
|
|||||||
answer_dict = {}
|
answer_dict = {}
|
||||||
for answer in game.questions[game.current_question].answers:
|
for answer in game.questions[game.current_question].answers:
|
||||||
answer_dict[answer.answer] = 0
|
answer_dict[answer.answer] = 0
|
||||||
for answer in answer_list.__root__:
|
for answer in answer_list:
|
||||||
answer_dict[answer.answer] += 1
|
answer_dict[answer.answer] += 1
|
||||||
if as_array:
|
if as_array:
|
||||||
return [answer_dict]
|
return [answer_dict]
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from classquiz.db.models import (
|
|||||||
AnswerDataList,
|
AnswerDataList,
|
||||||
AnswerData,
|
AnswerData,
|
||||||
)
|
)
|
||||||
from pydantic import BaseModel, ValidationError, field_validator
|
from pydantic import BaseModel, ValidationError, field_validator, ValidationInfo
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from classquiz.socket_server.export_helpers import save_quiz_to_storage
|
from classquiz.socket_server.export_helpers import save_quiz_to_storage
|
||||||
@@ -61,10 +61,10 @@ def calculate_score(z: float, t: int) -> int:
|
|||||||
|
|
||||||
async def set_answer(answers, game_pin: str, q_index: int, data: AnswerData) -> AnswerDataList:
|
async def set_answer(answers, game_pin: str, q_index: int, data: AnswerData) -> AnswerDataList:
|
||||||
if answers is None:
|
if answers is None:
|
||||||
answers = AnswerDataList(__root__=[data])
|
answers = AnswerDataList([data])
|
||||||
else:
|
else:
|
||||||
answers = AnswerDataList.model_validate_json(answers)
|
answers = AnswerDataList.model_validate_json(answers)
|
||||||
answers.__root__.append(data)
|
answers.append(data)
|
||||||
await redis.set(
|
await redis.set(
|
||||||
f"game_session:{game_pin}:{q_index}",
|
f"game_session:{game_pin}:{q_index}",
|
||||||
answers.model_dump_json(),
|
answers.model_dump_json(),
|
||||||
@@ -279,7 +279,7 @@ async def get_question_results(sid: str, data: dict):
|
|||||||
if redis_res is None:
|
if redis_res is None:
|
||||||
redis_res = []
|
redis_res = []
|
||||||
else:
|
else:
|
||||||
redis_res = AnswerDataList.model_validate_json(redis_res).model_dump()["__root__"]
|
redis_res = AnswerDataList.model_validate_json(redis_res).model_dump()
|
||||||
game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}"))
|
game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}"))
|
||||||
game_data.question_show = False
|
game_data.question_show = False
|
||||||
await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json())
|
await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json())
|
||||||
@@ -303,13 +303,13 @@ class ReturnQuestion(QuizQuestion):
|
|||||||
type: QuizQuestionType = QuizQuestionType.ABCD
|
type: QuizQuestionType = QuizQuestionType.ABCD
|
||||||
|
|
||||||
@field_validator("answers")
|
@field_validator("answers")
|
||||||
def answers_not_none_if_abcd_type(cls, v, values):
|
def answers_not_none_if_abcd_type(cls, v, info: ValidationInfo):
|
||||||
if values["type"] == QuizQuestionType.ABCD and type(v[0]) is not ABCDQuizAnswerWithoutSolution:
|
if info.data["type"] == QuizQuestionType.ABCD and type(v[0]) is not ABCDQuizAnswerWithoutSolution:
|
||||||
raise ValueError("Answers can't be none if type is ABCD")
|
raise ValueError("Answers can't be none if type is ABCD")
|
||||||
if values["type"] == QuizQuestionType.RANGE and type(v) is not RangeQuizAnswerWithoutSolution:
|
if info.data["type"] == QuizQuestionType.RANGE and type(v) is not RangeQuizAnswerWithoutSolution:
|
||||||
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
|
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
|
||||||
# skipcq: PTC-W0047
|
# skipcq: PTC-W0047
|
||||||
if values["type"] == QuizQuestionType.VOTING and type(v[0]) is not VotingQuizAnswer:
|
if info.data["type"] == QuizQuestionType.VOTING and type(v[0]) is not VotingQuizAnswer:
|
||||||
pass
|
pass
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@@ -358,7 +358,7 @@ class _SubmitAnswerDataOrderType(BaseModel):
|
|||||||
|
|
||||||
class _SubmitAnswerData(BaseModel):
|
class _SubmitAnswerData(BaseModel):
|
||||||
question_index: int
|
question_index: int
|
||||||
answer: str
|
answer: str | int
|
||||||
complex_answer: list[_SubmitAnswerDataOrderType] | None = None
|
complex_answer: list[_SubmitAnswerDataOrderType] | None = None
|
||||||
|
|
||||||
|
|
||||||
@@ -371,6 +371,7 @@ async def submit_answer(sid: str, data: dict):
|
|||||||
await sio.emit("error", room=sid)
|
await sio.emit("error", room=sid)
|
||||||
print(e)
|
print(e)
|
||||||
return
|
return
|
||||||
|
data.answer = str(data.answer)
|
||||||
session = await sio.get_session(sid)
|
session = await sio.get_session(sid)
|
||||||
game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}"))
|
game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}"))
|
||||||
answer_right = False
|
answer_right = False
|
||||||
@@ -447,7 +448,7 @@ async def submit_answer(sid: str, data: dict):
|
|||||||
)
|
)
|
||||||
player_count = await redis.scard(f"game_session:{session['game_pin']}:players")
|
player_count = await redis.scard(f"game_session:{session['game_pin']}:players")
|
||||||
await sio.emit("player_answer", {})
|
await sio.emit("player_answer", {})
|
||||||
if len(answers.__root__) == player_count:
|
if len(answers) == player_count:
|
||||||
# await sio.emit(
|
# await sio.emit(
|
||||||
# "question_results",
|
# "question_results",
|
||||||
# await redis.get(f"game_session:{session['game_pin']}:{data.question_index}"),
|
# await redis.get(f"game_session:{session['game_pin']}:{data.question_index}"),
|
||||||
|
|||||||
Reference in New Issue
Block a user