Cleanup: MB 2.0 Gap Analysis előtti állapot (adatok kizárva)

This commit is contained in:
2026-02-23 09:44:02 +01:00
parent 5757754aae
commit 893f39fa15
74 changed files with 34239 additions and 2834 deletions

View File

@@ -3,10 +3,10 @@
from app.db.base_class import Base
# Identitás és Jogosultság
from .identity import User, Person, Wallet, UserRole, VerificationToken, SocialAccount
from .identity import Person, User, Wallet, VerificationToken, SocialAccount
# Szervezeti struktúra (HOZZÁADVA: OrganizationSalesAssignment)
from .organization import Organization, OrganizationMember, OrganizationSalesAssignment
from .organization import Organization, OrganizationMember, OrganizationFinancials, OrganizationSalesAssignment
# Járművek és Eszközök (Digital Twin)
from .asset import (
@@ -15,13 +15,13 @@ from .asset import (
)
# Szerviz és Szakértelem
from .service import ServiceProfile, ExpertiseTag, ServiceExpertise, ServiceStaging
from .service import ServiceProfile, ExpertiseTag, ServiceExpertise, ServiceStaging, DiscoveryParameter
# Földrajzi adatok és Címek
from .address import Address, GeoPostalCode, GeoStreet, GeoStreetType, Branch
from .address import Address, GeoPostalCode, GeoStreet, GeoStreetType, Branch, Rating
# Gamification és Economy
from .gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, Rating, PointsLedger
from .gamification import PointRule, LevelConfig, UserStats, Badge, UserBadge, PointsLedger
# Rendszerkonfiguráció (HASZNÁLJUK a frissített system.py-t!)
from .system import SystemParameter

View File

@@ -1,12 +1,11 @@
import uuid
# Hozzáadva: Boolean, text, func
from sqlalchemy import Column, String, Integer, ForeignKey, Text, DateTime, Float, Boolean, text, func
# PostgreSQL specifikus típusok
from sqlalchemy import Column, String, Integer, ForeignKey, Text, DateTime, Float, Boolean, text, func, Numeric, Index
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
from sqlalchemy.orm import relationship
from sqlalchemy.orm import relationship, foreign
from app.db.base_class import Base
class GeoPostalCode(Base):
"""Irányítószám alapú földrajzi kereső tábla."""
__tablename__ = "geo_postal_codes"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
@@ -15,6 +14,7 @@ class GeoPostalCode(Base):
city = Column(String(100), nullable=False)
class GeoStreet(Base):
"""Utcajegyzék tábla."""
__tablename__ = "geo_streets"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
@@ -22,6 +22,7 @@ class GeoStreet(Base):
name = Column(String(200), nullable=False)
class GeoStreetType(Base):
"""Közterület jellege (utca, út, köz stb.)."""
__tablename__ = "geo_street_types"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
@@ -49,7 +50,6 @@ class Address(Base):
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Add to /app/models/address.py
class Branch(Base):
"""
Telephely entitás. A fizikai helyszín, ahol a szolgáltatás vagy flotta-kezelés zajlik.
@@ -62,7 +62,7 @@ class Branch(Base):
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
name = Column(String(100), nullable=False) # pl. "Központi iroda", "Dunakeszi Szerviz"
name = Column(String(100), nullable=False)
is_main = Column(Boolean, default=False)
# Részletes címadatok (Denormalizált a gyors kereséshez)
@@ -74,9 +74,8 @@ class Branch(Base):
stairwell = Column(String(20))
floor = Column(String(20))
door = Column(String(20))
hrsz = Column(String(50)) # Helyrajzi szám
hrsz = Column(String(50))
# Telephely specifikus adatok
opening_hours = Column(JSONB, server_default=text("'{}'::jsonb"))
branch_rating = Column(Float, default=0.0)
@@ -86,5 +85,34 @@ class Branch(Base):
organization = relationship("Organization", back_populates="branches")
address = relationship("Address")
# Kapcsolat a szerviz értékelésekkel
reviews = relationship("Rating", primaryjoin="and_(Branch.id==foreign(Rating.target_id), Rating.target_type=='branch')")
# JAVÍTOTT KAPCSOLAT: target_branch_id használata target_id helyett
reviews = relationship(
"Rating",
primaryjoin="and_(Branch.id==foreign(Rating.target_branch_id))"
)
class Rating(Base):
"""Univerzális értékelési rendszer - v1.3.1"""
__tablename__ = "ratings"
__table_args__ = (
Index('idx_rating_org', 'target_organization_id'),
Index('idx_rating_user', 'target_user_id'),
Index('idx_rating_branch', 'target_branch_id'),
{"schema": "data"}
)
# Az ID most már Integer, ahogy kérted a statisztikákhoz
id = Column(Integer, primary_key=True)
author_id = Column(Integer, ForeignKey("data.users.id"), nullable=False)
# Explicit célpontok a típusbiztonság és gyorsaság érdekében
target_organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
target_user_id = Column(Integer, ForeignKey("data.users.id"), nullable=True)
target_branch_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.branches.id"), nullable=True)
score = Column(Numeric(3, 2), nullable=False) # 1.00 - 5.00
comment = Column(Text)
images = Column(JSONB, server_default=text("'[]'::jsonb"))
is_verified = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())

View File

@@ -1,5 +1,5 @@
import uuid
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint, BigInteger
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
from sqlalchemy.sql import func
@@ -16,7 +16,6 @@ class AssetCatalog(Base):
)
id = Column(Integer, primary_key=True, index=True)
# Kapcsolat az MDM-hez
master_definition_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), nullable=True)
make = Column(String, index=True, nullable=False)
@@ -27,18 +26,15 @@ class AssetCatalog(Base):
year_to = Column(Integer)
vehicle_class = Column(String)
fuel_type = Column(String, index=True)
# ÚJ MEZŐ: Kapcsolat az MDM-hez
master_definition = relationship("VehicleModelDefinition", back_populates="variants")
# --- ÚJ OSZLOPOK (Ezeket add hozzá!) ---
power_kw = Column(Integer, index=True)
engine_capacity = Column(Integer, index=True)
max_weight_kg = Column(Integer)
axle_count = Column(Integer)
euro_class = Column(String(20))
body_type = Column(String(100))
# ---------------------------------------
engine_code = Column(String)
factory_data = Column(JSONB, server_default=text("'{}'::jsonb"))
@@ -56,18 +52,25 @@ class Asset(Base):
current_organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
catalog_id = Column(Integer, ForeignKey("data.vehicle_catalog.id"))
# Moderációs mezők a Robot 3 (OCR) számára
is_verified = Column(Boolean, default=False)
verification_method = Column(String(20)) # 'manual', 'ocr', 'vin_api'
verification_notes = Column(Text, nullable=True) # Eltérések jegyzőkönyve
catalog_match_score = Column(Numeric(5, 2), nullable=True) # 0-100% egyezési arány
verification_method = Column(String(20))
verification_notes = Column(Text, nullable=True)
catalog_match_score = Column(Numeric(5, 2), nullable=True)
status = Column(String(20), default="active")
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# --- KAPCSOLATOK (A kettőzött current_org törölve, pontosítva) ---
catalog = relationship("AssetCatalog", back_populates="assets")
current_org = relationship("Organization")
# 1. Jelenlegi szervezet (Üzemeltető telephely)
current_org = relationship(
"Organization",
primaryjoin="Asset.current_organization_id == Organization.id",
foreign_keys="[Asset.current_organization_id]"
)
financials = relationship("AssetFinancials", back_populates="asset", uselist=False)
telemetry = relationship("AssetTelemetry", back_populates="asset", uselist=False)
assignments = relationship("AssetAssignment", back_populates="asset")
@@ -76,6 +79,43 @@ class Asset(Base):
reviews = relationship("AssetReview", back_populates="asset")
ownership_history = relationship("VehicleOwnership", back_populates="vehicle")
registration_uuid = Column(PG_UUID(as_uuid=True), default=uuid.uuid4, index=True, nullable=False)
is_corporate = Column(Boolean, default=False, server_default=text("false"))
# Tulajdonos és Üzembentartó oszlopok
owner_person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
owner_org_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
operator_person_id = Column(BigInteger, ForeignKey("data.persons.id"), nullable=True)
operator_org_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=True)
# 2. Tulajdonos szervezet (Kapcsolat pótolva)
owner_org = relationship(
"Organization",
primaryjoin="Asset.owner_org_id == Organization.id",
foreign_keys="[Asset.owner_org_id]"
)
# 3. Üzembentartó szervezet
operator_org = relationship(
"Organization",
primaryjoin="Asset.operator_org_id == Organization.id",
foreign_keys="[Asset.operator_org_id]"
)
# 4. Tulajdonos magánszemély
owner_person = relationship(
"Person",
primaryjoin="Asset.owner_person_id == Person.id",
foreign_keys="[Asset.owner_person_id]"
)
# 5. Üzembentartó magánszemély
operator_person = relationship(
"Person",
primaryjoin="Asset.operator_person_id == Person.id",
foreign_keys="[Asset.operator_person_id]"
)
class AssetFinancials(Base):
__tablename__ = "asset_financials"
__table_args__ = {"schema": "data"}
@@ -117,17 +157,14 @@ class AssetAssignment(Base):
id = Column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
asset_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.assets.id"), nullable=False)
organization_id = Column(Integer, ForeignKey("data.organizations.id"), nullable=False)
# ÚJ: Telephelyi hozzárendelés
branch_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.branches.id"), nullable=True)
assigned_at = Column(DateTime(timezone=True), server_default=func.now())
released_at = Column(DateTime(timezone=True), nullable=True)
status = Column(String(30), default="active")
asset = relationship("Asset", back_populates="assignments")
organization = relationship("Organization")
branch = relationship("Branch") # Új kapcsolat
branch = relationship("Branch")
class AssetEvent(Base):
__tablename__ = "asset_events"
@@ -138,6 +175,7 @@ class AssetEvent(Base):
recorded_mileage = Column(Integer)
data = Column(JSONB, server_default=text("'{}'::jsonb"))
asset = relationship("Asset", back_populates="events")
registration_uuid = Column(PG_UUID(as_uuid=True), index=True, nullable=True)
class AssetCost(Base):
__tablename__ = "asset_costs"
@@ -159,6 +197,7 @@ class AssetCost(Base):
asset = relationship("Asset", back_populates="costs")
organization = relationship("Organization")
driver = relationship("User")
registration_uuid = Column(PG_UUID(as_uuid=True), index=True, nullable=True)
class ExchangeRate(Base):
__tablename__ = "exchange_rates"
@@ -169,23 +208,17 @@ class ExchangeRate(Base):
rate = Column(Numeric(18, 6), nullable=False)
class CatalogDiscovery(Base):
"""
Discovery tábla: Ide gyűjtjük a piaci 'neveket' (pl. Citroen C3).
A Robot innen indulva keresi meg az összes létező technikai variánst.
"""
__tablename__ = "catalog_discovery"
id = Column(Integer, primary_key=True, index=True)
make = Column(String(100), nullable=False, index=True)
model = Column(String(100), nullable=False, index=True)
vehicle_class = Column(String(50), index=True) # car, motorcycle, truck, stb.
source = Column(String(50)) # 'hasznaltauto', 'mobile.de'
vehicle_class = Column(String(50), index=True)
source = Column(String(50))
status = Column(String(20), server_default=text("'pending'"), index=True)
attempts = Column(Integer, default=0)
last_attempt = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# EGYESÍTETT __table_args__
__table_args__ = (
UniqueConstraint('make', 'model', 'vehicle_class', name='_make_model_class_uc'),
{"schema": "data"}

View File

@@ -81,12 +81,3 @@ class UserBadge(Base):
user: Mapped["User"] = relationship("User")
class Rating(Base):
__tablename__ = "ratings"
__table_args__ = SCHEMA_ARGS
id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
author_id: Mapped[int] = mapped_column(Integer, ForeignKey("data.users.id"))
target_type: Mapped[str] = mapped_column(String(20))
target_id: Mapped[uuid.UUID] = mapped_column(PG_UUID(as_uuid=True))
score: Mapped[int] = mapped_column(Integer)
comment: Mapped[Optional[str]] = mapped_column(String)

View File

@@ -32,6 +32,9 @@ class Organization(Base):
id = Column(Integer, primary_key=True, index=True)
address_id = Column(PG_UUID(as_uuid=True), ForeignKey("data.addresses.id"), nullable=True)
is_anonymized = Column(Boolean, default=False, server_default=text("false"))
anonymized_at = Column(DateTime(timezone=True), nullable=True)
full_name = Column(String, nullable=False) # Hivatalos név
name = Column(String, nullable=False) # Rövid név

View File

@@ -1,6 +1,6 @@
import uuid
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, JSON, text, Text, Float
from sqlalchemy.orm import relationship
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
@@ -8,45 +8,78 @@ from app.db.base_class import Base
class ServiceProfile(Base):
"""
Szerviz szolgáltató kiterjesztett adatai.
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__ = {"schema": "data"}
__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, active, flagged, inactive
# Á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())
last_audit_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
# --- MAGÁNNYOMOZÓ (Deep Enrichment) ADATOK ---
# --- GOOGLE ÉS KÜLSŐ ADATOK ---
google_place_id = Column(String(100), unique=True)
rating = Column(Float)
user_ratings_total = Column(Integer)
# Bentley vs BMW logika: JSONB a gyors, márkaszintű szűréshez
# Példa: {"brands": ["Bentley", "Audi"], "specialty": ["engine", "tuning"]}
# --- 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(JSON, server_default=text("'{}'::jsonb"))
verification_log = Column(JSONB, server_default=text("'{}'::jsonb"))
opening_hours = Column(JSON, 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")
# --- 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"
@@ -74,56 +107,57 @@ class ServiceExpertise(Base):
class ServiceStaging(Base):
"""
Átmeneti tábla a Hunter (n8n/scraping) adatoknak.
A címek itt már darabolva (IRSZ, Város, Utca, Házszám) szerepelnek
a jobb kereshetőség és validálás érdekében.
"""
__tablename__ = "service_staging"
__table_args__ = {"schema": "data"}
__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 (A kérésedre bontva) ---
# --- 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)) # utca, út, tér...
street_type = Column(String(50))
house_number = Column(String(20))
stairwell = Column(String(20)) # lépcsőház
floor = Column(String(20)) # emelet
door = Column(String(20)) # ajtó
hrsz = Column(String(50)) # helyrajzi szám
stairwell = Column(String(20))
floor = Column(String(20))
door = Column(String(20))
hrsz = Column(String(50))
full_address = Column(String) # Eredeti string (audit célból)
# --- Elérhetőségek ---
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) # Forrás: 'OSM', 'Facebook', stb.
external_id = Column(String(100), nullable=True, index=True) # Külső ID (pl. OSM node id)
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 ---
# Itt landol a teljes robot-zsákmány minden apró részlettel
raw_data = Column(JSONB, server_default=text("'{}'::jsonb"))
# --- Státusz és Bizalom ---
# status lehet: pending (feldolgozás alatt), enriched (nyomozó által bővített),
# duplicate (már megvan), verified (élesítésre kész)
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) # pl. "autóvillamosság"
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))

