Add monitoring TG service
This commit is contained in:
119
src/parser_bot/db/models.py
Normal file
119
src/parser_bot/db/models.py
Normal file
@@ -0,0 +1,119 @@
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import (
|
||||
BigInteger,
|
||||
DateTime,
|
||||
ForeignKey,
|
||||
Index,
|
||||
String,
|
||||
Text,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
)
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class Section(Base):
|
||||
"""A sub-section inside a vertical, e.g. ('real_estate', 'dubai').
|
||||
|
||||
The pair (vertical, slug) is unique and identifies a section in URLs
|
||||
and API calls. A channel belongs to exactly one section, the section
|
||||
knows its vertical, and the LLM prompt store can hold a per-section
|
||||
override that falls back to the vertical-level prompt.
|
||||
"""
|
||||
|
||||
__tablename__ = "sections"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("vertical", "slug", name="uq_section_vertical_slug"),
|
||||
Index("ix_sections_vertical", "vertical"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
vertical: Mapped[str] = mapped_column(String(32))
|
||||
slug: Mapped[str] = mapped_column(String(64))
|
||||
title: Mapped[str] = mapped_column(String(255))
|
||||
emoji: Mapped[str | None] = mapped_column(String(8), nullable=True)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
access_code: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
|
||||
channels: Mapped[list["Channel"]] = relationship(back_populates="section")
|
||||
|
||||
|
||||
class Channel(Base):
|
||||
__tablename__ = "channels"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
# Telegram numeric channel id (peer id), nullable until first resolve
|
||||
tg_id: Mapped[int | None] = mapped_column(BigInteger, unique=True, nullable=True)
|
||||
# Username or t.me/joinchat link supplied by user
|
||||
identifier: Mapped[str] = mapped_column(String(255), unique=True)
|
||||
title: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||
# 'real_estate' or 'hr' — picks which LLM prompt and lead schema is used
|
||||
vertical: Mapped[str] = mapped_column(
|
||||
String(32), default="real_estate", server_default="real_estate", index=True
|
||||
)
|
||||
section_id: Mapped[int] = mapped_column(
|
||||
ForeignKey("sections.id", ondelete="RESTRICT"), index=True
|
||||
)
|
||||
is_active: Mapped[bool] = mapped_column(default=True, server_default="true")
|
||||
last_message_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||||
last_polled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
|
||||
section: Mapped[Section] = relationship(back_populates="channels")
|
||||
messages: Mapped[list["Message"]] = relationship(
|
||||
back_populates="channel",
|
||||
cascade="all, delete-orphan",
|
||||
passive_deletes=True,
|
||||
)
|
||||
|
||||
|
||||
class Message(Base):
|
||||
__tablename__ = "messages"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("channel_id", "tg_message_id", name="uq_channel_message"),
|
||||
Index("ix_messages_channel_date", "channel_id", "date"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
channel_id: Mapped[int] = mapped_column(ForeignKey("channels.id", ondelete="CASCADE"))
|
||||
tg_message_id: Mapped[int] = mapped_column(BigInteger)
|
||||
date: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
||||
text: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
sender_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||||
sender_username: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
sender_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
grouped_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True)
|
||||
has_media: Mapped[bool] = mapped_column(default=False, server_default="false")
|
||||
views: Mapped[int | None] = mapped_column(nullable=True)
|
||||
forwards: Mapped[int | None] = mapped_column(nullable=True)
|
||||
raw: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
|
||||
media_files: Mapped[list | None] = mapped_column(JSONB, nullable=True)
|
||||
extracted: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
|
||||
fetched_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
|
||||
channel: Mapped[Channel] = relationship(back_populates="messages")
|
||||
|
||||
|
||||
class AppSetting(Base):
|
||||
"""Runtime-editable settings, edited from the UI without a restart."""
|
||||
|
||||
__tablename__ = "app_settings"
|
||||
|
||||
key: Mapped[str] = mapped_column(String(128), primary_key=True)
|
||||
value: Mapped[dict | str | int | bool | None] = mapped_column(JSONB, nullable=False)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
Reference in New Issue
Block a user