Untested Ormar and FastAPI update
This commit is contained in:
@@ -8,16 +8,13 @@ verify_ssl = true
|
|||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
fastapi = "*"
|
|
||||||
uvicorn = "*"
|
uvicorn = "*"
|
||||||
python-socketio = "*"
|
python-socketio = "*"
|
||||||
ormar = { version = "*", extras = ["postgresql"] }
|
|
||||||
passlib = "*"
|
passlib = "*"
|
||||||
python-jose = "*"
|
python-jose = "*"
|
||||||
alembic = "*"
|
alembic = "*"
|
||||||
email-validator = "*"
|
email-validator = "*"
|
||||||
python-multipart = "*"
|
python-multipart = "*"
|
||||||
pydantic = "1.10.8"
|
|
||||||
redis = "*"
|
redis = "*"
|
||||||
aiohttp = "*"
|
aiohttp = "*"
|
||||||
gunicorn = "*"
|
gunicorn = "*"
|
||||||
@@ -46,6 +43,11 @@ starlette = "*"
|
|||||||
pyopenssl = "*"
|
pyopenssl = "*"
|
||||||
python-dotenv = "*"
|
python-dotenv = "*"
|
||||||
webauthn = "==1.*"
|
webauthn = "==1.*"
|
||||||
|
fastapi = "*"
|
||||||
|
pypng = "*"
|
||||||
|
ormar = "*"
|
||||||
|
pydantic = "*"
|
||||||
|
pydantic-settings = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
coverage = "*"
|
coverage = "*"
|
||||||
|
|||||||
Generated
+429
-427
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -16,7 +16,7 @@ settings = settings()
|
|||||||
|
|
||||||
async def cache_account(criteria: str, content: str) -> Union[User, None]:
|
async def cache_account(criteria: str, content: str) -> Union[User, None]:
|
||||||
async def insert_into_redis(usermodel: User, key: str):
|
async def insert_into_redis(usermodel: User, key: str):
|
||||||
await redis.set(key, usermodel.json(), ex=settings.cache_expiry)
|
await redis.set(key, usermodel.model_dump_json(), ex=settings.cache_expiry)
|
||||||
|
|
||||||
if criteria == "email":
|
if criteria == "email":
|
||||||
try:
|
try:
|
||||||
@@ -54,7 +54,7 @@ async def get_from_redis(key: str) -> Union[None, User]:
|
|||||||
if user is None:
|
if user is None:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return User.parse_obj(loads(user))
|
return User.model_validate(loads(user))
|
||||||
|
|
||||||
|
|
||||||
async def get_cache(criteria: str, content: str) -> User:
|
async def get_cache(criteria: str, content: str) -> User:
|
||||||
|
|||||||
+5
-3
@@ -7,7 +7,8 @@ from functools import lru_cache
|
|||||||
|
|
||||||
from redis import asyncio as redis_lib
|
from redis import asyncio as redis_lib
|
||||||
import redis as redis_base_lib
|
import redis as redis_base_lib
|
||||||
from pydantic import BaseSettings, RedisDsn, PostgresDsn, BaseModel
|
from pydantic import RedisDsn, PostgresDsn, BaseModel
|
||||||
|
from pydantic_settings import BaseSettings
|
||||||
import meilisearch as MeiliSearch
|
import meilisearch as MeiliSearch
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from arq import create_pool
|
from arq import create_pool
|
||||||
@@ -17,7 +18,7 @@ from classquiz.storage import Storage
|
|||||||
|
|
||||||
|
|
||||||
class CustomOpenIDProvider(BaseModel):
|
class CustomOpenIDProvider(BaseModel):
|
||||||
scopes: str = "openid email profile"
|
scopes: str | None = "openid email profile"
|
||||||
server_metadata_url: str
|
server_metadata_url: str
|
||||||
client_id: str
|
client_id: str
|
||||||
client_secret: str
|
client_secret: str
|
||||||
@@ -72,6 +73,7 @@ class Settings(BaseSettings):
|
|||||||
env_file = ".env"
|
env_file = ".env"
|
||||||
env_file_encoding = "utf-8"
|
env_file_encoding = "utf-8"
|
||||||
env_nested_delimiter = "__"
|
env_nested_delimiter = "__"
|
||||||
|
extra = "allow"
|
||||||
|
|
||||||
|
|
||||||
async def initialize_arq():
|
async def initialize_arq():
|
||||||
@@ -85,7 +87,7 @@ def settings() -> Settings:
|
|||||||
return Settings()
|
return Settings()
|
||||||
|
|
||||||
|
|
||||||
pool = redis_lib.ConnectionPool().from_url(settings().redis)
|
pool = redis_lib.ConnectionPool().from_url(str(settings().redis))
|
||||||
|
|
||||||
redis: redis_base_lib.client.Redis = redis_lib.Redis(connection_pool=pool)
|
redis: redis_base_lib.client.Redis = redis_lib.Redis(connection_pool=pool)
|
||||||
arq: ArqRedis = ArqRedis(pool_or_conn=pool)
|
arq: ArqRedis = ArqRedis(pool_or_conn=pool)
|
||||||
|
|||||||
+83
-74
@@ -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, validator
|
from pydantic import BaseModel, Json, field_validator, ConfigDict, RootModel
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from . import metadata, database
|
from . import metadata, database
|
||||||
from .quiztivity import QuizTivityPage
|
from .quiztivity import QuizTivityPage
|
||||||
@@ -45,13 +45,13 @@ class User(ormar.Model):
|
|||||||
totp_secret: str = ormar.String(max_length=32, min_length=32, nullable=True, default=None)
|
totp_secret: str = ormar.String(max_length=32, min_length=32, nullable=True, default=None)
|
||||||
storage_used: int = ormar.BigInteger(nullable=False, default=0, minimum=0)
|
storage_used: int = ormar.BigInteger(nullable=False, default=0, minimum=0)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "users"
|
tablename="users",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
class Config:
|
model_config = ConfigDict(use_enum_values=True)
|
||||||
use_enum_values = True
|
|
||||||
|
|
||||||
|
|
||||||
class FidoCredentials(ormar.Model):
|
class FidoCredentials(ormar.Model):
|
||||||
@@ -61,20 +61,22 @@ class FidoCredentials(ormar.Model):
|
|||||||
sign_count: int = ormar.Integer()
|
sign_count: int = ormar.Integer()
|
||||||
user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
|
user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "fido_credentials"
|
tablename="fido_credentials",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ApiKey(ormar.Model):
|
class ApiKey(ormar.Model):
|
||||||
key: str = ormar.String(max_length=48, min_length=48, primary_key=True)
|
key: str = ormar.String(max_length=48, min_length=48, primary_key=True)
|
||||||
user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
|
user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "api_keys"
|
tablename="api_keys",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserSession(ormar.Model):
|
class UserSession(ormar.Model):
|
||||||
@@ -90,10 +92,11 @@ class UserSession(ormar.Model):
|
|||||||
user_agent: str = ormar.String(max_length=255, nullable=True)
|
user_agent: str = ormar.String(max_length=255, nullable=True)
|
||||||
last_seen: datetime = ormar.DateTime(default=datetime.now())
|
last_seen: datetime = ormar.DateTime(default=datetime.now())
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "user_sessions"
|
tablename="user_sessions",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ABCDQuizAnswer(BaseModel):
|
class ABCDQuizAnswer(BaseModel):
|
||||||
@@ -137,7 +140,7 @@ class QuizQuestion(BaseModel):
|
|||||||
answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[TextQuizAnswer] | list[VotingQuizAnswer] | str
|
answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[TextQuizAnswer] | list[VotingQuizAnswer] | str
|
||||||
image: str | None = None
|
image: str | None = None
|
||||||
|
|
||||||
@validator("answers")
|
@field_validator("answers")
|
||||||
def answers_not_none_if_abcd_type(cls, v, values):
|
def answers_not_none_if_abcd_type(cls, v, values):
|
||||||
if values["type"] == QuizQuestionType.ABCD and not isinstance(v[0], ABCDQuizAnswer):
|
if values["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")
|
||||||
@@ -157,13 +160,13 @@ class QuizQuestion(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class QuizInput(BaseModel):
|
class QuizInput(BaseModel):
|
||||||
public: bool = False
|
public: bool | None = 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):
|
||||||
@@ -186,19 +189,21 @@ class Quiz(ormar.Model):
|
|||||||
views: int = ormar.Integer(nullable=False, default=0, server_default="0")
|
views: int = ormar.Integer(nullable=False, default=0, server_default="0")
|
||||||
mod_rating: int | None = ormar.SmallInteger(nullable=True)
|
mod_rating: int | None = ormar.SmallInteger(nullable=True)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "quiz"
|
tablename="quiz",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InstanceData(ormar.Model):
|
class InstanceData(ormar.Model):
|
||||||
instance_id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4(), nullable=False, unique=True)
|
instance_id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4(), nullable=False, unique=True)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "instance_data"
|
tablename="instance_data",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Token(BaseModel):
|
class Token(BaseModel):
|
||||||
@@ -228,18 +233,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):
|
||||||
@@ -273,9 +278,7 @@ class AnswerData(BaseModel):
|
|||||||
score: int
|
score: int
|
||||||
|
|
||||||
|
|
||||||
class AnswerDataList(BaseModel):
|
AnswerDataList = RootModel[list[AnswerData]]
|
||||||
# Just a method to make a top-level list
|
|
||||||
__root__: list[AnswerData]
|
|
||||||
|
|
||||||
|
|
||||||
class GameInLobby(BaseModel):
|
class GameInLobby(BaseModel):
|
||||||
@@ -307,10 +310,11 @@ class GameResults(ormar.Model):
|
|||||||
description: str = ormar.Text(nullable=False)
|
description: str = ormar.Text(nullable=False)
|
||||||
questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False)
|
questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "game_results"
|
tablename="game_results",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class QuizTivityInput(BaseModel):
|
class QuizTivityInput(BaseModel):
|
||||||
@@ -325,10 +329,11 @@ class QuizTivity(ormar.Model):
|
|||||||
user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
|
user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
|
||||||
pages: list[QuizTivityPage] = ormar.JSON(nullable=False)
|
pages: list[QuizTivityPage] = ormar.JSON(nullable=False)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "quiztivitys"
|
tablename="quiztivitys",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class QuizTivityShare(ormar.Model):
|
class QuizTivityShare(ormar.Model):
|
||||||
@@ -338,10 +343,11 @@ class QuizTivityShare(ormar.Model):
|
|||||||
quiztivity: QuizTivity | None = ormar.ForeignKey(QuizTivity, ondelete=ReferentialAction.CASCADE)
|
quiztivity: QuizTivity | None = ormar.ForeignKey(QuizTivity, ondelete=ReferentialAction.CASCADE)
|
||||||
user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
|
user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "quiztivityshares"
|
tablename="quiztivityshares",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OnlyId(BaseModel):
|
class OnlyId(BaseModel):
|
||||||
@@ -350,8 +356,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
|
||||||
|
|
||||||
@@ -386,10 +392,11 @@ class StorageItem(ormar.Model):
|
|||||||
server: str | None = ormar.Text(default=None, nullable=True)
|
server: str | None = ormar.Text(default=None, nullable=True)
|
||||||
imported: bool = ormar.Boolean(default=False, nullable=True)
|
imported: bool = ormar.Boolean(default=False, nullable=True)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "storage_items"
|
tablename="storage_items",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PublicStorageItem(BaseModel):
|
class PublicStorageItem(BaseModel):
|
||||||
@@ -398,11 +405,11 @@ class PublicStorageItem(BaseModel):
|
|||||||
mime_type: str
|
mime_type: str
|
||||||
hash: str | None
|
hash: str | 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
|
||||||
@@ -458,8 +465,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):
|
||||||
@@ -473,10 +480,11 @@ class Controller(ormar.Model):
|
|||||||
os_version: str | None = ormar.Text(nullable=True)
|
os_version: str | None = ormar.Text(nullable=True)
|
||||||
wanted_os_version: str = ormar.Text(nullable=True, default=None)
|
wanted_os_version: str = ormar.Text(nullable=True, default=None)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "controller"
|
tablename="controller",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Rating(ormar.Model):
|
class Rating(ormar.Model):
|
||||||
@@ -486,7 +494,8 @@ class Rating(ormar.Model):
|
|||||||
created_at: datetime = ormar.DateTime(nullable=False, server_default=func.now())
|
created_at: datetime = ormar.DateTime(nullable=False, server_default=func.now())
|
||||||
quiz: uuid.UUID | Quiz = ormar.ForeignKey(Quiz)
|
quiz: uuid.UUID | Quiz = ormar.ForeignKey(Quiz)
|
||||||
|
|
||||||
class Meta:
|
ormar_config = ormar.OrmarConfig(
|
||||||
tablename = "rating"
|
tablename="rating",
|
||||||
metadata = metadata
|
metadata=metadata,
|
||||||
database = database
|
database=database,
|
||||||
|
)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ async def handle_import_from_excel(data: BinaryIO, user: User) -> Quiz:
|
|||||||
answers=answers_list,
|
answers=answers_list,
|
||||||
time=str(time),
|
time=str(time),
|
||||||
image=existing_question["image"] if existing_question is not None else None,
|
image=existing_question["image"] if existing_question is not None else None,
|
||||||
).dict()
|
).model_dump()
|
||||||
)
|
)
|
||||||
if existing_quiz is None:
|
if existing_quiz is None:
|
||||||
quiz = Quiz(
|
quiz = Quiz(
|
||||||
|
|||||||
@@ -127,9 +127,9 @@ class NotFoundError(Exception):
|
|||||||
async def get_images(api_key: str, params: GetImagesParams) -> GetImagesResponse:
|
async def get_images(api_key: str, params: GetImagesParams) -> GetImagesResponse:
|
||||||
async with (
|
async with (
|
||||||
ClientSession() as session,
|
ClientSession() as session,
|
||||||
session.get("https://pixabay.com/api/", params={"key": api_key, **params.dict()}) as resp,
|
session.get("https://pixabay.com/api/", params={"key": api_key, **params.model_dump()}) as resp,
|
||||||
):
|
):
|
||||||
if resp.status == 200:
|
if resp.status == 200:
|
||||||
return GetImagesResponse.parse_obj(await resp.json())
|
return GetImagesResponse.model_validate(await resp.json())
|
||||||
else:
|
else:
|
||||||
raise NotFoundError
|
raise NotFoundError
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from asyncio import run
|
|||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
kahoot_quiz: _Response = await get("GAME_ID")
|
kahoot_quiz: _Response = await get("GAME_ID")
|
||||||
print(kahoot_quiz.json(exclude={"kahoot"}))
|
print(kahoot_quiz.model_dump_json(exclude={"kahoot"}))
|
||||||
run(main())
|
run(main())
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ from asyncio import run
|
|||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
kahoot_quizzes: _Response = await search("QUERY")
|
kahoot_quizzes: _Response = await search("QUERY")
|
||||||
print(kahoot_quizzes.json())
|
print(kahoot_quizzes.model_dump_json())
|
||||||
run(main())
|
run(main())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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,7 +32,7 @@ class _LastEdit(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class _ImageMetadata(BaseModel):
|
class _ImageMetadata(BaseModel):
|
||||||
id: UUID | None
|
id: UUID | None = None
|
||||||
content_type: Optional[str]
|
content_type: Optional[str]
|
||||||
width: Optional[int]
|
width: Optional[int]
|
||||||
height: Optional[int]
|
height: Optional[int]
|
||||||
@@ -40,11 +40,11 @@ class _ImageMetadata(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
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]
|
||||||
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,7 +123,7 @@ 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):
|
||||||
@@ -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]
|
||||||
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
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | int:
|
|||||||
answers=answers,
|
answers=answers,
|
||||||
time=str(q.time / 1000),
|
time=str(q.time / 1000),
|
||||||
image=image,
|
image=image,
|
||||||
).dict()
|
).model_dump()
|
||||||
)
|
)
|
||||||
cover = None
|
cover = None
|
||||||
if quiz.kahoot.cover != "" and quiz.kahoot.cover is not None:
|
if quiz.kahoot.cover != "" and quiz.kahoot.cover is not None:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ 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
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+35
-35
@@ -35,43 +35,43 @@ class Plan(BaseModel):
|
|||||||
class GitHubOauthResponse(BaseModel):
|
class GitHubOauthResponse(BaseModel):
|
||||||
login: str
|
login: str
|
||||||
id: int
|
id: int
|
||||||
email: Optional[str]
|
email: Optional[str] = None
|
||||||
node_id: Optional[str]
|
node_id: Optional[str] = None
|
||||||
avatar_url: Optional[str]
|
avatar_url: Optional[str] = None
|
||||||
gravatar_id: Optional[str]
|
gravatar_id: Optional[str] = None
|
||||||
url: Optional[str]
|
url: Optional[str] = None
|
||||||
html_url: Optional[str]
|
html_url: Optional[str] = None
|
||||||
followers_url: Optional[str]
|
followers_url: Optional[str] = None
|
||||||
following_url: Optional[str]
|
following_url: Optional[str] = None
|
||||||
gists_url: Optional[str]
|
gists_url: Optional[str] = None
|
||||||
starred_url: Optional[str]
|
starred_url: Optional[str] = None
|
||||||
subscriptions_url: Optional[str]
|
subscriptions_url: Optional[str] = None
|
||||||
organizations_url: Optional[str]
|
organizations_url: Optional[str] = None
|
||||||
repos_url: Optional[str]
|
repos_url: Optional[str] = None
|
||||||
events_url: Optional[str]
|
events_url: Optional[str] = None
|
||||||
received_events_url: Optional[str]
|
received_events_url: Optional[str] = None
|
||||||
type: Optional[str]
|
type: Optional[str] = None
|
||||||
site_admin: Optional[str]
|
site_admin: Optional[str] = None
|
||||||
name: Optional[str]
|
name: Optional[str] = None
|
||||||
company: Optional[str]
|
company: Optional[str] = None
|
||||||
blog: Optional[str]
|
blog: Optional[str] = None
|
||||||
location: Optional[str]
|
location: Optional[str] = None
|
||||||
hireable: Optional[bool]
|
hireable: Optional[bool] = None
|
||||||
bio: Optional[str]
|
bio: Optional[str] = None
|
||||||
twitter_username: Optional[str]
|
twitter_username: Optional[str] = None
|
||||||
public_repos: Optional[int]
|
public_repos: Optional[int] = None
|
||||||
public_gists: Optional[int]
|
public_gists: Optional[int] = None
|
||||||
followers: Optional[int]
|
followers: Optional[int] = None
|
||||||
following: Optional[int]
|
following: Optional[int] = None
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
private_gists: Optional[int]
|
private_gists: Optional[int] = None
|
||||||
total_private_repos: Optional[int]
|
total_private_repos: Optional[int] = None
|
||||||
owned_private_repos: Optional[int]
|
owned_private_repos: Optional[int] = None
|
||||||
disk_usage: Optional[int]
|
disk_usage: Optional[int] = None
|
||||||
collaborators: Optional[int]
|
collaborators: Optional[int] = None
|
||||||
two_factor_authentication: Optional[bool]
|
two_factor_authentication: Optional[bool] = None
|
||||||
plan: Optional[Plan]
|
plan: Optional[Plan] = None
|
||||||
|
|
||||||
|
|
||||||
@router.get("/login")
|
@router.get("/login")
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ async def join_game(data: JoinGameInput) -> JoinGameResponse:
|
|||||||
if game_pin is None:
|
if game_pin is None:
|
||||||
raise HTTPException(status_code=404, detail="Game not found")
|
raise HTTPException(status_code=404, detail="Game not found")
|
||||||
game = await redis.get(f"game:{game_pin}")
|
game = await redis.get(f"game:{game_pin}")
|
||||||
game = PlayGame.parse_raw(game)
|
game = PlayGame.model_validate_json(game)
|
||||||
# Check if game is already running
|
# Check if game is already running
|
||||||
if game.started:
|
if game.started:
|
||||||
raise HTTPException(status_code=400, detail="Game started already")
|
raise HTTPException(status_code=400, detail="Game started already")
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ async def submit_answer_fn(data_answer: int, game_pin: str, player_id: str, now:
|
|||||||
username = await redis.get(f"game:cqc:player:{player_id}")
|
username = await redis.get(f"game:cqc:player:{player_id}")
|
||||||
if redis_res_game is None or username is None:
|
if redis_res_game is None or username is None:
|
||||||
raise HTTPException(status_code=404, detail="id not existent")
|
raise HTTPException(status_code=404, detail="id not existent")
|
||||||
game = PlayGame.parse_raw(redis_res_game)
|
game = PlayGame.model_validate_json(redis_res_game)
|
||||||
if not game.question_show:
|
if not game.question_show:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ async def websocket_endpoint(ws: WebSocket, game_id: str):
|
|||||||
|
|
||||||
player_id, game_pin = game_id.split(":")
|
player_id, game_pin = game_id.split(":")
|
||||||
if player_id is None or game_pin is None:
|
if player_id is None or game_pin is None:
|
||||||
await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="BadId").json())
|
await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="BadId").model_dump_json())
|
||||||
await ws.close(code=status.WS_1003_UNSUPPORTED_DATA)
|
await ws.close(code=status.WS_1003_UNSUPPORTED_DATA)
|
||||||
username = await redis.get(f"game:cqc:player:{player_id}")
|
username = await redis.get(f"game:cqc:player:{player_id}")
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
@@ -104,17 +104,19 @@ async def websocket_endpoint(ws: WebSocket, game_id: str):
|
|||||||
{"username": username, "sid": None},
|
{"username": username, "sid": None},
|
||||||
room=f"admin:{game_pin}",
|
room=f"admin:{game_pin}",
|
||||||
)
|
)
|
||||||
await redis.sadd(f"game_session:{game_pin}:players", GamePlayer(username=username, sid=None).json())
|
await redis.sadd(f"game_session:{game_pin}:players", GamePlayer(username=username, sid=None).model_dump_json())
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
raw_data = await ws.receive_text()
|
raw_data = await ws.receive_text()
|
||||||
try:
|
try:
|
||||||
data = WebSocketRequest.parse_raw(raw_data)
|
data = WebSocketRequest.model_validate_json(raw_data)
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
print("ValError")
|
print("ValError")
|
||||||
print(e)
|
print(e)
|
||||||
print(raw_data)
|
print(raw_data)
|
||||||
await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="ValidationError").json())
|
await ws.send_text(
|
||||||
|
WebSocketRequest(type=WebSocketTypes.Error, data="ValidationError").model_dump_json()
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if data.type == WebSocketTypes.ButtonPress:
|
if data.type == WebSocketTypes.ButtonPress:
|
||||||
@@ -122,7 +124,9 @@ async def websocket_endpoint(ws: WebSocket, game_id: str):
|
|||||||
try:
|
try:
|
||||||
answer_index = button_to_index_map[data.data.lower()]
|
answer_index = button_to_index_map[data.data.lower()]
|
||||||
except (KeyError, AttributeError):
|
except (KeyError, AttributeError):
|
||||||
await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="InvalidButton").json())
|
await ws.send_text(
|
||||||
|
WebSocketRequest(type=WebSocketTypes.Error, data="InvalidButton").model_dump_json()
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
await submit_answer_fn(answer_index, game_pin, player_id, now)
|
await submit_answer_fn(answer_index, game_pin, player_id, now)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ async def get_controller(id: uuid.UUID, user: User = Depends(get_current_user))
|
|||||||
controller = await Controller.objects.get_or_none(id=id, user=user.id)
|
controller = await Controller.objects.get_or_none(id=id, user=user.id)
|
||||||
if controller is None:
|
if controller is None:
|
||||||
raise HTTPException(status_code=404, detail="Controller not found")
|
raise HTTPException(status_code=404, detail="Controller not found")
|
||||||
return GetControllerResponse(**controller.dict())
|
return GetControllerResponse(**controller.model_dump())
|
||||||
|
|
||||||
|
|
||||||
class ModifyControllerInput(BaseModel):
|
class ModifyControllerInput(BaseModel):
|
||||||
@@ -76,7 +76,7 @@ async def modify_controller(
|
|||||||
controller.player_name = data.player_name
|
controller.player_name = data.player_name
|
||||||
controller.name = data.name
|
controller.name = data.name
|
||||||
await controller.update()
|
await controller.update()
|
||||||
return GetControllerResponse(**controller.dict())
|
return GetControllerResponse(**controller.model_dump())
|
||||||
|
|
||||||
|
|
||||||
@router.get("/list")
|
@router.get("/list")
|
||||||
@@ -86,7 +86,7 @@ async def get_all_controllers(user: User = Depends(get_current_user)) -> list[Ge
|
|||||||
return []
|
return []
|
||||||
return_list = []
|
return_list = []
|
||||||
for controller in controllers:
|
for controller in controllers:
|
||||||
return_list.append(GetControllerResponse(**controller.dict()))
|
return_list.append(GetControllerResponse(**controller.model_dump()))
|
||||||
return return_list
|
return return_list
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,9 @@ async def init_editor(edit: bool, quiz_id: Optional[UUID] = None, user: User = D
|
|||||||
edit_id = os.urandom(4).hex()
|
edit_id = os.urandom(4).hex()
|
||||||
await redis.sadd("edit_sessions", edit_id)
|
await redis.sadd("edit_sessions", edit_id)
|
||||||
await redis.set(
|
await redis.set(
|
||||||
f"edit_session:{edit_id}", EditSessionData(quiz_id=quiz_id, edit=edit, user_id=user.id).json(), ex=3600
|
f"edit_session:{edit_id}",
|
||||||
|
EditSessionData(quiz_id=quiz_id, edit=edit, user_id=user.id).model_dump_json(),
|
||||||
|
ex=3600,
|
||||||
)
|
)
|
||||||
return InitEditorResponse(token=edit_id)
|
return InitEditorResponse(token=edit_id)
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput):
|
|||||||
session_data = await redis.get(f"edit_session:{edit_id}")
|
session_data = await redis.get(f"edit_session:{edit_id}")
|
||||||
if session_data is None:
|
if session_data is None:
|
||||||
raise HTTPException(status_code=401, detail="Edit ID not found!")
|
raise HTTPException(status_code=401, detail="Edit ID not found!")
|
||||||
session_data = EditSessionData.parse_raw(session_data)
|
session_data = EditSessionData.model_validate_json(session_data)
|
||||||
quiz_input.title = bleach.clean(quiz_input.title, tags=ALLOWED_TAGS_FOR_QUIZ, strip=True)
|
quiz_input.title = bleach.clean(quiz_input.title, tags=ALLOWED_TAGS_FOR_QUIZ, strip=True)
|
||||||
quiz_input.description = bleach.clean(quiz_input.description, tags=ALLOWED_TAGS_FOR_QUIZ, strip=True)
|
quiz_input.description = bleach.clean(quiz_input.description, tags=ALLOWED_TAGS_FOR_QUIZ, strip=True)
|
||||||
if quiz_input.background_color is not None:
|
if quiz_input.background_color is not None:
|
||||||
@@ -122,7 +124,7 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput):
|
|||||||
quiz.public = quiz_input.public
|
quiz.public = quiz_input.public
|
||||||
quiz.description = quiz_input.description
|
quiz.description = quiz_input.description
|
||||||
quiz.updated_at = datetime.now()
|
quiz.updated_at = datetime.now()
|
||||||
quiz.questions = quiz_input.dict()["questions"]
|
quiz.questions = quiz_input.model_dump()["questions"]
|
||||||
quiz.cover_image = quiz_input.cover_image
|
quiz.cover_image = quiz_input.cover_image
|
||||||
quiz.background_color = quiz_input.background_color
|
quiz.background_color = quiz_input.background_color
|
||||||
quiz.background_image = quiz_input.background_image
|
quiz.background_image = quiz_input.background_image
|
||||||
@@ -140,7 +142,7 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput):
|
|||||||
return quiz
|
return quiz
|
||||||
else:
|
else:
|
||||||
quiz = Quiz(
|
quiz = Quiz(
|
||||||
**quiz_input.dict(),
|
**quiz_input.model_dump(),
|
||||||
user_id=session_data.user_id,
|
user_id=session_data.user_id,
|
||||||
id=session_data.quiz_id,
|
id=session_data.quiz_id,
|
||||||
created_at=datetime.now(),
|
created_at=datetime.now(),
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ async def export_quiz(quiz_id: uuid.UUID, user: User = Depends(get_current_user)
|
|||||||
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.dict()
|
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()
|
||||||
quiz_dict["updated_at"] = quiz_dict["updated_at"].isoformat()
|
quiz_dict["updated_at"] = quiz_dict["updated_at"].isoformat()
|
||||||
@@ -129,7 +129,7 @@ async def import_quiz(file: UploadFile = File(), user: User = Depends(get_curren
|
|||||||
question["image"] = image_urls[question["image"]]
|
question["image"] = image_urls[question["image"]]
|
||||||
if quiz_dict["cover_image"] is not None:
|
if quiz_dict["cover_image"] is not None:
|
||||||
quiz_dict["cover_image"] = image_urls[-1]
|
quiz_dict["cover_image"] = image_urls[-1]
|
||||||
quiz = Quiz.parse_obj(quiz_dict)
|
quiz = Quiz.model_validate(quiz_dict)
|
||||||
quiz.user_id = user.id
|
quiz.user_id = user.id
|
||||||
quiz.imported_from_kahoot = None
|
quiz.imported_from_kahoot = None
|
||||||
quiz.mod_rating = None
|
quiz.mod_rating = None
|
||||||
@@ -165,7 +165,7 @@ async def export_quiz_as_excel(quiz_id: uuid.UUID, user: User = Depends(get_curr
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
for i, question in enumerate(quiz.questions):
|
for i, question in enumerate(quiz.questions):
|
||||||
question = QuizQuestion.parse_obj(question)
|
question = QuizQuestion.model_validate(question)
|
||||||
data: list[Any] = [None] * 9
|
data: list[Any] = [None] * 9
|
||||||
data[0] = i + 1
|
data[0] = i + 1
|
||||||
data[1] = question.question
|
data[1] = question.question
|
||||||
|
|||||||
+19
-17
@@ -7,7 +7,7 @@ import json
|
|||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException
|
from fastapi import APIRouter, HTTPException
|
||||||
from pydantic import BaseModel, validator
|
from pydantic import BaseModel, field_validator
|
||||||
from classquiz.config import settings, redis
|
from classquiz.config import settings, redis
|
||||||
from classquiz.db.models import (
|
from classquiz.db.models import (
|
||||||
PlayGame,
|
PlayGame,
|
||||||
@@ -41,7 +41,7 @@ class _ABCDQuizAnswer(ABCDQuizAnswer):
|
|||||||
class _QuizQuestion(QuizQuestion):
|
class _QuizQuestion(QuizQuestion):
|
||||||
answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[VotingQuizAnswer]
|
answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[VotingQuizAnswer]
|
||||||
|
|
||||||
@validator("answers")
|
@field_validator("answers")
|
||||||
def answers_not_none_if_abcd_type(cls, v, values):
|
def answers_not_none_if_abcd_type(cls, v, values):
|
||||||
# if values["type"] == QuizQuestionType.ABCD and type(v[0]) != _ABCDQuizAnswer:
|
# if values["type"] == QuizQuestionType.ABCD and type(v[0]) != _ABCDQuizAnswer:
|
||||||
# print(type(v[0]), values)
|
# print(type(v[0]), values)
|
||||||
@@ -83,7 +83,7 @@ async def get_live_game_data(
|
|||||||
redis_res = await redis.get(f"game:{game_pin}")
|
redis_res = await redis.get(f"game:{game_pin}")
|
||||||
if redis_res is None or user_id is None:
|
if redis_res is None or user_id is None:
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
game = _PlayGame.parse_raw(redis_res)
|
game = _PlayGame.model_validate_json(redis_res)
|
||||||
if game.user_id != user_id:
|
if game.user_id != user_id:
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
for i, question in enumerate(game.questions):
|
for i, question in enumerate(game.questions):
|
||||||
@@ -105,18 +105,18 @@ async def get_live_game_data(
|
|||||||
data_redis_res = await redis.get(f"game_session:{game_pin}")
|
data_redis_res = await redis.get(f"game_session:{game_pin}")
|
||||||
if data_redis_res is None:
|
if data_redis_res is None:
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
data = GameSession.parse_raw(data_redis_res)
|
data = GameSession.model_validate_json(data_redis_res)
|
||||||
for i in range(0, len(game.questions)):
|
for i in range(0, len(game.questions)):
|
||||||
res = await redis.get(f"game_session:{game_pin}:{i}")
|
res = await redis.get(f"game_session:{game_pin}:{i}")
|
||||||
if res is None:
|
if res is None:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
res = json.loads(res)
|
res = json.loads(res)
|
||||||
ga_1 = GameAnswer1(id=i, answers=[GameAnswer2.parse_obj(i) for i in res])
|
ga_1 = GameAnswer1(id=i, answers=[GameAnswer2.model_validate(i) for i in res])
|
||||||
data.answers.append(ga_1)
|
data.answers.append(ga_1)
|
||||||
player_count = await redis.scard(f"game_session:{game_pin}:players")
|
player_count = await redis.scard(f"game_session:{game_pin}:players")
|
||||||
total_questions = len(game.questions)
|
total_questions = len(game.questions)
|
||||||
game = _GetLivePlayGame(**{**game.dict(), "total_questions": total_questions})
|
game = _GetLivePlayGame(**{**game.model_dump(), "total_questions": total_questions})
|
||||||
if in_human_count:
|
if in_human_count:
|
||||||
game.current_question += 1
|
game.current_question += 1
|
||||||
|
|
||||||
@@ -160,10 +160,10 @@ async def get_game_session(game_pin: str, api_key: str | None = None, game_id: u
|
|||||||
redis_res = await redis.get(f"game_session:{game_pin}")
|
redis_res = await redis.get(f"game_session:{game_pin}")
|
||||||
if redis_res is None:
|
if redis_res is None:
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
data = GameSession.parse_raw(redis_res)
|
data = GameSession.model_validate_json(redis_res)
|
||||||
if user_id is None and data.game_id != str(game_id):
|
if user_id is None and data.game_id != str(game_id):
|
||||||
raise HTTPException(status_code=401, detail="Game not found or API key not found")
|
raise HTTPException(status_code=401, detail="Game not found or API key not found")
|
||||||
game = PlayGame.parse_raw(await redis.get(f"game:{game_pin}"))
|
game = PlayGame.model_validate_json(await redis.get(f"game:{game_pin}"))
|
||||||
if game.user_id != user_id and data.game_id != str(game_id):
|
if game.user_id != user_id and data.game_id != str(game_id):
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
for i in range(0, len(game.questions)):
|
for i in range(0, len(game.questions)):
|
||||||
@@ -172,12 +172,12 @@ async def get_game_session(game_pin: str, api_key: str | None = None, game_id: u
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
res = json.loads(res)
|
res = json.loads(res)
|
||||||
ga_1 = GameAnswer1(id=i, answers=[GameAnswer2.parse_obj(i) for i in res])
|
ga_1 = GameAnswer1(id=i, answers=[GameAnswer2.model_validate(i) for i in res])
|
||||||
data.answers.append(ga_1)
|
data.answers.append(ga_1)
|
||||||
players = await redis.smembers(f"game_session:{game_pin}:players")
|
players = await redis.smembers(f"game_session:{game_pin}:players")
|
||||||
player_list = []
|
player_list = []
|
||||||
for p in players:
|
for p in players:
|
||||||
player_list.append(GamePlayer.parse_raw(p))
|
player_list.append(GamePlayer.model_validate_json(p))
|
||||||
return player_list
|
return player_list
|
||||||
|
|
||||||
|
|
||||||
@@ -190,16 +190,18 @@ async def set_next_question(game_pin: str, question_number: int, api_key: str):
|
|||||||
redis_res = await redis.get(f"game:{game_pin}")
|
redis_res = await redis.get(f"game:{game_pin}")
|
||||||
if redis_res is None or user_id is None:
|
if redis_res is None or user_id is None:
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
game_data = PlayGame.parse_raw(redis_res)
|
game_data = PlayGame.model_validate_json(redis_res)
|
||||||
if game_data.user_id != user_id:
|
if game_data.user_id != user_id:
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
game_data.current_question = question_number
|
game_data.current_question = question_number
|
||||||
await redis.set(f"game:{game_pin}", game_data.json(), ex=18000)
|
await redis.set(f"game:{game_pin}", game_data.model_dump_json(), ex=18000)
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
"set_question_number",
|
"set_question_number",
|
||||||
{
|
{
|
||||||
"question_index": question_number,
|
"question_index": question_number,
|
||||||
"question": ReturnQuestion(**game_data.dict(include={"questions"})["questions"][question_number]).dict(),
|
"question": ReturnQuestion(
|
||||||
|
**game_data.model_dump(include={"questions"})["questions"][question_number]
|
||||||
|
).model_dump(),
|
||||||
},
|
},
|
||||||
room=game_pin,
|
room=game_pin,
|
||||||
)
|
)
|
||||||
@@ -231,7 +233,7 @@ async def too_stupid_to_come_up_with_a_name(game_pin: str, api_key: str, in_huma
|
|||||||
redis_res = await redis.get(f"game:{game_pin}")
|
redis_res = await redis.get(f"game:{game_pin}")
|
||||||
if redis_res is None or user_id is None:
|
if redis_res is None or user_id is None:
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
game = PlayGame.parse_raw(redis_res)
|
game = PlayGame.model_validate_json(redis_res)
|
||||||
for i, question in enumerate(game.questions):
|
for i, question in enumerate(game.questions):
|
||||||
if question.type == QuizQuestionType.ABCD:
|
if question.type == QuizQuestionType.ABCD:
|
||||||
for o, answer in enumerate(question.answers):
|
for o, answer in enumerate(question.answers):
|
||||||
@@ -244,7 +246,7 @@ async def too_stupid_to_come_up_with_a_name(game_pin: str, api_key: str, in_huma
|
|||||||
if game.current_question >= 0:
|
if game.current_question >= 0:
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
**game.questions[game.current_question].dict(),
|
**game.questions[game.current_question].model_dump(),
|
||||||
"current_question": game.current_question + 1 if in_human_count else game.current_question,
|
"current_question": game.current_question + 1 if in_human_count else game.current_question,
|
||||||
"total_questions": len(game.questions),
|
"total_questions": len(game.questions),
|
||||||
}
|
}
|
||||||
@@ -263,13 +265,13 @@ async def voting_results(game_pin: str, api_key: str, as_array: bool = False):
|
|||||||
redis_res = await redis.get(f"game:{game_pin}")
|
redis_res = await redis.get(f"game:{game_pin}")
|
||||||
if redis_res is None or user_id is None:
|
if redis_res is None or user_id is None:
|
||||||
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
raise HTTPException(status_code=404, detail="Game not found or API key not found")
|
||||||
game = PlayGame.parse_raw(redis_res)
|
game = PlayGame.model_validate_json(redis_res)
|
||||||
if game.questions[game.current_question].type != QuizQuestionType.VOTING:
|
if game.questions[game.current_question].type != QuizQuestionType.VOTING:
|
||||||
return
|
return
|
||||||
answer_data = await redis.get(f"game_session:{game_pin}:{game.current_question}")
|
answer_data = await redis.get(f"game_session:{game_pin}:{game.current_question}")
|
||||||
if answer_data is None:
|
if answer_data is None:
|
||||||
return
|
return
|
||||||
answer_list = AnswerDataList.parse_raw(answer_data)
|
answer_list = AnswerDataList.model_validate_json(answer_data)
|
||||||
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
|
||||||
|
|||||||
@@ -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,12 +58,12 @@ 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):
|
||||||
try:
|
try:
|
||||||
credential = AuthenticationCredential.parse_obj(data)
|
credential = AuthenticationCredential.model_validate(data)
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
print("ValidationError")
|
print("ValidationError")
|
||||||
raise HTTPException(401)
|
raise HTTPException(401)
|
||||||
@@ -141,7 +141,7 @@ async def start_login(data: StartLoginInput):
|
|||||||
webauthn_challenge=webauthn_challenge,
|
webauthn_challenge=webauthn_challenge,
|
||||||
)
|
)
|
||||||
session_id = os.urandom(16).hex()
|
session_id = os.urandom(16).hex()
|
||||||
await redis.set(f"login_session:{session_id}", login_session.json(), ex=600)
|
await redis.set(f"login_session:{session_id}", login_session.model_dump_json(), ex=600)
|
||||||
return StartLoginResponse(step_1=step_1, step_2=step_2, session_id=session_id, webauthn_data=webauthn_data)
|
return StartLoginResponse(step_1=step_1, step_2=step_2, session_id=session_id, webauthn_data=webauthn_data)
|
||||||
|
|
||||||
|
|
||||||
@@ -157,7 +157,7 @@ async def step_1_endpoint(session_id: str, data: StepInput, request: Request, re
|
|||||||
redis_res = await redis.get(f"login_session:{session_id}")
|
redis_res = await redis.get(f"login_session:{session_id}")
|
||||||
if redis_res is None:
|
if redis_res is None:
|
||||||
raise HTTPException(401, detail="wrong credentials")
|
raise HTTPException(401, detail="wrong credentials")
|
||||||
login_session = LoginSession.parse_raw(redis_res)
|
login_session = LoginSession.model_validate_json(redis_res)
|
||||||
|
|
||||||
if step_id == 1:
|
if step_id == 1:
|
||||||
if data.auth_type not in {*login_session.step_1, StartLoginResponseTypes.BACKUP}:
|
if data.auth_type not in {*login_session.step_1, StartLoginResponseTypes.BACKUP}:
|
||||||
@@ -177,7 +177,7 @@ async def step_1_endpoint(session_id: str, data: StepInput, request: Request, re
|
|||||||
return await log_user_in(user, request, response)
|
return await log_user_in(user, request, response)
|
||||||
else:
|
else:
|
||||||
login_session.step1_success = True
|
login_session.step1_success = True
|
||||||
await redis.set(f"login_session:{session_id}", login_session.json(), ex=600)
|
await redis.set(f"login_session:{session_id}", login_session.model_dump_json(), ex=600)
|
||||||
return Response(status_code=202)
|
return Response(status_code=202)
|
||||||
else:
|
else:
|
||||||
print("Wrong Password")
|
print("Wrong Password")
|
||||||
@@ -189,7 +189,7 @@ async def step_1_endpoint(session_id: str, data: StepInput, request: Request, re
|
|||||||
return await log_user_in(user, request, response)
|
return await log_user_in(user, request, response)
|
||||||
else:
|
else:
|
||||||
login_session.step1_success = True
|
login_session.step1_success = True
|
||||||
await redis.set(f"login_session:{session_id}", login_session.json(), ex=600)
|
await redis.set(f"login_session:{session_id}", login_session.model_dump_json(), ex=600)
|
||||||
return Response(status_code=202)
|
return Response(status_code=202)
|
||||||
else:
|
else:
|
||||||
raise HTTPException(401, detail="webauthn failed")
|
raise HTTPException(401, detail="webauthn failed")
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ async def get_public_quiz(quiz_id: uuid.UUID):
|
|||||||
else:
|
else:
|
||||||
quiz.views += 1
|
quiz.views += 1
|
||||||
await quiz.update()
|
await quiz.update()
|
||||||
return PublicQuizResponse(**quiz.dict())
|
return PublicQuizResponse(**quiz.model_dump())
|
||||||
|
|
||||||
|
|
||||||
@router.post("/start/{quiz_id}")
|
@router.post("/start/{quiz_id}")
|
||||||
@@ -130,21 +130,21 @@ async def start_quiz(
|
|||||||
if cqcs_enabled:
|
if cqcs_enabled:
|
||||||
code = generate_code(6)
|
code = generate_code(6)
|
||||||
await redis.set(f"game:cqc:code:{code}", game_pin, ex=3600)
|
await redis.set(f"game:cqc:code:{code}", game_pin, ex=3600)
|
||||||
await redis.set(f"game:{str(game.game_pin)}", game.json(), ex=18000)
|
await redis.set(f"game:{str(game.game_pin)}", game.model_dump_json(), ex=18000)
|
||||||
await redis.set(f"game_pin:{user.id}:{quiz_id}", game_pin, ex=18000)
|
await redis.set(f"game_pin:{user.id}:{quiz_id}", game_pin, ex=18000)
|
||||||
|
|
||||||
await redis.set(
|
await redis.set(
|
||||||
f"game_in_lobby:{user.id.hex}",
|
f"game_in_lobby:{user.id.hex}",
|
||||||
GameInLobby(game_id=game.game_id, game_pin=str(game_pin), quiz_title=quiz.title).json(),
|
GameInLobby(game_id=game.game_id, game_pin=str(game_pin), quiz_title=quiz.title).model_dump_json(),
|
||||||
ex=900,
|
ex=900,
|
||||||
)
|
)
|
||||||
return {**quiz.dict(exclude={"id"}), **game.dict(exclude={"questions"}), "cqc_code": code}
|
return {**quiz.model_dump(exclude={"id"}), **game.model_dump(exclude={"questions"}), "cqc_code": code}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -152,7 +152,7 @@ async def check_if_captcha_enabled(game_pin: str):
|
|||||||
game = await redis.get(f"game:{game_pin}")
|
game = await redis.get(f"game:{game_pin}")
|
||||||
if game is None:
|
if game is None:
|
||||||
return JSONResponse(status_code=404, content={"detail": "game not found"})
|
return JSONResponse(status_code=404, content={"detail": "game not found"})
|
||||||
game = PlayGame.parse_raw(game)
|
game = PlayGame.model_validate_json(game)
|
||||||
if game.captcha_enabled:
|
if game.captcha_enabled:
|
||||||
return CheckIfCaptchaEnabledResponse(enabled=True, game_mode=game.game_mode, custom_field=game.custom_field)
|
return CheckIfCaptchaEnabledResponse(enabled=True, game_mode=game.game_mode, custom_field=game.custom_field)
|
||||||
else:
|
else:
|
||||||
@@ -228,7 +228,7 @@ async def export_quiz_answers(export_token: str, game_pin: str):
|
|||||||
raise HTTPException(status_code=404, detail="export token not found")
|
raise HTTPException(status_code=404, detail="export token not found")
|
||||||
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.model_validate_json(data2)
|
||||||
quiz = await Quiz.objects.get_or_none(id=game_data.quiz_id)
|
quiz = await Quiz.objects.get_or_none(id=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")
|
||||||
@@ -255,4 +255,4 @@ async def export_quiz_answers(export_token: str, game_pin: str):
|
|||||||
@router.post("/excel-import")
|
@router.post("/excel-import")
|
||||||
async def import_from_excel(file: UploadFile = File(), user: User = Depends(get_current_user)) -> Quiz:
|
async def import_from_excel(file: UploadFile = File(), user: User = Depends(get_current_user)) -> Quiz:
|
||||||
quiz = await handle_import_from_excel(file.file, user)
|
quiz = await handle_import_from_excel(file.file, user)
|
||||||
return Quiz.parse_obj(quiz.dict(exclude={"user_id": ...}))
|
return Quiz.model_validate(quiz.model_dump(exclude={"user_id": ...}))
|
||||||
|
|||||||
@@ -19,7 +19,9 @@ router.include_router(shares_router, prefix="/shares")
|
|||||||
|
|
||||||
@router.post("/create", response_model_exclude={"user": ...})
|
@router.post("/create", response_model_exclude={"user": ...})
|
||||||
async def create_quiztivity(data: QuizTivityInput, user: User = Depends(get_current_user)) -> QuizTivity:
|
async def create_quiztivity(data: QuizTivityInput, user: User = Depends(get_current_user)) -> QuizTivity:
|
||||||
quiztivity = QuizTivity.parse_obj({**data.dict(), "user": user, "id": uuid4(), "created_at": datetime.now()})
|
quiztivity = QuizTivity.model_validate(
|
||||||
|
{**data.model_dump(), "user": user, "id": uuid4(), "created_at": datetime.now()}
|
||||||
|
)
|
||||||
return await quiztivity.save()
|
return await quiztivity.save()
|
||||||
|
|
||||||
|
|
||||||
@@ -36,7 +38,7 @@ async def put_quiztivity(data: QuizTivityInput, uuid: UUID, user: User = Depends
|
|||||||
quiztivity = await QuizTivity.objects.get_or_none(id=uuid, user=user)
|
quiztivity = await QuizTivity.objects.get_or_none(id=uuid, user=user)
|
||||||
if quiztivity is None:
|
if quiztivity is None:
|
||||||
raise HTTPException(status_code=404, detail="QuizTivity not found")
|
raise HTTPException(status_code=404, detail="QuizTivity not found")
|
||||||
quiztivity.pages = data.dict()["pages"]
|
quiztivity.pages = data.model_dump()["pages"]
|
||||||
quiztivity.title = data.title
|
quiztivity.title = data.title
|
||||||
return await quiztivity.update()
|
return await quiztivity.update()
|
||||||
|
|
||||||
|
|||||||
@@ -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}")
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ async def get_game_in_lobby(user: User = Depends(get_current_user)):
|
|||||||
game_in_lobby_raw = await redis.get(f"game_in_lobby:{user.id.hex}")
|
game_in_lobby_raw = await redis.get(f"game_in_lobby:{user.id.hex}")
|
||||||
if game_in_lobby_raw is None:
|
if game_in_lobby_raw is None:
|
||||||
raise HTTPException(status_code=404, detail="No game waiting")
|
raise HTTPException(status_code=404, detail="No game waiting")
|
||||||
game_in_lobby = GameInLobby.parse_raw(game_in_lobby_raw)
|
game_in_lobby = GameInLobby.model_validate_json(game_in_lobby_raw)
|
||||||
return game_in_lobby
|
return game_in_lobby
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class SitemapQuiz(BaseModel):
|
|||||||
updated_at: datetime.datetime
|
updated_at: datetime.datetime
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
@router.get("/get")
|
@router.get("/get")
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ router.include_router(oauth.router, tags=["users", "oauth"], prefix="/oauth")
|
|||||||
async def create_user(user: RouteUser, background_task: BackgroundTasks) -> User | JSONResponse:
|
async def create_user(user: RouteUser, background_task: BackgroundTasks) -> User | JSONResponse:
|
||||||
if settings.registration_disabled:
|
if settings.registration_disabled:
|
||||||
raise HTTPException(status_code=423)
|
raise HTTPException(status_code=423)
|
||||||
user = User(**user.dict(), id=uuid.uuid4(), avatar=gzipped_user_avatar(), created_at=datetime.now())
|
user = User(**user.model_dump(), id=uuid.uuid4(), avatar=gzipped_user_avatar(), created_at=datetime.now())
|
||||||
try:
|
try:
|
||||||
validate_email(user.email)
|
validate_email(user.email)
|
||||||
except EmailNotValidError as e:
|
except EmailNotValidError as e:
|
||||||
@@ -232,7 +232,7 @@ async def reset_password_with_token(reset_password: ResetPassword, response: Res
|
|||||||
@router.get("/sessions/list", response_model=list[UserSession], response_model_exclude={"user", "session_key", "quizs"})
|
@router.get("/sessions/list", response_model=list[UserSession], response_model_exclude={"user", "session_key", "quizs"})
|
||||||
async def list_sessions(user: User = Depends(get_current_user)):
|
async def list_sessions(user: User = Depends(get_current_user)):
|
||||||
sessions = await UserSession.objects.filter(user=user).all()
|
sessions = await UserSession.objects.filter(user=user).all()
|
||||||
return [session.dict() for session in sessions]
|
return [session.model_dump() for session in sessions]
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/sessions/{session_id}")
|
@router.delete("/sessions/{session_id}")
|
||||||
@@ -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")
|
||||||
@@ -341,7 +341,7 @@ async def get_email_from_jwt(data: GetEmailFromJWT):
|
|||||||
async def generate_api_key(user: User = Depends(get_current_user)):
|
async def generate_api_key(user: User = Depends(get_current_user)):
|
||||||
key = ApiKey(key=os.urandom(24).hex(), user=user)
|
key = ApiKey(key=os.urandom(24).hex(), user=user)
|
||||||
await key.save()
|
await key.save()
|
||||||
return key.dict(include={"key"})
|
return key.model_dump(include={"key"})
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api_keys", response_model=list[ApiKey], response_model_include={"key"})
|
@router.get("/api_keys", response_model=list[ApiKey], response_model_include={"key"})
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class IpResponse(BaseModel):
|
|||||||
@router.get("/ip-lookup/{ip}", response_model=IpResponse)
|
@router.get("/ip-lookup/{ip}", response_model=IpResponse)
|
||||||
async def get_ip_data(ip: str, _: User = Depends(get_current_user)):
|
async def get_ip_data(ip: str, _: User = Depends(get_current_user)):
|
||||||
async with ClientSession() as session, session.get(f"http://ip-api.com/json/{ip}") as response:
|
async with ClientSession() as session, session.get(f"http://ip-api.com/json/{ip}") as response:
|
||||||
data = await response.json()
|
data = await response.model_dump_json()
|
||||||
try:
|
try:
|
||||||
return IpResponse(**data)
|
return IpResponse(**data)
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ from classquiz.db.models import (
|
|||||||
AnswerDataList,
|
AnswerDataList,
|
||||||
AnswerData,
|
AnswerData,
|
||||||
)
|
)
|
||||||
from pydantic import BaseModel, ValidationError, validator
|
from pydantic import BaseModel, ValidationError, field_validator
|
||||||
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
|
||||||
@@ -63,11 +63,11 @@ async def set_answer(answers, game_pin: str, q_index: int, data: AnswerData) ->
|
|||||||
if answers is None:
|
if answers is None:
|
||||||
answers = AnswerDataList(__root__=[data])
|
answers = AnswerDataList(__root__=[data])
|
||||||
else:
|
else:
|
||||||
answers = AnswerDataList.parse_raw(answers)
|
answers = AnswerDataList.model_validate_json(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.model_dump_json(),
|
||||||
ex=7200,
|
ex=7200,
|
||||||
)
|
)
|
||||||
return answers
|
return answers
|
||||||
@@ -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):
|
||||||
@@ -105,10 +105,12 @@ async def rejoin_game(sid: str, data: dict):
|
|||||||
await sio.emit("time_sync", encrypted_datetime, room=sid)
|
await sio.emit("time_sync", encrypted_datetime, room=sid)
|
||||||
await redis.set(redis_sid_key, sid)
|
await redis.set(redis_sid_key, sid)
|
||||||
await redis.srem(
|
await redis.srem(
|
||||||
f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=data.old_sid).json()
|
f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=data.old_sid).model_dump_json()
|
||||||
)
|
)
|
||||||
await redis.sadd(f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).json())
|
await redis.sadd(
|
||||||
game_data = PlayGame.parse_raw(redis_res)
|
f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).model_dump_json()
|
||||||
|
)
|
||||||
|
game_data = PlayGame.model_validate_json(redis_res)
|
||||||
session = {
|
session = {
|
||||||
"game_pin": data.game_pin,
|
"game_pin": data.game_pin,
|
||||||
"username": data.username,
|
"username": data.username,
|
||||||
@@ -120,7 +122,7 @@ async def rejoin_game(sid: str, data: dict):
|
|||||||
await sio.emit(
|
await sio.emit(
|
||||||
"rejoined_game",
|
"rejoined_game",
|
||||||
{
|
{
|
||||||
**json.loads(game_data.json(exclude={"quiz_id", "questions", "user_id"})),
|
**json.loads(game_data.model_dump_json(exclude={"quiz_id", "questions", "user_id"})),
|
||||||
"question_count": len(game_data.questions),
|
"question_count": len(game_data.questions),
|
||||||
},
|
},
|
||||||
room=sid,
|
room=sid,
|
||||||
@@ -139,7 +141,7 @@ async def join_game(sid: str, data: dict):
|
|||||||
await sio.emit("error", room=sid)
|
await sio.emit("error", room=sid)
|
||||||
print(e)
|
print(e)
|
||||||
return
|
return
|
||||||
game_data = PlayGame.parse_raw(redis_res)
|
game_data = PlayGame.model_validate_json(redis_res)
|
||||||
if game_data.started:
|
if game_data.started:
|
||||||
await sio.emit("game_already_started", room=sid)
|
await sio.emit("game_already_started", room=sid)
|
||||||
return
|
return
|
||||||
@@ -153,7 +155,7 @@ async def join_game(sid: str, data: dict):
|
|||||||
"https://hcaptcha.com/siteverify",
|
"https://hcaptcha.com/siteverify",
|
||||||
data={"response": data.captcha, "secret": settings.hcaptcha_key},
|
data={"response": data.captcha, "secret": settings.hcaptcha_key},
|
||||||
) as resp:
|
) as resp:
|
||||||
resp_data = await resp.json()
|
resp_data = await resp.model_dump_json()
|
||||||
if not resp_data["success"]:
|
if not resp_data["success"]:
|
||||||
print("CAPTCHA FAILED")
|
print("CAPTCHA FAILED")
|
||||||
return
|
return
|
||||||
@@ -166,7 +168,7 @@ async def join_game(sid: str, data: dict):
|
|||||||
data={"secret": settings.recaptcha_key, "response": data.captcha},
|
data={"secret": settings.recaptcha_key, "response": data.captcha},
|
||||||
) as resp:
|
) as resp:
|
||||||
try:
|
try:
|
||||||
resp_data = await resp.json()
|
resp_data = await resp.model_dump_json()
|
||||||
if not resp_data["success"]:
|
if not resp_data["success"]:
|
||||||
print("CAPTCHA FAILED")
|
print("CAPTCHA FAILED")
|
||||||
return
|
return
|
||||||
@@ -190,25 +192,22 @@ async def join_game(sid: str, data: dict):
|
|||||||
await sio.emit(
|
await sio.emit(
|
||||||
"joined_game",
|
"joined_game",
|
||||||
{
|
{
|
||||||
**json.loads(game_data.json(exclude={"quiz_id", "questions", "user_id"})),
|
**json.loads(game_data.model_dump_json(exclude={"quiz_id", "questions", "user_id"})),
|
||||||
"question_count": len(game_data.questions),
|
"question_count": len(game_data.questions),
|
||||||
},
|
},
|
||||||
room=sid,
|
room=sid,
|
||||||
)
|
)
|
||||||
redis_res = await redis.get(f"game_session:{data.game_pin}")
|
redis_res = await redis.get(f"game_session:{data.game_pin}")
|
||||||
redis_res = GameSession.parse_raw(redis_res)
|
redis_res = GameSession.model_validate_json(redis_res)
|
||||||
await redis.set(f"game_session:{data.game_pin}:players:{data.username}", sid, ex=7200)
|
await redis.set(f"game_session:{data.game_pin}:players:{data.username}", sid, ex=7200)
|
||||||
await redis.sadd(f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).json())
|
await redis.sadd(
|
||||||
|
f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).model_dump_json()
|
||||||
|
)
|
||||||
if data.custom_field == "":
|
if data.custom_field == "":
|
||||||
data.custom_field = None
|
data.custom_field = None
|
||||||
if data.custom_field is not None:
|
if data.custom_field is not None:
|
||||||
await redis.hset(f"game:{data.game_pin}:players:custom_fields", data.username, data.custom_field)
|
await redis.hset(f"game:{data.game_pin}:players:custom_fields", data.username, data.custom_field)
|
||||||
|
|
||||||
# await redis.set(
|
|
||||||
# f"game_session:{data.game_pin}",
|
|
||||||
# GameSession(admin=redis_res.admin, game_id=redis_res.game_id, answers=[]).json(),
|
|
||||||
# ex=18000,
|
|
||||||
# )
|
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
"player_joined",
|
"player_joined",
|
||||||
{"username": data.username, "sid": sid},
|
{"username": data.username, "sid": sid},
|
||||||
@@ -226,9 +225,9 @@ async def start_game(sid: str, _data: dict):
|
|||||||
session = await sio.get_session(sid)
|
session = await sio.get_session(sid)
|
||||||
if not session["admin"]:
|
if not session["admin"]:
|
||||||
return
|
return
|
||||||
game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}"))
|
game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}"))
|
||||||
game_data.started = True
|
game_data.started = True
|
||||||
await redis.set(f"game:{session['game_pin']}", game_data.json(), ex=7200)
|
await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json(), ex=7200)
|
||||||
await redis.delete(f"game_in_lobby:{game_data.user_id.hex}")
|
await redis.delete(f"game_in_lobby:{game_data.user_id.hex}")
|
||||||
await sio.emit("start_game", room=session["game_pin"])
|
await sio.emit("start_game", room=session["game_pin"])
|
||||||
|
|
||||||
@@ -251,7 +250,7 @@ async def register_as_admin(sid: str, data: dict):
|
|||||||
if (await redis.get(f"game_session:{game_pin}")) is None:
|
if (await redis.get(f"game_session:{game_pin}")) is None:
|
||||||
await redis.set(
|
await redis.set(
|
||||||
f"game_session:{game_pin}",
|
f"game_session:{game_pin}",
|
||||||
GameSession(admin=sid, game_id=game_id, answers=[]).json(),
|
GameSession(admin=sid, game_id=game_id, answers=[]).model_dump_json(),
|
||||||
ex=7200,
|
ex=7200,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -280,10 +279,10 @@ 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.model_validate_json(redis_res).model_dump()["__root__"]
|
||||||
game_data = PlayGame.parse_raw(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.json())
|
await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json())
|
||||||
game_pin = session["game_pin"]
|
game_pin = session["game_pin"]
|
||||||
|
|
||||||
await sio.emit("question_results", redis_res, room=game_pin)
|
await sio.emit("question_results", redis_res, room=game_pin)
|
||||||
@@ -291,7 +290,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):
|
||||||
@@ -303,7 +302,7 @@ class ReturnQuestion(QuizQuestion):
|
|||||||
answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution | list[VotingQuizAnswer]
|
answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution | list[VotingQuizAnswer]
|
||||||
type: QuizQuestionType = QuizQuestionType.ABCD
|
type: QuizQuestionType = QuizQuestionType.ABCD
|
||||||
|
|
||||||
@validator("answers")
|
@field_validator("answers")
|
||||||
def answers_not_none_if_abcd_type(cls, v, values):
|
def answers_not_none_if_abcd_type(cls, v, values):
|
||||||
if values["type"] == QuizQuestionType.ABCD and type(v[0]) is not ABCDQuizAnswerWithoutSolution:
|
if values["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")
|
||||||
@@ -322,12 +321,12 @@ async def set_question_number(sid, data: str):
|
|||||||
if not session["admin"]:
|
if not session["admin"]:
|
||||||
return
|
return
|
||||||
game_pin = session["game_pin"]
|
game_pin = session["game_pin"]
|
||||||
game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}"))
|
game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}"))
|
||||||
game_data.current_question = int(float(data))
|
game_data.current_question = int(float(data))
|
||||||
game_data.question_show = True
|
game_data.question_show = True
|
||||||
await redis.set(f"game:{session['game_pin']}", game_data.json(), ex=7200)
|
await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json(), ex=7200)
|
||||||
await redis.set(f"game:{session['game_pin']}:current_time", datetime.now().isoformat(), ex=7200)
|
await redis.set(f"game:{session['game_pin']}:current_time", datetime.now().isoformat(), ex=7200)
|
||||||
temp_return = game_data.dict(include={"questions"})["questions"][int(float(data))]
|
temp_return = game_data.model_dump(include={"questions"})["questions"][int(float(data))]
|
||||||
if game_data.questions[int(float(data))].type == QuizQuestionType.SLIDE:
|
if game_data.questions[int(float(data))].type == QuizQuestionType.SLIDE:
|
||||||
await sio.emit(
|
await sio.emit(
|
||||||
"set_question_number",
|
"set_question_number",
|
||||||
@@ -347,7 +346,7 @@ async def set_question_number(sid, data: str):
|
|||||||
"set_question_number",
|
"set_question_number",
|
||||||
{
|
{
|
||||||
"question_index": int(float(data)),
|
"question_index": int(float(data)),
|
||||||
"question": ReturnQuestion(**temp_return).dict(),
|
"question": ReturnQuestion(**temp_return).model_dump(),
|
||||||
},
|
},
|
||||||
room=game_pin,
|
room=game_pin,
|
||||||
)
|
)
|
||||||
@@ -360,7 +359,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
|
||||||
@@ -373,7 +372,7 @@ async def submit_answer(sid: str, data: dict):
|
|||||||
print(e)
|
print(e)
|
||||||
return
|
return
|
||||||
session = await sio.get_session(sid)
|
session = await sio.get_session(sid)
|
||||||
game_data = PlayGame.parse_raw(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
|
||||||
if game_data.questions[int(float(data.question_index))].type == QuizQuestionType.ABCD:
|
if game_data.questions[int(float(data.question_index))].type == QuizQuestionType.ABCD:
|
||||||
for answer in game_data.questions[int(float(data.question_index))].answers:
|
for answer in game_data.questions[int(float(data.question_index))].answers:
|
||||||
@@ -398,10 +397,10 @@ async def submit_answer(sid: str, data: dict):
|
|||||||
for a in question.answers:
|
for a in question.answers:
|
||||||
correct_answers.append({"answer": a.answer})
|
correct_answers.append({"answer": a.answer})
|
||||||
answer_order = []
|
answer_order = []
|
||||||
for a in data.dict()["complex_answer"]:
|
for a in data.model_dump()["complex_answer"]:
|
||||||
answer_order.append(a["answer"])
|
answer_order.append(a["answer"])
|
||||||
data.answer = ", ".join(answer_order)
|
data.answer = ", ".join(answer_order)
|
||||||
if correct_answers == data.dict()["complex_answer"]:
|
if correct_answers == data.model_dump()["complex_answer"]:
|
||||||
answer_right = True
|
answer_right = True
|
||||||
|
|
||||||
elif game_data.questions[int(float(data.question_index))].type == QuizQuestionType.TEXT:
|
elif game_data.questions[int(float(data.question_index))].type == QuizQuestionType.TEXT:
|
||||||
@@ -454,9 +453,9 @@ async def submit_answer(sid: str, data: dict):
|
|||||||
# await redis.get(f"game_session:{session['game_pin']}:{data.question_index}"),
|
# await redis.get(f"game_session:{session['game_pin']}:{data.question_index}"),
|
||||||
# room=session["game_pin"],
|
# room=session["game_pin"],
|
||||||
# )
|
# )
|
||||||
game_data = PlayGame.parse_raw(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.json())
|
await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json())
|
||||||
await sio.emit("everyone_answered", {})
|
await sio.emit("everyone_answered", {})
|
||||||
|
|
||||||
|
|
||||||
@@ -491,7 +490,7 @@ async def show_solutions(sid: str, _data: dict):
|
|||||||
game_data = PlayGame(**json.loads(await redis.get(f"game:{session['game_pin']}")))
|
game_data = PlayGame(**json.loads(await redis.get(f"game:{session['game_pin']}")))
|
||||||
if not session["admin"]:
|
if not session["admin"]:
|
||||||
return
|
return
|
||||||
await sio.emit("solutions", game_data.questions[game_data.current_question].dict(), room=session["game_pin"])
|
await sio.emit("solutions", game_data.questions[game_data.current_question].model_dump(), room=session["game_pin"])
|
||||||
|
|
||||||
|
|
||||||
@sio.event
|
@sio.event
|
||||||
@@ -523,7 +522,8 @@ async def kick_player(sid: str, data: dict):
|
|||||||
|
|
||||||
player_sid = await redis.get(f"game_session:{session['game_pin']}:players:{data.username}")
|
player_sid = await redis.get(f"game_session:{session['game_pin']}:players:{data.username}")
|
||||||
await redis.srem(
|
await redis.srem(
|
||||||
f"game_session:{session['game_pin']}:players", GamePlayer(username=data.username, sid=player_sid).json()
|
f"game_session:{session['game_pin']}:players",
|
||||||
|
GamePlayer(username=data.username, sid=player_sid).model_dump_json(),
|
||||||
)
|
)
|
||||||
await sio.leave_room(player_sid, session["game_pin"])
|
await sio.leave_room(player_sid, session["game_pin"])
|
||||||
await sio.emit("kick", room=player_sid)
|
await sio.emit("kick", room=player_sid)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from classquiz.db.models import PlayGame, GameResults
|
|||||||
|
|
||||||
|
|
||||||
async def save_quiz_to_storage(game_pin: str):
|
async def save_quiz_to_storage(game_pin: str):
|
||||||
game = PlayGame.parse_raw(await redis.get(f"game:{game_pin}"))
|
game = PlayGame.model_validate_json(await redis.get(f"game:{game_pin}"))
|
||||||
player_count = await redis.scard(f"game_session:{game_pin}:players")
|
player_count = await redis.scard(f"game_session:{game_pin}:players")
|
||||||
answers = []
|
answers = []
|
||||||
for i in range(len(game.questions)):
|
for i in range(len(game.questions)):
|
||||||
@@ -26,7 +26,7 @@ async def save_quiz_to_storage(game_pin: str):
|
|||||||
custom_field_data = await redis.hgetall(f"game:{game_pin}:players:custom_fields")
|
custom_field_data = await redis.hgetall(f"game:{game_pin}:players:custom_fields")
|
||||||
q_return = []
|
q_return = []
|
||||||
for q in game.questions:
|
for q in game.questions:
|
||||||
q_return.append(q.dict())
|
q_return.append(q.model_dump())
|
||||||
data = GameResults(
|
data = GameResults(
|
||||||
id=game.game_id,
|
id=game.game_id,
|
||||||
quiz=game.quiz_id,
|
quiz=game.quiz_id,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ services:
|
|||||||
# context: pg_uuidv7
|
# context: pg_uuidv7
|
||||||
# args:
|
# args:
|
||||||
# PG_MAJOR_VERSION: 16
|
# PG_MAJOR_VERSION: 16
|
||||||
image: postgres
|
image: postgres:16
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_PASSWORD: mysecretpassword
|
POSTGRES_PASSWORD: mysecretpassword
|
||||||
POSTGRES_DB: classquiz
|
POSTGRES_DB: classquiz
|
||||||
|
|||||||
Reference in New Issue
Block a user