# /opt/docker/dev/service_finder/backend/app/models/vehicle/vehicle_definitions.py from __future__ import annotations from datetime import datetime from typing import Optional, List, TYPE_CHECKING from sqlalchemy import Column, String, Integer, Boolean, DateTime, ForeignKey, text, JSON, Index, UniqueConstraint, Text, ARRAY, func, Numeric from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.dialects.postgresql import JSONB # MB 2.0: Egységesített Base import a központi adatbázis motorból from app.database import Base # Típus ellenőrzés a körkörös importok elkerülésére if TYPE_CHECKING: from .asset import AssetCatalog from .vehicle import VehicleCost, VehicleOdometerState, VehicleUserRating class VehicleType(Base): """ Jármű kategóriák (pl. Személyautó, Motorkerékpár, Teherautó, Hajó) """ __tablename__ = "vehicle_types" __table_args__ = {"schema": "vehicle"} id: Mapped[int] = mapped_column(Integer, primary_key=True) code: Mapped[str] = mapped_column(String(30), unique=True, index=True) name: Mapped[str] = mapped_column(String(50)) icon: Mapped[Optional[str]] = mapped_column(String(50)) units: Mapped[dict] = mapped_column(JSONB, server_default=text("'{\"power\": \"kW\", \"weight\": \"kg\"}'::jsonb")) # Kapcsolatok features: Mapped[List["FeatureDefinition"]] = relationship("FeatureDefinition", back_populates="vehicle_type") definitions: Mapped[List["VehicleModelDefinition"]] = relationship("VehicleModelDefinition", back_populates="v_type_rel") class FeatureDefinition(Base): """ Felszereltségi elemek definíciója (pl. ABS, Klíma, LED fényszóró) """ __tablename__ = "feature_definitions" __table_args__ = {"schema": "vehicle"} id: Mapped[int] = mapped_column(Integer, primary_key=True) vehicle_type_id: Mapped[int] = mapped_column(Integer, ForeignKey("vehicle.vehicle_types.id")) code: Mapped[str] = mapped_column(String(50), index=True) name: Mapped[str] = mapped_column(String(100)) category: Mapped[str] = mapped_column(String(50), index=True) vehicle_type: Mapped["VehicleType"] = relationship("VehicleType", back_populates="features") model_maps: Mapped[List["ModelFeatureMap"]] = relationship("ModelFeatureMap", back_populates="feature") class VehicleModelDefinition(Base): """ Robot v1.1.0 Multi-Tier MDM Master Adattábla. Az ökoszisztéma technikai igazságforrása. """ __tablename__ = "vehicle_model_definitions" id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) market: Mapped[str] = mapped_column(String(20), server_default=text("'EU'"), index=True) # GLOBÁLIS helyett EU az alap make: Mapped[str] = mapped_column(String(100), index=True) marketing_name: Mapped[str] = mapped_column(String(255), index=True) official_marketing_name: Mapped[Optional[str]] = mapped_column(String(255)) # --- ROBOT LOGIKAI MEZŐK --- attempts: Mapped[int] = mapped_column(Integer, default=0, server_default=text("0")) last_error: Mapped[Optional[str]] = mapped_column(Text, nullable=True) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now()) priority_score: Mapped[int] = mapped_column(Integer, default=0, server_default=text("0")) # --- PRECISION LOGIC MEZŐK --- normalized_name: Mapped[str] = mapped_column(String(255), index=True) # EZT KÖTELEZŐVÉ TETTÜK marketing_name_aliases: Mapped[dict] = mapped_column(JSONB, server_default=text("'[]'::jsonb")) engine_code: Mapped[Optional[str]] = mapped_column(String(50), index=True) # --- TECHNIKAI AZONOSÍTÓK --- technical_code: Mapped[str] = mapped_column(String(100), index=True, server_default=text("'UNKNOWN'")) variant_code: Mapped[str] = mapped_column(String(100), index=True, server_default=text("'UNKNOWN'")) version_code: Mapped[str] = mapped_column(String(100), index=True, server_default=text("'UNKNOWN'")) # --- MŰSZAKI MEZŐK --- type_approval_number: Mapped[Optional[str]] = mapped_column(String(100), index=True) seats: Mapped[int] = mapped_column(Integer, server_default=text("0")) width: Mapped[int] = mapped_column(Integer, server_default=text("0")) wheelbase: Mapped[int] = mapped_column(Integer, server_default=text("0")) list_price: Mapped[int] = mapped_column(Integer, server_default=text("0")) max_speed: Mapped[int] = mapped_column(Integer, server_default=text("0")) towing_weight_unbraked: Mapped[int] = mapped_column(Integer, server_default=text("0")) towing_weight_braked: Mapped[int] = mapped_column(Integer, server_default=text("0")) fuel_consumption_combined: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), server_default=text("0.0")) co2_emissions_combined: Mapped[int] = mapped_column(Integer, server_default=text("0")) # --- SPECIFIKÁCIÓK --- vehicle_type_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("vehicle.vehicle_types.id")) vehicle_class: Mapped[Optional[str]] = mapped_column(String(50), index=True) body_type: Mapped[Optional[str]] = mapped_column(String(100)) fuel_type: Mapped[str] = mapped_column(String(50), index=True, server_default=text("'Unknown'")) trim_level: Mapped[str] = mapped_column(String(100), server_default=text("''")) engine_capacity: Mapped[int] = mapped_column(Integer, server_default=text("0"), index=True) power_kw: Mapped[int] = mapped_column(Integer, server_default=text("0"), index=True) torque_nm: Mapped[Optional[int]] = mapped_column(Integer) cylinders: Mapped[Optional[int]] = mapped_column(Integer) cylinder_layout: Mapped[Optional[str]] = mapped_column(String(50)) curb_weight: Mapped[int] = mapped_column(Integer, server_default=text("0")) max_weight: Mapped[int] = mapped_column(Integer, server_default=text("0")) euro_classification: Mapped[Optional[str]] = mapped_column(String(20)) doors: Mapped[int] = mapped_column(Integer, server_default=text("0")) transmission_type: Mapped[Optional[str]] = mapped_column(String(50)) drive_type: Mapped[Optional[str]] = mapped_column(String(50)) # --- ÉLETCIKLUS --- year_from: Mapped[int] = mapped_column(Integer, index=True, server_default=text("0")) # EZT IS BELETETTÜK A KULCSBA year_to: Mapped[Optional[int]] = mapped_column(Integer, index=True) production_status: Mapped[Optional[str]] = mapped_column(String(50)) status: Mapped[str] = mapped_column(String(50), server_default=text("'unverified'"), index=True) is_manual: Mapped[bool] = mapped_column(Boolean, server_default=text("false")) source: Mapped[str] = mapped_column(String(100), server_default=text("'ROBOT'")) # --- ADATOK --- raw_search_context: Mapped[str] = mapped_column(Text, server_default=text("''")) # JSONB helyett TEXT a keresési adatoknak! raw_api_data: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb")) research_metadata: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb")) specifications: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb")) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) last_research_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) __table_args__ = ( # A LEGONTOSABB SOR: Ez határozza meg, mi számít duplikációnak! UniqueConstraint('make', 'normalized_name', 'variant_code', 'version_code', 'fuel_type', 'market', 'year_from', name='uix_vmd_precision_v2'), Index('idx_vmd_lookup_fast', 'make', 'normalized_name'), Index('idx_vmd_engine_bridge', 'make', 'engine_code'), {"schema": "vehicle"} ) # --- KAPCSOLATOK (Relationships) --- v_type_rel: Mapped["VehicleType"] = relationship("VehicleType", back_populates="definitions") feature_maps: Mapped[List["ModelFeatureMap"]] = relationship("ModelFeatureMap", back_populates="model_definition") variants: Mapped[List["AssetCatalog"]] = relationship("AssetCatalog", back_populates="master_definition") # JAVÍTÁS: Ez a sor hiányzott az API indításához! ratings: Mapped[List["VehicleUserRating"]] = relationship("VehicleUserRating", back_populates="vehicle", cascade="all, delete-orphan") costs: Mapped[List["VehicleCost"]] = relationship("VehicleCost", back_populates="vehicle", cascade="all, delete-orphan") odometer_state: Mapped[Optional["VehicleOdometerState"]] = relationship("VehicleOdometerState", back_populates="vehicle", uselist=False, cascade="all, delete-orphan") class ModelFeatureMap(Base): """ Kapcsolótábla a modellek és az alapfelszereltség között """ __tablename__ = "model_feature_maps" __table_args__ = {"schema": "vehicle"} id: Mapped[int] = mapped_column(Integer, primary_key=True) model_definition_id: Mapped[int] = mapped_column(Integer, ForeignKey("vehicle.vehicle_model_definitions.id")) feature_id: Mapped[int] = mapped_column(Integer, ForeignKey("vehicle.feature_definitions.id")) is_standard: Mapped[bool] = mapped_column(Boolean, default=True) model_definition: Mapped["VehicleModelDefinition"] = relationship("VehicleModelDefinition", back_populates="feature_maps") feature: Mapped["FeatureDefinition"] = relationship("FeatureDefinition", back_populates="model_maps")