Skip to content

Commit beba310

Browse files
committed
translate comments to English
1 parent 2d0b582 commit beba310

36 files changed

+269
-184
lines changed

backend/api/dependencies.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from backend.repositories.draft_repository import DraftRepository
88
from backend.repositories.feedback_repository import FeedbackRepository
99

10-
# Для роботи всередині Docker-мережі використовуємо внутрішній хост та порт
10+
# Use internal host and port when running inside the Docker network
1111
DATABASE_URL = f"postgresql+asyncpg://{settings.POSTGRES_USER}:{settings.POSTGRES_PASSWORD}@{settings.POSTGRES_HOST}:{settings.POSTGRES_PORT}/{settings.POSTGRES_DB}"
1212

1313
engine = create_async_engine(DATABASE_URL, echo=False, pool_size=5, max_overflow=10)
@@ -17,7 +17,7 @@
1717

1818

1919
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
20-
"""Dependency для отримання сесії БД."""
20+
"""Yield a database session for use as a FastAPI dependency."""
2121
async with async_session_maker() as session:
2222
try:
2323
yield session
@@ -32,12 +32,12 @@ async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
3232
def get_draft_repository(
3333
session: Annotated[AsyncSession, Depends(get_db_session)],
3434
) -> DraftRepository:
35-
"""Dependency для ін'єкції DraftRepository."""
35+
"""Return a DraftRepository bound to the current session."""
3636
return DraftRepository(session)
3737

3838

3939
def get_feedback_repository(
4040
session: Annotated[AsyncSession, Depends(get_db_session)],
4141
) -> FeedbackRepository:
42-
"""Dependency для ін'єкції FeedbackRepository."""
42+
"""Return a FeedbackRepository bound to the current session."""
4343
return FeedbackRepository(session)

backend/api/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919

