"""XML builder helpers for creating test reservation data. This module provides convenient builder classes for generating reservation XML structures used in conversion service tests. """ from datetime import datetime, timedelta from typing import Optional from xml.etree import ElementTree as ET class RoomReservationBuilder: """Builder for creating roomReservation XML elements with daily sales.""" def __init__( self, arrival: str, departure: str, room_type: str = "DZV", room_number: str = "101", status: str = "reserved", adults: int = 2, children: int = 0, infants: int = 0, rate_plan_code: str = "STANDARD", connected_room_type: str = "0", revenue_logis_per_day: Optional[float] = None, revenue_total_per_day: Optional[float] = None, ): """Initialize room reservation builder. Args: arrival: Arrival date in YYYY-MM-DD format departure: Departure date in YYYY-MM-DD format room_type: Room type code room_number: Room number status: Reservation status (reserved, request, confirmed, etc.) adults: Number of adults children: Number of children infants: Number of infants rate_plan_code: Rate plan code connected_room_type: Connected room type code revenue_logis_per_day: Revenue per day (if None, no revenue attributes) revenue_total_per_day: Total revenue per day (defaults to revenue_logis_per_day) """ self.arrival = arrival self.departure = departure self.room_type = room_type self.room_number = room_number self.status = status self.adults = adults self.children = children self.infants = infants self.rate_plan_code = rate_plan_code self.connected_room_type = connected_room_type self.revenue_logis_per_day = revenue_logis_per_day self.revenue_total_per_day = revenue_total_per_day or revenue_logis_per_day def build(self) -> ET.Element: """Build the roomReservation XML element with daily sales. Returns: XML Element for the room reservation """ room_attrs = { "arrival": self.arrival, "departure": self.departure, "status": self.status, "roomType": self.room_type, "roomNumber": self.room_number, "adults": str(self.adults), "ratePlanCode": self.rate_plan_code, "connectedRoomType": self.connected_room_type, } if self.children > 0: room_attrs["children"] = str(self.children) if self.infants > 0: room_attrs["infants"] = str(self.infants) room_elem = ET.Element("roomReservation", room_attrs) # Create dailySales element daily_sales_elem = ET.SubElement(room_elem, "dailySales") # Generate daily sale entries from arrival to departure (inclusive of departure for the no-revenue entry) arrival_date = datetime.strptime(self.arrival, "%Y-%m-%d") departure_date = datetime.strptime(self.departure, "%Y-%m-%d") current_date = arrival_date while current_date <= departure_date: date_str = current_date.strftime("%Y-%m-%d") daily_sale_attrs = {"date": date_str} # Add revenue attributes for all days except departure day if current_date < departure_date and self.revenue_logis_per_day is not None: daily_sale_attrs["revenueTotal"] = str(self.revenue_total_per_day) daily_sale_attrs["revenueLogis"] = str(self.revenue_logis_per_day) ET.SubElement(daily_sales_elem, "dailySale", daily_sale_attrs) current_date += timedelta(days=1) return room_elem class ReservationXMLBuilder: """Builder for creating complete reservation XML structures for testing. This builder provides a fluent interface for constructing reservation XML that matches the format expected by the ConversionService. Example usage: builder = ReservationXMLBuilder( hotel_id="39054_001", reservation_id="12345", reservation_number="RES-001", reservation_date="2025-11-14" ) builder.set_guest( guest_id="guest_001", first_name="John", last_name="Doe", email="john@example.com" ) builder.add_room( arrival="2025-12-01", departure="2025-12-05", revenue_logis_per_day=150.0 ) xml_string = builder.build_xml() """ def __init__( self, hotel_id: str, reservation_id: str, reservation_number: str, reservation_date: str, creation_time: Optional[str] = None, reservation_type: str = "reservation", advertising_medium: Optional[str] = None, advertising_partner: Optional[str] = None, advertising_campagne: Optional[str] = None, ): """Initialize reservation builder. Args: hotel_id: Hotel ID reservation_id: Reservation ID reservation_number: Reservation number reservation_date: Reservation date in YYYY-MM-DD format creation_time: Creation timestamp (defaults to reservation_date + T00:00:00) reservation_type: Type of reservation (reservation, request, etc.) advertising_medium: Advertising medium advertising_partner: Advertising partner advertising_campagne: Advertising campaign """ self.hotel_id = hotel_id self.reservation_id = reservation_id self.reservation_number = reservation_number self.reservation_date = reservation_date self.creation_time = creation_time or f"{reservation_date}T00:00:00" self.reservation_type = reservation_type self.advertising_medium = advertising_medium self.advertising_partner = advertising_partner self.advertising_campagne = advertising_campagne self.guest_data: Optional[dict] = None self.rooms: list[RoomReservationBuilder] = [] def set_guest( self, guest_id: str, first_name: str, last_name: str, email: str, language: str = "en", gender: Optional[str] = None, country_code: Optional[str] = None, country: Optional[str] = None, ) -> "ReservationXMLBuilder": """Set guest information for the reservation. Args: guest_id: Guest ID first_name: Guest first name last_name: Guest last name email: Guest email language: Guest language code gender: Guest gender country_code: Guest country code country: Guest country name Returns: Self for method chaining """ self.guest_data = { "id": guest_id, "firstName": first_name, "lastName": last_name, "email": email, "language": language, } if gender: self.guest_data["gender"] = gender if country_code: self.guest_data["countryCode"] = country_code if country: self.guest_data["country"] = country return self def add_room( self, arrival: str, departure: str, room_type: str = "DZV", room_number: str = "101", status: str = "reserved", adults: int = 2, children: int = 0, infants: int = 0, rate_plan_code: str = "STANDARD", connected_room_type: str = "0", revenue_logis_per_day: Optional[float] = None, revenue_total_per_day: Optional[float] = None, ) -> "ReservationXMLBuilder": """Add a room reservation with convenient daily sales generation. Args: arrival: Arrival date in YYYY-MM-DD format departure: Departure date in YYYY-MM-DD format room_type: Room type code room_number: Room number status: Reservation status adults: Number of adults children: Number of children infants: Number of infants rate_plan_code: Rate plan code connected_room_type: Connected room type revenue_logis_per_day: Fixed revenue per day (auto-generates dailySale entries) revenue_total_per_day: Total revenue per day (defaults to revenue_logis_per_day) Returns: Self for method chaining """ room_builder = RoomReservationBuilder( arrival=arrival, departure=departure, room_type=room_type, room_number=room_number, status=status, adults=adults, children=children, infants=infants, rate_plan_code=rate_plan_code, connected_room_type=connected_room_type, revenue_logis_per_day=revenue_logis_per_day, revenue_total_per_day=revenue_total_per_day, ) self.rooms.append(room_builder) return self def add_room_builder( self, room_builder: RoomReservationBuilder ) -> "ReservationXMLBuilder": """Add a pre-configured room builder. Args: room_builder: RoomReservationBuilder instance Returns: Self for method chaining """ self.rooms.append(room_builder) return self def build(self) -> ET.Element: """Build the reservation XML element. Returns: XML Element for the reservation """ reservation_attrs = { "hotelID": self.hotel_id, "id": self.reservation_id, "number": self.reservation_number, "date": self.reservation_date, "creationTime": self.creation_time, "type": self.reservation_type, } if self.advertising_medium: reservation_attrs["advertisingMedium"] = self.advertising_medium if self.advertising_partner: reservation_attrs["advertisingPartner"] = self.advertising_partner if self.advertising_campagne: reservation_attrs["advertisingCampagne"] = self.advertising_campagne reservation_elem = ET.Element("reservation", reservation_attrs) # Add guest element if self.guest_data: ET.SubElement(reservation_elem, "guest", self.guest_data) # Add roomReservations if self.rooms: room_reservations_elem = ET.SubElement( reservation_elem, "roomReservations" ) for room_builder in self.rooms: room_elem = room_builder.build() room_reservations_elem.append(room_elem) return reservation_elem def build_xml(self, include_xml_declaration: bool = True) -> str: """Build the complete XML string for this reservation. Args: include_xml_declaration: Whether to include declaration Returns: XML string """ reservation_elem = self.build() # Wrap in root element root = ET.Element("reservations") root.append(reservation_elem) xml_str = ET.tostring(root, encoding="unicode") if include_xml_declaration: xml_str = '\n' + xml_str return xml_str class MultiReservationXMLBuilder: """Builder for creating XML documents with multiple reservations. Example: builder = MultiReservationXMLBuilder() builder.add_reservation( ReservationXMLBuilder(...).set_guest(...).add_room(...) ) builder.add_reservation( ReservationXMLBuilder(...).set_guest(...).add_room(...) ) xml_string = builder.build_xml() """ def __init__(self): """Initialize multi-reservation builder.""" self.reservations: list[ReservationXMLBuilder] = [] def add_reservation( self, reservation_builder: ReservationXMLBuilder ) -> "MultiReservationXMLBuilder": """Add a reservation to the document. Args: reservation_builder: ReservationXMLBuilder instance Returns: Self for method chaining """ self.reservations.append(reservation_builder) return self def build_xml(self, include_xml_declaration: bool = True) -> str: """Build the complete XML string with all reservations. Args: include_xml_declaration: Whether to include declaration Returns: XML string with multiple reservations """ root = ET.Element("reservations") for reservation_builder in self.reservations: reservation_elem = reservation_builder.build() root.append(reservation_elem) xml_str = ET.tostring(root, encoding="unicode") if include_xml_declaration: xml_str = '\n' + xml_str return xml_str