Not quite done but mostly starting to remove hashed_customer references

This commit is contained in:
Jonas Linter
2025-12-03 12:00:02 +01:00
parent 3175342cb2
commit ad29a0a2f6
7 changed files with 75 additions and 72 deletions

View File

@@ -629,10 +629,8 @@ class ConversionService:
from sqlalchemy.orm import selectinload
query = select(Reservation).options(
selectinload(Reservation.customer).selectinload(
Customer.hashed_version
),
selectinload(Reservation.hashed_customer),
selectinload(Reservation.customer),
)
result = await session.execute(query)
reservations = result.scalars().all()
@@ -645,13 +643,11 @@ class ConversionService:
if hotel_code not in self._reservation_cache:
self._reservation_cache[hotel_code] = []
# Cache the hashed customer - prefer direct relationship, fall back to customer relationship
hashed_customer = None
if reservation.hashed_customer:
hashed_customer = reservation.hashed_customer
elif reservation.customer and reservation.customer.hashed_version:
hashed_customer = reservation.customer.hashed_version
customer = None
if reservation.customer:
customer = reservation.customer
self._reservation_cache[hotel_code].append(
(reservation, hashed_customer)
(reservation, customer)
)
self._cache_initialized = True
@@ -1431,7 +1427,6 @@ class ConversionService:
conversion.reservation_id = matched_reservation.id
conversion.customer_id = matched_hashed_customer.id
conversion.hashed_customer_id = matched_hashed_customer.id
conversion.directly_attributable = True
conversion.guest_matched = True
conversion.updated_at = datetime.now()
@@ -1447,7 +1442,6 @@ class ConversionService:
elif matched_hashed_customer and conversion.customer_id is None:
# Only count new customer matches (conversions that didn't have a customer before)
conversion.customer_id = matched_hashed_customer.id
conversion.hashed_customer_id = matched_hashed_customer.id
conversion.directly_attributable = False
conversion.guest_matched = True
conversion.updated_at = datetime.now()
@@ -1742,8 +1736,6 @@ class ConversionService:
if matched_reservation:
matched_customer = matched_reservation.customer
if matched_customer and matched_customer.hashed_version:
matched_hashed_customer = matched_customer.hashed_version
_LOGGER.info(
"Phase 3a: Matched conversion by advertising ID (pms_id=%s, reservation_id=%d)",
@@ -1752,14 +1744,12 @@ class ConversionService:
)
# Update the conversion with matched entities if found
if matched_reservation or matched_customer or matched_hashed_customer:
if matched_reservation or matched_customer:
conversion.reservation_id = (
matched_reservation.id if matched_reservation else None
)
conversion.customer_id = matched_customer.id if matched_customer else None
conversion.hashed_customer_id = (
matched_hashed_customer.id if matched_hashed_customer else None
)
# ID-based matches are always directly attributable
conversion.directly_attributable = True

View File

@@ -6,7 +6,7 @@ from pydantic import ValidationError
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from .db import Customer, HashedCustomer
from .db import Customer
from .logging_config import get_logger
from .schemas import CustomerData

View File

@@ -362,36 +362,6 @@ class Customer(Base):
self.hashed_birth_date = self._normalize_and_hash(self.birth_date)
class HashedCustomer(Base):
"""Hashed customer data for Meta Conversion API.
Stores SHA256 hashed versions of customer PII according to Meta's requirements.
This allows sending conversion events without exposing raw customer data.
"""
__tablename__ = "hashed_customers"
id = Column(Integer, primary_key=True)
customer_id = Column(
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
hashed_phone = Column(String(64))
hashed_given_name = Column(String(64))
hashed_surname = Column(String(64))
hashed_city = Column(String(64))
hashed_postal_code = Column(String(64))
hashed_country_code = Column(String(64))
hashed_gender = Column(String(64))
hashed_birth_date = Column(String(64))
created_at = Column(DateTime(timezone=True))
customer = relationship(
"Customer", backref=backref("hashed_version", uselist=False, lazy="joined")
)
class ConversionGuest(Base):
@@ -430,10 +400,7 @@ class ConversionGuest(Base):
hashed_country_code = Column(String(64))
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
)
# Guest classification
is_regular = Column(
@@ -452,7 +419,6 @@ class ConversionGuest(Base):
primaryjoin="and_(ConversionGuest.hotel_id == foreign(Conversion.hotel_id), "
"ConversionGuest.guest_id == foreign(Conversion.guest_id))",
)
hashed_customer = relationship("HashedCustomer", backref="conversion_guests")
@staticmethod
def _normalize_and_hash(value):
@@ -613,9 +579,6 @@ class Conversion(Base):
Integer, ForeignKey("reservations.id"), nullable=True, index=True
)
customer_id = Column(Integer, ForeignKey("customers.id"), nullable=True, index=True)
hashed_customer_id = Column(
Integer, ForeignKey("hashed_customers.id"), nullable=True, index=True
)
# Reservation metadata from XML
hotel_id = Column(
@@ -670,7 +633,6 @@ class Conversion(Base):
# Relationships
reservation = relationship("Reservation", backref="conversions")
customer = relationship("Customer", backref="conversions")
hashed_customer = relationship("HashedCustomer", backref="conversions")
guest = relationship(
"ConversionGuest",
back_populates="conversions",

View File

@@ -7,7 +7,7 @@ from typing import Optional
from sqlalchemy import and_, select
from sqlalchemy.ext.asyncio import AsyncSession
from .db import AckedRequest, Customer, HashedCustomer, Reservation
from .db import AckedRequest, Customer, Reservation
from .schemas import ReservationData
@@ -64,17 +64,6 @@ class ReservationService:
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)

View File

@@ -51,7 +51,6 @@ from alpine_bits_python.db import (
AckedRequest,
Base,
Customer,
HashedCustomer,
Reservation,
get_database_url,
)

View File

@@ -203,7 +203,7 @@ async def process_wix_form_submission(
"name_title": None,
}
# This automatically creates/updates both Customer and HashedCustomer
# This automatically creates/updates Customer
db_customer = await customer_service.get_or_create_customer(customer_data)
# Determine hotel_code and hotel_name