notif_report #3

Merged
jonas merged 18 commits from notif_report into main 2025-10-06 12:50:41 +00:00
6 changed files with 48 additions and 28 deletions
Showing only changes of commit 9f289e4750 - Show all commits

View File

@@ -681,7 +681,7 @@ def create_xml_from_db(list: list[Tuple[Reservation, Customer]]):
for reservation, customer in list: for reservation, customer in list:
_LOGGER.info( _LOGGER.info(
f"Creating XML for reservation {reservation.form_id} and customer {customer.given_name}" f"Creating XML for reservation {reservation.unique_id} and customer {customer.given_name}"
) )
try: try:
@@ -718,10 +718,7 @@ def create_xml_from_db(list: list[Tuple[Reservation, Customer]]):
reservation.num_adults, children_ages reservation.num_adults, children_ages
) )
unique_id_string = reservation.form_id unique_id_string = reservation.unique_id
if len(unique_id_string) > 32:
unique_id_string = unique_id_string[:32] # Truncate to 32 characters
# UniqueID # UniqueID
unique_id = OtaResRetrieveRs.ReservationsList.HotelReservation.UniqueId( unique_id = OtaResRetrieveRs.ReservationsList.HotelReservation.UniqueId(
@@ -828,7 +825,7 @@ def create_xml_from_db(list: list[Tuple[Reservation, Customer]]):
except Exception as e: except Exception as e:
_LOGGER.error( _LOGGER.error(
f"Error creating XML for reservation {reservation.form_id} and customer {customer.given_name}: {e}" f"Error creating XML for reservation {reservation.unique_id} and customer {customer.given_name}: {e}"
) )
retrieved_reservations = OtaResRetrieveRs.ReservationsList( retrieved_reservations = OtaResRetrieveRs.ReservationsList(

View File

@@ -26,7 +26,7 @@ from xsdata.formats.dataclass.serializers.config import SerializerConfig
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from xsdata_pydantic.bindings import XmlParser from xsdata_pydantic.bindings import XmlParser
import logging import logging
from .db import Reservation, Customer from .db import AckedRequest, Reservation, Customer
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
@@ -493,6 +493,8 @@ class ReadAction(AlpineBitsAction):
# query all reservations for this hotel from the database, where start_date is greater than or equal to the given start_date # query all reservations for this hotel from the database, where start_date is greater than or equal to the given start_date
stmt = ( stmt = (
select(Reservation, Customer) select(Reservation, Customer)
.join(Customer, Reservation.customer_id == Customer.id) .join(Customer, Reservation.customer_id == Customer.id)
@@ -500,6 +502,20 @@ class ReadAction(AlpineBitsAction):
) )
if start_date: if start_date:
stmt = stmt.filter(Reservation.start_date >= start_date) stmt = stmt.filter(Reservation.start_date >= start_date)
else:
# remove reservations that have been acknowledged via client_id
if client_info.client_id:
subquery = (
select(Reservation.id)
.join(
AckedRequest,
AckedRequest.unique_id == Reservation.unique_id,
)
.filter(AckedRequest.client_id == client_info.client_id)
)
stmt = stmt.filter(~Reservation.id.in_(subquery))
result = await dbsession.execute(stmt) result = await dbsession.execute(stmt)
reservation_customer_pairs: list[tuple[Reservation, Customer]] = ( reservation_customer_pairs: list[tuple[Reservation, Customer]] = (

View File

@@ -16,7 +16,7 @@ from .config_loader import load_config
from fastapi.responses import HTMLResponse, PlainTextResponse, Response from fastapi.responses import HTMLResponse, PlainTextResponse, Response
from .models import WixFormSubmission from .models import WixFormSubmission
from datetime import datetime, date, timezone from datetime import datetime, date, timezone
from .auth import validate_api_key, validate_wix_signature, generate_api_key from .auth import generate_unique_id, validate_api_key, validate_wix_signature, generate_api_key
from .rate_limit import ( from .rate_limit import (
limiter, limiter,
webhook_limiter, webhook_limiter,
@@ -280,12 +280,16 @@ async def process_wix_form_submission(request: Request, data: Dict[str, Any], db
("utm_Term", "utm_term"), ("utm_Term", "utm_term"),
("utm_Content", "utm_content"), ("utm_Content", "utm_content"),
] ]
utm_comment_text = []
for label, field in utm_fields: # get submissionId and ensure max length 35. Generate one if not present
val = data.get(f"field:{field}") or data.get(label)
if val: unique_id = data.get("submissionId", generate_unique_id())
utm_comment_text.append(f"{label}: {val}")
utm_comment = ",".join(utm_comment_text) if utm_comment_text else None if len(unique_id) > 35:
# strip to first 35 chars
unique_id = unique_id[:35]
# use database session # use database session
@@ -309,19 +313,19 @@ async def process_wix_form_submission(request: Request, data: Dict[str, Any], db
name_title=None, name_title=None,
) )
db.add(db_customer) db.add(db_customer)
await db.commit() await db.flush() # This assigns db_customer.id without committing
await db.refresh(db_customer) #await db.refresh(db_customer)
db_reservation = DBReservation( db_reservation = DBReservation(
customer_id=db_customer.id, customer_id=db_customer.id,
form_id=data.get("submissionId"), unique_id=unique_id,
start_date=date.fromisoformat(start_date) if start_date else None, start_date=date.fromisoformat(start_date) if start_date else None,
end_date=date.fromisoformat(end_date) if end_date else None, end_date=date.fromisoformat(end_date) if end_date else None,
num_adults=num_adults, num_adults=num_adults,
num_children=num_children, num_children=num_children,
children_ages=",".join(str(a) for a in children_ages), children_ages=",".join(str(a) for a in children_ages),
offer=offer, offer=offer,
utm_comment=utm_comment,
created_at=datetime.now(timezone.utc), created_at=datetime.now(timezone.utc),
utm_source=data.get("field:utm_source"), utm_source=data.get("field:utm_source"),
utm_medium=data.get("field:utm_medium"), utm_medium=data.get("field:utm_medium"),

View File

@@ -30,6 +30,10 @@ if os.getenv("WIX_API_KEY"):
if os.getenv("ADMIN_API_KEY"): if os.getenv("ADMIN_API_KEY"):
API_KEYS["admin-key"] = os.getenv("ADMIN_API_KEY") API_KEYS["admin-key"] = os.getenv("ADMIN_API_KEY")
def generate_unique_id() -> str:
"""Generate a unique ID with max length 35 characters"""
return secrets.token_urlsafe(26)[:35] # 26 bytes -> 35 chars in base64url
def generate_api_key() -> str: def generate_api_key() -> str:
"""Generate a secure API key""" """Generate a secure API key"""

View File

@@ -44,14 +44,13 @@ 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")) customer_id = Column(Integer, ForeignKey("customers.id"))
form_id = Column(String, unique=True) unique_id = Column(String(35), unique=True) # max length 35
start_date = Column(Date) start_date = Column(Date)
end_date = Column(Date) end_date = Column(Date)
num_adults = Column(Integer) num_adults = Column(Integer)
num_children = Column(Integer) num_children = Column(Integer)
children_ages = Column(String) # comma-separated children_ages = Column(String) # comma-separated
offer = Column(String) offer = Column(String)
utm_comment = Column(String)
created_at = Column(DateTime) created_at = Column(DateTime)
# Add all UTM fields and user comment for XML # Add all UTM fields and user comment for XML
utm_source = Column(String) utm_source = Column(String)
@@ -68,11 +67,11 @@ class Reservation(Base):
customer = relationship("Customer", back_populates="reservations") customer = relationship("Customer", back_populates="reservations")
class HashedCustomer(Base):
__tablename__ = "hashed_customers" # Table for tracking acknowledged requests by client
class AckedRequest(Base):
__tablename__ = 'acked_requests'
id = Column(Integer, primary_key=True) id = Column(Integer, primary_key=True)
customer_id = Column(Integer) client_id = Column(String, index=True)
hashed_email = Column(String) unique_id = Column(String, index=True) # Should match Reservation.form_id or another unique field
hashed_phone = Column(String) timestamp = Column(DateTime)
hashed_name = Column(String)
redacted_at = Column(DateTime)

View File

@@ -256,7 +256,7 @@ def create_xml_from_db(customer: DBCustomer, reservation: DBReservation):
# UniqueID # UniqueID
unique_id = ab.OtaResRetrieveRs.ReservationsList.HotelReservation.UniqueId( unique_id = ab.OtaResRetrieveRs.ReservationsList.HotelReservation.UniqueId(
type_value=ab.UniqueIdType2.VALUE_14, id=reservation.form_id type_value=ab.UniqueIdType2.VALUE_14, id=reservation.unique_id
) )
# TimeSpan # TimeSpan