View File

@@ -1,17 +1,35 @@
from sqlalchemy import Column, String, JSON, DateTime, Boolean
# backend/app/models/system.py
import enum
from sqlalchemy import Column, String, DateTime, Boolean, text, UniqueConstraint, Integer
from sqlalchemy.dialects.postgresql import JSONB # <-- JSONB-t használunk a stabilitásért
from sqlalchemy.sql import func
from app.db.base_class import Base
class SystemParameter(Base):
"""
Központi, dinamikus konfigurációs tábla.
Támogatja a többlépcsős felülbírálást (Global -> Country -> Region -> Individual).
"""
__tablename__ = "system_parameters"
__table_args__ = {"schema": "data", "extend_existing": True}
__table_args__ = (
UniqueConstraint('key', 'scope_level', 'scope_id', name='uix_param_scope'),
{"schema": "data", "extend_existing": True}
)
key = Column(String, primary_key=True, index=True, nullable=False)
# Csoportosítás az Admin felületnek (pl. 'xp', 'scout', 'routing')
# Technikai ID, hogy a 'key' ne legyen Primary Key, így engedve a hierarchiát
id = Column(Integer, primary_key=True, autoincrement=True)
key = Column(String, index=True, nullable=False) # pl. 'VEHICLE_LIMIT'
category = Column(String, index=True, server_default="general")
value = Column(JSON, nullable=False)
# A tényleges érték (JSONB-ben tárolva)
value = Column(JSONB, nullable=False) # pl. {"FREE": 1, "PREMIUM": 4}
# --- 🛡️ HIERARCHIKUS SZINTEK ---
scope_level = Column(String(30), server_default=text("'global'"), index=True)
scope_id = Column(String(50), nullable=True)
is_active = Column(Boolean, default=True)
description = Column(String)
# Kötelező audit mező: ki módosította utoljára?
last_modified_by = Column(String, nullable=True)
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), server_default=func.now())

