diff --git a/backend/app/models/vehicle.py b/backend/app/models/vehicle.py new file mode 100644 index 0000000..d7a6fc4 --- /dev/null +++ b/backend/app/models/vehicle.py @@ -0,0 +1,100 @@ +from datetime import datetime, date, timezone +from decimal import Decimal +from sqlalchemy import ( + String, Boolean, Date, DateTime, DECIMAL, Integer, SmallInteger, + ForeignKey, Text, JSON +) +from sqlalchemy.orm import Mapped, mapped_column, relationship +from app.core.database import Base + + +class VehicleRegistry(Base): + __tablename__ = "vehicle_registry" + + plate: Mapped[str] = mapped_column(String(10), primary_key=True) + vin: Mapped[str | None] = mapped_column(String(17)) + vehicle_type: Mapped[str | None] = mapped_column(String(50)) + registration_date: Mapped[date | None] = mapped_column(Date) + homologation_code: Mapped[str | None] = mapped_column(String(50)) + engine_code: Mapped[str | None] = mapped_column(String(20)) + last_revision_date: Mapped[date | None] = mapped_column(Date) + foreign_plate: Mapped[str | None] = mapped_column(String(20)) + foreign_registration_date: Mapped[date | None] = mapped_column(Date) + foreign_country: Mapped[str | None] = mapped_column(String(50)) + is_foreign_registered: Mapped[bool | None] = mapped_column(Boolean) + source: Mapped[str | None] = mapped_column(String(20)) + raw_response: Mapped[dict | None] = mapped_column(JSON) + fetched_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) + expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) + selected_motornet_code: Mapped[str | None] = mapped_column(String(20)) + + version_candidates: Mapped[list["PlateVersionCandidate"]] = relationship( + "PlateVersionCandidate", back_populates="registry" + ) + + +class VehicleVersion(Base): + __tablename__ = "vehicle_versions" + + motornet_code: Mapped[str] = mapped_column(String(20), primary_key=True) + brand_acronym: Mapped[str | None] = mapped_column(String(10)) + brand_name: Mapped[str | None] = mapped_column(String(50)) + model_code: Mapped[str | None] = mapped_column(String(10)) + model_description: Mapped[str | None] = mapped_column(String(100)) + gamma_code: Mapped[str | None] = mapped_column(String(10)) + gamma_description: Mapped[str | None] = mapped_column(String(100)) + series_code: Mapped[str | None] = mapped_column(String(10)) + series_description: Mapped[str | None] = mapped_column(String(100)) + historical_group_code: Mapped[str | None] = mapped_column(String(10)) + historical_group_desc: Mapped[str | None] = mapped_column(String(100)) + cod_desc_model_code: Mapped[str | None] = mapped_column(String(10)) + cod_desc_model_desc: Mapped[str | None] = mapped_column(String(100)) + version_label: Mapped[str | None] = mapped_column(String(200)) + body_type: Mapped[str | None] = mapped_column(String(10)) + doors: Mapped[int | None] = mapped_column(SmallInteger) + wheelbase: Mapped[int | None] = mapped_column(SmallInteger) + list_price: Mapped[Decimal | None] = mapped_column(DECIMAL(10, 2)) + production_start: Mapped[date | None] = mapped_column(Date) + production_end: Mapped[date | None] = mapped_column(Date) + commercial_start: Mapped[date | None] = mapped_column(Date) + commercial_end: Mapped[date | None] = mapped_column(Date) + first_seen_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) + last_seen_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) + seen_count: Mapped[int] = mapped_column(Integer, default=1) + source: Mapped[str | None] = mapped_column(String(20)) + + plate_candidates: Mapped[list["PlateVersionCandidate"]] = relationship( + "PlateVersionCandidate", back_populates="version" + ) + + +class PlateVersionCandidate(Base): + __tablename__ = "plate_version_candidates" + + plate: Mapped[str] = mapped_column( + String(10), ForeignKey("vehicle_registry.plate"), primary_key=True + ) + motornet_code: Mapped[str] = mapped_column( + String(20), ForeignKey("vehicle_versions.motornet_code"), primary_key=True + ) + is_selected: Mapped[bool] = mapped_column(Boolean, default=False) + + registry: Mapped["VehicleRegistry"] = relationship( + "VehicleRegistry", back_populates="version_candidates" + ) + version: Mapped["VehicleVersion"] = relationship( + "VehicleVersion", back_populates="plate_candidates" + ) + + +class ApiUsageLog(Base): + __tablename__ = "api_usage_log" + + id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) + source: Mapped[str | None] = mapped_column(String(20)) + plate: Mapped[str | None] = mapped_column(String(10)) + hit_cache: Mapped[bool] = mapped_column(Boolean, default=False) + remaining_queries: Mapped[int | None] = mapped_column(Integer) + called_at: Mapped[datetime] = mapped_column( + DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) + )