import uuid from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text, Text, Float, Index, Numeric from sqlalchemy.orm import relationship, backref from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB from geoalchemy2 import Geometry # PostGIS támogatás from sqlalchemy.sql import func from app.db.base_class import Base class ServiceProfile(Base): """ Szerviz szolgáltató kiterjesztett adatai (v1.3.1). Egy Organization-höz (org_type='service') kapcsolódik. Támogatja a hierarchiát (Franchise/Telephely) és az automatizált dúsítást. """ __tablename__ = "service_profiles" __table_args__ = ( # Egyedi ujjlenyomat index a robot számára a duplikációk elkerülésére Index('idx_service_fingerprint', 'fingerprint', unique=True), {"schema": "data"} ) id = Column(Integer, primary_key=True, index=True) # --- KAPCSOLAT A CÉGES IKERHEZ (Twin) --- organization_id = Column(Integer, ForeignKey("data.organizations.id"), unique=True) # --- HIERARCHIA (Fa struktúra) --- # Ez tárolja a szülő egység ID-ját (pl. hálózat központja) parent_id = Column(Integer, ForeignKey("data.service_profiles.id"), nullable=True) # --- ROBOT IDENTITÁS --- # Normalize(Név + Város + Utca) hash, hogy ne legyen duplikáció fingerprint = Column(String(255), nullable=False, index=True) # PostGIS GPS pont (SRID 4326 = WGS84 koordináták) location = Column(Geometry(geometry_type='POINT', srid=4326), index=True) # Állapotkezelés: ghost (robot találta), active, flagged, inactive status = Column(String(20), server_default=text("'ghost'"), index=True) last_audit_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) # --- GOOGLE ÉS KÜLSŐ ADATOK --- google_place_id = Column(String(100), unique=True) rating = Column(Float) user_ratings_total = Column(Integer) # --- MÉLYFÚRÁS (Deep Enrichment) ADATOK --- # AI elemzés: {"tone": "barátságos", "pricing": "közép", "reliability": "magas"} vibe_analysis = Column(JSONB, server_default=text("'{}'::jsonb")) # Közösségi háló: {"facebook": "url", "tiktok": "url", "insta": "url"} social_links = Column(JSONB, server_default=text("'{}'::jsonb")) # Speciális szűrő címkék: {"brands": ["Yamaha", "Suzuki"], "specialty": ["engine", "tuning"]} specialization_tags = Column(JSONB, server_default=text("'{}'::jsonb")) # Trust Engine (Bot Discovery=30, User Entry=50, Admin/Partner=100) trust_score = Column(Integer, default=30) is_verified = Column(Boolean, default=False) verification_log = Column(JSONB, server_default=text("'{}'::jsonb")) # --- ELÉRHETŐSÉG --- opening_hours = Column(JSONB, server_default=text("'{}'::jsonb")) contact_phone = Column(String) contact_email = Column(String) website = Column(String) bio = Column(Text) # --- KAPCSOLATOK --- organization = relationship("Organization", back_populates="service_profile") expertises = relationship("ServiceExpertise", back_populates="service") # --- ÖNMAGÁRA HIVATKOZÓ KAPCSOLAT (Hierarchia) --- sub_services = relationship( "ServiceProfile", backref=backref("parent_service", remote_side=[id]), cascade="all, delete-orphan" ) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) class ExpertiseTag(Base): """Szakmai szempontok taxonómiája.""" __tablename__ = "expertise_tags" __table_args__ = {"schema": "data"} id = Column(Integer, primary_key=True) key = Column(String(50), unique=True, index=True) # pl. 'bmw_gs_specialist' name_hu = Column(String(100)) category = Column(String(30)) # 'repair', 'fuel', 'food', 'emergency' class ServiceExpertise(Base): """Kapcsolótábla a szerviz és a szakterület között.""" __tablename__ = "service_expertises" __table_args__ = {"schema": "data"} service_id = Column(Integer, ForeignKey("data.service_profiles.id"), primary_key=True) expertise_id = Column(Integer, ForeignKey("data.expertise_tags.id"), primary_key=True) # Validációs szint (0-100% - Mennyire hiteles ez a szakértelem) validation_level = Column(Integer, default=0) service = relationship("ServiceProfile", back_populates="expertises") expertise = relationship("ExpertiseTag") class ServiceStaging(Base): """ Átmeneti tábla a Hunter (n8n/scraping) adatoknak. """ __tablename__ = "service_staging" __table_args__ = ( Index('idx_staging_fingerprint', 'fingerprint', unique=True), {"schema": "data"} ) id = Column(Integer, primary_key=True, index=True) # --- Alapadatok --- name = Column(String, nullable=False, index=True) # --- Strukturált cím adatok --- postal_code = Column(String(10), index=True) city = Column(String(100), index=True) street_name = Column(String(150)) street_type = Column(String(50)) house_number = Column(String(20)) stairwell = Column(String(20)) floor = Column(String(20)) door = Column(String(20)) hrsz = Column(String(50)) full_address = Column(String) contact_phone = Column(String, nullable=True) email = Column(String, nullable=True) website = Column(String, nullable=True) # --- Forrás és Azonosítás --- source = Column(String(50), nullable=True, index=True) external_id = Column(String(100), nullable=True, index=True) # Robot ujjlenyomat a Staging szintű deduplikációhoz fingerprint = Column(String(255), nullable=False) # --- Adatmentés --- raw_data = Column(JSONB, server_default=text("'{}'::jsonb")) # --- Státusz és Bizalom --- status = Column(String(20), server_default=text("'pending'"), index=True) trust_score = Column(Integer, default=0) created_at = Column(DateTime(timezone=True), server_default=func.now()) class DiscoveryParameter(Base): """Robot vezérlési paraméterek.""" __tablename__ = "discovery_parameters" __table_args__ = {"schema": "data"} id = Column(Integer, primary_key=True) city = Column(String(100), nullable=False) keyword = Column(String(100), nullable=False) country_code = Column(String(2), default="HU") is_active = Column(Boolean, default=True) last_run_at = Column(DateTime(timezone=True))