átlagos kiegészítséek jó sok
This commit is contained in:
63
backend/app/models/vehicle/__init__.py
Normal file
63
backend/app/models/vehicle/__init__.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# vehicle package exports
|
||||
from .vehicle_definitions import (
|
||||
VehicleModelDefinition,
|
||||
VehicleType,
|
||||
FeatureDefinition,
|
||||
ModelFeatureMap,
|
||||
)
|
||||
|
||||
from .vehicle import (
|
||||
CostCategory,
|
||||
VehicleCost,
|
||||
VehicleOdometerState,
|
||||
VehicleUserRating,
|
||||
GbCatalogDiscovery,
|
||||
)
|
||||
|
||||
from .external_reference import ExternalReferenceLibrary
|
||||
from .external_reference_queue import ExternalReferenceQueue
|
||||
from .asset import (
|
||||
Asset,
|
||||
AssetCatalog,
|
||||
AssetCost,
|
||||
AssetEvent,
|
||||
AssetFinancials,
|
||||
AssetTelemetry,
|
||||
AssetReview,
|
||||
ExchangeRate,
|
||||
CatalogDiscovery,
|
||||
VehicleOwnership,
|
||||
)
|
||||
|
||||
from .history import AuditLog, LogSeverity
|
||||
|
||||
# --- ÚJ MOTOROS SPECIFIKÁCIÓ MODELL BEEMELÉSE ---
|
||||
from .motorcycle_specs import MotorcycleSpecs
|
||||
|
||||
__all__ = [
|
||||
"VehicleModelDefinition",
|
||||
"VehicleType",
|
||||
"FeatureDefinition",
|
||||
"ModelFeatureMap",
|
||||
"CostCategory",
|
||||
"VehicleCost",
|
||||
"VehicleOdometerState",
|
||||
"VehicleUserRating",
|
||||
"GbCatalogDiscovery",
|
||||
"ExternalReferenceLibrary",
|
||||
"ExternalReferenceQueue",
|
||||
"Asset",
|
||||
"AssetCatalog",
|
||||
"AssetCost",
|
||||
"AssetEvent",
|
||||
"AssetFinancials",
|
||||
"AssetTelemetry",
|
||||
"AssetReview",
|
||||
"ExchangeRate",
|
||||
"CatalogDiscovery",
|
||||
"VehicleOwnership",
|
||||
"AuditLog",
|
||||
"LogSeverity",
|
||||
# --- EXPORT LISTA KIEGÉSZÍTÉSE ---
|
||||
"MotorcycleSpecs",
|
||||
]
|
||||
258
backend/app/models/vehicle/asset.py
Normal file
258
backend/app/models/vehicle/asset.py
Normal file
@@ -0,0 +1,258 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/vehicle/asset.py
|
||||
from __future__ import annotations
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
from sqlalchemy import String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint, BigInteger, Integer, Float
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
class AssetCatalog(Base):
|
||||
""" Jármű katalógus mesteradatok (Validált technikai sablonok). """
|
||||
__tablename__ = "vehicle_catalog"
|
||||
__table_args__ = (
|
||||
UniqueConstraint('make', 'model', 'year_from', 'fuel_type', name='uix_vehicle_catalog_full'),
|
||||
{"schema": "vehicle"}
|
||||
)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
master_definition_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("vehicle.vehicle_model_definitions.id"))
|
||||
|
||||
make: Mapped[str] = mapped_column(String, index=True, nullable=False)
|
||||
model: Mapped[str] = mapped_column(String, index=True, nullable=False)
|
||||
generation: Mapped[Optional[str]] = mapped_column(String, index=True)
|
||||
year_from: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
year_to: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
fuel_type: Mapped[Optional[str]] = mapped_column(String, index=True)
|
||||
power_kw: Mapped[Optional[int]] = mapped_column(Integer, index=True)
|
||||
engine_capacity: Mapped[Optional[int]] = mapped_column(Integer, index=True)
|
||||
|
||||
factory_data: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
||||
|
||||
master_definition: Mapped[Optional["VehicleModelDefinition"]] = relationship("VehicleModelDefinition", back_populates="variants")
|
||||
assets: Mapped[List["Asset"]] = relationship("Asset", back_populates="catalog")
|
||||
|
||||
class Asset(Base):
|
||||
""" A fizikai eszköz (Digital Twin) - Minden adat itt fut össze. """
|
||||
__tablename__ = "assets"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
vin: Mapped[str] = mapped_column(String(17), unique=True, index=True, nullable=False)
|
||||
license_plate: Mapped[Optional[str]] = mapped_column(String(20), index=True)
|
||||
name: Mapped[Optional[str]] = mapped_column(String)
|
||||
|
||||
# Állapot és életút mérőszámok
|
||||
year_of_manufacture: Mapped[Optional[int]] = mapped_column(Integer, index=True)
|
||||
first_registration_date: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
current_mileage: Mapped[int] = mapped_column(Integer, default=0, index=True)
|
||||
condition_score: Mapped[int] = mapped_column(Integer, default=100)
|
||||
|
||||
# Értékesítési modul
|
||||
is_for_sale: Mapped[bool] = mapped_column(Boolean, default=False, index=True)
|
||||
price: Mapped[Optional[float]] = mapped_column(Numeric(15, 2))
|
||||
currency: Mapped[str] = mapped_column(String(3), default="EUR")
|
||||
|
||||
catalog_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("vehicle.vehicle_catalog.id"))
|
||||
current_organization_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("fleet.organizations.id"))
|
||||
|
||||
# Identity kapcsolatok
|
||||
owner_person_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("identity.persons.id"))
|
||||
owner_org_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("fleet.organizations.id"))
|
||||
operator_person_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("identity.persons.id"))
|
||||
operator_org_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("fleet.organizations.id"))
|
||||
|
||||
status: Mapped[str] = mapped_column(String(20), default="active")
|
||||
individual_equipment: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# --- KAPCSOLATOK ---
|
||||
catalog: Mapped["AssetCatalog"] = relationship("AssetCatalog", back_populates="assets")
|
||||
financials: Mapped[Optional["AssetFinancials"]] = relationship("AssetFinancials", back_populates="asset", uselist=False)
|
||||
costs: Mapped[List["AssetCost"]] = relationship("AssetCost", back_populates="asset")
|
||||
events: Mapped[List["AssetEvent"]] = relationship("AssetEvent", back_populates="asset")
|
||||
logbook: Mapped[List["VehicleLogbook"]] = relationship("VehicleLogbook", back_populates="asset")
|
||||
inspections: Mapped[List["AssetInspection"]] = relationship("AssetInspection", back_populates="asset")
|
||||
reviews: Mapped[List["AssetReview"]] = relationship("AssetReview", back_populates="asset")
|
||||
telemetry: Mapped[Optional["AssetTelemetry"]] = relationship("AssetTelemetry", back_populates="asset", uselist=False)
|
||||
assignments: Mapped[List["AssetAssignment"]] = relationship("AssetAssignment", back_populates="asset")
|
||||
ownership_history: Mapped[List["VehicleOwnership"]] = relationship("VehicleOwnership", back_populates="asset")
|
||||
|
||||
# --- COMPUTED PROPERTIES (for Pydantic schema compatibility) ---
|
||||
@property
|
||||
def is_verified(self) -> bool:
|
||||
"""Always False for now, as verification is not yet implemented."""
|
||||
return False
|
||||
|
||||
class AssetFinancials(Base):
|
||||
""" I. Beszerzés és IV. Értékcsökkenés (Amortizáció). """
|
||||
__tablename__ = "asset_financials"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), unique=True)
|
||||
|
||||
purchase_price_net: Mapped[float] = mapped_column(Numeric(18, 2))
|
||||
purchase_price_gross: Mapped[float] = mapped_column(Numeric(18, 2))
|
||||
vat_rate: Mapped[float] = mapped_column(Numeric(5, 2), default=27.00)
|
||||
activation_date: Mapped[Optional[datetime]] = mapped_column(DateTime)
|
||||
financing_type: Mapped[str] = mapped_column(String(50))
|
||||
accounting_details: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
||||
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="financials")
|
||||
|
||||
class AssetCost(Base):
|
||||
""" II. Üzemeltetés és TCO kimutatás. """
|
||||
__tablename__ = "asset_costs"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
||||
organization_id: Mapped[int] = mapped_column(Integer, ForeignKey("fleet.organizations.id"), nullable=False)
|
||||
|
||||
cost_category: Mapped[str] = mapped_column(String(50), index=True)
|
||||
amount_net: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False)
|
||||
currency: Mapped[str] = mapped_column(String(3), default="HUF")
|
||||
date: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
invoice_number: Mapped[Optional[str]] = mapped_column(String(100), index=True)
|
||||
|
||||
data: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="costs")
|
||||
organization: Mapped["Organization"] = relationship("Organization")
|
||||
|
||||
class VehicleLogbook(Base):
|
||||
""" Útnyilvántartás (NAV, Kiküldetés, Munkábajárás). """
|
||||
__tablename__ = "vehicle_logbook"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
||||
driver_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False)
|
||||
|
||||
trip_type: Mapped[str] = mapped_column(String(30), index=True)
|
||||
is_reimbursable: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
start_mileage: Mapped[int] = mapped_column(Integer)
|
||||
end_mileage: Mapped[Optional[int]] = mapped_column(Integer)
|
||||
distance_km: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True)
|
||||
|
||||
# GPS koordináták
|
||||
start_lat: Mapped[Optional[float]] = mapped_column(Numeric(10, 6), nullable=True)
|
||||
start_lng: Mapped[Optional[float]] = mapped_column(Numeric(10, 6), nullable=True)
|
||||
end_lat: Mapped[Optional[float]] = mapped_column(Numeric(10, 6), nullable=True)
|
||||
end_lng: Mapped[Optional[float]] = mapped_column(Numeric(10, 6), nullable=True)
|
||||
gps_calculated_distance: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True)
|
||||
|
||||
# OBDII és telemetria
|
||||
obd_verified: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
max_acceleration: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
||||
average_speed: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
|
||||
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="logbook")
|
||||
driver: Mapped["User"] = relationship("User")
|
||||
|
||||
class AssetInspection(Base):
|
||||
""" Napi ellenőrző lista és Biztonsági check. """
|
||||
__tablename__ = "asset_inspections"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
||||
inspector_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False)
|
||||
|
||||
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
checklist_results: Mapped[dict] = mapped_column(JSONB, nullable=False)
|
||||
is_safe: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="inspections")
|
||||
inspector: Mapped["User"] = relationship("User")
|
||||
|
||||
class AssetReview(Base):
|
||||
""" Jármű értékelések és visszajelzések. """
|
||||
__tablename__ = "asset_reviews"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False)
|
||||
|
||||
overall_rating: Mapped[Optional[int]] = mapped_column(Integer) # 1-5 csillag
|
||||
comment: Mapped[Optional[str]] = mapped_column(Text)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="reviews")
|
||||
user: Mapped["User"] = relationship("User")
|
||||
|
||||
class VehicleOwnership(Base):
|
||||
""" Tulajdonosváltások története. """
|
||||
__tablename__ = "vehicle_ownership_history"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
||||
user_id: Mapped[int] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=False)
|
||||
|
||||
acquired_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
disposed_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True))
|
||||
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="ownership_history")
|
||||
# JAVÍTVA: Kapcsolat a User modellhez
|
||||
user: Mapped["User"] = relationship("User", back_populates="ownership_history")
|
||||
|
||||
class AssetTelemetry(Base):
|
||||
__tablename__ = "asset_telemetry"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), unique=True)
|
||||
current_mileage: Mapped[int] = mapped_column(Integer, default=0)
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="telemetry")
|
||||
|
||||
class AssetAssignment(Base):
|
||||
""" Eszköz-Szervezet összerendelés. """
|
||||
__tablename__ = "asset_assignments"
|
||||
__table_args__ = {"schema": "fleet"}
|
||||
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
||||
organization_id: Mapped[int] = mapped_column(Integer, ForeignKey("fleet.organizations.id"), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(30), default="active")
|
||||
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="assignments")
|
||||
organization: Mapped["Organization"] = relationship("Organization", back_populates="assets")
|
||||
|
||||
class AssetEvent(Base):
|
||||
""" Szerviz, baleset és egyéb jelentős események. """
|
||||
__tablename__ = "asset_events"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
asset_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), ForeignKey("vehicle.assets.id"), nullable=False)
|
||||
event_type: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||
asset: Mapped["Asset"] = relationship("Asset", back_populates="events")
|
||||
|
||||
class ExchangeRate(Base):
|
||||
__tablename__ = "exchange_rates"
|
||||
__table_args__ = {"schema": "finance"}
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
rate: Mapped[float] = mapped_column(Numeric(18, 6), nullable=False)
|
||||
|
||||
class CatalogDiscovery(Base):
|
||||
""" Robot munkaterület a felfedezett modelleknek. """
|
||||
__tablename__ = "catalog_discovery"
|
||||
__table_args__ = (
|
||||
# KIBŐVÍTETT EGYEDISÉGI SZABÁLY: Márka + Modell + Osztály + Piac + Évjárat
|
||||
UniqueConstraint('make', 'model', 'vehicle_class', 'market', 'model_year', name='_make_model_market_year_uc'),
|
||||
# Alapvető egyediség: make + model + vehicle_class (piac és évjárat nélkül)
|
||||
UniqueConstraint('make', 'model', 'vehicle_class', name='uq_make_model_class'),
|
||||
{"schema": "vehicle"}
|
||||
)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
make: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
|
||||
model: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
|
||||
vehicle_class: Mapped[str] = mapped_column(String(50), server_default=text("'car'"), index=True)
|
||||
|
||||
# --- ÚJ MEZŐK A STATISZTIKÁHOZ ÉS PIACAZONOSÍTÁSHOZ ---
|
||||
market: Mapped[str] = mapped_column(String(20), server_default=text("'GLOBAL'"), index=True) # pl: RDW, DVLA, USA_IMPORT
|
||||
model_year: Mapped[Optional[int]] = mapped_column(Integer, index=True)
|
||||
|
||||
# Robot vezérlés
|
||||
status: Mapped[str] = mapped_column(String(20), server_default=text("'pending'"), index=True)
|
||||
source: Mapped[Optional[str]] = mapped_column(String(100)) # pl: STRATEGIST-V2, NHTSA-V1
|
||||
priority_score: Mapped[int] = mapped_column(Integer, server_default=text("0"))
|
||||
attempts: Mapped[int] = mapped_column(Integer, server_default=text("0"))
|
||||
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
36
backend/app/models/vehicle/external_reference.py
Normal file
36
backend/app/models/vehicle/external_reference.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/vehicle/external_reference.py
|
||||
from sqlalchemy import Column, Integer, String, JSON, DateTime, UniqueConstraint, ForeignKey
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
class ExternalReferenceLibrary(Base):
|
||||
__tablename__ = "external_reference_library"
|
||||
__table_args__ = (
|
||||
UniqueConstraint('source_url', name='_source_url_uc'),
|
||||
{"schema": "vehicle"}
|
||||
)
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
source_name = Column(String(50), default="auto-data.net") # Később jöhet más forrás is (motorokhoz/kamionokhoz)
|
||||
make = Column(String(100), index=True)
|
||||
model = Column(String(100), index=True)
|
||||
generation = Column(String(255))
|
||||
modification = Column(String(255))
|
||||
year_from = Column(Integer)
|
||||
year_to = Column(Integer, nullable=True)
|
||||
power_kw = Column(Integer, index=True)
|
||||
engine_cc = Column(Integer, index=True)
|
||||
category = Column(String(20), default='car', index=True) # ÚJ
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
# Minden egyéb technikai adat (olaj, gumi, fogyasztás stb.) ide megy
|
||||
specifications = Column(JSON, default={})
|
||||
|
||||
source_url = Column(String(500), unique=True)
|
||||
last_scraped_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
pipeline_status = Column(String(30), default='pending_enrich', index=True)
|
||||
matched_vmd_id = Column(Integer, ForeignKey('vehicle.vehicle_model_definitions.id'), nullable=True, index=True)
|
||||
|
||||
# Biztosítjuk, hogy ne legyen duplikáció azonos linkről
|
||||
|
||||
33
backend/app/models/vehicle/external_reference_queue.py
Normal file
33
backend/app/models/vehicle/external_reference_queue.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/vehicle/external_reference_queue.py
|
||||
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, text
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
class ExternalReferenceQueue(Base):
|
||||
__tablename__ = "auto_data_crawler_queue"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
url = Column(String(500), unique=True, nullable=False)
|
||||
|
||||
# Szintek: 'brand', 'model', 'generation', 'engine'
|
||||
level = Column(String(20), nullable=False, index=True)
|
||||
|
||||
# Kategóriák
|
||||
category = Column(String(20), default='car', index=True)
|
||||
|
||||
# Szülő azonosító (pl. a modell tudja, melyik márkához tartozik)
|
||||
parent_id = Column(Integer, nullable=True)
|
||||
|
||||
# Megjelenítési név (pl. "Audi", "A3 Sportback")
|
||||
name = Column(String(255))
|
||||
|
||||
# Állapot: 'pending', 'processing', 'completed', 'error'
|
||||
status = Column(String(20), default='pending', index=True)
|
||||
|
||||
# Hibakezeléshez
|
||||
error_msg = Column(String(1000), nullable=True)
|
||||
retry_count = Column(Integer, default=0)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
47
backend/app/models/vehicle/history.py
Executable file
47
backend/app/models/vehicle/history.py
Executable file
@@ -0,0 +1,47 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/vehicle/history.py
|
||||
import uuid
|
||||
import enum
|
||||
from datetime import datetime, date
|
||||
from typing import Optional, Any
|
||||
from sqlalchemy import String, DateTime, ForeignKey, JSON, Date, Text, Integer
|
||||
from sqlalchemy.dialects.postgresql import ENUM as PG_ENUM, UUID as PG_UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
# MB 2.0: Központi aszinkron adatbázis motorból húzzuk be a Base-t
|
||||
from app.database import Base
|
||||
|
||||
class LogSeverity(str, enum.Enum):
|
||||
info = "info"
|
||||
warning = "warning"
|
||||
critical = "critical"
|
||||
emergency = "emergency"
|
||||
|
||||
|
||||
class AuditLog(Base):
|
||||
""" Rendszerszintű műveletnapló. """
|
||||
__tablename__ = "audit_logs"
|
||||
__table_args__ = {"schema": "audit"}
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
|
||||
# MB 2.0 JAVÍTÁS: A felhasználó az identity sémában lakik!
|
||||
user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id"))
|
||||
|
||||
severity: Mapped[LogSeverity] = mapped_column(
|
||||
PG_ENUM(LogSeverity, name="log_severity", schema="audit"),
|
||||
default=LogSeverity.info
|
||||
)
|
||||
|
||||
action: Mapped[str] = mapped_column(String(100), index=True)
|
||||
target_type: Mapped[Optional[str]] = mapped_column(String(50), index=True)
|
||||
target_id: Mapped[Optional[str]] = mapped_column(String(50), index=True)
|
||||
|
||||
old_data: Mapped[Optional[Any]] = mapped_column(JSON)
|
||||
new_data: Mapped[Optional[Any]] = mapped_column(JSON)
|
||||
|
||||
ip_address: Mapped[Optional[str]] = mapped_column(String(45), index=True)
|
||||
user_agent: Mapped[Optional[Text]] = mapped_column(Text)
|
||||
timestamp: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), index=True)
|
||||
|
||||
user: Mapped[Optional["User"]] = relationship("User")
|
||||
35
backend/app/models/vehicle/motorcycle_specs.py
Normal file
35
backend/app/models/vehicle/motorcycle_specs.py
Normal file
@@ -0,0 +1,35 @@
|
||||
from sqlalchemy import Column, Integer, Text, ForeignKey, DateTime
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
class MotorcycleSpecs(Base):
|
||||
"""
|
||||
Gondolatmenet: Ez a modell reprezentálja a motorok végleges technikai adatait.
|
||||
A JSONB mező lehetővé teszi, hogy az AutoEvolution-ról lekerülő összes változatos
|
||||
adatot (hengerűrtartalom, nyomaték, hűtés, stb.) sémakötöttség nélkül tároljuk.
|
||||
"""
|
||||
__tablename__ = "motorcycle_specs"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
# Kapcsolat a crawler várólistájával
|
||||
crawler_id = Column(
|
||||
Integer,
|
||||
ForeignKey("vehicle.auto_data_crawler_queue.id", ondelete="CASCADE"),
|
||||
unique=True,
|
||||
nullable=False
|
||||
)
|
||||
|
||||
full_name = Column(Text, nullable=False)
|
||||
url = Column(Text)
|
||||
|
||||
# A lényeg: ide kerül minden technikai adat kulcs-érték párban
|
||||
raw_data = Column(JSONB, nullable=False)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return f"<MotorcycleSpecs(name='{self.full_name}', crawler_id={self.crawler_id})>"
|
||||
203
backend/app/models/vehicle/vehicle.py
Normal file
203
backend/app/models/vehicle/vehicle.py
Normal file
@@ -0,0 +1,203 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/vehicle/vehicle.py
|
||||
"""
|
||||
TCO (Total Cost of Ownership) alapmodelljei a 'vehicle' sémában.
|
||||
- CostCategory: Standardizált költségkategóriák hierarchiája
|
||||
- VehicleCost: Járműhöz kapcsolódó tényleges költségnapló
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import uuid
|
||||
from sqlalchemy import Column, String, Integer, Boolean, DateTime, ForeignKey, Text, Numeric, UniqueConstraint, Float, CheckConstraint
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class CostCategory(Base):
|
||||
"""
|
||||
Standardizált költségkategóriák hierarchikus fája.
|
||||
Rendszerkategóriák (is_system=True) nem törölhetők, csak felhasználói kategóriák.
|
||||
"""
|
||||
__tablename__ = "cost_categories"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
parent_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vehicle.cost_categories.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True
|
||||
)
|
||||
code: Mapped[str] = mapped_column(String(50), unique=True, index=True, nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
is_system: Mapped[bool] = mapped_column(Boolean, default=False, server_default="false")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())
|
||||
|
||||
# Hierarchikus kapcsolatok
|
||||
parent: Mapped[Optional["CostCategory"]] = relationship(
|
||||
"CostCategory",
|
||||
remote_side=[id],
|
||||
back_populates="children",
|
||||
foreign_keys=[parent_id]
|
||||
)
|
||||
children: Mapped[list["CostCategory"]] = relationship(
|
||||
"CostCategory",
|
||||
back_populates="parent",
|
||||
foreign_keys=[parent_id]
|
||||
)
|
||||
|
||||
# Kapcsolódó költségek
|
||||
costs: Mapped[list["VehicleCost"]] = relationship("VehicleCost", back_populates="category")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"CostCategory(id={self.id}, code='{self.code}', name='{self.name}')"
|
||||
|
||||
|
||||
class VehicleCost(Base):
|
||||
"""
|
||||
Járműhöz kapcsolódó tényleges költségnapló.
|
||||
Minden költséghez kötelező az odometer állás (km) és a dátum.
|
||||
Az organization_id az Univerzális Flotta hivatkozás (fleet.organizations).
|
||||
"""
|
||||
__tablename__ = "costs"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("vehicle_id", "category_id", "date", "odometer", name="uq_cost_unique_entry"),
|
||||
{"schema": "vehicle"}
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
vehicle_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vehicle.vehicle_model_definitions.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True
|
||||
)
|
||||
organization_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("fleet.organizations.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True
|
||||
)
|
||||
category_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vehicle.cost_categories.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
index=True
|
||||
)
|
||||
amount: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False) # Összeg
|
||||
currency: Mapped[str] = mapped_column(String(3), default="HUF", server_default="'HUF'") # ISO valutakód
|
||||
odometer: Mapped[int] = mapped_column(Integer, nullable=False) # Kilométeróra állás (km)
|
||||
date: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, index=True)
|
||||
notes: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())
|
||||
|
||||
# Kapcsolatok
|
||||
vehicle: Mapped["VehicleModelDefinition"] = relationship("VehicleModelDefinition", back_populates="costs")
|
||||
organization: Mapped[Optional["Organization"]] = relationship("Organization", back_populates="vehicle_costs")
|
||||
category: Mapped["CostCategory"] = relationship("CostCategory", back_populates="costs")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"VehicleCost(id={self.id}, vehicle_id={self.vehicle_id}, amount={self.amount} {self.currency})"
|
||||
|
||||
|
||||
class VehicleOdometerState(Base):
|
||||
"""
|
||||
Jármű kilométeróra állapotának és becslésének tárolása.
|
||||
Adminisztrátor által paraméterezhető algoritmusokkal működik.
|
||||
"""
|
||||
__tablename__ = "vehicle_odometer_states"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
|
||||
vehicle_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vehicle.vehicle_model_definitions.id", ondelete="CASCADE"),
|
||||
primary_key=True,
|
||||
nullable=False
|
||||
)
|
||||
last_recorded_odometer: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
last_recorded_date: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
||||
daily_avg_distance: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False)
|
||||
estimated_current_odometer: Mapped[float] = mapped_column(Numeric(12, 2), nullable=False)
|
||||
confidence_score: Mapped[float] = mapped_column(Float, nullable=False, default=0.0)
|
||||
manual_override_avg: Mapped[Optional[float]] = mapped_column(Numeric(10, 2), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())
|
||||
|
||||
# Kapcsolat a jármű definícióval
|
||||
vehicle: Mapped["VehicleModelDefinition"] = relationship("VehicleModelDefinition", back_populates="odometer_state")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"VehicleOdometerState(vehicle_id={self.vehicle_id}, estimated={self.estimated_current_odometer}, confidence={self.confidence_score})"
|
||||
|
||||
|
||||
class VehicleUserRating(Base):
|
||||
"""
|
||||
Jármű értékelési rendszer - User -> Vehicle kapcsolat.
|
||||
Egy felhasználó csak egyszer értékelhet egy adott járművet.
|
||||
Értékelés 4 dimenzióban 1-10 skálán.
|
||||
"""
|
||||
__tablename__ = "vehicle_user_ratings"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("vehicle_id", "user_id", name="uq_vehicle_user_rating_unique"),
|
||||
CheckConstraint("driving_experience BETWEEN 1 AND 10", name="ck_driving_experience_range"),
|
||||
CheckConstraint("reliability BETWEEN 1 AND 10", name="ck_reliability_range"),
|
||||
CheckConstraint("comfort BETWEEN 1 AND 10", name="ck_comfort_range"),
|
||||
CheckConstraint("consumption_satisfaction BETWEEN 1 AND 10", name="ck_consumption_satisfaction_range"),
|
||||
{"schema": "vehicle"}
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
primary_key=True,
|
||||
default=uuid.uuid4,
|
||||
server_default=func.gen_random_uuid()
|
||||
)
|
||||
vehicle_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("vehicle.vehicle_model_definitions.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True
|
||||
)
|
||||
user_id: Mapped[int] = mapped_column(
|
||||
Integer,
|
||||
ForeignKey("identity.users.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True
|
||||
)
|
||||
driving_experience: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
reliability: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
comfort: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
consumption_satisfaction: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
comment: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())
|
||||
|
||||
# Kapcsolatok
|
||||
vehicle: Mapped["VehicleModelDefinition"] = relationship("VehicleModelDefinition", back_populates="ratings")
|
||||
user: Mapped["User"] = relationship("User", back_populates="vehicle_ratings")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"VehicleUserRating(id={self.id}, vehicle_id={self.vehicle_id}, user_id={self.user_id})"
|
||||
|
||||
@property
|
||||
def average_score(self) -> float:
|
||||
"""Számított átlagpontszám a 4 dimenzióból."""
|
||||
scores = [self.driving_experience, self.reliability, self.comfort, self.consumption_satisfaction]
|
||||
return sum(scores) / 4.0
|
||||
|
||||
|
||||
class GbCatalogDiscovery(Base):
|
||||
__tablename__ = "gb_catalog_discovery"
|
||||
__table_args__ = {"schema": "vehicle"}
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
vrm: Mapped[str] = mapped_column(String(20), unique=True)
|
||||
make: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
model: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
status: Mapped[str] = mapped_column(String(20), default='pending')
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
156
backend/app/models/vehicle/vehicle_definitions.py
Executable file
156
backend/app/models/vehicle/vehicle_definitions.py
Executable file
@@ -0,0 +1,156 @@
|
||||
# /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")
|
||||
Reference in New Issue
Block a user