View File

@@ -1,15 +0,0 @@
from sqlalchemy import Column, String, JSON
from app.db.base import Base
class SystemSetting(Base):
"""
Globális rendszerbeállítások tárolása.
Kulcs-Érték párok (JSON támogatással a komplex szabályokhoz).
Példa: key='FREE_VEHICLE_LIMIT', value='2'
"""
__tablename__ = "system_settings"
__table_args__ = {"schema": "data"}
key = Column(String, primary_key=True, index=True)
value = Column(JSON, nullable=False)
description = Column(String, nullable=True)

18
backend/app/models/translation.py Executable file → Normal file
View File

@@ -1,16 +1,10 @@
from sqlalchemy import Column, Integer, String, Text, Boolean, UniqueConstraint
# JAVÍTÁS: Közvetlenül a base_class-ból importálunk, hogy elkerüljük a körkörös importot
from app.db.base_class import Base
from sqlalchemy import Column, Integer, String, Text
from app.db.base_class import Base
class Translation(Base):
__tablename__ = "translations"
__table_args__ = (
UniqueConstraint("key", "lang_code", name="uq_translation_key_lang"),
{"schema": "data"}
)
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True, index=True)
key = Column(String(100), nullable=False, index=True)
lang_code = Column(String(5), nullable=False, index=True)
value = Column(Text, nullable=False)
is_published = Column(Boolean, default=False)
key = Column(String(255), index=True)
lang = Column(String(5), index=True) # pl: 'hu', 'en'
value = Column(Text)

