refactor: global codebase refactoring
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
@@ -1,4 +1,4 @@
|
|||||||
# Custom evnironment files
|
# Custom environment files
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Password files
|
# Password files
|
||||||
|
|||||||
@@ -8,15 +8,16 @@ DJANGO_LANGUAGE_CODE=en-us
|
|||||||
DJANGO_STATIC_URL=http://localhost:13241/
|
DJANGO_STATIC_URL=http://localhost:13241/
|
||||||
REDIS_URI=redis://redis:6379
|
REDIS_URI=redis://redis:6379
|
||||||
DJANGO_DB_URI=postgresql://postgres:postgres@postgres/postgres
|
DJANGO_DB_URI=postgresql://postgres:postgres@postgres/postgres
|
||||||
|
|
||||||
DJANGO_CREATE_SUPERUSER=True
|
DJANGO_CREATE_SUPERUSER=True
|
||||||
DJANGO_SUPERUSER_USERNAME=admin
|
DJANGO_SUPERUSER_USERNAME=admin
|
||||||
DJANGO_SUPERUSER_EMAIL=admin@mail.com
|
DJANGO_SUPERUSER_EMAIL=admin@mail.com
|
||||||
DJANGO_SUPERUSER_PASSWORD=admin
|
DJANGO_SUPERUSER_PASSWORD=admin
|
||||||
|
|
||||||
YANDEX_CLOUD_FOLDER_ID=b1grd7nb04jbfvgm9121
|
YANDEX_CLOUD_FOLDER_ID=b1grd7nb04jbfvgm9121
|
||||||
YANDEX_CLOUD_API_KEY=AQVN19lztDgUcBrHeW0HtJ0bbDGda6i6e3InXsDl
|
YANDEX_CLOUD_API_KEY=AQVN19lztDgUcBrHeW0HtJ0bbDGda6i6e3InXsDl
|
||||||
|
|
||||||
MINIO_ENDPOINT=minio:9000
|
MINIO_ENDPOINT=minio:9000
|
||||||
MINIO_CUSTOM_ENDPOINT_URL=http://127.0.0.1:13244
|
MINIO_CUSTOM_ENDPOINT_URL=http://127.0.0.1:13244
|
||||||
MINIO_ACCESS_KEY=admin
|
MINIO_ACCESS_KEY=admin
|
||||||
MINIO_SECRET_KEY=password
|
MINIO_SECRET_KEY=password
|
||||||
DJANGO_NOTIFIER_TELEGRAM_BOT_TOKEN=6196898691:AAFucgj7ieEuYMvWG_MZAn0Ao3UBuHpvaVY
|
|
||||||
DJANGO_NOTIFIER_TELEGRAM_CHAT_ID=-1002304409222
|
|
||||||
|
|||||||
@@ -31,12 +31,3 @@ DJANGO_CREATE_SUPERUSER=False
|
|||||||
DJANGO_SUPERUSER_USERNAME=
|
DJANGO_SUPERUSER_USERNAME=
|
||||||
DJANGO_SUPERUSER_EMAIL=
|
DJANGO_SUPERUSER_EMAIL=
|
||||||
DJANGO_SUPERUSER_PASSWORD=
|
DJANGO_SUPERUSER_PASSWORD=
|
||||||
|
|
||||||
|
|
||||||
# Notifiers settings (only with DJANGO_DEBUG=False)
|
|
||||||
|
|
||||||
# Telegram
|
|
||||||
|
|
||||||
DJANGO_NOTIFIER_TELEGRAM_BOT_TOKEN=
|
|
||||||
DJANGO_NOTIFIER_TELEGRAM_CHAT_ID=
|
|
||||||
DJANGO_NOTIFIER_TELEGRAM_THREAD_ID=
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ COPY . .
|
|||||||
|
|
||||||
RUN uv run python manage.py collectstatic --noinput
|
RUN uv run python manage.py collectstatic --noinput
|
||||||
|
|
||||||
# Stage 2: Start nginx to serve staticfiles
|
# Stage 2: Start nginx and serve staticfiles
|
||||||
FROM docker.io/nginx:latest
|
FROM docker.io/nginx:latest
|
||||||
|
|
||||||
COPY --from=builder /app/static /usr/share/nginx/html
|
COPY --from=builder /app/static /usr/share/nginx/html
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
from http import HTTPStatus as status
|
from http import HTTPStatus as status
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
|
||||||
import json
|
import json
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from apps.client.models import Client
|
from apps.client.models import Client
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ class AdvertiserModelTest(TestCase):
|
|||||||
|
|
||||||
new_id = uuid4()
|
new_id = uuid4()
|
||||||
self.advertiser.advertiser_id = new_id
|
self.advertiser.advertiser_id = new_id
|
||||||
|
|
||||||
self.assertEqual(self.advertiser.id, new_id)
|
self.assertEqual(self.advertiser.id, new_id)
|
||||||
|
|
||||||
@override_settings(
|
@override_settings(
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from uuid import UUID
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import ValidationError
|
|
||||||
from django.core.validators import (
|
from django.core.validators import (
|
||||||
MaxValueValidator,
|
MaxValueValidator,
|
||||||
MinLengthValidator,
|
MinLengthValidator,
|
||||||
@@ -20,6 +19,7 @@ from apps.campaign.validators import (
|
|||||||
CampaignDurationValidator,
|
CampaignDurationValidator,
|
||||||
CampaignLimitsValidator,
|
CampaignLimitsValidator,
|
||||||
CampaignReportMessageValidator,
|
CampaignReportMessageValidator,
|
||||||
|
CampaignStartDateValidator,
|
||||||
CampaignTargetingLocationValidator,
|
CampaignTargetingLocationValidator,
|
||||||
)
|
)
|
||||||
from apps.client.models import Client
|
from apps.client.models import Client
|
||||||
@@ -100,21 +100,7 @@ class Campaign(BaseModel):
|
|||||||
CampaignAgeValidator()(self)
|
CampaignAgeValidator()(self)
|
||||||
CampaignDurationValidator()(self)
|
CampaignDurationValidator()(self)
|
||||||
CampaignLimitsValidator()(self)
|
CampaignLimitsValidator()(self)
|
||||||
|
CampaignStartDateValidator()(self)
|
||||||
current_date = cache.get("current_date", default=0)
|
|
||||||
|
|
||||||
err = "start_date must be greater than the current date."
|
|
||||||
|
|
||||||
try:
|
|
||||||
original = Campaign.objects.get(id=self.id or "")
|
|
||||||
if (
|
|
||||||
original.start_date != self.start_date
|
|
||||||
and self.start_date < current_date
|
|
||||||
):
|
|
||||||
raise ValidationError(err)
|
|
||||||
except Campaign.DoesNotExist:
|
|
||||||
if self.start_date < current_date:
|
|
||||||
raise ValidationError(err) from None
|
|
||||||
|
|
||||||
def save(self, *args: Any, **kwargs: Any) -> None:
|
def save(self, *args: Any, **kwargs: Any) -> None:
|
||||||
created = self.pk is None
|
created = self.pk is None
|
||||||
@@ -134,6 +120,20 @@ class Campaign(BaseModel):
|
|||||||
)
|
)
|
||||||
cache.set(f"campaign_{self.id}_clicks_count", self.clicks.count())
|
cache.set(f"campaign_{self.id}_clicks_count", self.clicks.count())
|
||||||
|
|
||||||
|
def inc_views(self) -> None:
|
||||||
|
try:
|
||||||
|
cache.incr(f"campaign_{self.id}_impressions_count", 1)
|
||||||
|
except ValueError:
|
||||||
|
self.setup_cache()
|
||||||
|
logger.warning("Seems that %s missing caches", self.campaign_id)
|
||||||
|
|
||||||
|
def inc_clicks(self) -> None:
|
||||||
|
try:
|
||||||
|
cache.incr(f"campaign_{self.id}_clicks_count", 1)
|
||||||
|
except ValueError:
|
||||||
|
self.setup_cache()
|
||||||
|
logger.warning("Seems that %s missing caches", self.campaign_id)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ad_id(self) -> UUID:
|
def ad_id(self) -> UUID:
|
||||||
return self.id
|
return self.id
|
||||||
@@ -175,13 +175,7 @@ class Campaign(BaseModel):
|
|||||||
price=self.cost_per_impression,
|
price=self.cost_per_impression,
|
||||||
date=cache.get("current_date", default=0),
|
date=cache.get("current_date", default=0),
|
||||||
)
|
)
|
||||||
try:
|
self.inc_views()
|
||||||
cache.incr(f"campaign_{self.id}_impressions_count", 1)
|
|
||||||
except ValueError:
|
|
||||||
self.setup_cache()
|
|
||||||
logger.warning(
|
|
||||||
"Seems that %s missing caches", self.campaign_id
|
|
||||||
)
|
|
||||||
except ConflictError:
|
except ConflictError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -198,13 +192,7 @@ class Campaign(BaseModel):
|
|||||||
price=self.cost_per_click,
|
price=self.cost_per_click,
|
||||||
date=cache.get("current_date", default=0),
|
date=cache.get("current_date", default=0),
|
||||||
)
|
)
|
||||||
try:
|
self.inc_clicks()
|
||||||
cache.incr(f"campaign_{self.id}_clicks_count", 1)
|
|
||||||
except ValueError:
|
|
||||||
self.setup_cache()
|
|
||||||
logger.warning(
|
|
||||||
"Seems that %s missing caches", self.campaign_id
|
|
||||||
)
|
|
||||||
except ConflictError:
|
except ConflictError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -235,7 +223,6 @@ class Campaign(BaseModel):
|
|||||||
total=models.Count("id"),
|
total=models.Count("id"),
|
||||||
spent=models.Sum("price", default=0.0),
|
spent=models.Sum("price", default=0.0),
|
||||||
)
|
)
|
||||||
|
|
||||||
clicks = self.clicks.values("date").annotate(
|
clicks = self.clicks.values("date").annotate(
|
||||||
total=models.Count("id"),
|
total=models.Count("id"),
|
||||||
spent=models.Sum("price", default=0.0),
|
spent=models.Sum("price", default=0.0),
|
||||||
|
|||||||
@@ -1,48 +1,83 @@
|
|||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.db.models.fields import Field
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from apps.campaign.models import Campaign, CampaignReport
|
||||||
|
|
||||||
|
|
||||||
|
class CampaignTargetingLocationValidator:
|
||||||
|
def __call__(self, instance: "Campaign") -> None:
|
||||||
|
if instance.location == "":
|
||||||
|
err = {
|
||||||
|
"targeting": {
|
||||||
|
type(
|
||||||
|
instance
|
||||||
|
).location.field.name: Field.default_error_messages[
|
||||||
|
"blank"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raise ValidationError(err)
|
||||||
|
|
||||||
|
|
||||||
class CampaignAgeValidator:
|
class CampaignAgeValidator:
|
||||||
def __call__(self, instance) -> None: # noqa: ANN001
|
def __call__(self, instance: "Campaign") -> None:
|
||||||
if (
|
if (
|
||||||
isinstance(instance.age_from, int)
|
isinstance(instance.age_from, int)
|
||||||
and isinstance(instance.age_to, int)
|
and isinstance(instance.age_to, int)
|
||||||
and instance.age_from > instance.age_to
|
and instance.age_from > instance.age_to
|
||||||
):
|
):
|
||||||
err = "age_from can't be greater than age_to"
|
err = "targeting.age_from can't be greater than targeting.age_to."
|
||||||
raise ValidationError(err)
|
raise ValidationError(err)
|
||||||
|
|
||||||
|
|
||||||
class CampaignDurationValidator:
|
class CampaignDurationValidator:
|
||||||
def __call__(self, instance) -> None: # noqa: ANN001
|
def __call__(self, instance: "Campaign") -> None:
|
||||||
if (
|
if (
|
||||||
isinstance(instance.start_date, int)
|
isinstance(instance.start_date, int)
|
||||||
and isinstance(instance.end_date, int)
|
and isinstance(instance.end_date, int)
|
||||||
and instance.start_date > instance.end_date
|
and instance.start_date > instance.end_date
|
||||||
):
|
):
|
||||||
err = "start_date can't be greater than end_date"
|
err = "start_date can't be greater than end_date."
|
||||||
raise ValidationError(err)
|
raise ValidationError(err)
|
||||||
|
|
||||||
|
|
||||||
class CampaignLimitsValidator:
|
class CampaignLimitsValidator:
|
||||||
def __call__(self, instance) -> None: # noqa: ANN001
|
def __call__(self, instance: "Campaign") -> None:
|
||||||
if (
|
if (
|
||||||
isinstance(instance.impressions_limit, int)
|
isinstance(instance.impressions_limit, int)
|
||||||
and isinstance(instance.clicks_limit, int)
|
and isinstance(instance.clicks_limit, int)
|
||||||
and instance.impressions_limit < instance.clicks_limit
|
and instance.impressions_limit < instance.clicks_limit
|
||||||
):
|
):
|
||||||
err = "clicks_limit can't be greater than impressions_limit"
|
err = "clicks_limit can't be greater than impressions_limit."
|
||||||
raise ValidationError(err)
|
raise ValidationError(err)
|
||||||
|
|
||||||
|
|
||||||
class CampaignTargetingLocationValidator:
|
class CampaignStartDateValidator:
|
||||||
def __call__(self, instance) -> None: # noqa: ANN001
|
def __call__(self, instance: "Campaign") -> None:
|
||||||
if instance.location == "":
|
current_date = cache.get("current_date", default=0)
|
||||||
err = "targeting.location cannot be blank"
|
err = "start_date must be greater or equal than the current_date."
|
||||||
raise ValidationError(err)
|
try:
|
||||||
|
original = type(instance).objects.get(id=instance.id or "")
|
||||||
|
if (
|
||||||
|
original.start_date != instance.start_date
|
||||||
|
and instance.start_date < current_date
|
||||||
|
):
|
||||||
|
raise ValidationError(err)
|
||||||
|
except type(instance).DoesNotExist:
|
||||||
|
if instance.start_date < current_date:
|
||||||
|
raise ValidationError(err) from None
|
||||||
|
|
||||||
|
|
||||||
class CampaignReportMessageValidator:
|
class CampaignReportMessageValidator:
|
||||||
def __call__(self, instance) -> None: # noqa: ANN001
|
def __call__(self, instance: "CampaignReport") -> None:
|
||||||
if instance.message == "":
|
if instance.message == "":
|
||||||
err = "message cannot be blank"
|
err = {
|
||||||
|
instance.message.field.name: Field.default_error_messages[
|
||||||
|
"blank"
|
||||||
|
]
|
||||||
|
}
|
||||||
raise ValidationError(err)
|
raise ValidationError(err)
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
import datetime
|
|
||||||
import logging
|
|
||||||
import time
|
|
||||||
import traceback
|
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
from django.utils.timezone import get_current_timezone
|
|
||||||
|
|
||||||
TELEGRAM_LOG_HANDLER = logging.getLogger("telegram_log_handler")
|
|
||||||
|
|
||||||
LEVEL_EMOJIS = {
|
|
||||||
"DEBUG": "🐞",
|
|
||||||
"INFO": "ℹ️",
|
|
||||||
"WARNING": "⚠️",
|
|
||||||
"ERROR": "❌",
|
|
||||||
"CRITICAL": "🚨",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class LoggingHandler(logging.Handler):
|
|
||||||
_executor = ThreadPoolExecutor(max_workers=5)
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
token: str,
|
|
||||||
chat_id: int,
|
|
||||||
thread_id: int | None = None,
|
|
||||||
retries: int | None = 3,
|
|
||||||
delay: int | None = 2,
|
|
||||||
timeout: int | None = 5,
|
|
||||||
) -> None:
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
self.token = token
|
|
||||||
self.chat_id = chat_id
|
|
||||||
self.thread_id = thread_id
|
|
||||||
self.retries = retries
|
|
||||||
self.delay = delay
|
|
||||||
self.timeout = timeout
|
|
||||||
self.api_url = f"https://api.telegram.org/bot{self.token}/sendMessage"
|
|
||||||
|
|
||||||
self.template = (
|
|
||||||
"<b>{levelname}</b>\n"
|
|
||||||
"\t<b>Guid:</b> <code>{correlation_id}</code>\n"
|
|
||||||
"\t<b>Timestamp:</b> <code>{asctime}</code>\n"
|
|
||||||
"\t<b>Logger:</b> <code>{name}</code>\n"
|
|
||||||
"\t<b>File:</b> <code>{pathname}</code> "
|
|
||||||
"(Line: <code>{lineno}</code>)\n\n"
|
|
||||||
'<pre><code class="language-message">{message}</code></pre>\n'
|
|
||||||
)
|
|
||||||
|
|
||||||
def emit(self, record: logging.LogRecord) -> None:
|
|
||||||
try:
|
|
||||||
formatted_record = self.format(record)
|
|
||||||
self._executor.submit(self._send_message, formatted_record)
|
|
||||||
except Exception as e: # noqa: BLE001
|
|
||||||
self.handleError(record)
|
|
||||||
TELEGRAM_LOG_HANDLER.exception(e)
|
|
||||||
|
|
||||||
def _send_message(self, formatted_record: str) -> None:
|
|
||||||
payload = {
|
|
||||||
"chat_id": self.chat_id,
|
|
||||||
"text": formatted_record,
|
|
||||||
"parse_mode": "HTML",
|
|
||||||
}
|
|
||||||
if self.thread_id:
|
|
||||||
payload["reply_to_message_id"] = self.thread_id
|
|
||||||
|
|
||||||
for attempt in range(1, self.retries + 1):
|
|
||||||
response = httpx.post(
|
|
||||||
self.api_url,
|
|
||||||
data=payload,
|
|
||||||
timeout=self.timeout,
|
|
||||||
)
|
|
||||||
if response.status_code != httpx.codes.OK:
|
|
||||||
if attempt == self.retries:
|
|
||||||
TELEGRAM_LOG_HANDLER.exception(
|
|
||||||
"Failed to send to Telegram after %d attempts: %s",
|
|
||||||
self.retries,
|
|
||||||
response.text,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
time.sleep(self.delay)
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
def format(self, record: logging.LogRecord) -> str:
|
|
||||||
try:
|
|
||||||
asctime = datetime.datetime.fromtimestamp(
|
|
||||||
record.created,
|
|
||||||
tz=get_current_timezone(),
|
|
||||||
).strftime("%Y-%m-%d %H:%M:%S %Z")
|
|
||||||
level_emoji = LEVEL_EMOJIS.get(record.levelname, "")
|
|
||||||
|
|
||||||
formatted_message = self.template.format(
|
|
||||||
levelname=f"{level_emoji} {record.levelname}",
|
|
||||||
correlation_id=getattr(record, "correlation_id", "N/A"),
|
|
||||||
asctime=asctime,
|
|
||||||
name=record.name,
|
|
||||||
pathname=record.pathname,
|
|
||||||
lineno=record.lineno,
|
|
||||||
message=record.getMessage(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if record.exc_info:
|
|
||||||
formatted_message += self._format_exception(record.exc_info)
|
|
||||||
|
|
||||||
formatted_message += (
|
|
||||||
f"\n#{record.levelname.lower()} "
|
|
||||||
f"#{record.name.replace('.', '_')}"
|
|
||||||
)
|
|
||||||
if hasattr(record, "correlation_id"):
|
|
||||||
formatted_message += f" #{record.correlation_id}"
|
|
||||||
except Exception as format_error: # noqa: BLE001
|
|
||||||
TELEGRAM_LOG_HANDLER.exception(
|
|
||||||
"Error formatting log record: %s",
|
|
||||||
format_error,
|
|
||||||
)
|
|
||||||
return f"Error formatting log record: {format_error}"
|
|
||||||
else:
|
|
||||||
return formatted_message
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _format_exception(exc_info: Exception) -> str:
|
|
||||||
exc_text = "".join(traceback.format_exception(*exc_info))
|
|
||||||
return (
|
|
||||||
f"\n<pre><code class='language-traceback'>{exc_text}</code></pre>"
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def shutdown_executor(cls) -> None:
|
|
||||||
cls._executor.shutdown(wait=True)
|
|
||||||
@@ -41,7 +41,8 @@ YANDEX_CLOUD_INTEGRATION_ENABLED = (
|
|||||||
YANDEX_CLOUD_FOLDER_ID and YANDEX_CLOUD_API_KEY
|
YANDEX_CLOUD_FOLDER_ID and YANDEX_CLOUD_API_KEY
|
||||||
)
|
)
|
||||||
|
|
||||||
# Register healthcheck
|
|
||||||
|
# Register healthchecks
|
||||||
|
|
||||||
plugin_dir.register(YandexAIHealthCheck)
|
plugin_dir.register(YandexAIHealthCheck)
|
||||||
|
|
||||||
@@ -300,26 +301,6 @@ USE_X_FORWARDED_PORT = False
|
|||||||
WSGI_APPLICATION = "config.wsgi.application"
|
WSGI_APPLICATION = "config.wsgi.application"
|
||||||
|
|
||||||
|
|
||||||
# Notifiers
|
|
||||||
|
|
||||||
# Telegram
|
|
||||||
|
|
||||||
NOTIFIER_TELEGRAM_BOT_TOKEN = env(
|
|
||||||
"DJANGO_NOTIFIER_TELEGRAM_BOT_TOKEN",
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
NOTIFIER_TELEGRAM_CHAT_ID = env(
|
|
||||||
"DJANGO_NOTIFIER_TELEGRAM_CHAT_ID",
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
NOTIFIER_TELEGRAM_THREAD_ID = env(
|
|
||||||
"DJANGO_NOTIFIER_TELEGRAM_THREAD_ID",
|
|
||||||
default=None,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
|
|
||||||
LOGGER_NAME = "adnova"
|
LOGGER_NAME = "adnova"
|
||||||
@@ -435,24 +416,6 @@ LOGGING = {
|
|||||||
LOGGING_CONFIG = "logging.config.dictConfig"
|
LOGGING_CONFIG = "logging.config.dictConfig"
|
||||||
|
|
||||||
|
|
||||||
if NOTIFIER_TELEGRAM_BOT_TOKEN and NOTIFIER_TELEGRAM_CHAT_ID:
|
|
||||||
LOGGING_HANDLERS["telegram"] = {
|
|
||||||
"class": "config.notifiers.telegram.LoggingHandler",
|
|
||||||
"level": "INFO",
|
|
||||||
"filters": ["require_debug_false"],
|
|
||||||
"token": NOTIFIER_TELEGRAM_BOT_TOKEN,
|
|
||||||
"chat_id": NOTIFIER_TELEGRAM_CHAT_ID,
|
|
||||||
"thread_id": NOTIFIER_TELEGRAM_THREAD_ID,
|
|
||||||
"retries": 5,
|
|
||||||
"delay": 2,
|
|
||||||
"timeout": 5,
|
|
||||||
}
|
|
||||||
LOGGING_LOGGERS["django"]["handlers"].append("telegram")
|
|
||||||
LOGGING_LOGGERS["django.request"]["handlers"].append("telegram")
|
|
||||||
LOGGING_LOGGERS["health-check"]["handlers"].append("telegram")
|
|
||||||
LOGGING_LOGGERS[LOGGER_NAME]["handlers"].append("telegram")
|
|
||||||
|
|
||||||
|
|
||||||
# Models
|
# Models
|
||||||
|
|
||||||
ABSOLUTE_URL_OVERRIDES: dict[str, Callable] = {}
|
ABSOLUTE_URL_OVERRIDES: dict[str, Callable] = {}
|
||||||
|
|||||||
@@ -16,8 +16,10 @@ class YandexAIHealthCheck(BaseHealthCheckBackend):
|
|||||||
result = sdk.models.completions(
|
result = sdk.models.completions(
|
||||||
"yandexgpt-lite", model_version="latest"
|
"yandexgpt-lite", model_version="latest"
|
||||||
).tokenize("ping")
|
).tokenize("ping")
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
self.add_error("YandexAI API is unaccessible")
|
self.add_error("YandexAI API is unaccessible")
|
||||||
|
|
||||||
except YCloudMLError:
|
except YCloudMLError:
|
||||||
self.add_error("YandexAI API is unaccessible")
|
self.add_error("YandexAI API is unaccessible")
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# AdNova Tests
|
# AdNova Tests
|
||||||
|
|
||||||
There is `unit` tests and `e2e` tests available, unit tests are placed all around `backend` serivce folder and `e2e` tests placed [here](./e2e/)
|
There is `unit` and `e2e` tests available, unit tests are placed all around `backend` serivce folder and `e2e` tests placed [here](./e2e/)
|
||||||
|
|
||||||
## Running unit tests
|
## Running unit tests
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ See [services/backend/README.md](../services/backend/README.md#testing)
|
|||||||
|
|
||||||
### Backend service
|
### Backend service
|
||||||
|
|
||||||
-image-
|

|
||||||
|
|
||||||
## Running e2e tests
|
## Running e2e tests
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ cd devitq/solution/tests/e2e
|
|||||||
uv sync --no-dev
|
uv sync --no-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
## Customize environment
|
## Customize environment (optional)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.template .env
|
cp .env.template .env
|
||||||
|
|||||||
@@ -107,4 +107,3 @@ unfixable = []
|
|||||||
|
|
||||||
[tool.ruff.lint.pylint]
|
[tool.ruff.lint.pylint]
|
||||||
max-args = 6
|
max-args = 6
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,15 @@ logging.basicConfig(level=logging.INFO)
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# Tests integration between: backend, redis, yandexgpt and celery
|
|
||||||
def test_generate_ad_text(client: Client) -> None:
|
def test_generate_ad_text(client: Client) -> None:
|
||||||
|
"""
|
||||||
|
Tests integration between:
|
||||||
|
- backend
|
||||||
|
- redis
|
||||||
|
- yandexgpt
|
||||||
|
- celery
|
||||||
|
"""
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"advertiser_name": "Центральный Университет",
|
"advertiser_name": "Центральный Университет",
|
||||||
"ad_title": "Всероссийский кейс-чемпионат DEADLINE",
|
"ad_title": "Всероссийский кейс-чемпионат DEADLINE",
|
||||||
@@ -25,10 +32,7 @@ def test_generate_ad_text(client: Client) -> None:
|
|||||||
|
|
||||||
while True:
|
while True:
|
||||||
result_response = client.get(f"/generate/ad_text/{task_id}/result")
|
result_response = client.get(f"/generate/ad_text/{task_id}/result")
|
||||||
assert (
|
assert result_response.status_code in (status.OK, status.NOT_FOUND)
|
||||||
result_response.status_code == status.OK
|
|
||||||
or result_response.status_code == status.NOT_FOUND
|
|
||||||
)
|
|
||||||
result_data = result_response.json()
|
result_data = result_response.json()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
def test_healthcheck(client: Client) -> None:
|
def test_healthcheck(client: Client) -> None:
|
||||||
"""
|
"""
|
||||||
Checks that backend can use theese services:
|
Tests integration between:
|
||||||
- redis
|
- redis
|
||||||
- celery
|
- celery
|
||||||
- postgres
|
- postgres
|
||||||
|
|||||||
Reference in New Issue
Block a user