mss-python 0.1.2__py3-none-any.whl → 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mss_python/__init__.py ADDED
File without changes
mss_python/client.py ADDED
@@ -0,0 +1,111 @@
1
+ import requests
2
+ import xmltodict
3
+ import xml.etree.ElementTree as ET
4
+
5
+ from .request.methods import Method
6
+ from .request.types_mss import Root, BaseType, Credentials, Search, MethodName, Literal
7
+
8
+
9
+ # maybe recursion should be used instead of doing if checks?
10
+ def change_structure(root: Root, child: BaseType):
11
+ if child.tag == "root":
12
+ return child
13
+
14
+ if child.tag == "request":
15
+ root.request = child
16
+
17
+ elif child.tag == "search":
18
+ root.request.search = child
19
+
20
+ elif child.tag == "order":
21
+ root.request.order = child
22
+
23
+ elif child.tag == "options":
24
+ root.request.options = child
25
+
26
+ elif child.tag == "logging":
27
+ root.request.logging = child
28
+
29
+ elif child.tag == "header":
30
+ root.header = child
31
+
32
+ return root
33
+
34
+
35
+ class Client:
36
+ def __init__(
37
+ self, cred: Credentials, lang: list, elements: list | None = None
38
+ ) -> None:
39
+ self.cred = cred
40
+ self.lang = Search(lang)
41
+
42
+ def request(
43
+ self,
44
+ url: str,
45
+ method_name: MethodName,
46
+ data: BaseType | None = None,
47
+ _print: Literal[True, False] = False,
48
+ ) -> dict:
49
+ """
50
+ Makes a post request to the given URL with the specified data and method name.
51
+
52
+ data: ElementTree with the data required in the XML
53
+ url: Service URL
54
+ method: Method to be executed by the service
55
+ """
56
+
57
+ method = Method(method_name)
58
+
59
+ root = method.get_base_xml(self.cred, self.lang)
60
+
61
+ if data is None:
62
+ root = change_structure(root, data)
63
+
64
+ if _print is True:
65
+ print(ET.tostring(root.request.to_xml(), encoding="unicode"))
66
+
67
+ response = requests.post(
68
+ url,
69
+ data=ET.tostring(root.to_xml(), encoding="unicode"),
70
+ headers={"Content-Type": "application/xml"},
71
+ )
72
+
73
+ responseRoot = xmltodict.parse(response.content)
74
+ error = responseRoot["root"]["header"]["error"]
75
+ if int(error["code"]) > 0:
76
+ raise Exception(
77
+ f'MSS returned an error: code {error["code"]}, message: "{error["message"]}"'
78
+ )
79
+
80
+ return refine_response(responseRoot["root"], method_name)
81
+
82
+
83
+ def refine_response(resp: dict, meth: MethodName) -> dict:
84
+ if meth == MethodName.GetHotelList:
85
+ return refine_getHotelList(resp)
86
+
87
+ return resp
88
+
89
+
90
+ def refine_getHotelList(resp: dict) -> dict:
91
+ if resp["result"] is None:
92
+ return resp
93
+ elif "hotel" not in resp["result"] or resp["result"]["hotel"] is None:
94
+ return resp
95
+ elif resp["result"]["hotel"] is list:
96
+ resp["result"]["hotel"] = [resp["result"]["hotel"]]
97
+
98
+ for hotel in resp["result"]["hotel"]:
99
+ ensure_list_value(hotel, "pictures", "picture")
100
+ ensure_list_value(hotel, "gallery", "picture")
101
+ ensure_list_value(hotel, "feature_view", "feature")
102
+ ensure_list_value(hotel, "pos", "id_pos")
103
+
104
+ return resp
105
+
106
+
107
+ def ensure_list_value(hotel: dict, parent_fld: str, child_fld: str) -> None:
108
+ if parent_fld not in hotel:
109
+ hotel[parent_fld] = {child_fld: []}
110
+ elif hotel[parent_fld][child_fld] is list:
111
+ hotel[parent_fld][child_fld] = [hotel[parent_fld][child_fld]]
File without changes
@@ -0,0 +1,28 @@
1
+ from enum import Enum
2
+
3
+
4
+ class MethodName(Enum):
5
+ GetHotelList = "getHotelList"
6
+ GetHotelListByFilter = "getHotelListByFilter"
7
+ GetSpecialList = "getSpecialList"
8
+ GetRoomList = "getRoomList"
9
+ GetWidgetConfig = "getWidgetConfig"
10
+ GetPriceList = "getPriceList"
11
+ GetAvailability = "getAvailability"
12
+ GetRoomAvailability = "getRoomAvailability"
13
+ GetDayAvailability = "getDayAvailability"
14
+ PrepareBooking = "prepareBooking"
15
+ GetBooking = "getBooking"
16
+ CancelBooking = "cancelBooking"
17
+ CreateInquiry = "createInquiry"
18
+ GetInquiry = "getInquiry"
19
+ GetUserSources = "getUserSources"
20
+ GetLocationList = "getLocationList"
21
+ GetMasterpackagesList = "getMasterpackagesList"
22
+ NotifyMasterpackages = "notifyMasterpackages"
23
+ GetThemeList = "getThemeList"
24
+ GetLastminuteQuotations = "getLastminuteQuotations"
25
+ GetHotelPictures = "getHotelPictures"
26
+ GetHotelPictureGroups = "getHotelPictureGroups"
27
+ GetOptionalServices = "getOptionalServices"
28
+ ValidateCoupon = "validateCoupon"
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ from .method_names import MethodName
4
+ from .types_mss import Credentials, Search, Root, Header, Request
5
+
6
+
7
+ class Method:
8
+ def __init__(self, name: MethodName):
9
+ self.name = name
10
+
11
+ def get_base_xml(self, cred: Credentials, lang: Search) -> Root:
12
+ """
13
+ Returns an XML (ElementTree) with the mandatory elements needed for the request.
14
+
15
+ cred: XML Credentials Element (username, password, source)
16
+
17
+ lang: XML Search Element with the language element defined
18
+ """
19
+
20
+ header = Header(cred, self.name)
21
+ req = Request(lang)
22
+ root = Root(header, req)
23
+ return root
@@ -0,0 +1,835 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+ from enum import Enum, IntFlag
4
+ from typing import Literal
5
+ import xml.etree.ElementTree as ET
6
+ from abc import ABC
7
+
8
+ from .method_names import MethodName
9
+
10
+
11
+ def get_child_xml(value, field, root):
12
+ field = field.replace("___", "-")
13
+ match value:
14
+ case None:
15
+ return
16
+ case int() | float():
17
+ ET.SubElement(root, field).text = str(value)
18
+ case str():
19
+ if field == "tag":
20
+ return
21
+ ET.SubElement(root, field).text = value
22
+ case list():
23
+ for x in value:
24
+ get_child_xml(x, field, root)
25
+ case Enum():
26
+ ET.SubElement(root, field).text = str(value.value)
27
+ case _:
28
+ root.append(value.to_xml())
29
+
30
+
31
+ class BaseType(ABC):
32
+ def __init__(self, tag):
33
+ self.tag = tag
34
+
35
+ def to_xml(self) -> ET.Element:
36
+ root = ET.Element(self.tag)
37
+ # root.append(self.search.to_xml())
38
+
39
+ for field_, value in vars(self).items():
40
+ get_child_xml(value, field_, root)
41
+
42
+ return root
43
+
44
+
45
+ @dataclass()
46
+ class Root(BaseType):
47
+ header: Header
48
+ request: Request
49
+ version: str = "2.0"
50
+
51
+ def __post_init__(self):
52
+ super().__init__("root")
53
+
54
+
55
+ @dataclass()
56
+ class Header(BaseType):
57
+ credentials: Credentials
58
+ method: MethodName
59
+ # method: str
60
+ paging: Paging | None = field(default=None)
61
+
62
+ def __post_init__(self):
63
+ super().__init__("header")
64
+
65
+
66
+ @dataclass()
67
+ class Credentials(BaseType):
68
+ user: str
69
+ password: str
70
+ source: str
71
+
72
+ def __post_init__(self):
73
+ super().__init__("credentials")
74
+
75
+
76
+ @dataclass()
77
+ class Paging:
78
+ start: int
79
+ limit: int
80
+
81
+
82
+ @dataclass
83
+ class Request(BaseType):
84
+ search: Search
85
+ options: Options | None = field(default=None)
86
+
87
+ order: Order | None = field(default=None)
88
+
89
+ data: Data | None = field(default=None)
90
+
91
+ logging: Logging | None = field(default=None)
92
+
93
+ def __post_init__(self):
94
+ super().__init__("request")
95
+
96
+
97
+ @dataclass
98
+ class Search(BaseType):
99
+ lang: list # Literal["de", "it", "en", "es", "fr", "ru", "da"]
100
+ result_id: str | None = field(default=None)
101
+
102
+ agent: str | None = field(default=None)
103
+
104
+ id: list | None = field(default=None) # number[]
105
+
106
+ search_hotel: SearchHotel | None = field(default=None)
107
+
108
+ search_location: SearchLocation | None = field(default=None)
109
+
110
+ search_distance: SearchDistance | None = field(default=None)
111
+
112
+ search_offer: SearchOffer | None = field(default=None)
113
+
114
+ search_lts: SearchLts | None = field(default=None)
115
+
116
+ search_special: SearchSpecial | None = field(default=None)
117
+
118
+ search_availability: SearchAvailability | None = field(default=None)
119
+
120
+ search_pricelist: SearchPriceList | None = field(default=None)
121
+
122
+ in_: int | None = field(default=None) # number[]
123
+
124
+ id_ofchannel: Literal["hgv", "lts", "esy"] | None = field(default=None)
125
+
126
+ transaction_id: str | None = field(default=None)
127
+
128
+ booking_id: str | None = field(default=None)
129
+
130
+ guest_email: str | None = field(default=None)
131
+
132
+ pic_group_id: int | None = field(default=None)
133
+
134
+ id_apt: int | None = field(default=None)
135
+
136
+ root_id: list | None = field(default=None) # list of ints
137
+
138
+ pic_type: PictureType | None = field(default=None)
139
+
140
+ select___avail: HotelFilters | None = field(default=None)
141
+ select___rooms: HotelFilters | None = field(default=None)
142
+ select___price: HotelFilters | None = field(default=None)
143
+ avail_alert: Literal[0, 1] | None = field(default=None)
144
+ rooms_alert: Literal[0, 1] | None = field(default=None)
145
+ price_alert: Literal[0, 1] | None = field(default=None)
146
+
147
+ coupon_code: str | None = field(default=None)
148
+ total_price: int | None = field(default=None)
149
+ arrival: str | None = field(default=None)
150
+ departure: str | None = field(default=None)
151
+ coupon_type: Literal["promotion", "voucher"] | None = field(default=None)
152
+
153
+ def __post_init__(self):
154
+ super().__init__("search")
155
+
156
+
157
+ class HotelFilters(Enum):
158
+ OTA = "ota"
159
+ EASI_CHANNEL = "easiChannel"
160
+ ROTER_HAHN = "10ec953a9d700b9598580a52d3dcef6c"
161
+ HOTEL_PARTNER = "2d9038f6a34c23280669f73ff531683c"
162
+ FIVE_STELLE = "30fb54af1d0d231b01b778647cfd967c"
163
+ ASA_LIGHT = "51c761e7b7b87dbeafe25a9b"
164
+ ASA_HOTEL = "51c761e7b7b87dc3afe15afa"
165
+ ROOM_CLOUD = "65ee9db4c37baedc1032cbd72c8caaaf"
166
+ GS_DATAPROVIDER = "66461e9d7c481e44e632051c3d040a2b"
167
+ HGV_TEST_CLIENT = "95f6a058-45ca-49df-874f-86f41e0bb29f"
168
+ HGV_CLIENT_FOR_TRENTINO = "a47a9dd543e55f8b3ee7ce4cc88b6d57"
169
+ HOTEL_NET_SOLUTIONS = "a57f0615b226609ae409c421ff118a8a"
170
+ BEDDY = "a8513684c7683fd457f82818f5f15bbf"
171
+ PCS_PHOENIX_XENUS = "AE1D2D8776B3FC1C;XENUS"
172
+ AIDA = "AIbD6vqHlYHA17DA"
173
+ AE_SMO = "ba098f2c86b942e58b6e7a89448f4063"
174
+ MTS_ONLINE = "bdcec66dfe4e8ef9f5c7e71e4835204c"
175
+ LTS_BOZEN = "be6bc64c94bbc062bcebfb40b4f93304"
176
+ BOOKING_EXPERT = "BookingE47acfb09170e8"
177
+ CASABLANCA = "CasaifeDZQQDaqvlblanca"
178
+ APARTMENT4HOLIDAY = "ceaeed03a0711eec46582b689d16dc0b"
179
+ CIAOBNB = "CiaoK2VAvAkFlCTcbnb"
180
+ EASI_SUITE = "Easy68e9nMxChannel"
181
+ EASI_SUITE_2 = "Easy68e9nMxChannel2"
182
+ SCIDOO = "f0bdfb926c2a8d066a9d2ea3e632471c"
183
+ ERICSOFT = "fcfb97bb1a8e608e5971928ac2032a68"
184
+ HOTEL_SPIDER = "HoteltihPrvo8kMpbSpider"
185
+ IPER_BOOKING = "iperbooking"
186
+ KROSS_BOOKING = "Kross2XklTsfhbBooking"
187
+ MM_ONE = "MMwRNWBeSQiABuONE"
188
+ PASSEPARTOUT = "passPGkPyHnw3IbApartout"
189
+ SEEKDA = "SEEK8XLyALKEzmpyp2DA"
190
+ SIMPLE_BOOKING = "Simple89sKiOdS9PUcbooking"
191
+ ANEXIS = "SiPaCbW2I921EpMedia"
192
+ SMART_PRICING = "SmartqpukLfH5Kl1price"
193
+ TEST_YANOVIS = "travelsuite"
194
+ VERTICAL_BOOKING = "VertiexDH0x8XV7Booking"
195
+ ALPINE_BITS_CERTIFICATION = "VwnIjABCertification78czDK6"
196
+ WUBOOK = "Wub0Na0CxWovxdVook"
197
+
198
+
199
+ class PictureType(Enum):
200
+ Hotel = "hotel"
201
+ Gallery = "gallery"
202
+ Profile = "profile"
203
+ Roomgroup = "roomgroup"
204
+ Pricelist = "pricelist"
205
+ Special = "special"
206
+
207
+
208
+ @dataclass
209
+ class Options(BaseType):
210
+ hotel_details: HotelDetails | None = field(default=None)
211
+ offer_details: OfferDetails | None = field(default=None)
212
+
213
+ picture_date: str | None = field(default=None) # Date
214
+ lts_bookable: Literal[0, 1, 2] | None = field(default=None) # number, 0|1|2
215
+
216
+ base_price: int | None = field(default=None) # number, 0|1
217
+
218
+ # field is in easychannel but not in nodejs
219
+ only_available: Literal[0, 1] | None = field(default=None) # 0 | 1
220
+
221
+ # below fields are in nodejs but not in easychannel.it?
222
+ room_details: RoomDetails | None = field(default=None)
223
+ special_details: SpecialDetails | None = field(default=None)
224
+ # pricelist_details: PriceListDetails | None = field(default=None)
225
+
226
+ get_availability: Literal[0, 1] | None = field(default=None) # 0 | 1
227
+ get_restrictions: Literal[0, 1] | None = field(default=None) # 0 | 1
228
+ get_roomdetails: int | None = field(default=None) # 0 | 1
229
+ get_masterpackages: Literal[0, 1] | None = field(default=None)
230
+
231
+ location_details: Literal[0, 1] | None = field(default=None)
232
+ theme_details: Literal[0, 1] | None = field(default=None)
233
+
234
+ service_details: ServiceDetails | None = field(default=None)
235
+
236
+ def __post_init__(self):
237
+ super().__init__("options")
238
+
239
+
240
+ class ServiceDetails(IntFlag):
241
+ Headlines = 8
242
+ Pictures = 32
243
+ Descriptions = 256
244
+
245
+
246
+ class HotelDetails(IntFlag):
247
+ BasicInfo = 1
248
+ Themes = 2
249
+ HotelFacilities = 4
250
+ ShortDescription = 8
251
+ FullDescription = 16
252
+ GeographicInformation = 32
253
+ Coordinates = 64
254
+ Address = 128
255
+ Contacts = 256
256
+ PaymentOptionsForOnlineBooking = 512
257
+ PaymentOptionsAtHotel = 1024 #
258
+ Logo = 2048
259
+ HeaderImages = 4096
260
+ Gallery = 8192
261
+ HotelMatching = 16384
262
+ GeographicalInformationAsText = 32768
263
+ HotelReviews = 65536 # different name in GO
264
+ DetailedHotelFacilities = 131072
265
+ SalesPoint = 524288
266
+ LtsSpecificParameters = 262144
267
+ CouponServiceData = 16777216
268
+ CheckInOut = 1048576
269
+ SourceData = 2097152
270
+ AccountData = 67108864
271
+ RoomTypes = 134217728
272
+ # Payment provider missing?
273
+
274
+
275
+ class OfferDetails(IntFlag):
276
+ BasicInfo = 1
277
+ RoomCode = 4
278
+ RoomTitle = 8
279
+ PriceDetails = 16
280
+ RoomImages = 32
281
+ RoomFacilitiesFilter = 64
282
+ RoomDescription = 256
283
+ IncludedServices = 1024
284
+ AdditionalServices = 2048
285
+ RoomFacilitiesDetails = 4096
286
+ PriceImages = 8192
287
+ Themes = 16384
288
+ RoomFeatures = 32768
289
+ CancelPolicies = 262144
290
+ PaymentTerms = 1048576
291
+
292
+
293
+ class RoomDetails(IntFlag):
294
+ BasicInfo = 4
295
+ Title = 8
296
+ RoomImages = 32
297
+ RoomFacilitiesFilter = 64
298
+ RoomDescription = 256
299
+ RoomFacilitiesDetails = 4096
300
+ RoomFeatures = 32768
301
+ RoomNumbers = 65536
302
+
303
+
304
+ class SpecialDetails(IntFlag):
305
+ BasicInfo = 1
306
+ Title = 2
307
+ Descriptions = 4
308
+ Seasons = 8
309
+ Images = 16
310
+ Themes = 32
311
+ IncludedServices = 64
312
+
313
+
314
+ @dataclass
315
+ class Order(BaseType):
316
+ dir: Direction | None = field(default=None)
317
+ field: Field | None = field(default=None)
318
+
319
+ def __post_init__(self):
320
+ super().__init__("order")
321
+
322
+
323
+ class Field(Enum):
324
+ ValidityStart = "valid_start"
325
+ ValidityEnd = "valid_end"
326
+ Random = "rand"
327
+ Stars = "stars"
328
+ Name = "name"
329
+
330
+
331
+ class Direction(Enum):
332
+ Ascending = "asc"
333
+ Descending = "desc"
334
+
335
+
336
+ @dataclass
337
+ class Data(BaseType):
338
+ guest: Guest | None = field(default=None)
339
+ company: Company | None = field(default=None)
340
+ payment: Payment | None = field(default=None)
341
+ note: str | None = field(default=None)
342
+ details: Details | None = field(default=None)
343
+ form: Form | None = field(default=None)
344
+ tracking: Tracking | None = field(default=None)
345
+ # insurance: Ins
346
+ storno_reason: StornoReason | None = field(default=None)
347
+ storno_reason_text: str | None = field(default=None)
348
+
349
+ special: list | None = field(default=None) # list of specials
350
+
351
+ def __post_init__(self):
352
+ super().__init__("data")
353
+
354
+
355
+ @dataclass
356
+ class Special(BaseType):
357
+ offer_id: int
358
+ action: Literal["update", "delete"]
359
+ time_modified: str # ISO 8601 Date
360
+ reset_participants: Literal[0, 1] | None = field(default=None)
361
+
362
+ def __post_init__(self):
363
+ super().__init__("special")
364
+
365
+
366
+ class StornoReason(Enum):
367
+ Unknown = 0
368
+ GuestNotAvailable = 1
369
+ AccomodationRequestedToCancel = 2
370
+ GuestAnotherDestination = 3
371
+ GuestAnotherAccomodation = 4
372
+ Other = 99
373
+
374
+
375
+ @dataclass
376
+ class Logging(BaseType):
377
+ step: str | None = field(default=None)
378
+
379
+ def __post_init__(self):
380
+ super().__init__("logging")
381
+
382
+
383
+ class Step(Enum):
384
+ Search = "search"
385
+ SearchOffer = "search_offer"
386
+ SearchDetails = "search_details"
387
+ View = "view"
388
+ ViewOffer = "view_offer"
389
+ Step1 = "step_1"
390
+ Step2 = "step_2"
391
+ Step3 = "step_3"
392
+ Step4 = "step_4"
393
+
394
+
395
+ @dataclass
396
+ class SearchHotel(BaseType):
397
+ name: str | None = field(default=None)
398
+ type: HotelType | None = field(default=None)
399
+ stars: Stars | None = field(default=None)
400
+ feature: HotelFeature | None = field(default=None)
401
+ theme: HotelTheme | None = field(default=None)
402
+
403
+ def __post_init__(self):
404
+ super().__init__("search_hotel")
405
+
406
+
407
+ class HotelType(Enum):
408
+ Hotel = "1"
409
+ SkiSchool = "2"
410
+ Residence = "4"
411
+ Appartment = "16"
412
+ FarmVacation = "32"
413
+ MountainInn = "64"
414
+ CampingSite = "128"
415
+ HolidayHome = "256"
416
+ YouthHostel = "512"
417
+ Guesthouse = "1024"
418
+ Refuge = "2048"
419
+ Garni = "4096"
420
+ Inn = "8192"
421
+
422
+
423
+ class HotelFeature(Enum):
424
+ Garage = "1"
425
+ Elevator = "2"
426
+ Restaurant = "4"
427
+ Gym = "8"
428
+ Wellness = "16"
429
+ Spa = "32"
430
+ Breakfast = "64"
431
+ Buffet = "128"
432
+ OutdoorPool = "256"
433
+ IndoorPool = "512"
434
+ Bar = "1024"
435
+ BarrierFree = "2048"
436
+ Wlan = "4096"
437
+ ShuttleService = "8192"
438
+ Childcare = "16384"
439
+ SmallPetsAllowed = "32768"
440
+ BeautyFarm = "65536"
441
+ CentralLocation = "262144"
442
+ CoveredParking = "524288"
443
+ OpenParking = "1048576"
444
+ Massages = "2097152"
445
+ Sauna = "4194304"
446
+ SteamBath = "8388608"
447
+ PublicBar = "16777216"
448
+ DogsAllowed = "33554432"
449
+
450
+
451
+ class HotelTheme(Enum):
452
+ Family = "1"
453
+ Wellness = "2"
454
+ Hiking = "4"
455
+ Motorcycle = "8"
456
+ Bike = "16"
457
+ Golf = "32"
458
+ Riding = "64"
459
+ Romantic = "128"
460
+ Ski = "256"
461
+ Meeting = "512"
462
+ CrossCountrySkiing = "1024"
463
+ Culture = "2048"
464
+ Snowshoeing = "4096"
465
+
466
+
467
+ @dataclass
468
+ class SearchLocation(BaseType):
469
+ location: list | None = field(default=None) # number[], Location ID
470
+ location_lts: list | None = field(default=None) # string[], LTS Location RID
471
+
472
+ def __post_init__(self):
473
+ super().__init__("search_location")
474
+
475
+
476
+ @dataclass
477
+ class SearchDistance:
478
+ latitude: float # number
479
+ longitude: float # number
480
+ radius: int # number
481
+
482
+
483
+ @dataclass
484
+ class SearchOffer(BaseType):
485
+ arrival: str | None = field(default=None) # Date YYYY-MM-DD
486
+ departure: str | None = field(default=None) # Date YYYY-MM-DD
487
+ service: Board | None = field(default=None)
488
+ room: list | None = field(default=None) # Room[]
489
+ feature: RoomFeature | None = field(default=None)
490
+ channel_id: list | None = field(default=None) # string[]
491
+ typ: SearchOfferType | None = field(default=None)
492
+ rateplan: Rateplan | None = field(default=None)
493
+
494
+ # features from easychange missing?
495
+
496
+ def __post_init__(self):
497
+ super().__init__("search_offer")
498
+
499
+
500
+ class SearchOfferType(Enum):
501
+ DefaultPricelist = "10"
502
+ PeopleAge = "20"
503
+ PeopleNumber = "21"
504
+ Staying = "22"
505
+ BookingDate = "23"
506
+ Weekday = "24"
507
+ NoReference = "25"
508
+ SpecialPeopleAge = "50"
509
+ SpecialPeopleNumber = "51"
510
+ SpecialStaying = "52"
511
+ SpecialBookingDate = "53"
512
+ SpecialWeekday = "54"
513
+ SpecialNoReference = "55"
514
+
515
+
516
+ class Board(Enum):
517
+ Without = 1
518
+ Breakfast = 2
519
+ HalfBoard = 3
520
+ FullBoard = 4
521
+ AllInclusive = 5
522
+
523
+
524
+ class RoomFeature(IntFlag):
525
+ Balcony = 1
526
+ Terrace = 2
527
+ MiniBar = 4
528
+ Safe = 8
529
+ TV = 16
530
+ Satellite = 32
531
+ Wlan = 64
532
+ Internet = 128
533
+ BarrierFree = 512
534
+
535
+
536
+ @dataclass
537
+ class SearchLts(BaseType):
538
+ A0Ene: int | None = field(default=None) # number
539
+ A0MTV: int | None = field(default=None) # number
540
+ A0Rep: int | None = field(default=None) # number
541
+
542
+ def __post_init__(self):
543
+ super().__init__("search_lts")
544
+
545
+
546
+ @dataclass
547
+ class SearchSpecial(BaseType):
548
+ offer_id: list | None = field(default=None) # number[]
549
+ date_from: str | None = field(default=None) # Date YYYY-MM-DD
550
+ date_to: str | None = field(default=None) # Date YYYY-MM-DD
551
+ theme: list | None = field(default=None) # array of SpecialTheme
552
+ validity: Validity | None = field(default=None)
553
+ typ: SearchSpecialType | None = field(default=None)
554
+ premium: SearchSpecialPremium | None = field(default=None)
555
+
556
+ poi_id: list | None = field(default=None)
557
+ poi_cat: list | None = field(default=None)
558
+ status: Literal[0, 1] | None = field(default=None)
559
+
560
+ def __post_init__(self):
561
+ super().__init__("search_special")
562
+
563
+
564
+ class SearchSpecialType(Enum):
565
+ PriceLists = 0
566
+ Packages = 1
567
+ Specials = 2
568
+ ShortLongStays = 4
569
+
570
+
571
+ class SearchSpecialPremium(IntFlag):
572
+ FamilyHotelsPremium = 2
573
+ VinumHotelsPremium = 4
574
+ SüdtirolBalancePremium = 8
575
+ VitalpinaDurchatmen = 16
576
+ VitalpinaWohlfühlen = 32
577
+ VitalpinaErnährung = 64
578
+ VitalpinaAktiv = 128
579
+ VitalpinaPremium = 256
580
+ BikehotelsMountainbike = 512
581
+ BikehotelsBikeTouringEBike = 1024
582
+ BikehotelsRoadbike = 2048
583
+ BikehotelsPremium = 4096
584
+ ArchitectureDays = 8192
585
+ VinumHotels = 16384
586
+ FamilyHotels = 32768
587
+ FamilyHotelsNatureDetective = 65536
588
+ FamilyHotelsNatureDetectiveWinter = 131072
589
+
590
+
591
+ class SpecialTheme(Enum):
592
+ ThemeIDHiking = 1
593
+ ThemeIDCyclingMountainbike = 2
594
+ ThemeIDFamily = 3
595
+ ThemeIDWellnessHealth = 4
596
+ ThemeIDFoodAndDrink = 5
597
+ ThemeIDGolf = 6
598
+ ThemeIDCulture = 7
599
+ ThemeIDMotorsport = 8
600
+ ThemeIDCarFreeHolidays = 9
601
+ ThemeIDSkiSnowboard = 10
602
+ ThemeIDSummerActivities = 11
603
+ ThemeIDEvents = 12
604
+ ThemeIDChristmasMarkets = 13
605
+ ThemeIDActiveWinter = 14
606
+ ThemeIDVitalpina = 15
607
+ ThemeIDVitalpinaBreathe = 16
608
+ ThemeIDBikeHotelsEBike = 17
609
+ ThemeIDBikeHotelsFreeride = 18
610
+ ThemeIDBikeHotelsMountainbike = 20
611
+ ThemeIDBikeHotelsBikeTours = 21
612
+ ThemeIDBikeHotelsRacingBike = 22
613
+ ThemeIDFamilyHotels = 23
614
+ ThemeIDFamilyHotelsNatureDetective = 24
615
+ ThemeIDFamilyHotel = 26
616
+ ThemeIDNatureDetectivSummer = 27
617
+ ThemeIDNatureDetectivWinter = 28
618
+ ThemeIDEcologicHoliday = 79
619
+ ThemeIDHorseBackRiding = 80
620
+ ThemeIDLuxuryHoliday = 81
621
+ ThemeIDPetsFriendlyHoliday = 82
622
+ ThemeIDRoadBike = 83
623
+ ThemeIDRomanticHoliday = 84
624
+ ThemeIDWine = 85
625
+ ThemeIDBicycleTouring = 86
626
+ ThemeIDEBike = 87
627
+
628
+ ## These values were theme_bit
629
+ # Hiking = 1
630
+ # Cycling = 2
631
+ # Family = 4
632
+ # Wellness = 8
633
+ # Food = 16
634
+ # Golf = 32
635
+ # Culture = 64
636
+ # Motorsport = 128
637
+ # CarFree = 256
638
+ # SkiSnowboard = 512
639
+ # SummerActivities = 1024
640
+ # Events = 2048
641
+ # ChristmasMarkets = 4096
642
+ # ActiveWinter = 8192
643
+ # Vitalpina = 16384
644
+ # VitalpinaBreathe = 32768
645
+ # BikeHotelsEBike = 65536
646
+ # BikeHotelsFreeride = 131072
647
+ # BikeHotelsMountainbike = 524288
648
+ # BikeHotelsBikeTours = 1048576
649
+ # BikeHotelsRacingBike = 2097152
650
+ # FamilyHotels = 4194304
651
+ # FamilyHotelsNatureDetective = 8388608
652
+ # FamilyHotel = 33554432
653
+ # FamilyHotelsNatureDetectiveSummer = 67108864
654
+ # FamilyHotelsNatureDetectiveWinter = 134217728
655
+
656
+
657
+ @dataclass
658
+ class SearchAvailability(BaseType):
659
+ date_from: str # Date YYYY-MM-DD
660
+ date_to: str # Date YYYY-MM-DD
661
+ offer_id: list | None = field(default=None) # number[]
662
+ room_id: list | None = field(default=None) # number[]
663
+ duration: int | None = field(default=None)
664
+ room: Room | None = field(default=None)
665
+
666
+ def __post_init__(self):
667
+ super().__init__("search_availability")
668
+
669
+
670
+ @dataclass
671
+ class SearchPriceList(BaseType):
672
+ date_from: str | None = field(default=None) # Date YYYY-MM-DD
673
+ date_to: str | None = field(default=None) # Date YYYY-MM-DD
674
+ service: Board | None = field(default=None)
675
+ room_id: list | None = field(default=None) # number[]
676
+ typ: SearchSpecialType | None = field(default=None)
677
+
678
+ def __post_init__(self):
679
+ super().__init__("search_pricelist")
680
+
681
+
682
+ @dataclass
683
+ class Guest(BaseType):
684
+ firstname: str
685
+ lastname: str
686
+ email: str
687
+ gender: str | None = field(default=None)
688
+ prefix: str | None = field(default=None)
689
+ phone: str | None = field(default=None)
690
+ address: Address | None = field(default=None)
691
+ newsletter: Literal[0, 1] | None = field(default=None) # 0 | 1
692
+
693
+ def __post_init__(self):
694
+ super().__init__("guest")
695
+
696
+
697
+ @dataclass
698
+ class Company(BaseType):
699
+ name: str | None = field(default=None)
700
+ taxnumber: str | None = field(default=None)
701
+ recipient_code: str | None = field(default=None)
702
+ address: Address | None = field(default=None)
703
+
704
+ def __post_init__(self):
705
+ super().__init__("company")
706
+
707
+
708
+ @dataclass
709
+ class Payment(BaseType):
710
+ method: PaymentMethod | None = field(default=None)
711
+ invoice: Literal[0, 1] | None = field(default=None) # 0 | 1
712
+
713
+ def __post_init__(self):
714
+ super().__init__("payment")
715
+
716
+
717
+ class PaymentMethod(IntFlag):
718
+ CreditCardDeposit = 1
719
+ CreditCardAsSecurity = 2
720
+ CreditCardPayment = 8
721
+ BankTransferDeposit = 4
722
+ BankTransferPayment = 16
723
+ AccommodationPayment = 32
724
+
725
+
726
+ @dataclass
727
+ class Details(BaseType):
728
+ extr_price: list | None = field(default=None) # ExtraPrice[]
729
+
730
+ def __post_init__(self):
731
+ super().__init__("Details")
732
+
733
+
734
+ @dataclass
735
+ class ExtraPrice(BaseType):
736
+ price_id: int | None = field(default=None) # number
737
+ price_amount: int | None = field(default=None) # number
738
+
739
+ def __post_init__(self):
740
+ super().__init__("extra_price")
741
+
742
+
743
+ @dataclass
744
+ class Form(BaseType):
745
+ url_success: str | None = field(default=None)
746
+ url_failure: str | None = field(default=None)
747
+
748
+ def __post_init__(self):
749
+ super().__init__("form")
750
+
751
+
752
+ @dataclass
753
+ class Tracking(BaseType):
754
+ partner: str | None = field(default=None)
755
+ media: str | None = field(default=None)
756
+ campaign: str | None = field(default=None)
757
+ companyinfo: str | None = field(default=None)
758
+
759
+ def __post_init__(self):
760
+ super().__init__("tracking")
761
+
762
+
763
+ @dataclass
764
+ class Stars(BaseType):
765
+ min: Literal[1, 2, 3, 3.5, 4, 4.5, 5] | None = field(
766
+ default=None
767
+ ) # number, min can be 1, values are 1, 2, 3, 3.5, 4, 4.5, 5
768
+ max: Literal[1, 2, 3, 3.5, 4, 4.5, 5] | None = field(
769
+ default=None
770
+ ) # number, max can be 5
771
+
772
+ def __post_init__(self):
773
+ super().__init__("stars")
774
+
775
+
776
+ @dataclass
777
+ class Address(BaseType):
778
+ street: str | None = field(default=None)
779
+ zipcode: str | None = field(default=None)
780
+ city: str | None = field(default=None)
781
+ country: str | None = field(default=None)
782
+
783
+ def __post_init__(self):
784
+ super().__init__("address")
785
+
786
+
787
+ @dataclass
788
+ class Room(BaseType):
789
+ room_seq: int | None = field(default=None) # number
790
+ person: list | None = field(
791
+ default=None
792
+ ) # number[], 1..n Persons and their age (store only age inside tags)
793
+ offer_id: int | None = field(default=None) # number
794
+ room_id: int | None = field(default=None) # number
795
+ service: Board | None = field(default=None)
796
+ room_type: RoomType | None = field(default=None)
797
+
798
+ def __post_init__(self):
799
+ super().__init__("room")
800
+
801
+
802
+ class RoomType(Enum):
803
+ All = 0
804
+ Room = 1
805
+ Apartment = 2
806
+ Campsite = 4
807
+
808
+
809
+ @dataclass
810
+ class Rateplan(BaseType):
811
+ code: str # | None = field(default=None)
812
+ source: str # | None = field(default=None)
813
+
814
+ def __post_init__(self):
815
+ super().__init__("rateplan")
816
+
817
+
818
+ @dataclass
819
+ class Validity(BaseType):
820
+ valid: Literal[0, 1] # 0 | 1
821
+ offers: Literal[0, 1] | None = field(default=None) # 0 | 1
822
+ arrival: str | None = field(default=None) # Date YYYY-MM-DD
823
+ departure: str | None = field(default=None) # Date YYYY-MM-DD
824
+ service: Board | None = field(default=None)
825
+ room: list | None = field(default=None) # Room[]
826
+
827
+ def __post_init__(self):
828
+ super().__init__("validity")
829
+
830
+
831
+ def getHotelList_base_xml(root: Root, header: Header):
832
+ xml = '<?xml version="1.0" encoding="utf-8"?>'
833
+ xml += root.version
834
+ xml += header.credentials
835
+ return xml
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mss-python
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: MSS API wrapper for Python projects
5
5
  Home-page: https://github.com/bookingsuedtirol/mss-python