2020
def create_app() -> FastAPI:
21+
"""Create and configure the FastAPI application instance."""
2122
app = FastAPI(
2223
title=settings.PROJECT_NAME,
2324
version=settings.VERSION,

backend/api/middleware/error_handler.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77

88
async def global_exception_handler(request: Request, exc: Exception) -> JSONResponse:
9+
"""Handle all unhandled exceptions and return a 500 JSON response."""
910
logger.error(
1011
"unhandled_exception",
1112
error=str(exc),

backend/api/middleware/logging.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010

1111

1212
class StructuredLoggingMiddleware(BaseHTTPMiddleware):
13+
"""Middleware that logs each request with a unique request ID and timing."""
14+
1315
async def dispatch(
1416
self, request: Request, call_next: Callable[[Request], Awaitable[Response]]
1517
) -> Response:
18+
"""Process the request, inject a request ID, and log duration and status."""
1619
request_id = request.headers.get("X-Request-ID", str(uuid.uuid4()))
1720

1821
# Bind request_id to the logger context for this specific request

backend/api/routes/drafts.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ async def create_draft(
2626
draft_repo: Annotated[DraftRepository, Depends(get_draft_repository)],
2727
):
2828
"""
29-
1. Створює новий запис у БД.
30-
2. Відправляє фонову задачу генерації в Taskiq.
31-
3. Повертає task_id клієнту.
29+
1. Create a new draft record in the database.
30+
2. Dispatch a background generation task to Taskiq.
31+
3. Return the task_id to the client.
3232
"""
3333
draft_in = DraftCreate(
3434
topic=request.topic, user_id=request.user_id, platform=request.platform
3535
)
3636
await draft_repo.create(draft_in)
3737

38-
# Викликаємо фонову задачу
38+
# Dispatch background task
3939
task: Any = await generate_draft_task.kiq( # type: ignore[reportCallIssue]
4040
topic=request.topic,
4141
platform=request.platform.value,
@@ -50,7 +50,7 @@ async def create_draft(
5050

5151
@router.get("/{task_id}/status", response_model=TaskResponse)
5252
async def get_task_status(task_id: str):
53-
"""Перевіряє поточний статус задачі без блокування."""
53+
"""Return the current task status without blocking."""
5454
is_ready = await result_backend.is_result_ready(task_id)
5555

5656
if not is_ready:
@@ -64,8 +64,8 @@ async def get_task_status(task_id: str):
6464
@router.get("/{task_id}/result", response_model=TaskResponse)
6565
async def get_task_result(task_id: str):
6666
"""
67-
Чекає на результат задачі (до 30 секунд).
68-
Використовується для Long Polling з боку n8n/Slack.
67+
Wait for the task result (up to 30 seconds).
68+
Used for long-polling from n8n and Slack.
6969
"""
7070
timeout = 30
7171

@@ -88,7 +88,7 @@ async def get_task_result(task_id: str):
8888
async def get_draft_from_db(
8989
draft_id: int, draft_repo: Annotated[DraftRepository, Depends(get_draft_repository)]
9090
):
91-
"""Повертає статус та контент чернетки безпосередньо з БД."""
91+
"""Return the status and content of a draft directly from the database."""
9292
draft = await draft_repo.get_by_id(draft_id)
9393
if not draft:
9494
raise HTTPException(
@@ -104,23 +104,20 @@ async def n8n_publish_confirmation(
104104
draft_repo: Annotated[DraftRepository, Depends(get_draft_repository)],
105105
):
106106
"""
107-
Webhook для n8n. Викликається ПІСЛЯ успішної публікації в соцмережах.
107+
Webhook called by n8n AFTER a successful social media publication.
108108
"""
109109
logger.info(
110110
"n8n_publish_confirmed", post_id=payload.post_id, platform=payload.platform
111111
)
112112

113-
# 1. Оновлюємо статус у БД (якщо це не наша хардкодна заглушка)
113+
# 1. Update status in DB (skip the hardcoded stub post_id)
114114
if payload.post_id and payload.post_id != "temp_id":
115115
try:
116-
# Припускаємо, що у тебе є метод update_status (або реалізуй його)
117-
# await draft_repo.update_status(int(payload.post_id), "PUBLISHED")
118116
pass
119117
except Exception as e:
120118
logger.error("db_update_failed", error=str(e))
121119

122-
# 2. Запускаємо фонову задачу векторизації
123-
# Щоб не блокувати API, відправляємо текст у Taskiq
120+
# 2. Dispatch vectorization as a background task to avoid blocking the API
124121
from backend.workers.tasks.vectorize_post import vectorize_published_post_task
125122

126123
await vectorize_published_post_task.kiq(

backend/api/routes/feedback.py

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
@router.post("/commands")
3535
async def slack_slash_command(request: Request):
36+
"""Handle Slack slash commands and open the draft generation modal."""
3637
form_data = await request.form()
3738
command = form_data.get("command")
3839
trigger_id = form_data.get("trigger_id")
@@ -41,7 +42,7 @@ async def slack_slash_command(request: Request):
4142
if command != "/draft":
4243
return {"response_type": "ephemeral", "text": SLACK_UI["cmd_unknown"]}
4344

44-
# Відкриваємо модалку замість парсингу тексту
45+
# Open a modal instead of parsing the command text
4546
modal_view = build_generation_modal(channel_id=channel_id)
4647
slack_token = (
4748
settings.SLACK_BOT_TOKEN.get_secret_value() if settings.SLACK_BOT_TOKEN else ""
@@ -57,7 +58,7 @@ async def slack_slash_command(request: Request):
5758
if not res.json().get("ok"):
5859
logger.error("slack_modal_open_error", error=res.json())
5960

60-
# Повертаємо 200 OK без тіла, щоб Slack не дублював повідомлення
61+
# Return 200 OK with no body so Slack does not duplicate the message
6162
return Response(status_code=200)
6263

6364

@@ -66,6 +67,7 @@ async def slack_interactions(
6667
request: Request,
6768
session: AsyncSession = Depends(get_db_session), # noqa: B008
6869
):
70+
"""Dispatch Slack block_actions and view_submission interaction payloads."""
6971
form_data = await request.form()
7072
payload_str = form_data.get("payload")
7173

@@ -87,11 +89,11 @@ async def slack_interactions(
8789
action_id = action.get("action_id")
8890
response_url = payload.get("response_url")
8991
logger.info("slack_block_action", action_id=action_id, trigger_id=trigger_id)
90-
action_value = action.get("value", "temp_id|telegram")
92+
action_value = action.get("value", "temp_id|threads")
9193
value_parts = action_value.split("|")
9294
draft_id = value_parts[0]
93-
platform = value_parts[1] if len(value_parts) > 1 else "telegram"
94-
# --- ГРУПА 1: Дії, що відкривають модалки (response_url НЕ потрібен) ---
95+
platform = value_parts[1] if len(value_parts) > 1 else "threads"
96+
# --- GROUP 1: Actions that open modals (response_url not needed) ---
9597
if action_id == "action_open_upload_modal":
9698
modal_view = build_upload_modal()
9799
async with httpx.AsyncClient() as client:
@@ -186,7 +188,7 @@ async def slack_interactions(
186188
json={"trigger_id": trigger_id, "view": modal_view},
187189
)
188190
return Response(status_code=200)
189-
# --- ГРУПА 2: Дії з повідомленнями (response_url ОБОВ'ЯЗКОВИЙ) ---
191+
# --- GROUP 2: Message actions (response_url required) ---
190192
if action_id in [
191193
"action_publish_draft",
192194
"action_reject_draft",
@@ -200,7 +202,7 @@ async def slack_interactions(
200202
)
201203
return Response(status_code=200)
202204

203-
# Беремо topic і draft_text з БД якщо є draft_id
205+
# Load topic and draft_text from DB when draft_id is available
204206
topic = "Медичний пост"
205207
blocks = payload.get("message", {}).get("blocks", [])
206208
raw_draft = ""
@@ -220,7 +222,7 @@ async def slack_interactions(
220222

221223
async with httpx.AsyncClient() as client:
222224
if action_id == "action_publish_draft":
223-
# ДОДАНО: Змінюємо статус на Опубліковано
225+
# Update status to Published
224226
if draft_id.isdigit():
225227
repo = DraftRepository(session)
226228
await repo.update(
@@ -294,7 +296,7 @@ async def slack_interactions(
294296
callback_id = view.get("callback_id")
295297
state_values = view.get("state", {}).get("values", {})
296298

297-
# --- СЦЕНАРІЙ 1: Генерація нового драфту ---
299+
# --- SCENARIO 1: Generate a new draft ---
298300
if callback_id == "modal_generate_draft":
299301
channel_id = view.get("private_metadata")
300302
topic = (
@@ -321,8 +323,8 @@ async def slack_interactions(
321323
platform=platform,
322324
)
323325

324-
# --- ДОДАНО: СТВОРЕННЯ КОРИСТУВАЧА ТА ДРАФТУ В БД ---
325-
# 1. Знаходимо або створюємо користувача (Slack ID)
326+
# Create user and draft in the database
327+
# 1. Find or create the user by Slack ID
326328
user_query = await session.execute(
327329
select(User).where(User.username == user_id)
328330
)
@@ -332,15 +334,14 @@ async def slack_interactions(
332334
session.add(db_user)
333335
await session.flush()
334336

335-
# 2. Створюємо драфт (статус pending)
337+
# 2. Create draft with pending status
336338
repo = DraftRepository(session)
337339
new_draft = await repo.create(
338340
DraftCreate(topic=topic, platform=platform, user_id=db_user.id)
339341
)
340342
real_draft_id = str(new_draft.id)
341-
# ---------------------------------------------------
342343

343-
# Передаємо РЕАЛЬНИЙ ID з бази замість UUID
344+
# Pass the real database ID instead of a UUID
344345
await generate_draft_task.kiq( # type: ignore[call-overload]
345346
topic=topic,
346347
platform=platform.value,
@@ -367,15 +368,15 @@ async def slack_interactions(
367368
status_code=200,
368369
)
369370

370-
# --- СЦЕНАРІЙ 2: Збереження відредагованого драфту ---
371+
# --- SCENARIO 2: Save an edited draft ---
371372
elif callback_id == "modal_edit_draft":
372373
draft_content = (
373374
state_values.get("block_draft_content", {})
374375
.get("input_draft_content", {})
375376
.get("value", "")
376377
)
377378

378-
# Надійний парсинг селектора платформи
379+
# Safely parse the platform selector value
379380
block_state = state_values.get("block_platform_select", {}).get(
380381
"input_platform_select", {}
381382
)
@@ -387,7 +388,7 @@ async def slack_interactions(
387388
)
388389
platform = Platform(platform_raw) if platform_raw else None
389390

390-
# 1. СПОЧАТКУ витягуємо метадані (ID, канали, топік)
391+
# 1. Extract metadata first (ID, channels, topic)
391392
metadata_parts = view.get("private_metadata", "").split("|")
392393
topic = metadata_parts[0] if len(metadata_parts) > 0 else "Медичний пост"
393394
draft_id = metadata_parts[1] if len(metadata_parts) > 1 else "temp_id"
@@ -401,21 +402,21 @@ async def slack_interactions(
401402
draft_id=draft_id,
402403
)
403404

404-
# 2. ПОТІМ зберігаємо в базу даних
405+
# 2. Then save to the database
405406
if draft_id.isdigit():
406407
repo = DraftRepository(session)
407408
await repo.update(
408409
int(draft_id), DraftUpdate(content=draft_content, platform=platform)
409410
)
410411

411-
# 3. ПОТІМ перемальовуємо повідомлення новою карткою з кнопками
412+
# 3. Then redraw the message with an updated card and buttons
412413
if msg_channel_id and message_ts:
413414
updated_blocks = build_draft_card(
414415
topic=topic,
415416
draft=draft_content,
416417
user_id=user_id,
417418
draft_id=draft_id,
418-
platform=platform.value if platform else "telegram",
419+
platform=platform.value if platform else "threads",
419420
)
420421
async with httpx.AsyncClient() as client:
421422
await client.post(
@@ -431,14 +432,14 @@ async def slack_interactions(
431432
},
432433
)
433434

434-
# 4. В КІНЦІ закриваємо модалку одним return
435+
# 4. Close the modal with a single return at the end
435436
return Response(
436437
content=json.dumps({"response_action": "clear"}),
437438
media_type="application/json",
438439
status_code=200,
439440
)
440441

441-
# --- СЦЕНАРІЙ 3: Ручний пост ---
442+
# --- SCENARIO 3: Manual post ---
442443
elif callback_id == "modal_manual_post":
443444
content = (
444445
state_values.get("block_manual_content", {})
@@ -469,7 +470,7 @@ async def slack_interactions(
469470
else None
470471
)
471472

472-
# Знаходимо або створюємо користувача
473+
# Find or create the user
473474
user_query = await session.execute(
474475
select(User).where(User.username == user_id)
475476
)
@@ -522,11 +523,11 @@ async def slack_interactions(
522523
status_code=200,
523524
)
524525

525-
# --- СЦЕНАРІЙ 4: Планування публікації ---
526+
# --- SCENARIO 4: Schedule a publication ---
526527
elif callback_id == "modal_schedule_draft":
527528
metadata_parts = view.get("private_metadata", "").split("|")
528529
draft_id = metadata_parts[0] if len(metadata_parts) > 0 else ""
529-
platform = metadata_parts[1] if len(metadata_parts) > 1 else "telegram"
530+
platform = metadata_parts[1] if len(metadata_parts) > 1 else "threads"
530531

531532
schedule_timestamp = (
532533
state_values.get("block_schedule_time", {})
@@ -572,7 +573,7 @@ async def slack_interactions(
572573
status_code=200,
573574
)
574575

575-
# --- СЦЕНАРІЙ 5: Завантаження гайдлайну ---
576+
# --- SCENARIO 5: Upload a guideline ---
576577
elif callback_id == "modal_upload_guideline":
577578
files = (
578579
state_values.get("block_file_upload", {})
@@ -606,7 +607,7 @@ async def slack_events(
606607
request: Request,
607608
session: AsyncSession = Depends(get_db_session), # noqa: B008
608609
):
609-
"""Обробка Events API (наприклад, відкриття вкладки Home)."""
610+
"""Handle Events API callbacks such as the app_home_opened event."""
610611
data = await request.json()
611612

612613
if data.get("type") == "url_verification":
@@ -618,15 +619,15 @@ async def slack_events(
618619
logger.info("slack_event_received", event_type=event.get("type"), user_id=user_id)
619620

620621
if event.get("type") == "app_home_opened":
621-
# 1. Витягуємо останні 10 драфтів
622+
# 1. Fetch the latest 10 drafts
622623
repo = DraftRepository(session)
623624
recent_drafts = await repo.get_recent_drafts(limit=10)
624625

625626
logger.info(
626627
"slack_home_opened", user_id=user_id, drafts_count=len(recent_drafts)
627628
)
628629

629-
# 2. Рендеримо дашборд
630+
# 2. Render the dashboard
630631
slack_token = (
631632
settings.SLACK_BOT_TOKEN.get_secret_value()
632633
if hasattr(settings.SLACK_BOT_TOKEN, "get_secret_value")

backend/config/settings.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44

55
class Settings(BaseSettings):
6+
"""Application settings loaded from environment variables and .env file."""
7+
68
PROJECT_NAME: str = "Seratonin Script"
79
VERSION: str = "0.1.0"
810
DESCRIPTION: str = "AI Medical Content Generator"
@@ -51,7 +53,7 @@ class Settings(BaseSettings):
5153
SLACK_SIGNING_SECRET: SecretStr = Field(default=SecretStr(""))
5254
# n8n
5355
N8N_WEBHOOK_URL: str = "http://127.0.0.1:5678/webhook/publish-post"
54-
# Конфігурація Pydantic
56+
# Pydantic configuration
5557
model_config = SettingsConfigDict(
5658
env_file=".env", env_file_encoding="utf-8", extra="ignore"
5759
)

0 commit comments

Comments
 (0)