Updated requirements. Added
This commit is contained in:
10
src/alpine_bits_python/config_loader.py
Normal file
10
src/alpine_bits_python/config_loader.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import os
|
||||
import yaml
|
||||
import annotatedyaml
|
||||
|
||||
CONFIG_PATH = os.path.join(os.path.dirname(__file__), '../../config/config.yaml')
|
||||
|
||||
def load_config():
|
||||
with open(CONFIG_PATH, 'r', encoding='utf-8') as f:
|
||||
# Use annotatedyaml to load secrets if present
|
||||
return annotatedyaml.load(f)
|
||||
60
src/alpine_bits_python/db.py
Normal file
60
src/alpine_bits_python/db.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from sqlalchemy import create_engine, Column, Integer, String, Date, Boolean, ForeignKey, DateTime
|
||||
from sqlalchemy.orm import declarative_base, relationship, sessionmaker
|
||||
import os
|
||||
|
||||
Base = declarative_base()
|
||||
|
||||
class Customer(Base):
|
||||
__tablename__ = 'customers'
|
||||
id = Column(Integer, primary_key=True)
|
||||
given_name = Column(String)
|
||||
surname = Column(String)
|
||||
name_prefix = Column(String)
|
||||
email_address = Column(String)
|
||||
phone = Column(String)
|
||||
email_newsletter = Column(Boolean)
|
||||
address_line = Column(String)
|
||||
city_name = Column(String)
|
||||
postal_code = Column(String)
|
||||
country_code = Column(String)
|
||||
gender = Column(String)
|
||||
birth_date = Column(String)
|
||||
language = Column(String)
|
||||
reservations = relationship('Reservation', back_populates='customer')
|
||||
|
||||
class Reservation(Base):
|
||||
__tablename__ = 'reservations'
|
||||
id = Column(Integer, primary_key=True)
|
||||
customer_id = Column(Integer, ForeignKey('customers.id'))
|
||||
start_date = Column(Date)
|
||||
end_date = Column(Date)
|
||||
num_adults = Column(Integer)
|
||||
num_children = Column(Integer)
|
||||
children_ages = Column(String) # comma-separated
|
||||
offer = Column(String)
|
||||
utm_comment = Column(String)
|
||||
created_at = Column(DateTime)
|
||||
customer = relationship('Customer', back_populates='reservations')
|
||||
|
||||
class HashedCustomer(Base):
|
||||
__tablename__ = 'hashed_customers'
|
||||
id = Column(Integer, primary_key=True)
|
||||
customer_id = Column(Integer)
|
||||
hashed_email = Column(String)
|
||||
hashed_phone = Column(String)
|
||||
hashed_name = Column(String)
|
||||
redacted_at = Column(DateTime)
|
||||
|
||||
|
||||
def get_engine():
|
||||
db_url = os.environ.get('DATABASE_URL')
|
||||
if db_url:
|
||||
return create_engine(db_url)
|
||||
# Default to local sqlite
|
||||
return create_engine('sqlite:///alpinebits.db')
|
||||
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=get_engine())
|
||||
|
||||
def init_db():
|
||||
engine = get_engine()
|
||||
Base.metadata.create_all(bind=engine)
|
||||
@@ -2,7 +2,7 @@ from .alpinebits_guestrequests import ResGuest, RoomStay
|
||||
from .generated import alpinebits as ab
|
||||
from io import BytesIO
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timezone, date
|
||||
import re
|
||||
from xsdata_pydantic.bindings import XmlSerializer
|
||||
|
||||
@@ -11,18 +11,29 @@ from .simplified_access import (
|
||||
CommentsData,
|
||||
CommentListItemData,
|
||||
CustomerData,
|
||||
|
||||
HotelReservationIdData,
|
||||
PhoneTechType,
|
||||
AlpineBitsFactory,
|
||||
OtaMessageType
|
||||
)
|
||||
|
||||
# DB and config
|
||||
from .db import Customer as DBCustomer, Reservation as DBReservation, HashedCustomer, SessionLocal, init_db
|
||||
from .config_loader import load_config
|
||||
import hashlib
|
||||
|
||||
|
||||
def main():
|
||||
import json
|
||||
import os
|
||||
|
||||
# Load config (yaml, annotatedyaml)
|
||||
config = load_config()
|
||||
|
||||
# Init DB
|
||||
init_db()
|
||||
db = SessionLocal()
|
||||
|
||||
# Load data from JSON file
|
||||
json_path = os.path.join(os.path.dirname(__file__), '../../test_data/wix_test_data_20250927_070500.json')
|
||||
with open(json_path, 'r', encoding='utf-8') as f:
|
||||
@@ -61,6 +72,56 @@ def main():
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# UTM and offer
|
||||
utm_fields = [
|
||||
("utm_Source", "utm_source"),
|
||||
("utm_Medium", "utm_medium"),
|
||||
("utm_Campaign", "utm_campaign"),
|
||||
("utm_Term", "utm_term"),
|
||||
("utm_Content", "utm_content"),
|
||||
]
|
||||
utm_comment_text = []
|
||||
for label, field in utm_fields:
|
||||
val = data.get(f"field:{field}") or data.get(label)
|
||||
if val:
|
||||
utm_comment_text.append(f"{label}: {val}")
|
||||
utm_comment = " | ".join(utm_comment_text) if utm_comment_text else None
|
||||
offer = data.get("field:angebot_auswaehlen")
|
||||
|
||||
# Save customer and reservation to DB
|
||||
db_customer = DBCustomer(
|
||||
given_name=given_name,
|
||||
surname=surname,
|
||||
name_prefix=name_prefix,
|
||||
email_address=email_address,
|
||||
phone=phone,
|
||||
email_newsletter=email_newsletter,
|
||||
address_line=address_line,
|
||||
city_name=city_name,
|
||||
postal_code=postal_code,
|
||||
country_code=country_code,
|
||||
gender=gender,
|
||||
birth_date=birth_date,
|
||||
language=language,
|
||||
)
|
||||
db.add(db_customer)
|
||||
db.commit()
|
||||
db.refresh(db_customer)
|
||||
|
||||
db_reservation = DBReservation(
|
||||
customer_id=db_customer.id,
|
||||
start_date=date.fromisoformat(start_date) if start_date else None,
|
||||
end_date=date.fromisoformat(end_date) if end_date else None,
|
||||
num_adults=num_adults,
|
||||
num_children=num_children,
|
||||
children_ages=','.join(str(a) for a in children_ages),
|
||||
offer=offer,
|
||||
utm_comment=utm_comment,
|
||||
created_at=datetime.now(timezone.utc),
|
||||
)
|
||||
db.add(db_reservation)
|
||||
db.commit()
|
||||
|
||||
# Success - use None instead of object() for cleaner XML output
|
||||
success = None
|
||||
|
||||
@@ -69,11 +130,8 @@ def main():
|
||||
type_value=ab.UniqueIdType2.VALUE_14, id="6b34fe24ac2ff811"
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
time_span = ab.OtaResRetrieveRs.ReservationsList.HotelReservation.RoomStays.RoomStay.TimeSpan(
|
||||
start= start_date, end=end_date
|
||||
start=start_date, end=end_date
|
||||
)
|
||||
|
||||
# RoomStay with TimeSpan
|
||||
@@ -108,22 +166,6 @@ def main():
|
||||
alpine_bits_factory = AlpineBitsFactory()
|
||||
res_guests = alpine_bits_factory.create_res_guests(customer_data, OtaMessageType.RETRIEVE)
|
||||
|
||||
utm_fields = [
|
||||
("utm_Source", "utm_source"),
|
||||
("utm_Medium", "utm_medium"),
|
||||
("utm_Campaign", "utm_campaign"),
|
||||
("utm_Term", "utm_term"),
|
||||
("utm_Content", "utm_content"),
|
||||
]
|
||||
utm_comment_text = []
|
||||
for label, field in utm_fields:
|
||||
val = data.get(f"field:{field}") or data.get(label)
|
||||
if val:
|
||||
utm_comment_text.append(f"{label}: {val}")
|
||||
utm_comment = " | ".join(utm_comment_text) if utm_comment_text else None
|
||||
|
||||
|
||||
|
||||
hotel_res_id_data = HotelReservationIdData(
|
||||
res_id_type="13",
|
||||
res_id_value=data.get("field:fbclid") or data.get("field:gclid"),
|
||||
@@ -144,8 +186,6 @@ def main():
|
||||
)
|
||||
|
||||
# Comments from UTM fields and other info
|
||||
|
||||
|
||||
comment = CommentData(
|
||||
name= ab.CommentName2.CUSTOMER_COMMENT,
|
||||
text="Wix form submission",
|
||||
@@ -156,8 +196,6 @@ def main():
|
||||
)],
|
||||
)
|
||||
|
||||
|
||||
|
||||
comments_data = CommentsData(comments=[comment])
|
||||
comments = alpine_bits_factory.create(comments_data, OtaMessageType.RETRIEVE)
|
||||
|
||||
@@ -235,6 +273,5 @@ def main():
|
||||
except Exception as e:
|
||||
print(f"❌ Validation/Serialization failed: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
Reference in New Issue
Block a user