View File

@@ -1,6 +1,7 @@
from sqlalchemy import Column, Integer, String, JSON, UniqueConstraint, text, Boolean, DateTime, ForeignKey, Numeric, Index
from sqlalchemy import Column, Integer, String, JSON, UniqueConstraint, text, Boolean, DateTime, ForeignKey, Numeric, Index, Text
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from sqlalchemy.dialects.postgresql import JSONB
from app.db.base_class import Base
class VehicleType(Base):
@@ -9,8 +10,8 @@ class VehicleType(Base):
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
code = Column(String(30), unique=True, index=True) # car, motorcycle, truck, bus, boat, etc.
name = Column(String(50)) # Megjelenítendő név
code = Column(String(30), unique=True, index=True)
name = Column(String(50))
icon = Column(String(50))
units = Column(JSON, server_default=text("'{\"power\": \"kW\", \"weight\": \"kg\", \"cargo\": \"m3\"}'::jsonb"))
@@ -24,24 +25,24 @@ class FeatureDefinition(Base):
id = Column(Integer, primary_key=True)
vehicle_type_id = Column(Integer, ForeignKey("data.vehicle_types.id"))
category = Column(String(50)) # Műszaki, Beltér, Kültér, Multimédia
category = Column(String(50))
name = Column(String(100), nullable=False)
data_type = Column(String(20), default="boolean")
vehicle_type = relationship("VehicleType", back_populates="features")
class ModelFeatureMap(Base):
"""Modell-szintű felszereltségi sablon (Alap vs Extra)"""
"""Modell-szintű felszereltségi sablon"""
__tablename__ = "model_feature_maps"
__table_args__ = {"schema": "data"}
model_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), primary_key=True)
feature_id = Column(Integer, ForeignKey("data.feature_definitions.id"), primary_key=True)
availability = Column(String(20), default="standard") # standard, optional, accessory
availability = Column(String(20), default="standard")
value = Column(String(100))
class VehicleModelDefinition(Base):
"""MDM Master rekordok - Kibővítve Deduplikációs és Évjárat mezőkkel (v1.2.5)"""
"""MDM Master rekordok - v1.3.0 Pipeline Edition (Researcher & Alchemist)"""
__tablename__ = "vehicle_model_definitions"
__table_args__ = (
UniqueConstraint('make', 'technical_code', 'vehicle_type', name='uix_make_tech_type'),
@@ -59,19 +60,24 @@ class VehicleModelDefinition(Base):
vehicle_type_id = Column(Integer, ForeignKey("data.vehicle_types.id"))
vehicle_class = Column(String(50))
# --- ÚJ MEZŐK AZ INTELLIGENS ÖSSZEFÉSÜLÉSHEZ ---
# Ha ez a rekord egy duplikátum, itt tároljuk, melyik az eredeti (Master) rekord
parent_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), nullable=True)
# Gyártási intervallum meghatározása
year_from = Column(Integer, nullable=True, index=True)
year_to = Column(Integer, nullable=True, index=True)
# Alternatív elnevezések kereshetőséghez (pl. ["Tracer 9", "MT-09 Tracer"])
synonyms = Column(JSON, server_default=text("'[]'::jsonb"))
# -----------------------------------------------
# --- LOGISZTIKAI ÉS TECHNIKAI FIX OSZLOPOK ---
# --- ROBOT VÉDELMI ÉS PIPELINE MEZŐK (v1.3.0) ---
is_manual = Column(Boolean, default=False, server_default=text("false"), index=True)
attempts = Column(Integer, default=0, server_default=text("0"), index=True)
last_error = Column(Text, nullable=True)
# Robot 2.1 "Researcher" porszívózott nyers adatai (A szemetesláda)
raw_search_context = Column(Text, nullable=True)
# Telemetria és forrás adatok (JSONB a hatékonyabb kereséshez)
research_metadata = Column(JSONB, server_default=text("'{}'::jsonb"), nullable=False)
# --------------------------------------------------
# --- TECHNIKAI FIX OSZLOPOK ---
engine_capacity = Column(Integer, index=True)
power_kw = Column(Integer, index=True)
max_weight_kg = Column(Integer, index=True)
@@ -82,23 +88,19 @@ class VehicleModelDefinition(Base):
cargo_length_mm = Column(Integer)
cargo_width_mm = Column(Integer)
cargo_height_mm = Column(Integer)
# ----------------------------------------------
specifications = Column(JSON, server_default=text("'{}'::jsonb"))
features_json = Column(JSON, server_default=text("'{}'::jsonb"))
status = Column(String(20), server_default="unverified") # unverified, ai_enriched, duplicate, manual_check
# Státusz mező hossza 30-ra növelve az automatikus migrációhoz
status = Column(String(30), server_default="unverified", index=True)
is_master = Column(Boolean, default=False)
source = Column(String(50))
source = Column(String(50))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Kapcsolatok
v_type_rel = relationship("VehicleType", back_populates="definitions")
# Önmagára hivatkozó kapcsolat a duplikációk kezeléséhez
master_record = relationship("VehicleModelDefinition", remote_side=[id], backref="merged_variants")
# Meglévő kapcsolatok megtartása
variants = relationship("AssetCatalog", back_populates="master_definition")
variants = relationship("AssetCatalog", back_populates="master_definition", primaryjoin="VehicleModelDefinition.id == AssetCatalog.master_definition_id")