6
6
  Requires-Python: >=3.7, <4
@@ -0,0 +1,11 @@
1
+ mss_python/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ mss_python/client.py,sha256=DEMAGATE08MW42Ldtepk95RV6funmyyuFbN9XFhiwPk,3202
3
+ mss_python/request/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ mss_python/request/method_names.py,sha256=DP22Tra-F86FP-6771q7bDwuX6ojPCidFPzztl4UThI,1030
5
+ mss_python/request/methods.py,sha256=pu_U6z8ANktuv0r3P6MmLKW66fwRhiDCp261vNxqrYE,664
6
+ mss_python/request/types_mss.py,sha256=Xq9YYA5-f_j2Gt7bCcHSC6qEzHs0hswpoXm2IGB9SdA,22354
7
+ mss_python-0.1.3.dist-info/LICENSE,sha256=SxLvAIuAvi2G5v6pRCopG3gAH9SLL_-xACJ0QBz_Blw,1074
8
+ mss_python-0.1.3.dist-info/METADATA,sha256=QHC1KmbqxFdqD8QY-9HHEkc692274XYUHh-AjhUQ1X0,1642
9
+ mss_python-0.1.3.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
10
+ mss_python-0.1.3.dist-info/top_level.txt,sha256=-5Wm4NpowVgq9ImH61vhAiBQFoE21fg4r-7Pn7mOYyE,11
11
+ mss_python-0.1.3.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ mss_python
@@ -1,5 +0,0 @@
1
- mss_python-0.1.2.dist-info/LICENSE,sha256=SxLvAIuAvi2G5v6pRCopG3gAH9SLL_-xACJ0QBz_Blw,1074
2
- mss_python-0.1.2.dist-info/METADATA,sha256=i7grL02p2I8iduVTeiEgedCI6Txz-3FPNISkziOZBzg,1642
3
- mss_python-0.1.2.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
4
- mss_python-0.1.2.dist-info/top_level.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
5
- mss_python-0.1.2.dist-info/RECORD,,
@@ -1 +0,0 @@
1
-