diff --git a/pyproject.toml b/pyproject.toml index 073ea9b..11a764a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,3 +40,110 @@ pythonpath = ["src"] [tool.ruff] src = ["src", "test"] + +[tool.ruff.lint] +select = [ + "A001", # Variable {name} is shadowing a Python builtin + "ASYNC210", # Async functions should not call blocking HTTP methods + "ASYNC220", # Async functions should not create subprocesses with blocking methods + "ASYNC221", # Async functions should not run processes with blocking methods + "ASYNC222", # Async functions should not wait on processes with blocking methods + "ASYNC230", # Async functions should not open files with blocking methods like open + "ASYNC251", # Async functions should not call time.sleep + "B002", # Python does not support the unary prefix increment + "B005", # Using .strip() with multi-character strings is misleading + "B007", # Loop control variable {name} not used within loop body + "B014", # Exception handler with duplicate exception + "B015", # Pointless comparison. Did you mean to assign a value? Otherwise, prepend assert or remove it. + "B017", # pytest.raises(BaseException) should be considered evil + "B018", # Found useless attribute access. Either assign it to a variable or remove it. + "B023", # Function definition does not bind loop variable {name} + "B024", # `{name}` is an abstract base class, but it has no abstract methods or properties + "B026", # Star-arg unpacking after a keyword argument is strongly discouraged + "B032", # Possible unintentional type annotation (using :). Did you mean to assign (using =)? + "B035", # Dictionary comprehension uses static key + "B904", # Use raise from to specify exception cause + "B905", # zip() without an explicit strict= parameter + "BLE", + "C", # complexity + "COM818", # Trailing comma on bare tuple prohibited + "D", # docstrings + "DTZ003", # Use datetime.now(tz=) instead of datetime.utcnow() + "DTZ004", # Use datetime.fromtimestamp(ts, tz=) instead of datetime.utcfromtimestamp(ts) + "E", # pycodestyle + "F", # pyflakes/autoflake + "F541", # f-string without any placeholders + "FLY", # flynt + "FURB", # refurb + "G", # flake8-logging-format + "I", # isort + "INP", # flake8-no-pep420 + "ISC", # flake8-implicit-str-concat + "ICN001", # import concentions; {name} should be imported as {asname} + "LOG", # flake8-logging + "N804", # First argument of a class method should be named cls + "N805", # First argument of a method should be named self + "N815", # Variable {name} in class scope should not be mixedCase + "PERF", # Perflint + "PGH", # pygrep-hooks + "PIE", # flake8-pie + "PL", # pylint + "PT", # flake8-pytest-style + "PTH", # flake8-pathlib + "PYI", # flake8-pyi + "RET", # flake8-return + "RSE", # flake8-raise + "RUF005", # Consider iterable unpacking instead of concatenation + "RUF006", # Store a reference to the return value of asyncio.create_task + "RUF007", # Prefer itertools.pairwise() over zip() when iterating over successive pairs + "RUF008", # Do not use mutable default values for dataclass attributes + "RUF010", # Use explicit conversion flag + "RUF013", # PEP 484 prohibits implicit Optional + "RUF016", # Slice in indexed access to type {value_type} uses type {index_type} instead of an integer + "RUF017", # Avoid quadratic list summation + "RUF018", # Avoid assignment expressions in assert statements + "RUF019", # Unnecessary key check before dictionary access + "RUF020", # {never_like} | T is equivalent to T + "RUF021", # Parenthesize a and b expressions when chaining and and or together, to make the precedence clear + "RUF022", # Sort __all__ + "RUF023", # Sort __slots__ + "RUF024", # Do not pass mutable objects as values to dict.fromkeys + "RUF026", # default_factory is a positional-only argument to defaultdict + "RUF030", # print() call in assert statement is likely unintentional + "RUF032", # Decimal() called with float literal argument + "RUF033", # __post_init__ method with argument defaults + "RUF034", # Useless if-else condition + "RUF100", # Unused `noqa` directive + "RUF101", # noqa directives that use redirected rule codes + "RUF200", # Failed to parse pyproject.toml: {message} + "S102", # Use of exec detected + "S103", # bad-file-permissions + "S108", # hardcoded-temp-file + "S306", # suspicious-mktemp-usage + "S307", # suspicious-eval-usage + "S313", # suspicious-xmlc-element-tree-usage + "S314", # suspicious-xml-element-tree-usage + "S315", # suspicious-xml-expat-reader-usage + "S316", # suspicious-xml-expat-builder-usage + "S317", # suspicious-xml-sax-usage + "S318", # suspicious-xml-mini-dom-usage + "S319", # suspicious-xml-pull-dom-usage + "S601", # paramiko-call + "S602", # subprocess-popen-with-shell-equals-true + "S604", # call-with-shell-equals-true + "S608", # hardcoded-sql-expression + "S609", # unix-command-wildcard-injection + "SIM", # flake8-simplify + "SLF", # flake8-self + "SLOT", # flake8-slots + "T100", # Trace found: {name} used + "T20", # flake8-print + "TC", # flake8-type-checking + "TID", # Tidy imports + "TRY", # tryceratops + "UP", # pyupgrade + "UP031", # Use format specifiers instead of percent format + "UP032", # Use f-string instead of `format` call + "W", # pycodestyle +] + diff --git a/src/alpine_bits_python/alpine_bits_helpers.py b/src/alpine_bits_python/alpine_bits_helpers.py index 32642bf..0f3c641 100644 --- a/src/alpine_bits_python/alpine_bits_helpers.py +++ b/src/alpine_bits_python/alpine_bits_helpers.py @@ -1,24 +1,21 @@ -from datetime import datetime, timezone +import logging import traceback -from typing import Union, Optional, Any, TypeVar -from pydantic import BaseModel, ConfigDict, Field from dataclasses import dataclass +from datetime import UTC, datetime from enum import Enum - -from typing import Tuple +from typing import Any from alpine_bits_python.db import Customer, Reservation # Import the generated classes from .generated.alpinebits import ( + CommentName2, HotelReservationResStatus, OtaHotelResNotifRq, OtaResRetrieveRs, - CommentName2, ProfileProfileType, UniqueIdType2, ) -import logging _LOGGER = logging.getLogger(__name__) _LOGGER.setLevel(logging.INFO) @@ -127,11 +124,10 @@ class GuestCountsFactory: @staticmethod def create_guest_counts( adults: int, - kids: Optional[list[int]] = None, + kids: list[int] | None = None, message_type: OtaMessageType = OtaMessageType.RETRIEVE, ) -> NotifGuestCounts: - """ - Create a GuestCounts object for OtaHotelResNotifRq or OtaResRetrieveRs. + """Create a GuestCounts object for OtaHotelResNotifRq or OtaResRetrieveRs. :param adults: Number of adults :param kids: List of ages for each kid (optional) :return: GuestCounts instance @@ -140,19 +136,17 @@ class GuestCountsFactory: return GuestCountsFactory._create_guest_counts( adults, kids, RetrieveGuestCounts ) - elif message_type == OtaMessageType.NOTIF: + if message_type == OtaMessageType.NOTIF: return GuestCountsFactory._create_guest_counts( adults, kids, NotifGuestCounts ) - else: - raise ValueError(f"Unsupported message type: {message_type}") + raise ValueError(f"Unsupported message type: {message_type}") @staticmethod def _create_guest_counts( - adults: int, kids: Optional[list[int]], guest_counts_class: type + adults: int, kids: list[int] | None, guest_counts_class: type ) -> Any: - """ - Internal method to create a GuestCounts object of the specified type. + """Internal method to create a GuestCounts object of the specified type. :param adults: Number of adults :param kids: List of ages for each kid (optional) :param guest_counts_class: The GuestCounts class to instantiate @@ -193,7 +187,6 @@ class CustomerFactory: @staticmethod def _create_customer(customer_class: type, data: CustomerData) -> Any: """Internal method to create a customer of the specified type.""" - # Create PersonName person_name = customer_class.PersonName( given_name=data.given_name, @@ -267,7 +260,6 @@ class CustomerFactory: @staticmethod def _customer_to_data(customer: Any) -> CustomerData: """Internal method to convert any customer type to CustomerData.""" - # Extract phone numbers phone_numbers = [] if customer.telephone: @@ -420,7 +412,7 @@ class CommentData: """Simple data class to hold comment information without nested type constraints.""" name: CommentName2 # Required: "included services", "customer comment", "additional info" - text: Optional[str] = None # Optional text content + text: str | None = None # Optional text content list_items: list[CommentListItemData] = None # Optional list items def __post_init__(self): @@ -459,7 +451,6 @@ class CommentFactory: data: CommentsData, ) -> Any: """Internal method to create comments of the specified type.""" - comments_list = [] for comment_data in data.comments: # Create list items @@ -498,7 +489,6 @@ class CommentFactory: @staticmethod def _comments_to_data(comments: Any) -> CommentsData: """Internal method to convert any comments type to CommentsData.""" - comments_data_list = [] for comment in comments.comment: # Extract list items @@ -549,7 +539,6 @@ class ResGuestFactory: res_guests_class: type, customer_class: type, customer_data: CustomerData ) -> Any: """Internal method to create complete ResGuests structure.""" - # Create the customer using the existing CustomerFactory customer = CustomerFactory._create_customer(customer_class, customer_data) @@ -572,18 +561,16 @@ class ResGuestFactory: @staticmethod def extract_primary_customer( - res_guests: Union[NotifResGuests, RetrieveResGuests], + res_guests: NotifResGuests | RetrieveResGuests, ) -> CustomerData: """Extract the primary customer data from a ResGuests structure.""" - # Navigate down the nested structure to get the customer customer = res_guests.res_guest.profiles.profile_info.profile.customer # Use the existing CustomerFactory conversion method if isinstance(res_guests, NotifResGuests): return CustomerFactory.from_notif_customer(customer) - else: - return CustomerFactory.from_retrieve_customer(customer) + return CustomerFactory.from_retrieve_customer(customer) class AlpineBitsFactory: @@ -591,11 +578,10 @@ class AlpineBitsFactory: @staticmethod def create( - data: Union[CustomerData, HotelReservationIdData, CommentsData], + data: CustomerData | HotelReservationIdData | CommentsData, message_type: OtaMessageType, ) -> Any: - """ - Create an AlpineBits object based on the data type and message type. + """Create an AlpineBits object based on the data type and message type. Args: data: The data object (CustomerData, HotelReservationIdData, CommentsData, etc.) @@ -603,36 +589,32 @@ class AlpineBitsFactory: Returns: The appropriate AlpineBits object based on the data type and message type + """ if isinstance(data, CustomerData): if message_type == OtaMessageType.NOTIF: return CustomerFactory.create_notif_customer(data) - else: - return CustomerFactory.create_retrieve_customer(data) + return CustomerFactory.create_retrieve_customer(data) - elif isinstance(data, HotelReservationIdData): + if isinstance(data, HotelReservationIdData): if message_type == OtaMessageType.NOTIF: return HotelReservationIdFactory.create_notif_hotel_reservation_id(data) - else: - return HotelReservationIdFactory.create_retrieve_hotel_reservation_id( - data - ) + return HotelReservationIdFactory.create_retrieve_hotel_reservation_id( + data + ) - elif isinstance(data, CommentsData): + if isinstance(data, CommentsData): if message_type == OtaMessageType.NOTIF: return CommentFactory.create_notif_comments(data) - else: - return CommentFactory.create_retrieve_comments(data) + return CommentFactory.create_retrieve_comments(data) - else: - raise ValueError(f"Unsupported data type: {type(data)}") + raise ValueError(f"Unsupported data type: {type(data)}") @staticmethod def create_res_guests( customer_data: CustomerData, message_type: OtaMessageType - ) -> Union[NotifResGuests, RetrieveResGuests]: - """ - Create a complete ResGuests structure with a primary customer. + ) -> NotifResGuests | RetrieveResGuests: + """Create a complete ResGuests structure with a primary customer. Args: customer_data: The customer data @@ -640,44 +622,44 @@ class AlpineBitsFactory: Returns: The appropriate ResGuests object + """ if message_type == OtaMessageType.NOTIF: return ResGuestFactory.create_notif_res_guests(customer_data) - else: - return ResGuestFactory.create_retrieve_res_guests(customer_data) + return ResGuestFactory.create_retrieve_res_guests(customer_data) @staticmethod def extract_data( obj: Any, - ) -> Union[CustomerData, HotelReservationIdData, CommentsData]: - """ - Extract data from an AlpineBits object back to a simple data class. + ) -> CustomerData | HotelReservationIdData | CommentsData: + """Extract data from an AlpineBits object back to a simple data class. Args: obj: The AlpineBits object to extract data from Returns: The appropriate data object + """ # Check if it's a Customer object if hasattr(obj, "person_name") and hasattr(obj.person_name, "given_name"): if isinstance(obj, NotifCustomer): return CustomerFactory.from_notif_customer(obj) - elif isinstance(obj, RetrieveCustomer): + if isinstance(obj, RetrieveCustomer): return CustomerFactory.from_retrieve_customer(obj) # Check if it's a HotelReservationId object elif hasattr(obj, "res_id_type"): if isinstance(obj, NotifHotelReservationId): return HotelReservationIdFactory.from_notif_hotel_reservation_id(obj) - elif isinstance(obj, RetrieveHotelReservationId): + if isinstance(obj, RetrieveHotelReservationId): return HotelReservationIdFactory.from_retrieve_hotel_reservation_id(obj) # Check if it's a Comments object elif hasattr(obj, "comment"): if isinstance(obj, NotifComments): return CommentFactory.from_notif_comments(obj) - elif isinstance(obj, RetrieveComments): + if isinstance(obj, RetrieveComments): return CommentFactory.from_retrieve_comments(obj) # Check if it's a ResGuests object @@ -688,15 +670,13 @@ class AlpineBitsFactory: raise ValueError(f"Unsupported object type: {type(obj)}") -def create_res_retrieve_response(list: list[Tuple[Reservation, Customer]]): +def create_res_retrieve_response(list: list[tuple[Reservation, Customer]]): """Create RetrievedReservation XML from database entries.""" - return _create_xml_from_db(list, OtaMessageType.RETRIEVE) -def create_res_notif_push_message(list: Tuple[Reservation, Customer]): +def create_res_notif_push_message(list: tuple[Reservation, Customer]): """Create Reservation Notification XML from database entries.""" - return _create_xml_from_db(list, OtaMessageType.NOTIF) @@ -819,8 +799,7 @@ def _process_single_reservation( if reservation.hotel_code is None: raise ValueError("Reservation hotel_code is None") - else: - hotel_code = str(reservation.hotel_code) + hotel_code = str(reservation.hotel_code) if reservation.hotel_name is None: hotel_name = None else: @@ -897,7 +876,7 @@ def _process_single_reservation( ) hotel_reservation = HotelReservation( - create_date_time=datetime.now(timezone.utc).isoformat(), + create_date_time=datetime.now(UTC).isoformat(), res_status=HotelReservationResStatus.REQUESTED, room_stay_reservation="true", unique_id=unique_id, @@ -910,14 +889,13 @@ def _process_single_reservation( def _create_xml_from_db( - entries: list[Tuple[Reservation, Customer]] | Tuple[Reservation, Customer], + entries: list[tuple[Reservation, Customer]] | tuple[Reservation, Customer], type: OtaMessageType, ): """Create RetrievedReservation XML from database entries. list of pairs (Reservation, Customer) """ - reservations_list = [] # if entries isn't a list wrap the element in a list @@ -957,7 +935,7 @@ def _create_xml_from_db( raise return ota_hotel_res_notif_rq - elif type == OtaMessageType.RETRIEVE: + if type == OtaMessageType.RETRIEVE: retrieved_reservations = OtaResRetrieveRs.ReservationsList( hotel_reservation=reservations_list ) @@ -974,8 +952,7 @@ def _create_xml_from_db( return ota_res_retrieve_rs - else: - raise ValueError(f"Unsupported message type: {type}") + raise ValueError(f"Unsupported message type: {type}") # Usage examples diff --git a/src/alpine_bits_python/alpinebits_server.py b/src/alpine_bits_python/alpinebits_server.py index cfe541a..ae880cb 100644 --- a/src/alpine_bits_python/alpinebits_server.py +++ b/src/alpine_bits_python/alpinebits_server.py @@ -1,47 +1,39 @@ -""" -AlpineBits Server for handling hotel data exchange. +"""AlpineBits Server for handling hotel data exchange. This module provides an asynchronous AlpineBits server that can handle various OTA (OpenTravel Alliance) actions for hotel data exchange. Currently implements handshaking functionality with configurable supported actions and capabilities. """ -import asyncio -from datetime import datetime -from zoneinfo import ZoneInfo -import difflib -import json import inspect +import json +import logging import re -from typing import Dict, List, Optional, Any, Union, Tuple, Type, override -from xml.etree import ElementTree as ET +from abc import ABC from dataclasses import dataclass +from datetime import datetime from enum import Enum, IntEnum +from typing import Any, Optional, override +from zoneinfo import ZoneInfo + +from sqlalchemy import select +from xsdata.formats.dataclass.serializers.config import SerializerConfig +from xsdata_pydantic.bindings import XmlParser, XmlSerializer from alpine_bits_python.alpine_bits_helpers import ( - PhoneTechType, create_res_notif_push_message, create_res_retrieve_response, ) - +from .db import AckedRequest, Customer, Reservation from .generated.alpinebits import ( OtaNotifReportRq, OtaNotifReportRs, OtaPingRq, OtaPingRs, - WarningStatus, OtaReadRq, + WarningStatus, ) -from xsdata_pydantic.bindings import XmlSerializer -from xsdata.formats.dataclass.serializers.config import SerializerConfig -from abc import ABC, abstractmethod -from xsdata_pydantic.bindings import XmlParser -import logging -from .db import AckedRequest, Reservation, Customer -from sqlalchemy import select -from sqlalchemy.orm import joinedload - # Configure logging logging.basicConfig(level=logging.INFO) @@ -178,8 +170,7 @@ class AlpineBitsAction(ABC): dbsession=None, server_capabilities=None, ) -> AlpineBitsResponse: - """ - Handle the incoming request XML and return response XML. + """Handle the incoming request XML and return response XML. Default implementation returns "not implemented" error. Override this method in subclasses to provide actual functionality. @@ -191,18 +182,19 @@ class AlpineBitsAction(ABC): Returns: AlpineBitsResponse with error or actual response + """ return_string = f"Error: Action {action} not implemented" return AlpineBitsResponse(return_string, HttpStatusCode.BAD_REQUEST) async def check_version_supported(self, version: Version) -> bool: - """ - Check if the action supports the given version. + """Check if the action supports the given version. Args: version: The AlpineBits version to check Returns: True if supported, False otherwise + """ if isinstance(self.version, list): return version in self.version @@ -210,12 +202,11 @@ class AlpineBitsAction(ABC): class ServerCapabilities: - """ - Automatically discovers AlpineBitsAction implementations and generates capabilities. + """Automatically discovers AlpineBitsAction implementations and generates capabilities. """ def __init__(self): - self.action_registry: Dict[AlpineBitsActionName, Type[AlpineBitsAction]] = {} + self.action_registry: dict[AlpineBitsActionName, type[AlpineBitsAction]] = {} self._discover_actions() self.capability_dict = None @@ -236,9 +227,8 @@ class ServerCapabilities: # Use capability attribute as registry key self.action_registry[action_instance.name] = obj - def _is_action_implemented(self, action_class: Type[AlpineBitsAction]) -> bool: - """ - Check if an action is actually implemented or just uses the default behavior. + def _is_action_implemented(self, action_class: type[AlpineBitsAction]) -> bool: + """Check if an action is actually implemented or just uses the default behavior. This is a simple check - in practice, you might want more sophisticated detection. """ # Check if the class has overridden the handle method @@ -247,8 +237,7 @@ class ServerCapabilities: return False def create_capabilities_dict(self) -> None: - """ - Generate the capabilities dictionary based on discovered actions. + """Generate the capabilities dictionary based on discovered actions. """ versions_dict = {} @@ -298,18 +287,15 @@ class ServerCapabilities: if action.get("action") != "action_OTA_Ping" ] - return None - def get_capabilities_dict(self) -> Dict: + def get_capabilities_dict(self) -> dict: + """Get capabilities as a dictionary. Generates if not already created. """ - Get capabilities as a dictionary. Generates if not already created. - """ - if self.capability_dict is None: self.create_capabilities_dict() return self.capability_dict - def get_supported_actions(self) -> List[str]: + def get_supported_actions(self) -> list[str]: """Get list of all supported action names.""" return list(self.action_registry.keys()) @@ -320,7 +306,7 @@ class ServerCapabilities: class PingAction(AlpineBitsAction): """Implementation for OTA_Ping action (handshaking).""" - def __init__(self, config: Dict = {}): + def __init__(self, config: dict = {}): self.name = AlpineBitsActionName.OTA_PING self.version = [ Version.V2024_10, @@ -338,10 +324,9 @@ class PingAction(AlpineBitsAction): server_capabilities: None | ServerCapabilities = None, ) -> AlpineBitsResponse: """Handle ping requests.""" - if request_xml is None: return AlpineBitsResponse( - f"Error: Xml Request missing", HttpStatusCode.BAD_REQUEST + "Error: Xml Request missing", HttpStatusCode.BAD_REQUEST ) if server_capabilities is None: @@ -356,9 +341,9 @@ class PingAction(AlpineBitsAction): parsed_request = parser.from_string(request_xml, OtaPingRq) echo_data_client = json.loads(parsed_request.echo_data) - except Exception as e: + except Exception: return AlpineBitsResponse( - f"Error: Invalid XML request", HttpStatusCode.BAD_REQUEST + "Error: Invalid XML request", HttpStatusCode.BAD_REQUEST ) # compare echo data with capabilities, create a dictionary containing the matching capabilities @@ -441,7 +426,7 @@ def strip_control_chars(s): def validate_hotel_authentication( - username: str, password: str, hotelid: str, config: Dict + username: str, password: str, hotelid: str, config: dict ) -> bool: """Validate hotel authentication based on username, password, and hotel ID. @@ -452,7 +437,6 @@ def validate_hotel_authentication( username: "alice" password: !secret ALICE_PASSWORD """ - if not config or "alpine_bits_auth" not in config: return False auth_list = config["alpine_bits_auth"] @@ -471,7 +455,7 @@ def validate_hotel_authentication( class ReadAction(AlpineBitsAction): """Implementation for OTA_Read action.""" - def __init__(self, config: Dict = {}): + def __init__(self, config: dict = {}): self.name = AlpineBitsActionName.OTA_READ self.version = [Version.V2024_10, Version.V2022_10] self.config = config @@ -486,7 +470,6 @@ class ReadAction(AlpineBitsAction): server_capabilities=None, ) -> AlpineBitsResponse: """Handle read requests.""" - clean_action = strip_control_chars(str(action)).strip() clean_expected = strip_control_chars(self.name.value[1]).strip() @@ -513,7 +496,7 @@ class ReadAction(AlpineBitsAction): if hotelid is None: return AlpineBitsResponse( - f"Error: Unauthorized Read Request. No target hotel specified. Check credentials", + "Error: Unauthorized Read Request. No target hotel specified. Check credentials", HttpStatusCode.UNAUTHORIZED, ) @@ -541,18 +524,17 @@ class ReadAction(AlpineBitsAction): ) if 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) + # remove reservations that have been acknowledged via client_id + elif client_info.client_id: + subquery = ( + select(Reservation.id) + .join( + AckedRequest, + AckedRequest.unique_id == Reservation.unique_id, ) - stmt = stmt.filter(~Reservation.id.in_(subquery)) + .filter(AckedRequest.client_id == client_info.client_id) + ) + stmt = stmt.filter(~Reservation.id.in_(subquery)) result = await dbsession.execute(stmt) reservation_customer_pairs: list[tuple[Reservation, Customer]] = ( @@ -583,7 +565,7 @@ class ReadAction(AlpineBitsAction): class NotifReportReadAction(AlpineBitsAction): """Necessary for read action to follow specification. Clients need to report acknowledgements""" - def __init__(self, config: Dict = {}): + def __init__(self, config: dict = {}): self.name = AlpineBitsActionName.OTA_HOTEL_NOTIF_REPORT self.version = [Version.V2024_10, Version.V2022_10] self.config = config @@ -598,7 +580,6 @@ class NotifReportReadAction(AlpineBitsAction): server_capabilities=None, ) -> AlpineBitsResponse: """Handle read requests.""" - notif_report = XmlParser().from_string(request_xml, OtaNotifReportRq) # we can't check hotel auth here, because this action does not contain hotel info @@ -621,34 +602,29 @@ class NotifReportReadAction(AlpineBitsAction): success_message, ns_map={None: "http://www.opentravel.org/OTA/2003/05"} ) - if warnings is None and notif_report_details is None: - return AlpineBitsResponse( - response_xml, HttpStatusCode.OK - ) # Nothing to process - elif ( + if (warnings is None and notif_report_details is None) or ( notif_report_details is not None and notif_report_details.hotel_notif_report is None ): return AlpineBitsResponse( response_xml, HttpStatusCode.OK ) # Nothing to process - else: - if dbsession is None: - return AlpineBitsResponse( - "Error: Something went wrong", HttpStatusCode.INTERNAL_SERVER_ERROR - ) + if dbsession is None: + return AlpineBitsResponse( + "Error: Something went wrong", HttpStatusCode.INTERNAL_SERVER_ERROR + ) - timestamp = datetime.now(ZoneInfo("UTC")) - for entry in notif_report_details.hotel_notif_report.hotel_reservations.hotel_reservation: # type: ignore - unique_id = entry.unique_id.id - acked_request = AckedRequest( - unique_id=unique_id, - client_id=client_info.client_id, - timestamp=timestamp, - ) - dbsession.add(acked_request) + timestamp = datetime.now(ZoneInfo("UTC")) + for entry in notif_report_details.hotel_notif_report.hotel_reservations.hotel_reservation: # type: ignore + unique_id = entry.unique_id.id + acked_request = AckedRequest( + unique_id=unique_id, + client_id=client_info.client_id, + timestamp=timestamp, + ) + dbsession.add(acked_request) - await dbsession.commit() + await dbsession.commit() return AlpineBitsResponse(response_xml, HttpStatusCode.OK) @@ -656,7 +632,7 @@ class NotifReportReadAction(AlpineBitsAction): class PushAction(AlpineBitsAction): """Creates the necessary xml for OTA_HotelResNotif:GuestRequests""" - def __init__(self, config: Dict = {}): + def __init__(self, config: dict = {}): self.name = AlpineBitsActionName.OTA_HOTEL_RES_NOTIF_GUEST_REQUESTS self.version = [Version.V2024_10, Version.V2022_10] self.config = config @@ -664,14 +640,13 @@ class PushAction(AlpineBitsAction): async def handle( self, action: str, - request_xml: Tuple[Reservation, Customer], + request_xml: tuple[Reservation, Customer], version: Version, client_info: AlpineBitsClientInfo, dbsession=None, server_capabilities=None, ) -> AlpineBitsResponse: """Create push request XML.""" - xml_push_request = create_res_notif_push_message(request_xml) config = SerializerConfig( @@ -686,15 +661,14 @@ class PushAction(AlpineBitsAction): class AlpineBitsServer: - """ - Asynchronous AlpineBits server for handling hotel data exchange requests. + """Asynchronous AlpineBits server for handling hotel data exchange requests. This server handles various OTA actions and implements the AlpineBits protocol for hotel data exchange. It maintains a registry of supported actions and their capabilities, and can respond to handshake requests with its capabilities. """ - def __init__(self, config: Dict = None): + def __init__(self, config: dict = None): self.capabilities = ServerCapabilities() self._action_instances = {} self.config = config @@ -706,20 +680,19 @@ class AlpineBitsServer: _LOGGER.info(f"Initializing action instance for {capability_name}") self._action_instances[capability_name] = action_class(config=self.config) - def get_capabilities(self) -> Dict: + def get_capabilities(self) -> dict: """Get server capabilities.""" return self.capabilities.get_capabilities_dict() async def handle_request( self, request_action_name: str, - request_xml: str | Tuple[Reservation, Customer], + request_xml: str | tuple[Reservation, Customer], client_info: AlpineBitsClientInfo, version: str = "2024-10", dbsession=None, ) -> AlpineBitsResponse: - """ - Handle an incoming AlpineBits request by routing to appropriate action handler. + """Handle an incoming AlpineBits request by routing to appropriate action handler. Args: request_action_name: The action name from the request (e.g., "OTA_Read:GuestRequests") @@ -728,6 +701,7 @@ class AlpineBitsServer: Returns: AlpineBitsResponse with the result + """ # Convert string version to enum try: @@ -774,7 +748,7 @@ class AlpineBitsServer: action_instance: PushAction if request_xml is None or not isinstance(request_xml, tuple): return AlpineBitsResponse( - f"Error: Invalid data for push request", + "Error: Invalid data for push request", HttpStatusCode.BAD_REQUEST, ) return await action_instance.handle( @@ -792,26 +766,25 @@ class AlpineBitsServer: server_capabilities=self.capabilities, client_info=client_info, ) - else: - return await action_instance.handle( - action=request_action_name, - request_xml=request_xml, - version=version_enum, - dbsession=dbsession, - client_info=client_info, - ) + return await action_instance.handle( + action=request_action_name, + request_xml=request_xml, + version=version_enum, + dbsession=dbsession, + client_info=client_info, + ) except Exception as e: - print(f"Error handling request {request_action_name}: {str(e)}") + print(f"Error handling request {request_action_name}: {e!s}") # print stack trace for debugging import traceback traceback.print_exc() return AlpineBitsResponse( - f"Error: Internal server error while processing {request_action_name}: {str(e)}", + f"Error: Internal server error while processing {request_action_name}: {e!s}", HttpStatusCode.INTERNAL_SERVER_ERROR, ) - def get_supported_request_names(self) -> List[str]: + def get_supported_request_names(self) -> list[str]: """Get all supported request names (not capability names).""" request_names = [] for capability_name in self._action_instances.keys(): @@ -823,8 +796,7 @@ class AlpineBitsServer: def is_action_supported( self, request_action_name: str, version: str | None = None ) -> bool: - """ - Check if a request action is supported. + """Check if a request action is supported. Args: request_action_name: The request action name (e.g., "OTA_Read:GuestRequests") @@ -832,6 +804,7 @@ class AlpineBitsServer: Returns: True if supported, False otherwise + """ action_enum = AlpineBitsActionName.get_by_request_name(request_action_name) if not action_enum: @@ -848,8 +821,7 @@ class AlpineBitsServer: # This would need to be async, but for simplicity we'll just check if version exists if isinstance(action_instance.version, list): return version_enum in action_instance.version - else: - return action_instance.version == version_enum + return action_instance.version == version_enum except ValueError: return False diff --git a/src/alpine_bits_python/api.py b/src/alpine_bits_python/api.py index 9aeaabd..4947695 100644 --- a/src/alpine_bits_python/api.py +++ b/src/alpine_bits_python/api.py @@ -1,62 +1,57 @@ -from fastapi import ( - FastAPI, - HTTPException, - BackgroundTasks, - Request, - Depends, - APIRouter, - Form, - File, - UploadFile, -) -from fastapi.concurrency import asynccontextmanager -from fastapi.middleware.cors import CORSMiddleware -from fastapi.security import HTTPBearer, HTTPBasicCredentials, HTTPBasic -from .config_loader import load_config -from fastapi.responses import HTMLResponse, PlainTextResponse, Response -from .models import WixFormSubmission -from datetime import datetime, date, timezone -from .auth import ( - generate_unique_id, - validate_api_key, - validate_wix_signature, - generate_api_key, -) -from .rate_limit import ( - limiter, - webhook_limiter, - custom_rate_limit_handler, - DEFAULT_RATE_LIMIT, - WEBHOOK_RATE_LIMIT, - BURST_RATE_LIMIT, -) -from slowapi.errors import RateLimitExceeded -import logging -from datetime import datetime -from typing import Dict, Any, Optional, List -import json -import os import asyncio import gzip -import xml.etree.ElementTree as ET +import json +import logging +import os +import urllib.parse +from datetime import UTC, date, datetime +from functools import partial +from typing import Any + +import httpx +from fastapi import ( + APIRouter, + Depends, + FastAPI, + HTTPException, + Request, +) +from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import HTMLResponse, Response +from fastapi.security import HTTPBasic, HTTPBasicCredentials +from slowapi.errors import RateLimitExceeded +from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine + from .alpinebits_server import ( + AlpineBitsActionName, AlpineBitsClientInfo, AlpineBitsServer, Version, - AlpineBitsActionName, ) -import urllib.parse -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker -from functools import partial -import httpx - +from .auth import ( + generate_api_key, + generate_unique_id, + validate_api_key, +) +from .config_loader import load_config from .db import ( Base, - Customer as DBCustomer, - Reservation as DBReservation, get_database_url, ) - +from .db import ( + Customer as DBCustomer, +) +from .db import ( + Reservation as DBReservation, +) +from .rate_limit import ( + BURST_RATE_LIMIT, + DEFAULT_RATE_LIMIT, + WEBHOOK_RATE_LIMIT, + custom_rate_limit_handler, + limiter, + webhook_limiter, +) # Configure logging logging.basicConfig(level=logging.INFO) @@ -98,8 +93,7 @@ event_dispatcher = EventDispatcher() async def push_listener(customer: DBCustomer, reservation: DBReservation, hotel): - """ - Push listener that sends reservation data to hotel's push endpoint. + """Push listener that sends reservation data to hotel's push endpoint. Only called for reservations that match this hotel's hotel_id. """ push_endpoint = hotel.get("push_endpoint") @@ -186,7 +180,7 @@ async def lifespan(app: FastAPI): try: config = load_config() except Exception as e: - _LOGGER.error(f"Failed to load config: {str(e)}") + _LOGGER.error(f"Failed to load config: {e!s}") config = {} DATABASE_URL = get_database_url(config) @@ -263,9 +257,8 @@ app.add_middleware( ) -async def process_form_submission(submission_data: Dict[str, Any]) -> None: - """ - Background task to process the form submission. +async def process_form_submission(submission_data: dict[str, Any]) -> None: + """Background task to process the form submission. Add your business logic here. """ try: @@ -297,7 +290,7 @@ async def process_form_submission(submission_data: Dict[str, Any]) -> None: # - Process the data further except Exception as e: - _LOGGER.error(f"Error processing form submission: {str(e)}") + _LOGGER.error(f"Error processing form submission: {e!s}") @api_router.get("/") @@ -332,9 +325,8 @@ async def health_check(request: Request): # Extracted business logic for handling Wix form submissions -async def process_wix_form_submission(request: Request, data: Dict[str, Any], db): - """ - Shared business logic for handling Wix form submissions (test and production). +async def process_wix_form_submission(request: Request, data: dict[str, Any], db): + """Shared business logic for handling Wix form submissions (test and production). """ timestamp = datetime.now().isoformat() @@ -485,7 +477,7 @@ async def process_wix_form_submission(request: Request, data: Dict[str, Any], db num_children=num_children, children_ages=",".join(str(a) for a in children_ages), offer=offer, - created_at=datetime.now(timezone.utc), + created_at=datetime.now(UTC), utm_source=data.get("field:utm_source"), utm_medium=data.get("field:utm_medium"), utm_campaign=data.get("field:utm_campaign"), @@ -532,38 +524,36 @@ async def process_wix_form_submission(request: Request, data: Dict[str, Any], db @api_router.post("/webhook/wix-form") @webhook_limiter.limit(WEBHOOK_RATE_LIMIT) async def handle_wix_form( - request: Request, data: Dict[str, Any], db_session=Depends(get_async_session) + request: Request, data: dict[str, Any], db_session=Depends(get_async_session) ): - """ - Unified endpoint to handle Wix form submissions (test and production). + """Unified endpoint to handle Wix form submissions (test and production). No authentication required for this endpoint. """ try: return await process_wix_form_submission(request, data, db_session) except Exception as e: - _LOGGER.error(f"Error in handle_wix_form: {str(e)}") + _LOGGER.error(f"Error in handle_wix_form: {e!s}") # log stacktrace import traceback traceback_str = traceback.format_exc() _LOGGER.error(f"Stack trace for handle_wix_form: {traceback_str}") - raise HTTPException(status_code=500, detail=f"Error processing Wix form data") + raise HTTPException(status_code=500, detail="Error processing Wix form data") @api_router.post("/webhook/wix-form/test") @limiter.limit(DEFAULT_RATE_LIMIT) async def handle_wix_form_test( - request: Request, data: Dict[str, Any], db_session=Depends(get_async_session) + request: Request, data: dict[str, Any], db_session=Depends(get_async_session) ): - """ - Test endpoint to verify the API is working with raw JSON data. + """Test endpoint to verify the API is working with raw JSON data. No authentication required for testing purposes. """ try: return await process_wix_form_submission(request, data, db_session) except Exception as e: - _LOGGER.error(f"Error in handle_wix_form_test: {str(e)}") - raise HTTPException(status_code=500, detail=f"Error processing test data") + _LOGGER.error(f"Error in handle_wix_form_test: {e!s}") + raise HTTPException(status_code=500, detail="Error processing test data") @api_router.post("/admin/generate-api-key") @@ -571,8 +561,7 @@ async def handle_wix_form_test( async def generate_new_api_key( request: Request, admin_key: str = Depends(validate_api_key) ): - """ - Admin endpoint to generate new API keys. + """Admin endpoint to generate new API keys. Requires admin API key and is heavily rate limited. """ if admin_key != "admin-key": @@ -593,8 +582,7 @@ async def generate_new_api_key( async def validate_basic_auth( credentials: HTTPBasicCredentials = Depends(security_basic), ) -> str: - """ - Validate basic authentication for AlpineBits protocol. + """Validate basic authentication for AlpineBits protocol. Returns username if valid, raises HTTPException if not. """ # Accept any username/password pair present in config['alpine_bits_auth'] @@ -626,9 +614,8 @@ async def validate_basic_auth( return credentials.username, credentials.password -def parse_multipart_data(content_type: str, body: bytes) -> Dict[str, Any]: - """ - Parse multipart/form-data from raw request body. +def parse_multipart_data(content_type: str, body: bytes) -> dict[str, Any]: + """Parse multipart/form-data from raw request body. This is a simplified parser for the AlpineBits use case. """ if "multipart/form-data" not in content_type: @@ -692,8 +679,7 @@ async def alpinebits_server_handshake( credentials_tupel: tuple = Depends(validate_basic_auth), dbsession=Depends(get_async_session), ): - """ - AlpineBits server endpoint implementing the handshake protocol. + """AlpineBits server endpoint implementing the handshake protocol. This endpoint handles: - Protocol version negotiation via X-AlpineBits-ClientProtocolVersion header @@ -747,10 +733,10 @@ async def alpinebits_server_handshake( try: body = gzip.decompress(body) - except Exception as e: + except Exception: raise HTTPException( status_code=400, - detail=f"ERROR: Failed to decompress gzip content", + detail="ERROR: Failed to decompress gzip content", ) # Check content type (after decompression) @@ -767,10 +753,10 @@ async def alpinebits_server_handshake( if "multipart/form-data" in content_type: try: form_data = parse_multipart_data(content_type, body) - except Exception as e: + except Exception: raise HTTPException( status_code=400, - detail=f"ERROR: Failed to parse multipart/form-data", + detail="ERROR: Failed to parse multipart/form-data", ) elif "application/x-www-form-urlencoded" in content_type: # Parse as urlencoded @@ -829,15 +815,14 @@ async def alpinebits_server_handshake( # Re-raise HTTP exceptions (auth errors, etc.) raise except Exception as e: - _LOGGER.error(f"Error in AlpineBits handshake: {str(e)}") - raise HTTPException(status_code=500, detail=f"Internal server error") + _LOGGER.error(f"Error in AlpineBits handshake: {e!s}") + raise HTTPException(status_code=500, detail="Internal server error") @api_router.get("/admin/stats") @limiter.limit("10/minute") async def get_api_stats(request: Request, admin_key: str = Depends(validate_api_key)): - """ - Admin endpoint to get API usage statistics. + """Admin endpoint to get API usage statistics. Requires admin API key. """ if admin_key != "admin-key": @@ -862,8 +847,7 @@ app.include_router(api_router) @app.get("/", response_class=HTMLResponse) async def landing_page(): - """ - Serve the under construction landing page at the root route + """Serve the under construction landing page at the root route """ try: # Get the path to the HTML file @@ -871,7 +855,7 @@ async def landing_page(): html_path = os.path.join(os.path.dirname(__file__), "templates", "index.html") - with open(html_path, "r", encoding="utf-8") as f: + with open(html_path, encoding="utf-8") as f: html_content = f.read() return HTMLResponse(content=html_content, status_code=200) diff --git a/src/alpine_bits_python/auth.py b/src/alpine_bits_python/auth.py index e6e67d5..6f32046 100644 --- a/src/alpine_bits_python/auth.py +++ b/src/alpine_bits_python/auth.py @@ -1,13 +1,12 @@ -import os -import secrets -from typing import Optional -from fastapi import HTTPException, Security, status -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials import hashlib import hmac -from datetime import datetime, timedelta import logging +import os +import secrets + from dotenv import load_dotenv +from fastapi import HTTPException, Security, status +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer # Load environment variables from .env file load_dotenv() @@ -44,8 +43,7 @@ def generate_api_key() -> str: def validate_api_key( credentials: HTTPAuthorizationCredentials = Security(security), ) -> str: - """ - Validate API key from Authorization header. + """Validate API key from Authorization header. Expected format: Authorization: Bearer your_api_key_here """ token = credentials.credentials @@ -65,8 +63,7 @@ def validate_api_key( def validate_wix_signature(payload: bytes, signature: str, secret: str) -> bool: - """ - Validate Wix webhook signature for additional security. + """Validate Wix webhook signature for additional security. Wix signs their webhooks with HMAC-SHA256. """ if not signature or not secret: @@ -74,8 +71,7 @@ def validate_wix_signature(payload: bytes, signature: str, secret: str) -> bool: try: # Remove 'sha256=' prefix if present - if signature.startswith("sha256="): - signature = signature[7:] + signature = signature.removeprefix("sha256=") # Calculate expected signature expected_signature = hmac.new( @@ -95,7 +91,7 @@ class APIKeyAuth: def __init__(self, api_keys: dict): self.api_keys = api_keys - def authenticate(self, api_key: str) -> Optional[str]: + def authenticate(self, api_key: str) -> str | None: """Authenticate an API key and return the key name if valid""" for key_name, valid_key in self.api_keys.items(): if secrets.compare_digest(api_key, valid_key): diff --git a/src/alpine_bits_python/config_loader.py b/src/alpine_bits_python/config_loader.py index 229bda7..2da722f 100644 --- a/src/alpine_bits_python/config_loader.py +++ b/src/alpine_bits_python/config_loader.py @@ -1,25 +1,20 @@ import os from pathlib import Path -from typing import Any, Dict, List + from annotatedyaml.loader import ( - HAS_C_LOADER, - JSON_TYPE, - LoaderType, Secrets, - add_constructor, +) +from annotatedyaml.loader import ( load_yaml as load_annotated_yaml, - load_yaml_dict as load_annotated_yaml_dict, - parse_yaml as parse_annotated_yaml, - secret_yaml as annotated_secret_yaml, ) from voluptuous import ( - Schema, - Required, + PREVENT_EXTRA, All, Length, - PREVENT_EXTRA, MultipleInvalid, Optional, + Required, + Schema, ) # --- Voluptuous schemas --- @@ -101,7 +96,7 @@ class Config: return self.basic_auth["hotel_name"] @property - def users(self) -> List[Dict[str, str]]: + def users(self) -> list[dict[str, str]]: return self.basic_auth["users"] diff --git a/src/alpine_bits_python/db.py b/src/alpine_bits_python/db.py index 8a0cf82..9a58840 100644 --- a/src/alpine_bits_python/db.py +++ b/src/alpine_bits_python/db.py @@ -1,8 +1,8 @@ -from sqlalchemy import Column, Integer, String, Date, Boolean, ForeignKey, DateTime -from sqlalchemy.orm import declarative_base, relationship -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker import os +from sqlalchemy import Boolean, Column, Date, DateTime, ForeignKey, Integer, String +from sqlalchemy.orm import declarative_base, relationship + Base = declarative_base() diff --git a/src/alpine_bits_python/generated/__init__.py b/src/alpine_bits_python/generated/__init__.py index b5ee0c8..d802660 100644 --- a/src/alpine_bits_python/generated/__init__.py +++ b/src/alpine_bits_python/generated/__init__.py @@ -85,6 +85,7 @@ __all__ = [ "CommentName1", "CommentName2", "ContactInfoLocation", + "DefSendComplete", "DescriptionName", "DescriptionTextFormat1", "DescriptionTextFormat2", @@ -103,6 +104,7 @@ __all__ = [ "MealsIncludedMealPlanIndicator", "MultimediaDescriptionInfoCode1", "MultimediaDescriptionInfoCode2", + "OccupancyAgeQualifyingCode", "OtaHotelDescriptiveContentNotifRq", "OtaHotelDescriptiveContentNotifRs", "OtaHotelDescriptiveInfoRq", @@ -123,7 +125,6 @@ __all__ = [ "OtaPingRs", "OtaReadRq", "OtaResRetrieveRs", - "OccupancyAgeQualifyingCode", "PositionAltitudeUnitOfMeasureCode", "PrerequisiteInventoryInvType", "ProfileProfileType", @@ -150,12 +151,11 @@ __all__ = [ "TextTextFormat2", "TimeUnitType", "TypeRoomRoomType", - "UrlType", "UniqueIdInstance", "UniqueIdType1", "UniqueIdType2", "UniqueIdType3", + "UrlType", "VideoItemCategory", "WarningStatus", - "DefSendComplete", ] diff --git a/src/alpine_bits_python/generated/alpinebits.py b/src/alpine_bits_python/generated/alpinebits.py index 42c9ff3..82b4909 100644 --- a/src/alpine_bits_python/generated/alpinebits.py +++ b/src/alpine_bits_python/generated/alpinebits.py @@ -1,6 +1,6 @@ from decimal import Decimal from enum import Enum -from typing import Optional, Union +from typing import Optional from pydantic import BaseModel, ConfigDict from xsdata.models.datatype import XmlDate, XmlDateTime, XmlTime @@ -158,7 +158,7 @@ class OtaHotelDescriptiveInfoRq(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -187,7 +187,7 @@ class OtaHotelDescriptiveInfoRq(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -218,7 +218,7 @@ class OtaHotelRatePlanRq(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -264,7 +264,7 @@ class OtaHotelRatePlanRq(BaseModel): class DateRange(BaseModel): model_config = ConfigDict(defer_build=True) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -272,7 +272,7 @@ class OtaHotelRatePlanRq(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -296,7 +296,7 @@ class OtaHotelRatePlanRq(BaseModel): class RatePlanCandidate(BaseModel): model_config = ConfigDict(defer_build=True) - rate_plan_code: Optional[str] = field( + rate_plan_code: str | None = field( default=None, metadata={ "name": "RatePlanCode", @@ -305,7 +305,7 @@ class OtaHotelRatePlanRq(BaseModel): "max_length": 64, }, ) - rate_plan_id: Optional[str] = field( + rate_plan_id: str | None = field( default=None, metadata={ "name": "RatePlanID", @@ -326,7 +326,7 @@ class OtaHotelRatePlanRq(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -358,7 +358,7 @@ class OtaPingRq(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -387,7 +387,7 @@ class OtaReadRq(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -425,7 +425,7 @@ class OtaReadRq(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -585,8 +585,7 @@ class TextTextFormat2(Enum): class TimeUnitType(Enum): - """ - Defines the unit in which the time is expressed (e.g. year, day, hour). + """Defines the unit in which the time is expressed (e.g. year, day, hour). """ YEAR = "Year" @@ -673,7 +672,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -738,7 +737,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "type": "Element", }, ) - hotel_city_code: Optional[str] = field( + hotel_city_code: str | None = field( default=None, metadata={ "name": "HotelCityCode", @@ -756,7 +755,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -765,7 +764,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 128, }, ) - area_id: Optional[str] = field( + area_id: str | None = field( default=None, metadata={ "name": "AreaID", @@ -812,7 +811,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "type": "Element", }, ) - hotel_status_code: Optional[str] = field( + hotel_status_code: str | None = field( default=None, metadata={ "name": "HotelStatusCode", @@ -833,7 +832,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): class HotelCategory(BaseModel): model_config = ConfigDict(defer_build=True) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -903,7 +902,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "type": "Element", }, ) - info_code: Optional[MultimediaDescriptionInfoCode1] = field( + info_code: MultimediaDescriptionInfoCode1 | None = field( default=None, metadata={ "name": "InfoCode", @@ -935,7 +934,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "min_occurs": 1, }, ) - source_id: Optional[str] = field( + source_id: str | None = field( default=None, metadata={ "name": "SourceID", @@ -944,7 +943,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 32, }, ) - copyright_notice: Optional[str] = field( + copyright_notice: str | None = field( default=None, metadata={ "name": "CopyrightNotice", @@ -1028,7 +1027,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"https?://.+", } ) - copyright_notice: Optional[str] = field( + copyright_notice: str | None = field( default=None, metadata={ "name": "CopyrightNotice", @@ -1037,7 +1036,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 64, }, ) - source_id: Optional[str] = field( + source_id: str | None = field( default=None, metadata={ "name": "SourceID", @@ -1046,7 +1045,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 32, }, ) - title: Optional[str] = field( + title: str | None = field( default=None, metadata={ "name": "Title", @@ -1055,7 +1054,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 64, }, ) - applicable_start: Optional[str] = field( + applicable_start: str | None = field( default=None, metadata={ "name": "ApplicableStart", @@ -1063,7 +1062,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"--[0-1][0-9]-[0-3][0-9]", }, ) - applicable_end: Optional[str] = field( + applicable_end: str | None = field( default=None, metadata={ "name": "ApplicableEnd", @@ -1146,7 +1145,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"https?://.+", } ) - copyright_notice: Optional[str] = field( + copyright_notice: str | None = field( default=None, metadata={ "name": "CopyrightNotice", @@ -1155,7 +1154,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 64, }, ) - source_id: Optional[str] = field( + source_id: str | None = field( default=None, metadata={ "name": "SourceID", @@ -1164,7 +1163,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 32, }, ) - title: Optional[str] = field( + title: str | None = field( default=None, metadata={ "name": "Title", @@ -1173,7 +1172,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 64, }, ) - applicable_start: Optional[str] = field( + applicable_start: str | None = field( default=None, metadata={ "name": "ApplicableStart", @@ -1209,30 +1208,28 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): class Position(BaseModel): model_config = ConfigDict(defer_build=True) - altitude: Optional[Decimal] = field( + altitude: Decimal | None = field( default=None, metadata={ "name": "Altitude", "type": "Attribute", }, ) - altitude_unit_of_measure_code: Optional[ - PositionAltitudeUnitOfMeasureCode - ] = field( + altitude_unit_of_measure_code: PositionAltitudeUnitOfMeasureCode | None = field( default=None, metadata={ "name": "AltitudeUnitOfMeasureCode", "type": "Attribute", }, ) - latitude: Optional[Decimal] = field( + latitude: Decimal | None = field( default=None, metadata={ "name": "Latitude", "type": "Attribute", }, ) - longitude: Optional[Decimal] = field( + longitude: Decimal | None = field( default=None, metadata={ "name": "Longitude", @@ -1271,14 +1268,14 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]{1,3}(\.[A-Z]{3}){0,1}", } ) - meal_plan_code: Optional[ServiceMealPlanCode] = field( + meal_plan_code: ServiceMealPlanCode | None = field( default=None, metadata={ "name": "MealPlanCode", "type": "Attribute", }, ) - proximity_code: Optional[str] = field( + proximity_code: str | None = field( default=None, metadata={ "name": "ProximityCode", @@ -1286,7 +1283,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - included: Optional[str] = field( + included: str | None = field( default=None, metadata={ "name": "Included", @@ -1376,7 +1373,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 8, } ) - max_occupancy: Optional[str] = field( + max_occupancy: str | None = field( default=None, metadata={ "name": "MaxOccupancy", @@ -1384,7 +1381,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - min_occupancy: Optional[str] = field( + min_occupancy: str | None = field( default=None, metadata={ "name": "MinOccupancy", @@ -1392,7 +1389,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - max_child_occupancy: Optional[str] = field( + max_child_occupancy: str | None = field( default=None, metadata={ "name": "MaxChildOccupancy", @@ -1400,7 +1397,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - id: Optional[str] = field( + id: str | None = field( default=None, metadata={ "name": "ID", @@ -1412,7 +1409,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): class TypeRoom(BaseModel): model_config = ConfigDict(defer_build=True) - standard_occupancy: Optional[str] = field( + standard_occupancy: str | None = field( default=None, metadata={ "name": "StandardOccupancy", @@ -1420,7 +1417,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - room_classification_code: Optional[str] = field( + room_classification_code: str | None = field( default=None, metadata={ "name": "RoomClassificationCode", @@ -1428,7 +1425,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - room_id: Optional[str] = field( + room_id: str | None = field( default=None, metadata={ "name": "RoomID", @@ -1437,7 +1434,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 16, }, ) - size: Optional[str] = field( + size: str | None = field( default=None, metadata={ "name": "Size", @@ -1445,7 +1442,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - room_type: Optional[TypeRoomRoomType] = field( + room_type: TypeRoomRoomType | None = field( default=None, metadata={ "name": "RoomType", @@ -1468,7 +1465,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): class Amenity(BaseModel): model_config = ConfigDict(defer_build=True) - room_amenity_code: Optional[str] = field( + room_amenity_code: str | None = field( default=None, metadata={ "name": "RoomAmenityCode", @@ -1509,7 +1506,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "type": "Element", }, ) - info_code: Optional[MultimediaDescriptionInfoCode2] = ( + info_code: MultimediaDescriptionInfoCode2 | None = ( field( default=None, metadata={ @@ -1617,7 +1614,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"https?://.+", } ) - copyright_notice: Optional[str] = field( + copyright_notice: str | None = field( default=None, metadata={ "name": "CopyrightNotice", @@ -1808,7 +1805,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "required": True, } ) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -1817,7 +1814,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -1826,7 +1823,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 3, }, ) - decimal_places: Optional[str] = field( + decimal_places: str | None = field( default=None, metadata={ "name": "DecimalPlaces", @@ -1892,7 +1889,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "required": True, } ) - max_pet_quantity: Optional[str] = field( + max_pet_quantity: str | None = field( default=None, metadata={ "name": "MaxPetQuantity", @@ -1900,7 +1897,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - non_refundable_fee: Optional[str] = field( + non_refundable_fee: str | None = field( default=None, metadata={ "name": "NonRefundableFee", @@ -1909,7 +1906,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - charge_code: Optional[str] = field( + charge_code: str | None = field( default=None, metadata={ "name": "ChargeCode", @@ -1917,7 +1914,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -1926,7 +1923,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 3, }, ) - decimal_places: Optional[str] = field( + decimal_places: str | None = field( default=None, metadata={ "name": "DecimalPlaces", @@ -1992,7 +1989,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "required": True, } ) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -2001,7 +1998,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -2010,7 +2007,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "max_length": 3, }, ) - decimal_places: Optional[str] = field( + decimal_places: str | None = field( default=None, metadata={ "name": "DecimalPlaces", @@ -2018,14 +2015,14 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - code: Optional[TaxPolicyCode] = field( + code: TaxPolicyCode | None = field( default=None, metadata={ "name": "Code", "type": "Attribute", }, ) - charge_frequency: Optional[TaxPolicyChargeFrequency] = ( + charge_frequency: TaxPolicyChargeFrequency | None = ( field( default=None, metadata={ @@ -2034,7 +2031,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): }, ) ) - charge_unit: Optional[TaxPolicyChargeUnit] = field( + charge_unit: TaxPolicyChargeUnit | None = field( default=None, metadata={ "name": "ChargeUnit", @@ -2222,7 +2219,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): class PaymentCard(BaseModel): model_config = ConfigDict(defer_build=True) - card_type: Optional[str] = field( + card_type: str | None = field( default=None, metadata={ "name": "CardType", @@ -2230,7 +2227,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "min_length": 1, }, ) - card_code: Optional[str] = field( + card_code: str | None = field( default=None, metadata={ "name": "CardCode", @@ -2279,7 +2276,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): class PolicyInfo(BaseModel): model_config = ConfigDict(defer_build=True) - min_guest_age: Optional[str] = field( + min_guest_age: str | None = field( default=None, metadata={ "name": "MinGuestAge", @@ -2304,14 +2301,14 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): class StayRequirement(BaseModel): model_config = ConfigDict(defer_build=True) - stay_context: Optional[StayRequirementStayContext] = field( + stay_context: StayRequirementStayContext | None = field( default=None, metadata={ "name": "StayContext", "type": "Attribute", }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -2319,7 +2316,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "pattern": r"[0-2][0-9]:[0-5][0-9]:[0-5][0-9]", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -2370,7 +2367,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "min_length": 1, } ) - rating_symbol: Optional[str] = field( + rating_symbol: str | None = field( default=None, metadata={ "name": "RatingSymbol", @@ -2378,7 +2375,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "min_length": 1, }, ) - official_appointment_ind: Optional[str] = field( + official_appointment_ind: str | None = field( default=None, metadata={ "name": "OfficialAppointmentInd", @@ -2435,7 +2432,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "type": "Element", }, ) - company_name: Optional[str] = field( + company_name: str | None = field( default=None, metadata={ "name": "CompanyName", @@ -2443,7 +2440,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "min_length": 0, }, ) - location: Optional[ContactInfoLocation] = field( + location: ContactInfoLocation | None = field( default=None, metadata={ "name": "Location", @@ -2511,7 +2508,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "type": "Element", }, ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -2626,7 +2623,7 @@ class OtaHotelDescriptiveContentNotifRq(BaseModel): "required": True, }, ) - id: Optional[str] = field( + id: str | None = field( default=None, metadata={ "name": "ID", @@ -2649,7 +2646,7 @@ class OtaHotelDescriptiveContentNotifRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -2670,7 +2667,7 @@ class OtaHotelDescriptiveContentNotifRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -2698,7 +2695,7 @@ class OtaHotelDescriptiveContentNotifRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -2706,7 +2703,7 @@ class OtaHotelDescriptiveContentNotifRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -2743,7 +2740,7 @@ class OtaHotelDescriptiveContentNotifRs(BaseModel): "pattern": r"[0-9]+", } ) - record_id: Optional[str] = field( + record_id: str | None = field( default=None, metadata={ "name": "RecordID", @@ -2752,7 +2749,7 @@ class OtaHotelDescriptiveContentNotifRs(BaseModel): "max_length": 64, }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -2782,7 +2779,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -2812,7 +2809,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -2840,7 +2837,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -2848,7 +2845,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -2885,7 +2882,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", } ) - record_id: Optional[str] = field( + record_id: str | None = field( default=None, metadata={ "name": "RecordID", @@ -2894,7 +2891,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 64, }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -2967,7 +2964,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "type": "Element", }, ) - hotel_city_code: Optional[str] = field( + hotel_city_code: str | None = field( default=None, metadata={ "name": "HotelCityCode", @@ -2985,7 +2982,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -2994,7 +2991,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 128, }, ) - area_id: Optional[str] = field( + area_id: str | None = field( default=None, metadata={ "name": "AreaID", @@ -3041,7 +3038,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "type": "Element", }, ) - hotel_status_code: Optional[str] = field( + hotel_status_code: str | None = field( default=None, metadata={ "name": "HotelStatusCode", @@ -3062,7 +3059,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): class HotelCategory(BaseModel): model_config = ConfigDict(defer_build=True) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -3132,7 +3129,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "type": "Element", }, ) - info_code: Optional[MultimediaDescriptionInfoCode1] = field( + info_code: MultimediaDescriptionInfoCode1 | None = field( default=None, metadata={ "name": "InfoCode", @@ -3164,7 +3161,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "min_occurs": 1, }, ) - source_id: Optional[str] = field( + source_id: str | None = field( default=None, metadata={ "name": "SourceID", @@ -3173,7 +3170,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 32, }, ) - copyright_notice: Optional[str] = field( + copyright_notice: str | None = field( default=None, metadata={ "name": "CopyrightNotice", @@ -3257,7 +3254,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"https?://.+", } ) - copyright_notice: Optional[str] = field( + copyright_notice: str | None = field( default=None, metadata={ "name": "CopyrightNotice", @@ -3266,7 +3263,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 64, }, ) - source_id: Optional[str] = field( + source_id: str | None = field( default=None, metadata={ "name": "SourceID", @@ -3275,7 +3272,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 32, }, ) - title: Optional[str] = field( + title: str | None = field( default=None, metadata={ "name": "Title", @@ -3284,7 +3281,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 64, }, ) - applicable_start: Optional[str] = field( + applicable_start: str | None = field( default=None, metadata={ "name": "ApplicableStart", @@ -3292,7 +3289,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"--[0-1][0-9]-[0-3][0-9]", }, ) - applicable_end: Optional[str] = field( + applicable_end: str | None = field( default=None, metadata={ "name": "ApplicableEnd", @@ -3375,7 +3372,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"https?://.+", } ) - copyright_notice: Optional[str] = field( + copyright_notice: str | None = field( default=None, metadata={ "name": "CopyrightNotice", @@ -3384,7 +3381,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 64, }, ) - source_id: Optional[str] = field( + source_id: str | None = field( default=None, metadata={ "name": "SourceID", @@ -3393,7 +3390,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 32, }, ) - title: Optional[str] = field( + title: str | None = field( default=None, metadata={ "name": "Title", @@ -3402,7 +3399,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 64, }, ) - applicable_start: Optional[str] = field( + applicable_start: str | None = field( default=None, metadata={ "name": "ApplicableStart", @@ -3438,30 +3435,28 @@ class OtaHotelDescriptiveInfoRs(BaseModel): class Position(BaseModel): model_config = ConfigDict(defer_build=True) - altitude: Optional[Decimal] = field( + altitude: Decimal | None = field( default=None, metadata={ "name": "Altitude", "type": "Attribute", }, ) - altitude_unit_of_measure_code: Optional[ - PositionAltitudeUnitOfMeasureCode - ] = field( + altitude_unit_of_measure_code: PositionAltitudeUnitOfMeasureCode | None = field( default=None, metadata={ "name": "AltitudeUnitOfMeasureCode", "type": "Attribute", }, ) - latitude: Optional[Decimal] = field( + latitude: Decimal | None = field( default=None, metadata={ "name": "Latitude", "type": "Attribute", }, ) - longitude: Optional[Decimal] = field( + longitude: Decimal | None = field( default=None, metadata={ "name": "Longitude", @@ -3500,14 +3495,14 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]{1,3}(\.[A-Z]{3}){0,1}", } ) - meal_plan_code: Optional[ServiceMealPlanCode] = field( + meal_plan_code: ServiceMealPlanCode | None = field( default=None, metadata={ "name": "MealPlanCode", "type": "Attribute", }, ) - proximity_code: Optional[str] = field( + proximity_code: str | None = field( default=None, metadata={ "name": "ProximityCode", @@ -3515,7 +3510,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - included: Optional[str] = field( + included: str | None = field( default=None, metadata={ "name": "Included", @@ -3605,7 +3600,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 8, } ) - max_occupancy: Optional[str] = field( + max_occupancy: str | None = field( default=None, metadata={ "name": "MaxOccupancy", @@ -3613,7 +3608,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - min_occupancy: Optional[str] = field( + min_occupancy: str | None = field( default=None, metadata={ "name": "MinOccupancy", @@ -3621,7 +3616,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - max_child_occupancy: Optional[str] = field( + max_child_occupancy: str | None = field( default=None, metadata={ "name": "MaxChildOccupancy", @@ -3629,7 +3624,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - id: Optional[str] = field( + id: str | None = field( default=None, metadata={ "name": "ID", @@ -3641,7 +3636,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): class TypeRoom(BaseModel): model_config = ConfigDict(defer_build=True) - standard_occupancy: Optional[str] = field( + standard_occupancy: str | None = field( default=None, metadata={ "name": "StandardOccupancy", @@ -3649,7 +3644,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - room_classification_code: Optional[str] = field( + room_classification_code: str | None = field( default=None, metadata={ "name": "RoomClassificationCode", @@ -3657,7 +3652,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - room_id: Optional[str] = field( + room_id: str | None = field( default=None, metadata={ "name": "RoomID", @@ -3666,7 +3661,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 16, }, ) - size: Optional[str] = field( + size: str | None = field( default=None, metadata={ "name": "Size", @@ -3674,7 +3669,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - room_type: Optional[TypeRoomRoomType] = field( + room_type: TypeRoomRoomType | None = field( default=None, metadata={ "name": "RoomType", @@ -3697,7 +3692,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): class Amenity(BaseModel): model_config = ConfigDict(defer_build=True) - room_amenity_code: Optional[str] = field( + room_amenity_code: str | None = field( default=None, metadata={ "name": "RoomAmenityCode", @@ -3738,7 +3733,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "type": "Element", }, ) - info_code: Optional[MultimediaDescriptionInfoCode2] = ( + info_code: MultimediaDescriptionInfoCode2 | None = ( field( default=None, metadata={ @@ -3846,7 +3841,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"https?://.+", } ) - copyright_notice: Optional[str] = field( + copyright_notice: str | None = field( default=None, metadata={ "name": "CopyrightNotice", @@ -4037,7 +4032,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "required": True, } ) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -4046,7 +4041,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -4055,7 +4050,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 3, }, ) - decimal_places: Optional[str] = field( + decimal_places: str | None = field( default=None, metadata={ "name": "DecimalPlaces", @@ -4121,7 +4116,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "required": True, } ) - max_pet_quantity: Optional[str] = field( + max_pet_quantity: str | None = field( default=None, metadata={ "name": "MaxPetQuantity", @@ -4129,7 +4124,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - non_refundable_fee: Optional[str] = field( + non_refundable_fee: str | None = field( default=None, metadata={ "name": "NonRefundableFee", @@ -4138,7 +4133,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - charge_code: Optional[str] = field( + charge_code: str | None = field( default=None, metadata={ "name": "ChargeCode", @@ -4146,7 +4141,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -4155,7 +4150,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 3, }, ) - decimal_places: Optional[str] = field( + decimal_places: str | None = field( default=None, metadata={ "name": "DecimalPlaces", @@ -4221,7 +4216,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "required": True, } ) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -4230,7 +4225,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -4239,7 +4234,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "max_length": 3, }, ) - decimal_places: Optional[str] = field( + decimal_places: str | None = field( default=None, metadata={ "name": "DecimalPlaces", @@ -4247,14 +4242,14 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-9]+", }, ) - code: Optional[TaxPolicyCode] = field( + code: TaxPolicyCode | None = field( default=None, metadata={ "name": "Code", "type": "Attribute", }, ) - charge_frequency: Optional[TaxPolicyChargeFrequency] = ( + charge_frequency: TaxPolicyChargeFrequency | None = ( field( default=None, metadata={ @@ -4263,7 +4258,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): }, ) ) - charge_unit: Optional[TaxPolicyChargeUnit] = field( + charge_unit: TaxPolicyChargeUnit | None = field( default=None, metadata={ "name": "ChargeUnit", @@ -4451,7 +4446,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): class PaymentCard(BaseModel): model_config = ConfigDict(defer_build=True) - card_type: Optional[str] = field( + card_type: str | None = field( default=None, metadata={ "name": "CardType", @@ -4459,7 +4454,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "min_length": 1, }, ) - card_code: Optional[str] = field( + card_code: str | None = field( default=None, metadata={ "name": "CardCode", @@ -4508,7 +4503,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): class PolicyInfo(BaseModel): model_config = ConfigDict(defer_build=True) - min_guest_age: Optional[str] = field( + min_guest_age: str | None = field( default=None, metadata={ "name": "MinGuestAge", @@ -4533,14 +4528,14 @@ class OtaHotelDescriptiveInfoRs(BaseModel): class StayRequirement(BaseModel): model_config = ConfigDict(defer_build=True) - stay_context: Optional[StayRequirementStayContext] = field( + stay_context: StayRequirementStayContext | None = field( default=None, metadata={ "name": "StayContext", "type": "Attribute", }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -4548,7 +4543,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "pattern": r"[0-2][0-9]:[0-5][0-9]:[0-5][0-9]", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -4599,7 +4594,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "min_length": 1, } ) - rating_symbol: Optional[str] = field( + rating_symbol: str | None = field( default=None, metadata={ "name": "RatingSymbol", @@ -4607,7 +4602,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "min_length": 1, }, ) - official_appointment_ind: Optional[str] = field( + official_appointment_ind: str | None = field( default=None, metadata={ "name": "OfficialAppointmentInd", @@ -4664,7 +4659,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "type": "Element", }, ) - company_name: Optional[str] = field( + company_name: str | None = field( default=None, metadata={ "name": "CompanyName", @@ -4672,7 +4667,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "min_length": 0, }, ) - location: Optional[ContactInfoLocation] = field( + location: ContactInfoLocation | None = field( default=None, metadata={ "name": "Location", @@ -4740,7 +4735,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "type": "Element", }, ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -4855,7 +4850,7 @@ class OtaHotelDescriptiveInfoRs(BaseModel): "required": True, }, ) - id: Optional[str] = field( + id: str | None = field( default=None, metadata={ "name": "ID", @@ -4936,7 +4931,7 @@ class OtaHotelInvCountNotifRq(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -4985,7 +4980,7 @@ class OtaHotelInvCountNotifRq(BaseModel): "pattern": r"\S+", } ) - inv_type_code: Optional[str] = field( + inv_type_code: str | None = field( default=None, metadata={ "name": "InvTypeCode", @@ -4994,7 +4989,7 @@ class OtaHotelInvCountNotifRq(BaseModel): "max_length": 8, }, ) - inv_code: Optional[str] = field( + inv_code: str | None = field( default=None, metadata={ "name": "InvCode", @@ -5003,7 +4998,7 @@ class OtaHotelInvCountNotifRq(BaseModel): "max_length": 16, }, ) - all_inv_code: Optional[str] = field( + all_inv_code: str | None = field( default=None, metadata={ "name": "AllInvCode", @@ -5058,7 +5053,7 @@ class OtaHotelInvCountNotifRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -5079,7 +5074,7 @@ class OtaHotelInvCountNotifRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -5107,7 +5102,7 @@ class OtaHotelInvCountNotifRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -5115,7 +5110,7 @@ class OtaHotelInvCountNotifRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -5152,7 +5147,7 @@ class OtaHotelInvCountNotifRs(BaseModel): "pattern": r"[0-9]+", } ) - record_id: Optional[str] = field( + record_id: str | None = field( default=None, metadata={ "name": "RecordID", @@ -5161,7 +5156,7 @@ class OtaHotelInvCountNotifRs(BaseModel): "max_length": 64, }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -5256,7 +5251,7 @@ class OtaHotelPostEventNotifRq(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -5332,7 +5327,7 @@ class OtaHotelPostEventNotifRq(BaseModel): "type": "Element", }, ) - type_value: Optional[str] = field( + type_value: str | None = field( default=None, metadata={ "name": "Type", @@ -5340,7 +5335,7 @@ class OtaHotelPostEventNotifRq(BaseModel): "min_length": 1, }, ) - url: Optional[str] = field( + url: str | None = field( default=None, metadata={ "name": "URL", @@ -5348,7 +5343,7 @@ class OtaHotelPostEventNotifRq(BaseModel): "pattern": r"https?://.+", }, ) - acronym: Optional[str] = field( + acronym: str | None = field( default=None, metadata={ "name": "Acronym", @@ -5400,7 +5395,7 @@ class OtaHotelPostEventNotifRq(BaseModel): "type": "Element", }, ) - role: Optional[str] = field( + role: str | None = field( default=None, metadata={ "name": "Role", @@ -5412,7 +5407,7 @@ class OtaHotelPostEventNotifRq(BaseModel): class PersonName(BaseModel): model_config = ConfigDict(defer_build=True) - given_name: Optional[str] = field( + given_name: str | None = field( default=None, metadata={ "name": "GivenName", @@ -5450,7 +5445,7 @@ class OtaHotelPostEventNotifRq(BaseModel): class EmployeeInfo(BaseModel): model_config = ConfigDict(defer_build=True) - employee_id: Optional[str] = field( + employee_id: str | None = field( default=None, metadata={ "name": "EmployeeId", @@ -5513,14 +5508,14 @@ class OtaHotelPostEventNotifRq(BaseModel): "type": "Element", }, ) - start: Union[XmlDate, XmlDateTime, XmlTime] = field( + start: XmlDate | XmlDateTime | XmlTime = field( metadata={ "name": "Start", "type": "Attribute", "required": True, } ) - end: Optional[Union[XmlDate, XmlDateTime, XmlTime]] = field( + end: XmlDate | XmlDateTime | XmlTime | None = field( default=None, metadata={ "name": "End", @@ -5530,7 +5525,7 @@ class OtaHotelPostEventNotifRq(BaseModel): class EndDateWindow(BaseModel): model_config = ConfigDict(defer_build=True) - latest_date: Union[XmlDate, XmlDateTime] = field( + latest_date: XmlDate | XmlDateTime = field( metadata={ "name": "LatestDate", "type": "Attribute", @@ -5638,7 +5633,7 @@ class OtaHotelPostEventNotifRq(BaseModel): "min_length": 1, }, ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -5661,7 +5656,7 @@ class OtaHotelPostEventNotifRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -5682,7 +5677,7 @@ class OtaHotelPostEventNotifRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -5710,7 +5705,7 @@ class OtaHotelPostEventNotifRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -5718,7 +5713,7 @@ class OtaHotelPostEventNotifRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -5755,7 +5750,7 @@ class OtaHotelPostEventNotifRs(BaseModel): "pattern": r"[0-9]+", } ) - record_id: Optional[str] = field( + record_id: str | None = field( default=None, metadata={ "name": "RecordID", @@ -5764,7 +5759,7 @@ class OtaHotelPostEventNotifRs(BaseModel): "max_length": 64, }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -5808,7 +5803,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -5859,7 +5854,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -5915,7 +5910,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_occurs": 5, }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -5923,7 +5918,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -5931,14 +5926,14 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - rate_plan_notif_type: Optional[RatePlanRatePlanNotifType] = field( + rate_plan_notif_type: RatePlanRatePlanNotifType | None = field( default=None, metadata={ "name": "RatePlanNotifType", "type": "Attribute", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -5947,7 +5942,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_length": 3, }, ) - rate_plan_code: Optional[str] = field( + rate_plan_code: str | None = field( default=None, metadata={ "name": "RatePlanCode", @@ -5956,14 +5951,14 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_length": 64, }, ) - rate_plan_type: Optional[RatePlanRatePlanType] = field( + rate_plan_type: RatePlanRatePlanType | None = field( default=None, metadata={ "name": "RatePlanType", "type": "Attribute", }, ) - rate_plan_category: Optional[str] = field( + rate_plan_category: str | None = field( default=None, metadata={ "name": "RatePlanCategory", @@ -5972,7 +5967,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_length": 64, }, ) - rate_plan_id: Optional[str] = field( + rate_plan_id: str | None = field( default=None, metadata={ "name": "RatePlanID", @@ -5981,7 +5976,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_length": 64, }, ) - rate_plan_qualifier: Optional[str] = field( + rate_plan_qualifier: str | None = field( default=None, metadata={ "name": "RatePlanQualifier", @@ -6032,14 +6027,14 @@ class OtaHotelRatePlanNotifRq(BaseModel): "type": "Element", }, ) - code_context: Optional[BookingRuleCodeContext] = field( + code_context: BookingRuleCodeContext | None = field( default=None, metadata={ "name": "CodeContext", "type": "Attribute", }, ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -6048,7 +6043,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_length": 8, }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -6056,7 +6051,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -6129,7 +6124,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): class ArrivalDaysOfWeek(BaseModel): model_config = ConfigDict(defer_build=True) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -6137,7 +6132,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -6145,7 +6140,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -6153,7 +6148,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -6161,7 +6156,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -6169,7 +6164,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -6177,7 +6172,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -6188,7 +6183,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): class DepartureDaysOfWeek(BaseModel): model_config = ConfigDict(defer_build=True) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -6196,7 +6191,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -6204,7 +6199,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -6212,7 +6207,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -6220,7 +6215,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -6228,7 +6223,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -6236,7 +6231,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -6247,14 +6242,14 @@ class OtaHotelRatePlanNotifRq(BaseModel): class RestrictionStatus(BaseModel): model_config = ConfigDict(defer_build=True) - restriction: Optional[RestrictionStatusRestriction] = field( + restriction: RestrictionStatusRestriction | None = field( default=None, metadata={ "name": "Restriction", "type": "Attribute", }, ) - status: Optional[RestrictionStatusStatus] = field( + status: RestrictionStatusStatus | None = field( default=None, metadata={ "name": "Status", @@ -6313,7 +6308,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "type": "Element", }, ) - min_guest_applicable: Optional[str] = field( + min_guest_applicable: str | None = field( default=None, metadata={ "name": "MinGuestApplicable", @@ -6321,7 +6316,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -6329,7 +6324,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -6337,14 +6332,14 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - rate_time_unit: Optional[RateRateTimeUnit] = field( + rate_time_unit: RateRateTimeUnit | None = field( default=None, metadata={ "name": "RateTimeUnit", "type": "Attribute", }, ) - unit_multiplier: Optional[str] = field( + unit_multiplier: str | None = field( default=None, metadata={ "name": "UnitMultiplier", @@ -6352,7 +6347,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -6360,7 +6355,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -6368,7 +6363,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -6376,7 +6371,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -6384,7 +6379,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -6392,7 +6387,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -6400,7 +6395,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -6408,7 +6403,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - duration: Optional[str] = field( + duration: str | None = field( default=None, metadata={ "name": "Duration", @@ -6416,7 +6411,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"P[0-9]+N", }, ) - inv_type_code: Optional[str] = field( + inv_type_code: str | None = field( default=None, metadata={ "name": "InvTypeCode", @@ -6441,7 +6436,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): class BaseByGuestAmt(BaseModel): model_config = ConfigDict(defer_build=True) - number_of_guests: Optional[str] = field( + number_of_guests: str | None = field( default=None, metadata={ "name": "NumberOfGuests", @@ -6449,7 +6444,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - amount_after_tax: Optional[str] = field( + amount_after_tax: str | None = field( default=None, metadata={ "name": "AmountAfterTax", @@ -6458,7 +6453,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -6467,14 +6462,14 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_length": 3, }, ) - type_value: Optional[BaseByGuestAmtType] = field( + type_value: BaseByGuestAmtType | None = field( default=None, metadata={ "name": "Type", "type": "Attribute", }, ) - age_qualifying_code: Optional[str] = field( + age_qualifying_code: str | None = field( default=None, metadata={ "name": "AgeQualifyingCode", @@ -6498,7 +6493,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): class AdditionalGuestAmount(BaseModel): model_config = ConfigDict(defer_build=True) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -6507,7 +6502,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - age_qualifying_code: Optional[str] = field( + age_qualifying_code: str | None = field( default=None, metadata={ "name": "AgeQualifyingCode", @@ -6515,7 +6510,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - min_age: Optional[str] = field( + min_age: str | None = field( default=None, metadata={ "name": "MinAge", @@ -6523,7 +6518,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - max_age: Optional[str] = field( + max_age: str | None = field( default=None, metadata={ "name": "MaxAge", @@ -6580,7 +6575,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): class MealsIncluded(BaseModel): model_config = ConfigDict(defer_build=True) - breakfast: Optional[str] = field( + breakfast: str | None = field( default=None, metadata={ "name": "Breakfast", @@ -6588,7 +6583,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - lunch: Optional[str] = field( + lunch: str | None = field( default=None, metadata={ "name": "Lunch", @@ -6596,7 +6591,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - dinner: Optional[str] = field( + dinner: str | None = field( default=None, metadata={ "name": "Dinner", @@ -6604,16 +6599,14 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - meal_plan_codes: Optional[MealsIncludedMealPlanCodes] = field( + meal_plan_codes: MealsIncludedMealPlanCodes | None = field( default=None, metadata={ "name": "MealPlanCodes", "type": "Attribute", }, ) - meal_plan_indicator: Optional[ - MealsIncludedMealPlanIndicator - ] = field( + meal_plan_indicator: MealsIncludedMealPlanIndicator | None = field( default=None, metadata={ "name": "MealPlanIndicator", @@ -6655,23 +6648,21 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_occurs": 5, }, ) - add_to_basic_rate_indicator: Optional[ - SupplementAddToBasicRateIndicator - ] = field( + add_to_basic_rate_indicator: SupplementAddToBasicRateIndicator | None = field( default=None, metadata={ "name": "AddToBasicRateIndicator", "type": "Attribute", }, ) - charge_type_code: Optional[SupplementChargeTypeCode] = field( + charge_type_code: SupplementChargeTypeCode | None = field( default=None, metadata={ "name": "ChargeTypeCode", "type": "Attribute", }, ) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -6696,7 +6687,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "max_length": 16, } ) - mandatory_indicator: Optional[str] = field( + mandatory_indicator: str | None = field( default=None, metadata={ "name": "MandatoryIndicator", @@ -6704,7 +6695,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -6712,7 +6703,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -6780,7 +6771,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "required": True, } ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -6894,7 +6885,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "min_occurs": 1, }, ) - min_advanced_booking_offset: Optional[str] = field( + min_advanced_booking_offset: str | None = field( default=None, metadata={ "name": "MinAdvancedBookingOffset", @@ -6902,7 +6893,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"P[0-9]+D", }, ) - max_advanced_booking_offset: Optional[str] = field( + max_advanced_booking_offset: str | None = field( default=None, metadata={ "name": "MaxAdvancedBookingOffset", @@ -6973,7 +6964,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): class ArrivalDaysOfWeek(BaseModel): model_config = ConfigDict(defer_build=True) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -6981,7 +6972,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -6989,7 +6980,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -6997,7 +6988,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -7005,7 +6996,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -7013,7 +7004,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -7021,7 +7012,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -7032,7 +7023,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): class DepartureDaysOfWeek(BaseModel): model_config = ConfigDict(defer_build=True) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -7040,7 +7031,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -7048,7 +7039,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -7056,7 +7047,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -7064,7 +7055,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -7072,7 +7063,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -7080,7 +7071,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -7098,7 +7089,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "required": True, } ) - min_age: Optional[str] = field( + min_age: str | None = field( default=None, metadata={ "name": "MinAge", @@ -7106,7 +7097,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - max_age: Optional[str] = field( + max_age: str | None = field( default=None, metadata={ "name": "MaxAge", @@ -7114,7 +7105,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - min_occupancy: Optional[str] = field( + min_occupancy: str | None = field( default=None, metadata={ "name": "MinOccupancy", @@ -7122,7 +7113,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - max_occupancy: Optional[str] = field( + max_occupancy: str | None = field( default=None, metadata={ "name": "MaxOccupancy", @@ -7140,7 +7131,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "required": True, } ) - nights_required: Optional[str] = field( + nights_required: str | None = field( default=None, metadata={ "name": "NightsRequired", @@ -7148,7 +7139,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - nights_discounted: Optional[str] = field( + nights_discounted: str | None = field( default=None, metadata={ "name": "NightsDiscounted", @@ -7156,7 +7147,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - discount_pattern: Optional[str] = field( + discount_pattern: str | None = field( default=None, metadata={ "name": "DiscountPattern", @@ -7278,7 +7269,7 @@ class OtaHotelRatePlanNotifRq(BaseModel): "required": True, } ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -7301,7 +7292,7 @@ class OtaHotelRatePlanNotifRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -7322,7 +7313,7 @@ class OtaHotelRatePlanNotifRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -7350,7 +7341,7 @@ class OtaHotelRatePlanNotifRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -7358,7 +7349,7 @@ class OtaHotelRatePlanNotifRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -7395,7 +7386,7 @@ class OtaHotelRatePlanNotifRs(BaseModel): "pattern": r"[0-9]+", } ) - record_id: Optional[str] = field( + record_id: str | None = field( default=None, metadata={ "name": "RecordID", @@ -7404,7 +7395,7 @@ class OtaHotelRatePlanNotifRs(BaseModel): "max_length": 64, }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -7434,7 +7425,7 @@ class OtaHotelRatePlanRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -7462,7 +7453,7 @@ class OtaHotelRatePlanRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -7490,7 +7481,7 @@ class OtaHotelRatePlanRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -7498,7 +7489,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -7535,7 +7526,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", } ) - record_id: Optional[str] = field( + record_id: str | None = field( default=None, metadata={ "name": "RecordID", @@ -7544,7 +7535,7 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 64, }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -7579,7 +7570,7 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -7633,7 +7624,7 @@ class OtaHotelRatePlanRs(BaseModel): }, ) ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -7641,7 +7632,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -7649,14 +7640,14 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - rate_plan_notif_type: Optional[RatePlanRatePlanNotifType] = field( + rate_plan_notif_type: RatePlanRatePlanNotifType | None = field( default=None, metadata={ "name": "RatePlanNotifType", "type": "Attribute", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -7665,7 +7656,7 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 3, }, ) - rate_plan_code: Optional[str] = field( + rate_plan_code: str | None = field( default=None, metadata={ "name": "RatePlanCode", @@ -7674,14 +7665,14 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 64, }, ) - rate_plan_type: Optional[RatePlanRatePlanType] = field( + rate_plan_type: RatePlanRatePlanType | None = field( default=None, metadata={ "name": "RatePlanType", "type": "Attribute", }, ) - rate_plan_category: Optional[str] = field( + rate_plan_category: str | None = field( default=None, metadata={ "name": "RatePlanCategory", @@ -7690,7 +7681,7 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 64, }, ) - rate_plan_id: Optional[str] = field( + rate_plan_id: str | None = field( default=None, metadata={ "name": "RatePlanID", @@ -7699,7 +7690,7 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 64, }, ) - rate_plan_qualifier: Optional[str] = field( + rate_plan_qualifier: str | None = field( default=None, metadata={ "name": "RatePlanQualifier", @@ -7750,14 +7741,14 @@ class OtaHotelRatePlanRs(BaseModel): "type": "Element", }, ) - code_context: Optional[BookingRuleCodeContext] = field( + code_context: BookingRuleCodeContext | None = field( default=None, metadata={ "name": "CodeContext", "type": "Attribute", }, ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -7766,7 +7757,7 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 8, }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -7774,7 +7765,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -7847,7 +7838,7 @@ class OtaHotelRatePlanRs(BaseModel): class ArrivalDaysOfWeek(BaseModel): model_config = ConfigDict(defer_build=True) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -7855,7 +7846,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -7863,7 +7854,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -7871,7 +7862,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -7879,7 +7870,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -7887,7 +7878,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -7895,7 +7886,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -7906,7 +7897,7 @@ class OtaHotelRatePlanRs(BaseModel): class DepartureDaysOfWeek(BaseModel): model_config = ConfigDict(defer_build=True) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -7914,7 +7905,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -7922,7 +7913,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -7930,7 +7921,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -7938,7 +7929,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -7946,7 +7937,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -7954,7 +7945,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -7965,14 +7956,14 @@ class OtaHotelRatePlanRs(BaseModel): class RestrictionStatus(BaseModel): model_config = ConfigDict(defer_build=True) - restriction: Optional[RestrictionStatusRestriction] = field( + restriction: RestrictionStatusRestriction | None = field( default=None, metadata={ "name": "Restriction", "type": "Attribute", }, ) - status: Optional[RestrictionStatusStatus] = field( + status: RestrictionStatusStatus | None = field( default=None, metadata={ "name": "Status", @@ -8029,7 +8020,7 @@ class OtaHotelRatePlanRs(BaseModel): "type": "Element", }, ) - min_guest_applicable: Optional[str] = field( + min_guest_applicable: str | None = field( default=None, metadata={ "name": "MinGuestApplicable", @@ -8037,7 +8028,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -8045,7 +8036,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -8053,14 +8044,14 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - rate_time_unit: Optional[RateRateTimeUnit] = field( + rate_time_unit: RateRateTimeUnit | None = field( default=None, metadata={ "name": "RateTimeUnit", "type": "Attribute", }, ) - unit_multiplier: Optional[str] = field( + unit_multiplier: str | None = field( default=None, metadata={ "name": "UnitMultiplier", @@ -8068,7 +8059,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -8076,7 +8067,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -8084,7 +8075,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -8092,7 +8083,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -8100,7 +8091,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -8108,7 +8099,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -8116,7 +8107,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -8124,7 +8115,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - duration: Optional[str] = field( + duration: str | None = field( default=None, metadata={ "name": "Duration", @@ -8132,7 +8123,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"P[0-9]+N", }, ) - inv_type_code: Optional[str] = field( + inv_type_code: str | None = field( default=None, metadata={ "name": "InvTypeCode", @@ -8157,7 +8148,7 @@ class OtaHotelRatePlanRs(BaseModel): class BaseByGuestAmt(BaseModel): model_config = ConfigDict(defer_build=True) - number_of_guests: Optional[str] = field( + number_of_guests: str | None = field( default=None, metadata={ "name": "NumberOfGuests", @@ -8165,7 +8156,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - amount_after_tax: Optional[str] = field( + amount_after_tax: str | None = field( default=None, metadata={ "name": "AmountAfterTax", @@ -8174,7 +8165,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -8183,14 +8174,14 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 3, }, ) - type_value: Optional[BaseByGuestAmtType] = field( + type_value: BaseByGuestAmtType | None = field( default=None, metadata={ "name": "Type", "type": "Attribute", }, ) - age_qualifying_code: Optional[str] = field( + age_qualifying_code: str | None = field( default=None, metadata={ "name": "AgeQualifyingCode", @@ -8214,7 +8205,7 @@ class OtaHotelRatePlanRs(BaseModel): class AdditionalGuestAmount(BaseModel): model_config = ConfigDict(defer_build=True) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -8223,7 +8214,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - age_qualifying_code: Optional[str] = field( + age_qualifying_code: str | None = field( default=None, metadata={ "name": "AgeQualifyingCode", @@ -8231,7 +8222,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - min_age: Optional[str] = field( + min_age: str | None = field( default=None, metadata={ "name": "MinAge", @@ -8239,7 +8230,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - max_age: Optional[str] = field( + max_age: str | None = field( default=None, metadata={ "name": "MaxAge", @@ -8296,7 +8287,7 @@ class OtaHotelRatePlanRs(BaseModel): class MealsIncluded(BaseModel): model_config = ConfigDict(defer_build=True) - breakfast: Optional[str] = field( + breakfast: str | None = field( default=None, metadata={ "name": "Breakfast", @@ -8304,7 +8295,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - lunch: Optional[str] = field( + lunch: str | None = field( default=None, metadata={ "name": "Lunch", @@ -8312,7 +8303,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - dinner: Optional[str] = field( + dinner: str | None = field( default=None, metadata={ "name": "Dinner", @@ -8320,16 +8311,14 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - meal_plan_codes: Optional[MealsIncludedMealPlanCodes] = field( + meal_plan_codes: MealsIncludedMealPlanCodes | None = field( default=None, metadata={ "name": "MealPlanCodes", "type": "Attribute", }, ) - meal_plan_indicator: Optional[ - MealsIncludedMealPlanIndicator - ] = field( + meal_plan_indicator: MealsIncludedMealPlanIndicator | None = field( default=None, metadata={ "name": "MealPlanIndicator", @@ -8371,23 +8360,21 @@ class OtaHotelRatePlanRs(BaseModel): "max_occurs": 5, }, ) - add_to_basic_rate_indicator: Optional[ - SupplementAddToBasicRateIndicator - ] = field( + add_to_basic_rate_indicator: SupplementAddToBasicRateIndicator | None = field( default=None, metadata={ "name": "AddToBasicRateIndicator", "type": "Attribute", }, ) - charge_type_code: Optional[SupplementChargeTypeCode] = field( + charge_type_code: SupplementChargeTypeCode | None = field( default=None, metadata={ "name": "ChargeTypeCode", "type": "Attribute", }, ) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -8412,7 +8399,7 @@ class OtaHotelRatePlanRs(BaseModel): "max_length": 16, } ) - mandatory_indicator: Optional[str] = field( + mandatory_indicator: str | None = field( default=None, metadata={ "name": "MandatoryIndicator", @@ -8420,7 +8407,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -8428,7 +8415,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -8496,7 +8483,7 @@ class OtaHotelRatePlanRs(BaseModel): "required": True, } ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -8610,7 +8597,7 @@ class OtaHotelRatePlanRs(BaseModel): "min_occurs": 1, }, ) - min_advanced_booking_offset: Optional[str] = field( + min_advanced_booking_offset: str | None = field( default=None, metadata={ "name": "MinAdvancedBookingOffset", @@ -8618,7 +8605,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"P[0-9]+D", }, ) - max_advanced_booking_offset: Optional[str] = field( + max_advanced_booking_offset: str | None = field( default=None, metadata={ "name": "MaxAdvancedBookingOffset", @@ -8689,7 +8676,7 @@ class OtaHotelRatePlanRs(BaseModel): class ArrivalDaysOfWeek(BaseModel): model_config = ConfigDict(defer_build=True) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -8697,7 +8684,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -8705,7 +8692,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -8713,7 +8700,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -8721,7 +8708,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -8729,7 +8716,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -8737,7 +8724,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -8748,7 +8735,7 @@ class OtaHotelRatePlanRs(BaseModel): class DepartureDaysOfWeek(BaseModel): model_config = ConfigDict(defer_build=True) - mon: Optional[str] = field( + mon: str | None = field( default=None, metadata={ "name": "Mon", @@ -8756,7 +8743,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - tue: Optional[str] = field( + tue: str | None = field( default=None, metadata={ "name": "Tue", @@ -8764,7 +8751,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - weds: Optional[str] = field( + weds: str | None = field( default=None, metadata={ "name": "Weds", @@ -8772,7 +8759,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - thur: Optional[str] = field( + thur: str | None = field( default=None, metadata={ "name": "Thur", @@ -8780,7 +8767,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - fri: Optional[str] = field( + fri: str | None = field( default=None, metadata={ "name": "Fri", @@ -8788,7 +8775,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sat: Optional[str] = field( + sat: str | None = field( default=None, metadata={ "name": "Sat", @@ -8796,7 +8783,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"\S+", }, ) - sun: Optional[str] = field( + sun: str | None = field( default=None, metadata={ "name": "Sun", @@ -8814,7 +8801,7 @@ class OtaHotelRatePlanRs(BaseModel): "required": True, } ) - min_age: Optional[str] = field( + min_age: str | None = field( default=None, metadata={ "name": "MinAge", @@ -8822,7 +8809,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - max_age: Optional[str] = field( + max_age: str | None = field( default=None, metadata={ "name": "MaxAge", @@ -8830,7 +8817,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - min_occupancy: Optional[str] = field( + min_occupancy: str | None = field( default=None, metadata={ "name": "MinOccupancy", @@ -8838,7 +8825,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - max_occupancy: Optional[str] = field( + max_occupancy: str | None = field( default=None, metadata={ "name": "MaxOccupancy", @@ -8856,7 +8843,7 @@ class OtaHotelRatePlanRs(BaseModel): "required": True, } ) - nights_required: Optional[str] = field( + nights_required: str | None = field( default=None, metadata={ "name": "NightsRequired", @@ -8864,7 +8851,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - nights_discounted: Optional[str] = field( + nights_discounted: str | None = field( default=None, metadata={ "name": "NightsDiscounted", @@ -8872,7 +8859,7 @@ class OtaHotelRatePlanRs(BaseModel): "pattern": r"[0-9]+", }, ) - discount_pattern: Optional[str] = field( + discount_pattern: str | None = field( default=None, metadata={ "name": "DiscountPattern", @@ -8994,7 +8981,7 @@ class OtaHotelRatePlanRs(BaseModel): "required": True, } ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -9024,7 +9011,7 @@ class OtaHotelResNotifRq(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -9097,7 +9084,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+", } ) - last_modify_date_time: Optional[str] = field( + last_modify_date_time: str | None = field( default=None, metadata={ "name": "LastModifyDateTime", @@ -9112,7 +9099,7 @@ class OtaHotelResNotifRq(BaseModel): "required": True, } ) - room_stay_reservation: Optional[str] = field( + room_stay_reservation: str | None = field( default=None, metadata={ "name": "RoomStayReservation", @@ -9225,7 +9212,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - room_stay_group_id: Optional[str] = field( + room_stay_group_id: str | None = field( default=None, metadata={ "name": "RoomStayGroupID", @@ -9247,7 +9234,7 @@ class OtaHotelResNotifRq(BaseModel): class RoomType(BaseModel): model_config = ConfigDict(defer_build=True) - room_type_code: Optional[str] = field( + room_type_code: str | None = field( default=None, metadata={ "name": "RoomTypeCode", @@ -9256,7 +9243,7 @@ class OtaHotelResNotifRq(BaseModel): "max_length": 8, }, ) - room_classification_code: Optional[str] = field( + room_classification_code: str | None = field( default=None, metadata={ "name": "RoomClassificationCode", @@ -9264,7 +9251,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9]+", }, ) - room_type: Optional[RoomTypeRoomType] = field( + room_type: RoomTypeRoomType | None = field( default=None, metadata={ "name": "RoomType", @@ -9302,7 +9289,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - rate_plan_code: Optional[str] = field( + rate_plan_code: str | None = field( default=None, metadata={ "name": "RatePlanCode", @@ -9323,7 +9310,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - percent: Optional[str] = field( + percent: str | None = field( default=None, metadata={ "name": "Percent", @@ -9334,7 +9321,7 @@ class OtaHotelResNotifRq(BaseModel): class CommissionPayableAmount(BaseModel): model_config = ConfigDict(defer_build=True) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -9343,7 +9330,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -9443,7 +9430,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+", } ) - expire_date: Optional[str] = field( + expire_date: str | None = field( default=None, metadata={ "name": "ExpireDate", @@ -9451,7 +9438,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+", }, ) - expire_date_exclusive_ind: Optional[str] = field( + expire_date_exclusive_ind: str | None = field( default=None, metadata={ "name": "ExpireDateExclusiveInd", @@ -9459,14 +9446,14 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+", }, ) - rate_time_unit: Optional[RateRateTimeUnit] = field( + rate_time_unit: RateRateTimeUnit | None = field( default=None, metadata={ "name": "RateTimeUnit", "type": "Attribute", }, ) - unit_multiplier: Optional[str] = field( + unit_multiplier: str | None = field( default=None, metadata={ "name": "UnitMultiplier", @@ -9477,7 +9464,7 @@ class OtaHotelResNotifRq(BaseModel): class Base(BaseModel): model_config = ConfigDict(defer_build=True) - amount_after_tax: Optional[str] = field( + amount_after_tax: str | None = field( default=None, metadata={ "name": "AmountAfterTax", @@ -9486,7 +9473,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -9519,7 +9506,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9]+", } ) - age: Optional[str] = field( + age: str | None = field( default=None, metadata={ "name": "Age", @@ -9539,7 +9526,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -9547,7 +9534,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -9555,7 +9542,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+", }, ) - duration: Optional[str] = field( + duration: str | None = field( default=None, metadata={ "name": "Duration", @@ -9650,7 +9637,7 @@ class OtaHotelResNotifRq(BaseModel): class CardNumber(BaseModel): model_config = ConfigDict(defer_build=True) - plain_text: Optional[str] = field( + plain_text: str | None = field( default=None, metadata={ "name": "PlainText", @@ -9658,7 +9645,7 @@ class OtaHotelResNotifRq(BaseModel): "min_length": 1, }, ) - encrypted_value: Optional[str] = field( + encrypted_value: str | None = field( default=None, metadata={ "name": "EncryptedValue", @@ -9666,7 +9653,7 @@ class OtaHotelResNotifRq(BaseModel): "min_length": 1, }, ) - encryption_method: Optional[str] = field( + encryption_method: str | None = field( default=None, metadata={ "name": "EncryptionMethod", @@ -9774,7 +9761,7 @@ class OtaHotelResNotifRq(BaseModel): "max_length": 16, } ) - service_rph: Optional[str] = field( + service_rph: str | None = field( default=None, metadata={ "name": "ServiceRPH", @@ -9867,7 +9854,7 @@ class OtaHotelResNotifRq(BaseModel): class GuestCount(BaseModel): model_config = ConfigDict(defer_build=True) - age: Optional[str] = field( + age: str | None = field( default=None, metadata={ "name": "Age", @@ -9894,7 +9881,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+", } ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -10074,7 +10061,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - gender: Optional[str] = field( + gender: str | None = field( default=None, metadata={ "name": "Gender", @@ -10082,7 +10069,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"(Unknown|Male|Female)", }, ) - birth_date: Optional[str] = field( + birth_date: str | None = field( default=None, metadata={ "name": "BirthDate", @@ -10090,7 +10077,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+", }, ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -10101,7 +10088,7 @@ class OtaHotelResNotifRq(BaseModel): class PersonName(BaseModel): model_config = ConfigDict(defer_build=True) - name_prefix: Optional[str] = field( + name_prefix: str | None = field( default=None, metadata={ "name": "NamePrefix", @@ -10128,7 +10115,7 @@ class OtaHotelResNotifRq(BaseModel): "max_length": 64, } ) - name_title: Optional[str] = field( + name_title: str | None = field( default=None, metadata={ "name": "NameTitle", @@ -10140,7 +10127,7 @@ class OtaHotelResNotifRq(BaseModel): class Telephone(BaseModel): model_config = ConfigDict(defer_build=True) - phone_tech_type: Optional[str] = field( + phone_tech_type: str | None = field( default=None, metadata={ "name": "PhoneTechType", @@ -10166,7 +10153,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"\S+@\S+", }, ) - remark: Optional[str] = field( + remark: str | None = field( default=None, metadata={ "name": "Remark", @@ -10177,7 +10164,7 @@ class OtaHotelResNotifRq(BaseModel): class Address(BaseModel): model_config = ConfigDict(defer_build=True) - address_line: Optional[str] = field( + address_line: str | None = field( default=None, metadata={ "name": "AddressLine", @@ -10186,7 +10173,7 @@ class OtaHotelResNotifRq(BaseModel): "max_length": 255, }, ) - city_name: Optional[str] = field( + city_name: str | None = field( default=None, metadata={ "name": "CityName", @@ -10195,7 +10182,7 @@ class OtaHotelResNotifRq(BaseModel): "max_length": 64, }, ) - postal_code: Optional[str] = field( + postal_code: str | None = field( default=None, metadata={ "name": "PostalCode", @@ -10213,7 +10200,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - remark: Optional[str] = field( + remark: str | None = field( default=None, metadata={ "name": "Remark", @@ -10322,7 +10309,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - text: Optional[str] = field( + text: str | None = field( default=None, metadata={ "name": "Text", @@ -10452,14 +10439,14 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - start: Optional[object] = field( + start: object | None = field( default=None, metadata={ "name": "Start", "type": "Attribute", }, ) - guarantee_code: Optional[object] = field( + guarantee_code: object | None = field( default=None, metadata={ "name": "GuaranteeCode", @@ -10506,21 +10493,21 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - guarantee_type_code: Optional[object] = field( + guarantee_type_code: object | None = field( default=None, metadata={ "name": "GuaranteeTypeCode", "type": "Attribute", }, ) - guarantee_id: Optional[object] = field( + guarantee_id: object | None = field( default=None, metadata={ "name": "GuaranteeID", "type": "Attribute", }, ) - payment_transaction_type_code: Optional[object] = field( + payment_transaction_type_code: object | None = field( default=None, metadata={ "name": "PaymentTransactionTypeCode", @@ -10530,7 +10517,7 @@ class OtaHotelResNotifRq(BaseModel): class PaymentCard(BaseModel): model_config = ConfigDict(defer_build=True) - card_holder_name: Optional[str] = field( + card_holder_name: str | None = field( default=None, metadata={ "name": "CardHolderName", @@ -10548,7 +10535,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - card_code: Optional[str] = field( + card_code: str | None = field( default=None, metadata={ "name": "CardCode", @@ -10556,7 +10543,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[A-Z]{1,2}", }, ) - expire_date: Optional[str] = field( + expire_date: str | None = field( default=None, metadata={ "name": "ExpireDate", @@ -10567,7 +10554,7 @@ class OtaHotelResNotifRq(BaseModel): class CardNumber(BaseModel): model_config = ConfigDict(defer_build=True) - mask: Optional[str] = field( + mask: str | None = field( default=None, metadata={ "name": "Mask", @@ -10575,7 +10562,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9a-zA-Z]{1,32}", }, ) - token: Optional[str] = field( + token: str | None = field( default=None, metadata={ "name": "Token", @@ -10583,7 +10570,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9a-zA-Z]{1,32}", }, ) - token_provider_id: Optional[str] = field( + token_provider_id: str | None = field( default=None, metadata={ "name": "TokenProviderID", @@ -10594,7 +10581,7 @@ class OtaHotelResNotifRq(BaseModel): class BankAcct(BaseModel): model_config = ConfigDict(defer_build=True) - bank_acct_name: Optional[str] = field( + bank_acct_name: str | None = field( default=None, metadata={ "name": "BankAcctName", @@ -10621,7 +10608,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9]{1,19}", } ) - mask: Optional[str] = field( + mask: str | None = field( default=None, metadata={ "name": "Mask", @@ -10661,7 +10648,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", } ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -10725,7 +10712,7 @@ class OtaHotelResNotifRq(BaseModel): "pattern": r"[0-9]+", } ) - res_id_value: Optional[str] = field( + res_id_value: str | None = field( default=None, metadata={ "name": "ResID_Value", @@ -10734,7 +10721,7 @@ class OtaHotelResNotifRq(BaseModel): "max_length": 64, }, ) - res_id_source: Optional[str] = field( + res_id_source: str | None = field( default=None, metadata={ "name": "ResID_Source", @@ -10743,7 +10730,7 @@ class OtaHotelResNotifRq(BaseModel): "max_length": 64, }, ) - res_id_source_context: Optional[str] = field( + res_id_source_context: str | None = field( default=None, metadata={ "name": "ResID_SourceContext", @@ -10817,7 +10804,7 @@ class OtaHotelResNotifRq(BaseModel): "type": "Element", }, ) - email: Optional[str] = field( + email: str | None = field( default=None, metadata={ "name": "Email", @@ -10902,7 +10889,7 @@ class OtaHotelResNotifRq(BaseModel): class TelephoneInfo(BaseModel): model_config = ConfigDict(defer_build=True) - phone_tech_type: Optional[str] = field( + phone_tech_type: str | None = field( default=None, metadata={ "name": "PhoneTechType", @@ -10930,7 +10917,7 @@ class OtaHotelResNotifRq(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", @@ -10954,7 +10941,7 @@ class OtaHotelResNotifRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -10982,7 +10969,7 @@ class OtaHotelResNotifRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -11010,7 +10997,7 @@ class OtaHotelResNotifRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -11018,7 +11005,7 @@ class OtaHotelResNotifRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -11072,7 +11059,7 @@ class OtaHotelResNotifRs(BaseModel): "max_length": 64, } ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -11137,7 +11124,7 @@ class OtaNotifReportRq(BaseModel): namespace = "http://www.opentravel.org/OTA/2003/05" model_config = ConfigDict(defer_build=True) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -11165,7 +11152,7 @@ class OtaNotifReportRq(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -11211,7 +11198,7 @@ class OtaNotifReportRq(BaseModel): "max_length": 64, } ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -11303,7 +11290,7 @@ class OtaNotifReportRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -11324,7 +11311,7 @@ class OtaNotifReportRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -11352,7 +11339,7 @@ class OtaNotifReportRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -11360,7 +11347,7 @@ class OtaNotifReportRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -11397,7 +11384,7 @@ class OtaNotifReportRs(BaseModel): "pattern": r"[0-9]+", } ) - record_id: Optional[str] = field( + record_id: str | None = field( default=None, metadata={ "name": "RecordID", @@ -11406,7 +11393,7 @@ class OtaNotifReportRs(BaseModel): "max_length": 64, }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -11436,7 +11423,7 @@ class OtaPingRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -11450,7 +11437,7 @@ class OtaPingRs(BaseModel): "type": "Element", }, ) - echo_data: Optional[str] = field( + echo_data: str | None = field( default=None, metadata={ "name": "EchoData", @@ -11465,7 +11452,7 @@ class OtaPingRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -11493,7 +11480,7 @@ class OtaPingRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -11501,7 +11488,7 @@ class OtaPingRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[ErrorStatus] = field( + status: ErrorStatus | None = field( default=None, metadata={ "name": "Status", @@ -11538,7 +11525,7 @@ class OtaPingRs(BaseModel): "pattern": r"[0-9]+", } ) - status: Optional[WarningStatus] = field( + status: WarningStatus | None = field( default=None, metadata={ "name": "Status", @@ -11568,7 +11555,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - success: Optional[object] = field( + success: object | None = field( default=None, metadata={ "name": "Success", @@ -11596,7 +11583,7 @@ class OtaResRetrieveRs(BaseModel): "required": True, } ) - time_stamp: Optional[object] = field( + time_stamp: object | None = field( default=None, metadata={ "name": "TimeStamp", @@ -11624,7 +11611,7 @@ class OtaResRetrieveRs(BaseModel): "required": True, } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -11632,7 +11619,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]+", }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -11669,7 +11656,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]+", } ) - code: Optional[str] = field( + code: str | None = field( default=None, metadata={ "name": "Code", @@ -11677,7 +11664,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]+", }, ) - record_id: Optional[str] = field( + record_id: str | None = field( default=None, metadata={ "name": "RecordID", @@ -11686,7 +11673,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 64, }, ) - status: Optional[DefSendComplete] = field( + status: DefSendComplete | None = field( default=None, metadata={ "name": "Status", @@ -11769,7 +11756,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+", } ) - last_modify_date_time: Optional[str] = field( + last_modify_date_time: str | None = field( default=None, metadata={ "name": "LastModifyDateTime", @@ -11784,7 +11771,7 @@ class OtaResRetrieveRs(BaseModel): "required": True, } ) - room_stay_reservation: Optional[str] = field( + room_stay_reservation: str | None = field( default=None, metadata={ "name": "RoomStayReservation", @@ -11897,7 +11884,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - room_stay_group_id: Optional[str] = field( + room_stay_group_id: str | None = field( default=None, metadata={ "name": "RoomStayGroupID", @@ -11919,7 +11906,7 @@ class OtaResRetrieveRs(BaseModel): class RoomType(BaseModel): model_config = ConfigDict(defer_build=True) - room_type_code: Optional[str] = field( + room_type_code: str | None = field( default=None, metadata={ "name": "RoomTypeCode", @@ -11928,7 +11915,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 8, }, ) - room_classification_code: Optional[str] = field( + room_classification_code: str | None = field( default=None, metadata={ "name": "RoomClassificationCode", @@ -11936,7 +11923,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]+", }, ) - room_type: Optional[RoomTypeRoomType] = field( + room_type: RoomTypeRoomType | None = field( default=None, metadata={ "name": "RoomType", @@ -11974,7 +11961,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - rate_plan_code: Optional[str] = field( + rate_plan_code: str | None = field( default=None, metadata={ "name": "RatePlanCode", @@ -11995,7 +11982,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - percent: Optional[str] = field( + percent: str | None = field( default=None, metadata={ "name": "Percent", @@ -12006,7 +11993,7 @@ class OtaResRetrieveRs(BaseModel): class CommissionPayableAmount(BaseModel): model_config = ConfigDict(defer_build=True) - amount: Optional[str] = field( + amount: str | None = field( default=None, metadata={ "name": "Amount", @@ -12015,7 +12002,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -12115,7 +12102,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+", } ) - expire_date: Optional[str] = field( + expire_date: str | None = field( default=None, metadata={ "name": "ExpireDate", @@ -12123,7 +12110,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+", }, ) - expire_date_exclusive_ind: Optional[str] = field( + expire_date_exclusive_ind: str | None = field( default=None, metadata={ "name": "ExpireDateExclusiveInd", @@ -12131,14 +12118,14 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+", }, ) - rate_time_unit: Optional[RateRateTimeUnit] = field( + rate_time_unit: RateRateTimeUnit | None = field( default=None, metadata={ "name": "RateTimeUnit", "type": "Attribute", }, ) - unit_multiplier: Optional[str] = field( + unit_multiplier: str | None = field( default=None, metadata={ "name": "UnitMultiplier", @@ -12149,7 +12136,7 @@ class OtaResRetrieveRs(BaseModel): class Base(BaseModel): model_config = ConfigDict(defer_build=True) - amount_after_tax: Optional[str] = field( + amount_after_tax: str | None = field( default=None, metadata={ "name": "AmountAfterTax", @@ -12158,7 +12145,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", }, ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -12191,7 +12178,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]+", } ) - age: Optional[str] = field( + age: str | None = field( default=None, metadata={ "name": "Age", @@ -12211,7 +12198,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - start: Optional[str] = field( + start: str | None = field( default=None, metadata={ "name": "Start", @@ -12219,7 +12206,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+", }, ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -12227,7 +12214,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+", }, ) - duration: Optional[str] = field( + duration: str | None = field( default=None, metadata={ "name": "Duration", @@ -12322,7 +12309,7 @@ class OtaResRetrieveRs(BaseModel): class CardNumber(BaseModel): model_config = ConfigDict(defer_build=True) - plain_text: Optional[str] = field( + plain_text: str | None = field( default=None, metadata={ "name": "PlainText", @@ -12330,7 +12317,7 @@ class OtaResRetrieveRs(BaseModel): "min_length": 1, }, ) - encrypted_value: Optional[str] = field( + encrypted_value: str | None = field( default=None, metadata={ "name": "EncryptedValue", @@ -12338,7 +12325,7 @@ class OtaResRetrieveRs(BaseModel): "min_length": 1, }, ) - encryption_method: Optional[str] = field( + encryption_method: str | None = field( default=None, metadata={ "name": "EncryptionMethod", @@ -12446,7 +12433,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 16, } ) - service_rph: Optional[str] = field( + service_rph: str | None = field( default=None, metadata={ "name": "ServiceRPH", @@ -12539,7 +12526,7 @@ class OtaResRetrieveRs(BaseModel): class GuestCount(BaseModel): model_config = ConfigDict(defer_build=True) - age: Optional[str] = field( + age: str | None = field( default=None, metadata={ "name": "Age", @@ -12566,7 +12553,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+", } ) - end: Optional[str] = field( + end: str | None = field( default=None, metadata={ "name": "End", @@ -12746,7 +12733,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - gender: Optional[str] = field( + gender: str | None = field( default=None, metadata={ "name": "Gender", @@ -12754,7 +12741,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"(Unknown|Male|Female)", }, ) - birth_date: Optional[str] = field( + birth_date: str | None = field( default=None, metadata={ "name": "BirthDate", @@ -12762,7 +12749,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+", }, ) - language: Optional[str] = field( + language: str | None = field( default=None, metadata={ "name": "Language", @@ -12773,7 +12760,7 @@ class OtaResRetrieveRs(BaseModel): class PersonName(BaseModel): model_config = ConfigDict(defer_build=True) - name_prefix: Optional[str] = field( + name_prefix: str | None = field( default=None, metadata={ "name": "NamePrefix", @@ -12800,7 +12787,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 64, } ) - name_title: Optional[str] = field( + name_title: str | None = field( default=None, metadata={ "name": "NameTitle", @@ -12812,7 +12799,7 @@ class OtaResRetrieveRs(BaseModel): class Telephone(BaseModel): model_config = ConfigDict(defer_build=True) - phone_tech_type: Optional[str] = field( + phone_tech_type: str | None = field( default=None, metadata={ "name": "PhoneTechType", @@ -12838,7 +12825,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"\S+@\S+", }, ) - remark: Optional[str] = field( + remark: str | None = field( default=None, metadata={ "name": "Remark", @@ -12849,7 +12836,7 @@ class OtaResRetrieveRs(BaseModel): class Address(BaseModel): model_config = ConfigDict(defer_build=True) - address_line: Optional[str] = field( + address_line: str | None = field( default=None, metadata={ "name": "AddressLine", @@ -12858,7 +12845,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 255, }, ) - city_name: Optional[str] = field( + city_name: str | None = field( default=None, metadata={ "name": "CityName", @@ -12867,7 +12854,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 64, }, ) - postal_code: Optional[str] = field( + postal_code: str | None = field( default=None, metadata={ "name": "PostalCode", @@ -12885,7 +12872,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - remark: Optional[str] = field( + remark: str | None = field( default=None, metadata={ "name": "Remark", @@ -12994,7 +12981,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - text: Optional[str] = field( + text: str | None = field( default=None, metadata={ "name": "Text", @@ -13124,14 +13111,14 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - start: Optional[object] = field( + start: object | None = field( default=None, metadata={ "name": "Start", "type": "Attribute", }, ) - guarantee_code: Optional[object] = field( + guarantee_code: object | None = field( default=None, metadata={ "name": "GuaranteeCode", @@ -13178,21 +13165,21 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - guarantee_type_code: Optional[object] = field( + guarantee_type_code: object | None = field( default=None, metadata={ "name": "GuaranteeTypeCode", "type": "Attribute", }, ) - guarantee_id: Optional[object] = field( + guarantee_id: object | None = field( default=None, metadata={ "name": "GuaranteeID", "type": "Attribute", }, ) - payment_transaction_type_code: Optional[object] = field( + payment_transaction_type_code: object | None = field( default=None, metadata={ "name": "PaymentTransactionTypeCode", @@ -13202,7 +13189,7 @@ class OtaResRetrieveRs(BaseModel): class PaymentCard(BaseModel): model_config = ConfigDict(defer_build=True) - card_holder_name: Optional[str] = field( + card_holder_name: str | None = field( default=None, metadata={ "name": "CardHolderName", @@ -13220,7 +13207,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - card_code: Optional[str] = field( + card_code: str | None = field( default=None, metadata={ "name": "CardCode", @@ -13228,7 +13215,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[A-Z]{1,2}", }, ) - expire_date: Optional[str] = field( + expire_date: str | None = field( default=None, metadata={ "name": "ExpireDate", @@ -13239,7 +13226,7 @@ class OtaResRetrieveRs(BaseModel): class CardNumber(BaseModel): model_config = ConfigDict(defer_build=True) - mask: Optional[str] = field( + mask: str | None = field( default=None, metadata={ "name": "Mask", @@ -13247,7 +13234,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9a-zA-Z]{1,32}", }, ) - token: Optional[str] = field( + token: str | None = field( default=None, metadata={ "name": "Token", @@ -13255,7 +13242,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9a-zA-Z]{1,32}", }, ) - token_provider_id: Optional[str] = field( + token_provider_id: str | None = field( default=None, metadata={ "name": "TokenProviderID", @@ -13266,7 +13253,7 @@ class OtaResRetrieveRs(BaseModel): class BankAcct(BaseModel): model_config = ConfigDict(defer_build=True) - bank_acct_name: Optional[str] = field( + bank_acct_name: str | None = field( default=None, metadata={ "name": "BankAcctName", @@ -13293,7 +13280,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]{1,19}", } ) - mask: Optional[str] = field( + mask: str | None = field( default=None, metadata={ "name": "Mask", @@ -13333,7 +13320,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]*\.?[0-9]*", } ) - currency_code: Optional[str] = field( + currency_code: str | None = field( default=None, metadata={ "name": "CurrencyCode", @@ -13397,7 +13384,7 @@ class OtaResRetrieveRs(BaseModel): "pattern": r"[0-9]+", } ) - res_id_value: Optional[str] = field( + res_id_value: str | None = field( default=None, metadata={ "name": "ResID_Value", @@ -13406,7 +13393,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 64, }, ) - res_id_source: Optional[str] = field( + res_id_source: str | None = field( default=None, metadata={ "name": "ResID_Source", @@ -13415,7 +13402,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 64, }, ) - res_id_source_context: Optional[str] = field( + res_id_source_context: str | None = field( default=None, metadata={ "name": "ResID_SourceContext", @@ -13489,7 +13476,7 @@ class OtaResRetrieveRs(BaseModel): "type": "Element", }, ) - email: Optional[str] = field( + email: str | None = field( default=None, metadata={ "name": "Email", @@ -13574,7 +13561,7 @@ class OtaResRetrieveRs(BaseModel): class TelephoneInfo(BaseModel): model_config = ConfigDict(defer_build=True) - phone_tech_type: Optional[str] = field( + phone_tech_type: str | None = field( default=None, metadata={ "name": "PhoneTechType", @@ -13602,7 +13589,7 @@ class OtaResRetrieveRs(BaseModel): "max_length": 16, } ) - hotel_name: Optional[str] = field( + hotel_name: str | None = field( default=None, metadata={ "name": "HotelName", diff --git a/src/alpine_bits_python/main.py b/src/alpine_bits_python/main.py index 3112d20..38a303c 100644 --- a/src/alpine_bits_python/main.py +++ b/src/alpine_bits_python/main.py @@ -1,52 +1,36 @@ +import asyncio +import json import logging -from .alpinebits_guestrequests import ResGuest, RoomStay -from .generated import alpinebits as ab -from io import BytesIO -import sys -from datetime import datetime, timezone, date -import re -from xsdata_pydantic.bindings import XmlSerializer -from .alpine_bits_helpers import ( - CustomerData, - GuestCountsFactory, - HotelReservationIdData, - AlpineBitsFactory, - OtaMessageType, - CommentData, - CommentsData, - CommentListItemData, -) -from .generated import alpinebits as ab -from datetime import datetime, timezone +import os +from datetime import UTC, date, datetime + +from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from .alpine_bits_helpers import ( + AlpineBitsFactory, CommentData, - CommentsData, CommentListItemData, + CommentsData, CustomerData, GuestCountsFactory, HotelReservationIdData, - PhoneTechType, - AlpineBitsFactory, OtaMessageType, + PhoneTechType, ) +from .config_loader import load_config # DB and config from .db import ( Base, - Customer as DBCustomer, - Reservation as DBReservation, - HashedCustomer, get_database_url, ) -from .config_loader import load_config -import hashlib -import json -import os -from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker -import asyncio - -from alpine_bits_python import db +from .db import ( + Customer as DBCustomer, +) +from .db import ( + Reservation as DBReservation, +) +from .generated import alpinebits as ab # Configure logging logging.basicConfig(level=logging.INFO) @@ -101,7 +85,7 @@ async def main(): os.path.dirname(__file__), "../../test_data/wix_test_data_20250928_132611.json", ) - with open(json_path, "r", encoding="utf-8") as f: + with open(json_path, encoding="utf-8") as f: wix_data = json.load(f) data = wix_data["data"]["data"] @@ -197,7 +181,7 @@ async def main(): children_ages=",".join(str(a) for a in children_ages), offer=offer, utm_comment=utm_comment, - created_at=datetime.now(timezone.utc), + created_at=datetime.now(UTC), utm_source=data.get("field:utm_source"), utm_medium=data.get("field:utm_medium"), utm_campaign=data.get("field:utm_campaign"), @@ -330,7 +314,7 @@ def create_xml_from_db(customer: DBCustomer, reservation: DBReservation): ) hotel_reservation = ab.OtaResRetrieveRs.ReservationsList.HotelReservation( - create_date_time=datetime.now(timezone.utc).isoformat(), + create_date_time=datetime.now(UTC).isoformat(), res_status=ab.HotelReservationResStatus.REQUESTED, room_stay_reservation="true", unique_id=unique_id, @@ -361,13 +345,13 @@ def create_xml_from_db(customer: DBCustomer, reservation: DBReservation): with open("output.xml", "w", encoding="utf-8") as outfile: outfile.write(xml_string) print("āœ… XML serialization successful!") - print(f"Generated XML written to output.xml") + print("Generated XML written to output.xml") print("\nšŸ“„ Generated XML:") print(xml_string) from xsdata_pydantic.bindings import XmlParser parser = XmlParser() - with open("output.xml", "r", encoding="utf-8") as infile: + with open("output.xml", encoding="utf-8") as infile: xml_content = infile.read() parsed_result = parser.from_string(xml_content, ab.OtaResRetrieveRs) print("āœ… Round-trip validation successful!") diff --git a/src/alpine_bits_python/models.py b/src/alpine_bits_python/models.py index 27b6f31..359fcc0 100644 --- a/src/alpine_bits_python/models.py +++ b/src/alpine_bits_python/models.py @@ -1,6 +1,6 @@ -from typing import Dict, List, Any, Optional +from typing import Any + from pydantic import BaseModel, Field -from datetime import datetime class AlpineBitsHandshakeRequest(BaseModel): @@ -9,64 +9,64 @@ class AlpineBitsHandshakeRequest(BaseModel): action: str = Field( ..., description="Action parameter, typically 'OTA_Ping:Handshaking'" ) - request_xml: Optional[str] = Field(None, description="XML request document") + request_xml: str | None = Field(None, description="XML request document") class ContactName(BaseModel): """Contact name structure""" - first: Optional[str] = None - last: Optional[str] = None + first: str | None = None + last: str | None = None class ContactAddress(BaseModel): """Contact address structure""" - street: Optional[str] = None - city: Optional[str] = None - state: Optional[str] = None - country: Optional[str] = None - postalCode: Optional[str] = None + street: str | None = None + city: str | None = None + state: str | None = None + country: str | None = None + postalCode: str | None = None class Contact(BaseModel): """Contact information from Wix form""" - name: Optional[ContactName] = None - email: Optional[str] = None - locale: Optional[str] = None - company: Optional[str] = None - birthdate: Optional[str] = None - labelKeys: Optional[Dict[str, Any]] = None - contactId: Optional[str] = None - address: Optional[ContactAddress] = None - jobTitle: Optional[str] = None - imageUrl: Optional[str] = None - updatedDate: Optional[str] = None - phone: Optional[str] = None - createdDate: Optional[str] = None + name: ContactName | None = None + email: str | None = None + locale: str | None = None + company: str | None = None + birthdate: str | None = None + labelKeys: dict[str, Any] | None = None + contactId: str | None = None + address: ContactAddress | None = None + jobTitle: str | None = None + imageUrl: str | None = None + updatedDate: str | None = None + phone: str | None = None + createdDate: str | None = None class SubmissionPdf(BaseModel): """PDF submission structure""" - url: Optional[str] = None - filename: Optional[str] = None + url: str | None = None + filename: str | None = None class WixFormSubmission(BaseModel): """Model for Wix form submission data""" formName: str - submissions: List[Dict[str, Any]] = Field(default_factory=list) + submissions: list[dict[str, Any]] = Field(default_factory=list) submissionTime: str - formFieldMask: List[str] = Field(default_factory=list) + formFieldMask: list[str] = Field(default_factory=list) submissionId: str contactId: str submissionsLink: str - submissionPdf: Optional[SubmissionPdf] = None + submissionPdf: SubmissionPdf | None = None formId: str - contact: Optional[Contact] = None + contact: Contact | None = None # Dynamic form fields - these will capture all field:* entries class Config: diff --git a/src/alpine_bits_python/rate_limit.py b/src/alpine_bits_python/rate_limit.py index 638ea59..97d4411 100644 --- a/src/alpine_bits_python/rate_limit.py +++ b/src/alpine_bits_python/rate_limit.py @@ -1,10 +1,11 @@ -from slowapi import Limiter, _rate_limit_exceeded_handler -from slowapi.util import get_remote_address -from slowapi.errors import RateLimitExceeded -from fastapi import Request -import redis -import os import logging +import os + +import redis +from fastapi import Request +from slowapi import Limiter, _rate_limit_exceeded_handler +from slowapi.errors import RateLimitExceeded +from slowapi.util import get_remote_address logger = logging.getLogger(__name__) @@ -18,8 +19,7 @@ REDIS_URL = os.getenv("REDIS_URL", None) def get_remote_address_with_forwarded(request: Request): - """ - Get client IP address, considering forwarded headers from proxies/load balancers + """Get client IP address, considering forwarded headers from proxies/load balancers """ # Check for forwarded headers (common in production behind proxies) forwarded_for = request.headers.get("X-Forwarded-For") @@ -58,8 +58,7 @@ else: def get_api_key_identifier(request: Request) -> str: - """ - Get identifier for rate limiting based on API key if available, otherwise IP + """Get identifier for rate limiting based on API key if available, otherwise IP This allows different rate limits per API key """ # Try to get API key from Authorization header diff --git a/src/alpine_bits_python/run_api.py b/src/alpine_bits_python/run_api.py index 28d921b..93b202f 100644 --- a/src/alpine_bits_python/run_api.py +++ b/src/alpine_bits_python/run_api.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 -""" -Startup script for the Wix Form Handler API +"""Startup script for the Wix Form Handler API """ import os + import uvicorn -from .api import app if __name__ == "__main__": db_path = "alpinebits.db" # Adjust path if needed diff --git a/src/alpine_bits_python/scripts/setup_security.py b/src/alpine_bits_python/scripts/setup_security.py index e565a85..20028bd 100644 --- a/src/alpine_bits_python/scripts/setup_security.py +++ b/src/alpine_bits_python/scripts/setup_security.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 -""" -Configuration and setup script for the Wix Form Handler API +"""Configuration and setup script for the Wix Form Handler API """ import os -import sys import secrets +import sys # Add parent directory to path to import from src sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -15,7 +14,6 @@ from alpine_bits_python.auth import generate_api_key def generate_secure_keys(): """Generate secure API keys for the application""" - print("šŸ” Generating Secure API Keys") print("=" * 50) @@ -33,7 +31,7 @@ def generate_secure_keys(): print(f"export WIX_API_KEY='{wix_api_key}'") print(f"export ADMIN_API_KEY='{admin_api_key}'") print(f"export WIX_WEBHOOK_SECRET='{webhook_secret}'") - print(f"export REDIS_URL='redis://localhost:6379' # Optional for production") + print("export REDIS_URL='redis://localhost:6379' # Optional for production") print("\nšŸ”§ .env File Content") print("-" * 20) @@ -73,7 +71,6 @@ def generate_secure_keys(): def check_security_setup(): """Check current security configuration""" - print("šŸ” Security Configuration Check") print("=" * 40) @@ -93,11 +90,10 @@ def check_security_setup(): print("\nšŸ›”ļø Security Recommendations:") if not wix_key: print(" āŒ Set WIX_API_KEY environment variable") + elif len(wix_key) < 32: + print(" āš ļø WIX_API_KEY should be longer for better security") else: - if len(wix_key) < 32: - print(" āš ļø WIX_API_KEY should be longer for better security") - else: - print(" āœ… WIX_API_KEY looks secure") + print(" āœ… WIX_API_KEY looks secure") if not admin_key: print(" āŒ Set ADMIN_API_KEY environment variable") diff --git a/src/alpine_bits_python/scripts/test_api.py b/src/alpine_bits_python/scripts/test_api.py index 021f621..407ce4c 100644 --- a/src/alpine_bits_python/scripts/test_api.py +++ b/src/alpine_bits_python/scripts/test_api.py @@ -1,15 +1,14 @@ #!/usr/bin/env python3 -""" -Test script for the Secure Wix Form Handler API +"""Test script for the Secure Wix Form Handler API """ import asyncio -import aiohttp -import json import os import sys from datetime import datetime +import aiohttp + # Add parent directory to path to import from src sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) @@ -67,7 +66,6 @@ SAMPLE_WIX_DATA = { async def test_api(): """Test the API endpoints with authentication""" - headers_with_auth = { "Content-Type": "application/json", "Authorization": f"Bearer {TEST_API_KEY}", diff --git a/src/alpine_bits_python/util/handshake_util.py b/src/alpine_bits_python/util/handshake_util.py index 74409dd..121954b 100644 --- a/src/alpine_bits_python/util/handshake_util.py +++ b/src/alpine_bits_python/util/handshake_util.py @@ -1,6 +1,7 @@ -from ..generated.alpinebits import OtaPingRq, OtaPingRs from xsdata_pydantic.bindings import XmlParser +from ..generated.alpinebits import OtaPingRs + def main(): # test parsing a ping request sample @@ -9,7 +10,7 @@ def main(): "AlpineBits-HotelData-2024-10/files/samples/Handshake/Handshake-OTA_PingRS.xml" ) - with open(path, "r", encoding="utf-8") as f: + with open(path, encoding="utf-8") as f: xml = f.read() # Parse the XML into the request object diff --git a/start_api.py b/start_api.py index fb51cd2..608740a 100644 --- a/start_api.py +++ b/start_api.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -""" -Convenience launcher for the Wix Form Handler API +"""Convenience launcher for the Wix Form Handler API """ import os @@ -11,4 +10,4 @@ src_dir = os.path.join(os.path.dirname(__file__), "src/alpine_bits_python") # Run the API using uv if __name__ == "__main__": - subprocess.run(["uv", "run", "python", os.path.join(src_dir, "run_api.py")]) + subprocess.run(["uv", "run", "python", os.path.join(src_dir, "run_api.py")], check=False) diff --git a/test/test_alpine_bits_helper.py b/test/test_alpine_bits_helper.py index cc988a2..a3f4e0c 100644 --- a/test/test_alpine_bits_helper.py +++ b/test/test_alpine_bits_helper.py @@ -1,24 +1,21 @@ -import pytest -from typing import Union -import sys -import os +import pytest from alpine_bits_python.alpine_bits_helpers import ( + AlpineBitsFactory, CustomerData, CustomerFactory, - ResGuestFactory, HotelReservationIdData, HotelReservationIdFactory, - AlpineBitsFactory, - PhoneTechType, - OtaMessageType, NotifCustomer, - RetrieveCustomer, - NotifResGuests, - RetrieveResGuests, NotifHotelReservationId, + NotifResGuests, + OtaMessageType, + PhoneTechType, + ResGuestFactory, + RetrieveCustomer, RetrieveHotelReservationId, + RetrieveResGuests, ) diff --git a/test/test_alpine_bits_server_read.py b/test/test_alpine_bits_server_read.py index 4c37c79..139597f 100644 --- a/test/test_alpine_bits_server_read.py +++ b/test/test_alpine_bits_server_read.py @@ -1,7 +1,2 @@ -import pytest -from alpine_bits_python.alpinebits_server import AlpineBitsServer, AlpineBitsClientInfo -from xsdata_pydantic.bindings import XmlParser -from alpine_bits_python.generated.alpinebits import OtaResRetrieveRs, OtaHotelResNotifRq -pass diff --git a/test/test_alpinebits_server_ping.py b/test/test_alpinebits_server_ping.py index 9cb3b77..7c5c101 100644 --- a/test/test_alpinebits_server_ping.py +++ b/test/test_alpinebits_server_ping.py @@ -1,9 +1,9 @@ import json + import pytest -import asyncio -from alpine_bits_python.alpinebits_server import AlpineBitsServer, AlpineBitsClientInfo -import re from xsdata_pydantic.bindings import XmlParser + +from alpine_bits_python.alpinebits_server import AlpineBitsClientInfo, AlpineBitsServer from alpine_bits_python.generated.alpinebits import OtaPingRs @@ -17,14 +17,14 @@ def extract_relevant_sections(xml_string): @pytest.mark.asyncio async def test_ping_action_response_matches_expected(): - with open("test/test_data/Handshake-OTA_PingRQ.xml", "r", encoding="utf-8") as f: + with open("test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8") as f: server = AlpineBitsServer() with open( - "test/test_data/Handshake-OTA_PingRQ.xml", "r", encoding="utf-8" + "test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8" ) as f: request_xml = f.read() with open( - "test/test_data/Handshake-OTA_PingRS.xml", "r", encoding="utf-8" + "test/test_data/Handshake-OTA_PingRS.xml", encoding="utf-8" ) as f: expected_xml = f.read() client_info = AlpineBitsClientInfo(username="irrelevant", password="irrelevant") @@ -56,7 +56,7 @@ async def test_ping_action_response_matches_expected(): @pytest.mark.asyncio async def test_ping_action_response_success(): server = AlpineBitsServer() - with open("test/test_data/Handshake-OTA_PingRQ.xml", "r", encoding="utf-8") as f: + with open("test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8") as f: request_xml = f.read() client_info = AlpineBitsClientInfo(username="irrelevant", password="irrelevant") response = await server.handle_request( @@ -74,7 +74,7 @@ async def test_ping_action_response_success(): @pytest.mark.asyncio async def test_ping_action_response_version_arbitrary(): server = AlpineBitsServer() - with open("test/test_data/Handshake-OTA_PingRQ.xml", "r", encoding="utf-8") as f: + with open("test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8") as f: request_xml = f.read() client_info = AlpineBitsClientInfo(username="irrelevant", password="irrelevant") response = await server.handle_request( @@ -91,7 +91,7 @@ async def test_ping_action_response_version_arbitrary(): @pytest.mark.asyncio async def test_ping_action_response_invalid_action(): server = AlpineBitsServer() - with open("test/test_data/Handshake-OTA_PingRQ.xml", "r", encoding="utf-8") as f: + with open("test/test_data/Handshake-OTA_PingRQ.xml", encoding="utf-8") as f: request_xml = f.read() client_info = AlpineBitsClientInfo(username="irrelevant", password="irrelevant") response = await server.handle_request( diff --git a/test_handshake.py b/test_handshake.py index 00c87b0..75b33f5 100644 --- a/test_handshake.py +++ b/test_handshake.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 -""" -Test the handshake functionality with the real AlpineBits sample file. +"""Test the handshake functionality with the real AlpineBits sample file. """ import asyncio + from alpine_bits_python.alpinebits_server import AlpineBitsServer @@ -17,7 +17,6 @@ async def main(): # Read the sample handshake request with open( "AlpineBits-HotelData-2024-10/files/samples/Handshake/Handshake-OTA_PingRQ.xml", - "r", ) as f: ping_request_xml = f.read()