View File

@@ -0,0 +1,109 @@
from sqlalchemy import Column, Integer, String, JSON, UniqueConstraint, text, Boolean, DateTime, ForeignKey, Numeric, Index, Text
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from sqlalchemy.dialects.postgresql import JSONB # PostgreSQL specifikus JSONB a hatékony kereséshez
from app.db.base_class import Base
class VehicleType(Base):
"""Jármű főtípusok sémája (Séma-gazda)"""
__tablename__ = "vehicle_types"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
code = Column(String(30), unique=True, index=True) # car, motorcycle, truck, bus, boat, etc.
name = Column(String(50)) # Megjelenítendő név
icon = Column(String(50))
units = Column(JSON, server_default=text("'{\"power\": \"kW\", \"weight\": \"kg\", \"cargo\": \"m3\"}'::jsonb"))
features = relationship("FeatureDefinition", back_populates="vehicle_type")
definitions = relationship("VehicleModelDefinition", back_populates="v_type_rel")
class FeatureDefinition(Base):
"""Globális felszereltség szótár"""
__tablename__ = "feature_definitions"
__table_args__ = {"schema": "data"}
id = Column(Integer, primary_key=True)
vehicle_type_id = Column(Integer, ForeignKey("data.vehicle_types.id"))
category = Column(String(50)) # Műszaki, Beltér, Kültér, Multimédia
name = Column(String(100), nullable=False)
data_type = Column(String(20), default="boolean")
vehicle_type = relationship("VehicleType", back_populates="features")
class ModelFeatureMap(Base):
"""Modell-szintű felszereltségi sablon (Alap vs Extra)"""
__tablename__ = "model_feature_maps"
__table_args__ = {"schema": "data"}
model_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), primary_key=True)
feature_id = Column(Integer, ForeignKey("data.feature_definitions.id"), primary_key=True)
availability = Column(String(20), default="standard") # standard, optional, accessory
value = Column(String(100))
class VehicleModelDefinition(Base):
"""MDM Master rekordok - v1.3.0 Pipeline Edition (Researcher & Alchemist)"""
__tablename__ = "vehicle_model_definitions"
__table_args__ = (
UniqueConstraint('make', 'technical_code', 'vehicle_type', name='uix_make_tech_type'),
Index('idx_vmd_lookup', 'make', 'technical_code'),
{"schema": "data"}
)
id = Column(Integer, primary_key=True)
make = Column(String(50), nullable=False, index=True)
technical_code = Column(String(50), nullable=False, index=True)
marketing_name = Column(String(100), index=True)
family_name = Column(String(100))
vehicle_type = Column(String(30), index=True)
vehicle_type_id = Column(Integer, ForeignKey("data.vehicle_types.id"))
vehicle_class = Column(String(50))
parent_id = Column(Integer, ForeignKey("data.vehicle_model_definitions.id"), nullable=True)
year_from = Column(Integer, nullable=True, index=True)
year_to = Column(Integer, nullable=True, index=True)
synonyms = Column(JSON, server_default=text("'[]'::jsonb"))
# --- ROBOT VÉDELMI ÉS PIPELINE MEZŐK (v1.3.0) ---
is_manual = Column(Boolean, default=False, server_default=text("false"), index=True)
attempts = Column(Integer, default=0, server_default=text("0"), index=True)
last_error = Column(Text, nullable=True)
# Robot 2.1 "Researcher" porszívózott nyers adatai (A szemetesláda)
raw_search_context = Column(Text, nullable=True)
# Telemetria és forrás adatok (melyik API/URL hozta az adatot)
research_metadata = Column(JSONB, server_default=text("'{}'::jsonb"), nullable=False)
# --------------------------------------------------
# --- TECHNIKAI FIX OSZLOPOK ---
engine_capacity = Column(Integer, index=True)
power_kw = Column(Integer, index=True)
max_weight_kg = Column(Integer, index=True)
axle_count = Column(Integer)
payload_capacity_kg = Column(Integer)
cargo_volume_m3 = Column(Numeric(10, 2))
cargo_length_mm = Column(Integer)
cargo_width_mm = Column(Integer)
cargo_height_mm = Column(Integer)
specifications = Column(JSON, server_default=text("'{}'::jsonb"))
features_json = Column(JSON, server_default=text("'{}'::jsonb"))
# Státusz mező hossza növelve a pipeline flagekhez
status = Column(String(30), server_default="unverified", index=True)
is_master = Column(Boolean, default=False)
source = Column(String(50)) # 'ROBOT-v1.3.0-Pipeline'
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Kapcsolatok
v_type_rel = relationship("VehicleType", back_populates="definitions")
master_record = relationship("VehicleModelDefinition", remote_side=[id], backref="merged_variants")
# AssetCatalog kapcsolat
# Megjegyzés: Ellenőrizd, hogy az AssetCatalog modell be van-e importálva a Base-be!
variants = relationship("AssetCatalog", back_populates="master_definition", primaryjoin="VehicleModelDefinition.id == AssetCatalog.master_definition_id")