🎨 black
This commit is contained in:
@@ -28,6 +28,7 @@ coverage = "*"
|
||||
pytest = "*"
|
||||
pytest-asyncio = "*"
|
||||
flake8 = "*"
|
||||
black = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.10"
|
||||
|
||||
Generated
+64
-5
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "5764345626b30779222e0a592f2ab91ce0938823d1baa12a4168ceed03a95170"
|
||||
"sha256": "075e07880b28878645b61e807c6091ae0ecdef50b3486e047aa853023fc2ae33"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -331,7 +331,7 @@
|
||||
"sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e",
|
||||
"sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"
|
||||
],
|
||||
"markers": "python_version >= '3.6' and python_full_version < '4.0.0'",
|
||||
"markers": "python_version >= '3.6' and python_version < '4'",
|
||||
"version": "==2.2.1"
|
||||
},
|
||||
"ecdsa": {
|
||||
@@ -933,7 +933,7 @@
|
||||
"sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17",
|
||||
"sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"
|
||||
],
|
||||
"markers": "python_version >= '3.6' and python_full_version < '4.0.0'",
|
||||
"markers": "python_version >= '3.6' and python_version < '4'",
|
||||
"version": "==4.8"
|
||||
},
|
||||
"sentry-sdk": {
|
||||
@@ -1031,7 +1031,7 @@
|
||||
"sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14",
|
||||
"sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_full_version < '4.0.0'",
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
|
||||
"version": "==1.26.9"
|
||||
},
|
||||
"uvicorn": {
|
||||
@@ -1284,6 +1284,43 @@
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.4.0"
|
||||
},
|
||||
"black": {
|
||||
"hashes": [
|
||||
"sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b",
|
||||
"sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176",
|
||||
"sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09",
|
||||
"sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a",
|
||||
"sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015",
|
||||
"sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79",
|
||||
"sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb",
|
||||
"sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20",
|
||||
"sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464",
|
||||
"sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968",
|
||||
"sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82",
|
||||
"sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21",
|
||||
"sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0",
|
||||
"sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265",
|
||||
"sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b",
|
||||
"sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a",
|
||||
"sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72",
|
||||
"sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce",
|
||||
"sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0",
|
||||
"sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a",
|
||||
"sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163",
|
||||
"sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad",
|
||||
"sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==22.3.0"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e",
|
||||
"sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==8.1.2"
|
||||
},
|
||||
"coverage": {
|
||||
"hashes": [
|
||||
"sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9",
|
||||
@@ -1353,6 +1390,13 @@
|
||||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"mypy-extensions": {
|
||||
"hashes": [
|
||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
|
||||
"sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
|
||||
],
|
||||
"version": "==0.4.3"
|
||||
},
|
||||
"packaging": {
|
||||
"hashes": [
|
||||
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb",
|
||||
@@ -1361,6 +1405,21 @@
|
||||
"markers": "python_version >= '3.6'",
|
||||
"version": "==21.3"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
"sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a",
|
||||
"sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"
|
||||
],
|
||||
"version": "==0.9.0"
|
||||
},
|
||||
"platformdirs": {
|
||||
"hashes": [
|
||||
"sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d",
|
||||
"sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==2.5.1"
|
||||
},
|
||||
"pluggy": {
|
||||
"hashes": [
|
||||
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
|
||||
@@ -1423,7 +1482,7 @@
|
||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
|
||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"markers": "python_version < '3.11'",
|
||||
"version": "==2.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -28,11 +28,11 @@ ACCESS_TOKEN_EXPIRE_MINUTES = settings.access_token_expire_minutes
|
||||
|
||||
class OAuth2PasswordBearerWithCookie(OAuth2):
|
||||
def __init__(
|
||||
self,
|
||||
tokenUrl: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
scopes: Optional[Dict[str, str]] = None,
|
||||
auto_error: bool = True,
|
||||
self,
|
||||
tokenUrl: str,
|
||||
scheme_name: Optional[str] = None,
|
||||
scopes: Optional[Dict[str, str]] = None,
|
||||
auto_error: bool = True,
|
||||
):
|
||||
if not scopes:
|
||||
scopes = {}
|
||||
|
||||
+8
-3
@@ -10,6 +10,7 @@ class Settings(BaseSettings):
|
||||
"""
|
||||
Settings class for the shop app.
|
||||
"""
|
||||
|
||||
root_address: str = "http://127.0.0.1:8000"
|
||||
redis: RedisDsn = "redis://localhost:6379/0?decode_responses=True"
|
||||
skip_email_verification: bool = False
|
||||
@@ -36,7 +37,7 @@ class Settings(BaseSettings):
|
||||
|
||||
class Config:
|
||||
env_file = ".env"
|
||||
env_file_encoding = 'utf-8'
|
||||
env_file_encoding = "utf-8"
|
||||
|
||||
|
||||
@lru_cache()
|
||||
@@ -45,5 +46,9 @@ def settings() -> Settings:
|
||||
|
||||
|
||||
redis: redis_lib.client.Redis = redis_lib.Redis().from_url(settings().redis)
|
||||
storage: Storage = Storage(backend=settings().storage_backend, deta_key=settings().deta_project_key,
|
||||
deta_id=settings().deta_project_id, storage_path=settings().storage_path)
|
||||
storage: Storage = Storage(
|
||||
backend=settings().storage_backend,
|
||||
deta_key=settings().deta_project_key,
|
||||
deta_id=settings().deta_project_id,
|
||||
storage_path=settings().storage_path,
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ class User(ormar.Model):
|
||||
"""
|
||||
The user model in the database
|
||||
"""
|
||||
|
||||
id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4())
|
||||
email: str = ormar.String(unique=True, max_length=100)
|
||||
username: str = ormar.String(unique=True, max_length=100)
|
||||
@@ -20,7 +21,7 @@ class User(ormar.Model):
|
||||
created_at: datetime = ormar.DateTime(default=datetime.now())
|
||||
|
||||
class Meta:
|
||||
tablename = 'users'
|
||||
tablename = "users"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
@@ -29,6 +30,7 @@ class UserSession(ormar.Model):
|
||||
"""
|
||||
The user session model for user-sessions
|
||||
"""
|
||||
|
||||
id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4())
|
||||
user: uuid.UUID = ormar.ForeignKey(User)
|
||||
session_key: str = ormar.String(unique=True, max_length=64)
|
||||
@@ -38,7 +40,7 @@ class UserSession(ormar.Model):
|
||||
last_seen: datetime = ormar.DateTime(default=datetime.now())
|
||||
|
||||
class Meta:
|
||||
tablename = 'user_sessions'
|
||||
tablename = "user_sessions"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
@@ -73,7 +75,7 @@ class Quiz(ormar.Model):
|
||||
questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False)
|
||||
|
||||
class Meta:
|
||||
tablename = 'quiz'
|
||||
tablename = "quiz"
|
||||
metadata = metadata
|
||||
database = database
|
||||
|
||||
@@ -82,6 +84,7 @@ class Token(BaseModel):
|
||||
"""
|
||||
For JWT
|
||||
"""
|
||||
|
||||
access_token: str
|
||||
token_type: str
|
||||
|
||||
@@ -90,6 +93,7 @@ class TokenData(BaseModel):
|
||||
"""
|
||||
For JWT
|
||||
"""
|
||||
|
||||
email: str | None = None
|
||||
|
||||
|
||||
@@ -97,6 +101,7 @@ class PlayGame(BaseModel):
|
||||
"""
|
||||
For JWT
|
||||
"""
|
||||
|
||||
quiz_id: uuid.UUID | str
|
||||
description: str
|
||||
title: str
|
||||
|
||||
@@ -12,17 +12,18 @@ from classquiz.db.models import User
|
||||
settings = settings()
|
||||
|
||||
jinja = Environment(
|
||||
loader=PackageLoader('classquiz.emails', 'templates'),
|
||||
autoescape=select_autoescape(['html', 'xml']),
|
||||
enable_async=True)
|
||||
loader=PackageLoader("classquiz.emails", "templates"),
|
||||
autoescape=select_autoescape(["html", "xml"]),
|
||||
enable_async=True,
|
||||
)
|
||||
|
||||
|
||||
def _sendMail(template: str, to: str, subject: str):
|
||||
msg = MIMEMultipart('alternative')
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = settings.mail_address
|
||||
msg['To'] = to
|
||||
msg.attach(MIMEText(template, 'html'))
|
||||
msg = MIMEMultipart("alternative")
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = settings.mail_address
|
||||
msg["To"] = to
|
||||
msg.attach(MIMEText(template, "html"))
|
||||
context = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
server = smtplib.SMTP(host=settings.mail_server, port=settings.mail_port)
|
||||
server.ehlo()
|
||||
@@ -35,25 +36,19 @@ def _sendMail(template: str, to: str, subject: str):
|
||||
async def send_register_email(email: str):
|
||||
user = await User.objects.get_or_none(email=email, verified=False)
|
||||
if user is None:
|
||||
raise ValueError('User not found')
|
||||
template = jinja.get_template('register.jinja2')
|
||||
template = await template.render_async(
|
||||
base_url=settings.root_address,
|
||||
token=user.verify_key
|
||||
)
|
||||
_sendMail(template=template, to=email, subject='Verify your email')
|
||||
raise ValueError("User not found")
|
||||
template = jinja.get_template("register.jinja2")
|
||||
template = await template.render_async(base_url=settings.root_address, token=user.verify_key)
|
||||
_sendMail(template=template, to=email, subject="Verify your email")
|
||||
|
||||
|
||||
async def send_forgotten_password_email(email: str):
|
||||
user = await User.objects.get_or_none(email=email)
|
||||
if user is None:
|
||||
raise ValueError('User not found')
|
||||
template = jinja.get_template('forgotten_password.jinja2')
|
||||
raise ValueError("User not found")
|
||||
template = jinja.get_template("forgotten_password.jinja2")
|
||||
token = os.urandom(32).hex()
|
||||
template = await template.render_async(
|
||||
base_url=settings.root_address,
|
||||
token=token
|
||||
)
|
||||
template = await template.render_async(base_url=settings.root_address, token=token)
|
||||
await redis.set(f"reset_passwd:{token}", str(user.id), ex=3600)
|
||||
_sendMail(template=template, to=email, subject='Reset your password')
|
||||
_sendMail(template=template, to=email, subject="Reset your password")
|
||||
pass
|
||||
|
||||
@@ -53,6 +53,7 @@ async def _download_image(url: str) -> bytes:
|
||||
# data = await resp.json()
|
||||
# return data["data"]["id"]
|
||||
|
||||
|
||||
async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
|
||||
"""
|
||||
Imports a quiz from Kahoot.
|
||||
@@ -78,8 +79,21 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | str:
|
||||
for a in q.choices:
|
||||
answers.append((QuizAnswer(right=a.correct, answer=html.unescape(a.answer))))
|
||||
quiz_questions.append(
|
||||
QuizQuestion(question=q.question, answers=answers, time=str(q.time / 1000), image=image).dict())
|
||||
quiz_data = Quiz(id=quiz_id, public=False, title=quiz.kahoot.title, description=quiz.kahoot.description,
|
||||
created_at=datetime.now(), updated_at=datetime.now(), user_id=user.id,
|
||||
questions=json.dumps(quiz_questions))
|
||||
QuizQuestion(
|
||||
question=q.question,
|
||||
answers=answers,
|
||||
time=str(q.time / 1000),
|
||||
image=image,
|
||||
).dict()
|
||||
)
|
||||
quiz_data = Quiz(
|
||||
id=quiz_id,
|
||||
public=False,
|
||||
title=quiz.kahoot.title,
|
||||
description=quiz.kahoot.description,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now(),
|
||||
user_id=user.id,
|
||||
questions=json.dumps(quiz_questions),
|
||||
)
|
||||
return await quiz_data.save()
|
||||
|
||||
@@ -14,8 +14,13 @@ class _Response(BaseModel):
|
||||
pageTimestamp: int
|
||||
|
||||
|
||||
async def search(query: str | None, limit: int | None = 9, cursor: int | None = 1,
|
||||
search_cluster: int | None = 1, inventory_item_id: str | None = "ANY") -> _Response:
|
||||
async def search(
|
||||
query: str | None,
|
||||
limit: int | None = 9,
|
||||
cursor: int | None = 1,
|
||||
search_cluster: int | None = 1,
|
||||
inventory_item_id: str | None = "ANY",
|
||||
) -> _Response:
|
||||
"""
|
||||
|
||||
:param inventory_item_id: I dkon't know
|
||||
@@ -27,8 +32,7 @@ async def search(query: str | None, limit: int | None = 9, cursor: int | None =
|
||||
"""
|
||||
async with ClientSession() as session:
|
||||
async with session.get(
|
||||
f"https://create.kahoot.it/rest/kahoots/?query={query}&limit={limit}&cursor={cursor}&searchCluster={search_cluster}&includeExtendedCounters=false&inventoryItemId={inventory_item_id}" # noqa : E501
|
||||
|
||||
f"https://create.kahoot.it/rest/kahoots/?query={query}&limit={limit}&cursor={cursor}&searchCluster={search_cluster}&includeExtendedCounters=false&inventoryItemId={inventory_item_id}" # noqa : E501
|
||||
) as response:
|
||||
# print(
|
||||
# f"https://create.kahoot.it/rest/kahoots/?query={query}&limit={limit}&cursor={cursor}&searchCluster={search_cluster}&includeExtendedCounters=false&inventoryItemId={inventory_item_id}")
|
||||
|
||||
@@ -62,8 +62,14 @@ async def start_quiz(quiz_id: str, user: User = Depends(get_current_user)):
|
||||
return JSONResponse(status_code=404, content={"detail": "quiz not found"})
|
||||
else:
|
||||
game_pin = randint(10000000, 99999999)
|
||||
game = PlayGame(quiz_id=quiz_id, game_pin=str(game_pin), questions=quiz.questions, game_id=uuid.uuid4(),
|
||||
title=quiz.title, description=quiz.description)
|
||||
game = PlayGame(
|
||||
quiz_id=quiz_id,
|
||||
game_pin=str(game_pin),
|
||||
questions=quiz.questions,
|
||||
game_id=uuid.uuid4(),
|
||||
title=quiz.title,
|
||||
description=quiz.description,
|
||||
)
|
||||
await redis.set(f"game:{str(game.game_pin)}", (game.json()), ex=18000)
|
||||
return {**quiz.dict(exclude={"id"}), **game.dict(exclude={"questions"})}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ router = APIRouter()
|
||||
file_regex = r"^[a-z0-9]{8}-[a-z0-9-]{27}--[a-z0-9-]{36}$"
|
||||
|
||||
|
||||
@router.get('/download/{file_name}')
|
||||
@router.get("/download/{file_name}")
|
||||
async def download_file(file_name: str):
|
||||
if not re.match(file_regex, file_name):
|
||||
raise HTTPException(status_code=400, detail="Invalid file name")
|
||||
@@ -24,5 +24,8 @@ async def download_file(file_name: str):
|
||||
def iter_file():
|
||||
yield from download
|
||||
|
||||
return StreamingResponse(iter_file(), media_type='image/*',
|
||||
headers={"Cache-Control": "public, immutable, max-age=31536000"})
|
||||
return StreamingResponse(
|
||||
iter_file(),
|
||||
media_type="image/*",
|
||||
headers={"Cache-Control": "public, immutable, max-age=31536000"},
|
||||
)
|
||||
|
||||
+57
-21
@@ -7,18 +7,32 @@ from fastapi.background import BackgroundTasks
|
||||
from fastapi.responses import JSONResponse, RedirectResponse
|
||||
from fastapi.security import OAuth2PasswordRequestForm
|
||||
|
||||
from classquiz.auth import get_password_hash, verify_password, authenticate_user, create_access_token, get_current_user
|
||||
from classquiz.auth import (
|
||||
get_password_hash,
|
||||
verify_password,
|
||||
authenticate_user,
|
||||
create_access_token,
|
||||
get_current_user,
|
||||
)
|
||||
from classquiz.cache import clear_cache_for_account
|
||||
from classquiz.config import redis, settings
|
||||
import uuid
|
||||
from pydantic import BaseModel
|
||||
from classquiz.db.models import User, UserSession, UpdatePassword, Token
|
||||
from classquiz.emails import send_register_email, send_forgotten_password_email
|
||||
|
||||
settings = settings()
|
||||
router = APIRouter()
|
||||
|
||||
route_user = User.get_pydantic(
|
||||
exclude={"id": ..., "verified": ..., "verify_key": ..., "created_at": ..., "usersessions": ...})
|
||||
exclude={
|
||||
"id": ...,
|
||||
"verified": ...,
|
||||
"verify_key": ...,
|
||||
"created_at": ...,
|
||||
"usersessions": ...,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def _sign_out_everywhere(user: User) -> None:
|
||||
@@ -26,8 +40,11 @@ async def _sign_out_everywhere(user: User) -> None:
|
||||
await clear_cache_for_account(user)
|
||||
|
||||
|
||||
@router.post("/create", response_model=User,
|
||||
response_model_include={"id": ..., "verified": ..., "email": ...})
|
||||
@router.post(
|
||||
"/create",
|
||||
response_model=User,
|
||||
response_model_include={"id": ..., "verified": ..., "email": ...},
|
||||
)
|
||||
async def create_user(user: route_user, background_task: BackgroundTasks) -> User | JSONResponse:
|
||||
user = User(**user.dict(), id=uuid.uuid4())
|
||||
try:
|
||||
@@ -50,8 +67,11 @@ async def create_user(user: route_user, background_task: BackgroundTasks) -> Use
|
||||
|
||||
|
||||
@router.post("/token/cookie", response_model=Token)
|
||||
async def login_for_cookie_access_token(request: Request, response: Response,
|
||||
form_data: OAuth2PasswordRequestForm = Depends()):
|
||||
async def login_for_cookie_access_token(
|
||||
request: Request,
|
||||
response: Response,
|
||||
form_data: OAuth2PasswordRequestForm = Depends(),
|
||||
):
|
||||
user = await authenticate_user(form_data.username, form_data.password)
|
||||
if not user:
|
||||
raise HTTPException(
|
||||
@@ -59,21 +79,29 @@ async def login_for_cookie_access_token(request: Request, response: Response,
|
||||
detail="Incorrect username or password",
|
||||
)
|
||||
session_key = os.urandom(32).hex()
|
||||
user_session = UserSession(user=user, session_key=session_key, ip_address=request.client.host,
|
||||
user_agent=request.headers.get("User-Agent"), id=uuid.uuid4())
|
||||
user_session = UserSession(
|
||||
user=user,
|
||||
session_key=session_key,
|
||||
ip_address=request.client.host,
|
||||
user_agent=request.headers.get("User-Agent"),
|
||||
id=uuid.uuid4(),
|
||||
)
|
||||
await user_session.save()
|
||||
# await user_session.save()
|
||||
access_token_expires = timedelta(minutes=settings.access_token_expire_minutes)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user.email}, expires_delta=access_token_expires
|
||||
)
|
||||
access_token = create_access_token(data={"sub": user.email}, expires_delta=access_token_expires)
|
||||
await redis.set(access_token, user.email, ex=settings.access_token_expire_minutes * 60)
|
||||
|
||||
response.set_cookie(key="access_token", value=f"Bearer {access_token}",
|
||||
httponly=True, samesite='strict', max_age=settings.access_token_expire_minutes * 60)
|
||||
response.set_cookie(
|
||||
key="access_token",
|
||||
value=f"Bearer {access_token}",
|
||||
httponly=True,
|
||||
samesite="strict",
|
||||
max_age=settings.access_token_expire_minutes * 60,
|
||||
)
|
||||
response.set_cookie(key="expiry", value="", max_age=settings.access_token_expire_minutes * 60)
|
||||
response.set_cookie(key="rememberme", value="")
|
||||
response.set_cookie(key="rememberme_token", value=session_key, httponly=True, samesite='strict')
|
||||
response.set_cookie(key="rememberme_token", value=session_key, httponly=True, samesite="strict")
|
||||
return {"access_token": access_token, "token_type": "bearer"}
|
||||
|
||||
|
||||
@@ -82,16 +110,20 @@ async def rememberme_token(request: Request, response: Response):
|
||||
rememberme_cookie = request.cookies.get("rememberme_token")
|
||||
if rememberme_cookie is None:
|
||||
raise HTTPException(status_code=400, detail="No rememberme cookie")
|
||||
user_session: UserSession | None = await UserSession.objects.filter(session_key=rememberme_cookie).select_related(
|
||||
UserSession.user).get_or_none()
|
||||
user_session: UserSession | None = (
|
||||
await UserSession.objects.filter(session_key=rememberme_cookie).select_related(UserSession.user).get_or_none()
|
||||
)
|
||||
if (user_session is None) or (user_session.user is None):
|
||||
raise HTTPException(status_code=401, detail="No user session")
|
||||
access_token_expires = timedelta(minutes=settings.access_token_expire_minutes * 60)
|
||||
access_token = create_access_token(
|
||||
data={"sub": user_session.user.email}, expires_delta=access_token_expires
|
||||
access_token = create_access_token(data={"sub": user_session.user.email}, expires_delta=access_token_expires)
|
||||
response.set_cookie(
|
||||
key="access_token",
|
||||
value=f"Bearer {access_token}",
|
||||
httponly=True,
|
||||
samesite="strict",
|
||||
max_age=settings.access_token_expire_minutes * 60,
|
||||
)
|
||||
response.set_cookie(key="access_token", value=f"Bearer {access_token}",
|
||||
httponly=True, samesite='strict', max_age=settings.access_token_expire_minutes * 60)
|
||||
response.set_cookie(key="expiry", value="", max_age=settings.access_token_expire_minutes * 60)
|
||||
await user_session.update(last_seen=datetime.now())
|
||||
|
||||
@@ -127,7 +159,11 @@ async def verify_user(verify_key: str):
|
||||
|
||||
|
||||
@router.put("/password/update")
|
||||
async def change_password(password_data: UpdatePassword, response: Response, user: User = Depends(get_current_user)):
|
||||
async def change_password(
|
||||
password_data: UpdatePassword,
|
||||
response: Response,
|
||||
user: User = Depends(get_current_user),
|
||||
):
|
||||
if not verify_password(password_data.old_password, user.password):
|
||||
raise HTTPException(status_code=400, detail="Incorrect password")
|
||||
user.password = get_password_hash(password_data.new_password)
|
||||
|
||||
@@ -14,8 +14,10 @@ settings = settings()
|
||||
async def join_game(sid, data):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
try:
|
||||
async with session.post("https://hcaptcha.com/siteverify",
|
||||
data={"response": data["captcha"], "secret": settings.hcaptcha_key}) as resp:
|
||||
async with session.post(
|
||||
"https://hcaptcha.com/siteverify",
|
||||
data={"response": data["captcha"], "secret": settings.hcaptcha_key},
|
||||
) as resp:
|
||||
resp_data = await resp.json()
|
||||
if not resp_data["success"]:
|
||||
print("CAPTCHA FAILED")
|
||||
@@ -27,17 +29,34 @@ async def join_game(sid, data):
|
||||
if redis_res is None:
|
||||
await sio.emit("game_not_found", room=sid)
|
||||
else:
|
||||
session = {'game_pin': data["game_pin"], "username": data["username"], "admin": False}
|
||||
session = {
|
||||
"game_pin": data["game_pin"],
|
||||
"username": data["username"],
|
||||
"admin": False,
|
||||
}
|
||||
await sio.save_session(sid, session)
|
||||
await sio.emit("joined_game", redis_res, 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 = json.loads(redis_res)
|
||||
# print(redis_res)
|
||||
redis_res["players"].append({"username": data["username"], "sid": sid})
|
||||
await redis.set(f"game_session:{data['game_pin']}",
|
||||
json.dumps({"admin": redis_res["admin"], "game_id": redis_res["game_id"],
|
||||
"players": redis_res["players"], "answers": []}), ex=18000)
|
||||
await sio.emit("player_joined", {"username": data["username"], "sid": sid}, room=redis_res["admin"])
|
||||
await redis.set(
|
||||
f"game_session:{data['game_pin']}",
|
||||
json.dumps(
|
||||
{
|
||||
"admin": redis_res["admin"],
|
||||
"game_id": redis_res["game_id"],
|
||||
"players": redis_res["players"],
|
||||
"answers": [],
|
||||
}
|
||||
),
|
||||
ex=18000,
|
||||
)
|
||||
await sio.emit(
|
||||
"player_joined",
|
||||
{"username": data["username"], "sid": sid},
|
||||
room=redis_res["admin"],
|
||||
)
|
||||
sio.enter_room(sid, data["game_pin"]) # TODO: make more secure
|
||||
|
||||
|
||||
@@ -55,14 +74,19 @@ async def register_as_admin(sid, data):
|
||||
game_pin = data["game_pin"]
|
||||
game_id = data["game_id"]
|
||||
if (await redis.get(f"game_session:{game_pin}")) is None:
|
||||
await redis.set(f"game_session:{game_pin}", json.dumps({"admin": sid, "game_id": game_id, "players": []}),
|
||||
ex=18000)
|
||||
await redis.set(
|
||||
f"game_session:{game_pin}",
|
||||
json.dumps({"admin": sid, "game_id": game_id, "players": []}),
|
||||
ex=18000,
|
||||
)
|
||||
|
||||
await sio.emit("registered_as_admin", {"game_id": game_id, "game": await redis.get(
|
||||
f"game:{data['game_pin']}")},
|
||||
room=sid)
|
||||
await sio.emit(
|
||||
"registered_as_admin",
|
||||
{"game_id": game_id, "game": await redis.get(f"game:{data['game_pin']}")},
|
||||
room=sid,
|
||||
)
|
||||
async with sio.session(sid) as session:
|
||||
session['game_pin'] = data["game_pin"]
|
||||
session["game_pin"] = data["game_pin"]
|
||||
session["admin"] = True
|
||||
sio.enter_room(sid, data["game_pin"])
|
||||
else:
|
||||
@@ -74,7 +98,7 @@ async def get_question_results(sid, data):
|
||||
session = await sio.get_session(sid)
|
||||
if session["admin"]:
|
||||
redis_res = await redis.get(f"game_session:{session['game_pin']}:{data['question_number']}")
|
||||
game_pin = session['game_pin']
|
||||
game_pin = session["game_pin"]
|
||||
await sio.emit("question_results", redis_res, room=game_pin)
|
||||
|
||||
|
||||
@@ -82,7 +106,7 @@ async def get_question_results(sid, data):
|
||||
async def set_question_number(sid, data):
|
||||
session = await sio.get_session(sid)
|
||||
if session["admin"]:
|
||||
game_pin = session['game_pin']
|
||||
game_pin = session["game_pin"]
|
||||
await sio.emit("set_question_number", data, room=game_pin)
|
||||
|
||||
|
||||
@@ -100,21 +124,40 @@ async def submit_answer(sid, data):
|
||||
break
|
||||
answers = await redis.get(f"game_session:{session['game_pin']}:{data['question_index']}")
|
||||
if answers is None:
|
||||
await redis.set(f"game_session:{session['game_pin']}:{data['question_index']}",
|
||||
json.dumps(
|
||||
[{"username": session["username"], "answer": data["answer"], "right": answer_right}]),
|
||||
ex=18000)
|
||||
await redis.set(
|
||||
f"game_session:{session['game_pin']}:{data['question_index']}",
|
||||
json.dumps(
|
||||
[
|
||||
{
|
||||
"username": session["username"],
|
||||
"answer": data["answer"],
|
||||
"right": answer_right,
|
||||
}
|
||||
]
|
||||
),
|
||||
ex=18000,
|
||||
)
|
||||
else:
|
||||
answers = json.loads(answers)
|
||||
answers.append({"username": session["username"], "answer": data["answer"], "right": answer_right})
|
||||
await redis.set(f"game_session:{session['game_pin']}:{data['question_index']}", json.dumps(answers), ex=18000)
|
||||
answers.append(
|
||||
{
|
||||
"username": session["username"],
|
||||
"answer": data["answer"],
|
||||
"right": answer_right,
|
||||
}
|
||||
)
|
||||
await redis.set(
|
||||
f"game_session:{session['game_pin']}:{data['question_index']}",
|
||||
json.dumps(answers),
|
||||
ex=18000,
|
||||
)
|
||||
|
||||
# await redis.set(f"game_data:{session['game_pin']}", json.dumps(data))
|
||||
|
||||
|
||||
@sio.event
|
||||
async def get_game_data(sid, _data):
|
||||
game_pin = (await sio.get_session(sid))['game_pin']
|
||||
game_pin = (await sio.get_session(sid))["game_pin"]
|
||||
game_data = await redis.get(f"game:{game_pin}")
|
||||
if game_data is not None:
|
||||
await sio.emit("game_data", json.loads(game_data), room=game_pin)
|
||||
|
||||
@@ -6,7 +6,13 @@ from .local_storage import LocalStorage
|
||||
|
||||
|
||||
class Storage:
|
||||
def __init__(self, backend: str, deta_key: Optional[str], deta_id: Optional[str], storage_path: Optional[str]):
|
||||
def __init__(
|
||||
self,
|
||||
backend: str,
|
||||
deta_key: Optional[str],
|
||||
deta_id: Optional[str],
|
||||
storage_path: Optional[str],
|
||||
):
|
||||
self.backend = backend
|
||||
self.deta_key: str | None = deta_key
|
||||
self.deta_id: str | None = deta_id
|
||||
@@ -15,8 +21,11 @@ class Storage:
|
||||
if deta_key is None or deta_id is None:
|
||||
raise ValueError("deta_key and deta_id must be provided")
|
||||
else:
|
||||
self.deta_instance = DetaStorage(deta_base_url=self.deta_base_url, deta_key=self.deta_key,
|
||||
deta_id=self.deta_id)
|
||||
self.deta_instance = DetaStorage(
|
||||
deta_base_url=self.deta_base_url,
|
||||
deta_key=self.deta_key,
|
||||
deta_id=self.deta_id,
|
||||
)
|
||||
|
||||
elif backend == "local":
|
||||
if storage_path is None:
|
||||
@@ -29,8 +38,7 @@ class Storage:
|
||||
|
||||
async def download(self, file_name: str) -> BytesIO | None:
|
||||
if self.backend == "deta":
|
||||
return await self.deta_instance.download(
|
||||
file_name)
|
||||
return await self.deta_instance.download(file_name)
|
||||
elif self.backend == "local":
|
||||
return await self.local_instance.get_file(file_name)
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@ class LocalStorage:
|
||||
|
||||
async def get_file(self, file_name: str) -> io.BytesIO | None:
|
||||
try:
|
||||
async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode='rb') as f:
|
||||
async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode="rb") as f:
|
||||
return io.BytesIO(await f.read())
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
|
||||
async def write_file(self, file_name: str, data: bytes) -> None:
|
||||
async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode='wb') as f:
|
||||
async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode="wb") as f:
|
||||
await f.write(data)
|
||||
|
||||
async def delete_file(self, file_names: [str]) -> None:
|
||||
|
||||
@@ -5,60 +5,112 @@ from classquiz.kahoot_importer.get import get
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get():
|
||||
await get('1f95eb0b-fcf4-4db2-879b-5418ef75116b')
|
||||
await get("1f95eb0b-fcf4-4db2-879b-5418ef75116b")
|
||||
await get("c63d4d39-767e-4754-8c16-66c3c2d9633c")
|
||||
await get("c5d31de6-4598-4574-8e67-ca9a2aba49f8")
|
||||
await get("3d545fc3-b830-44b6-94ac-c8e4a3c9fb88")
|
||||
quiz_list = ['c63d4d39-767e-4754-8c16-66c3c2d9633c', 'a73689d2-1108-4524-88e1-10c54282346c',
|
||||
'e8e2d869-7c75-4ad6-8872-031bc8e37fa9', '387b4139-9e77-4b47-93b7-dbb6d75c54d1',
|
||||
'a1fff657-b99d-4547-93cf-4e9629994844', '0880d792-a77e-4cba-8a8b-359de40ffe76',
|
||||
'e2d12f01-a3dd-4d0e-a6c4-29f38046117f', 'e0af9b8e-8840-4a7d-8d51-3d02bb6a07e7',
|
||||
'e655c086-558d-4602-99fd-5fc429ffe0ce', '1642f29d-b5cb-4213-82e6-7c1fae401aca',
|
||||
'fabdd572-da3e-4621-b912-41b9bc5a18fb', 'ded3cbbf-5af3-4761-a6da-b3b6ca040e1a',
|
||||
'370b3e33-ed97-4d36-9362-7f76aa9d12b9', 'cfa32a28-1a01-47f1-9bca-c85640bceeb8',
|
||||
'993f615d-6890-4dd3-ac08-f462de9ac5a3', '7f08a377-e371-425e-95f9-7196401ae7ff',
|
||||
'19656fed-5891-48e7-ae7f-1358f623bfd0', '5e766d32-930a-4640-82a1-87c58612f579',
|
||||
'ab2450e8-ecbb-4aed-bb3c-87e47b2b81f5', 'dd3c9175-addd-48df-8297-7598d4f12006',
|
||||
'b8de862c-fc4b-4df6-9fa5-81b91868fc3b', '3b082f4e-9c50-45ea-98c4-3845b14e400c',
|
||||
'10179f67-a344-4fd0-80a1-d1e1b6ca7953', '8288c1dd-9675-411c-9175-4180004da3c3',
|
||||
'af838c22-dcbb-497d-8c7b-f6499835b83d', '152c5fdb-3b0a-4cdf-a0d9-1984de0643ad',
|
||||
'846fd2bb-6b95-4333-b5d4-ef0a363e73a9', 'dd6b446f-817b-469b-9e6b-e1d34c466a7f',
|
||||
'2720d636-8918-4c6a-86a9-928c489382f4', 'a6985641-df97-4e56-97fc-e0901e32b464',
|
||||
'1d2705b4-b79e-4b51-8d61-cfb60fc7d1b9', '677fa485-4231-4479-beb2-fe757053bf41',
|
||||
'2b6379f8-450e-46c0-a854-5e368a03d5a6', 'f3ba42fe-11e8-47fc-8978-8d67bc0ce0b1',
|
||||
'51d2e839-a9d7-4f26-b1fd-fb0743c3a236', 'f2212243-4553-456d-87e7-7ddfd6ee7563',
|
||||
'2d6982d3-7b46-4022-bbb6-cb5451c581ba', '1cccae22-5140-46b5-ba6a-aa17cec14dc3',
|
||||
'b01dd2c2-ffcf-4c02-a274-e12296dbe4e1', '2669d193-a81f-4752-a003-dd2433beef92',
|
||||
'06203ee6-3615-4e26-bb08-4fab249698a4', '933c1426-c759-43a3-9770-c8e3948db998',
|
||||
'02046384-fb05-4f1d-855a-f0e52a0761e3', 'b5266e9b-7372-4df0-94a9-ec8a6c0aabc6',
|
||||
'fb8e3a3d-505a-4157-bd9a-b73b00591f10', '42092675-9171-400f-ab4d-c898ec9f70bd',
|
||||
'1d6307e2-98c2-42f2-b765-d02a68e0da80', '8f431ed0-31df-4435-bcba-aff46929fe82',
|
||||
'd573d76f-d715-4a54-bdde-7ac7b6235fb2', 'feb8985b-84fb-4d41-bc41-70c322f508fa',
|
||||
'f9f945cc-c166-4f7d-a95f-f0be154c43cf', '009b130a-1709-41a6-943d-85eee4889792',
|
||||
'a3f765f7-2329-47e0-83eb-0837b4b54b43', 'fea9e558-206f-444a-8d7f-a3f0301fda8d',
|
||||
'9b419098-ccfc-467d-9de0-db18b60d6c28', 'e6c2174e-3a3f-4193-9782-1bf3b5cfae63',
|
||||
'd42c962e-1691-4fa4-8926-c33157423bd2', '8d27aa83-a103-4f05-8230-69e857bc3ffd',
|
||||
'60cd19b2-6858-4704-b07b-5196e8e24325', '31624ea4-1e4f-4f90-ae4f-759a0eb93b61',
|
||||
'fcd038f4-6b05-485b-afe3-ce67e05b1695', '1207bbe5-2df5-4c86-8f54-1b2675a86859',
|
||||
'eea3b0b0-deae-41de-b010-d6d50855d016', 'ca8b0278-744c-4a3f-a2b8-6ab29d76fa7f',
|
||||
'68d37bc8-28b9-49fd-9e02-48a3c9195fdf', '8bf13f61-c5cc-4332-aab9-37405d1fdd14',
|
||||
'8ceef7de-9408-48d1-8bb9-b8ac3ef73596', '7e053a85-b04c-484b-8815-d26ce646c748',
|
||||
'dfc09237-85b7-4596-b4e8-0bfbfd1aaeb7', 'd8d291ab-9dfa-45c0-bfc2-0a81167e3c63',
|
||||
'd0834157-d464-4632-b620-147e211e0614', '30758f35-fabc-48bc-b410-47450de64c82',
|
||||
'7232bc80-a9cb-4668-9449-16b9282a2331', '29a194bd-2ecc-44bc-a6a9-6b8a25c3d860',
|
||||
'52b43400-117a-4e16-81b8-b510fe987750', '52ef0c45-9ba1-4d5e-ba0c-82703196ec6c',
|
||||
'9f0fa778-e6ae-4f7b-b4fd-2ccb1bd37eb7', '54b57afd-c91c-4d5f-8177-c3fe940bc723',
|
||||
'23620661-6eb6-41df-9de4-18ff99e6f35f', 'c983fe89-6ac3-4395-882e-4b11aa11a99d',
|
||||
'd4d01494-e7d0-4a38-aa93-523a77855371', 'e5a77df6-fd29-48c7-8df6-f625de0179e7',
|
||||
'bca45eef-fc64-465f-9106-403814f180a0', '3b5c305d-be5e-4209-9ba2-c2d1620a5a4f',
|
||||
'14c533a5-c15c-493b-8c7d-7c20a0bc5ea1', '7b3b1e40-c75d-4c2c-843b-1599c10123c2',
|
||||
'54604482-72aa-4d64-a3bb-9d8dc4a38b41', 'baf6f609-2dce-4978-840b-b74261c5ba6d',
|
||||
'250c536f-6cd9-4707-8706-b682961be032', 'a9b08140-ae44-409a-9941-22794c3fd097',
|
||||
'e516bd18-c46f-46b5-88dc-c7eac5a0f5a9', '6a220133-c71f-44df-8912-16f5b72568cb',
|
||||
'b63c2adf-b8f3-4e43-8651-44574475621f', '01d88287-387b-4a34-b6d3-0931aba8eb1a',
|
||||
'eb0cef3d-4e2b-45b3-88df-fc547a763d01', 'a79dee0f-8579-4e0a-b77f-0a468a3ca71f',
|
||||
'62e50edb-03e6-4f95-a6f4-8ff3ef28a32b', '337fffe8-cc68-44b7-9636-2433f86ed5a9',
|
||||
'bd68f7b7-f8bb-489e-ac29-f8cbfe1978b3', '93d2a7d6-06a6-41bf-9632-c24e1f1c917a']
|
||||
quiz_list = [
|
||||
"c63d4d39-767e-4754-8c16-66c3c2d9633c",
|
||||
"a73689d2-1108-4524-88e1-10c54282346c",
|
||||
"e8e2d869-7c75-4ad6-8872-031bc8e37fa9",
|
||||
"387b4139-9e77-4b47-93b7-dbb6d75c54d1",
|
||||
"a1fff657-b99d-4547-93cf-4e9629994844",
|
||||
"0880d792-a77e-4cba-8a8b-359de40ffe76",
|
||||
"e2d12f01-a3dd-4d0e-a6c4-29f38046117f",
|
||||
"e0af9b8e-8840-4a7d-8d51-3d02bb6a07e7",
|
||||
"e655c086-558d-4602-99fd-5fc429ffe0ce",
|
||||
"1642f29d-b5cb-4213-82e6-7c1fae401aca",
|
||||
"fabdd572-da3e-4621-b912-41b9bc5a18fb",
|
||||
"ded3cbbf-5af3-4761-a6da-b3b6ca040e1a",
|
||||
"370b3e33-ed97-4d36-9362-7f76aa9d12b9",
|
||||
"cfa32a28-1a01-47f1-9bca-c85640bceeb8",
|
||||
"993f615d-6890-4dd3-ac08-f462de9ac5a3",
|
||||
"7f08a377-e371-425e-95f9-7196401ae7ff",
|
||||
"19656fed-5891-48e7-ae7f-1358f623bfd0",
|
||||
"5e766d32-930a-4640-82a1-87c58612f579",
|
||||
"ab2450e8-ecbb-4aed-bb3c-87e47b2b81f5",
|
||||
"dd3c9175-addd-48df-8297-7598d4f12006",
|
||||
"b8de862c-fc4b-4df6-9fa5-81b91868fc3b",
|
||||
"3b082f4e-9c50-45ea-98c4-3845b14e400c",
|
||||
"10179f67-a344-4fd0-80a1-d1e1b6ca7953",
|
||||
"8288c1dd-9675-411c-9175-4180004da3c3",
|
||||
"af838c22-dcbb-497d-8c7b-f6499835b83d",
|
||||
"152c5fdb-3b0a-4cdf-a0d9-1984de0643ad",
|
||||
"846fd2bb-6b95-4333-b5d4-ef0a363e73a9",
|
||||
"dd6b446f-817b-469b-9e6b-e1d34c466a7f",
|
||||
"2720d636-8918-4c6a-86a9-928c489382f4",
|
||||
"a6985641-df97-4e56-97fc-e0901e32b464",
|
||||
"1d2705b4-b79e-4b51-8d61-cfb60fc7d1b9",
|
||||
"677fa485-4231-4479-beb2-fe757053bf41",
|
||||
"2b6379f8-450e-46c0-a854-5e368a03d5a6",
|
||||
"f3ba42fe-11e8-47fc-8978-8d67bc0ce0b1",
|
||||
"51d2e839-a9d7-4f26-b1fd-fb0743c3a236",
|
||||
"f2212243-4553-456d-87e7-7ddfd6ee7563",
|
||||
"2d6982d3-7b46-4022-bbb6-cb5451c581ba",
|
||||
"1cccae22-5140-46b5-ba6a-aa17cec14dc3",
|
||||
"b01dd2c2-ffcf-4c02-a274-e12296dbe4e1",
|
||||
"2669d193-a81f-4752-a003-dd2433beef92",
|
||||
"06203ee6-3615-4e26-bb08-4fab249698a4",
|
||||
"933c1426-c759-43a3-9770-c8e3948db998",
|
||||
"02046384-fb05-4f1d-855a-f0e52a0761e3",
|
||||
"b5266e9b-7372-4df0-94a9-ec8a6c0aabc6",
|
||||
"fb8e3a3d-505a-4157-bd9a-b73b00591f10",
|
||||
"42092675-9171-400f-ab4d-c898ec9f70bd",
|
||||
"1d6307e2-98c2-42f2-b765-d02a68e0da80",
|
||||
"8f431ed0-31df-4435-bcba-aff46929fe82",
|
||||
"d573d76f-d715-4a54-bdde-7ac7b6235fb2",
|
||||
"feb8985b-84fb-4d41-bc41-70c322f508fa",
|
||||
"f9f945cc-c166-4f7d-a95f-f0be154c43cf",
|
||||
"009b130a-1709-41a6-943d-85eee4889792",
|
||||
"a3f765f7-2329-47e0-83eb-0837b4b54b43",
|
||||
"fea9e558-206f-444a-8d7f-a3f0301fda8d",
|
||||
"9b419098-ccfc-467d-9de0-db18b60d6c28",
|
||||
"e6c2174e-3a3f-4193-9782-1bf3b5cfae63",
|
||||
"d42c962e-1691-4fa4-8926-c33157423bd2",
|
||||
"8d27aa83-a103-4f05-8230-69e857bc3ffd",
|
||||
"60cd19b2-6858-4704-b07b-5196e8e24325",
|
||||
"31624ea4-1e4f-4f90-ae4f-759a0eb93b61",
|
||||
"fcd038f4-6b05-485b-afe3-ce67e05b1695",
|
||||
"1207bbe5-2df5-4c86-8f54-1b2675a86859",
|
||||
"eea3b0b0-deae-41de-b010-d6d50855d016",
|
||||
"ca8b0278-744c-4a3f-a2b8-6ab29d76fa7f",
|
||||
"68d37bc8-28b9-49fd-9e02-48a3c9195fdf",
|
||||
"8bf13f61-c5cc-4332-aab9-37405d1fdd14",
|
||||
"8ceef7de-9408-48d1-8bb9-b8ac3ef73596",
|
||||
"7e053a85-b04c-484b-8815-d26ce646c748",
|
||||
"dfc09237-85b7-4596-b4e8-0bfbfd1aaeb7",
|
||||
"d8d291ab-9dfa-45c0-bfc2-0a81167e3c63",
|
||||
"d0834157-d464-4632-b620-147e211e0614",
|
||||
"30758f35-fabc-48bc-b410-47450de64c82",
|
||||
"7232bc80-a9cb-4668-9449-16b9282a2331",
|
||||
"29a194bd-2ecc-44bc-a6a9-6b8a25c3d860",
|
||||
"52b43400-117a-4e16-81b8-b510fe987750",
|
||||
"52ef0c45-9ba1-4d5e-ba0c-82703196ec6c",
|
||||
"9f0fa778-e6ae-4f7b-b4fd-2ccb1bd37eb7",
|
||||
"54b57afd-c91c-4d5f-8177-c3fe940bc723",
|
||||
"23620661-6eb6-41df-9de4-18ff99e6f35f",
|
||||
"c983fe89-6ac3-4395-882e-4b11aa11a99d",
|
||||
"d4d01494-e7d0-4a38-aa93-523a77855371",
|
||||
"e5a77df6-fd29-48c7-8df6-f625de0179e7",
|
||||
"bca45eef-fc64-465f-9106-403814f180a0",
|
||||
"3b5c305d-be5e-4209-9ba2-c2d1620a5a4f",
|
||||
"14c533a5-c15c-493b-8c7d-7c20a0bc5ea1",
|
||||
"7b3b1e40-c75d-4c2c-843b-1599c10123c2",
|
||||
"54604482-72aa-4d64-a3bb-9d8dc4a38b41",
|
||||
"baf6f609-2dce-4978-840b-b74261c5ba6d",
|
||||
"250c536f-6cd9-4707-8706-b682961be032",
|
||||
"a9b08140-ae44-409a-9941-22794c3fd097",
|
||||
"e516bd18-c46f-46b5-88dc-c7eac5a0f5a9",
|
||||
"6a220133-c71f-44df-8912-16f5b72568cb",
|
||||
"b63c2adf-b8f3-4e43-8651-44574475621f",
|
||||
"01d88287-387b-4a34-b6d3-0931aba8eb1a",
|
||||
"eb0cef3d-4e2b-45b3-88df-fc547a763d01",
|
||||
"a79dee0f-8579-4e0a-b77f-0a468a3ca71f",
|
||||
"62e50edb-03e6-4f95-a6f4-8ff3ef28a32b",
|
||||
"337fffe8-cc68-44b7-9636-2433f86ed5a9",
|
||||
"bd68f7b7-f8bb-489e-ac29-f8cbfe1978b3",
|
||||
"93d2a7d6-06a6-41bf-9632-c24e1f1c917a",
|
||||
]
|
||||
rounds = 0
|
||||
for i in quiz_list:
|
||||
rounds = rounds + 1
|
||||
|
||||
@@ -10,11 +10,14 @@ file_contents = b"hello world"
|
||||
|
||||
def test_storage_init():
|
||||
with pytest.raises(NotImplementedError):
|
||||
Storage(backend="asdsad", deta_key=settings.deta_project_key,
|
||||
deta_id=settings.deta_project_id, storage_path=settings.storage_path)
|
||||
Storage(
|
||||
backend="asdsad",
|
||||
deta_key=settings.deta_project_key,
|
||||
deta_id=settings.deta_project_id,
|
||||
storage_path=settings.storage_path,
|
||||
)
|
||||
with pytest.raises(ValueError):
|
||||
Storage(backend="deta", deta_key=None,
|
||||
deta_id=None, storage_path=None)
|
||||
Storage(backend="deta", deta_key=None, deta_id=None, storage_path=None)
|
||||
with pytest.raises(ValueError):
|
||||
Storage(backend="local", storage_path=None, deta_key=None, deta_id=None)
|
||||
|
||||
@@ -34,8 +37,12 @@ async def storage_tester(storage: Storage):
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_deta():
|
||||
storage: Storage = Storage(backend="deta", deta_key=settings.deta_project_key,
|
||||
deta_id=settings.deta_project_id, storage_path=settings.storage_path)
|
||||
storage: Storage = Storage(
|
||||
backend="deta",
|
||||
deta_key=settings.deta_project_key,
|
||||
deta_id=settings.deta_project_id,
|
||||
storage_path=settings.storage_path,
|
||||
)
|
||||
await storage_tester(storage)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[tool.black]
|
||||
line-length = 120
|
||||
target-version = ["py310"]
|
||||
Reference in New Issue
Block a user