diff --git a/src/alpine_bits_python/alpine_bits_helpers.py b/src/alpine_bits_python/alpine_bits_helpers.py index ee5c3ea..e09cadd 100644 --- a/src/alpine_bits_python/alpine_bits_helpers.py +++ b/src/alpine_bits_python/alpine_bits_helpers.py @@ -1,4 +1,5 @@ from datetime import datetime, timezone +import traceback from typing import Union, Optional, Any, TypeVar from pydantic import BaseModel, ConfigDict, Field from dataclasses import dataclass @@ -14,6 +15,7 @@ from .generated.alpinebits import ( OtaHotelResNotifRq, OtaResRetrieveRs, CommentName2, + ProfileProfileType, UniqueIdType2, ) import logging @@ -735,10 +737,12 @@ def _process_single_reservation(reservation: Reservation, customer: Customer, me UniqueId = NotifUniqueId RoomStays = NotifRoomStays HotelReservation = NotifHotelReservation + Profile = OtaHotelResNotifRq.HotelReservations.HotelReservation.ResGlobalInfo.Profiles.ProfileInfo.Profile elif message_type == OtaMessageType.RETRIEVE: UniqueId = RetrieveUniqueId RoomStays = RetrieveRoomStays HotelReservation = RetrieveHotelReservation + Profile = OtaResRetrieveRs.ReservationsList.HotelReservation.ResGlobalInfo.Profiles.ProfileInfo.Profile else: raise ValueError(f"Unsupported message type: {message_type}") @@ -795,10 +799,24 @@ def _process_single_reservation(reservation: Reservation, customer: Customer, me else: # extract string from Column object klick_id = str(klick_id) + + + + + utm_medium = ( + str(reservation.utm_medium) + if reservation.utm_medium is not None and str(reservation.utm_medium) != "" + else "website" + ) + + #shorten klick_id if longer than 64 characters + if klick_id is not None and len(klick_id) > 64: + klick_id = klick_id[:64] + hotel_res_id_data = HotelReservationIdData( res_id_type="13", res_id_value=klick_id, - res_id_source=None, + res_id_source=utm_medium, res_id_source_context="99tales", ) @@ -866,12 +884,26 @@ def _process_single_reservation(reservation: Reservation, customer: Customer, me comments_xml = alpine_bits_factory.create( comments_data, message_type ) + + + company_name = Profile.CompanyInfo.CompanyName(value="99tales GmbH", code="who knows?", code_context="who knows?") + + company_info = Profile.CompanyInfo(company_name=company_name) + + profile = Profile(company_info=company_info, profile_type=ProfileProfileType.VALUE_4) + + profile_info = HotelReservation.ResGlobalInfo.Profiles.ProfileInfo(profile=profile) + + _LOGGER.info(f"Type of profile_info: {type(profile_info)}") + + profiles = HotelReservation.ResGlobalInfo.Profiles(profile_info=profile_info) res_global_info = ( HotelReservation.ResGlobalInfo( hotel_reservation_ids=hotel_res_ids, basic_property_info=basic_property_info, comments=comments_xml, + profiles=profiles, ) ) @@ -917,6 +949,7 @@ def _create_xml_from_db(entries: list[Tuple[Reservation, Customer]] | Tuple[Rese _LOGGER.error( f"Error creating XML for reservation {reservation.unique_id} and customer {customer.given_name}: {e}" ) + _LOGGER.debug(traceback.format_exc()) if type == OtaMessageType.NOTIF: retrieved_reservations = OtaHotelResNotifRq.HotelReservations( diff --git a/src/alpine_bits_python/api.py b/src/alpine_bits_python/api.py index 68381ea..252368f 100644 --- a/src/alpine_bits_python/api.py +++ b/src/alpine_bits_python/api.py @@ -31,6 +31,7 @@ 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 from .alpinebits_server import AlpineBitsClientInfo, AlpineBitsServer, Version, AlpineBitsActionName @@ -113,13 +114,25 @@ async def push_listener(customer: DBCustomer, reservation: DBReservation, hotel) if request.status_code != 200: _LOGGER.error(f"Failed to generate push request for hotel {hotel_id}, reservation {reservation.unique_id}: {request.xml_content}") return - - print(request.xml_content) - # TODO: Generate AlpineBits OTA_HotelResNotifRQ request - # action = "OTA_HotelResNotifRQ" - # request = server.handle_request(action, ...) - print(f"--- Push Payload --- received. Sending to endpoint., hotelid {hotel_id}, reservation {reservation.unique_id}") + + # save push request to file + + logs_dir = "logs/push_requests" + if not os.path.exists(logs_dir): + os.makedirs(logs_dir, mode=0o755, exist_ok=True) + stat_info = os.stat(logs_dir) + _LOGGER.info( + f"Created directory owner: uid:{stat_info.st_uid}, gid:{stat_info.st_gid}" + ) + _LOGGER.info(f"Directory mode: {oct(stat_info.st_mode)[-3:]}") + log_filename = ( + f"{logs_dir}/alpinebits_push_{hotel_id}_{reservation.unique_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xml" + ) + + + with open(log_filename, "w", encoding="utf-8") as f: + f.write(request.xml_content) return headers = {"Authorization": f"Bearer {push_endpoint.get('token','')}"} if push_endpoint.get('token') else {} @@ -337,7 +350,7 @@ async def process_wix_form_submission(request: Request, data: Dict[str, Any], db name_prefix = data.get("field:anrede") email_newsletter_string = data.get("field:form_field_5a7b", "") - yes_values = {"Selezionato", "Angekreuzt"} + yes_values = {"Selezionato", "Angekreuzt", "Checked"} email_newsletter = (email_newsletter_string in yes_values) address_line = None city_name = None @@ -460,17 +473,21 @@ async def process_wix_form_submission(request: Request, data: Dict[str, Any], db await db.commit() await db.refresh(db_reservation) + + async def push_event(): + # Fire event for listeners (push, etc.) - hotel-specific dispatch + dispatcher = getattr(request.app.state, "event_dispatcher", None) + if dispatcher: + # Get hotel_code from reservation to target the right listeners + hotel_code = getattr(db_reservation, 'hotel_code', None) + if hotel_code and hotel_code.strip(): + await dispatcher.dispatch_for_hotel("form_processed", hotel_code, db_customer, db_reservation) + _LOGGER.info(f"Dispatched form_processed event for hotel {hotel_code}") + else: + _LOGGER.warning("No hotel_code in reservation, skipping push notifications") - # Fire event for listeners (push, etc.) - hotel-specific dispatch - dispatcher = getattr(request.app.state, "event_dispatcher", None) - if dispatcher: - # Get hotel_code from reservation to target the right listeners - hotel_code = getattr(db_reservation, 'hotel_code', None) - if hotel_code and hotel_code.strip(): - await dispatcher.dispatch_for_hotel("form_processed", hotel_code, db_customer, db_reservation) - _LOGGER.info(f"Dispatched form_processed event for hotel {hotel_code}") - else: - _LOGGER.warning("No hotel_code in reservation, skipping push notifications") + asyncio.create_task(push_event()) + return { "status": "success",