Dmitry Afanasyev 7ef8d6e19d
add style check by ruff (#34)
* reformat settings

* add init tests for timed lru cache

* add check style by ruff
2023-10-11 22:38:46 +03:00

156 lines
4.2 KiB
Python

from functools import cache, cached_property
from os import environ
from pathlib import Path
from typing import Any
from dotenv import load_dotenv
from pydantic import model_validator
from pydantic_settings import BaseSettings
from yarl import URL
from constants import API_PREFIX, CHATGPT_BASE_URI
BASE_DIR = Path(__file__).parent.parent
SHARED_DIR = BASE_DIR.resolve().joinpath("shared")
SHARED_DIR.mkdir(exist_ok=True)
SHARED_DIR.joinpath("logs").mkdir(exist_ok=True)
DIR_LOGS = SHARED_DIR.joinpath("logs")
env_path = f"{BASE_DIR}/settings/.env"
if environ.get("STAGE") == "runtests":
if "LOCALTEST" in environ:
env_path = f"{BASE_DIR}/settings/.env.local.runtests"
else:
env_path = f"{BASE_DIR}/settings/.env.ci.runtests"
load_dotenv(env_path, override=True)
class SentrySettings(BaseSettings):
ENABLE_SENTRY: bool = False
SENTRY_DSN: str | None = None
DEPLOY_ENVIRONMENT: str | None = None
SENTRY_TRACES_SAMPLE_RATE: float = 0.95
@model_validator(mode="after")
def validate_sentry_enabled(self) -> "SentrySettings":
if self.ENABLE_SENTRY and not self.SENTRY_DSN:
raise RuntimeError("sentry dsn must be set")
return self
class LoggingSettings(BaseSettings):
ENABLE_JSON_LOGS: bool = True
ENABLE_SENTRY_LOGS: bool = False
ENABLE_GRAYLOG: bool = False
GRAYLOG_HOST: str | None = None
GRAYLOG_PORT: int | None = None
LOG_TO_FILE: str | None = None
@model_validator(mode="after")
def validate_graylog_enabled(self) -> "LoggingSettings":
if self.ENABLE_GRAYLOG and not all([self.GRAYLOG_HOST, self.GRAYLOG_PORT]):
raise RuntimeError("graylog host and port must be set")
return self
class AppSettings(SentrySettings, LoggingSettings, BaseSettings):
"""Application settings."""
PROJECT_NAME: str = "chat gpt bot"
APP_HOST: str = "0.0.0.0"
APP_PORT: int = 8000
STAGE: str = "dev"
DEBUG: bool = False
# quantity of workers for uvicorn
WORKERS_COUNT: int = 1
# Enable uvicorn reloading
RELOAD: bool = False
# telegram settings
TELEGRAM_API_TOKEN: str = "123456789:AABBCCDDEEFFaabbccddeeff-1234567890"
START_WITH_WEBHOOK: bool = False
# domain settings
DOMAIN: str = "https://localhost"
URL_PREFIX: str = ""
CHAT_PREFIX: str = ""
DB_NAME: str = "chatgpt.db"
DB_ECHO: bool = False
# ==== gpt settings ====
GPT_BASE_HOST: str = "http://chathpt_chat_service:8858"
@model_validator(mode="before") # type: ignore[arg-type]
def validate_boolean_fields(self) -> Any:
values_dict: dict[str, Any] = self # type: ignore[assignment]
for value in (
"ENABLE_JSON_LOGS",
"ENABLE_SENTRY_LOGS",
"START_WITH_WEBHOOK",
"RELOAD",
"DEBUG",
"ENABLE_GRAYLOG",
"ENABLE_SENTRY",
):
setting_value: str | None = values_dict.get(value)
if setting_value and setting_value.lower() == "false":
values_dict[value] = False
return values_dict
@cached_property
def api_prefix(self) -> str:
if self.URL_PREFIX:
return "/" + "/".join([self.URL_PREFIX.strip("/"), API_PREFIX.strip("/")])
return API_PREFIX
@cached_property
def chat_prefix(self) -> str:
return self.URL_PREFIX + self.CHAT_PREFIX
@cached_property
def chatgpt_backend_url(self) -> str:
return self.chat_prefix + CHATGPT_BASE_URI
@cached_property
def token_part(self) -> str:
return self.TELEGRAM_API_TOKEN[15:30]
@cached_property
def bot_webhook_url(self) -> str:
return "/".join([self.api_prefix, self.token_part])
@cached_property
def db_file(self) -> Path:
return SHARED_DIR / self.DB_NAME
@cached_property
def async_db_url(self) -> URL:
return URL.build(
scheme="sqlite+aiosqlite",
path=f"///{self.db_file}",
)
@cached_property
def sync_db_url(self) -> URL:
return URL.build(
scheme="sqlite",
path=f"///{self.db_file}",
)
class Config:
case_sensitive = True
@cache
def get_settings() -> AppSettings:
return AppSettings()
settings = get_settings()