Files
monitoring-pf/app/models.py
Grendgi 6966e6810c
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 35s
Auto-sync permit competitors in monitoring PF
2026-06-05 12:09:51 +03:00

107 lines
4.6 KiB
Python

from datetime import datetime
from enum import Enum
from sqlalchemy import Boolean, DateTime, Enum as SAEnum, Float, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db import Base
class DealType(str, Enum):
SALE = "sale"
RENT = "rent"
class Source(str, Enum):
PROPERTYFINDER = "propertyfinder"
BAYUT = "bayut"
class ListingStatus(str, Enum):
ACTIVE = "active"
REMOVED = "removed"
class Employee(Base):
__tablename__ = "employees"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(200))
portal_user_id: Mapped[str | None] = mapped_column(String(100), unique=True, index=True, nullable=True)
tg_chat_id: Mapped[str | None] = mapped_column(String(64), unique=True, nullable=True)
tg_username: Mapped[str | None] = mapped_column(String(200), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
projects: Mapped[list["Project"]] = relationship(back_populates="owner")
class Project(Base):
"""Наш проект — квартира, которую агентство рекламирует."""
__tablename__ = "projects"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
title: Mapped[str] = mapped_column(String(300))
deal_type: Mapped[DealType] = mapped_column(SAEnum(DealType))
our_price: Mapped[float | None] = mapped_column(Float, nullable=True)
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
# Опциональные параметры — используются для подсказок похожих объявлений
dld_permit: Mapped[str | None] = mapped_column(String(100), index=True, nullable=True)
building: Mapped[str | None] = mapped_column(String(300), nullable=True)
bedrooms: Mapped[int | None] = mapped_column(Integer, nullable=True)
size_sqft: Mapped[float | None] = mapped_column(Float, nullable=True)
our_url: Mapped[str | None] = mapped_column(Text, nullable=True)
owner_id: Mapped[int] = mapped_column(ForeignKey("employees.id"))
owner: Mapped[Employee] = relationship(back_populates="projects")
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
last_checked_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
listings: Mapped[list["CompetitorListing"]] = relationship(
back_populates="project", cascade="all, delete-orphan"
)
class CompetitorListing(Base):
"""Объявление конкурента, найденное на PF/Bayut по DLD permit нашего проекта."""
__tablename__ = "competitor_listings"
__table_args__ = (UniqueConstraint("project_id", "source", "external_id", name="uq_listing"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
project_id: Mapped[int] = mapped_column(ForeignKey("projects.id"))
project: Mapped[Project] = relationship(back_populates="listings")
source: Mapped[Source] = mapped_column(SAEnum(Source))
external_id: Mapped[str] = mapped_column(String(100)) # ID на стороне PF/Bayut
url: Mapped[str] = mapped_column(Text)
title: Mapped[str | None] = mapped_column(String(500), nullable=True)
agent_name: Mapped[str | None] = mapped_column(String(300), nullable=True)
agency_name: Mapped[str | None] = mapped_column(String(300), nullable=True)
permit_number: Mapped[str | None] = mapped_column(String(100), nullable=True)
auto_discovered: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
current_price: Mapped[float | None] = mapped_column(Float, nullable=True)
currency: Mapped[str | None] = mapped_column(String(10), nullable=True, default="AED")
status: Mapped[ListingStatus] = mapped_column(SAEnum(ListingStatus), default=ListingStatus.ACTIVE)
first_seen_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
last_seen_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
price_history: Mapped[list["PriceHistory"]] = relationship(
back_populates="listing", cascade="all, delete-orphan", order_by="PriceHistory.recorded_at.desc()"
)
class PriceHistory(Base):
__tablename__ = "price_history"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
listing_id: Mapped[int] = mapped_column(ForeignKey("competitor_listings.id"))
listing: Mapped[CompetitorListing] = relationship(back_populates="price_history")
price: Mapped[float | None] = mapped_column(Float, nullable=True)
recorded_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)