mirror of
https://github.com/koloideal/Quizzi.git
synced 2026-06-10 10:25:28 +03:00
update
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from aiogram.types import CallbackQuery, Message
|
from aiogram.types import CallbackQuery, Message
|
||||||
from aiogram_dialog import Dialog, DialogManager, Window
|
from aiogram_dialog import Dialog, DialogManager, Window
|
||||||
from aiogram_dialog.widgets.input import MessageInput
|
from aiogram_dialog.widgets.input import MessageInput
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from quizzi.infrastructure.database.dao.user_answer import UserAnswerDAO
|
|||||||
from quizzi.infrastructure.database.repo.test import TestRepository
|
from quizzi.infrastructure.database.repo.test import TestRepository
|
||||||
from quizzi.infrastructure.database.repo.test_attempt import TestAttemptRepository
|
from quizzi.infrastructure.database.repo.test_attempt import TestAttemptRepository
|
||||||
from quizzi.infrastructure.utils.rate_limiter import PasswordRateLimiter
|
from quizzi.infrastructure.utils.rate_limiter import PasswordRateLimiter
|
||||||
from quizzi.infrastructure.utils.timezone import now_msk_naive
|
from quizzi.infrastructure.utils.timezone import now_utc_naive
|
||||||
|
|
||||||
|
|
||||||
async def get_state_for_question_type(question_type: str):
|
async def get_state_for_question_type(question_type: str):
|
||||||
@@ -33,7 +33,7 @@ async def get_state_for_question_type(question_type: str):
|
|||||||
def get_remaining_time(started_at: datetime, time_limit: int) -> int | None:
|
def get_remaining_time(started_at: datetime, time_limit: int) -> int | None:
|
||||||
if not time_limit:
|
if not time_limit:
|
||||||
return None
|
return None
|
||||||
elapsed = (now_msk_naive() - started_at).total_seconds()
|
elapsed = (now_utc_naive() - started_at).total_seconds()
|
||||||
remaining = time_limit - elapsed
|
remaining = time_limit - elapsed
|
||||||
return max(0, int(remaining))
|
return max(0, int(remaining))
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ async def start_test_directly(
|
|||||||
return
|
return
|
||||||
|
|
||||||
attempt = await attempt_repo.attempt_dao.create(user_id=user_id, test_id=test_id)
|
attempt = await attempt_repo.attempt_dao.create(user_id=user_id, test_id=test_id)
|
||||||
started_at = now_msk_naive()
|
started_at = now_utc_naive()
|
||||||
|
|
||||||
first_question, _ = await test_repo.get_question_with_options(questions[0].id)
|
first_question, _ = await test_repo.get_question_with_options(questions[0].id)
|
||||||
first_state = await get_state_for_question_type(first_question.question_type if first_question else QuestionType.SINGLE)
|
first_state = await get_state_for_question_type(first_question.question_type if first_question else QuestionType.SINGLE)
|
||||||
@@ -364,7 +364,7 @@ async def on_password_input(
|
|||||||
return
|
return
|
||||||
|
|
||||||
attempt = await attempt_repo.attempt_dao.create(user_id=message.from_user.id, test_id=test_id)
|
attempt = await attempt_repo.attempt_dao.create(user_id=message.from_user.id, test_id=test_id)
|
||||||
started_at = now_msk_naive()
|
started_at = now_utc_naive()
|
||||||
|
|
||||||
first_question, _ = await test_repo.get_question_with_options(questions[0].id)
|
first_question, _ = await test_repo.get_question_with_options(questions[0].id)
|
||||||
first_state = await get_state_for_question_type(first_question.question_type if first_question else QuestionType.SINGLE)
|
first_state = await get_state_for_question_type(first_question.question_type if first_question else QuestionType.SINGLE)
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import NotRequired, TypedDict, Unpack
|
|
||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
@@ -9,25 +8,6 @@ from quizzi.infrastructure.database.dto.test import TestDTO
|
|||||||
from quizzi.infrastructure.database.models import Test
|
from quizzi.infrastructure.database.models import Test
|
||||||
|
|
||||||
|
|
||||||
class _UNSET:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
UNSET = _UNSET()
|
|
||||||
|
|
||||||
|
|
||||||
class TestUpdateFields(TypedDict, total=False):
|
|
||||||
title: str
|
|
||||||
description: str | None
|
|
||||||
for_group: int | None
|
|
||||||
password: str | None
|
|
||||||
expires_at: datetime | None
|
|
||||||
attempts: int | None
|
|
||||||
time_limit: int | None
|
|
||||||
is_active: bool
|
|
||||||
are_results_viewable: bool
|
|
||||||
|
|
||||||
|
|
||||||
class TestDAO:
|
class TestDAO:
|
||||||
def __init__(self, session: AsyncSession) -> None:
|
def __init__(self, session: AsyncSession) -> None:
|
||||||
self.session: AsyncSession = session
|
self.session: AsyncSession = session
|
||||||
@@ -87,15 +67,15 @@ class TestDAO:
|
|||||||
async def update(
|
async def update(
|
||||||
self,
|
self,
|
||||||
test_id: int,
|
test_id: int,
|
||||||
title: str | _UNSET = UNSET,
|
title: str | None = None,
|
||||||
description: str | None | _UNSET = UNSET,
|
description: str | None = None,
|
||||||
for_group: int | None | _UNSET = UNSET,
|
for_group: int | None = None,
|
||||||
password: str | None | _UNSET = UNSET,
|
password: str | None = None,
|
||||||
expires_at: datetime | None | _UNSET = UNSET,
|
expires_at: datetime | None = None,
|
||||||
attempts: int | None | _UNSET = UNSET,
|
attempts: int | None = None,
|
||||||
time_limit: int | None | _UNSET = UNSET,
|
time_limit: int | None = None,
|
||||||
is_active: bool | _UNSET = UNSET,
|
is_active: bool | None = None,
|
||||||
are_results_viewable: bool | _UNSET = UNSET,
|
are_results_viewable: bool | None = None,
|
||||||
) -> DomainTest | None:
|
) -> DomainTest | None:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(
|
||||||
select(Test).where(Test.id == test_id)
|
select(Test).where(Test.id == test_id)
|
||||||
@@ -104,23 +84,23 @@ class TestDAO:
|
|||||||
if not test:
|
if not test:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not isinstance(title, _UNSET):
|
if title is not None:
|
||||||
test.title = title
|
test.title = title
|
||||||
if not isinstance(description, _UNSET):
|
if description is not None:
|
||||||
test.description = description
|
test.description = description
|
||||||
if not isinstance(for_group, _UNSET):
|
if for_group is not None:
|
||||||
test.for_group = for_group
|
test.for_group = for_group
|
||||||
if not isinstance(password, _UNSET):
|
if password is not None:
|
||||||
test.password = password
|
test.password = password
|
||||||
if not isinstance(expires_at, _UNSET):
|
if expires_at is not None:
|
||||||
test.expires_at = expires_at
|
test.expires_at = expires_at
|
||||||
if not isinstance(attempts, _UNSET):
|
if attempts is not None:
|
||||||
test.attempts = attempts
|
test.attempts = attempts
|
||||||
if not isinstance(time_limit, _UNSET):
|
if time_limit is not None:
|
||||||
test.time_limit = time_limit
|
test.time_limit = time_limit
|
||||||
if not isinstance(is_active, _UNSET):
|
if is_active is not None:
|
||||||
test.is_active = is_active
|
test.is_active = is_active
|
||||||
if not isinstance(are_results_viewable, _UNSET):
|
if are_results_viewable is not None:
|
||||||
test.are_results_viewable = are_results_viewable
|
test.are_results_viewable = are_results_viewable
|
||||||
|
|
||||||
await self.session.flush()
|
await self.session.flush()
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ class TestAttemptDAO:
|
|||||||
attempt = TestAttempt(
|
attempt = TestAttempt(
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
test_id=test_id,
|
test_id=test_id,
|
||||||
|
started_at=datetime.utcnow(),
|
||||||
score=score,
|
score=score,
|
||||||
is_passed=is_passed,
|
is_passed=is_passed,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,13 +8,6 @@ from quizzi.infrastructure.database.dto.user import UserDTO
|
|||||||
from quizzi.infrastructure.database.models import User
|
from quizzi.infrastructure.database.models import User
|
||||||
|
|
||||||
|
|
||||||
class _UNSET:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
UNSET = _UNSET()
|
|
||||||
|
|
||||||
|
|
||||||
class UserDAO:
|
class UserDAO:
|
||||||
def __init__(self, session: AsyncSession) -> None:
|
def __init__(self, session: AsyncSession) -> None:
|
||||||
self.session: AsyncSession = session
|
self.session: AsyncSession = session
|
||||||
@@ -74,14 +67,14 @@ class UserDAO:
|
|||||||
async def update(
|
async def update(
|
||||||
self,
|
self,
|
||||||
user_id: int,
|
user_id: int,
|
||||||
username: str | None | _UNSET = UNSET,
|
username: str | None = None,
|
||||||
first_name: str | _UNSET = UNSET,
|
first_name: str | None = None,
|
||||||
last_name: str | None | _UNSET = UNSET,
|
last_name: str | None = None,
|
||||||
name: str | None | _UNSET = UNSET,
|
name: str | None = None,
|
||||||
group: int | None | _UNSET = UNSET,
|
group: int | None = None,
|
||||||
is_admin: bool | _UNSET = UNSET,
|
is_admin: bool | None = None,
|
||||||
name_updated_at: datetime | None | _UNSET = UNSET,
|
name_updated_at: datetime | None = None,
|
||||||
group_updated_at: datetime | None | _UNSET = UNSET,
|
group_updated_at: datetime | None = None,
|
||||||
) -> DomainUser | None:
|
) -> DomainUser | None:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(
|
||||||
select(User).where(User.id == user_id)
|
select(User).where(User.id == user_id)
|
||||||
@@ -90,21 +83,21 @@ class UserDAO:
|
|||||||
if not user:
|
if not user:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if not isinstance(username, _UNSET):
|
if username is not None:
|
||||||
user.username = username
|
user.username = username
|
||||||
if not isinstance(first_name, _UNSET):
|
if first_name is not None:
|
||||||
user.first_name = first_name
|
user.first_name = first_name
|
||||||
if not isinstance(last_name, _UNSET):
|
if last_name is not None:
|
||||||
user.last_name = last_name
|
user.last_name = last_name
|
||||||
if not isinstance(name, _UNSET):
|
if name is not None:
|
||||||
user.name = name
|
user.name = name
|
||||||
if not isinstance(group, _UNSET):
|
if group is not None:
|
||||||
user.group = group
|
user.group = group
|
||||||
if not isinstance(is_admin, _UNSET):
|
if is_admin is not None:
|
||||||
user.is_admin = is_admin
|
user.is_admin = is_admin
|
||||||
if not isinstance(name_updated_at, _UNSET):
|
if name_updated_at is not None:
|
||||||
user.name_updated_at = name_updated_at
|
user.name_updated_at = name_updated_at
|
||||||
if not isinstance(group_updated_at, _UNSET):
|
if group_updated_at is not None:
|
||||||
user.group_updated_at = group_updated_at
|
user.group_updated_at = group_updated_at
|
||||||
|
|
||||||
await self.session.flush()
|
await self.session.flush()
|
||||||
@@ -129,9 +122,9 @@ class UserDAO:
|
|||||||
first_name: str,
|
first_name: str,
|
||||||
username: str | None = None,
|
username: str | None = None,
|
||||||
last_name: str | None = None,
|
last_name: str | None = None,
|
||||||
name: str | None | _UNSET = UNSET,
|
name: str | None = None,
|
||||||
group: int | None | _UNSET = UNSET,
|
group: int | None = None,
|
||||||
is_admin: bool | _UNSET = UNSET,
|
is_admin: bool | None = None,
|
||||||
) -> DomainUser:
|
) -> DomainUser:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(
|
||||||
select(User).where(User.id == user_id)
|
select(User).where(User.id == user_id)
|
||||||
@@ -142,11 +135,11 @@ class UserDAO:
|
|||||||
user.username = username
|
user.username = username
|
||||||
user.first_name = first_name
|
user.first_name = first_name
|
||||||
user.last_name = last_name
|
user.last_name = last_name
|
||||||
if not isinstance(name, _UNSET):
|
if name is not None:
|
||||||
user.name = name
|
user.name = name
|
||||||
if not isinstance(group, _UNSET):
|
if group is not None:
|
||||||
user.group = group
|
user.group = group
|
||||||
if not isinstance(is_admin, _UNSET):
|
if is_admin is not None:
|
||||||
user.is_admin = is_admin
|
user.is_admin = is_admin
|
||||||
await self.session.flush()
|
await self.session.flush()
|
||||||
await self.session.refresh(user)
|
await self.session.refresh(user)
|
||||||
@@ -157,7 +150,7 @@ class UserDAO:
|
|||||||
username=username,
|
username=username,
|
||||||
first_name=first_name,
|
first_name=first_name,
|
||||||
last_name=last_name,
|
last_name=last_name,
|
||||||
name=name if not isinstance(name, _UNSET) else None,
|
name=name,
|
||||||
group=group if not isinstance(group, _UNSET) else None,
|
group=group,
|
||||||
is_admin=is_admin if not isinstance(is_admin, _UNSET) else False,
|
is_admin=is_admin if is_admin is not None else False,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ def now_msk_naive() -> datetime:
|
|||||||
return datetime.now(MSK_TZ).replace(tzinfo=None)
|
return datetime.now(MSK_TZ).replace(tzinfo=None)
|
||||||
|
|
||||||
|
|
||||||
|
def now_utc_naive() -> datetime:
|
||||||
|
return datetime.utcnow()
|
||||||
|
|
||||||
|
|
||||||
def to_msk(dt: datetime | None) -> datetime | None:
|
def to_msk(dt: datetime | None) -> datetime | None:
|
||||||
if dt is None:
|
if dt is None:
|
||||||
return None
|
return None
|
||||||
|
|||||||
Reference in New Issue
Block a user