179 lines
6.4 KiB
Python
179 lines
6.4 KiB
Python
import xml.etree.ElementTree as ET
|
|
from datetime import datetime, timezone
|
|
from typing import List, Optional
|
|
|
|
|
|
# TimeSpan class according to XSD: <TimeSpan Start="..." End="..." Duration="..." StartWindow="..." EndWindow="..."/>
|
|
class TimeSpan:
|
|
def __init__(
|
|
self,
|
|
start: str,
|
|
end: str = None,
|
|
duration: str = None,
|
|
start_window: str = None,
|
|
end_window: str = None,
|
|
):
|
|
self.start = start
|
|
self.end = end
|
|
self.duration = duration
|
|
self.start_window = start_window
|
|
self.end_window = end_window
|
|
|
|
def to_xml(self):
|
|
attrib = {"Start": self.start}
|
|
if self.end:
|
|
attrib["End"] = self.end
|
|
if self.duration:
|
|
attrib["Duration"] = self.duration
|
|
if self.start_window:
|
|
attrib["StartWindow"] = self.start_window
|
|
if self.end_window:
|
|
attrib["EndWindow"] = self.end_window
|
|
return ET.Element(_ns("TimeSpan"), attrib)
|
|
|
|
|
|
NAMESPACE = "http://www.opentravel.org/OTA/2003/05"
|
|
ET.register_namespace("", NAMESPACE)
|
|
|
|
|
|
def _ns(tag):
|
|
return f"{{{NAMESPACE}}}{tag}"
|
|
|
|
|
|
class ResGuest:
|
|
def __init__(
|
|
self,
|
|
given_name: str,
|
|
surname: str,
|
|
gender: Optional[str] = None,
|
|
birth_date: Optional[str] = None,
|
|
language: Optional[str] = None,
|
|
name_prefix: Optional[str] = None,
|
|
name_title: Optional[str] = None,
|
|
email: Optional[str] = None,
|
|
address: Optional[dict] = None,
|
|
telephones: Optional[list] = None,
|
|
):
|
|
self.given_name = given_name
|
|
self.surname = surname
|
|
self.gender = gender
|
|
self.birth_date = birth_date
|
|
self.language = language
|
|
self.name_prefix = name_prefix
|
|
self.name_title = name_title
|
|
self.email = email
|
|
self.address = address or {}
|
|
self.telephones = telephones or []
|
|
|
|
def to_xml(self):
|
|
resguest_elem = ET.Element(_ns("ResGuest"))
|
|
profiles_elem = ET.SubElement(resguest_elem, _ns("Profiles"))
|
|
profileinfo_elem = ET.SubElement(profiles_elem, _ns("ProfileInfo"))
|
|
profile_elem = ET.SubElement(profileinfo_elem, _ns("Profile"))
|
|
customer_elem = ET.SubElement(profile_elem, _ns("Customer"))
|
|
if self.gender:
|
|
customer_elem.set("Gender", self.gender)
|
|
if self.birth_date:
|
|
customer_elem.set("BirthDate", self.birth_date)
|
|
if self.language:
|
|
customer_elem.set("Language", self.language)
|
|
personname_elem = ET.SubElement(customer_elem, _ns("PersonName"))
|
|
if self.name_prefix:
|
|
ET.SubElement(personname_elem, _ns("NamePrefix")).text = self.name_prefix
|
|
ET.SubElement(personname_elem, _ns("GivenName")).text = self.given_name
|
|
ET.SubElement(personname_elem, _ns("Surname")).text = self.surname
|
|
if self.name_title:
|
|
ET.SubElement(personname_elem, _ns("NameTitle")).text = self.name_title
|
|
for tel in self.telephones:
|
|
tel_elem = ET.SubElement(customer_elem, _ns("Telephone"))
|
|
for k, v in tel.items():
|
|
tel_elem.set(k, v)
|
|
if self.email:
|
|
ET.SubElement(customer_elem, _ns("Email")).text = self.email
|
|
if self.address:
|
|
address_elem = ET.SubElement(customer_elem, _ns("Address"))
|
|
for k, v in self.address.items():
|
|
if k == "CountryName":
|
|
country_elem = ET.SubElement(address_elem, _ns("CountryName"))
|
|
if isinstance(v, dict):
|
|
for ck, cv in v.items():
|
|
country_elem.set(ck, cv)
|
|
else:
|
|
country_elem.text = v
|
|
else:
|
|
ET.SubElement(address_elem, _ns(k)).text = v
|
|
return resguest_elem
|
|
|
|
def __str__(self):
|
|
from lxml import etree
|
|
|
|
elem = self.to_xml()
|
|
xml_bytes = ET.tostring(elem, encoding="utf-8")
|
|
parser = etree.XMLParser(remove_blank_text=True)
|
|
lxml_elem = etree.fromstring(xml_bytes, parser)
|
|
return etree.tostring(lxml_elem, pretty_print=True, encoding="unicode")
|
|
|
|
|
|
class RoomStay:
|
|
def __init__(self, room_type: str, timespan: TimeSpan, guests: List[ResGuest]):
|
|
self.room_type = room_type
|
|
self.timespan = timespan
|
|
self.guests = guests
|
|
|
|
def to_xml(self):
|
|
roomstay_elem = ET.Element(_ns("RoomStay"))
|
|
ET.SubElement(roomstay_elem, _ns("RoomType")).set(
|
|
"RoomTypeCode", self.room_type
|
|
)
|
|
roomstay_elem.append(self.timespan.to_xml())
|
|
guests_elem = ET.SubElement(roomstay_elem, _ns("Guests"))
|
|
for guest in self.guests:
|
|
guests_elem.append(guest.to_xml())
|
|
return roomstay_elem
|
|
|
|
def __str__(self):
|
|
from lxml import etree
|
|
|
|
elem = self.to_xml()
|
|
xml_bytes = ET.tostring(elem, encoding="utf-8")
|
|
parser = etree.XMLParser(remove_blank_text=True)
|
|
lxml_elem = etree.fromstring(xml_bytes, parser)
|
|
return etree.tostring(lxml_elem, pretty_print=True, encoding="unicode")
|
|
|
|
|
|
class Reservation:
|
|
def __init__(
|
|
self,
|
|
reservation_id: str,
|
|
hotel_code: str,
|
|
roomstays: List[RoomStay],
|
|
create_time: Optional[str] = None,
|
|
):
|
|
self.reservation_id = reservation_id
|
|
self.hotel_code = hotel_code
|
|
self.roomstays = roomstays
|
|
self.create_time = create_time or datetime.now(timezone.utc).isoformat()
|
|
|
|
def to_xml(self):
|
|
res_elem = ET.Element(_ns("HotelReservation"))
|
|
uniqueid_elem = ET.SubElement(res_elem, _ns("UniqueID"))
|
|
uniqueid_elem.set("Type", "14")
|
|
uniqueid_elem.set("ID", self.reservation_id)
|
|
hotel_elem = ET.SubElement(res_elem, _ns("Hotel"))
|
|
hotel_elem.set("HotelCode", self.hotel_code)
|
|
roomstays_elem = ET.SubElement(res_elem, _ns("RoomStays"))
|
|
for rs in self.roomstays:
|
|
roomstays_elem.append(rs.to_xml())
|
|
res_elem.set("CreateDateTime", self.create_time)
|
|
return res_elem
|
|
|
|
def to_xml_string(self):
|
|
root = ET.Element(
|
|
_ns("OTA_ResRetrieveRS"),
|
|
{"Version": "2024-10", "TimeStamp": datetime.now(timezone.utc).isoformat()},
|
|
)
|
|
success_elem = ET.SubElement(root, _ns("Success"))
|
|
reservations_list = ET.SubElement(root, _ns("ReservationsList"))
|
|
reservations_list.append(self.to_xml())
|
|
return ET.tostring(root, encoding="utf-8", xml_declaration=True).decode("utf-8")
|