🐛 Fix Pydantic breaking changes

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