250 lines
12 KiB
Python
250 lines
12 KiB
Python
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import Any
|
|
from uuid import UUID
|
|
|
|
from sqlalchemy import (
|
|
CheckConstraint,
|
|
DateTime,
|
|
ForeignKey,
|
|
ForeignKeyConstraint,
|
|
Numeric,
|
|
String,
|
|
Text,
|
|
UniqueConstraint,
|
|
)
|
|
from sqlalchemy.dialects.postgresql import JSONB, UUID as PGUUID
|
|
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
pass
|
|
|
|
|
|
class AttributeDefinitionRecord(Base):
|
|
__tablename__ = "attribute_definitions"
|
|
__table_args__ = (
|
|
CheckConstraint(
|
|
"value_type IN ('number', 'string', 'boolean', 'string_list')",
|
|
name="ck_attribute_definitions_value_type",
|
|
),
|
|
)
|
|
|
|
key: Mapped[str] = mapped_column(String(128), primary_key=True)
|
|
value_type: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
canonical_unit: Mapped[str | None] = mapped_column(String(32))
|
|
minimum: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
|
|
maximum: Mapped[Decimal | None] = mapped_column(Numeric(12, 4))
|
|
description: Mapped[str | None] = mapped_column(Text)
|
|
|
|
|
|
class SourceRecord(Base):
|
|
__tablename__ = "sources"
|
|
|
|
id: Mapped[str] = mapped_column(String(64), primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
adapter_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
|
|
|
|
class ScoringModelRecord(Base):
|
|
__tablename__ = "scoring_models"
|
|
|
|
version: Mapped[str] = mapped_column(String(64), primary_key=True)
|
|
algorithm_id: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
definition: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
|
|
definition_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
|
|
|
|
class ProductProfileRecord(Base):
|
|
__tablename__ = "product_profiles"
|
|
__table_args__ = (
|
|
CheckConstraint("version > 0", name="ck_product_profiles_version"),
|
|
)
|
|
|
|
id: Mapped[str] = mapped_column(String(128), primary_key=True)
|
|
version: Mapped[int] = mapped_column(primary_key=True)
|
|
name: Mapped[str] = mapped_column(String(256), nullable=False)
|
|
category: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
scoring_version: Mapped[str] = mapped_column(ForeignKey("scoring_models.version"), nullable=False)
|
|
definition: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
|
|
definition_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
|
|
|
|
class ProductRecord(Base):
|
|
__tablename__ = "products"
|
|
|
|
id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True)
|
|
category: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
brand: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
name: Mapped[str] = mapped_column(String(256), nullable=False)
|
|
model: Mapped[str | None] = mapped_column(String(128))
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
|
|
variants: Mapped[list["ProductVariantRecord"]] = relationship(back_populates="product")
|
|
|
|
|
|
class ProductVariantRecord(Base):
|
|
__tablename__ = "product_variants"
|
|
|
|
id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True)
|
|
product_id: Mapped[UUID] = mapped_column(ForeignKey("products.id"), nullable=False, index=True)
|
|
name: Mapped[str] = mapped_column(String(256), nullable=False)
|
|
manufacturer_sku: Mapped[str | None] = mapped_column(String(128))
|
|
identifiers: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False, default=dict)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
|
|
product: Mapped[ProductRecord] = relationship(back_populates="variants")
|
|
|
|
|
|
class VariantAttributeRecord(Base):
|
|
__tablename__ = "variant_attributes"
|
|
__table_args__ = (
|
|
CheckConstraint("confidence >= 0 AND confidence <= 1", name="ck_variant_attributes_confidence"),
|
|
CheckConstraint(
|
|
"normalization_status IN ('normalized', 'unknown')",
|
|
name="ck_variant_attributes_status",
|
|
),
|
|
)
|
|
|
|
id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True)
|
|
variant_id: Mapped[UUID] = mapped_column(ForeignKey("product_variants.id"), nullable=False)
|
|
attribute_key: Mapped[str] = mapped_column(ForeignKey("attribute_definitions.key"), nullable=False)
|
|
source_id: Mapped[str] = mapped_column(ForeignKey("sources.id"), nullable=False)
|
|
raw_value: Mapped[Any] = mapped_column(JSONB, nullable=False)
|
|
normalized_value: Mapped[Any] = mapped_column(JSONB)
|
|
unit: Mapped[str | None] = mapped_column(String(32))
|
|
normalization_status: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
normalizer_version: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
confidence: Mapped[Decimal] = mapped_column(Numeric(4, 3), nullable=False)
|
|
source_reference: Mapped[str] = mapped_column(String(256), nullable=False)
|
|
observed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
ingested_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
normalization_error: Mapped[str | None] = mapped_column(Text)
|
|
|
|
|
|
class OfferRecord(Base):
|
|
__tablename__ = "offers"
|
|
__table_args__ = (
|
|
UniqueConstraint("source_id", "external_id", name="uq_offers_source_external"),
|
|
CheckConstraint("currency ~ '^[A-Z]{3}$'", name="ck_offers_currency"),
|
|
CheckConstraint("condition IN ('new', 'refurbished', 'used')", name="ck_offers_condition"),
|
|
)
|
|
|
|
id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True)
|
|
variant_id: Mapped[UUID] = mapped_column(ForeignKey("product_variants.id"), nullable=False, index=True)
|
|
source_id: Mapped[str] = mapped_column(ForeignKey("sources.id"), nullable=False)
|
|
external_id: Mapped[str] = mapped_column(String(256), nullable=False)
|
|
seller: Mapped[str] = mapped_column(String(256), nullable=False)
|
|
url: Mapped[str | None] = mapped_column(Text)
|
|
market: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
currency: Mapped[str] = mapped_column(String(3), nullable=False)
|
|
condition: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
|
|
|
|
class PriceObservationRecord(Base):
|
|
__tablename__ = "price_observations"
|
|
__table_args__ = (
|
|
UniqueConstraint("offer_id", "external_observation_id", name="uq_price_obs_offer_external"),
|
|
CheckConstraint("item_price > 0", name="ck_price_obs_item_price"),
|
|
CheckConstraint("shipping_price IS NULL OR shipping_price >= 0", name="ck_price_obs_shipping"),
|
|
CheckConstraint("confidence >= 0 AND confidence <= 1", name="ck_price_obs_confidence"),
|
|
CheckConstraint(
|
|
"availability IN ('in_stock', 'out_of_stock', 'unknown')",
|
|
name="ck_price_obs_availability",
|
|
),
|
|
CheckConstraint("currency ~ '^[A-Z]{3}$'", name="ck_price_obs_currency"),
|
|
)
|
|
|
|
id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True)
|
|
offer_id: Mapped[UUID] = mapped_column(ForeignKey("offers.id"), nullable=False)
|
|
source_id: Mapped[str] = mapped_column(ForeignKey("sources.id"), nullable=False)
|
|
external_observation_id: Mapped[str] = mapped_column(String(256), nullable=False)
|
|
observed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
ingested_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
item_price: Mapped[Decimal] = mapped_column(Numeric(12, 2), nullable=False)
|
|
shipping_price: Mapped[Decimal | None] = mapped_column(Numeric(12, 2))
|
|
currency: Mapped[str] = mapped_column(String(3), nullable=False)
|
|
availability: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
raw_payload: Mapped[Any] = mapped_column(JSONB, nullable=False)
|
|
confidence: Mapped[Decimal] = mapped_column(Numeric(4, 3), nullable=False)
|
|
|
|
|
|
class EvaluationRecord(Base):
|
|
__tablename__ = "evaluations"
|
|
__table_args__ = (
|
|
ForeignKeyConstraint(
|
|
["profile_id", "profile_version"],
|
|
["product_profiles.id", "product_profiles.version"],
|
|
name="fk_evaluations_profile",
|
|
),
|
|
CheckConstraint(
|
|
"product_eligibility IN ('ELIGIBLE', 'INELIGIBLE', 'UNKNOWN')",
|
|
name="ck_eval_product_eligibility",
|
|
),
|
|
CheckConstraint(
|
|
"offer_eligibility IN ('ELIGIBLE', 'INELIGIBLE', 'UNKNOWN')",
|
|
name="ck_eval_offer_eligibility",
|
|
),
|
|
CheckConstraint(
|
|
"overall_eligibility IN ('ELIGIBLE', 'INELIGIBLE', 'UNKNOWN')",
|
|
name="ck_eval_overall_eligibility",
|
|
),
|
|
CheckConstraint(
|
|
"product_score IS NULL OR (product_score >= 0 AND product_score <= 100)",
|
|
name="ck_eval_product_score",
|
|
),
|
|
CheckConstraint(
|
|
"price_score IS NULL OR (price_score >= 0 AND price_score <= 100)",
|
|
name="ck_eval_price_score",
|
|
),
|
|
CheckConstraint(
|
|
"deal_score IS NULL OR (deal_score >= 0 AND deal_score <= 100)",
|
|
name="ck_eval_deal_score",
|
|
),
|
|
)
|
|
|
|
id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True)
|
|
variant_id: Mapped[UUID] = mapped_column(ForeignKey("product_variants.id"), nullable=False, index=True)
|
|
offer_id: Mapped[UUID] = mapped_column(ForeignKey("offers.id"), nullable=False)
|
|
profile_id: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
profile_version: Mapped[int] = mapped_column(nullable=False)
|
|
scoring_version: Mapped[str] = mapped_column(ForeignKey("scoring_models.version"), nullable=False)
|
|
as_of: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
snapshot_cutoff: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
scoring_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
engine_build: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
product_eligibility: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
offer_eligibility: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
overall_eligibility: Mapped[str] = mapped_column(String(16), nullable=False)
|
|
product_score: Mapped[Decimal | None] = mapped_column(Numeric(6, 2))
|
|
price_score: Mapped[Decimal | None] = mapped_column(Numeric(6, 2))
|
|
deal_score: Mapped[Decimal | None] = mapped_column(Numeric(6, 2))
|
|
classification: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
price_reference_scope: Mapped[str] = mapped_column(String(32), nullable=False)
|
|
input_snapshot: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
|
|
result_snapshot: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
|
|
|
|
components: Mapped[list["EvaluationComponentRecord"]] = relationship(back_populates="evaluation")
|
|
|
|
|
|
class EvaluationComponentRecord(Base):
|
|
__tablename__ = "evaluation_components"
|
|
__table_args__ = (
|
|
UniqueConstraint("evaluation_id", "kind", "component_key", name="uq_eval_component"),
|
|
)
|
|
|
|
id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True)
|
|
evaluation_id: Mapped[UUID] = mapped_column(ForeignKey("evaluations.id"), nullable=False)
|
|
kind: Mapped[str] = mapped_column(String(64), nullable=False)
|
|
component_key: Mapped[str] = mapped_column(String(128), nullable=False)
|
|
raw_score: Mapped[Decimal | None] = mapped_column(Numeric(12, 6))
|
|
weight: Mapped[Decimal | None] = mapped_column(Numeric(12, 6))
|
|
contribution: Mapped[Decimal | None] = mapped_column(Numeric(12, 6))
|
|
details: Mapped[dict[str, Any]] = mapped_column(JSONB, nullable=False)
|
|
|
|
evaluation: Mapped[EvaluationRecord] = relationship(back_populates="components")
|