Add monitoring TG service

This commit is contained in:
Grendgi
2026-06-04 14:55:41 +03:00
commit f9e072774c
74 changed files with 7232 additions and 0 deletions

View File

@@ -0,0 +1,110 @@
"""sub-sections inside each vertical (e.g. Real Estate → Dubai / Moscow)
A channel now belongs to exactly one section, and each section to exactly
one vertical. The migration auto-creates a `Общий` section per vertical
that has at least one channel and pins all existing channels there, so the
service keeps working without manual reclassification after upgrade.
Revision ID: 0008
Revises: 0007
Create Date: 2026-05-20
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
revision: str = "0008"
down_revision: Union[str, None] = "0007"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
op.create_table(
"sections",
sa.Column("id", sa.Integer(), primary_key=True),
sa.Column("vertical", sa.String(length=32), nullable=False),
sa.Column("slug", sa.String(length=64), nullable=False),
sa.Column("title", sa.String(length=255), nullable=False),
sa.Column("emoji", sa.String(length=8), nullable=True),
sa.Column("description", sa.Text(), nullable=True),
sa.Column(
"created_at",
sa.DateTime(timezone=True),
nullable=False,
server_default=sa.func.now(),
),
sa.UniqueConstraint("vertical", "slug", name="uq_section_vertical_slug"),
)
op.create_index("ix_sections_vertical", "sections", ["vertical"])
# Auto-create a `default` section for each vertical that already has channels,
# so the backfill below has somewhere to point.
op.execute(
"""
INSERT INTO sections (vertical, slug, title, emoji)
SELECT DISTINCT c.vertical,
'default',
CASE c.vertical
WHEN 'hr' THEN 'Общий HR'
ELSE 'Общий'
END,
CASE c.vertical WHEN 'hr' THEN '👥' ELSE '🏠' END
FROM channels c
ON CONFLICT (vertical, slug) DO NOTHING
"""
)
# Add nullable section_id first so the backfill can populate it.
op.add_column(
"channels",
sa.Column("section_id", sa.Integer(), nullable=True),
)
op.create_foreign_key(
"fk_channels_section",
"channels",
"sections",
["section_id"],
["id"],
ondelete="RESTRICT",
)
op.create_index("ix_channels_section_id", "channels", ["section_id"])
op.execute(
"""
UPDATE channels c
SET section_id = s.id
FROM sections s
WHERE s.vertical = c.vertical AND s.slug = 'default'
"""
)
# Now we can safely require section_id.
op.alter_column("channels", "section_id", nullable=False)
# Per-section LLM prompt keys are longer than 64 chars
# (`llm_system_prompt:real_estate:some-long-slug`), so widen the key column.
op.alter_column(
"app_settings",
"key",
existing_type=sa.String(length=64),
type_=sa.String(length=128),
existing_nullable=False,
)
def downgrade() -> None:
op.alter_column(
"app_settings",
"key",
existing_type=sa.String(length=128),
type_=sa.String(length=64),
existing_nullable=False,
)
op.drop_index("ix_channels_section_id", table_name="channels")
op.drop_constraint("fk_channels_section", "channels", type_="foreignkey")
op.drop_column("channels", "section_id")
op.drop_index("ix_sections_vertical", table_name="sections")
op.drop_table("sections")