Migrating to Postgres from sqlite3

This commit is contained in:
machka
2025-05-20 18:41:32 +00:00
parent ac2f83c061
commit a56d804b58
38 changed files with 5017 additions and 4389 deletions
+66 -69
View File
@@ -1,13 +1,13 @@
import logging
import json
import os
import aiosqlite
from typing import Any, Awaitable, Callable, Dict, Optional
from aiogram import BaseMiddleware
from aiogram.types import TelegramObject, User, Update
from aiogram.types import User, Update
from sqlalchemy.ext.asyncio import AsyncSession
from db.database import get_user, get_db_connection_manager, _setup_db_connection
from db.dal import user_dal
from config.settings import Settings
@@ -25,7 +25,8 @@ class JsonI18n:
def _load_locales(self):
if not os.path.isdir(self.path):
logging.error(f"Locales path not found: {self.path}")
logging.error(
f"Locales path not found or not a directory: {self.path}")
return
for item in os.listdir(self.path):
if item.endswith(".json"):
@@ -34,52 +35,73 @@ class JsonI18n:
try:
with open(file_path, "r", encoding="utf-8") as f:
self.locales_data[lang_code] = json.load(f)
except Exception as e:
except json.JSONDecodeError as e_json_load:
logging.error(
f"Error loading locale {lang_code} from {file_path}: {e}"
f"Error loading locale {lang_code} from {file_path} (JSON Decode Error): {e_json_load}"
)
except Exception as e_load:
logging.error(
f"Error loading locale {lang_code} from {file_path}: {e_load}",
exc_info=True)
def gettext(self, lang_code: Optional[str], key: str, **kwargs) -> str:
effective_lang_code = lang_code
if not effective_lang_code or effective_lang_code not in self.locales_data:
effective_lang_code = self.default_lang
effective_lang_code = lang_code if lang_code and lang_code in self.locales_data else self.default_lang
lang_data = self.locales_data.get(effective_lang_code)
if lang_data is None:
logging.warning(
f"No language data for '{effective_lang_code}' (default: '{self.default_lang}'). Key '{key}'."
f"No language data for '{effective_lang_code}' (default '{self.default_lang}' also missing). Key '{key}' will be returned as is."
)
return key.format(**kwargs) if kwargs else key
text = lang_data.get(key)
if text is None:
if effective_lang_code != self.default_lang:
default_lang_data = self.locales_data.get(
self.default_lang, {})
text = default_lang_data.get(key)
if text is None: return key.format(**kwargs) if kwargs else key
if text is None:
logging.warning(
f"Translation key '{key}' not found for lang '{effective_lang_code}' or default '{self.default_lang}'. Returning key."
)
return key.format(**kwargs) if kwargs else key
try:
return text.format(**kwargs) if kwargs else text
except KeyError as e:
except KeyError as e_format:
logging.warning(
f"Missing format key {e} for key '{key}' (lang: {effective_lang_code}). Text: '{text}'"
f"Missing format key '{e_format}' for i18n key '{key}' (lang: {effective_lang_code}). Original text: '{text}'"
)
return text
except Exception as e:
except Exception as e_general_format:
logging.error(
f"Error formatting key '{key}' (lang: {effective_lang_code}): {e}. Text: '{text}'"
)
f"General error formatting i18n key '{key}' (lang: {effective_lang_code}): {e_general_format}. Original text: '{text}'",
exc_info=True)
return text
_i18n_instance: Optional[JsonI18n] = None
_i18n_instance_singleton: Optional[JsonI18n] = None
def get_i18n_instance(path: str = "locales",
default: str = "en",
domain: str = "bot") -> JsonI18n:
global _i18n_instance
if _i18n_instance is None:
_i18n_instance = JsonI18n(path=path, default=default, domain=domain)
return _i18n_instance
global _i18n_instance_singleton
if _i18n_instance_singleton is None:
if not os.path.exists(path) or not os.path.isdir(path):
logging.error(
f"CRITICAL: Locales directory '{path}' not found. i18n will not work correctly."
)
_i18n_instance_singleton = JsonI18n(path=path,
default=default,
domain=domain)
else:
_i18n_instance_singleton = JsonI18n(path=path,
default=default,
domain=domain)
return _i18n_instance_singleton
class I18nMiddleware(BaseMiddleware):
@@ -92,65 +114,40 @@ class I18nMiddleware(BaseMiddleware):
async def __call__(self, handler: Callable[[Update, Dict[str, Any]],
Awaitable[Any]], event: Update,
data: Dict[str, Any]) -> Any:
session: AsyncSession = data["session"]
event_user: Optional[User] = data.get("event_from_user")
current_language = self.i18n.default_lang
if event_user:
logging.debug(
f"I18nMiddleware: Processing for user {event_user.id}")
try:
user_db_data = await get_user(event_user.id)
if user_db_data and user_db_data[
'language_code'] and user_db_data[
'language_code'] in self.i18n.locales_data:
current_language = user_db_data['language_code']
logging.debug(
f"I18nMiddleware: User {event_user.id} language loaded from DB: {current_language}"
)
else:
if event_user.language_code:
lang_prefix = event_user.language_code.split('-')[0]
if lang_prefix in self.i18n.locales_data:
current_language = lang_prefix
logging.debug(
f"I18nMiddleware: User {event_user.id} language set from Telegram client (prefix): {current_language}"
)
elif event_user.language_code in self.i18n.locales_data:
current_language = event_user.language_code
logging.debug(
f"I18nMiddleware: User {event_user.id} language set from Telegram client (full): {current_language}"
)
else:
logging.debug(
f"I18nMiddleware: User {event_user.id} Telegram client language '{event_user.language_code}' not supported. Using default: {current_language}"
)
else:
logging.debug(
f"I18nMiddleware: User {event_user.id} has no language_code from Telegram. Using default: {current_language}"
)
except Exception as e_db_lang:
logging.error(
f"I18nMiddleware: Error fetching user language from DB for user {event_user.id}: {e_db_lang}. Falling back."
)
if event_user.language_code:
lang_prefix = event_user.language_code.split('-')[0]
user_db_model = await user_dal.get_user_by_id(
session, event_user.id)
if user_db_model and user_db_model.language_code and user_db_model.language_code in self.i18n.locales_data:
current_language = user_db_model.language_code
elif event_user.language_code:
lang_prefix = event_user.language_code.split(
'-')[0].lower()
if lang_prefix in self.i18n.locales_data:
current_language = lang_prefix
elif event_user.language_code in self.i18n.locales_data:
current_language = event_user.language_code
elif event_user.language_code.lower(
) in self.i18n.locales_data:
current_language = event_user.language_code.lower()
except Exception as e_db_lang:
logging.error(
f"I18nMiddleware: Error fetching user lang from DB for {event_user.id}: {e_db_lang}. Falling back.",
exc_info=True)
if event_user.language_code:
lang_prefix = event_user.language_code.split(
'-')[0].lower()
if lang_prefix in self.i18n.locales_data:
current_language = lang_prefix
elif event_user.language_code.lower(
) in self.i18n.locales_data:
current_language = event_user.language_code.lower()
data["i18n_data"] = {
"i18n_instance": self.i18n,
"current_language": current_language
}
logging.debug(
f"I18nMiddleware: Final current_language for event: {current_language}"
)
return await handler(event, data)