From d0217e154e346f4c35d271d81507ce032c615a55 Mon Sep 17 00:00:00 2001 From: Jonas Linter Date: Sat, 20 Sep 2025 15:20:12 +0200 Subject: [PATCH] Slowly recreating alpine bits schemas. Our usecase will need both server and client parts --- pyproject.toml | 4 +- src/alpinebits_guestrequests.py | 126 ++++++++++++++++++++++++++++++++ src/main.py | 10 +++ uv.lock | 58 +++++++++++++++ 4 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 src/alpinebits_guestrequests.py create mode 100644 src/main.py create mode 100644 uv.lock diff --git a/pyproject.toml b/pyproject.toml index cb783b3..e708483 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,4 +4,6 @@ version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.13" -dependencies = [] +dependencies = [ + "lxml>=6.0.1", +] diff --git a/src/alpinebits_guestrequests.py b/src/alpinebits_guestrequests.py new file mode 100644 index 0000000..0fcabde --- /dev/null +++ b/src/alpinebits_guestrequests.py @@ -0,0 +1,126 @@ +import xml.etree.ElementTree as ET +from datetime import datetime, timezone +from typing import List, Optional + +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, start_date: str, end_date: str, guests: List[ResGuest]): + self.room_type = room_type + self.start_date = start_date + self.end_date = end_date + self.guests = guests + + def to_xml(self): + roomstay_elem = ET.Element(_ns("RoomStay")) + ET.SubElement(roomstay_elem, _ns("RoomType")).set("RoomTypeCode", self.room_type) + ET.SubElement(roomstay_elem, _ns("TimeSpan"), { + "Start": self.start_date, + "End": self.end_date + }) + guests_elem = ET.SubElement(roomstay_elem, _ns("Guests")) + for guest in self.guests: + guests_elem.append(guest.to_xml()) + return roomstay_elem + + +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") diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..4b7e744 --- /dev/null +++ b/src/main.py @@ -0,0 +1,10 @@ +from alpinebits_guestrequests import ResGuest + + + +def main(): + guest1 = ResGuest(given_name="John", surname="Doe", email="john.doe@example.com") + print(guest1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..36f87a1 --- /dev/null +++ b/uv.lock @@ -0,0 +1,58 @@ +version = 1 +revision = 1 +requires-python = ">=3.13" + +[[package]] +name = "alpine-bits-python-server" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "lxml" }, +] + +[package.metadata] +requires-dist = [{ name = "lxml", specifier = ">=6.0.1" }] + +[[package]] +name = "lxml" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8f/bd/f9d01fd4132d81c6f43ab01983caea69ec9614b913c290a26738431a015d/lxml-6.0.1.tar.gz", hash = "sha256:2b3a882ebf27dd026df3801a87cf49ff791336e0f94b0fad195db77e01240690", size = 4070214 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/c4/cd757eeec4548e6652eff50b944079d18ce5f8182d2b2cf514e125e8fbcb/lxml-6.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:485eda5d81bb7358db96a83546949c5fe7474bec6c68ef3fa1fb61a584b00eea", size = 8405139 }, + { url = "https://files.pythonhosted.org/packages/ff/99/0290bb86a7403893f5e9658490c705fcea103b9191f2039752b071b4ef07/lxml-6.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d12160adea318ce3d118f0b4fbdff7d1225c75fb7749429541b4d217b85c3f76", size = 4585954 }, + { url = "https://files.pythonhosted.org/packages/88/a7/4bb54dd1e626342a0f7df6ec6ca44fdd5d0e100ace53acc00e9a689ead04/lxml-6.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:48c8d335d8ab72f9265e7ba598ae5105a8272437403f4032107dbcb96d3f0b29", size = 4944052 }, + { url = "https://files.pythonhosted.org/packages/71/8d/20f51cd07a7cbef6214675a8a5c62b2559a36d9303fe511645108887c458/lxml-6.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:405e7cf9dbdbb52722c231e0f1257214202dfa192327fab3de45fd62e0554082", size = 5098885 }, + { url = "https://files.pythonhosted.org/packages/5a/63/efceeee7245d45f97d548e48132258a36244d3c13c6e3ddbd04db95ff496/lxml-6.0.1-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:299a790d403335a6a057ade46f92612ebab87b223e4e8c5308059f2dc36f45ed", size = 5017542 }, + { url = "https://files.pythonhosted.org/packages/57/5d/92cb3d3499f5caba17f7933e6be3b6c7de767b715081863337ced42eb5f2/lxml-6.0.1-cp313-cp313-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:48da704672f6f9c461e9a73250440c647638cc6ff9567ead4c3b1f189a604ee8", size = 5347303 }, + { url = "https://files.pythonhosted.org/packages/69/f8/606fa16a05d7ef5e916c6481c634f40870db605caffed9d08b1a4fb6b989/lxml-6.0.1-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:21e364e1bb731489e3f4d51db416f991a5d5da5d88184728d80ecfb0904b1d68", size = 5641055 }, + { url = "https://files.pythonhosted.org/packages/b3/01/15d5fc74ebb49eac4e5df031fbc50713dcc081f4e0068ed963a510b7d457/lxml-6.0.1-cp313-cp313-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1bce45a2c32032afddbd84ed8ab092130649acb935536ef7a9559636ce7ffd4a", size = 5242719 }, + { url = "https://files.pythonhosted.org/packages/42/a5/1b85e2aaaf8deaa67e04c33bddb41f8e73d07a077bf9db677cec7128bfb4/lxml-6.0.1-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:fa164387ff20ab0e575fa909b11b92ff1481e6876835014e70280769920c4433", size = 4717310 }, + { url = "https://files.pythonhosted.org/packages/42/23/f3bb1292f55a725814317172eeb296615db3becac8f1a059b53c51fc1da8/lxml-6.0.1-cp313-cp313-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7587ac5e000e1594e62278422c5783b34a82b22f27688b1074d71376424b73e8", size = 5254024 }, + { url = "https://files.pythonhosted.org/packages/b4/be/4d768f581ccd0386d424bac615d9002d805df7cc8482ae07d529f60a3c1e/lxml-6.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:57478424ac4c9170eabf540237125e8d30fad1940648924c058e7bc9fb9cf6dd", size = 5055335 }, + { url = "https://files.pythonhosted.org/packages/40/07/ed61d1a3e77d1a9f856c4fab15ee5c09a2853fb7af13b866bb469a3a6d42/lxml-6.0.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:09c74afc7786c10dd6afaa0be2e4805866beadc18f1d843cf517a7851151b499", size = 4784864 }, + { url = "https://files.pythonhosted.org/packages/01/37/77e7971212e5c38a55431744f79dff27fd751771775165caea096d055ca4/lxml-6.0.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7fd70681aeed83b196482d42a9b0dc5b13bab55668d09ad75ed26dff3be5a2f5", size = 5657173 }, + { url = "https://files.pythonhosted.org/packages/32/a3/e98806d483941cd9061cc838b1169626acef7b2807261fbe5e382fcef881/lxml-6.0.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:10a72e456319b030b3dd900df6b1f19d89adf06ebb688821636dc406788cf6ac", size = 5245896 }, + { url = "https://files.pythonhosted.org/packages/07/de/9bb5a05e42e8623bf06b4638931ea8c8f5eb5a020fe31703abdbd2e83547/lxml-6.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b0fa45fb5f55111ce75b56c703843b36baaf65908f8b8d2fbbc0e249dbc127ed", size = 5267417 }, + { url = "https://files.pythonhosted.org/packages/f2/43/c1cb2a7c67226266c463ef8a53b82d42607228beb763b5fbf4867e88a21f/lxml-6.0.1-cp313-cp313-win32.whl", hash = "sha256:01dab65641201e00c69338c9c2b8a0f2f484b6b3a22d10779bb417599fae32b5", size = 3610051 }, + { url = "https://files.pythonhosted.org/packages/34/96/6a6c3b8aa480639c1a0b9b6faf2a63fb73ab79ffcd2a91cf28745faa22de/lxml-6.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:bdf8f7c8502552d7bff9e4c98971910a0a59f60f88b5048f608d0a1a75e94d1c", size = 4009325 }, + { url = "https://files.pythonhosted.org/packages/8c/66/622e8515121e1fd773e3738dae71b8df14b12006d9fb554ce90886689fd0/lxml-6.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:a6aeca75959426b9fd8d4782c28723ba224fe07cfa9f26a141004210528dcbe2", size = 3670443 }, + { url = "https://files.pythonhosted.org/packages/38/e3/b7eb612ce07abe766918a7e581ec6a0e5212352194001fd287c3ace945f0/lxml-6.0.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:29b0e849ec7030e3ecb6112564c9f7ad6881e3b2375dd4a0c486c5c1f3a33859", size = 8426160 }, + { url = "https://files.pythonhosted.org/packages/35/8f/ab3639a33595cf284fe733c6526da2ca3afbc5fd7f244ae67f3303cec654/lxml-6.0.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:02a0f7e629f73cc0be598c8b0611bf28ec3b948c549578a26111b01307fd4051", size = 4589288 }, + { url = "https://files.pythonhosted.org/packages/2c/65/819d54f2e94d5c4458c1db8c1ccac9d05230b27c1038937d3d788eb406f9/lxml-6.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:beab5e54de016e730875f612ba51e54c331e2fa6dc78ecf9a5415fc90d619348", size = 4964523 }, + { url = "https://files.pythonhosted.org/packages/5b/4a/d4a74ce942e60025cdaa883c5a4478921a99ce8607fc3130f1e349a83b28/lxml-6.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:92a08aefecd19ecc4ebf053c27789dd92c87821df2583a4337131cf181a1dffa", size = 5101108 }, + { url = "https://files.pythonhosted.org/packages/cb/48/67f15461884074edd58af17b1827b983644d1fae83b3d909e9045a08b61e/lxml-6.0.1-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36c8fa7e177649470bc3dcf7eae6bee1e4984aaee496b9ccbf30e97ac4127fa2", size = 5053498 }, + { url = "https://files.pythonhosted.org/packages/b6/d4/ec1bf1614828a5492f4af0b6a9ee2eb3e92440aea3ac4fa158e5228b772b/lxml-6.0.1-cp314-cp314-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:5d08e0f1af6916267bb7eff21c09fa105620f07712424aaae09e8cb5dd4164d1", size = 5351057 }, + { url = "https://files.pythonhosted.org/packages/65/2b/c85929dacac08821f2100cea3eb258ce5c8804a4e32b774f50ebd7592850/lxml-6.0.1-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9705cdfc05142f8c38c97a61bd3a29581ceceb973a014e302ee4a73cc6632476", size = 5671579 }, + { url = "https://files.pythonhosted.org/packages/d0/36/cf544d75c269b9aad16752fd9f02d8e171c5a493ca225cb46bb7ba72868c/lxml-6.0.1-cp314-cp314-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74555e2da7c1636e30bff4e6e38d862a634cf020ffa591f1f63da96bf8b34772", size = 5250403 }, + { url = "https://files.pythonhosted.org/packages/c2/e8/83dbc946ee598fd75fdeae6151a725ddeaab39bb321354a9468d4c9f44f3/lxml-6.0.1-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:e38b5f94c5a2a5dadaddd50084098dfd005e5a2a56cd200aaf5e0a20e8941782", size = 4696712 }, + { url = "https://files.pythonhosted.org/packages/f4/72/889c633b47c06205743ba935f4d1f5aa4eb7f0325d701ed2b0540df1b004/lxml-6.0.1-cp314-cp314-manylinux_2_38_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a5ec101a92ddacb4791977acfc86c1afd624c032974bfb6a21269d1083c9bc49", size = 5268177 }, + { url = "https://files.pythonhosted.org/packages/b0/b6/f42a21a1428479b66ea0da7bd13e370436aecaff0cfe93270c7e165bd2a4/lxml-6.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5c17e70c82fd777df586c12114bbe56e4e6f823a971814fd40dec9c0de518772", size = 5094648 }, + { url = "https://files.pythonhosted.org/packages/51/b0/5f8c1e8890e2ee1c2053c2eadd1cb0e4b79e2304e2912385f6ca666f48b1/lxml-6.0.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:45fdd0415a0c3d91640b5d7a650a8f37410966a2e9afebb35979d06166fd010e", size = 4745220 }, + { url = "https://files.pythonhosted.org/packages/eb/f9/820b5125660dae489ca3a21a36d9da2e75dd6b5ffe922088f94bbff3b8a0/lxml-6.0.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:d417eba28981e720a14fcb98f95e44e7a772fe25982e584db38e5d3b6ee02e79", size = 5692913 }, + { url = "https://files.pythonhosted.org/packages/23/8e/a557fae9eec236618aecf9ff35fec18df41b6556d825f3ad6017d9f6e878/lxml-6.0.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:8e5d116b9e59be7934febb12c41cce2038491ec8fdb743aeacaaf36d6e7597e4", size = 5259816 }, + { url = "https://files.pythonhosted.org/packages/fa/fd/b266cfaab81d93a539040be699b5854dd24c84e523a1711ee5f615aa7000/lxml-6.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c238f0d0d40fdcb695c439fe5787fa69d40f45789326b3bb6ef0d61c4b588d6e", size = 5276162 }, + { url = "https://files.pythonhosted.org/packages/25/6c/6f9610fbf1de002048e80585ea4719591921a0316a8565968737d9f125ca/lxml-6.0.1-cp314-cp314-win32.whl", hash = "sha256:537b6cf1c5ab88cfd159195d412edb3e434fee880f206cbe68dff9c40e17a68a", size = 3669595 }, + { url = "https://files.pythonhosted.org/packages/72/a5/506775e3988677db24dc75a7b03e04038e0b3d114ccd4bccea4ce0116c15/lxml-6.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:911d0a2bb3ef3df55b3d97ab325a9ca7e438d5112c102b8495321105d25a441b", size = 4079818 }, + { url = "https://files.pythonhosted.org/packages/0a/44/9613f300201b8700215856e5edd056d4e58dd23368699196b58877d4408b/lxml-6.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:2834377b0145a471a654d699bdb3a2155312de492142ef5a1d426af2c60a0a31", size = 3753901 }, +]