Implement user-specific question numbering and update database schema. Added triggers for automatic question numbering and adjustments upon deletion. Enhanced CRUD operations to manage user_question_number effectively.

This commit is contained in:
2025-09-06 18:35:12 +03:00
parent 50be010026
commit 596a2fa813
111 changed files with 16847 additions and 65 deletions

92
models/user.py Normal file
View File

@@ -0,0 +1,92 @@
"""
Модель пользователя
"""
import asyncio
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from config.constants import EMPTY_VALUES
def escape_html(text: str) -> str:
"""Экранирование HTML символов"""
if not text:
return ""
return (text
.replace('&', '&')
.replace('<', '&lt;')
.replace('>', '&gt;')
.replace('"', '&quot;')
.replace("'", '&#x27;'))
@dataclass
class User:
"""Модель пользователя бота"""
id: Optional[int] = None
telegram_id: int = None
username: Optional[str] = None
first_name: str = ""
last_name: Optional[str] = None
chat_id: int = None
profile_link: str = ""
is_active: bool = True
is_superuser: bool = False
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
banned_until: Optional[datetime] = None
ban_reason: Optional[str] = None
@property
def full_name(self) -> str:
"""Полное имя пользователя"""
parts = []
if self.first_name:
parts.append(escape_html(self.first_name))
if self.last_name:
parts.append(escape_html(self.last_name))
return ' '.join(parts) if parts else 'Неизвестно'
@property
def display_name(self) -> str:
"""Отображаемое имя пользователя"""
if self.username:
return f"@{escape_html(self.username)}"
return escape_html(self.full_name)
@property
def is_banned(self) -> bool:
"""Проверка, забанен ли пользователь"""
if not self.banned_until:
return False
return datetime.now() < self.banned_until
@classmethod
def _parse_datetime(cls, date_str) -> Optional[datetime]:
"""Безопасный парсинг datetime из строки"""
if not date_str or date_str in EMPTY_VALUES:
return None
try:
return datetime.fromisoformat(date_str)
except (ValueError, TypeError):
return None
@classmethod
async def _parse_datetime_async(cls, date_str) -> Optional[datetime]:
"""Асинхронный безопасный парсинг datetime из строки"""
if not date_str or date_str in EMPTY_VALUES:
return None
try:
loop = asyncio.get_event_loop()
return await loop.run_in_executor(None, datetime.fromisoformat, date_str)
except (ValueError, TypeError):
return None
def update_timestamp(self):
"""Обновление времени последнего обновления"""
self.updated_at = datetime.now()