mirror of
https://github.com/koloideal/Quizzi.git
synced 2026-06-10 02:15:29 +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_dialog import Dialog, DialogManager, Window
|
||||
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_attempt import TestAttemptRepository
|
||||
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):
|
||||
@@ -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:
|
||||
if not time_limit:
|
||||
return None
|
||||
elapsed = (now_msk_naive() - started_at).total_seconds()
|
||||
elapsed = (now_utc_naive() - started_at).total_seconds()
|
||||
remaining = time_limit - elapsed
|
||||
return max(0, int(remaining))
|
||||
|
||||
@@ -308,7 +308,7 @@ async def start_test_directly(
|
||||
return
|
||||
|
||||
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_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
|
||||
|
||||
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_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 typing import NotRequired, TypedDict, Unpack
|
||||
|
||||
from sqlalchemy import select
|
||||
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
|
||||
|
||||
|
||||
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:
|
||||
def __init__(self, session: AsyncSession) -> None:
|
||||
self.session: AsyncSession = session
|
||||
@@ -87,15 +67,15 @@ class TestDAO:
|
||||
async def update(
|
||||
self,
|
||||
test_id: int,
|
||||
title: str | _UNSET = UNSET,
|
||||
description: str | None | _UNSET = UNSET,
|
||||
for_group: int | None | _UNSET = UNSET,
|
||||
password: str | None | _UNSET = UNSET,
|
||||
expires_at: datetime | None | _UNSET = UNSET,
|
||||
attempts: int | None | _UNSET = UNSET,
|
||||
time_limit: int | None | _UNSET = UNSET,
|
||||
is_active: bool | _UNSET = UNSET,
|
||||
are_results_viewable: bool | _UNSET = UNSET,
|
||||
title: str | None = None,
|
||||
description: str | None = None,
|
||||
for_group: int | None = None,
|
||||
password: str | None = None,
|
||||
expires_at: datetime | None = None,
|
||||
attempts: int | None = None,
|
||||
time_limit: int | None = None,
|
||||
is_active: bool | None = None,
|
||||
are_results_viewable: bool | None = None,
|
||||
) -> DomainTest | None:
|
||||
result = await self.session.execute(
|
||||
select(Test).where(Test.id == test_id)
|
||||
@@ -104,23 +84,23 @@ class TestDAO:
|
||||
if not test:
|
||||
return None
|
||||
|
||||
if not isinstance(title, _UNSET):
|
||||
if title is not None:
|
||||
test.title = title
|
||||
if not isinstance(description, _UNSET):
|
||||
if description is not None:
|
||||
test.description = description
|
||||
if not isinstance(for_group, _UNSET):
|
||||
if for_group is not None:
|
||||
test.for_group = for_group
|
||||
if not isinstance(password, _UNSET):
|
||||
if password is not None:
|
||||
test.password = password
|
||||
if not isinstance(expires_at, _UNSET):
|
||||
if expires_at is not None:
|
||||
test.expires_at = expires_at
|
||||
if not isinstance(attempts, _UNSET):
|
||||
if attempts is not None:
|
||||
test.attempts = attempts
|
||||
if not isinstance(time_limit, _UNSET):
|
||||
if time_limit is not None:
|
||||
test.time_limit = time_limit
|
||||
if not isinstance(is_active, _UNSET):
|
||||
if is_active is not None:
|
||||
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
|
||||
|
||||
await self.session.flush()
|
||||
|
||||
@@ -34,6 +34,7 @@ class TestAttemptDAO:
|
||||
attempt = TestAttempt(
|
||||
user_id=user_id,
|
||||
test_id=test_id,
|
||||
started_at=datetime.utcnow(),
|
||||
score=score,
|
||||
is_passed=is_passed,
|
||||
)
|
||||
|
||||
@@ -8,13 +8,6 @@ from quizzi.infrastructure.database.dto.user import UserDTO
|
||||
from quizzi.infrastructure.database.models import User
|
||||
|
||||
|
||||
class _UNSET:
|
||||
pass
|
||||
|
||||
|
||||
UNSET = _UNSET()
|
||||
|
||||
|
||||
class UserDAO:
|
||||
def __init__(self, session: AsyncSession) -> None:
|
||||
self.session: AsyncSession = session
|
||||
@@ -74,14 +67,14 @@ class UserDAO:
|
||||
async def update(
|
||||
self,
|
||||
user_id: int,
|
||||
username: str | None | _UNSET = UNSET,
|
||||
first_name: str | _UNSET = UNSET,
|
||||
last_name: str | None | _UNSET = UNSET,
|
||||
name: str | None | _UNSET = UNSET,
|
||||
group: int | None | _UNSET = UNSET,
|
||||
is_admin: bool | _UNSET = UNSET,
|
||||
name_updated_at: datetime | None | _UNSET = UNSET,
|
||||
group_updated_at: datetime | None | _UNSET = UNSET,
|
||||
username: str | None = None,
|
||||
first_name: str | None = None,
|
||||
last_name: str | None = None,
|
||||
name: str | None = None,
|
||||
group: int | None = None,
|
||||
is_admin: bool | None = None,
|
||||
name_updated_at: datetime | None = None,
|
||||
group_updated_at: datetime | None = None,
|
||||
) -> DomainUser | None:
|
||||
result = await self.session.execute(
|
||||
select(User).where(User.id == user_id)
|
||||
@@ -90,21 +83,21 @@ class UserDAO:
|
||||
if not user:
|
||||
return None
|
||||
|
||||
if not isinstance(username, _UNSET):
|
||||
if username is not None:
|
||||
user.username = username
|
||||
if not isinstance(first_name, _UNSET):
|
||||
if first_name is not None:
|
||||
user.first_name = first_name
|
||||
if not isinstance(last_name, _UNSET):
|
||||
if last_name is not None:
|
||||
user.last_name = last_name
|
||||
if not isinstance(name, _UNSET):
|
||||
if name is not None:
|
||||
user.name = name
|
||||
if not isinstance(group, _UNSET):
|
||||
if group is not None:
|
||||
user.group = group
|
||||
if not isinstance(is_admin, _UNSET):
|
||||
if is_admin is not None:
|
||||
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
|
||||
if not isinstance(group_updated_at, _UNSET):
|
||||
if group_updated_at is not None:
|
||||
user.group_updated_at = group_updated_at
|
||||
|
||||
await self.session.flush()
|
||||
@@ -129,9 +122,9 @@ class UserDAO:
|
||||
first_name: str,
|
||||
username: str | None = None,
|
||||
last_name: str | None = None,
|
||||
name: str | None | _UNSET = UNSET,
|
||||
group: int | None | _UNSET = UNSET,
|
||||
is_admin: bool | _UNSET = UNSET,
|
||||
name: str | None = None,
|
||||
group: int | None = None,
|
||||
is_admin: bool | None = None,
|
||||
) -> DomainUser:
|
||||
result = await self.session.execute(
|
||||
select(User).where(User.id == user_id)
|
||||
@@ -142,11 +135,11 @@ class UserDAO:
|
||||
user.username = username
|
||||
user.first_name = first_name
|
||||
user.last_name = last_name
|
||||
if not isinstance(name, _UNSET):
|
||||
if name is not None:
|
||||
user.name = name
|
||||
if not isinstance(group, _UNSET):
|
||||
if group is not None:
|
||||
user.group = group
|
||||
if not isinstance(is_admin, _UNSET):
|
||||
if is_admin is not None:
|
||||
user.is_admin = is_admin
|
||||
await self.session.flush()
|
||||
await self.session.refresh(user)
|
||||
@@ -157,7 +150,7 @@ class UserDAO:
|
||||
username=username,
|
||||
first_name=first_name,
|
||||
last_name=last_name,
|
||||
name=name if not isinstance(name, _UNSET) else None,
|
||||
group=group if not isinstance(group, _UNSET) else None,
|
||||
is_admin=is_admin if not isinstance(is_admin, _UNSET) else False,
|
||||
name=name,
|
||||
group=group,
|
||||
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)
|
||||
|
||||
|
||||
def now_utc_naive() -> datetime:
|
||||
return datetime.utcnow()
|
||||
|
||||
|
||||
def to_msk(dt: datetime | None) -> datetime | None:
|
||||
if dt is None:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user