mss-python 0.1.2__tar.gz → 0.1.5__tar.gz

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.
@@ -0,0 +1,3 @@
1
+ [flake8]
2
+ exclude = simple # Exclude for the moment
3
+ ignore = E501,E265,E266,E402
@@ -0,0 +1,46 @@
1
+ # This workflow will upload a Python Package using Twine when a release is created
2
+ # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
3
+
4
+ # This workflow uses actions that are not certified by GitHub.
5
+ # They are provided by a third-party and are governed by
6
+ # separate terms of service, privacy policy, and support
7
+ # documentation.
8
+
9
+ name: Upload Python Package
10
+
11
+ on:
12
+ release:
13
+ types: [published]
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ deploy:
20
+ runs-on: ubuntu-latest
21
+
22
+ permissions:
23
+ id-token: write
24
+
25
+ environment:
26
+ name: pypi
27
+ url: https://pypi.org/p/mss-python
28
+
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - name: Set up Python
32
+ uses: actions/setup-python@v3
33
+ with:
34
+ python-version: "3.x"
35
+ - name: Install dependencies
36
+ run: |
37
+ python -m pip install --upgrade pip
38
+ pip install build
39
+ - name: Install dependencies
40
+ run: |
41
+ python -m pip install --upgrade pip
42
+ pip install -r requirements.txt
43
+ - name: Build package
44
+ run: python -m build
45
+ - name: Publish package
46
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ XML-samples
2
+ .env
3
+ __pycache__
4
+ src/mss_python.egg-info/
5
+ setup.cfg
6
+ .DS_Store
7
+ dist/
8
+ _version.py
@@ -0,0 +1,4 @@
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "python.analysis.typeCheckingMode": "off"
4
+ }
@@ -0,0 +1,99 @@
1
+ Metadata-Version: 2.1
2
+ Name: mss-python
3
+ Version: 0.1.5
4
+ Summary: MSS API wrapper for Python projects
5
+ Author-email: HGV <development@hgv.it>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 Booking Südtirol
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Repository, https://github.com/bookingsuedtirol/mss-python
29
+ Requires-Python: <4,>=3.7
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+
33
+ # mss-python
34
+
35
+ MSS API client for Python projects
36
+
37
+ <!-- ## Dependencies
38
+
39
+ This library requires an IANA Time Zone database to be present on the operating system (because it uses https://pkg.go.dev/time#LoadLocation). This database comes pre-installed with many Linux distros. If it’s unavailable (such as on Windows), https://pkg.go.dev/time/tzdata can be imported in the main program (which uses mss-go) instead. -->
40
+
41
+ ## Available methods
42
+
43
+ - [x] getHotelList
44
+ - [x] getHotelListByFilter
45
+ - [x] getSpecialList
46
+ - [x] getRoomList
47
+ - [x] getWidgetConfig
48
+ - [x] getBooking
49
+ - [x] cancelBooking
50
+ - [x] prepareBooking
51
+ - [x] getAvailability
52
+ - [x] getRoomAvailability
53
+ - [x] getDayAvailability
54
+ - [x] getPriceList
55
+ - [x] createInquiry
56
+ - [x] getLocationList
57
+ - [x] getThemeList
58
+ - [x] getMasterpackagesList
59
+ - [x] notifyMasterpackages
60
+ - [x] getLastMinuteQuotation
61
+ - [x] validateCoupon
62
+ - [x] getHotelPictures
63
+ - [x] getHotelPictureGroups
64
+ - [x] getOptionalServices
65
+
66
+ ## Example Execution
67
+
68
+ `pip install -r requirements.txt`
69
+
70
+ `python3 -m examples.simple.main`
71
+
72
+ <!-- -
73
+
74
+ TODO
75
+
76
+ - [x] getInquiry - how to find booking IDs?
77
+
78
+
79
+ - [x] getUserSources
80
+
81
+ -->
82
+
83
+ <!-- Warning: Only the methods with a ✓ next to them have been tested so far.
84
+
85
+ ## Before running examples/tests
86
+
87
+ Set the environment variables with:
88
+
89
+ ```Bash
90
+ export $(make env)
91
+ ```
92
+
93
+ ## Examples
94
+
95
+ Run `make simple` or `make advanced`
96
+
97
+ ## Tests
98
+
99
+ Run `make test` -->
@@ -8,20 +8,20 @@ This library requires an IANA Time Zone database to be present on the operating
8
8
 
9
9
  ## Available methods
10
10
 
11
- - [x] getHotelList
11
+ - [x] getHotelList
12
12
  - [x] getHotelListByFilter
13
- - [x] getSpecialList
13
+ - [x] getSpecialList
14
14
  - [x] getRoomList
15
- - [x] getWidgetConfig
15
+ - [x] getWidgetConfig
16
16
  - [x] getBooking
17
- - [x] cancelBooking
18
- - [x] prepareBooking
19
- - [x] getAvailability
17
+ - [x] cancelBooking
18
+ - [x] prepareBooking
19
+ - [x] getAvailability
20
20
  - [x] getRoomAvailability
21
21
  - [x] getDayAvailability
22
- - [x] getPriceList
22
+ - [x] getPriceList
23
23
  - [x] createInquiry
24
- - [x] getLocationList
24
+ - [x] getLocationList
25
25
  - [x] getThemeList
26
26
  - [x] getMasterpackagesList
27
27
  - [x] notifyMasterpackages
@@ -31,25 +31,20 @@ This library requires an IANA Time Zone database to be present on the operating
31
31
  - [x] getHotelPictureGroups
32
32
  - [x] getOptionalServices
33
33
 
34
-
35
34
  ## Example Execution
36
35
 
37
- `cd ./examples/simple`
38
-
39
- `python3 main.py`
40
-
41
- or
36
+ `pip install -r requirements.txt`
42
37
 
43
- `python3 -m main`
38
+ `python3 -m examples.simple.main`
44
39
 
45
- <!-- -
40
+ <!-- -
46
41
 
47
42
  TODO
48
43
 
49
44
  - [x] getInquiry - how to find booking IDs?
50
45
 
51
46
 
52
- - [x] getUserSources
47
+ - [x] getUserSources
53
48
 
54
49
  -->
55
50
 
@@ -0,0 +1,42 @@
1
+ from src.mss_python.client import Client
2
+ from src.mss_python.request.types_mss import (
3
+ Credentials,
4
+ Request,
5
+ Search,
6
+ Options,
7
+ HotelDetails,
8
+ )
9
+ from src.mss_python.request.methods import MethodName
10
+
11
+
12
+ if __name__ == "__main__":
13
+ credentials = Credentials(
14
+ "MSS_SERVICE_USERNAME",
15
+ "MSS_SERVICE_PASSWORD",
16
+ "MSS_SERVICE_SOURCE",
17
+ )
18
+ lang = "de"
19
+ client = Client(credentials, lang)
20
+
21
+ req = Request(
22
+ Search("de", id=["YOUR_HOTEL_ID_1", "YOUR_HOTEL_ID_2", "YOUR_HOTEL_ID_3"]),
23
+ Options(HotelDetails.BasicInfo | HotelDetails.Coordinates),
24
+ )
25
+
26
+ try:
27
+
28
+ resp = client.request(
29
+ "MSS_SERVICE_URL",
30
+ MethodName.GetHotelList,
31
+ req,
32
+ )
33
+
34
+ hotel = resp["result"]["hotel"][0]
35
+
36
+ print(type(hotel["name"]), hotel["name"])
37
+ print(type(hotel["stars"]), hotel["stars"])
38
+
39
+ print(type(hotel["geolocation"]["latitude"]), hotel["geolocation"]["latitude"])
40
+
41
+ except Exception as e:
42
+ print(e)
@@ -0,0 +1,23 @@
1
+ [build-system]
2
+ requires = ["setuptools>=64", "setuptools_scm>=8"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "mss-python"
7
+ description = "MSS API wrapper for Python projects"
8
+ readme = "README.md"
9
+ requires-python = ">=3.7, <4"
10
+ license = { file = "LICENSE" }
11
+ authors = [{ name = "HGV", email = "development@hgv.it" }]
12
+ dynamic = ["version"]
13
+
14
+ [project.urls]
15
+ Repository = "https://github.com/bookingsuedtirol/mss-python"
16
+
17
+ [tool.setuptools.packages.find]
18
+ where = ["src"]
19
+ include = ["mss_python"]
20
+ namespaces = false
21
+
22
+ [tool.setuptools_scm]
23
+ version_file = "src/mss_python/_version.py"
@@ -0,0 +1 @@
1
+ xmltodict==0.13.0
File without changes
@@ -0,0 +1,16 @@
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '0.1.5'
16
+ __version_tuple__ = version_tuple = (0, 1, 5)
@@ -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]]
@@ -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