Hashed conversion matching and more. #12
@@ -0,0 +1,65 @@
|
|||||||
|
"""add hashed_customer_id to reservations with cascade delete
|
||||||
|
|
||||||
|
Revision ID: 08fe946414d8
|
||||||
|
Revises: 70b2579d1d96
|
||||||
|
Create Date: 2025-11-19 14:57:27.178924
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '08fe946414d8'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '70b2579d1d96'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column('hashed_customers', 'customer_id',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=True)
|
||||||
|
op.drop_constraint(op.f('hashed_customers_customer_id_fkey'), 'hashed_customers', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'hashed_customers', 'customers', ['customer_id'], ['id'], ondelete='SET NULL')
|
||||||
|
op.drop_constraint(op.f('reservations_customer_id_fkey'), 'reservations', type_='foreignkey')
|
||||||
|
op.create_foreign_key(None, 'reservations', 'customers', ['customer_id'], ['id'], ondelete='SET NULL')
|
||||||
|
|
||||||
|
# Add hashed_customer_id column to reservations with cascade delete
|
||||||
|
op.add_column('reservations', sa.Column('hashed_customer_id', sa.Integer(), nullable=True))
|
||||||
|
op.create_index(op.f('ix_reservations_hashed_customer_id'), 'reservations', ['hashed_customer_id'], unique=False)
|
||||||
|
op.create_foreign_key(None, 'reservations', 'hashed_customers', ['hashed_customer_id'], ['id'], ondelete='CASCADE')
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
# Data migration: Populate hashed_customer_id from customer relationship
|
||||||
|
connection = op.get_bind()
|
||||||
|
update_stmt = sa.text("""
|
||||||
|
UPDATE reservations r
|
||||||
|
SET hashed_customer_id = hc.id
|
||||||
|
FROM hashed_customers hc
|
||||||
|
WHERE r.customer_id = hc.customer_id
|
||||||
|
AND hc.customer_id IS NOT NULL
|
||||||
|
""")
|
||||||
|
connection.execute(update_stmt)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
# Drop the hashed_customer_id column and its constraints
|
||||||
|
op.drop_constraint(None, 'reservations', type_='foreignkey')
|
||||||
|
op.drop_index(op.f('ix_reservations_hashed_customer_id'), table_name='reservations')
|
||||||
|
op.drop_column('reservations', 'hashed_customer_id')
|
||||||
|
|
||||||
|
op.drop_constraint(None, 'reservations', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('reservations_customer_id_fkey'), 'reservations', 'customers', ['customer_id'], ['id'])
|
||||||
|
op.drop_constraint(None, 'hashed_customers', type_='foreignkey')
|
||||||
|
op.create_foreign_key(op.f('hashed_customers_customer_id_fkey'), 'hashed_customers', 'customers', ['customer_id'], ['id'])
|
||||||
|
op.alter_column('hashed_customers', 'customer_id',
|
||||||
|
existing_type=sa.INTEGER(),
|
||||||
|
nullable=False)
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -253,7 +253,8 @@ class ConversionService:
|
|||||||
# Load all reservations with their hashed customers in one query
|
# Load all reservations with their hashed customers in one query
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
query = select(Reservation).options(
|
query = select(Reservation).options(
|
||||||
selectinload(Reservation.customer).selectinload(Customer.hashed_version)
|
selectinload(Reservation.customer).selectinload(Customer.hashed_version),
|
||||||
|
selectinload(Reservation.hashed_customer)
|
||||||
)
|
)
|
||||||
result = await session.execute(query)
|
result = await session.execute(query)
|
||||||
reservations = result.scalars().all()
|
reservations = result.scalars().all()
|
||||||
@@ -265,9 +266,11 @@ class ConversionService:
|
|||||||
hotel_code = reservation.hotel_code
|
hotel_code = reservation.hotel_code
|
||||||
if hotel_code not in self._reservation_cache:
|
if hotel_code not in self._reservation_cache:
|
||||||
self._reservation_cache[hotel_code] = []
|
self._reservation_cache[hotel_code] = []
|
||||||
# Cache the hashed customer instead of raw customer
|
# Cache the hashed customer - prefer direct relationship, fall back to customer relationship
|
||||||
hashed_customer = None
|
hashed_customer = None
|
||||||
if reservation.customer and reservation.customer.hashed_version:
|
if reservation.hashed_customer:
|
||||||
|
hashed_customer = reservation.hashed_customer
|
||||||
|
elif reservation.customer and reservation.customer.hashed_version:
|
||||||
hashed_customer = reservation.customer.hashed_version
|
hashed_customer = reservation.customer.hashed_version
|
||||||
self._reservation_cache[hotel_code].append(
|
self._reservation_cache[hotel_code].append(
|
||||||
(reservation, hashed_customer)
|
(reservation, hashed_customer)
|
||||||
|
|||||||
@@ -478,6 +478,7 @@ class Reservation(Base):
|
|||||||
__tablename__ = "reservations"
|
__tablename__ = "reservations"
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
customer_id = Column(Integer, ForeignKey("customers.id", ondelete="SET NULL"))
|
customer_id = Column(Integer, ForeignKey("customers.id", ondelete="SET NULL"))
|
||||||
|
hashed_customer_id = Column(Integer, ForeignKey("hashed_customers.id", ondelete="CASCADE"))
|
||||||
unique_id = Column(String, unique=True)
|
unique_id = Column(String, unique=True)
|
||||||
md5_unique_id = Column(String(32), unique=True) # max length 32 guaranteed
|
md5_unique_id = Column(String(32), unique=True) # max length 32 guaranteed
|
||||||
start_date = Column(Date)
|
start_date = Column(Date)
|
||||||
@@ -507,6 +508,7 @@ class Reservation(Base):
|
|||||||
room_classification_code = Column(String)
|
room_classification_code = Column(String)
|
||||||
room_type = Column(String)
|
room_type = Column(String)
|
||||||
customer = relationship("Customer", back_populates="reservations")
|
customer = relationship("Customer", back_populates="reservations")
|
||||||
|
hashed_customer = relationship("HashedCustomer", backref="reservations")
|
||||||
|
|
||||||
|
|
||||||
# Table for tracking acknowledged requests by client
|
# Table for tracking acknowledged requests by client
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from typing import Optional
|
|||||||
from sqlalchemy import and_, select
|
from sqlalchemy import and_, select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from .db import AckedRequest, Customer, Reservation
|
from .db import AckedRequest, Customer, HashedCustomer, Reservation
|
||||||
from .schemas import ReservationData
|
from .schemas import ReservationData
|
||||||
|
|
||||||
|
|
||||||
@@ -63,6 +63,19 @@ class ReservationService:
|
|||||||
reservation = self._convert_reservation_data_to_db(
|
reservation = self._convert_reservation_data_to_db(
|
||||||
reservation_data, customer_id
|
reservation_data, customer_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Automatically populate hashed_customer_id from the customer
|
||||||
|
# Since hashed_customer is always created when a customer is created,
|
||||||
|
# we can get it by querying for the hashed_customer with matching customer_id
|
||||||
|
hashed_customer_result = await self.session.execute(
|
||||||
|
select(HashedCustomer).where(
|
||||||
|
HashedCustomer.customer_id == customer_id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
hashed_customer = hashed_customer_result.scalar_one_or_none()
|
||||||
|
if hashed_customer:
|
||||||
|
reservation.hashed_customer_id = hashed_customer.id
|
||||||
|
|
||||||
self.session.add(reservation)
|
self.session.add(reservation)
|
||||||
|
|
||||||
if auto_commit:
|
if auto_commit:
|
||||||
|
|||||||
Reference in New Issue
Block a user