Migration successfull. Does not cause any problems and new foreign keys work
This commit is contained in:
@@ -4,8 +4,6 @@ import os
|
||||
from collections.abc import AsyncGenerator, Callable
|
||||
from typing import TypeVar
|
||||
|
||||
from .const import WebhookStatus
|
||||
|
||||
from sqlalchemy import (
|
||||
JSON,
|
||||
Boolean,
|
||||
@@ -17,6 +15,7 @@ from sqlalchemy import (
|
||||
ForeignKeyConstraint,
|
||||
Index,
|
||||
Integer,
|
||||
MetaData,
|
||||
String,
|
||||
UniqueConstraint,
|
||||
func,
|
||||
@@ -30,6 +29,7 @@ from sqlalchemy.ext.asyncio import (
|
||||
)
|
||||
from sqlalchemy.orm import backref, declarative_base, relationship
|
||||
|
||||
from .const import WebhookStatus
|
||||
from .logging_config import get_logger
|
||||
|
||||
_LOGGER = get_logger(__name__)
|
||||
@@ -58,7 +58,16 @@ class Base:
|
||||
# __table_args__ = {"schema": _SCHEMA}
|
||||
|
||||
|
||||
Base = declarative_base(cls=Base)
|
||||
# Define naming convention for constraints
|
||||
metadata = MetaData(naming_convention={
|
||||
"ix": "ix_%(column_0_label)s",
|
||||
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
||||
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||
"pk": "pk_%(table_name)s"
|
||||
})
|
||||
|
||||
Base = declarative_base(cls=Base, metadata=metadata)
|
||||
|
||||
# Type variable for async functions
|
||||
T = TypeVar("T")
|
||||
@@ -353,7 +362,10 @@ class HashedCustomer(Base):
|
||||
__tablename__ = "hashed_customers"
|
||||
id = Column(Integer, primary_key=True)
|
||||
customer_id = Column(
|
||||
Integer, ForeignKey("customers.id", ondelete="SET NULL"), unique=True, nullable=True
|
||||
Integer,
|
||||
ForeignKey("customers.id", ondelete="SET NULL"),
|
||||
unique=True,
|
||||
nullable=True,
|
||||
)
|
||||
contact_id = Column(String, unique=True) # Keep unhashed for reference
|
||||
hashed_email = Column(String(64)) # SHA256 produces 64 hex chars
|
||||
@@ -367,7 +379,9 @@ class HashedCustomer(Base):
|
||||
hashed_birth_date = Column(String(64))
|
||||
created_at = Column(DateTime(timezone=True))
|
||||
|
||||
customer = relationship("Customer", backref=backref("hashed_version", uselist=False, lazy="joined"))
|
||||
customer = relationship(
|
||||
"Customer", backref=backref("hashed_version", uselist=False, lazy="joined")
|
||||
)
|
||||
|
||||
|
||||
class ConversionGuest(Base):
|
||||
@@ -383,7 +397,13 @@ class ConversionGuest(Base):
|
||||
__tablename__ = "conversion_guests"
|
||||
|
||||
# Natural keys from PMS - composite primary key
|
||||
hotel_id = Column(String(50), ForeignKey("hotels.hotel_id", ondelete="CASCADE"), nullable=False, primary_key=True, index=True)
|
||||
hotel_id = Column(
|
||||
String(50),
|
||||
ForeignKey("hotels.hotel_id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
primary_key=True,
|
||||
index=True,
|
||||
)
|
||||
guest_id = Column(Integer, nullable=False, primary_key=True, index=True)
|
||||
|
||||
# Unhashed guest information (for reference/transition period)
|
||||
@@ -401,10 +421,14 @@ class ConversionGuest(Base):
|
||||
hashed_birth_date = Column(String(64))
|
||||
|
||||
# Matched customer reference (nullable, filled after matching)
|
||||
hashed_customer_id = Column(Integer, ForeignKey("hashed_customers.id"), nullable=True, index=True)
|
||||
hashed_customer_id = Column(
|
||||
Integer, ForeignKey("hashed_customers.id"), nullable=True, index=True
|
||||
)
|
||||
|
||||
# Guest classification
|
||||
is_regular = Column(Boolean, default=False) # True if guest has many prior stays before appearing in our reservations
|
||||
is_regular = Column(
|
||||
Boolean, default=False
|
||||
) # True if guest has many prior stays before appearing in our reservations
|
||||
|
||||
# Metadata
|
||||
first_seen = Column(DateTime(timezone=True))
|
||||
@@ -428,7 +452,7 @@ class ConversionGuest(Base):
|
||||
def create_from_conversion_data(
|
||||
cls,
|
||||
hotel_id: str,
|
||||
guest_id: str | None,
|
||||
guest_id: int | None,
|
||||
guest_first_name: str | None,
|
||||
guest_last_name: str | None,
|
||||
guest_email: str | None,
|
||||
@@ -483,7 +507,9 @@ class ConversionGuest(Base):
|
||||
self.hashed_country_code = self._normalize_and_hash(guest_country_code)
|
||||
if guest_birth_date:
|
||||
self.guest_birth_date = guest_birth_date
|
||||
self.hashed_birth_date = self._normalize_and_hash(guest_birth_date.isoformat())
|
||||
self.hashed_birth_date = self._normalize_and_hash(
|
||||
guest_birth_date.isoformat()
|
||||
)
|
||||
self.last_seen = now
|
||||
|
||||
|
||||
@@ -491,7 +517,9 @@ class Reservation(Base):
|
||||
__tablename__ = "reservations"
|
||||
id = Column(Integer, primary_key=True)
|
||||
customer_id = Column(Integer, ForeignKey("customers.id", ondelete="SET NULL"))
|
||||
hashed_customer_id = Column(Integer, ForeignKey("hashed_customers.id", ondelete="CASCADE"))
|
||||
hashed_customer_id = Column(
|
||||
Integer, ForeignKey("hashed_customers.id", ondelete="CASCADE")
|
||||
)
|
||||
unique_id = Column(String, unique=True)
|
||||
md5_unique_id = Column(String(32), unique=True) # max length 32 guaranteed
|
||||
start_date = Column(Date)
|
||||
@@ -578,9 +606,18 @@ class Conversion(Base):
|
||||
)
|
||||
|
||||
# Reservation metadata from XML
|
||||
hotel_id = Column(String(50), ForeignKey("hotels.hotel_id", ondelete="CASCADE"), nullable=False, index=True) # hotelID attribute
|
||||
pms_reservation_id = Column(Integer, nullable=False, index=True) # id attribute from reservation
|
||||
guest_id = Column(Integer, nullable=True, index=True) # PMS guest ID, FK to conversion_guests
|
||||
hotel_id = Column(
|
||||
String(50),
|
||||
ForeignKey("hotels.hotel_id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
) # hotelID attribute
|
||||
pms_reservation_id = Column(
|
||||
Integer, nullable=False, index=True
|
||||
) # id attribute from reservation
|
||||
guest_id = Column(
|
||||
Integer, nullable=True, index=True
|
||||
) # PMS guest ID, FK to conversion_guests
|
||||
|
||||
reservation_number = Column(String) # number attribute
|
||||
reservation_date = Column(Date) # date attribute (when reservation was made)
|
||||
@@ -588,9 +625,6 @@ class Conversion(Base):
|
||||
reservation_type = Column(String) # type attribute (e.g., "reservation")
|
||||
booking_channel = Column(String) # bookingChannel attribute
|
||||
|
||||
|
||||
|
||||
|
||||
# Advertising/tracking data - used for matching to existing reservations
|
||||
advertising_medium = Column(
|
||||
String, index=True
|
||||
@@ -603,7 +637,9 @@ class Conversion(Base):
|
||||
) # advertisingCampagne (contains fbclid/gclid)
|
||||
|
||||
# Attribution flags - track how this conversion was matched
|
||||
directly_attributable = Column(Boolean, default=False) # Matched by ID (high confidence)
|
||||
directly_attributable = Column(
|
||||
Boolean, default=False
|
||||
) # Matched by ID (high confidence)
|
||||
guest_matched = Column(Boolean, default=False) # Matched by guest details only
|
||||
|
||||
# Metadata
|
||||
@@ -617,7 +653,9 @@ class Conversion(Base):
|
||||
["conversion_guests.hotel_id", "conversion_guests.guest_id"],
|
||||
ondelete="SET NULL",
|
||||
),
|
||||
UniqueConstraint("hotel_id", "pms_reservation_id", name="uq_conversion_hotel_reservation"),
|
||||
UniqueConstraint(
|
||||
"hotel_id", "pms_reservation_id", name="uq_conversion_hotel_reservation"
|
||||
),
|
||||
)
|
||||
|
||||
# Relationships
|
||||
@@ -690,7 +728,10 @@ class HotelInventory(Base):
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
hotel_id = Column(
|
||||
String(50), ForeignKey("hotels.hotel_id", ondelete="CASCADE"), nullable=False, index=True
|
||||
String(50),
|
||||
ForeignKey("hotels.hotel_id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
inv_type_code = Column(String(8), nullable=False, index=True)
|
||||
inv_code = Column(String(16), nullable=True, index=True)
|
||||
@@ -726,7 +767,10 @@ class RoomAvailability(Base):
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
inventory_id = Column(
|
||||
Integer, ForeignKey("hotel_inventory.id", ondelete="CASCADE"), nullable=False, index=True
|
||||
Integer,
|
||||
ForeignKey("hotel_inventory.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
date = Column(Date, nullable=False, index=True)
|
||||
count_type_2 = Column(Integer, nullable=True)
|
||||
@@ -739,7 +783,9 @@ class RoomAvailability(Base):
|
||||
inventory_item = relationship("HotelInventory", back_populates="availability")
|
||||
|
||||
__table_args__ = (
|
||||
UniqueConstraint("inventory_id", "date", name="uq_room_availability_unique_key"),
|
||||
UniqueConstraint(
|
||||
"inventory_id", "date", name="uq_room_availability_unique_key"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -787,7 +833,9 @@ class WebhookEndpoint(Base):
|
||||
id = Column(Integer, primary_key=True)
|
||||
|
||||
# Hotel association
|
||||
hotel_id = Column(String(50), ForeignKey("hotels.hotel_id"), nullable=False, index=True)
|
||||
hotel_id = Column(
|
||||
String(50), ForeignKey("hotels.hotel_id"), nullable=False, index=True
|
||||
)
|
||||
|
||||
# Webhook configuration
|
||||
webhook_secret = Column(String(64), unique=True, nullable=False, index=True)
|
||||
@@ -803,7 +851,7 @@ class WebhookEndpoint(Base):
|
||||
webhook_requests = relationship("WebhookRequest", back_populates="webhook_endpoint")
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_webhook_endpoint_hotel_type', 'hotel_id', 'webhook_type'),
|
||||
Index("idx_webhook_endpoint_hotel_type", "hotel_id", "webhook_type"),
|
||||
)
|
||||
|
||||
|
||||
@@ -816,11 +864,17 @@ class WebhookRequest(Base):
|
||||
|
||||
# Request identification
|
||||
payload_hash = Column(String(64), unique=True, nullable=False, index=True) # SHA256
|
||||
webhook_endpoint_id = Column(Integer, ForeignKey("webhook_endpoints.id"), nullable=True, index=True)
|
||||
hotel_id = Column(String(50), ForeignKey("hotels.hotel_id"), nullable=True, index=True)
|
||||
webhook_endpoint_id = Column(
|
||||
Integer, ForeignKey("webhook_endpoints.id"), nullable=True, index=True
|
||||
)
|
||||
hotel_id = Column(
|
||||
String(50), ForeignKey("hotels.hotel_id"), nullable=True, index=True
|
||||
)
|
||||
|
||||
# Processing tracking
|
||||
status = Column(String(20), nullable=False, default=WebhookStatus.PENDING.value, index=True)
|
||||
status = Column(
|
||||
String(20), nullable=False, default=WebhookStatus.PENDING.value, index=True
|
||||
)
|
||||
# Status values: 'pending', 'processing', 'completed', 'failed' set by Enum WebhookStatus
|
||||
|
||||
processing_started_at = Column(DateTime(timezone=True), nullable=True)
|
||||
@@ -841,16 +895,20 @@ class WebhookRequest(Base):
|
||||
|
||||
# Result tracking
|
||||
created_customer_id = Column(Integer, ForeignKey("customers.id"), nullable=True)
|
||||
created_reservation_id = Column(Integer, ForeignKey("reservations.id"), nullable=True)
|
||||
created_reservation_id = Column(
|
||||
Integer, ForeignKey("reservations.id"), nullable=True
|
||||
)
|
||||
|
||||
# Relationships
|
||||
webhook_endpoint = relationship("WebhookEndpoint", back_populates="webhook_requests")
|
||||
webhook_endpoint = relationship(
|
||||
"WebhookEndpoint", back_populates="webhook_requests"
|
||||
)
|
||||
hotel = relationship("Hotel")
|
||||
customer = relationship("Customer")
|
||||
reservation = relationship("Reservation")
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_webhook_status_created', 'status', 'created_at'),
|
||||
Index('idx_webhook_hotel_created', 'hotel_id', 'created_at'),
|
||||
Index('idx_webhook_purge_candidate', 'status', 'purged_at', 'created_at'),
|
||||
Index("idx_webhook_status_created", "status", "created_at"),
|
||||
Index("idx_webhook_hotel_created", "hotel_id", "created_at"),
|
||||
Index("idx_webhook_purge_candidate", "status", "purged_at", "created_at"),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user