🐛 Fix Pydantic breaking changes
This commit is contained in:
+21
-21
@@ -84,7 +84,7 @@ class UserSession(ormar.Model):
|
||||
class ABCDQuizAnswer(BaseModel):
|
||||
right: bool
|
||||
answer: str
|
||||
color: str | None
|
||||
color: str | None = None
|
||||
|
||||
|
||||
class RangeQuizAnswer(BaseModel):
|
||||
@@ -97,7 +97,7 @@ class RangeQuizAnswer(BaseModel):
|
||||
class VotingQuizAnswer(BaseModel):
|
||||
answer: str
|
||||
image: str | None = None
|
||||
color: str | None
|
||||
color: str | None = None
|
||||
|
||||
|
||||
class QuizQuestionType(str, Enum):
|
||||
@@ -145,10 +145,10 @@ class QuizInput(BaseModel):
|
||||
public: bool = False
|
||||
title: str
|
||||
description: str
|
||||
cover_image: str | None
|
||||
background_color: str | None
|
||||
cover_image: str | None = None
|
||||
background_color: str | None = None
|
||||
questions: list[QuizQuestion]
|
||||
background_image: str | None
|
||||
background_image: str | None = None
|
||||
|
||||
|
||||
class Quiz(ormar.Model):
|
||||
@@ -207,18 +207,18 @@ class PlayGame(BaseModel):
|
||||
game_pin: str
|
||||
started: bool = False
|
||||
captcha_enabled: bool = False
|
||||
cover_image: str | None
|
||||
game_mode: str | None
|
||||
cover_image: str | None = None
|
||||
game_mode: str | None = None
|
||||
current_question: int = -1
|
||||
background_color: str | None
|
||||
background_image: str | None
|
||||
custom_field: str | None
|
||||
background_color: str | None = None
|
||||
background_image: str | None = None
|
||||
custom_field: str | None = None
|
||||
question_show: bool = False
|
||||
|
||||
|
||||
class GamePlayer(BaseModel):
|
||||
username: str
|
||||
sid: str | None
|
||||
sid: str | None = None
|
||||
|
||||
|
||||
class GameAnswer2(BaseModel):
|
||||
@@ -320,8 +320,8 @@ class OnlyId(BaseModel):
|
||||
|
||||
class PublicQuizTivityShare(BaseModel):
|
||||
id: uuid.UUID
|
||||
name: str | None
|
||||
expire_in: int | None
|
||||
name: str | None = None
|
||||
expire_in: int | None = None
|
||||
quiztivity: OnlyId
|
||||
user: OnlyId
|
||||
|
||||
@@ -363,13 +363,13 @@ class PublicStorageItem(BaseModel):
|
||||
id: uuid.UUID
|
||||
uploaded_at: datetime
|
||||
mime_type: str
|
||||
hash: str | None
|
||||
hash: str | None = None
|
||||
size: int
|
||||
deleted_at: datetime | None
|
||||
alt_text: str | None
|
||||
filename: str | None
|
||||
thumbhash: str | None
|
||||
server: str | None
|
||||
deleted_at: datetime | None = None
|
||||
alt_text: str | None = None
|
||||
filename: str | None = None
|
||||
thumbhash: str | None = None
|
||||
server: str | None = None
|
||||
imported: bool
|
||||
|
||||
@classmethod
|
||||
@@ -425,8 +425,8 @@ class PrivateStorageItem(PublicStorageItem):
|
||||
|
||||
|
||||
class UpdateStorageItem(BaseModel):
|
||||
filename: str | None
|
||||
alt_text: str | None
|
||||
filename: str | None = None
|
||||
alt_text: str | None = None
|
||||
|
||||
|
||||
class Controller(ormar.Model):
|
||||
|
||||
@@ -12,8 +12,8 @@ class Pdf(BaseModel):
|
||||
|
||||
|
||||
class _MemoryCard(BaseModel):
|
||||
image: str | None
|
||||
text: str | None
|
||||
image: str | None = None
|
||||
text: str | None = None
|
||||
id: str
|
||||
|
||||
|
||||
@@ -53,6 +53,6 @@ TYPE_CLASS_LIST = {
|
||||
|
||||
|
||||
class QuizTivityPage(BaseModel):
|
||||
title: str | None
|
||||
title: str | None = None
|
||||
type: QuizTivityTypes
|
||||
data: Pdf | Memory | Markdown | Abcd
|
||||
|
||||
@@ -10,19 +10,19 @@ from pydantic import BaseModel
|
||||
|
||||
|
||||
class _CoverMetadata(BaseModel):
|
||||
id: UUID | None
|
||||
resources: str | None
|
||||
id: UUID | None = None
|
||||
resources: str | None = None
|
||||
|
||||
|
||||
class _CreatorAvatar(BaseModel):
|
||||
url: str | None
|
||||
id: UUID | None
|
||||
type: str | None
|
||||
bitmojiAvatarId: str | None
|
||||
altText: str | None
|
||||
contentType: str | None
|
||||
width: int | None
|
||||
height: int | None
|
||||
url: str | None = None
|
||||
id: UUID | None = None
|
||||
type: str | None = None
|
||||
bitmojiAvatarId: str | None = None
|
||||
altText: str | None = None
|
||||
contentType: str | None = None
|
||||
width: int | None = None
|
||||
height: int | None = None
|
||||
|
||||
|
||||
class _LastEdit(BaseModel):
|
||||
@@ -32,19 +32,19 @@ class _LastEdit(BaseModel):
|
||||
|
||||
|
||||
class _ImageMetadata(BaseModel):
|
||||
id: UUID | None
|
||||
content_type: Optional[str]
|
||||
width: Optional[int]
|
||||
height: Optional[int]
|
||||
resources: Optional[str]
|
||||
id: UUID | None = None
|
||||
content_type: Optional[str] = None
|
||||
width: Optional[int] = None
|
||||
height: Optional[int] = None
|
||||
resources: Optional[str] = None
|
||||
|
||||
|
||||
class _SampleQuestion(BaseModel):
|
||||
image: str | None
|
||||
imageMetadata: _ImageMetadata | None
|
||||
image: str | None = None
|
||||
imageMetadata: _ImageMetadata | None = None
|
||||
title: str
|
||||
type: str
|
||||
time: int | None
|
||||
time: int | None = None
|
||||
|
||||
|
||||
class _Access(BaseModel):
|
||||
@@ -57,19 +57,19 @@ class _Card(BaseModel):
|
||||
title: str
|
||||
description: str
|
||||
slug: str
|
||||
cover: str | None
|
||||
coverMetadata: _CoverMetadata | dict[None, None] | None
|
||||
cover: str | None = None
|
||||
coverMetadata: _CoverMetadata | dict[None, None] | None = None
|
||||
draftExists: bool
|
||||
inventoryItemIds: List[Any]
|
||||
inventoryItemIds: List[Any] = None
|
||||
number_of_questions: int
|
||||
creator: UUID
|
||||
creator_username: str
|
||||
creator_avatar: _CreatorAvatar | dict[None, None] | None
|
||||
creator_avatar: _CreatorAvatar | dict[None, None] | None = None
|
||||
badges: List[str]
|
||||
visibility: int
|
||||
locked: bool
|
||||
writeProtection: bool
|
||||
lastEdit: _LastEdit | None
|
||||
lastEdit: _LastEdit | None = None
|
||||
featured: bool
|
||||
young_featured: bool
|
||||
sponsored: bool
|
||||
@@ -111,8 +111,8 @@ class _LanguageInfo(BaseModel):
|
||||
|
||||
class _Metadata(BaseModel):
|
||||
access: _Access
|
||||
duplicationProtection: bool | None
|
||||
lastEdit: _LastEdit | None
|
||||
duplicationProtection: bool | None = None
|
||||
lastEdit: _LastEdit | None = None
|
||||
|
||||
|
||||
class _Parent(BaseModel):
|
||||
@@ -123,15 +123,15 @@ class _Parent(BaseModel):
|
||||
class _Choice(BaseModel):
|
||||
answer: str
|
||||
correct: bool
|
||||
languageInfo: _LanguageInfo | None
|
||||
languageInfo: _LanguageInfo | None = None
|
||||
|
||||
|
||||
class _Video(BaseModel):
|
||||
startTime: float
|
||||
endTime: float
|
||||
service: str
|
||||
full_url: Optional[str]
|
||||
id: Optional[str]
|
||||
full_url: Optional[str] = None
|
||||
id: Optional[str] = None
|
||||
|
||||
|
||||
class _Question(BaseModel):
|
||||
@@ -141,12 +141,12 @@ class _Question(BaseModel):
|
||||
points: bool
|
||||
pointsMultiplier: int
|
||||
choices: List[_Choice]
|
||||
image: str | None
|
||||
imageMetadata: _ImageMetadata | None
|
||||
resources: Optional[str]
|
||||
image: str | None = None
|
||||
imageMetadata: _ImageMetadata | None = None
|
||||
resources: Optional[str] = None
|
||||
video: _Video
|
||||
questionFormat: int
|
||||
languageInfo: _LanguageInfo | None
|
||||
languageInfo: _LanguageInfo | None = None
|
||||
media: List[Any]
|
||||
|
||||
|
||||
@@ -157,23 +157,23 @@ class _Kahoot(BaseModel):
|
||||
creator_username: str
|
||||
compatibilityLevel: int
|
||||
creator_primary_usage: str
|
||||
folderId: UUID | None
|
||||
folderId: UUID | None = None
|
||||
visibility: int
|
||||
difficulty: int | None
|
||||
difficulty: int | None = None
|
||||
audience: str
|
||||
audience: str
|
||||
title: str
|
||||
description: str
|
||||
quizType: str
|
||||
tags: str | None | List[str]
|
||||
cover: str | None
|
||||
coverMetadata: _CoverMetadata | dict[None, None] | None
|
||||
tags: str | None | List[str] = None
|
||||
cover: str | None = None
|
||||
coverMetadata: _CoverMetadata | dict[None, None] | None = None
|
||||
questions: List[_Question]
|
||||
metadata: _Metadata
|
||||
parent: _Parent | None
|
||||
resources: str | None
|
||||
parent: _Parent | None = None
|
||||
resources: str | None = None
|
||||
slug: str
|
||||
languageInfo: _LanguageInfo | None
|
||||
languageInfo: _LanguageInfo | None = None
|
||||
inventoryItemIds: List[Any]
|
||||
type: str
|
||||
created: int
|
||||
|
||||
@@ -15,12 +15,12 @@ from classquiz.kahoot_importer import _Entity
|
||||
class _Response(BaseModel):
|
||||
entities: List[_Entity]
|
||||
totalHits: int
|
||||
cursor: int | None
|
||||
cursor: int | None = None
|
||||
pageTimestamp: int
|
||||
|
||||
|
||||
async def search(
|
||||
query: str | None,
|
||||
query: str | None = None,
|
||||
limit: int | None = 9,
|
||||
cursor: int | None = 1,
|
||||
search_cluster: int | None = 1,
|
||||
|
||||
@@ -18,7 +18,7 @@ router = APIRouter()
|
||||
|
||||
|
||||
class SetControllerUpInput(BaseModel):
|
||||
player_name: str | None
|
||||
player_name: str | None = None
|
||||
name: str
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class LoginSession(BaseModel):
|
||||
user_id: str
|
||||
step_1: set[StartLoginResponseTypes]
|
||||
step_2: set[StartLoginResponseTypes]
|
||||
webauthn_challenge: str | None
|
||||
webauthn_challenge: str | None = None
|
||||
step1_success: bool = False
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ class StartLoginResponse(BaseModel):
|
||||
step_1: set[StartLoginResponseTypes]
|
||||
step_2: set[StartLoginResponseTypes]
|
||||
session_id: str
|
||||
webauthn_data: None | str
|
||||
webauthn_data: None | str = None
|
||||
|
||||
|
||||
def verify_webauthn(data, fidocredentialss: list[FidoCredentials], login_session: LoginSession):
|
||||
|
||||
@@ -47,7 +47,7 @@ async def get_newest_quizzes(
|
||||
|
||||
|
||||
class SetModRatingForQuizInput(BaseModel):
|
||||
rating: int | None
|
||||
rating: int | None = None
|
||||
|
||||
|
||||
@router.post("/rating/set/{quiz_id}")
|
||||
|
||||
@@ -15,13 +15,14 @@ import ormar.exceptions
|
||||
from classquiz.helpers import generate_spreadsheet, handle_import_from_excel
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
from pydantic import ValidationError, BaseModel, Field
|
||||
from pydantic import ValidationError, BaseModel
|
||||
|
||||
from classquiz.auth import get_current_user
|
||||
from classquiz.config import redis, settings, storage, meilisearch
|
||||
from classquiz.db.models import Quiz, User, PlayGame, GameInLobby, QuizQuestion, QuizQuestionType
|
||||
from classquiz.helpers.box_controller import generate_code
|
||||
from classquiz.kahoot_importer.import_quiz import import_quiz
|
||||
from uuid import UUID
|
||||
import urllib.parse
|
||||
|
||||
settings = settings()
|
||||
@@ -54,9 +55,9 @@ class PublicQuizResponseUser(BaseModel):
|
||||
id: uuid.UUID
|
||||
|
||||
|
||||
class PublicQuizResponse(Quiz.get_pydantic()):
|
||||
class PublicQuizResponse(Quiz.get_pydantic(exclude={"questions"})):
|
||||
user_id: PublicQuizResponseUser
|
||||
var_questions: list[QuizQuestion] = Field(..., alias="questions")
|
||||
questions: list[QuizQuestion]
|
||||
likes: int
|
||||
dislikes: int
|
||||
views: int
|
||||
@@ -143,8 +144,8 @@ async def start_quiz(
|
||||
|
||||
class CheckIfCaptchaEnabledResponse(BaseModel):
|
||||
enabled: bool
|
||||
game_mode: str | None
|
||||
custom_field: str | None
|
||||
game_mode: str | None = None
|
||||
custom_field: str | None = None
|
||||
|
||||
|
||||
@router.get("/play/check_captcha/{game_pin}", response_model=CheckIfCaptchaEnabledResponse)
|
||||
@@ -229,7 +230,8 @@ async def export_quiz_answers(export_token: str, game_pin: str):
|
||||
data = json.loads(data)
|
||||
data2 = await redis.get(f"game:{game_pin}")
|
||||
game_data = PlayGame.parse_raw(data2)
|
||||
quiz = await Quiz.objects.get_or_none(id=game_data.quiz_id)
|
||||
print(type(game_data.quiz_id))
|
||||
quiz = await Quiz.objects.get_or_none(id=UUID(game_data.quiz_id))
|
||||
if quiz is None:
|
||||
raise HTTPException(status_code=404, detail="quiz not found")
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ async def get_shares(user: User = Depends(get_current_user)) -> list[PublicQuizT
|
||||
|
||||
|
||||
class CreateShareInput(BaseModel):
|
||||
name: str | None
|
||||
name: str | None = None
|
||||
quiztivity: UUID
|
||||
expire_in: int | None
|
||||
expire_in: int | None = None
|
||||
|
||||
|
||||
@router.post("/")
|
||||
@@ -55,8 +55,8 @@ async def delete_share(uuid: UUID, user: User = Depends(get_current_user)):
|
||||
|
||||
|
||||
class UpdateShareInput(BaseModel):
|
||||
name: str | None
|
||||
expire_in: int | None
|
||||
name: str | None = None
|
||||
expire_in: int | None = None
|
||||
|
||||
|
||||
@router.put("/{uuid}")
|
||||
|
||||
@@ -297,7 +297,7 @@ async def get_other_avatar(respo: Response, user_id: uuid.UUID):
|
||||
|
||||
class InternalAuthData(BaseModel):
|
||||
rememberme: str
|
||||
jwt: str | None
|
||||
jwt: str | None = None
|
||||
|
||||
|
||||
@router.post("/auth/internal")
|
||||
|
||||
@@ -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:
|
||||
if answers is None:
|
||||
answers = AnswerDataList(__root__=[data])
|
||||
answers = AnswerDataList([data])
|
||||
else:
|
||||
answers = AnswerDataList.parse_raw(answers)
|
||||
answers.__root__.append(data)
|
||||
answers.root.append(data)
|
||||
await redis.set(
|
||||
f"game_session:{game_pin}:{q_index}",
|
||||
answers.json(),
|
||||
@@ -76,8 +76,8 @@ async def set_answer(answers, game_pin: str, q_index: int, data: AnswerData) ->
|
||||
class _JoinGameData(BaseModel):
|
||||
username: str
|
||||
game_pin: str
|
||||
captcha: str | None
|
||||
custom_field: str | None
|
||||
captcha: str | None = None
|
||||
custom_field: str | None = None
|
||||
|
||||
|
||||
class _RejoinGameData(BaseModel):
|
||||
@@ -280,7 +280,7 @@ async def get_question_results(sid: str, data: dict):
|
||||
if redis_res is None:
|
||||
redis_res = []
|
||||
else:
|
||||
redis_res = AnswerDataList.parse_raw(redis_res).dict()["__root__"]
|
||||
redis_res = AnswerDataList.parse_raw(redis_res).model_dump()
|
||||
game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}"))
|
||||
game_data.question_show = False
|
||||
await redis.set(f"game:{session['game_pin']}", game_data.json())
|
||||
@@ -291,7 +291,7 @@ async def get_question_results(sid: str, data: dict):
|
||||
|
||||
class ABCDQuizAnswerWithoutSolution(BaseModel):
|
||||
answer: str
|
||||
color: str | None
|
||||
color: str | None = None
|
||||
|
||||
|
||||
class RangeQuizAnswerWithoutSolution(BaseModel):
|
||||
@@ -360,7 +360,7 @@ class _SubmitAnswerDataOrderType(BaseModel):
|
||||
class _SubmitAnswerData(BaseModel):
|
||||
question_index: int
|
||||
answer: str
|
||||
complex_answer: list[_SubmitAnswerDataOrderType] | None
|
||||
complex_answer: list[_SubmitAnswerDataOrderType] | None = None
|
||||
|
||||
|
||||
@sio.event
|
||||
@@ -448,7 +448,7 @@ async def submit_answer(sid: str, data: dict):
|
||||
)
|
||||
player_count = await redis.scard(f"game_session:{session['game_pin']}:players")
|
||||
await sio.emit("player_answer", {})
|
||||
if len(answers.__root__) == player_count:
|
||||
if len(answers.root) == player_count:
|
||||
# await sio.emit(
|
||||
# "question_results",
|
||||
# await redis.get(f"game_session:{session['game_pin']}:{data.question_index}"),
|
||||
|
||||
Reference in New Issue
Block a user