├── .coveragerc
├── .github
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
├── SUPPORT.md
├── images
│ ├── dependencies.svg
│ └── support.svg
└── workflows
│ ├── build.yml
│ └── publish.yml
├── .gitignore
├── CHANGELOG.rst
├── LICENSE
├── MANIFEST.in
├── Makefile
├── README.rst
├── amadeus
├── __init__.py
├── airline
│ ├── __init__.py
│ └── _destinations.py
├── airport
│ ├── __init__.py
│ ├── _direct_destinations.py
│ ├── _predictions.py
│ └── predictions
│ │ ├── __init__.py
│ │ └── _on_time.py
├── amadeus.py
├── analytics
│ ├── __init__.py
│ └── _itinerary_price_metrics.py
├── booking
│ ├── __init__.py
│ ├── _flight_order.py
│ ├── _flight_orders.py
│ ├── _hotel_bookings.py
│ └── _hotel_orders.py
├── client
│ ├── __init__.py
│ ├── access_token.py
│ ├── decorator.py
│ ├── direction.py
│ ├── errors.py
│ ├── hotel.py
│ ├── location.py
│ ├── request.py
│ └── response.py
├── e_reputation
│ ├── __init__.py
│ └── _hotel_sentiments.py
├── media
│ └── _files.py
├── mixins
│ ├── __init__.py
│ ├── http.py
│ ├── pagination.py
│ ├── parser.py
│ └── validator.py
├── namespaces
│ ├── __init__.py
│ ├── _airline.py
│ ├── _airport.py
│ ├── _analytics.py
│ ├── _booking.py
│ ├── _e_reputation.py
│ ├── _ordering.py
│ ├── _reference_data.py
│ ├── _schedule.py
│ ├── _shopping.py
│ ├── _travel.py
│ └── core.py
├── ordering
│ ├── __init__.py
│ ├── _transfer_order.py
│ ├── _transfer_orders.py
│ └── transfer_orders
│ │ ├── __init__.py
│ │ ├── _transfers.py
│ │ └── transfers
│ │ ├── __init__.py
│ │ └── _cancellation.py
├── reference_data
│ ├── __init__.py
│ ├── _airlines.py
│ ├── _location.py
│ ├── _locations.py
│ ├── _recommended_locations.py
│ ├── _urls.py
│ ├── locations
│ │ ├── __init__.py
│ │ ├── _airports.py
│ │ ├── _cities.py
│ │ ├── _hotel.py
│ │ ├── _hotels.py
│ │ └── hotels
│ │ │ ├── __init__.py
│ │ │ ├── _by_city.py
│ │ │ ├── _by_geocode.py
│ │ │ └── _by_hotels.py
│ └── urls
│ │ ├── __init__.py
│ │ └── _checkin_links.py
├── schedule
│ ├── __init__.py
│ └── _flights.py
├── shopping
│ ├── __init__.py
│ ├── _activities.py
│ ├── _activity.py
│ ├── _availability.py
│ ├── _flight_dates.py
│ ├── _flight_destinations.py
│ ├── _flight_offers.py
│ ├── _flight_offers_search.py
│ ├── _hotel_offer_search.py
│ ├── _hotel_offers_search.py
│ ├── _seatmaps.py
│ ├── _transfer_offers.py
│ ├── activities
│ │ ├── __init__.py
│ │ └── _by_square.py
│ ├── availability
│ │ ├── __init__.py
│ │ └── _flight_availabilities.py
│ └── flight_offers
│ │ ├── __init__.py
│ │ ├── _prediction.py
│ │ ├── _pricing.py
│ │ └── _upselling.py
├── travel
│ ├── __init__.py
│ ├── _analytics.py
│ ├── _predictions.py
│ ├── analytics
│ │ ├── __init__.py
│ │ ├── _air_traffic.py
│ │ └── air_traffic
│ │ │ ├── __init__.py
│ │ │ ├── _booked.py
│ │ │ ├── _busiest_period.py
│ │ │ └── _traveled.py
│ └── predictions
│ │ ├── __init__.py
│ │ ├── _flight_delay.py
│ │ └── _trip_purpose.py
└── version.py
├── docs
├── _static
│ └── .gitkeep
├── conf.py
└── index.rst
├── requirements.txt
├── setup.cfg
├── setup.py
├── specs
├── client
│ ├── test_access_token.py
│ ├── test_request.py
│ └── test_response.py
├── mixins
│ ├── test_errors.py
│ ├── test_http.py
│ ├── test_pagination.py
│ ├── test_parser.py
│ └── test_validator.py
├── namespaces
│ └── test_namespaces.py
├── test_client.py
└── test_version.py
└── tox.ini
/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | source = ./amadeus
3 | branch = True
4 |
5 | [report]
6 | fail_under = 95
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at developer@amadeus.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Development and Testing
2 |
3 | To run the project locally, clone the repository, and then create a virtual environment and install the dependencies.
4 | ```sh
5 | git clone https://github.com/amadeus4dev/amadeus-python.git
6 | cd amadeus-python
7 | ```
8 |
9 | First, make sure your pyenv is initialized for each environment (`pyenv init `).
10 | If you want to have it loaded automatically, add the following to ~/.zshrc:
11 |
12 | ```sh
13 | eval "$(pyenv init -)"
14 | ```
15 |
16 | Second, ensure you have a version of every Python we support installed. Your versions may differ.
17 |
18 | ```sh
19 | pyenv install 3.8.0
20 | pyenv install 3.9.4
21 | pyenv install 3.10.3
22 | pyenv global 3.8.0 3.9.4 3.10.3
23 | ```
24 |
25 | Next ensure you create a virtual environment.
26 |
27 | ```sh
28 | virtualenv venv
29 | source venv/bin/activate
30 | pip install -r requirements.txt
31 | ```
32 |
33 | ### Running tests
34 |
35 | To run the tests against every supported Python version, use `tox`.
36 |
37 | ```sh
38 | tox
39 | ```
40 |
41 | Alternatively, to run tests just against a specific Python environment run:
42 |
43 | ```sh
44 | tox -e py
45 | ```
46 |
47 | In order to see the code coverage results, open the `index.html` file in the `htmlcov` folder.
48 |
49 | ### Using a library locally
50 |
51 | To install a library locally, use `pip` to install the library in editable mode.
52 |
53 | ```sh
54 | pip install -e .
55 | ```
56 |
57 | This will make the current code available for editing and using in live scripts, for example.
58 |
59 | ```py
60 | from amadeus import Client
61 | ```
62 |
63 | ### Releasing
64 |
65 | - [ ] Update the version in `amadeus/version.py` using semver rules
66 | - [ ] Update the `CHANGELOG.rst` with the new version
67 | - [ ] Push all changes and ensure all tests pass on GitHub Actions
68 | - [ ] Draft a new [release](https://github.com/amadeus4dev/amadeus-java/releases/new) by creating a tag and copying the description from the `CHANGELOG.rst`
69 |
70 | ## How to contribute to the Amadeus Python SDK
71 |
72 | #### **Did you find a bug?**
73 |
74 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/amadeus4dev/amadeus-python/issues).
75 |
76 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/amadeus4dev/amadeus-python/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring.
77 |
78 | #### **Did you write a patch that fixes a bug?**
79 |
80 | * Open a new GitHub pull request with the patch.
81 |
82 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
83 |
84 | #### **Do you intend to add a new feature or change an existing one?**
85 |
86 | * Suggest your change [in a new issue](https://github.com/amadeus4dev/amadeus-python/issues/new) and start writing code.
87 |
88 | * Make sure your new code does not break any tests and include new tests.
89 |
90 | * With good code comes good documentation. Try to copy the existing documentation and adapt it to your needs.
91 |
92 | * Close the issue or mark it as inactive if you decide to discontinue working on the code.
93 |
94 | #### **Do you have questions about the source code?**
95 |
96 | * Ask any question about how to use the library by [raising a new issue](https://github.com/amadeus4dev/amadeus-python/issues/new).
97 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 | [Describe the issue]
4 |
5 | ## Steps to Reproduce
6 |
7 | 1. [First step]
8 | 2. [Second step]
9 | 3. [and so on...]
10 |
11 | __Expected Behavior:__ [What you expect to happen]
12 |
13 | __Actual Behavior:__ [What actually happens]
14 |
15 | __Stable Behavior?__ [What percentage of the time does it reproduce?]
16 |
17 | ## Versions
18 |
19 | What version of Python/Pip are you running? What Operating System are you on?
20 |
21 | ## Checklist
22 |
23 | Please make sure you checked the following:
24 |
25 | * Which version of Python are you using?
26 | * Did you download the latest version of this package?
27 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Fixes #
2 |
3 | ## Changes for this pull request
4 |
5 | ## Checklist
6 |
7 | Remove this if you have done all of these:
8 |
9 | * Ensure all tests pass and linting
10 | * Add any changes to the README
11 | * Add any changes or new comments to SDK methods
12 | * Ensure this PR only changes what it is intended to change
13 |
--------------------------------------------------------------------------------
/.github/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Amadeus Support
2 |
3 | Our [developer support team](https://developers.amadeus.com/support) is here to
4 | help you. You can find us on
5 | [StackOverflow](https://stackoverflow.com/questions/tagged/amadeus) and
6 | [email](mailto:developers@amadeus.com).
7 |
--------------------------------------------------------------------------------
/.github/images/dependencies.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.github/images/support.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build
2 | on:
3 | push:
4 | branches: [ master ]
5 | pull_request:
6 | branches: [ master ]
7 | jobs:
8 | build:
9 | runs-on: ubuntu-24.04
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | python-version: ['3.8', '3.9', '3.10']
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Set up Python ${{ matrix.python-version }}
17 | uses: actions/setup-python@v2
18 | with:
19 | python-version: ${{ matrix.python-version }}
20 | - name: Install dependencies
21 | run: |
22 | python -m pip install --upgrade pip
23 | python -m pip install -r requirements.txt
24 | - name: Test with tox
25 | run: |
26 | tox -e py
27 | - name: Before deploy
28 | run: |
29 | pip install -e .
30 | pip install --upgrade setuptools
31 | make docs
32 | - name: Build binary wheel and a source tarball
33 | if: ${{ matrix.python-version == '3.10' }}
34 | run: python setup.py sdist
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: publish
2 | on:
3 | push:
4 | # Sequence of patterns matched against refs/tags
5 | tags:
6 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
7 | jobs:
8 | build:
9 | runs-on: Ubuntu-20.04
10 | strategy:
11 | fail-fast: false
12 | matrix:
13 | python-version: ['3.10']
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - name: Set up Python ${{ matrix.python-version }}
18 | uses: actions/setup-python@v2
19 | with:
20 | python-version: ${{ matrix.python-version }}
21 | - name: Install dependencies
22 | run: |
23 | python -m pip install --upgrade pip
24 | python -m pip install -r requirements.txt
25 | - name: Test with tox
26 | run: |
27 | tox -e py
28 | - name: Before deploy
29 | run: |
30 | pip install -e .
31 | pip install --upgrade setuptools
32 | make docs
33 | - name: Build binary wheel and a source tarball
34 | run: python setup.py sdist
35 | - name: Publish a Python distribution to PyPI
36 | uses: pypa/gh-action-pypi-publish@release/v1
37 | with:
38 | user: __token__
39 | password: ${{ secrets.PYPI_API_TOKEN }}
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.egg-info
2 | venv
3 | __pycache__
4 | test.py
5 | *.pyc
6 | .python-version
7 | .tox
8 | .pytest_cache
9 | htmlcov
10 | .coverage
11 | _docs
12 | dist
13 | build
14 | .vscode
15 | env
--------------------------------------------------------------------------------
/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | Changelog
2 | =========
3 | 12.0.0 - 2025-04-11
4 | --------------------
5 | Decommissioned the Points of Interest and Location Score APIs
6 |
7 | Updated the Ubuntu 24.04 for GitHub Actions
8 |
9 | Big thanks to `Siddhartha Dutta `_ for his contribution to the above implementations!
10 |
11 | 11.0.0 - 2024-10-02
12 | --------------------
13 | Decommissioned the Trip Parser API
14 |
15 | 10.1.0 - 2024-06-20
16 | --------------------
17 | Add support for the `Hotel Booking v2 API `_. Big thanks to `Siddhartha Dutta `_ for his contribution
18 |
19 | 10.0.0 - 2024-04-17
20 | --------------------
21 | Decommissioned the Safe Place API
22 |
23 | Fixed the List Type Query Parameter for Hotel List API, Big thanks to `Siddhartha Dutta `_ for his contribution!
24 |
25 | Updated sphinx version from 3.4.1 to 5.0.0
26 |
27 | 9.0.0 - 2023-09-04
28 | --------------------
29 | Decommissioned the Travel Restrictions API v2
30 |
31 | 8.1.0 - 2023-06-22
32 | --------------------
33 | Add support for the `Transfer Search API `_
34 |
35 | Add support for the `Transfer Booking API `_
36 |
37 | Add support for the `Transfer Management API `_
38 |
39 | Big thanks to `Siddhartha Dutta `_ for his contribution to the above implementations!
40 |
41 | 8.0.0 - 2023-01-30
42 | --------------------
43 | Decommissioned Travel Restrictions API v1
44 |
45 | Decommissioned Hotel Search API v2
46 |
47 | Upgraded Python v3.8+ support and old dependencies
48 |
49 | Upgraded testing by using `pytest` and `mock`
50 |
51 | Fixed #175 Replace type() with instance()
52 |
53 | Fixed #177 Update the default value {} as an argument
54 |
55 | Minor updates in How to Release and running test in contribution guide
56 |
57 | 7.1.0 - 2022-11-04
58 | --------------------
59 | Add support for `Travel Restrictions v2 API `_
60 |
61 | Bug fix in pagination
62 |
63 | Add SonarCloud support
64 |
65 | 7.0.0 - 2022-07-20
66 | --------------------
67 | Add support for `Trip Parser v3 API `_ and remove Trip Parser v2
68 |
69 | Add support for `City Search API `_
70 |
71 | Add support for `Airline Routes API `_ and `Hotel Name Autocomplete API `_. Big thanks to `Siddhartha Dutta `_ for his contribution!
72 |
73 | Implement the coverage report generation at CI time
74 |
75 | 6.0.1 - 2022-05-23
76 | --------------------
77 | Removing all references to the unused media namespace
78 |
79 | 6.0.0 - 2022-05-23
80 | --------------------
81 | Add support for `Hotel List API `_
82 |
83 | Add support for `Hotel Search v3 `_
84 |
85 | Add support for the X-HTTP-Method-Override header
86 |
87 | Remove the AI-Generated Photos API
88 |
89 | 5.3.1 - 2022-02-24
90 | --------------------
91 | Update release workflow
92 |
93 | 5.3.0 - 2022-02-24
94 | --------------------
95 | Add support for `Travel Restrictions API `_
96 | Add support for `Airport Routes API `_
97 |
98 | 5.2.0 - 2021-11-29
99 | --------------------
100 | Migrate CI/CD to GitHub Actions
101 |
102 | 5.1.0 - 2021-05-12
103 | --------------------
104 | Add support for the `Flight Availabilities Search API `_
105 |
106 | Add support for the `Branded Fares Upsell API `_
107 |
108 | 5.0.0 - 2021-02-02
109 | --------------------
110 | Remove support for Python 2. The SDK requires Python 3.4+
111 |
112 | Fix unwanted exception on DELETE method of Flight Order Management API
113 |
114 | 4.5.0 - 2020-11-05
115 | --------------------
116 | Add support for the `Flight Price Analysis API `_
117 |
118 | 4.4.0 - 2020-10-09
119 | --------------------
120 | Add support for the `Tours and Activities API `_
121 |
122 | 4.3.0 - 2020-09-10
123 | --------------------
124 | Add support for the `On-Demand Flight Status API `_
125 |
126 | 4.2.0 - 2020-08-05
127 | --------------------
128 | Add support for the `Travel Recommendations API `_
129 |
130 | Moved the code examples directory to a dedicated `code examples repository `_
131 |
132 | 4.1.0 - 2020-06-11
133 | --------------------
134 | Add support for the `Safe Place API `_
135 |
136 | 4.0.0 - 2020-04-27
137 | --------------------
138 | Add support for the `Flight Choice Prediction v2 `_
139 |
140 | The input of Flight Choice Prediction v2 is the result of Flight Offers Search API - in v1 the input was the result of Flight Low-Fare Search
141 |
142 | Add support for the Retrieve (3rd) endpoint of `Points Of Interest API `_
143 |
144 | Remove support for Flight Choice Prediction v1
145 |
146 | Remove support for Flight Low-Fare Search: decommission on May 28, 2020 and mandatory migration to Flight Offers Search
147 |
148 | Remove support for Most Searched Destinations
149 |
150 | Add Trip Parser, Flight Create Orders and Flight Order Management executable examples
151 |
152 | 3.5.0 - 2020-02-13
153 | --------------------
154 | Add support for the `SeatMap Display `_
155 |
156 | SeatMap Display API allows you to get information to display airplane cabin plan from a Flight Offer in order for the traveler to be able to choose his seat during the flight booking flow thanks to POST method. In addition GET method allows you to display airplane cabin plan from an existing Flight Order.
157 |
158 | 3.4.0 - 2020-01-28
159 | --------------------
160 | Add support for the `Hotel Booking `_
161 |
162 | The Amadeus Hotel Booking API lets you complete bookings at over 150,000 hotels and accommodations around the world. To complete bookings, you must first use the Amadeus Hotel Search API to search for hotel deals, select the desired offer and confirm the final price and availability. You can then use the Hotel Booking API to complete the reservation by providing an offer id, guest information and payment information.
163 |
164 | Add support for the `Flight Order Management `_
165 |
166 | The Flight Order Management API lets you consult bookings created through the Flight Create Orders API. Using the booking ID generated by Flight Create Orders, Flight Order Management returns the last-updated version of the booking record with any post-booking modifications including but not limited to ticket information, form of payment or other remarks.
167 |
168 | Add support for the `Flight Create Orders `_
169 |
170 | The Flight Create Order API is a flight booking API that lets you perform the final booking for a desired flight and ancillary products (additional bags, extra legroom, etc.). The API returns a unique ID for the flight order and reservation details. This API is used to perform the final booking on confirmed fares returned by the Flight Offers Price API.
171 |
172 | Add support for the `Flight Offers Price `_
173 |
174 | The Flight Offers Price API confirms the flight price (including taxes and fees) and availability for a given flight returned by the Flight Offers Search API. The API also returns pricing for ancillary products (additional bags, extra legroom, etc.) and the payment information details needed for booking.
175 |
176 | Add support for the `Flight Offers Search `_
177 |
178 | The Flight Offers Search API is a flight search API that returns cheap flights between two airports for a given number of passengers and for a given date or date range. The API returns airline name, price and fare details, as well as additional information like baggage allowance, prices for additional baggage and departure terminal.
179 |
180 | Add support for the `Trip Parser `_
181 |
182 | The Trip Parser API parses information from various booking confirmation emails and returns a standardized, structured travel itinerary. The API can extract relevant information from a wide variety of flight, hotel, rental car and rail providers’ confirmation emails by first identifying the provider and then using a database of provider-specific email structures to determine which information to extract. The API then returns a link to the JSON structure of the itinerary.
183 |
184 | Add self-containing executable examples for the existing supported endpoints.
185 |
186 | 3.3.0 - 2019-12-04
187 | --------------------
188 | Add support for the `AI-Generated Photos`
189 |
190 | The AI-Generated Photos API returns a link to download a rendered image of a landscape. The image size is 512x512 pixels and the currently available image categories are BEACH and MOUNTAIN. The link to download the AI-generated picture is valid for 24 hours. This API is an experimental project created by the Amadeus AI Lab using the Nvidia StyleGAN framework. This API is free to use and we welcome any feedback you may have about improvements.
191 |
192 | Add support for the `Flight Delay Prediction `_
193 |
194 | The Flight Delay Prediction API returns the probability that a given flight will be delayed by four possible delay lengths: less than 30 minutes, 30-60 minutes, 60-120 minutes and over 120 minutes/cancellation. The API receives flight information and applies a machine-learning model trained with Amadeus historical data to determine the probability of flight delay.
195 |
196 | Release of the `Airport On-Time Performance `_
197 |
198 | The Airport On-Time Performance API returns the estimated percentage of on-time flight departures for a given airport and date. The API receives the 3-letter IATA airport code and departure date and applies a machine-learning model trained with Amadeus historical data to estimate the overall airport on-time performance. This API is in currently in beta and only returns accurate data for airports located in the U.S.
199 |
200 | 3.2.0 - 2019-11-07
201 | --------------------
202 | Add support for the `Trip Purpose Prediction API `_
203 |
204 | The Trip Purpose Prediction API returns the probability of whether a round-trip flight itinerary is for business or leisure travel. The API takes flight dates, departure city and arrival city and then applies a machine-learning model trained with Amadeus historical data to determine the probability that the itinerary is for business or leisure travel. This API is useful for gaining insight and optimizing the search and shopping experience.
205 |
206 | Add support for the `Hotel Ratings API `_
207 |
208 | The Hotel Ratings API provides hotel ratings based on automated sentiment analysis algorithm applied on the online reviews. Apart from an overall rating for a hotel also provides ratings for different categories of each (e.g.: staff, pool, internet, location). This provides a key content information for decision making during a shopping experience being able to compare how good a hotel is compared to others, sort hotels by ratings, filter by categories or recommend a hotel based on the trip context.
209 |
210 | Release of the `Flight Choice Prediction API `_
211 |
212 | The Flight Choice Prediction API allows developers to forecast traveler choices in the context of search & shopping. Exposing machine learning & AI services for travel, this API consumes the output of the Flight Low-fare Search API and returns augmented content with probabilities of choices for each flight offers.
213 |
214 | 3.1.0 - 2019-03-25
215 | --------------------
216 | Release of the `Points Of Interest API `_
217 |
218 | The Points Of Interest API, powered by AVUXI TopPlace, is a search API that returns a list of popular places for a particular location. The location can be defined as area bound by four coordinates or as a geographical coordinate with a radius. The popularity of a place or 'point of interest' is determined by AVUXI's proprietary algorithm that considers factors such as ratings, check-ins, category scores among other factors from a host of online media sources.
219 |
220 |
221 | 3.0.0 - 2019-01-22
222 | --------------------
223 | ** Hotel Search v2 has been deployed (Hotel Search v1 is now deprecated) **
224 |
225 | ** General **
226 | - Remove support of Hotel Search v1
227 | - URLs for all three endpoints have been simplified for ease-of-use and consistency
228 | ** Find Hotels - 1st endpoint **
229 | - The parameter `hotels` has been renamed to `hotelIds`
230 | ** View Hotel Rooms - 2nd endpoint **
231 | - Update from `amadeus.shopping.hotel('IALONCHO').hotel_offers.get` to `amadeus.shopping.hotel_offers_by_hotel.get(hotelId: 'IALONCHO')`
232 | - Now get all images in ‘View Hotels Rooms’ endpoint using the view parameter as `FULL_ALL_IMAGES`
233 | ** View Room Details - 3rd endpoint **
234 | - Updated from `amadeus.shopping.hotel('IALONCHO').offer('XXX').get` to `amadeus.shopping.hotel_offer('XXX').get`
235 | - Image category added under Media in the response
236 | - Hotel distance added in the response
237 | - Response now refers to the common HotelOffer object model
238 |
239 | 2.0.1 - 2019-01-17
240 | --------------------
241 |
242 | Fix pagination URL encoding parameters
243 |
244 | 2.0.0 - 2018-10-14
245 | --------------------
246 |
247 | `Flight Most Searched Destinations `_: Redesign of the API - Split the previous endpoint in 2 endpoints:
248 |
249 | - 1st endpoint to find the most searched destinations
250 | - 2nd endpoint to have more data about a dedicated origin & destination
251 |
252 | `Flight Most Booked Destinations `_:
253 |
254 | - Rename origin to originCityCode
255 |
256 | `Flight Most Traveled Destinations `_:
257 |
258 | - Rename origin in originCityCode
259 |
260 | `Flight Check-in Links `_:
261 |
262 | - Rename airline to airlineCode
263 |
264 | `Airport & City Search `_:
265 |
266 | - Remove parameter onlyMajor
267 |
268 | `Airport Nearest Relevant `_:
269 |
270 | - Add radius as parameter
271 |
272 | `Airline Code Lookup `_:
273 |
274 | - Regroup parameters *IATACode* and *ICAOCode* under the same name *airlineCodes*
275 |
276 | 1.1.0 - 2018-08-01
277 | --------------------
278 |
279 | Release 1.1.0
280 |
281 | 1.0.0 - 2018-04-20
282 | --------------------
283 |
284 | Release 1.0.0
285 |
286 | 1.0.0b8 - 2018-04-19
287 | --------------------
288 |
289 | Update namespace for `air_traffic/traveled` path.
290 |
291 | 1.0.0b7 - 2018-04-09
292 | --------------------
293 |
294 | Fix an issue where UTF8 was not properly decoded.
295 |
296 | 1.0.0b6 - 2018-04-05
297 | --------------------
298 |
299 | Set logging to silent by default
300 |
301 | 1.0.0b5 - 2018-04-05
302 | --------------------
303 |
304 | Adds easier to read error messages
305 |
306 | 1.0.0b4 - 2018-04-04
307 | --------------------
308 |
309 | Bug fix for install from PyPi
310 |
311 | 1.0.0b3 - 2018-04-05
312 | --------------------
313 |
314 | - Renamed back to “amadeus”
315 |
316 | 1.0.0b2 - 2018-04-05
317 | --------------------
318 |
319 | - Updated README for PyPi
320 |
321 | 1.0.0b1 - 2018-04-05
322 | --------------------
323 |
324 | - Initial Beta Release
325 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2018 Amadeus IT Group SA
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
5 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
7 | persons to whom the Software is furnished to do so, subject to the following conditions:
8 |
9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
10 | Software.
11 |
12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
13 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
15 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.rst LICENSE CHANGELOG.rst
2 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SOURCES=amadeus specs setup.py
2 | DOC_SOURCES=amadeus docs README.rst
3 |
4 | test:
5 | mamba --format=documentation --enable-coverage
6 |
7 | coverage:
8 | coverage html
9 | open htmlcov/index.html
10 |
11 | watch:
12 | make run
13 | make coverage
14 | fswatch -o ${SOURCES} | xargs -n1 -I{} make run
15 |
16 | run:
17 | make lint
18 | make test
19 | coverage html
20 |
21 | lint:
22 | flake8 $(SOURCES) --exit-zero
23 |
24 | docs:
25 | rm -rf _docs
26 | sphinx-build -b html docs _docs
27 |
28 | clean:
29 | rm -rf _docs build dist
30 |
31 | build:
32 | python setup.py sdist bdist_wheel
33 |
34 | watchdocs:
35 | make docs
36 | open _docs/index.html
37 | fswatch -o ${DOC_SOURCES} | xargs -n1 -I{} make docs
38 |
39 | .PHONY: test coverage watch run lint docs clean build
40 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | Amadeus Python SDK
2 | ==================
3 |
4 | |Module Version| |Build Status| |Maintainability| |Dependencies| |Discord|
5 |
6 |
7 | Amadeus provides a rich set of APIs for the travel industry. For more details, check out the `Amadeus for Developers Portal `__ or the `SDK class reference `__.
8 |
9 | Installation
10 | ------------
11 |
12 | This SDK requires Python 3.8+. You can install it directly with pip:
13 |
14 | .. code:: sh
15 |
16 | pip install amadeus
17 |
18 | OR, add it to your `requirements.txt` file and install using:
19 |
20 | .. code:: sh
21 |
22 | pip install -r requirements.txt
23 |
24 |
25 | Getting Started
26 | ---------------
27 |
28 | To make your first API call, you will need to `register `__ for an Amadeus Developer Account and `set up your first
29 | application `__.
30 |
31 | .. code:: py
32 |
33 | from amadeus import Client, ResponseError
34 |
35 | amadeus = Client(
36 | client_id='REPLACE_BY_YOUR_API_KEY',
37 | client_secret='REPLACE_BY_YOUR_API_SECRET'
38 | )
39 |
40 | try:
41 | response = amadeus.shopping.flight_offers_search.get(
42 | originLocationCode='MAD',
43 | destinationLocationCode='ATH',
44 | departureDate='2024-11-01',
45 | adults=1)
46 | print(response.data)
47 | except ResponseError as error:
48 | print(error)
49 |
50 | Examples
51 | --------------------------
52 | You can find all the endpoints in self-contained `code examples `_.
53 |
54 | Initialization
55 | --------------
56 |
57 | The client can be initialized directly.
58 |
59 | .. code:: py
60 |
61 | # Initialize using parameters
62 | amadeus = Client(client_id='REPLACE_BY_YOUR_API_KEY', client_secret='REPLACE_BY_YOUR_API_SECRET')
63 |
64 | Alternatively, it can be initialized without any parameters if the environment variables ``AMADEUS_CLIENT_ID`` and ``AMADEUS_CLIENT_SECRET`` are present.
65 |
66 | .. code:: py
67 |
68 | amadeus = Client()
69 |
70 | Your credentials can be found on the `Amadeus dashboard `__.
71 |
72 | By default, the SDK environment is set to ``test`` environment. To switch to a production (pay-as-you-go) environment, please switch the hostname as follows:
73 |
74 | .. code:: py
75 |
76 | amadeus = Client(hostname='production')
77 |
78 | Documentation
79 | -------------
80 |
81 | Amadeus has a large set of APIs, and our documentation is here to get you started today. Head over to our `reference documentation `__ for in-depth information about every SDK method, as well as its arguments and return types.
82 |
83 | - `Initialize the SDK `__
84 | - `Find an Airport `__
85 | - `Find a Flight `__
86 | - `Get Flight Inspiration `__
87 |
88 | Making API calls
89 | ----------------
90 |
91 | This library conveniently maps every API path to a similar path.
92 |
93 | For example, ``GET /v2/reference-data/urls/checkin-links?airlineCode=BA`` would be:
94 |
95 | .. code:: py
96 |
97 | amadeus.reference_data.urls.checkin_links.get(airlineCode='BA')
98 |
99 | Similarly, to select a resource by ID, you can pass in the ID to the singular path.
100 |
101 | For example, ``GET /v2/shopping/hotel-offers/XZY`` would be:
102 |
103 | .. code:: py
104 |
105 | amadeus.shopping.hotel_offer('XYZ').get()
106 |
107 | You can make any arbitrary API call directly with the ``.get`` method as well:
108 |
109 | .. code:: py
110 |
111 | amadeus.get('/v2/reference-data/urls/checkin-links', airlineCode='BA')
112 |
113 | Or, with ``POST`` method:
114 |
115 | .. code:: py
116 |
117 | amadeus.post('/v1/shopping/flight-offers/pricing', body)
118 |
119 | Response
120 | --------
121 |
122 | Every API call returns a ``Response`` object. If the API call contained a JSON response it will parse the JSON into the ``.result`` attribute. If this data also contains a ``data`` key, it will make that available as the ``.data`` attribute. The raw body of the response is always available as the ``.body`` attribute.
123 |
124 | .. code:: py
125 |
126 | from amadeus import Location
127 |
128 | response = amadeus.reference_data.locations.get(
129 | keyword='LON',
130 | subType=Location.ANY
131 | )
132 |
133 | print(response.body) #=> The raw response, as a string
134 | print(response.result) #=> The body parsed as JSON, if the result was parsable
135 | print(response.data) #=> The list of locations, extracted from the JSON
136 |
137 | Pagination
138 | ----------
139 |
140 | If an API endpoint supports pagination, the other pages are available under the ``.next``, ``.previous``, ``.last`` and ``.first`` methods.
141 |
142 | .. code:: py
143 |
144 | from amadeus import Location
145 |
146 | response = amadeus.reference_data.locations.get(
147 | keyword='LON',
148 | subType=Location.ANY
149 | )
150 |
151 | amadeus.next(response) #=> returns a new response for the next page
152 |
153 | If a page is not available, the method will return ``None``.
154 |
155 | Logging & Debugging
156 | -------------------
157 |
158 | The SDK makes it easy to add your own logger.
159 |
160 | .. code:: py
161 |
162 | import logging
163 |
164 | logger = logging.getLogger('your_logger')
165 | logger.setLevel(logging.DEBUG)
166 |
167 | amadeus = Client(
168 | client_id='REPLACE_BY_YOUR_API_KEY',
169 | client_secret='REPLACE_BY_YOUR_API_SECRET',
170 | logger=logger
171 | )
172 |
173 | Additionally, to enable more verbose logging, you can set the appropriate level on your own logger. The easiest way would be to enable debugging via a parameter during initialization, or using the ``AMADEUS_LOG_LEVEL`` environment variable.
174 |
175 | .. code:: py
176 |
177 | amadeus = Client(
178 | client_id='REPLACE_BY_YOUR_API_KEY',
179 | client_secret='REPLACE_BY_YOUR_API_SECRET',
180 | log_level='debug'
181 | )
182 |
183 | List of supported endpoints
184 | ---------------------------
185 |
186 | .. code:: py
187 |
188 | # Flight Inspiration Search
189 | amadeus.shopping.flight_destinations.get(origin='MAD')
190 |
191 | # Flight Cheapest Date Search
192 | amadeus.shopping.flight_dates.get(origin='MAD', destination='MUC')
193 |
194 | # Flight Offers Search GET
195 | amadeus.shopping.flight_offers_search.get(originLocationCode='SYD', destinationLocationCode='BKK', departureDate='2022-11-01', adults=1)
196 | # Flight Offers Search POST
197 | amadeus.shopping.flight_offers_search.post(body)
198 |
199 | # Flight Offers Price
200 | flights = amadeus.shopping.flight_offers_search.get(originLocationCode='SYD', destinationLocationCode='BKK', departureDate='2022-11-01', adults=1).data
201 | amadeus.shopping.flight_offers.pricing.post(flights[0])
202 | amadeus.shopping.flight_offers.pricing.post(flights[0:2], include='credit-card-fees,other-services')
203 |
204 | # Flight Create Orders
205 | amadeus.booking.flight_orders.post(flights[0], traveler)
206 |
207 | # Flight Order Management
208 | # The flight ID comes from the Flight Create Orders (in test environment it's temporary)
209 | # Retrieve the order based on it's ID
210 | flight_booking = amadeus.booking.flight_orders.post(body).data
211 | amadeus.booking.flight_order(flight_booking['id']).get()
212 | # Delete the order based on it's ID
213 | amadeus.booking.flight_order(flight_booking['id']).delete()
214 |
215 | # Flight SeatMap Display GET
216 | amadeus.shopping.seatmaps.get(**{"flight-orderId": "orderid"})
217 | # Flight SeatMap Display POST
218 | amadeus.shopping.seatmaps.post(body)
219 |
220 | # Flight Availabilities POST
221 | amadeus.shopping.availability.flight_availabilities.post(body)
222 |
223 | # Branded Fares Upsell
224 | amadeus.shopping.flight_offers.upselling.post(body)
225 |
226 | # Flight Choice Prediction
227 | body = amadeus.shopping.flight_offers_search.get(
228 | originLocationCode='MAD',
229 | destinationLocationCode='NYC',
230 | departureDate='2022-11-01',
231 | adults=1).result
232 | amadeus.shopping.flight_offers.prediction.post(body)
233 |
234 | # Flight Checkin Links
235 | amadeus.reference_data.urls.checkin_links.get(airlineCode='BA')
236 |
237 | # Airline Code Lookup
238 | amadeus.reference_data.airlines.get(airlineCodes='U2')
239 |
240 | # Airport and City Search (autocomplete)
241 | # Find all the cities and airports starting by 'LON'
242 | amadeus.reference_data.locations.get(keyword='LON', subType=Location.ANY)
243 | # Get a specific city or airport based on its id
244 | amadeus.reference_data.location('ALHR').get()
245 |
246 | # City Search
247 | amadeus.reference_data.locations.cities.get(keyword='PAR')
248 |
249 | # Airport Nearest Relevant Airport (for London)
250 | amadeus.reference_data.locations.airports.get(longitude=0.1278, latitude=51.5074)
251 |
252 | # Flight Most Booked Destinations
253 | amadeus.travel.analytics.air_traffic.booked.get(originCityCode='MAD', period='2017-08')
254 |
255 | # Flight Most Traveled Destinations
256 | amadeus.travel.analytics.air_traffic.traveled.get(originCityCode='MAD', period='2017-01')
257 |
258 | # Flight Busiest Travel Period
259 | amadeus.travel.analytics.air_traffic.busiest_period.get(cityCode='MAD', period='2017', direction='ARRIVING')
260 |
261 | # Hotel Search v3
262 | # Get list of available offers by hotel ids
263 | amadeus.shopping.hotel_offers_search.get(hotelIds='RTPAR001', adults='2')
264 | # Check conditions of a specific offer
265 | amadeus.shopping.hotel_offer_search('XXX').get()
266 |
267 | # Hotel List
268 | # Get list of hotels by hotel id
269 | amadeus.reference_data.locations.hotels.by_hotels.get(hotelIds='ADPAR001')
270 | # Get list of hotels by city code
271 | amadeus.reference_data.locations.hotels.by_city.get(cityCode='PAR')
272 | # Get list of hotels by a geocode
273 | amadeus.reference_data.locations.hotels.by_geocode.get(longitude=2.160873,latitude=41.397158)
274 |
275 | # Hotel Name Autocomplete
276 | amadeus.reference_data.locations.hotel.get(keyword='PARI', subType=[Hotel.HOTEL_GDS, Hotel.HOTEL_LEISURE])
277 |
278 | # Hotel Booking v2
279 | # The offerId comes from the hotel_offer above
280 | amadeus.booking.hotel_orders.post(
281 | guests=guests,
282 | travel_agent=travel_agent,
283 | room_associations=room_associations,
284 | payment=payment)
285 |
286 | # Hotel Booking v1
287 | # The offerId comes from the hotel_offer above
288 | amadeus.booking.hotel_bookings.post(offerId, guests, payments)
289 |
290 | # Hotel Ratings
291 | # What travelers think about this hotel?
292 | amadeus.e_reputation.hotel_sentiments.get(hotelIds = 'ADNYCCTB')
293 |
294 | # Trip Purpose Prediction
295 | amadeus.travel.predictions.trip_purpose.get(originLocationCode='ATH', destinationLocationCode='MAD', departureDate='2022-11-01', returnDate='2022-11-08')
296 |
297 | # Flight Delay Prediction
298 | amadeus.travel.predictions.flight_delay.get(originLocationCode='NCE', destinationLocationCode='IST', departureDate='2022-08-01', \
299 | departureTime='18:20:00', arrivalDate='2022-08-01', arrivalTime='22:15:00', aircraftCode='321', carrierCode='TK', flightNumber='1816', duration='PT31H10M')
300 |
301 | # Airport On-Time Performance
302 | amadeus.airport.predictions.on_time.get(airportCode='JFK', date='2022-11-01')
303 |
304 | # Airport Routes
305 | amadeus.airport.direct_destinations.get(departureAirportCode='BLR')
306 |
307 | # Travel Recommendations
308 | amadeus.reference_data.recommended_locations.get(cityCodes='PAR', travelerCountryCode='FR')
309 |
310 | # Retrieve status of a given flight
311 | amadeus.schedule.flights.get(carrierCode='AZ', flightNumber='319', scheduledDepartureDate='2022-09-13')
312 |
313 | # Tours and Activities
314 | # What are the popular activities in Madrid (based a geo location and a radius)
315 | amadeus.shopping.activities.get(latitude=40.41436995, longitude=-3.69170868)
316 | # What are the popular activities in Barcelona? (based on a square)
317 | amadeus.shopping.activities.by_square.get(north=41.397158, west=2.160873,
318 | south=41.394582, east=2.177181)
319 | # Returns a single activity from a given id
320 | amadeus.shopping.activity('4615').get()
321 |
322 | # Returns itinerary price metrics
323 | amadeus.analytics.itinerary_price_metrics.get(originIataCode='MAD', destinationIataCode='CDG',
324 | departureDate='2021-03-21')
325 |
326 | # Airline Routes
327 | amadeus.airline.destinations.get(airlineCode='BA')
328 |
329 | # Transfer Search
330 | amadeus.shopping.transfer_offers.post(body)
331 |
332 | # Transfer Booking
333 | amadeus.ordering.transfer_orders.post(body, offerId='1000000000')
334 |
335 | # Transfer Management
336 | amadeus.ordering.transfer_order('ABC').transfers.cancellation.post(body, confirmNbr=123)
337 |
338 | Development & Contributing
339 | --------------------------
340 |
341 | Want to contribute? Read our `Contributors
342 | Guide <.github/CONTRIBUTING.md>`__ for guidance on installing and
343 | running this code in a development environment.
344 |
345 | License
346 | -------
347 |
348 | This library is released under the `MIT License `__.
349 |
350 | Help
351 | ----
352 |
353 | You can find us on `StackOverflow `__ or join our developer community on `Discord `__.
354 |
355 | .. |Module Version| image:: https://badge.fury.io/py/amadeus.svg
356 | :target: https://pypi.org/project/amadeus/
357 | .. |Build Status| image:: https://github.com/amadeus4dev/amadeus-python/actions/workflows/build.yml/badge.svg
358 | :target: https://github.com/amadeus4dev/amadeus-python/actions/workflows/build.yml
359 | .. |Maintainability| image:: https://api.codeclimate.com/v1/badges/c2e19cf9628d6f4aece2/maintainability
360 | :target: https://codeclimate.com/github/amadeus4dev/amadeus-python/maintainability
361 | .. |Dependencies| image:: https://raw.githubusercontent.com/amadeus4dev/amadeus-python/master/.github/images/dependencies.svg?sanitize=true
362 | :target: https://badge.fury.io/py/amadeus
363 | .. |Discord| image:: https://img.shields.io/discord/696822960023011329?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2
364 | :target: https://discord.gg/cVrFBqx
365 |
--------------------------------------------------------------------------------
/amadeus/__init__.py:
--------------------------------------------------------------------------------
1 | from .amadeus import Client
2 | from .version import version
3 |
4 | from .client.location import Location
5 | from .client.hotel import Hotel
6 | from .client.direction import Direction
7 | from .client.request import Request
8 | from .client.response import Response
9 | from .client.errors import ResponseError
10 | from .client.errors import ParserError, ServerError, AuthenticationError
11 | from .client.errors import NotFoundError, ClientError, NetworkError
12 |
13 | __all__ = [
14 | 'Client', 'Location', 'Direction', 'version', 'ResponseError',
15 | 'ParserError', 'ServerError', 'AuthenticationError',
16 | 'NotFoundError', 'ClientError', 'Request', 'Response',
17 | 'NetworkError', 'Hotel'
18 | ]
19 |
--------------------------------------------------------------------------------
/amadeus/airline/__init__.py:
--------------------------------------------------------------------------------
1 | from ._destinations import Destinations
2 |
3 | __all__ = ['Destinations']
4 |
--------------------------------------------------------------------------------
/amadeus/airline/_destinations.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Destinations(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Get airline destinations.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.airline.destinations.get(airlineCode='BA')
12 |
13 | :param airlineCode: the airline code following IATA standard.
14 | Example: ``"BA"``
15 |
16 | :rtype: amadeus.Response
17 | :raises amadeus.ResponseError: if the request could not be completed
18 | '''
19 | return self.client.get('/v1/airline/destinations', **params)
20 |
--------------------------------------------------------------------------------
/amadeus/airport/__init__.py:
--------------------------------------------------------------------------------
1 | from ._predictions import AirportOnTime
2 | from ._direct_destinations import DirectDestinations
3 |
4 | __all__ = ['AirportOnTime', 'DirectDestinations']
5 |
--------------------------------------------------------------------------------
/amadeus/airport/_direct_destinations.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class DirectDestinations(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns airport direct routes.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.airport.direct_destinations.get(
12 | departureAirportCode='BLR')
13 |
14 | :param departureAirportCode: the departure Airport code following
15 | IATA standard. ``"BLR"``, for example for Bengaluru
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | return self.client.get('/v1/airport/direct-destinations', **params)
21 |
--------------------------------------------------------------------------------
/amadeus/airport/_predictions.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from .predictions import AirportOnTime
3 |
4 |
5 | class Predictions(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.on_time = AirportOnTime(client)
9 |
--------------------------------------------------------------------------------
/amadeus/airport/predictions/__init__.py:
--------------------------------------------------------------------------------
1 | from ._on_time import AirportOnTime
2 |
3 | __all__ = ['AirportOnTime']
4 |
--------------------------------------------------------------------------------
/amadeus/airport/predictions/_on_time.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class AirportOnTime(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns a percentage of on-time flight departures
8 |
9 | .. code-block:: python
10 |
11 | amadeus.airport.predictions.on_time.get(
12 | airportCode='JFK',
13 | date='2020-09-01')
14 |
15 | :param airportCode: the City/Airport IATA code from which
16 | the flight will depart. ``"NYC"``, for example for New York
17 |
18 | :param date: the date on which to fly out, in `YYYY-MM-DD` format
19 |
20 | :rtype: amadeus.Response
21 | :raises amadeus.ResponseError: if the request could not be completed
22 | '''
23 | return self.client.get('/v1/airport/predictions/on-time', **params)
24 |
--------------------------------------------------------------------------------
/amadeus/amadeus.py:
--------------------------------------------------------------------------------
1 | from .mixins.validator import Validator
2 | from .mixins.http import HTTP
3 | from .mixins.pagination import Pagination
4 | from .namespaces import Core as Namespaces
5 |
6 |
7 | class Client(Namespaces, Pagination, Validator, HTTP, object):
8 | '''
9 | The Amadeus client library for accessing
10 | the travel APIs.
11 | '''
12 |
13 | # The available hosts for this API
14 | HOSTS = {
15 | 'test': 'test.api.amadeus.com',
16 | 'production': 'api.amadeus.com'
17 | }
18 |
19 | # The initialization method for the entire module
20 | def __init__(self, **options):
21 | '''
22 | Initialize using your credentials:
23 |
24 | .. code-block:: python
25 |
26 |
27 | from amadeus import Client
28 |
29 | amadeus = Client(
30 | client_id='YOUR_CLIENT_ID',
31 | client_secret='YOUR_CLIENT_SECRET'
32 | )
33 |
34 | Alternatively, initialize the library using the environment variables
35 | ``AMADEUS_CLIENT_ID`` and ``AMADEUS_CLIENT_SECRET``.
36 |
37 | .. code-block:: python
38 |
39 |
40 | amadeus = amadeus.Client()
41 |
42 | :param client_id: the API key used to authenticate the API
43 | :paramtype client_id: str
44 |
45 | :param client_secret: the API secret used to authenticate the API
46 | :paramtype client_secret: str
47 |
48 | :param logger: (optional) a Python compatible logger
49 | (Default: ``logging.Logger``)
50 | :paramtype logger: logging.Logger
51 |
52 | :param log_level: (optional) the log level of the client, either
53 | ``"debug"``, ``"warn"``, or ``"silent"`` mode
54 | (Default: ``"silent"``)
55 | :paramtype log_level: str
56 |
57 | :param hostname: (optional) the name of the server API calls are made
58 | to, ``"production"`` or ``"test"``. (Default: ``"test"``)
59 | :paramtype hostname: str
60 |
61 | :param host: (optional) alternatively, you can specify a full host
62 | domain name instead, e.g. ``"api.example.com"``
63 | :paramtype host: str
64 |
65 | :param ssl: if this client is should use HTTPS (Default: ``True``)
66 | :paramtype ssl: bool
67 |
68 | :param port: the port this client should use (Default: ``80`` for HTTP
69 | and ``443`` for HTTPS)
70 | :paramtype port: int
71 |
72 | :param custom_app_id: (optional) a custom App ID to be passed in
73 | the User Agent to the server (Default: ``None``)
74 | :paramtype custom_app_id: str
75 |
76 | :param custom_app_version: (optional) a custom App Version number to
77 | be passed in the User Agent to the server (Default: ``None``)
78 | :paramtype custom_app_version: str
79 |
80 | :param http: (optional) a :func:`urllib.request.urlopen` compatible
81 | client that accepts a :class:`urllib.request.Request` compatible
82 | object (Default: ``urlopen``)
83 | :paramtype http: urllib.request.urlopen
84 |
85 | :raises ValueError: when a required param is missing
86 | '''
87 | self._initialize_client_credentials(options)
88 | self._initialize_logger(options)
89 | self._initialize_host(options)
90 | self._initialize_custom_app(options)
91 | self._initialize_http(options)
92 |
93 | recognized_options = ['client_id', 'client_secret', 'logger', 'host',
94 | 'hostname', 'custom_app_id',
95 | 'custom_app_version', 'http',
96 | 'log_level', 'ssl', 'port']
97 | self._warn_on_unrecognized_options(options, self.logger,
98 | recognized_options)
99 | Namespaces.__init__(self)
100 |
--------------------------------------------------------------------------------
/amadeus/analytics/__init__.py:
--------------------------------------------------------------------------------
1 | from ._itinerary_price_metrics import ItineraryPriceMetrics
2 |
3 | __all__ = ['ItineraryPriceMetrics']
4 |
--------------------------------------------------------------------------------
/amadeus/analytics/_itinerary_price_metrics.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class ItineraryPriceMetrics(Decorator, object):
5 |
6 | def get(self, **params):
7 | '''
8 | Returns itinerary price metrics by search criteria
9 |
10 | .. code-block:: python
11 |
12 | amadeus.analytics.itinerary_price_metrics.get(
13 | originIataCode='MAD',
14 | destinationIataCode='CDG',
15 | departureDate='2021-03-21')
16 |
17 | :param originIataCode: IATA code of the origin city, for
18 | example ``"BOS"`` for Boston.
19 |
20 | :param destinationIataCode: IATA code of the destination city,
21 | for example ``"ATH"`` for Athens.
22 |
23 | :param departureDate: scheduled departure date of the flight,
24 | local to the departure airport, format YYYY-MM-DD
25 |
26 | :rtype: amadeus.Response
27 | :raises amadeus.ResponseError: if the request could not be completed
28 | '''
29 | return self.client.get('/v1/analytics/itinerary-price-metrics', **params)
30 |
--------------------------------------------------------------------------------
/amadeus/booking/__init__.py:
--------------------------------------------------------------------------------
1 | from ._flight_orders import FlightOrders
2 | from ._flight_order import FlightOrder
3 | from ._hotel_bookings import HotelBookings
4 | from ._hotel_orders import HotelOrders
5 |
6 | __all__ = ['FlightOrders', 'FlightOrder', 'HotelBookings', 'HotelOrders']
7 |
--------------------------------------------------------------------------------
/amadeus/booking/_flight_order.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class FlightOrder(Decorator, object):
5 | def __init__(self, client, flight_order_id):
6 | Decorator.__init__(self, client)
7 | self.flight_order_id = flight_order_id
8 |
9 | def get(self, **params):
10 | '''
11 | Retrieves a flight order based on its ID.
12 |
13 | .. code-block:: python
14 |
15 | amadeus.booking.flight_order('eJzTd9f3NjIJdzUGAAp%2fAiY=').get()
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | return self.client.get('/v1/booking/flight-orders/{0}'
21 | .format(self.flight_order_id), **params)
22 |
23 | def delete(self, **params):
24 | '''
25 | Deletes a flight order based on its ID.
26 |
27 | .. code-block:: python
28 |
29 | amadeus.booking.flight_order('eJzTd9f3NjIJdzUGAAp%2fAiY=').delete()
30 |
31 | :rtype: amadeus.Response
32 | :raises amadeus.ResponseError: if the request could not be completed
33 | '''
34 | return self.client.delete('/v1/booking/flight-orders/{0}'
35 | .format(self.flight_order_id), **params)
36 |
--------------------------------------------------------------------------------
/amadeus/booking/_flight_orders.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class FlightOrders(Decorator, object):
5 | def post(self, flight, travelers):
6 | '''
7 | Books a flight
8 |
9 | .. code-block:: python
10 |
11 | amadeus.booking.flight_orders.post(flight, travelers)
12 |
13 | :rtype: amadeus.Response
14 | :raises amadeus.ResponseError: if the request could not be completed
15 | '''
16 | flight_offers = []
17 | if not isinstance(flight, list):
18 | flight_offers.append(flight)
19 | else:
20 | flight_offers.extend(flight)
21 | travelers_info = []
22 | if not isinstance(travelers, list):
23 | travelers_info.append(travelers)
24 | else:
25 | travelers_info.extend(travelers)
26 | body = {'data': {'type': 'flight-order', 'flightOffers': flight_offers,
27 | 'travelers': travelers_info}}
28 | return self.client.post('/v1/booking/flight-orders', body)
29 |
--------------------------------------------------------------------------------
/amadeus/booking/_hotel_bookings.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class HotelBookings(Decorator, object):
5 | def post(self, hotel_offer_id, guests, payments):
6 | '''
7 | Books a hotel
8 |
9 | .. code-block:: python
10 |
11 | amadeus.booking.hotel_bookings.post(hotel_offer_id, guests, payments)
12 |
13 | The parameters guests and payments can be passed as dictionary
14 | or list of dictionaries. If they are dictionary in this method they are
15 | converted to a list of dictionaries.
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | guests_info = []
21 | payment_info = []
22 | if not isinstance(guests, list):
23 | guests_info.append(guests)
24 | else:
25 | guests_info.extend(guests)
26 | if not isinstance(payments, list):
27 | payment_info.append(payments)
28 | else:
29 | payment_info.extend(payments)
30 | body = {'data': {'offerId': hotel_offer_id,
31 | 'guests': guests_info,
32 | 'payments': payment_info}}
33 | return self.client.post('/v1/booking/hotel-bookings', body)
34 |
--------------------------------------------------------------------------------
/amadeus/booking/_hotel_orders.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class HotelOrders(Decorator, object):
5 | def post(self,
6 | guests,
7 | travel_agent,
8 | room_associations=[],
9 | payment={},
10 | arrival_information={}):
11 | '''
12 | Book hotel(s) via Hotel Booking API V2
13 |
14 | .. code-block:: python
15 |
16 | amadeus.booking.hotel_orders.post(guests,
17 | travel_agent,
18 | room_associations,
19 | payment,
20 | arrival_information)
21 |
22 | The parameters guests and room_associations can be passed as dictionary
23 | or list of dictionaries. If they are dictionary in this method they are
24 | converted to a list of dictionaries.
25 |
26 | :rtype: amadeus.Response
27 | :raises amadeus.ResponseError: if the request could not be completed
28 | '''
29 | guests_info = []
30 | room_associations_info = []
31 | if not isinstance(guests, list):
32 | guests_info.append(guests)
33 | else:
34 | guests_info.extend(guests)
35 | if not isinstance(room_associations, list):
36 | room_associations_info.append(room_associations)
37 | else:
38 | room_associations_info.extend(room_associations)
39 | body = {'data': {'type': 'hotel-order',
40 | 'guests': guests_info,
41 | 'travelAgent': travel_agent,
42 | 'roomAssociations': room_associations_info,
43 | 'arrivalInformation': arrival_information,
44 | 'payment': payment}}
45 | return self.client.post('/v2/booking/hotel-orders', body)
46 |
--------------------------------------------------------------------------------
/amadeus/client/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amadeus4dev/amadeus-python/0bc7089cb2e49b5b662ee890a5fe611f59ad4332/amadeus/client/__init__.py
--------------------------------------------------------------------------------
/amadeus/client/access_token.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 |
4 | class AccessToken(object):
5 | # The number of seconds before the token expires, when
6 | # we will already try to refresh it
7 | TOKEN_BUFFER = 10
8 |
9 | def __init__(self, client):
10 | self.access_token = None
11 | self.expires_at = 0
12 | self.client = client
13 |
14 | # PROTECTED
15 |
16 | # The bearer token that can be used directly in API request headers
17 | def _bearer_token(self):
18 | return 'Bearer {0}'.format(self.__token())
19 |
20 | # PRIVATE
21 |
22 | # Returns the access token if it is still valid,
23 | # or refreshes it if it is not (or about to expire)
24 | def __token(self):
25 | if self.__needs_refresh():
26 | self.__update_access_token()
27 | return self.access_token
28 |
29 | # Checks if the token needs a refesh by checking if the token
30 | # is nil or (about to) expire(d)
31 | def __needs_refresh(self):
32 | has_access_token = self.access_token is not None
33 | current_time_window = int(time.time()) + self.TOKEN_BUFFER
34 | has_valid_token = current_time_window < self.expires_at
35 |
36 | return not (has_access_token and has_valid_token)
37 |
38 | # Fetches a new access token and stores it and its expiry date
39 | def __update_access_token(self):
40 | response = self.__fetch_access_token()
41 | self.__store_access_token(response.result)
42 |
43 | # Fetches a new access token
44 | def __fetch_access_token(self):
45 | return self.client._unauthenticated_request(
46 | 'POST',
47 | '/v1/security/oauth2/token',
48 | {
49 | 'grant_type': 'client_credentials',
50 | 'client_id': self.client.client_id,
51 | 'client_secret': self.client.client_secret
52 | }
53 | )
54 |
55 | # Store an access token and calculates the expiry date
56 | def __store_access_token(self, data):
57 | self.access_token = data.get('access_token', None)
58 | current_time = int(time.time())
59 | self.expires_at = current_time + data.get('expires_in', 0)
60 |
--------------------------------------------------------------------------------
/amadeus/client/decorator.py:
--------------------------------------------------------------------------------
1 | class Decorator(object):
2 | def __init__(self, client):
3 | self.client = client
4 |
--------------------------------------------------------------------------------
/amadeus/client/direction.py:
--------------------------------------------------------------------------------
1 | #
2 | class Direction (object):
3 | '''
4 | A list of direction types, as used in Busiest Travel Period
5 |
6 | .. code-block:: python
7 |
8 |
9 | from amadeus import Direction
10 |
11 | client.travel.analytics.air_traffic.busiest_period.get(
12 | cityCode = 'MAD',
13 | period = '2017',
14 | direction = Direction.ARRIVING
15 | )
16 |
17 | :cvar ARRIVING: ``"ARRIVING"``
18 | :cvar DEPARTING: ``"DEPARTING"``
19 | '''
20 | # Arriving
21 | ARRIVING = 'ARRIVING'
22 | # Departing
23 | DEPARTING = 'DEPARTING'
24 |
--------------------------------------------------------------------------------
/amadeus/client/errors.py:
--------------------------------------------------------------------------------
1 | from pprint import pformat
2 |
3 |
4 | class ResponseError(RuntimeError):
5 | '''
6 | An Amadeus error
7 |
8 | :var response: The response object containing the raw HTTP response and
9 | the request used to make the API call.
10 | :vartype response: amadeus.Response
11 |
12 | :var code: A unique code for this type of error. Options include
13 | ``NetworkError``, ``ParserError``, ``ServerError``,
14 | ``AuthenticationError``, ``NotFoundError`` and ``UnknownError``.
15 | :vartype code: str
16 | '''
17 |
18 | def __init__(self, response):
19 | self.response = response
20 | self.code = self.__determine_code()
21 | RuntimeError.__init__(self, self.description())
22 |
23 | # PROTECTED
24 |
25 | # Log the error
26 | def _log(self, client):
27 | if (client.log_level == 'warn'):
28 | client.logger.warning(
29 | 'Amadeus %s: %s', self.code, pformat(self.description)
30 | )
31 |
32 | # PRIVATE
33 |
34 | # Determines the description for this error, as used in in the error output
35 | def description(self):
36 | description = self.short_description(self.response)
37 | return description + self.long_description(self.response)
38 |
39 | # Determines the short description, printed after on the same line as the
40 | # error class name
41 | @staticmethod
42 | def short_description(response):
43 | if hasattr(response, 'status_code') and response.status_code:
44 | return '[{0}]'.format(response.status_code)
45 | else:
46 | return '[---]'
47 |
48 | # Determines the longer description, printed after the initial error
49 | def long_description(self, response):
50 | message = ''
51 | if not(response and response.parsed):
52 | return message
53 | if 'error_description' in response.result:
54 | message += self.error_description(self.response)
55 | if 'errors' in response.result:
56 | message += self.errors_descriptions(self.response)
57 | return message
58 |
59 | # Returns the description of a single error
60 | @staticmethod
61 | def error_description(response):
62 | message = ''
63 | if 'error' in response.result:
64 | message += '\n{0}'.format(response.result['error'])
65 | message += '\n{0}'.format(response.result['error_description'])
66 | return message
67 |
68 | # Returns the description of multiple errors
69 | def errors_descriptions(self, response):
70 | messages = map(self.errors_description, response.result['errors'])
71 | return ''.join(messages)
72 |
73 | # Returns the description of a single error in a multi error response
74 | @staticmethod
75 | def errors_description(error):
76 | message = '\n'
77 | if ('source' in error) and ('parameter' in error['source']):
78 | message += '[{0}] '.format(error['source']['parameter'])
79 | if 'detail' in error:
80 | message += error['detail']
81 | return message
82 |
83 | # sets the error code to the name of this class
84 | def __determine_code(self):
85 | return self.__class__.__name__
86 |
87 |
88 | class NetworkError(ResponseError):
89 | '''
90 | This error occurs when there is some kind of error in the network
91 | '''
92 | pass
93 |
94 |
95 | class ParserError(ResponseError):
96 | '''
97 | This error occurs when the response type was JSOn but could not be parsed
98 | '''
99 | pass
100 |
101 |
102 | class ServerError(ResponseError):
103 | '''
104 | This error occurs when there is an error on the server
105 | '''
106 | pass
107 |
108 |
109 | class ClientError(ResponseError):
110 | '''
111 | This error occurs when the client did not provide the right parameters
112 | '''
113 | pass
114 |
115 |
116 | class AuthenticationError(ResponseError):
117 | '''
118 | This error occurs when the client did not provide the right credentials
119 | '''
120 | pass
121 |
122 |
123 | class NotFoundError(ResponseError):
124 | '''
125 | This error occurs when the path could not be found
126 | '''
127 | pass
128 |
--------------------------------------------------------------------------------
/amadeus/client/hotel.py:
--------------------------------------------------------------------------------
1 | #
2 | class Hotel(object):
3 | '''
4 | A list of hotel sub types, as used in Hotel Name Autocomplete
5 |
6 | .. code-block:: python
7 |
8 |
9 | from amadeus import Hotel
10 |
11 | amadeus.reference_data.locations.hotel.get(
12 | keyword='PARI',
13 | subType=[Hotel.HOTEL_LEISURE, Hotel.HOTEL_GDS]
14 | )
15 |
16 | :cvar HOTEL_LEISURE: ``"HOTEL_LEISURE"``
17 | :cvar HOTEL_GDS: ``"HOTEL_GDS"``
18 | '''
19 | # Hotel Leisure
20 | HOTEL_LEISURE = 'HOTEL_LEISURE'
21 | # Hotel GDS
22 | HOTEL_GDS = 'HOTEL_GDS'
23 |
--------------------------------------------------------------------------------
/amadeus/client/location.py:
--------------------------------------------------------------------------------
1 | #
2 | class Location(object):
3 | '''
4 | A list of location types, as used in searching for locations
5 |
6 | .. code-block:: python
7 |
8 |
9 | from amadeus import Location
10 |
11 | amadeus.reference_data.locations.get(
12 | keyword='lon',
13 | subType=Location.ANY
14 | )
15 |
16 | :cvar AIRPORT: ``"AIRPORT"``
17 | :cvar CITY: ``"CITY"``
18 | :cvar ANY: ``"AIRPORT,CITY"``
19 | '''
20 | # Airport
21 | AIRPORT = 'AIRPORT'
22 | # City
23 | CITY = 'CITY'
24 | # Any
25 | ANY = ','.join([AIRPORT, CITY])
26 |
--------------------------------------------------------------------------------
/amadeus/client/request.py:
--------------------------------------------------------------------------------
1 | # Support Python API calls without importing 3rd party library
2 |
3 | import json
4 |
5 | from urllib.request import Request as HTTPRequest
6 | from urllib.parse import urlencode
7 |
8 |
9 | class Request(object):
10 | '''
11 | An object containing all the compiled information about the request made.
12 |
13 | :var host: The host used for this API call
14 | :vartype host: str
15 |
16 | :var port: The port for this API call. Standard set to 443.
17 | :vartype port: int
18 |
19 | :var ssl: Wether to use SSL for a call, defaults to true
20 | :vartype ssl: bool
21 |
22 | :var scheme: The scheme used to make the API call
23 | :vartype scheme: str
24 |
25 | :var params: The GET/POST params for the API call
26 | :vartype params: dict
27 |
28 | :var path: The path of the API to be called
29 | :vartype path: str
30 |
31 | :var verb: The verb used to make an API call ('GET' or 'POST')
32 | :vartype verb: str
33 |
34 | :var bearer_token: The bearer token (if any) that was used for
35 | authentication
36 | :vartype bearer_token: str
37 |
38 | :var headers: The headers used for the API call
39 | :vartype headers: dict
40 |
41 | :var client_version: The library version used for this request
42 | :vartype client_version: str
43 |
44 | :var language_version: The Python language version used for this request
45 | :vartype language_version: str
46 |
47 | :var app_id: The custom app ID passed in for this request
48 | :vartype app_id: str
49 |
50 | :var app_version: The custom app version used for this request
51 | :vartype app_version: str
52 | '''
53 |
54 | def __init__(self, options):
55 | self.host = options['host']
56 | self.port = options['port']
57 | self.ssl = options['ssl']
58 | self.scheme = 'https' if self.ssl else 'http'
59 | self.verb = options['verb']
60 | self.path = options['path']
61 | self.params = options['params']
62 | self.bearer_token = options['bearer_token']
63 | self.client_version = options['client_version']
64 | self.language_version = options['language_version']
65 | self.app_id = options['app_id']
66 | self.app_version = options['app_version']
67 |
68 | self.headers = {
69 | 'User-Agent': self.__build_user_agent(),
70 | 'Accept': 'application/json, application/vnd.amadeus+json',
71 | 'Content-Type': 'application/vnd.amadeus+json'
72 | }
73 |
74 | self.url = self.__build_url()
75 | self.http_request = self.__build_http_request()
76 |
77 | # PRIVATE
78 |
79 | # Determines the User Agent
80 | def __build_user_agent(self):
81 | user_agent = 'amadeus-python/{0}'.format(self.client_version)
82 | user_agent += ' python/{0}'.format(self.language_version)
83 | if self.app_id:
84 | user_agent += ' {0}/{1}'.format(self.app_id, self.app_version)
85 | return user_agent
86 |
87 | # The list of paths that require HTTP override in header
88 | list_httpoverride = [
89 | '/v2/shopping/flight-offers',
90 | '/v1/shopping/seatmaps',
91 | '/v1/shopping/availability/flight-availabilities',
92 | '/v2/shopping/flight-offers/prediction',
93 | '/v1/shopping/flight-offers/pricing?',
94 | '/v1/shopping/flight-offers/upselling'
95 | ]
96 |
97 | # Builds a HTTP Request object based on the path, params, and verb
98 | def __build_http_request(self):
99 | # Requests token in case has not been set
100 | if (self.bearer_token is None):
101 | self.headers['Content-Type'] = 'application/x-www-form-urlencoded'
102 | return HTTPRequest(self.url,
103 | data=urlencode(self.params).encode(),
104 | headers=self.headers)
105 | # Adds the authentication header since the bearer token has been set
106 | self.headers['Authorization'] = self.bearer_token
107 |
108 | if self.verb == 'POST':
109 | # Adds HTTP override in Header for the list of paths required
110 | if self.path in Request.list_httpoverride:
111 | self.headers['X-HTTP-Method-Override'] = 'GET'
112 | if isinstance(self.params, dict):
113 | return HTTPRequest(self.url, headers=self.headers, method='POST',
114 | data=json.dumps(self.params).encode())
115 | else:
116 | return HTTPRequest(self.url, headers=self.headers, method='POST',
117 | data=self.params.encode())
118 | else:
119 | return HTTPRequest(self.url, headers=self.headers, method=self.verb)
120 |
121 | # Encodes the params before sending them
122 | def _encoded_params(self):
123 | return self._urlencode(self.params)
124 |
125 | # Builds up the full URL based on the scheme, host, path, and params
126 | def __build_url(self):
127 | full_url = '{0}://{1}'.format(self.scheme, self.host)
128 | if not self.__port_matches_scheme():
129 | full_url = '{0}:{1}'.format(full_url, self.port)
130 | full_url = '{0}{1}'.format(full_url, self.path)
131 | if (self.verb == 'GET'):
132 | full_url += '?{0}'.format(self._encoded_params())
133 | return full_url
134 |
135 | def __port_matches_scheme(self):
136 | return ((self.ssl and self.port == 443) or
137 | (not self.ssl and self.port == 80))
138 |
139 | # Helper method to prepare the parameter encoding
140 | def _urlencode(self, d):
141 | return urlencode(self._flatten_keys(d, '', {}), doseq=True)
142 |
143 | # Flattens the hash keys, so page: { offset: 1 } becomes page[offet] = 1
144 | def _flatten_keys(self, d, key, out):
145 | if not isinstance(d, dict):
146 | raise TypeError('Only dicts can be encoded')
147 |
148 | for k in d:
149 | keystr = k if not key else '[{}]'.format(k)
150 | if isinstance(d[k], dict):
151 | self._flatten_keys(d[k], str(key) + str(keystr), out)
152 | else:
153 | out['{}{}'.format(key, keystr)] = d[k]
154 | return out
155 |
--------------------------------------------------------------------------------
/amadeus/client/response.py:
--------------------------------------------------------------------------------
1 | from amadeus.mixins.parser import Parser
2 |
3 |
4 | class Response(Parser, object):
5 | '''
6 | The response object returned for every API call.
7 |
8 | :var http_response: the raw http response
9 |
10 | :var request: the original Request object used to make this call
11 | :vartype request: amadeus.Request
12 |
13 | :var result: the parsed JSON received from the API, if the result was JSON
14 | :vartype result: dict
15 |
16 | :var data: the data extracted from the JSON data, if the body contained
17 | JSON
18 | :vartype data: dict
19 |
20 | :var body: the raw body received from the API
21 | :vartype body: str
22 |
23 | :var parsed: wether the raw body has been parsed into JSON
24 | :vartype parsed: bool
25 |
26 | :var status_code: The HTTP status code for the response, if any
27 | :vartype status_code: int
28 | '''
29 |
30 | # Initialize the Response object with the
31 | # HTTPResponse object to parse, the client that made the request
32 | # and the original request made
33 | def __init__(self, http_response, request):
34 | self.http_response = http_response
35 | self.request = request
36 |
37 | # PROTECTED
38 |
39 | # Parses the response, using the client to log any errors
40 | def _parse(self, client):
41 | self._parse_status_code()
42 | self._parse_data(client)
43 | return self
44 |
--------------------------------------------------------------------------------
/amadeus/e_reputation/__init__.py:
--------------------------------------------------------------------------------
1 | from ._hotel_sentiments import HotelSentiments
2 |
3 | __all__ = ['HotelSentiments']
4 |
--------------------------------------------------------------------------------
/amadeus/e_reputation/_hotel_sentiments.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class HotelSentiments(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Provides ratings and sentiments scores for hotels
8 |
9 | .. code-block:: python
10 |
11 | amadeus.e_reputation.hotel_sentiments.get(hotelIds='TELONMFS,ADNYCCTB'])
12 |
13 | :param hotelIds: comma separated string list of amadeus hotel Ids (max
14 | 3). These Ids are found in the Hotel Search response. ``"RDLON308"``,
15 | for example for the Radisson Blu Hampshire hotel.
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | return self.client.get('/v2/e-reputation/hotel-sentiments', **params)
21 |
--------------------------------------------------------------------------------
/amadeus/media/_files.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Files(Decorator, object):
5 | def __init__(self, client):
6 | Decorator.__init__(self, client)
7 |
--------------------------------------------------------------------------------
/amadeus/mixins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amadeus4dev/amadeus-python/0bc7089cb2e49b5b662ee890a5fe611f59ad4332/amadeus/mixins/__init__.py
--------------------------------------------------------------------------------
/amadeus/mixins/http.py:
--------------------------------------------------------------------------------
1 | from platform import python_version
2 | from pprint import pformat
3 | from urllib.error import URLError
4 |
5 | from amadeus.version import version
6 | from amadeus.client.request import Request
7 | from amadeus.client.response import Response
8 | from amadeus.client.access_token import AccessToken
9 |
10 |
11 | # A helper module for making generic API calls. It is used by
12 | # every namespaced API method.
13 | class HTTP(object):
14 | '''
15 | A helper module for making generic API calls. It is used by
16 | every namespaced API method.
17 | '''
18 |
19 | def get(self, path, **params):
20 | '''
21 | A helper function for making generic GET requests calls. It is used by
22 | every namespaced API GET method.
23 |
24 | It can be used to make any generic API call that is automatically
25 | authenticated using your API credentials:
26 |
27 | .. code-block:: python
28 |
29 | amadeus.get('/foo/bar', airline='1X')
30 |
31 | :param path: path the full path for the API call
32 | :paramtype path: str
33 |
34 | :param params: (optional) params to pass to the API
35 | :paramtype params: dict
36 |
37 | :rtype: amadeus.Response
38 | :raises amadeus.ResponseError: when the request fails
39 | '''
40 | return self.request('GET', path, params)
41 |
42 | def post(self, path, params=None):
43 | '''
44 | A helper function for making generic POST requests calls. It is used by
45 | every namespaced API POST method.
46 |
47 | It can be used to make any generic API call that is automatically
48 | authenticated using your API credentials:
49 |
50 | .. code-block:: python
51 |
52 | amadeus.post('/foo/bar', airline='1X')
53 |
54 | :param path: path the full path for the API call
55 | :paramtype path: str
56 |
57 | :param params: (optional) params to pass to the API
58 | :paramtype params: dict
59 |
60 | :rtype: amadeus.Response
61 | :raises amadeus.ResponseError: when the request fails
62 | '''
63 | return self.request('POST', path, params)
64 |
65 | def delete(self, path, **params):
66 | '''
67 | A helper function for making generic DELETE requests calls. It is used by
68 | every namespaced API DELETE method.
69 |
70 | It can be used to make any generic API call that is automatically
71 | authenticated using your API credentials:
72 |
73 | .. code-block:: python
74 |
75 | amadeus.delete('/foo/bar', airline='1X')
76 |
77 | :param path: path the full path for the API call
78 | :paramtype path: str
79 |
80 | :param params: (optional) params to pass to the API
81 | :paramtype params: dict
82 |
83 | :rtype: amadeus.Response
84 | :raises amadeus.ResponseError: when the request fails
85 | '''
86 | return self.request('DELETE', path, params)
87 |
88 | def request(self, verb, path, params):
89 | '''
90 | A helper function for making generic POST requests calls. It is used by
91 | every namespaced API method. It can be used to make any generic API
92 | call that is automatically authenticated using your API credentials:
93 |
94 | .. code-block:: python
95 |
96 | amadeus.request('GET', '/foo/bar', airline='1X')
97 |
98 | :param verb: the HTTP verb to use
99 | :paramtype verb: str
100 |
101 | :param path: path the full path for the API call
102 | :paramtype path: str
103 |
104 | :param params: (optional) params to pass to the API
105 | :paramtype params: dict
106 |
107 | :rtype: amadeus.Response
108 | :raises amadeus.ResponseError: when the request fails
109 | '''
110 | return self._unauthenticated_request(
111 | verb, path, params,
112 | self.__access_token()._bearer_token()
113 | )
114 |
115 | # PROTECTED
116 |
117 | # Builds the URI, the request object, and makes the actual API calls.
118 | #
119 | # Used by the AccessToken to fetch a new Bearer Token
120 | #
121 | # Passes the response to a Response object for further parsing.
122 | #
123 | def _unauthenticated_request(self, verb, path, params, bearer_token=None):
124 | request = self.__build_request(verb, path, params, bearer_token)
125 | self.__log(request)
126 | return self.__execute(request)
127 |
128 | # PRIVATE
129 |
130 | # Builds a HTTP request object that contains all the information about
131 | # this request
132 | def __build_request(self, verb, path, params, bearer_token):
133 | return Request({
134 | 'host': self.host,
135 | 'verb': verb,
136 | 'path': path,
137 | 'params': params,
138 | 'bearer_token': bearer_token,
139 | 'client_version': version,
140 | 'language_version': python_version(),
141 | 'app_id': self.custom_app_id,
142 | 'app_version': self.custom_app_version,
143 | 'ssl': self.ssl,
144 | 'port': self.port
145 | })
146 |
147 | # Executes the request and wraps it in a Response
148 | def __execute(self, request):
149 | http_response = self.__fetch(request)
150 | response = Response(http_response, request)._parse(self)
151 | self.__log(response)
152 | response._detect_error(self)
153 | return response
154 |
155 | # A memoized AccessToken object, so we don't keep reauthenticating
156 | def __access_token(self):
157 | if (not hasattr(self, 'access_token')):
158 | self.access_token = AccessToken(self)
159 | return self.access_token
160 |
161 | # Log any object
162 | def __log(self, object):
163 | if (self.log_level == 'debug'):
164 | self.logger.debug(
165 | '%s\n%s', object.__class__.__name__, pformat(object.__dict__)
166 | )
167 |
168 | # Actually make the HTTP call, making sure to catch it in case of an error
169 | def __fetch(self, request):
170 | try:
171 | return self.http(request.http_request)
172 | except URLError as exception:
173 | return exception
174 |
--------------------------------------------------------------------------------
/amadeus/mixins/pagination.py:
--------------------------------------------------------------------------------
1 | import copy
2 |
3 |
4 | # A set of helper methods to allow the validating of
5 | # arguments past into the Client
6 | class Pagination(object):
7 |
8 | def previous(self, response):
9 | return self.__page('previous', response)
10 |
11 | def next(self, response):
12 | return self.__page('next', response)
13 |
14 | def first(self, response):
15 | return self.__page('first', response)
16 |
17 | def last(self, response):
18 | return self.__page('last', response)
19 |
20 | # PRIVATE
21 |
22 | def __page(self, name, response):
23 | page_number = self.__page_number_for(name, response)
24 | if page_number is None:
25 | return None
26 | params = copy.deepcopy(response.request.params)
27 | if 'page' not in params:
28 | params['page'] = {}
29 | params['page']['offset'] = page_number
30 | return self.request(
31 | response.request.verb,
32 | response.request.path,
33 | params
34 | )
35 |
36 | @staticmethod
37 | def __page_number_for(name, response):
38 | try:
39 | url = response.result['meta']['links'][name]
40 | return url.split('page%5Boffset%5D=')[1].split('&')[0]
41 | except Exception:
42 | return None
43 |
--------------------------------------------------------------------------------
/amadeus/mixins/parser.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from amadeus.client.errors import ParserError, ServerError
4 | from amadeus.client.errors import AuthenticationError, NetworkError
5 | from amadeus.client.errors import NotFoundError, ClientError
6 |
7 |
8 | class Parser(object):
9 |
10 | # PROTECTED
11 |
12 | # Tries to detect for appropriate errors
13 | def _detect_error(self, client):
14 | error = self.error_for(self.status_code, self.parsed)
15 | if error is not None:
16 | self.__raise_error(error, client)
17 |
18 | @staticmethod
19 | def error_for(status_code, parsed): # noqa: C901
20 | if status_code is None:
21 | return NetworkError
22 | if status_code >= 500:
23 | return ServerError
24 | if status_code == 401:
25 | return AuthenticationError
26 | if status_code == 404:
27 | return NotFoundError
28 | if status_code >= 400:
29 | return ClientError
30 | if status_code == 204:
31 | return None
32 | if not parsed:
33 | return ParserError
34 |
35 | # Parses the HTTP status code
36 | def _parse_status_code(self):
37 | http_response = self.http_response
38 | self.status_code = getattr(http_response, 'status', None)
39 | self.status_code = getattr(http_response, 'code', self.status_code)
40 |
41 | # Tries to parse the received data from raw string to parsed data and into
42 | # a data object
43 | def _parse_data(self, client):
44 | self.parsed = False
45 | self.data = None
46 | self.body = None
47 | self.result = None
48 | self.headers = {}
49 |
50 | self.__parse_headers(self.http_response, client)
51 |
52 | # Avoid parsing body in 204 responses
53 | if self.status_code == 204:
54 | return
55 |
56 | self.__parse_body(self.http_response, client)
57 |
58 | self.result = self.__parse_json(client)
59 | if (self.result is not None):
60 | self.data = self.result.get('data', None)
61 |
62 | # PRIVATE
63 |
64 | # Logs and raises the error
65 | def __raise_error(self, error_class, client):
66 | error = error_class(self)
67 | error._log(client)
68 | raise error
69 |
70 | def __parse_headers(self, http_response, client):
71 | if hasattr(http_response, 'getheaders'):
72 | self.headers = dict(http_response.getheaders()) or self.headers
73 | if hasattr(http_response, 'info'):
74 | self.headers = http_response.info() or self.headers
75 |
76 | # Extract the body and headers
77 | def __parse_body(self, http_response, client):
78 | if hasattr(http_response, 'read'):
79 | self.body = http_response.read()
80 | if hasattr(self.body, 'decode'):
81 | self.body = self.body.decode('utf8')
82 |
83 | # Tries to parse the JSON, if there is any
84 | def __parse_json(self, client):
85 | try:
86 | if (self.__is_json()):
87 | result = json.loads(self.body)
88 | self.parsed = True
89 | return result
90 | else:
91 | return None
92 | except Exception:
93 | self.__raise_error(ParserError, client)
94 |
95 | # checks if the HTTPResponse included JSON
96 | def __is_json(self):
97 | return self.__has_json_header() and self.__has_body()
98 |
99 | # checks if the HTTPResponse has a non-empty body
100 | def __has_body(self):
101 | return self.body and len(self.body) > 0
102 |
103 | # checks if the HTTPResponse has a JSON header
104 | def __has_json_header(self):
105 | content_type = self.headers.get('Content-Type', None)
106 | if (content_type is not None):
107 | types = content_type.split(';')[0]
108 | types = ['application/json', 'application/vnd.amadeus+json']
109 | return content_type in types
110 | else:
111 | return False
112 |
--------------------------------------------------------------------------------
/amadeus/mixins/validator.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import logging
4 |
5 | from urllib.request import urlopen
6 |
7 |
8 | # A set of helper methods to allow the validating of
9 | # arguments past into the Client
10 | class Validator(object):
11 |
12 | # PROTECTED
13 |
14 | # Checks a list of options for unrecognized keys and warns the user.
15 | # This is mainly used to provide a nice experience when users make a typo
16 | # in their arguments.
17 | @staticmethod
18 | def _warn_on_unrecognized_options(options, logger, valid_options):
19 | for key in options:
20 | if (key in valid_options):
21 | continue
22 | logger.warning('Unrecognized option: {0}'.format(key))
23 |
24 | # Initializes the credentials, requiring an ID and Secret
25 | def _initialize_client_credentials(self, options):
26 | self.client_id = self.__init_required('client_id', options)
27 | self.client_secret = self.__init_required('client_secret', options)
28 |
29 | # Initializes an optional Logger
30 | def _initialize_logger(self, options):
31 | default_logger = logging.getLogger('amadeus')
32 | default_logger.setLevel(logging.DEBUG)
33 | handler = logging.StreamHandler(sys.stdout)
34 | default_logger.addHandler(handler)
35 |
36 | self.logger = self.__init_optional('logger', options, default_logger)
37 | self.log_level = self.__init_optional('log_level', options, 'silent')
38 |
39 | # Initializes an optional host, hostname, port, and SSL requirements
40 | def _initialize_host(self, options):
41 | self.hostname = self.__init_optional('hostname', options, 'test')
42 | self.host = self.__init_optional('host', options,
43 | self.HOSTS[self.hostname])
44 | self.ssl = self.__init_optional('ssl', options, True)
45 | self.port = self.__init_optional('port', options, 443)
46 |
47 | # Initializes an optional custom App ID and Version
48 | def _initialize_custom_app(self, options):
49 | self.custom_app_id = self.__init_optional('custom_app_id',
50 | options, None)
51 | self.custom_app_version = self.__init_optional('custom_app_version',
52 | options, None)
53 |
54 | # Initializes a custom http handler
55 | def _initialize_http(self, options):
56 | self.http = self.__init_optional('http', options, urlopen)
57 |
58 | # PRIVATE
59 |
60 | # Uses 'init_optional' to find an entry, and it that returns
61 | # nil it raises an ArgumentError
62 | #
63 | def __init_required(self, key, options):
64 | value = self.__init_optional(key, options)
65 | if (value is None):
66 | raise ValueError('Missing required argument: {0}'.format(key))
67 | return value
68 |
69 | # Tries to find an option by string or symbol in the options hash and
70 | # in the environment variables.When it can not find it anywhere it
71 | # defaults to the provided default option.
72 | @staticmethod
73 | def __init_optional(key, options, defa_ult=None):
74 | value = options.get(key, None)
75 | if (value is None):
76 | env_key = 'AMADEUS_{0}'.format(key.upper())
77 | value = os.environ.get(env_key, None)
78 | if (value is None):
79 | value = defa_ult
80 | return value
81 |
--------------------------------------------------------------------------------
/amadeus/namespaces/__init__.py:
--------------------------------------------------------------------------------
1 | from .core import Core
2 |
3 | __all__ = ['Core']
4 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_airline.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.airline._destinations import Destinations
3 |
4 |
5 | class Airline(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.destinations = Destinations(client)
9 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_airport.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.airport._predictions import Predictions
3 | from amadeus.airport._direct_destinations import DirectDestinations
4 |
5 |
6 | class Airport(Decorator, object):
7 | def __init__(self, client):
8 | Decorator.__init__(self, client)
9 | self.predictions = Predictions(client)
10 | self.direct_destinations = DirectDestinations(client)
11 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_analytics.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.analytics._itinerary_price_metrics import ItineraryPriceMetrics
3 |
4 |
5 | class Analytics(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.itinerary_price_metrics = ItineraryPriceMetrics(client)
9 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_booking.py:
--------------------------------------------------------------------------------
1 | from amadeus.booking._flight_orders import FlightOrders
2 | from amadeus.booking._flight_order import FlightOrder
3 | from amadeus.booking._hotel_bookings import HotelBookings
4 | from amadeus.booking._hotel_orders import HotelOrders
5 | from amadeus.client.decorator import Decorator
6 |
7 |
8 | class Booking(Decorator, object):
9 | def __init__(self, client):
10 | Decorator.__init__(self, client)
11 | self.flight_orders = FlightOrders(client)
12 | self.hotel_bookings = HotelBookings(client)
13 | self.hotel_orders = HotelOrders(client)
14 |
15 | def flight_order(self, flight_order_id):
16 | return FlightOrder(self.client, flight_order_id)
17 |
18 |
19 | __all__ = ['FlightOrders', 'FlightOrder', 'HotelBookings', 'HotelOrders']
20 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_e_reputation.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.e_reputation._hotel_sentiments import HotelSentiments
3 |
4 |
5 | class EReputation(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.hotel_sentiments = HotelSentiments(client)
9 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_ordering.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.ordering._transfer_orders import TransferOrders
3 | from amadeus.ordering._transfer_order import TransferOrder
4 |
5 |
6 | class Ordering(Decorator, object):
7 | def __init__(self, client):
8 | Decorator.__init__(self, client)
9 | self.transfer_orders = TransferOrders(client)
10 |
11 | def transfer_order(self, order_id):
12 | return TransferOrder(self.client, order_id)
13 |
14 |
15 | __all__ = ['TransferOrders', 'TransferOrder']
16 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_reference_data.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.reference_data._urls import Urls
3 | from amadeus.reference_data._location import Location
4 | from amadeus.reference_data._locations import Locations
5 | from amadeus.reference_data._airlines import Airlines
6 | from amadeus.reference_data._recommended_locations import RecommendedLocations
7 |
8 |
9 | class ReferenceData(Decorator, object):
10 | def __init__(self, client):
11 | Decorator.__init__(self, client)
12 | self.urls = Urls(client)
13 | self.locations = Locations(client)
14 | self.airlines = Airlines(client)
15 | self.recommended_locations = RecommendedLocations(client)
16 |
17 | def location(self, location_id):
18 | return Location(self.client, location_id)
19 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_schedule.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.schedule._flights import Flights
3 |
4 |
5 | class Schedule(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.flights = Flights(client)
9 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_shopping.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.shopping._flight_dates import FlightDates
3 | from amadeus.shopping._flight_destinations import FlightDestinations
4 | from amadeus.shopping._flight_offers import FlightOffers
5 | from amadeus.shopping._flight_offers_search import FlightOffersSearch
6 | from amadeus.shopping._seatmaps import Seatmaps
7 | from amadeus.shopping._activities import Activities
8 | from amadeus.shopping._activity import Activity
9 | from amadeus.shopping._availability import Availability
10 | from amadeus.shopping._hotel_offer_search import HotelOfferSearch
11 | from amadeus.shopping._hotel_offers_search import HotelOffersSearch
12 | from amadeus.shopping._transfer_offers import TransferOffers
13 |
14 |
15 | class Shopping(Decorator, object):
16 | def __init__(self, client):
17 | Decorator.__init__(self, client)
18 | self.flight_dates = FlightDates(client)
19 | self.flight_destinations = FlightDestinations(client)
20 | self.flight_offers = FlightOffers(client)
21 | self.flight_offers_search = FlightOffersSearch(client)
22 | self.seatmaps = Seatmaps(client)
23 | self.activities = Activities(client)
24 | self.availability = Availability(client)
25 | self.hotel_offers_search = HotelOffersSearch(client)
26 | self.transfer_offers = TransferOffers(client)
27 |
28 | def hotel_offer_search(self, offer_id):
29 | return HotelOfferSearch(self.client, offer_id)
30 |
31 | def activity(self, activity_id):
32 | return Activity(self.client, activity_id)
33 |
34 |
35 | __all__ = ['FlightDates', 'FlightDestinations', 'FlightOffers',
36 | 'FlightOffersSearch', 'Availability']
37 |
--------------------------------------------------------------------------------
/amadeus/namespaces/_travel.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.travel._analytics import Analytics
3 | from amadeus.travel._predictions import Predictions
4 |
5 |
6 | class Travel(Decorator, object):
7 | def __init__(self, client):
8 | Decorator.__init__(self, client)
9 | self.analytics = Analytics(client)
10 | self.predictions = Predictions(client)
11 |
--------------------------------------------------------------------------------
/amadeus/namespaces/core.py:
--------------------------------------------------------------------------------
1 | from amadeus.namespaces._reference_data import ReferenceData
2 | from amadeus.namespaces._travel import Travel
3 | from amadeus.namespaces._shopping import Shopping
4 | from amadeus.namespaces._e_reputation import EReputation
5 | from amadeus.namespaces._airport import Airport
6 | from amadeus.namespaces._booking import Booking
7 | from amadeus.namespaces._schedule import Schedule
8 | from amadeus.namespaces._analytics import Analytics
9 | from amadeus.namespaces._airline import Airline
10 | from amadeus.namespaces._ordering import Ordering
11 |
12 |
13 | class Core(object):
14 | def __init__(self):
15 | self.reference_data = ReferenceData(self)
16 | self.travel = Travel(self)
17 | self.shopping = Shopping(self)
18 | self.e_reputation = EReputation(self)
19 | self.airport = Airport(self)
20 | self.booking = Booking(self)
21 | self.schedule = Schedule(self)
22 | self.analytics = Analytics(self)
23 | self.airline = Airline(self)
24 | self.ordering = Ordering(self)
25 |
--------------------------------------------------------------------------------
/amadeus/ordering/__init__.py:
--------------------------------------------------------------------------------
1 | from ._transfer_orders import TransferOrders
2 | from ._transfer_order import TransferOrder
3 |
4 | __all__ = ['TransferOrders', 'TransferOrder']
5 |
--------------------------------------------------------------------------------
/amadeus/ordering/_transfer_order.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.ordering.transfer_orders import Transfers
3 |
4 |
5 | class TransferOrder(Decorator, object):
6 | def __init__(self, client, order_id):
7 | Decorator.__init__(self, client)
8 | self.transfers = Transfers(client, order_id)
9 |
--------------------------------------------------------------------------------
/amadeus/ordering/_transfer_orders.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 | from urllib.parse import urlencode
4 |
5 |
6 | class TransferOrders(Decorator, object):
7 | def post(self, body, **params):
8 | '''
9 | Performs the final booking for a chosen transfer
10 |
11 | .. code-block:: python
12 |
13 | amadeus.ordering.transfer_orders.post(body, offerId=offer_id)
14 |
15 | :rtype: amadeus.Response
16 | :raises amadeus.ResponseError: if the request could not be completed
17 | '''
18 | url = '/v1/ordering/transfer-orders?'
19 | return self.client.post(url + urlencode(params), body)
20 |
--------------------------------------------------------------------------------
/amadeus/ordering/transfer_orders/__init__.py:
--------------------------------------------------------------------------------
1 | from ._transfers import Transfers
2 |
3 | __all__ = ['Transfers']
4 |
--------------------------------------------------------------------------------
/amadeus/ordering/transfer_orders/_transfers.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.ordering.transfer_orders.transfers import Cancellation
3 |
4 |
5 | class Transfers(Decorator, object):
6 | def __init__(self, client, order_id):
7 | Decorator.__init__(self, client)
8 | self.cancellation = Cancellation(client, order_id)
9 |
--------------------------------------------------------------------------------
/amadeus/ordering/transfer_orders/transfers/__init__.py:
--------------------------------------------------------------------------------
1 | from ._cancellation import Cancellation
2 |
3 | __all__ = ['Cancellation']
4 |
--------------------------------------------------------------------------------
/amadeus/ordering/transfer_orders/transfers/_cancellation.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 | from urllib.parse import urlencode
4 |
5 |
6 | class Cancellation(Decorator, object):
7 | def __init__(self, client, order_id):
8 | Decorator.__init__(self, client)
9 | self.order_id = order_id
10 |
11 | def post(self, body, **params):
12 | '''
13 | Cancels a transfer reservation
14 |
15 | .. code-block:: python
16 |
17 | amadeus.ordering.transfer_order(order_id).transfers.cancellation.post(body,
18 | confirmNbr=confirm_nbr)
19 |
20 | :rtype: amadeus.Response
21 | :raises amadeus.ResponseError: if the request could not be completed
22 | '''
23 | url = '/v1/ordering/transfer-orders/{0}/transfers/cancellation?'.format(
24 | self.order_id
25 | )
26 | return self.client.post(url + urlencode(params), body)
27 |
--------------------------------------------------------------------------------
/amadeus/reference_data/__init__.py:
--------------------------------------------------------------------------------
1 | from ._location import Location
2 | from ._locations import Locations
3 | from ._airlines import Airlines
4 | from ._recommended_locations import RecommendedLocations
5 |
6 | __all__ = ['Location', 'Locations', 'Airlines', 'RecommendedLocations']
7 |
--------------------------------------------------------------------------------
/amadeus/reference_data/_airlines.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Airlines(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns the name of the airline given an IATA code.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.reference_data.airlines.get(airlineCodes='U2')
12 |
13 | :param airlineCodes: the IATA or ICAO code for the airline, e.g.
14 | :``"AF"`` (Air France IATA code)
15 | :or ``"AFR"`` (Air France ICAO code)
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | return self.client.get('/v1/reference-data/airlines', **params)
21 |
--------------------------------------------------------------------------------
/amadeus/reference_data/_location.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Location(Decorator, object):
5 | def __init__(self, client, location_id):
6 | Decorator.__init__(self, client)
7 | self.location_id = location_id
8 |
9 | def get(self, **params):
10 | '''
11 | Returns details for a specific airport.
12 |
13 | .. code-block:: python
14 |
15 | amadeus.reference_data.location('ALHR').get()
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | return self.client.get('/v1/reference-data/locations/{0}'
21 | .format(self.location_id), **params)
22 |
--------------------------------------------------------------------------------
/amadeus/reference_data/_locations.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.reference_data.locations._airports import Airports
3 | from amadeus.reference_data.locations._hotels import Hotels
4 | from amadeus.reference_data.locations._hotel import Hotel
5 | from amadeus.reference_data.locations._cities import Cities
6 |
7 |
8 | class Locations(Decorator, object):
9 | def __init__(self, client):
10 | Decorator.__init__(self, client)
11 | self.airports = Airports(client)
12 | self.hotels = Hotels(client)
13 | self.hotel = Hotel(client)
14 | self.cities = Cities(client)
15 |
16 | def get(self, **params):
17 | '''
18 | Returns details for a specific airport.
19 |
20 | .. code-block:: python
21 |
22 |
23 | from amadeus import Location
24 |
25 | amadeus.reference_data.locations.get(
26 | keyword='lon',
27 | subType=Location.ANY
28 | )
29 |
30 | :param keyword: keyword that should represent the start of
31 | a word in a city or airport name or code
32 |
33 | :param subType: a comma seperate list of location types to search
34 | for. You can use :class:`amadeus.Location` as a helper for this.
35 |
36 | :rtype: amadeus.Response
37 | :raises amadeus.ResponseError: if the request could not be completed
38 | '''
39 | return self.client.get('/v1/reference-data/locations', **params)
40 |
--------------------------------------------------------------------------------
/amadeus/reference_data/_recommended_locations.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class RecommendedLocations(Decorator, object):
5 | def __init__(self, client):
6 | Decorator.__init__(self, client)
7 |
8 | def get(self, **params):
9 | '''
10 | Returns a list of destination recommendations
11 |
12 | .. code-block:: python
13 |
14 |
15 | client.reference_data.recommended_locations.get(
16 | cityCodes='PAR'
17 | travelerCountryCode='FR'
18 | )
19 |
20 | :param cityCodes: city used by the algorithm to recommend new destinations
21 | For example: ``PAR``
22 | :param travelerCountryCode: origin country following IATA standard
23 | For example: ``FR``
24 |
25 | :rtype: amadeus.Response
26 | :raises amadeus.ResponseError: if the request could not be completed
27 | '''
28 | return self.client.get(
29 | '/v1/reference-data/recommended-locations', **params)
30 |
--------------------------------------------------------------------------------
/amadeus/reference_data/_urls.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.reference_data.urls._checkin_links import CheckinLinks
3 |
4 |
5 | class Urls(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.checkin_links = CheckinLinks(client)
9 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/__init__.py:
--------------------------------------------------------------------------------
1 | from ._airports import Airports
2 | from ._hotel import Hotel
3 | from ._cities import Cities
4 |
5 | __all__ = ['Airports', 'Hotel', 'Cities']
6 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/_airports.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Airports(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns a list of relevant airports near to a given point.
8 |
9 | .. code-block:: python
10 |
11 |
12 | amadeus.reference_data.locations.airports.get(
13 | longitude=49.0000,
14 | latitude=2.55
15 | )
16 |
17 | :param latitude: latitude of geographic location to search around.
18 | For example: ``52.5238``
19 | :param longitude: longitude of geographic location to search around.
20 | For example: ``13.3835``
21 |
22 | :rtype: amadeus.Response
23 | :raises amadeus.ResponseError: if the request could not be completed
24 | '''
25 | return self.client.get(
26 | '/v1/reference-data/locations/airports', **params)
27 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/_cities.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Cities(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns cities that match a specific word or letters.
8 |
9 | .. code-block:: python
10 |
11 |
12 | amadeus.reference_data.locations.cities.get(
13 | keyword='PARI'
14 | )
15 |
16 | :param keyword: location query keyword.
17 | For example: ``PARI``
18 |
19 | :rtype: amadeus.Response
20 | :raises amadeus.ResponseError: if the request could not be completed
21 | '''
22 | return self.client.get(
23 | '/v1/reference-data/locations/cities', **params)
24 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/_hotel.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Hotel(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns a list of hotels matching a given keyword.
8 |
9 | .. code-block:: python
10 |
11 |
12 | amadeus.reference_data.locations.hotel.get(
13 | keyword='PARI',
14 | subType=[Hotel.HOTEL_LEISURE, Hotel.HOTEL_GDS]
15 | )
16 |
17 | :param keyword: location query keyword.
18 | For example: ``PARI``
19 | :param subType: category of search.
20 | For example: ``[Hotel.HOTEL_LEISURE, Hotel.HOTEL_GDS]``
21 |
22 | :rtype: amadeus.Response
23 | :raises amadeus.ResponseError: if the request could not be completed
24 | '''
25 | return self.client.get(
26 | '/v1/reference-data/locations/hotel', **params)
27 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/_hotels.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.reference_data.locations.hotels import ByCity
3 | from amadeus.reference_data.locations.hotels import ByGeocode
4 | from amadeus.reference_data.locations.hotels import ByHotels
5 |
6 |
7 | class Hotels(Decorator, object):
8 | def __init__(self, client):
9 | Decorator.__init__(self, client)
10 | self.by_hotels = ByHotels(client)
11 | self.by_geocode = ByGeocode(client)
12 | self.by_city = ByCity(client)
13 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/hotels/__init__.py:
--------------------------------------------------------------------------------
1 | from ._by_city import ByCity
2 | from ._by_geocode import ByGeocode
3 | from ._by_hotels import ByHotels
4 |
5 | __all__ = ['ByCity', 'ByGeocode', 'ByHotels']
6 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/hotels/_by_city.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class ByCity(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Searches for hotel in a given city.
8 |
9 | .. code-block:: python
10 |
11 |
12 | amadeus.reference_data.locations.hotels.by_city.get(
13 | cityCode='PAR')
14 |
15 | :param cityCode: the City IATA code for which to find a hotel, for
16 | example ``"PAR"`` for Paris.
17 |
18 | :rtype: amadeus.Response
19 | :raises amadeus.ResponseError: if the request could not be completed
20 | '''
21 | return self.client.get(
22 | '/v1/reference-data/locations/hotels/by-city', **params)
23 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/hotels/_by_geocode.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class ByGeocode(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Searches for hotel using a geocode.
8 |
9 | .. code-block:: python
10 |
11 |
12 | amadeus.reference_data.locations.hotels.by_geocode.get(
13 | longitude=2.160873,
14 | latitude=41.397158
15 | )
16 |
17 | :param latitude: latitude of geographic location to search around.
18 | For example: ``41.397158``
19 | :param longitude: longitude of geographic location to search around.
20 | For example: ``2.160873``
21 |
22 | :rtype: amadeus.Response
23 | :raises amadeus.ResponseError: if the request could not be completed
24 | '''
25 | return self.client.get(
26 | '/v1/reference-data/locations/hotels/by-geocode', **params)
27 |
--------------------------------------------------------------------------------
/amadeus/reference_data/locations/hotels/_by_hotels.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class ByHotels(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Searches for hotel using it's unique id.
8 |
9 | .. code-block:: python
10 |
11 |
12 | amadeus.reference_data.locations.hotels.by_hotels.get(
13 | hotelIds=["ADPAR001"])
14 |
15 | :param hotelIds: Amadeus Property Codes (8 chars)
16 | For example: ``["ADPAR001"]``
17 |
18 | :rtype: amadeus.Response
19 | :raises amadeus.ResponseError: if the request could not be completed
20 | '''
21 | for key, value in params.items():
22 | if isinstance(value, list):
23 | params[key] = ','.join(value)
24 | return self.client.get(
25 | '/v1/reference-data/locations/hotels/by-hotels', **params)
26 |
--------------------------------------------------------------------------------
/amadeus/reference_data/urls/__init__.py:
--------------------------------------------------------------------------------
1 | from ._checkin_links import CheckinLinks
2 |
3 | __all__ = ['CheckinLinks']
4 |
--------------------------------------------------------------------------------
/amadeus/reference_data/urls/_checkin_links.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class CheckinLinks(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns the checkin links for an airline, for the
8 | language of your choice
9 |
10 | .. code-block:: python
11 |
12 | amadeus.reference_data.urls.checkin_links.get(airlineCode='BA')
13 |
14 | :param airlineCode: the IATA code for the airline, e.g. ``"BA"``
15 | :param language: the locale for the links, for example ``"en-GB"``
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | return self.client.get(
21 | '/v2/reference-data/urls/checkin-links', **params)
22 |
--------------------------------------------------------------------------------
/amadeus/schedule/__init__.py:
--------------------------------------------------------------------------------
1 | from ._flights import Flights
2 |
3 | __all__ = ['Flights']
4 |
--------------------------------------------------------------------------------
/amadeus/schedule/_flights.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Flights(Decorator, object):
5 |
6 | def get(self, **params):
7 | '''
8 | Retrieves a unique flight status by search criteria
9 |
10 | .. code-block:: python
11 |
12 | amadeus.schedule.flights.get(
13 | carrierCode='AZ',
14 | flightNumber='319',
15 | scheduledDepartureDate='2021-03-13')
16 |
17 | :param carrierCode: the IATA carrier code
18 |
19 | :param flightNumber: flight number as assigned by the carrier
20 |
21 | :param scheduledDepartureDate: scheduled departure date of the flight,
22 | local to the departure airport, format YYYY-MM-DD
23 |
24 | :rtype: amadeus.Response
25 | :raises amadeus.ResponseError: if the request could not be completed
26 | '''
27 | return self.client.get('/v2/schedule/flights', **params)
28 |
--------------------------------------------------------------------------------
/amadeus/shopping/__init__.py:
--------------------------------------------------------------------------------
1 | from ._flight_dates import FlightDates
2 | from ._flight_destinations import FlightDestinations
3 | from ._flight_offers_search import FlightOffersSearch
4 | from ._hotel_offer_search import HotelOfferSearch
5 | from ._hotel_offers_search import HotelOffersSearch
6 | from ._activities import Activities
7 | from ._transfer_offers import TransferOffers
8 |
9 | __all__ = ['FlightDates', 'FlightDestinations',
10 | 'HotelOffersSearch', 'HotelOfferSearch',
11 | 'FlightOffersSearch', 'Activities',
12 | 'TransferOffers']
13 |
--------------------------------------------------------------------------------
/amadeus/shopping/_activities.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.shopping.activities._by_square \
3 | import BySquare
4 |
5 |
6 | class Activities(Decorator, object):
7 | def __init__(self, client):
8 | Decorator.__init__(self, client)
9 | self.by_square = BySquare(client)
10 |
11 | def get(self, **params):
12 | '''
13 | Returns activities for a given location
14 |
15 | .. code-block:: python
16 |
17 |
18 | client.shopping.activities.get(
19 | longitude=2.160873,
20 | latitude=41.397158
21 | )
22 |
23 | :param latitude: latitude of geographic location to search around.
24 | For example: ``41.397158``
25 | :param longitude: longitude of geographic location to search around.
26 | For example: ``2.160873``
27 |
28 | :rtype: amadeus.Response
29 | :raises amadeus.ResponseError: if the request could not be completed
30 | '''
31 | return self.client.get(
32 | '/v1/shopping/activities', **params)
33 |
--------------------------------------------------------------------------------
/amadeus/shopping/_activity.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Activity(Decorator, object):
5 | def __init__(self, client, activity_id):
6 | Decorator.__init__(self, client)
7 | self.activity_id = activity_id
8 |
9 | def get(self, **params):
10 | '''
11 | Returns a single activity from a given id.
12 |
13 | .. code-block:: python
14 |
15 | client.shopping.activities('4615').get()
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | return self.client.get('/v1/shopping/activities/{0}'
21 | .format(self.activity_id), **params)
22 |
--------------------------------------------------------------------------------
/amadeus/shopping/_availability.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from .availability import FlightAvailabilities
3 |
4 |
5 | class Availability(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.flight_availabilities = FlightAvailabilities(client)
9 |
--------------------------------------------------------------------------------
/amadeus/shopping/_flight_dates.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class FlightDates(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Find the cheapest flight dates from an origin to a destination.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.shopping.flight_dates.get(origin='NYC', destination='MAD')
12 |
13 | :param origin: the City/Airport IATA code from which the flight will
14 | depart. ``"NYC"``, for example for New-York.
15 |
16 | :param destination: the City/Airport IATA code to which the flight is
17 | going. ``"MAD"``, for example for Madrid.
18 |
19 | :rtype: amadeus.Response
20 | :raises amadeus.ResponseError: if the request could not be completed
21 | '''
22 | return self.client.get('/v1/shopping/flight-dates', **params)
23 |
--------------------------------------------------------------------------------
/amadeus/shopping/_flight_destinations.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class FlightDestinations(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Find the cheapest destinations where you can fly to.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.shopping.flight_destinations.get(origin='LON')
12 |
13 | :param origin: the City/Airport IATA code from which the flight will
14 | depart. ``"LON"``, for example for London.
15 |
16 | :rtype: amadeus.Response
17 | :raises amadeus.ResponseError: if the request could not be completed
18 | '''
19 |
20 | return self.client.get('/v1/shopping/flight-destinations', **params)
21 |
--------------------------------------------------------------------------------
/amadeus/shopping/_flight_offers.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from amadeus.shopping.flight_offers._prediction import FlightChoicePrediction
3 | from amadeus.shopping.flight_offers._pricing import FlightOffersPrice
4 | from amadeus.shopping.flight_offers._upselling import Upselling
5 |
6 |
7 | class FlightOffers(Decorator, object):
8 | def __init__(self, client):
9 | Decorator.__init__(self, client)
10 | self.prediction = FlightChoicePrediction(client)
11 | self.pricing = FlightOffersPrice(client)
12 | self.upselling = Upselling(client)
13 |
--------------------------------------------------------------------------------
/amadeus/shopping/_flight_offers_search.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class FlightOffersSearch(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Get the cheapest flights on a given journey
8 |
9 | .. code-block:: python
10 |
11 | amadeus.shopping.flight_offers_search.get(
12 | originLocationCode='MAD',
13 | destinationLocationCode='BOS',
14 | departureDate='2019-11-01',
15 | adults='1'
16 | )
17 |
18 | :param originLocationCode: the City/Airport IATA code from which
19 | the flight will depart. ``"MAD"``, for example for Madrid.
20 |
21 | :param destinationLocationCode: the City/Airport IATA code to
22 | which the flight is going. ``"BOS"``, for example for Boston.
23 |
24 | :param departureDate: the date on which to fly out, in `YYYY-MM-DD`
25 | format
26 |
27 | :param adults: the number of adult passengers with age 12 or older
28 |
29 | :rtype: amadeus.Response
30 | :raises amadeus.ResponseError: if the request could not be completed
31 | '''
32 | return self.client.get('/v2/shopping/flight-offers', **params)
33 |
34 | def post(self, body):
35 | '''
36 | Get the cheapest flights on a given journey.
37 |
38 | .. code-block:: python
39 |
40 | amadeus.shopping.flight_offers_search.post(body)
41 |
42 | :param body: the parameters to send to the API
43 |
44 | :rtype: amadeus.Response
45 | :raises amadeus.ResponseError: if the request could not be completed
46 | '''
47 | return self.client.post('/v2/shopping/flight-offers', body)
48 |
--------------------------------------------------------------------------------
/amadeus/shopping/_hotel_offer_search.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class HotelOfferSearch(Decorator, object):
5 | def __init__(self, client, offer_id):
6 | Decorator.__init__(self, client)
7 | self.offer_id = offer_id
8 |
9 | def get(self, **params):
10 | '''
11 | Returns details for a specific offer
12 |
13 | .. code-block:: python
14 |
15 | amadeus.shopping.hotel_offer_search('XXX').get
16 |
17 | :rtype: amadeus.Response
18 | :raises amadeus.ResponseError: if the request could not be completed
19 | '''
20 | return self.client.get('/v3/shopping/hotel-offers/{0}'
21 | .format(self.offer_id), **params)
22 |
--------------------------------------------------------------------------------
/amadeus/shopping/_hotel_offers_search.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class HotelOffersSearch(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Get all offers given hotels
8 |
9 | .. code-block:: python
10 |
11 | amadeus.shopping.hotel_offers_search.get(hotelIds=RTPAR001',
12 | adults='2')
13 |
14 | :param hotelId: Amadeus Property Code (8 chars), for
15 | example ``RTPAR001``.
16 |
17 | :param adults: the number of adult passengers with age 12 or older
18 |
19 | :rtype: amadeus.Response
20 | :raises amadeus.ResponseError: if the request could not be completed
21 | '''
22 | return self.client.get('/v3/shopping/hotel-offers', **params)
23 |
--------------------------------------------------------------------------------
/amadeus/shopping/_seatmaps.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Seatmaps(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Allows you to retrieve the seat map of one or several flights based
8 | on the flight-orderId returned from Flight Create Orders API Call.
9 |
10 | .. code-block:: python
11 |
12 | amadeus.shopping.seatmaps.get(
13 | flight-orderId=''
14 | )
15 |
16 | :param flight-orderId: identifier of the order.
17 | Either a flight offer or a flight order Id.
18 |
19 | :rtype: amadeus.Response
20 | :raises amadeus.ResponseError: if the request could not be completed
21 | '''
22 | return self.client.get('/v1/shopping/seatmaps', **params)
23 |
24 | def post(self, body):
25 | '''
26 | Allows you to retrieve the seat map of one or several flights.
27 | Take the body of a flight offer search or flight offer price
28 | and pass it in to this method to get a seatmap
29 |
30 | .. code-block:: python
31 |
32 | amadeus.shopping.seatmaps.post(body)
33 |
34 | :param body: the parameters to send to the API
35 |
36 | :rtype: amadeus.Response
37 | :raises amadeus.ResponseError: if the request could not be completed
38 | '''
39 | return self.client.post('/v1/shopping/seatmaps', body)
40 |
--------------------------------------------------------------------------------
/amadeus/shopping/_transfer_offers.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class TransferOffers(Decorator, object):
5 | def post(self, body):
6 | '''
7 | Get transfer offers
8 |
9 | .. code-block:: python
10 |
11 | amadeus.shopping.transfer_offers.post(body)
12 |
13 | :param body: the parameters to send to the API
14 |
15 | :rtype: amadeus.Response
16 | :raises amadeus.ResponseError: if the request could not be completed
17 | '''
18 | return self.client.post('/v1/shopping/transfer-offers', body)
19 |
--------------------------------------------------------------------------------
/amadeus/shopping/activities/__init__.py:
--------------------------------------------------------------------------------
1 | from ._by_square import BySquare
2 | __all__ = ['BySquare']
3 |
--------------------------------------------------------------------------------
/amadeus/shopping/activities/_by_square.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class BySquare(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns a list of activities
8 | around a defined square (4 points).
9 |
10 | .. code-block:: python
11 |
12 |
13 | client.shopping.activities.by_square.get(
14 | north=41.397158,
15 | west=2.160873,
16 | south=41.394582,
17 | east=2.177181
18 | )
19 |
20 | :param north: latitude north of bounding box.
21 | For example: ``41.397158``
22 | :param west: longitude west of bounding box.
23 | For example: ``2.160873``
24 | :param south: latitude south of bounding box.
25 | For example: ``41.394582``
26 | :param east: longitude east of bounding box.
27 | For example: ``2.177181``
28 |
29 | :rtype: amadeus.Response
30 | :raises amadeus.ResponseError: if the request could not be completed
31 | '''
32 | return self.client.get(
33 | '/v1/shopping/activities/by-square', **params)
34 |
--------------------------------------------------------------------------------
/amadeus/shopping/availability/__init__.py:
--------------------------------------------------------------------------------
1 | from ._flight_availabilities import FlightAvailabilities
2 |
3 | __all__ = ['FlightAvailabilities']
4 |
--------------------------------------------------------------------------------
/amadeus/shopping/availability/_flight_availabilities.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class FlightAvailabilities(Decorator, object):
5 | def post(self, body):
6 | '''
7 | Get available seats in different fare classes
8 |
9 | .. code-block:: python
10 |
11 | amadeus.shopping.availability.flight_availabilities.post(body)
12 |
13 | :param body: the parameters to send to the API
14 |
15 | :rtype: amadeus.Response
16 | :raises amadeus.ResponseError: if the request could not be completed
17 | '''
18 | return self.client.post(
19 | '/v1/shopping/availability/flight-availabilities', body)
20 |
--------------------------------------------------------------------------------
/amadeus/shopping/flight_offers/__init__.py:
--------------------------------------------------------------------------------
1 | from ._prediction import FlightChoicePrediction
2 | from ._pricing import FlightOffersPrice
3 | from ._upselling import Upselling
4 |
5 | __all__ = ['FlightChoicePrediction', 'FlightOffersPrice',
6 | 'Upselling']
7 |
--------------------------------------------------------------------------------
/amadeus/shopping/flight_offers/_prediction.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class FlightChoicePrediction(Decorator, object):
5 | def post(self, body):
6 | '''
7 | Forecast traveler choices in the context of search & shopping.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.shopping.flight_offers.prediction.post(
12 | amadeus.shopping.flight_offers_search.get(
13 | originLocationCode='SYD',
14 | destinationLocationCode='BKK',
15 | departureDate='2020-11-01',
16 | adults=1).result
17 | )
18 |
19 | :rtype: amadeus.Response
20 | :raises amadeus.ResponseError: if the request could not be completed
21 | '''
22 | return self.client.post('/v2/shopping/flight-offers/prediction', body)
23 |
--------------------------------------------------------------------------------
/amadeus/shopping/flight_offers/_pricing.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 | from urllib.parse import urlencode
4 |
5 |
6 | class FlightOffersPrice(Decorator, object):
7 | def post(self, body, **params):
8 | '''
9 | Gets a confirmed price and availability of a flight
10 |
11 | .. code-block:: python
12 |
13 | amadeus.shopping.flight_offers.pricing.post(body, params)
14 |
15 | :rtype: amadeus.Response
16 | :raises amadeus.ResponseError: if the request could not be completed
17 | '''
18 | url = '/v1/shopping/flight-offers/pricing?'
19 | flight_offers = []
20 | if not isinstance(body, list):
21 | flight_offers.append(body)
22 | else:
23 | flight_offers.extend(body)
24 | return self.client.post(url + urlencode(params),
25 | {'data':
26 | {'type': 'flight-offers-pricing',
27 | 'flightOffers': flight_offers}})
28 |
--------------------------------------------------------------------------------
/amadeus/shopping/flight_offers/_upselling.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Upselling(Decorator, object):
5 | def post(self, body):
6 | '''
7 | Get flight offers with branded fares
8 |
9 | .. code-block:: python
10 |
11 | amadeus.shopping.flight_offers.upselling.post(body)
12 |
13 | :param body: the parameters to send to the API
14 |
15 | :rtype: amadeus.Response
16 | :raises amadeus.ResponseError: if the request could not be completed
17 | '''
18 | return self.client.post(
19 | '/v1/shopping/flight-offers/upselling', body)
20 |
--------------------------------------------------------------------------------
/amadeus/travel/__init__.py:
--------------------------------------------------------------------------------
1 | from ._analytics import Analytics
2 | from ._predictions import TripPurpose, FlightDelay
3 |
4 | __all__ = ['Analytics', 'TripPurpose', 'FlightDelay']
5 |
--------------------------------------------------------------------------------
/amadeus/travel/_analytics.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from .analytics._air_traffic import AirTraffic
3 |
4 |
5 | class Analytics(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.air_traffic = AirTraffic(client)
9 |
--------------------------------------------------------------------------------
/amadeus/travel/_predictions.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from .predictions import TripPurpose, FlightDelay
3 |
4 |
5 | class Predictions(Decorator, object):
6 | def __init__(self, client):
7 | Decorator.__init__(self, client)
8 | self.trip_purpose = TripPurpose(client)
9 | self.flight_delay = FlightDelay(client)
10 |
--------------------------------------------------------------------------------
/amadeus/travel/analytics/__init__.py:
--------------------------------------------------------------------------------
1 | from ._air_traffic import AirTraffic
2 |
3 |
4 | __all__ = ['AirTraffic']
5 |
--------------------------------------------------------------------------------
/amadeus/travel/analytics/_air_traffic.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 | from .air_traffic._traveled import Traveled
3 | from .air_traffic._booked import Booked
4 | from .air_traffic._busiest_period import BusiestPeriod
5 |
6 |
7 | class AirTraffic(Decorator, object):
8 | def __init__(self, client):
9 | Decorator.__init__(self, client)
10 | self.booked = Booked(client)
11 | self.traveled = Traveled(client)
12 | self.busiest_period = BusiestPeriod(client)
13 |
--------------------------------------------------------------------------------
/amadeus/travel/analytics/air_traffic/__init__.py:
--------------------------------------------------------------------------------
1 | from ._traveled import Traveled
2 | from ._booked import Booked
3 | from ._busiest_period import BusiestPeriod
4 |
5 |
6 | __all__ = ['Traveled', 'Booked', 'BusiestPeriod']
7 |
--------------------------------------------------------------------------------
/amadeus/travel/analytics/air_traffic/_booked.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Booked(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns a list of air traffic reports, based on bookings.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.travel.analytics.air_traffic.booked.get(
12 | originCityCode='LHR',
13 | period='2017-01'
14 | )
15 |
16 | :param originCityCode: IATA code of the origin city, for
17 | example ``"BOS"`` for Boston.
18 | :param query: period when consumers are traveling
19 | in ``YYYY-MM`` format
20 |
21 | :rtype: amadeus.Response
22 | :raises amadeus.ResponseError: if the request could not be completed
23 | '''
24 | return self.client.get('/v1/travel/analytics/air-traffic/booked',
25 | **params)
26 |
--------------------------------------------------------------------------------
/amadeus/travel/analytics/air_traffic/_busiest_period.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class BusiestPeriod(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns a list of air traffic reports, based on number of travelers.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.travel.analytics.air_traffic.busiest_period.get(
12 | cityCode='MAD',
13 | period='2017',
14 | direction=Direction.ARRIVING
15 | )
16 |
17 | :param cityCode: IATA code of the origin city, for
18 | example ``"BOS"`` for Boston.
19 | :param period: period when consumers are traveling
20 | in ``YYYY`` format
21 | :param direction: to select between
22 | arrivals and departures (default: arrivals)
23 |
24 | :rtype: amadeus.Response
25 | :raises amadeus.ResponseError: if the request could not be completed
26 | '''
27 | return self.client.get('/v1/travel/analytics/air-traffic/busiest-period',
28 | **params)
29 |
--------------------------------------------------------------------------------
/amadeus/travel/analytics/air_traffic/_traveled.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class Traveled(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Returns a list of air traffic reports, based on number of travelers.
8 |
9 | .. code-block:: python
10 |
11 | amadeus.travel.analytics.air_traffic.traveled.get(
12 | originCityCode='LHR',
13 | period='2017-01'
14 | )
15 |
16 | :param originCityCode: IATA code of the origin city, for
17 | example ``"BOS"`` for Boston.
18 | :param period: period when consumers are traveling
19 | in ``YYYY-MM`` format
20 |
21 | :rtype: amadeus.Response
22 | :raises amadeus.ResponseError: if the request could not be completed
23 | '''
24 | return self.client.get('/v1/travel/analytics/air-traffic/traveled',
25 | **params)
26 |
--------------------------------------------------------------------------------
/amadeus/travel/predictions/__init__.py:
--------------------------------------------------------------------------------
1 | from ._trip_purpose import TripPurpose
2 | from ._flight_delay import FlightDelay
3 |
4 |
5 | __all__ = ['TripPurpose', 'FlightDelay']
6 |
--------------------------------------------------------------------------------
/amadeus/travel/predictions/_flight_delay.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class FlightDelay(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Forecast the chances for a flight to be delayed
8 |
9 | .. code-block:: python
10 |
11 | amadeus.travel.predictions.flight_delay.get(originLocationCode='NCE',
12 | destinationLocationCode='IST',
13 | departureDate='2020-08-01',
14 | departureTime='18:20:00',
15 | arrivalDate='2020-08-01',
16 | arrivalTime='22:15:00',
17 | aircraftCode='321',
18 | carrierCode='TK',
19 | flightNumber='1816',
20 | duration='PT31H10M')
21 |
22 | :param originLocationCode: the City/Airport IATA code from which
23 | the flight will depart. ``"NYC"``, for example for New York
24 |
25 | :param destinationLocationCode: the City/Airport IATA code to which
26 | the flight is going. ``"MAD"``, for example for Madrid
27 |
28 | :param departureDate: the date on which the traveler departs
29 | from the origin, in `YYYY-MM-DD` format
30 |
31 | :param departureTime: local time on which to fly out,
32 | in `HH:MM:SS` format
33 |
34 | :param arrivalDate: the date on which the traveler arrives
35 | to the destination, in `YYYY-MM-DD` format
36 |
37 | :param arrivalTime: local time on which the traveler arrives
38 | to the destination, in `HH:MM:SS` format
39 |
40 | :param aircraftCode: IATA aircraft code
41 |
42 | :param carrierCode: airline / carrier code
43 |
44 | :param flightNumber: flight number as assigned by the carrier
45 |
46 | :param duration: flight duration,
47 | in `PnYnMnDTnHnMnS` format e.g. PT2H10M
48 |
49 | :rtype: amadeus.Response
50 | :raises amadeus.ResponseError: if the request could not be completed
51 | '''
52 | return self.client.get('/v1/travel/predictions/flight-delay', **params)
53 |
--------------------------------------------------------------------------------
/amadeus/travel/predictions/_trip_purpose.py:
--------------------------------------------------------------------------------
1 | from amadeus.client.decorator import Decorator
2 |
3 |
4 | class TripPurpose(Decorator, object):
5 | def get(self, **params):
6 | '''
7 | Predicts traveler purpose, Business or Leisure,
8 | with the probability in the context of search & shopping
9 |
10 | .. code-block:: python
11 |
12 | amadeus.travel.predictions.trip_purpose.get(
13 | originLocationCode='NYC',
14 | destinationLocationCode='MAD',
15 | departureDate='2020-08-01',
16 | returnDate='2020-08-12',
17 | searchDate='2020-06-11')
18 |
19 | :param originLocationCode: the City/Airport IATA code from which
20 | the flight will depart. ``"NYC"``, for example for New York
21 |
22 | :param destinationLocationCode: the City/Airport IATA code to which
23 | the flight is going. ``"MAD"``, for example for Madrid
24 |
25 | :param departureDate: the date on which to fly out, in `YYYY-MM-DD` format
26 |
27 | :param returnDate: the date on which the flight returns to the origin,
28 | in `YYYY-MM-DD` format
29 |
30 | :param searchDate: the date on which the traveler performs the search,
31 | in `YYYY-MM-DD` format.
32 | If it is not specified the current date will be used
33 |
34 | :rtype: amadeus.Response
35 | :raises amadeus.ResponseError: if the request could not be completed
36 | '''
37 | return self.client.get('/v1/travel/predictions/trip-purpose', **params)
38 |
--------------------------------------------------------------------------------
/amadeus/version.py:
--------------------------------------------------------------------------------
1 | version_info = (12, 0, 0)
2 | version = '.'.join(str(v) for v in version_info)
3 |
--------------------------------------------------------------------------------
/docs/_static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/amadeus4dev/amadeus-python/0bc7089cb2e49b5b662ee890a5fe611f59ad4332/docs/_static/.gitkeep
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | #
3 | # Configuration file for the Sphinx documentation builder.
4 | #
5 | # This file does only contain a selection of the most common options. For a
6 | # full list see the documentation:
7 | # http://www.sphinx-doc.org/en/stable/config
8 |
9 | # -- Path setup --------------------------------------------------------------
10 |
11 | # If extensions (or modules to document with autodoc) are in another directory,
12 | # add these directories to sys.path here. If the directory is relative to the
13 | # documentation root, use os.path.abspath to make it absolute, like shown here.
14 | #
15 | # import os
16 | # import sys
17 | # sys.path.insert(0, os.path.abspath('.'))
18 |
19 | from amadeus import version
20 |
21 | # -- Project information -----------------------------------------------------
22 |
23 | project = 'Amadeus'
24 | copyright = '2018, Amadeus4Dev'
25 | author = 'Amadeus4Dev'
26 |
27 | # The short X.Y version
28 | version = version
29 | # The full version, including alpha/beta/rc tags
30 | release = version
31 |
32 |
33 | # -- General configuration ---------------------------------------------------
34 |
35 | # If your documentation needs a minimal Sphinx version, state it here.
36 | #
37 | # needs_sphinx = '1.0'
38 |
39 | # Add any Sphinx extension module names here, as strings. They can be
40 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
41 | # ones.
42 | extensions = [
43 | 'sphinx.ext.autodoc',
44 | 'sphinx.ext.doctest',
45 | 'sphinx.ext.coverage',
46 | 'sphinx.ext.viewcode',
47 | 'sphinx.ext.githubpages',
48 | 'sphinx.ext.intersphinx',
49 | ]
50 |
51 | intersphinx_mapping = {
52 | 'python': ('https://docs.python.org/3', None)
53 | }
54 |
55 | # Add any paths that contain templates here, relative to this directory.
56 | templates_path = ['_templates']
57 |
58 | # The suffix(es) of source filenames.
59 | # You can specify multiple suffix as a list of string:
60 | #
61 | # source_suffix = ['.rst', '.md']
62 | source_suffix = '.rst'
63 |
64 | # The master toctree document.
65 | master_doc = 'index'
66 |
67 | # The language for content autogenerated by Sphinx. Refer to documentation
68 | # for a list of supported languages.
69 | #
70 | # This is also used if you do content translation via gettext catalogs.
71 | # Usually you set "language" from the command line for these cases.
72 | language = None
73 |
74 | # List of patterns, relative to source directory, that match files and
75 | # directories to ignore when looking for source files.
76 | # This pattern also affects html_static_path and html_extra_path .
77 | exclude_patterns = []
78 |
79 | # The name of the Pygments (syntax highlighting) style to use.
80 | pygments_style = 'sphinx'
81 |
82 |
83 | # -- Options for HTML output -------------------------------------------------
84 |
85 | # The theme to use for HTML and HTML Help pages. See the documentation for
86 | # a list of builtin themes.
87 | #
88 | html_theme = "sphinx_rtd_theme"
89 |
90 | # Theme options are theme-specific and customize the look and feel of a theme
91 | # further. For a list of options available for each theme, see the
92 | # documentation.
93 | #
94 | # html_theme_options = {}
95 |
96 | # Add any paths that contain custom static files (such as style sheets) here,
97 | # relative to this directory. They are copied after the builtin static files,
98 | # so a file named "default.css" will overwrite the builtin "default.css".
99 | html_static_path = ['_static']
100 |
101 | # Custom sidebar templates, must be a dictionary that maps document names
102 | # to template names.
103 | #
104 | # The default sidebars (for documents that don't match any pattern) are
105 | # defined by theme itself. Builtin themes are using these templates by
106 | # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
107 | # 'searchbox.html']``.
108 | #
109 | # html_sidebars = {}
110 |
111 |
112 | # -- Options for HTMLHelp output ---------------------------------------------
113 |
114 | # Output file base name for HTML help builder.
115 | htmlhelp_basename = 'Amadeusdoc'
116 |
117 |
118 | # -- Options for LaTeX output ------------------------------------------------
119 |
120 | latex_elements = {
121 | # The paper size ('letterpaper' or 'a4paper').
122 | #
123 | # 'papersize': 'letterpaper',
124 |
125 | # The font size ('10pt', '11pt' or '12pt').
126 | #
127 | # 'pointsize': '10pt',
128 |
129 | # Additional stuff for the LaTeX preamble.
130 | #
131 | # 'preamble': '',
132 |
133 | # Latex figure (float) alignment
134 | #
135 | # 'figure_align': 'htbp',
136 | }
137 |
138 | # Grouping the document tree into LaTeX files. List of tuples
139 | # (source start file, target name, title,
140 | # author, documentclass [howto, manual, or own class]).
141 | latex_documents = [
142 | (master_doc, 'Amadeus.tex', 'Amadeus Documentation',
143 | 'Amadeus4Dev', 'manual'),
144 | ]
145 |
146 |
147 | # -- Options for manual page output ------------------------------------------
148 |
149 | # One entry per manual page. List of tuples
150 | # (source start file, name, description, authors, manual section).
151 | man_pages = [
152 | (master_doc, 'amadeus', 'Amadeus Documentation',
153 | [author], 1)
154 | ]
155 |
156 |
157 | # -- Options for Texinfo output ----------------------------------------------
158 |
159 | # Grouping the document tree into Texinfo files. List of tuples
160 | # (source start file, target name, title, author,
161 | # dir menu entry, description, category)
162 | texinfo_documents = [
163 | (master_doc, 'Amadeus', 'Amadeus Documentation',
164 | author, 'Amadeus', 'One line description of project.',
165 | 'Miscellaneous'),
166 | ]
167 |
168 |
169 | # -- Extension configuration -------------------------------------------------
170 |
171 | html_theme_options = {
172 | 'display_version': True,
173 | }
174 |
175 | html_context = {
176 | 'display_github': True,
177 | 'github_user': 'amadeus4dev',
178 | 'github_repo': 'amadeus-python',
179 | 'github_version': 'master',
180 | 'conf_py_path': '/docs/'
181 | }
182 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Reference
2 | *********
3 |
4 | Client
5 | ======
6 |
7 | .. autoclass:: amadeus.Client
8 | :members: __init__, get, post, request, previous, next, first, last
9 |
10 | Response
11 | ========
12 |
13 | .. autoclass:: amadeus.Response
14 |
15 | ResponseError
16 | =============
17 |
18 | .. autoclass:: amadeus.ResponseError
19 | .. autoclass:: amadeus.AuthenticationError
20 | :show-inheritance:
21 | .. autoclass:: amadeus.ClientError
22 | :show-inheritance:
23 | .. autoclass:: amadeus.NetworkError
24 | :show-inheritance:
25 | .. autoclass:: amadeus.ServerError
26 | :show-inheritance:
27 | .. autoclass:: amadeus.NotFoundError
28 | :show-inheritance:
29 | .. autoclass:: amadeus.ParserError
30 | :show-inheritance:
31 |
32 | Request
33 | =======
34 |
35 | .. autoclass:: amadeus.Request
36 |
37 |
38 | Shopping/Flights
39 | ================
40 |
41 | .. autoclass:: amadeus.shopping.FlightDestinations
42 | :members: get
43 |
44 | .. autoclass:: amadeus.shopping.FlightDates
45 | :members: get
46 |
47 | .. autoclass:: amadeus.shopping.FlightOffersSearch
48 | :members: get
49 |
50 | .. autoclass:: amadeus.shopping.FlightOffersSearch
51 | :members: post
52 |
53 | Shopping/Hotels
54 | ===============
55 |
56 | .. autoclass:: amadeus.shopping.hotel.HotelOffersSearch
57 | :members: get
58 |
59 | .. autoclass:: amadeus.shopping.hotel.HotelOfferSearch
60 | :members: get
61 |
62 | Shopping/FlightOffers
63 | ===============
64 |
65 | .. autoclass:: amadeus.shopping.flight_offers.FlightChoicePrediction
66 | :members: post
67 |
68 | .. autoclass:: amadeus.shopping.flight_offers.FlightChoicePrice
69 | :members: post
70 |
71 | .. autoclass:: amadeus.shopping.flight_offers.Upselling
72 | :members: post
73 |
74 | Shopping/Activities
75 | ===============
76 |
77 | .. autoclass:: amadeus.shopping.Activities
78 | :members: get
79 |
80 | .. autoclass:: amadeus.shopping.activities.BySquare
81 | :members: get
82 |
83 | .. autoclass:: amadeus.shopping.Activity
84 | :members: get
85 |
86 | Shopping/Availability
87 | ===============
88 |
89 | .. autoclass:: amadeus.shopping.availability.FlightAvailabilities
90 | :members: post
91 |
92 | Shopping/Transfers
93 | ================
94 |
95 | .. autoclass:: amadeus.shopping.TransferOffers
96 | :members: post
97 |
98 | Travel/Analytics
99 | ================
100 |
101 | .. autoclass:: amadeus.travel.analytics.AirTraffic
102 | :members: get
103 |
104 | .. autoclass:: amadeus.travel.analytics.FareSearches
105 | :members: get
106 |
107 | Travel/Predictions
108 | ================
109 |
110 | .. autoclass:: amadeus.travel.predictions.TripPurpose
111 | :members: get
112 |
113 | .. autoclass:: amadeus.travel.predictions.FlightDelay
114 | :members: get
115 |
116 | ReferenceData/Locations
117 | =======================
118 |
119 | .. autoclass:: amadeus.reference_data.Location
120 | :members: get
121 |
122 | .. autoclass:: amadeus.reference_data.Locations
123 | :members: get
124 |
125 | .. autoclass:: amadeus.reference_data.locations.Airports
126 | :members: get
127 |
128 | .. autoclass:: amadeus.reference_data.Airlines
129 | :members: get
130 |
131 | ReferenceData/Urls
132 | ==================
133 |
134 | .. autoclass:: amadeus.reference_data.urls.CheckinLinks
135 | :members: get
136 |
137 | ReferenceData/RecommendedLocations
138 | ==================
139 |
140 | .. autoclass:: amadeus.reference_data.RecommendedLocations
141 | :members: get
142 |
143 | ReferenceData/Locations/Hotels
144 | =======================
145 |
146 | .. autoclass:: amadeus.reference_data.hotels.ByHotels
147 | :members: get
148 |
149 | .. autoclass:: amadeus.reference_data.hotels.ByCity
150 | :members: get
151 |
152 | .. autoclass:: amadeus.reference_data.hotels.ByGeocode
153 | :members: get
154 |
155 | ReferenceData/Locations/Hotel
156 | =======================
157 |
158 | .. autoclass:: amadeus.reference_data.locations.Hotel
159 | :members: get
160 |
161 | ReferenceData/Locations/Cities
162 | =======================
163 |
164 | .. autoclass:: amadeus.reference_data.locations.Cities
165 | :members: get
166 |
167 | Helper/Location
168 | ==================
169 |
170 | .. autoclass:: amadeus.Location
171 |
172 | Airport/Predictions
173 | ================
174 |
175 | .. autoclass:: amadeus.airport.predictions.AirportOnTime
176 | :members: get
177 |
178 | Airport/DirectDestinations
179 | ================
180 |
181 | .. autoclass:: amadeus.airport.DirectDestinations
182 | :members: get
183 |
184 | Media/Files
185 | ================
186 |
187 |
188 | Booking
189 | ================
190 |
191 | .. autoclass:: amadeus.booking.FlightOrders
192 | :members: post
193 |
194 | .. autoclass:: amadeus.booking.FlightOrder
195 | :members: get
196 |
197 | .. autoclass:: amadeus.booking.FlightOrder
198 | :members: delete
199 |
200 | .. autoclass:: amadeus.booking.HotelBookings
201 | :members: post
202 |
203 | .. autoclass:: amadeus.booking.HotelOrders
204 | :members: post
205 |
206 |
207 | Schedule/Flights
208 | ================
209 |
210 | .. autoclass:: amadeus.schedule.Flights
211 | :members: get
212 |
213 | Analytics/ItineraryPriceMetrics
214 | ================
215 |
216 | .. autoclass:: amadeus.analytics.ItineraryPriceMetrics
217 | :members: get
218 |
219 | Airline/Destinations
220 | ================
221 |
222 | .. autoclass:: amadeus.airline.Destinations
223 | :members: get
224 |
225 | Ordering/Transfers
226 | ================
227 |
228 | .. autoclass:: amadeus.ordering.TransferOrders
229 | :members: post
230 |
231 | .. autoclass:: amadeus.ordering.transfer_orders.transfers.Cancellation
232 | :members: post
233 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | flake8==3.8.0
2 | flake8-quotes==2.1.1
3 | tox==3.20.0
4 | sphinx==5.0.0
5 | sphinx-rtd-theme==0.5.2
6 | pytest==7.2.0
7 | pytest-cov==4.0.0
8 | Jinja2<3.1
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [bdist_wheel]
2 | universal=1
3 |
4 | [metadata]
5 | license_file = LICENSE
6 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import io
2 | import os
3 |
4 | from setuptools import setup, find_packages
5 |
6 | # Load the version number
7 | about = {}
8 | here = os.path.abspath(os.path.dirname(__file__))
9 | with open(os.path.join(here, 'amadeus', 'version.py')) as f:
10 | exec(f.read(), about)
11 |
12 | # Import the README and use it as the long-description.
13 | with io.open(os.path.join(here, 'README.rst'), encoding='utf-8') as f:
14 | long_description = '\n' + f.read()
15 |
16 | setup(
17 | name='amadeus',
18 | version=about['version'],
19 | description='Python module for the Amadeus travel APIs',
20 | long_description=long_description,
21 | author='Amadeus',
22 | author_email='developers@amadeus.com',
23 | python_requires='>=3.4.8',
24 | url='https://github.com/amadeus4dev/amadeus-python',
25 | install_requires=[],
26 | packages=find_packages(),
27 | data_files=[('docs', ['README.rst', 'CHANGELOG.rst'])],
28 | include_package_data=True,
29 | license='MIT',
30 | classifiers=[
31 | 'License :: OSI Approved :: MIT License',
32 | 'Operating System :: OS Independent',
33 | 'Programming Language :: Python',
34 | 'Programming Language :: Python :: 3.4',
35 | 'Programming Language :: Python :: 3.5',
36 | 'Programming Language :: Python :: 3.6',
37 | 'Topic :: Software Development :: Libraries :: Python Modules',
38 | ]
39 | )
40 |
--------------------------------------------------------------------------------
/specs/client/test_access_token.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import mock
3 | from amadeus import Client
4 | from amadeus.client.access_token import AccessToken
5 |
6 |
7 | class TokenResponse:
8 | def __init__(self, result):
9 | self.result = result
10 |
11 |
12 | @pytest.fixture
13 | def self():
14 | self.request = mock.MagicMock(
15 | return_value=TokenResponse({'access_token': 'abc', 'expires_in': 1799}))
16 | self.client = mock.MagicMock(Client)
17 | self.client._unauthenticated_request = self.request
18 | self.client.client_id = '123'
19 | self.client.client_secret = '234'
20 | self.access_token = AccessToken(self.client)
21 | return self
22 |
23 |
24 | def test_bearer_token(self):
25 | token = self.access_token._bearer_token()
26 | assert token == 'Bearer abc'
27 | self.client._unauthenticated_request.assert_called_with(
28 | 'POST', '/v1/security/oauth2/token',
29 | {
30 | 'grant_type': 'client_credentials',
31 | 'client_id': '123',
32 | 'client_secret': '234'
33 | })
34 |
35 |
36 | def test_cached_token_valid(self):
37 | access_token = self.access_token
38 | token = access_token._bearer_token()
39 | assert token == 'Bearer abc'
40 | self.client._unauthenticated_request.assert_called_once()
41 | token2 = access_token._bearer_token()
42 | assert token2 == 'Bearer abc'
43 | self.client._unauthenticated_request.assert_called_once()
44 |
45 |
46 | def test_cached_token_expired(self):
47 | access_token = self.access_token
48 | token = access_token._bearer_token()
49 | assert token == 'Bearer abc'
50 | access_token.expires_at = 0
51 | token2 = access_token._bearer_token()
52 | assert token2 == 'Bearer abc'
53 | assert self.client._unauthenticated_request.call_count == 2
54 |
--------------------------------------------------------------------------------
/specs/client/test_request.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from amadeus import Request
3 | from urllib.request import Request as HTTPRequest
4 |
5 |
6 | @pytest.fixture
7 | def self():
8 | self.host = 'example.com'
9 | self.verb = 'GET'
10 | self.path = '/foo/bar'
11 | self.params = {'foo': 'bar'}
12 | self.bearer_token = 'Bearer 123'
13 | self.client_version = '1.2.3'
14 | self.lang_version = '2.3.4'
15 | self.app_id = 'amadeus-cli'
16 | self.app_version = '3.4.5'
17 | self.ssl = True
18 | self.port = 443
19 | self.request = Request({
20 | 'host': self.host,
21 | 'verb': self.verb,
22 | 'path': self.path,
23 | 'params': self.params,
24 | 'bearer_token': self.bearer_token,
25 | 'client_version': self.client_version,
26 | 'language_version': self.lang_version,
27 | 'app_id': self.app_id,
28 | 'app_version': self.app_version,
29 | 'ssl': self.ssl,
30 | 'port': self.port
31 | })
32 | return self
33 |
34 |
35 | def test_init(self):
36 | assert self.request.host == self.host
37 | assert self.request.port == self.port
38 | assert self.request.ssl is True
39 | assert self.request.scheme == 'https'
40 | assert self.request.verb == self.verb
41 | assert self.request.path == self.path
42 | assert self.request.params == self.params
43 | assert self.request.bearer_token == self.bearer_token
44 | assert self.request.client_version == self.client_version
45 | assert self.request.language_version == self.lang_version
46 | assert self.request.app_id == self.app_id
47 | assert self.request.app_version == self.app_version
48 |
49 |
50 | def test_http_request(self):
51 | assert isinstance(self.request.http_request, HTTPRequest)
52 | assert self.request.http_request.data is None
53 | assert self.request.http_request.get_header('Content-Type', None) is None
54 | assert self.request.http_request.get_header(
55 | 'Authorization') == self.bearer_token
56 | assert self.request.http_request.get_header(
57 | 'Accept') == 'application/json, application/vnd.amadeus+json'
58 | assert self.request.http_request.get_header(
59 | 'User-agent') == 'amadeus-python/1.2.3 python/2.3.4 amadeus-cli/3.4.5'
60 |
61 | self.request = Request({
62 | 'host': self.host,
63 | 'verb': 'POST',
64 | 'path': self.path,
65 | 'params': self.params,
66 | 'bearer_token': self.bearer_token,
67 | 'client_version': self.client_version,
68 | 'language_version': self.lang_version,
69 | 'app_id': self.app_id,
70 | 'app_version': self.app_version,
71 | 'port': self.port,
72 | 'ssl': self.ssl
73 | })
74 | assert isinstance(self.request.http_request, HTTPRequest)
75 | url = self.request.http_request.get_full_url()
76 | assert url == 'https://example.com/foo/bar'
77 | assert self.request.http_request.data == b'{\"foo\": \"bar\"}'
78 |
79 |
80 | def test_x_http_method_override(self):
81 | for path in Request.list_httpoverride:
82 | self.request = Request({
83 | 'host': self.host,
84 | 'verb': 'POST',
85 | 'path': path,
86 | 'params': self.params,
87 | 'bearer_token': self.bearer_token,
88 | 'client_version': self.client_version,
89 | 'language_version': self.lang_version,
90 | 'app_id': self.app_id,
91 | 'app_version': self.app_version,
92 | 'port': self.port,
93 | 'ssl': self.ssl
94 | })
95 | assert isinstance(self.request.http_request, HTTPRequest)
96 | assert self.request.headers['X-HTTP-Method-Override'] == 'GET'
97 |
98 |
99 | def test_custom_scheme_and_port(self):
100 | self.request = Request({
101 | 'host': self.host,
102 | 'verb': 'POST',
103 | 'path': self.path,
104 | 'params': self.params,
105 | 'bearer_token': self.bearer_token,
106 | 'client_version': self.client_version,
107 | 'language_version': self.lang_version,
108 | 'app_id': self.app_id,
109 | 'app_version': self.app_version,
110 | 'port': 8080,
111 | 'ssl': False
112 | })
113 | url = self.request.http_request.get_full_url()
114 | assert url == 'http://example.com:8080/foo/bar'
115 |
--------------------------------------------------------------------------------
/specs/client/test_response.py:
--------------------------------------------------------------------------------
1 | import mock
2 | from amadeus import Response
3 |
4 |
5 | def test_response_init():
6 | http_response = mock.MagicMock()
7 | request = mock.MagicMock()
8 | response = Response(http_response, request)
9 |
10 | assert response.http_response == http_response
11 | assert response.request == request
12 |
--------------------------------------------------------------------------------
/specs/mixins/test_errors.py:
--------------------------------------------------------------------------------
1 | from mock import MagicMock
2 | from unittest.mock import Mock
3 |
4 | from amadeus import NetworkError, Response, Client
5 |
6 |
7 | def test_ResponseError_str_representation():
8 | # Test str(error) with no response present
9 | error = NetworkError(None)
10 | assert str(error) == '[---]'
11 |
12 | # Test str(error) with no data present
13 | response = Mock(Response)
14 | response.parsed = True
15 | response.result = {}
16 | response.status_code = 400
17 |
18 | error = NetworkError(response)
19 | assert str(error) == '[400]'
20 |
21 | # Test str(error) with errors present
22 | response = Mock(Response)
23 | response.parsed = True
24 | response.result = {
25 | 'errors': [
26 | {
27 | 'detail': 'This field must be filled.',
28 | 'source': {'parameter': 'departureDate'},
29 | },
30 | {
31 | 'detail': 'This field must be filled.',
32 | 'source': {'parameter': 'origin'},
33 | },
34 | {
35 | 'detail': 'This field must be filled.',
36 | 'source': {'parameter': 'destination'},
37 | },
38 | ]
39 | }
40 | response.status_code = 401
41 |
42 | error = NetworkError(response)
43 | error = NetworkError(response)
44 | assert (
45 | ('''[401]
46 | [departureDate] This field must be filled.
47 | [origin] This field must be filled.
48 | [destination] This field must be filled.''')
49 | )
50 |
51 | # Test str(error) with error_description present
52 | response = Mock(Response)
53 | response.parsed = True
54 | response.result = {'error_description': 'error'}
55 | response.status_code = 401
56 |
57 | error = NetworkError(response)
58 | assert str(error) == '[401]\nerror'
59 |
60 |
61 | def test_ResponseError_code():
62 | # Test .code with no response present
63 | error = NetworkError(None)
64 | assert error.code == 'NetworkError'
65 |
66 |
67 | def test_ResponseError_log():
68 | # Test .log with log level set to 'warn'
69 | client = Mock(Client)
70 | client.logger = MagicMock()
71 | client.log_level = 'warn'
72 |
73 | error = NetworkError(None)
74 | error.code = 'Foo'
75 | error.description = 'Bar'
76 | error._log(client)
77 |
78 | assert client.logger.warning.called_with('Amadeus %s: %s', 'Foo', 'Bar')
79 |
80 | # Test .log with log level set to 'silent'
81 | client = Mock(Client)
82 | client.logger = MagicMock()
83 | client.log_level = 'silent'
84 |
85 | error = NetworkError(None)
86 | error._log(client)
87 |
88 | assert not client.logger.warning.called
89 |
--------------------------------------------------------------------------------
/specs/mixins/test_http.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import mock
3 | import unittest.mock
4 | from amadeus import Client, Response, ResponseError
5 | from urllib.error import URLError
6 |
7 |
8 | @pytest.fixture
9 | def self():
10 | self = mock.MagicMock()
11 | self.client = Client(client_id='123', client_secret='234', log_level='silent')
12 | self.response = Response(None, None)
13 | self.request_method = mock.MagicMock(return_value=self.response)
14 | return self
15 |
16 |
17 | def test_client_get(self):
18 | self.client.request = self.request_method
19 | response = self.client.get('/foo', foo='bar')
20 | assert response == self.response
21 | self.client.request.assert_called_with('GET', '/foo', {'foo': 'bar'})
22 |
23 |
24 | def test_client_delete(self):
25 | self.client.request = self.request_method
26 | response = self.client.delete('/foo', foo='bar')
27 | assert response == self.response
28 | self.client.request.assert_called_with(
29 | 'DELETE', '/foo', {'foo': 'bar'})
30 |
31 |
32 | def test_client_post(self):
33 | self.client.request = self.request_method
34 | response = self.client.post('/foo', {'foo': 'bar'})
35 | assert response == self.response
36 | self.client.request.assert_called_with('POST', '/foo', {'foo': 'bar'})
37 |
38 |
39 | def test_client_request(self):
40 | self.response.result = {'access_token': '123'}
41 | self.client._unauthenticated_request = self.request_method
42 | response = self.client.request('POST', '/foo', {'foo': 'bar'})
43 | assert response == self.response
44 | assert self.client._unauthenticated_request.call_args == mock.call(
45 | 'POST', '/foo', {'foo': 'bar'}, 'Bearer 123'
46 | )
47 |
48 |
49 | def test_client_request_use_same_access_token(self):
50 | self.response.result = {'access_token': '123', 'expires_in': 2000}
51 | self.client._unauthenticated_request = self.request_method
52 | self.client.request('POST', '/foo', {'foo': 'bar'})
53 |
54 | self.response.result = {'access_token': '234', 'expires_in': 2000}
55 | self.client._unauthenticated_request = self.request_method
56 | self.client.request('POST', '/foo', {'foo': 'bar'})
57 | assert not self.client._unauthenticated_request.assert_called_with(
58 | 'POST', '/foo', {'foo': 'bar'}, 'Bearer 123')
59 |
60 |
61 | def test_unauthenticated_request(self):
62 | http_response = mock.MagicMock()
63 | http_response.code = 200
64 | http_response.getheaders.return_value = [('Content-Type', 'application/json')]
65 | http_response.read.return_value = '{ "data" : { "a" : 1 } }'
66 |
67 | # Patch the client's `http` method to return the mock HTTP response
68 | unittest.mock.patch.object(self.client, 'http', return_value=http_response)
69 |
70 | # Test the client's _unauthenticated_request method
71 | with pytest.raises(ResponseError):
72 | self.client._unauthenticated_request('GET', '/foo', {}, None)
73 |
74 | # Test that a HTTPError is caught
75 | self.client.http.side_effect = URLError('Error')
76 | with pytest.raises(ResponseError):
77 | self.client._unauthenticated_request('GET', '/foo', {}, None)
78 |
79 | # Test logging in debug mode
80 | with mock.patch.object(self.client, 'http', return_value=http_response):
81 | logger = mock.MagicMock()
82 | self.client.logger = logger
83 | self.client.log_level = 'debug'
84 | with pytest.raises(ResponseError):
85 | self.client._unauthenticated_request(
86 | 'GET', '/foo', {}, None)
87 |
--------------------------------------------------------------------------------
/specs/mixins/test_pagination.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import mock
3 | from amadeus import Client, Response, Request
4 | from amadeus.mixins.pagination import Pagination
5 |
6 |
7 | @pytest.fixture
8 | def self():
9 | # Initialize the client and pagination object
10 | self.next_response = mock.MagicMock(Response)
11 | client = mock.MagicMock(Client)
12 | client.request = self.next_response
13 | self.client = client
14 | self.pagination = Pagination()
15 | self.pagination.request = self.client.request
16 |
17 | # Initialize the previous request made and response received
18 | self.request = mock.MagicMock(Request)
19 | self.request.verb = self.verb = 'GET'
20 | self.request.path = self.path = '/a'
21 | self.request.params = self.params = {'a': 'b'}
22 | self.response = Response(mock.MagicMock('http_response'), self.request)
23 | return self
24 |
25 |
26 | def test_create_new_request_when_previous(self):
27 | self.response.result = {
28 | 'meta': {'links': {'previous': 'https://f.co?page%5Boffset%5D=1'}}
29 | }
30 | self.pagination.previous(self.response)
31 | self.client.request.call_args.assert_called_with(
32 | 'GET', '/a', {'a': 'b', 'page': {'offset': '1'}})
33 |
34 |
35 | def test_should_return_nil_page_not_found_with_previous(self):
36 | self.response.result = {'meta': {'links': {}}}
37 | next_response = self.pagination.previous(self.response)
38 | assert next_response is None
39 |
40 |
41 | def test_create_new_request_when_next(self):
42 | self.response.result = {
43 | 'meta': {'links': {'next': 'https://f.co?page%5Boffset%5D=1'}}
44 | }
45 | self.pagination.next(self.response)
46 | self.client.request.call_args.assert_called_with(
47 | 'GET', '/a', {'a': 'b', 'page': {'offset': '1'}})
48 |
49 |
50 | def test_should_return_nil_page_not_found_with_next(self):
51 | self.response.result = {'meta': {'links': {}}}
52 | next_response = self.pagination.next(self.response)
53 | assert next_response is None
54 |
55 |
56 | def test_create_new_request_when_first(self):
57 | self.response.result = {
58 | 'meta': {'links': {'first': 'https://f.co?page%5Boffset%5D=1'}}
59 | }
60 | self.pagination.first(self.response)
61 | self.client.request.call_args.assert_called_with(
62 | 'GET', '/a', {'a': 'b', 'page': {'offset': '1'}})
63 |
64 |
65 | def test_should_return_nil_page_not_found_with_first(self):
66 | self.response.result = {'meta': {'links': {}}}
67 | next_response = self.pagination.first(self.response)
68 | assert next_response is None
69 |
70 |
71 | def test_create_new_request_when_last(self):
72 | self.response.result = {
73 | 'meta': {'links': {'last': 'https://f.co?page%5Boffset%5D=1'}}
74 | }
75 | self.pagination.last(self.response)
76 | self.client.request.call_args.assert_called_with(
77 | 'GET', '/a', {'a': 'b', 'page': {'offset': '1'}})
78 |
79 |
80 | def test_should_return_nil_page_not_found_with_last(self):
81 | self.response.result = {'meta': {'links': {}}}
82 | next_response = self.pagination.last(self.response)
83 | assert next_response is None
84 |
--------------------------------------------------------------------------------
/specs/mixins/test_parser.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import mock
3 | from amadeus import Response, Request, Client
4 | from amadeus import NetworkError, ServerError, AuthenticationError
5 | from amadeus import NotFoundError, ClientError, ParserError
6 |
7 |
8 | @pytest.fixture
9 | def self():
10 | self.request = mock.MagicMock(Request)
11 | self.client = mock.MagicMock(Client)
12 | self.client.log_level = 'silent'
13 | return self
14 |
15 |
16 | @pytest.fixture
17 | def response_setup():
18 | http_response = mock.MagicMock()
19 | request = mock.MagicMock(Request)
20 | response_setup.client = mock.MagicMock(Client)
21 | response_setup.client.log_level = 'silent'
22 | response_setup.response = Response(http_response, request)
23 | return response_setup
24 |
25 |
26 | def test_should_parse_body(self, response_setup):
27 | response_setup.response.status_code = '200'
28 | response_setup.response.headers = {'Content-Type': 'application/json'}
29 | response_setup.response.text = '{ "data" : { "a" : 1 } }'
30 | response = Response(response_setup.response, self.request)
31 | response = response._parse(response_setup.client)
32 | assert isinstance(response, Response)
33 |
34 |
35 | def test_should_raise_network_error_with_no_code(response_setup):
36 | response_setup.response.status_code = None
37 | response_setup.response.parsed = False
38 | with pytest.raises(NetworkError):
39 | response_setup.response._detect_error(response_setup.client)
40 |
41 |
42 | def test_should_raise_server_error_with_500(response_setup):
43 | response_setup.response.status_code = 500
44 | response_setup.response.parsed = False
45 | with pytest.raises(ServerError):
46 | response_setup.response._detect_error(response_setup.client)
47 |
48 |
49 | def test_should_raise_auth_error_with_401(response_setup):
50 | response_setup.response.status_code = 401
51 | response_setup.response.parsed = False
52 | with pytest.raises(AuthenticationError):
53 | response_setup.response._detect_error(response_setup.client)
54 |
55 |
56 | def test_should_raise_not_found_error_with_404(response_setup):
57 | response_setup.response.status_code = 404
58 | response_setup.response.parsed = False
59 | with pytest.raises(NotFoundError):
60 | response_setup.response._detect_error(response_setup.client)
61 |
62 |
63 | def test_should_raise_a_generic_error_with_400(response_setup):
64 | response_setup.response.status_code = 400
65 | response_setup.response.parsed = False
66 | with pytest.raises(ClientError):
67 | response_setup.response._detect_error(response_setup.client)
68 |
69 |
70 | def test_should_raise_error_if_not_parsed_with_200(response_setup):
71 | response_setup.response.status_code = 200
72 | response_setup.response.parsed = False
73 | with pytest.raises(ParserError):
74 | response_setup.response._detect_error(response_setup.client)
75 |
76 |
77 | def test_should_raise_error_when_exception(response_setup):
78 | response_setup.response.status_code = 200
79 | response_setup.response.parsed = True
80 | response_setup.response.body = None
81 | response_setup.response._detect_error(response_setup.client)
82 |
83 |
84 | def test_should_not_raise_error_body_with_204(response_setup):
85 | response_setup.response.status_code = 204
86 | response_setup.response.parsed = False
87 | assert response_setup.response.status_code == 204
88 | response_setup.response._detect_error(response_setup.client)
89 |
--------------------------------------------------------------------------------
/specs/mixins/test_validator.py:
--------------------------------------------------------------------------------
1 | from amadeus import Client
2 | from os import environ
3 | from logging import Logger, getLogger
4 | import pytest
5 | from mock import MagicMock
6 |
7 |
8 | @pytest.fixture
9 | def valid_params():
10 | return {
11 | 'client_id': '1234',
12 | 'client_secret': '4546'
13 | }
14 |
15 |
16 | def test_client_init(valid_params):
17 | client = Client(**valid_params)
18 | assert isinstance(client, Client)
19 |
20 |
21 | def test_client_init_with_env_vars(valid_params):
22 | environ['AMADEUS_CLIENT_ID'] = '123'
23 | environ['AMADEUS_CLIENT_SECRET'] = '234'
24 |
25 | client = Client()
26 | assert isinstance(client, Client)
27 |
28 | del environ['AMADEUS_CLIENT_ID']
29 | del environ['AMADEUS_CLIENT_SECRET']
30 |
31 |
32 | def test_client_init_with_missing_params(valid_params):
33 | # Test missing client_id
34 | valid_params_copy = valid_params.copy()
35 | del valid_params_copy['client_id']
36 | with pytest.raises(ValueError):
37 | Client(**valid_params_copy)
38 |
39 | # Test missing client_secret
40 | valid_params_copy = valid_params.copy()
41 | del valid_params_copy['client_secret']
42 | with pytest.raises(ValueError):
43 | Client(**valid_params_copy)
44 |
45 |
46 | def test_client_logging(valid_params):
47 | # Test default logger
48 | amadeus = Client(**valid_params)
49 | assert isinstance(amadeus.logger, Logger)
50 | assert amadeus.log_level == 'silent'
51 |
52 | # Test custom logger
53 | logger = getLogger('amadeus')
54 | valid_params['logger'] = logger
55 | amadeus = Client(**valid_params)
56 | assert amadeus.logger is logger
57 | assert amadeus.log_level == 'silent'
58 |
59 | # Test custom log level
60 | logger.setLevel(10)
61 | amadeus = Client(**valid_params)
62 | assert amadeus.logger is logger
63 | assert amadeus.logger.level == 10
64 |
65 |
66 | def test_client_options(valid_params):
67 | # Test unrecognized option warning
68 | logger = MagicMock()
69 | valid_params['logger'] = logger
70 | valid_params['foobar'] = 'test'
71 | Client(**valid_params)
72 | logger.warning.assert_called_with('Unrecognized option: foobar')
73 |
74 | # Test default host
75 | amadeus = Client(**valid_params)
76 | assert amadeus.host == Client.HOSTS['test']
77 |
78 | # Test custom hostname
79 | valid_params['hostname'] = 'production'
80 | amadeus = Client(**valid_params)
81 | assert amadeus.host == Client.HOSTS['production']
82 |
83 | # Test custom host
84 | host = 'https://foo.bar.com/'
85 | valid_params['host'] = host
86 | amadeus = Client(**valid_params)
87 | assert amadeus.host == host
88 |
--------------------------------------------------------------------------------
/specs/namespaces/test_namespaces.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from mock import MagicMock
3 | from amadeus import Client
4 |
5 |
6 | @pytest.fixture
7 | def client():
8 | return Client(client_id='123', client_secret='234')
9 |
10 |
11 | def test_expected_paths(client):
12 | assert client.reference_data is not None
13 | assert client.reference_data.urls is not None
14 | assert client.reference_data.urls.checkin_links is not None
15 | assert client.reference_data.location is not None
16 | assert client.reference_data.locations is not None
17 | assert client.reference_data.locations.airports is not None
18 | assert client.reference_data.locations.hotels is not None
19 | assert client.reference_data.locations.hotels.by_hotels is not None
20 | assert client.reference_data.locations.hotels.by_city is not None
21 | assert client.reference_data.locations.hotels.by_geocode is not None
22 | assert client.reference_data.locations.hotel is not None
23 | assert client.reference_data.locations.cities is not None
24 | assert client.travel is not None
25 | assert client.travel.analytics is not None
26 | assert client.travel.analytics.air_traffic.traveled is not None
27 | assert client.travel.analytics.air_traffic.booked is not None
28 | assert client.travel.analytics.air_traffic.busiest_period is not None
29 | assert client.travel.predictions is not None
30 | assert client.travel.predictions.trip_purpose is not None
31 | assert client.travel.predictions.flight_delay is not None
32 | assert client.shopping is not None
33 | assert client.shopping.flight_dates is not None
34 | assert client.shopping.flight_destinations is not None
35 | assert client.shopping.flight_offers is not None
36 | assert client.shopping.flight_offers_search is not None
37 | assert client.shopping.flight_offers.pricing is not None
38 | assert client.shopping.flight_offers.upselling is not None
39 | assert client.shopping.seatmaps is not None
40 | assert client.shopping.hotel_offers_search is not None
41 | assert client.shopping.hotel_offer_search is not None
42 | assert client.shopping.activities is not None
43 | assert client.shopping.availability is not None
44 | assert client.shopping.availability.flight_availabilities is not None
45 | assert client.e_reputation.hotel_sentiments is not None
46 | assert client.airport is not None
47 | assert client.airport.predictions is not None
48 | assert client.airport.predictions.on_time is not None
49 | assert client.airport.direct_destinations is not None
50 | assert client.booking.flight_orders is not None
51 | assert client.booking.flight_order is not None
52 | assert client.booking.hotel_orders is not None
53 | assert client.schedule is not None
54 | assert client.schedule.flights is not None
55 | assert client.analytics is not None
56 | assert client.analytics.itinerary_price_metrics is not None
57 | assert client.airline.destinations is not None
58 | assert client.shopping.transfer_offers is not None
59 | assert client.ordering.transfer_orders is not None
60 | assert client.ordering.transfer_order is not None
61 |
62 |
63 | def test_expected_get_methods(client):
64 | assert client.reference_data.urls.checkin_links.get is not None
65 | assert client.reference_data.location('ALHR').get is not None
66 | assert client.reference_data.locations.get is not None
67 | assert client.reference_data.locations.airports.get is not None
68 | assert client.reference_data.recommended_locations.get is not None
69 | assert client.reference_data.locations.hotels.by_city.get is not None
70 | assert client.reference_data.locations.hotels.by_hotels.get is not None
71 | assert client.reference_data.locations.hotels.by_geocode.get is not None
72 | assert client.reference_data.locations.hotel.get is not None
73 | assert client.travel.analytics.air_traffic.traveled.get is not None
74 | assert client.travel.analytics.air_traffic.booked.get is not None
75 | assert client.travel.analytics.air_traffic.busiest_period.get is not None
76 | assert client.travel.predictions.trip_purpose.get is not None
77 | assert client.travel.predictions.flight_delay.get is not None
78 | assert client.shopping.flight_dates.get is not None
79 | assert client.shopping.flight_destinations.get is not None
80 | assert client.shopping.flight_offers_search.get is not None
81 | assert client.shopping.seatmaps.get is not None
82 | assert client.shopping.hotel_offers_search.get is not None
83 | assert client.shopping.hotel_offer_search('123').get is not None
84 | assert client.e_reputation.hotel_sentiments.get is not None
85 | assert client.airport.predictions.on_time.get is not None
86 | assert client.airport.direct_destinations.get is not None
87 | assert client.booking.flight_order('123').get is not None
88 | assert client.booking.flight_order('123').delete is not None
89 | assert client.schedule.flights.get is not None
90 | assert client.analytics.itinerary_price_metrics.get is not None
91 | assert client.airline.destinations.get is not None
92 |
93 |
94 | def test_expected_delete_methods(client):
95 | assert client.booking.flight_order('123').delete is not None
96 | assert client.reference_data.location('ALHR').get is not None
97 | assert client.reference_data.locations.get is not None
98 |
99 |
100 | @pytest.fixture
101 | def client_setup():
102 | client = Client(client_id='123', client_secret='234')
103 | client.get = MagicMock(return_value=None)
104 | client.post = MagicMock(return_value=None)
105 | client.delete = MagicMock(return_value=None)
106 | yield client
107 |
108 |
109 | def test_reference_data_urls_checkin_links_get(client_setup):
110 | client_setup.reference_data.urls.checkin_links.get(a='b')
111 | client_setup.get.assert_called_with(
112 | '/v2/reference-data/urls/checkin-links',
113 | a='b'
114 | )
115 |
116 |
117 | def test_reference_data_airlines_get(client_setup):
118 | client_setup.reference_data.airlines.get(a='b')
119 | client_setup.get.assert_called_with('/v1/reference-data/airlines', a='b')
120 |
121 |
122 | def test_reference_data_location_get(client_setup):
123 | client_setup.reference_data.location('ALHR').get(a='b')
124 | client_setup.get.assert_called_with(
125 | '/v1/reference-data/locations/ALHR', a='b'
126 | )
127 |
128 |
129 | def test_reference_data_locations_get(client_setup):
130 | client_setup.reference_data.locations.get(a='b')
131 | client_setup.get.assert_called_with(
132 | '/v1/reference-data/locations', a='b'
133 | )
134 |
135 |
136 | def test_reference_data_locations_airports_get(client_setup):
137 | client_setup.reference_data.locations.airports.get(a='b')
138 | client_setup.get.assert_called_with(
139 | '/v1/reference-data/locations/airports', a='b'
140 | )
141 |
142 |
143 | def test_reference_data_recommended_locations_get(client_setup):
144 | client_setup.reference_data.recommended_locations.get(a='b')
145 | client_setup.get.assert_called_with(
146 | '/v1/reference-data/recommended-locations', a='b'
147 | )
148 |
149 |
150 | def test_travel_analytics_air_traffic_traveled_get(client_setup):
151 | client_setup.travel.analytics.air_traffic.traveled.get(a='b')
152 | client_setup.get.assert_called_with(
153 | '/v1/travel/analytics/air-traffic/traveled', a='b'
154 | )
155 |
156 |
157 | def test_travel_analytics_air_traffic_booked_get(client_setup):
158 | client_setup.travel.analytics.air_traffic.booked.get(a='b')
159 | client_setup.get.assert_called_with(
160 | '/v1/travel/analytics/air-traffic/booked', a='b'
161 | )
162 |
163 |
164 | def test_travel_analytics_air_traffic_busiest_period_get(client_setup):
165 | client_setup.travel.analytics.air_traffic.busiest_period.get(a='b')
166 | client_setup.get.assert_called_with(
167 | '/v1/travel/analytics/air-traffic/busiest-period', a='b'
168 | )
169 |
170 |
171 | def test_travel_predictions_trip_purpose_get(client_setup):
172 | client_setup.travel.predictions.trip_purpose.get(a='b')
173 | client_setup.get.assert_called_with(
174 | '/v1/travel/predictions/trip-purpose', a='b'
175 | )
176 |
177 |
178 | def test_travel_predictions_flight_delay_get(client_setup):
179 | client_setup.travel.predictions.flight_delay.get(a='b')
180 | client_setup.get.assert_called_with(
181 | '/v1/travel/predictions/flight-delay', a='b'
182 | )
183 |
184 |
185 | def test_shopping_flight_dates_get(client_setup):
186 | client_setup.shopping.flight_dates.get(a='b')
187 | client_setup.get.assert_called_with(
188 | '/v1/shopping/flight-dates', a='b'
189 | )
190 |
191 |
192 | def test_shopping_flight_destinations_get(client_setup):
193 | client_setup.shopping.flight_destinations.get(a='b')
194 | client_setup.get.assert_called_with(
195 | '/v1/shopping/flight-destinations', a='b'
196 | )
197 |
198 |
199 | def test_shopping_flight_offers_search_get(client_setup):
200 | client_setup.shopping.flight_offers_search.get(a='b')
201 | client_setup.get.assert_called_with(
202 | '/v2/shopping/flight-offers', a='b'
203 | )
204 |
205 |
206 | def test_shopping_hotel_offers_search_get(client_setup):
207 | client_setup.shopping.hotel_offers_search.get(
208 | hotelIds='RTPAR001', adults=2)
209 | client_setup.get.assert_called_with(
210 | '/v3/shopping/hotel-offers', hotelIds='RTPAR001',
211 | adults=2
212 | )
213 |
214 |
215 | def test_shopping_hotel_offer_search_get(client_setup):
216 | client_setup.shopping.hotel_offer_search('XXX').get(a='b')
217 | client_setup.get.assert_called_with(
218 | '/v3/shopping/hotel-offers/XXX', a='b'
219 | )
220 |
221 |
222 | def test_shopping_seatmaps_get(client_setup):
223 | client_setup.shopping.seatmaps.get(**{'a': 'b'})
224 | client_setup.get.assert_called_with(
225 | '/v1/shopping/seatmaps', a='b'
226 | )
227 |
228 |
229 | def test_e_reputation_hotel_sentiments_get(client_setup):
230 | client_setup.e_reputation.hotel_sentiments.get(hotelIds='XKPARC12')
231 | client_setup.get.assert_called_with(
232 | '/v2/e-reputation/hotel-sentiments', hotelIds='XKPARC12'
233 | )
234 |
235 |
236 | def test_airport_predictions_on_time_get(client_setup):
237 | client_setup.airport.predictions.on_time.get(a='b')
238 | client_setup.get.assert_called_with(
239 | '/v1/airport/predictions/on-time', a='b'
240 | )
241 |
242 |
243 | def test_airport_direct_destinations_get(client_setup):
244 | client_setup.airport.direct_destinations.get(a='b')
245 | client_setup.get.assert_called_with(
246 | '/v1/airport/direct-destinations', a='b'
247 | )
248 |
249 |
250 | def test_shopping_flight_offers_prediction_post(client_setup):
251 | client_setup.shopping.flight_offers.prediction.post({'foo': 'bar'})
252 | client_setup.post.assert_called_with(
253 | '/v2/shopping/flight-offers/prediction', {'foo': 'bar'}
254 | )
255 |
256 |
257 | def test_shopping_flight_offers_search_post(client_setup):
258 | client_setup.shopping.flight_offers_search.post({'foo': 'bar'})
259 | client_setup.post.assert_called_with(
260 | '/v2/shopping/flight-offers', {'foo': 'bar'}
261 | )
262 |
263 |
264 | def test_shopping_seatmaps_post(client_setup):
265 | client_setup.shopping.seatmaps.post({'foo': 'bar'})
266 | client_setup.post.assert_called_with(
267 | '/v1/shopping/seatmaps', {'foo': 'bar'}
268 | )
269 |
270 |
271 | def test_shopping_flight_offers_pricing_post(client_setup):
272 | client_setup.shopping.flight_offers.pricing.post(
273 | {'foo': 'bar'}, include='other-services')
274 | client_setup.post.assert_called_with(
275 | '/v1/shopping/flight-offers/pricing?'+'include=other-services',
276 | {'data': {'type': 'flight-offers-pricing',
277 | 'flightOffers': [{'foo': 'bar'}]}}
278 | )
279 |
280 |
281 | def test_shopping_flight_offers_pricing_post_list(client_setup):
282 | client_setup.shopping.flight_offers.pricing.post([{'foo': 'bar'}])
283 | client_setup.post.assert_called_with(
284 | '/v1/shopping/flight-offers/pricing?',
285 | {'data': {'type': 'flight-offers-pricing',
286 | 'flightOffers': [{'foo': 'bar'}]}}
287 | )
288 |
289 |
290 | def test_shopping_booking_flight_orders_post(client_setup):
291 | client_setup.booking.flight_orders.post({'foo': 'bar'}, {'bar': 'foo'})
292 | client_setup.post.assert_called_with(
293 | '/v1/booking/flight-orders',
294 | {'data': {'type': 'flight-order',
295 | 'flightOffers': [{'foo': 'bar'}],
296 | 'travelers': [{'bar': 'foo'}]
297 | }}
298 | )
299 |
300 |
301 | def test_shopping_booking_flight_orders_post_list(client_setup):
302 | client_setup.booking.flight_orders.post(
303 | [{'foo': 'bar'}], [{'bar': 'foo'}])
304 | client_setup.post.assert_called_with(
305 | '/v1/booking/flight-orders',
306 | {'data': {'type': 'flight-order',
307 | 'flightOffers': [{'foo': 'bar'}],
308 | 'travelers': [{'bar': 'foo'}]
309 | }}
310 | )
311 |
312 |
313 | def test_shopping_availability_flight_availabilities_post(client_setup):
314 | client_setup.shopping.availability.flight_availabilities.post(
315 | {'foo': 'bar'})
316 | client_setup.post.assert_called_with(
317 | '/v1/shopping/availability/flight-availabilities', {'foo': 'bar'}
318 | )
319 |
320 |
321 | def test_shopping_flight_offers_upselling_post(client_setup):
322 | client_setup.shopping.flight_offers.upselling.post(
323 | {'foo': 'bar'})
324 | client_setup.post.assert_called_with(
325 | '/v1/shopping/flight-offers/upselling', {'foo': 'bar'}
326 | )
327 |
328 |
329 | def test_booking_flight_order_get(client_setup):
330 | client_setup.booking.flight_order('123').get(a='b')
331 | client_setup.get.assert_called_with(
332 | '/v1/booking/flight-orders/123', a='b'
333 | )
334 |
335 |
336 | def test_booking_flight_order_delete(client_setup):
337 | client_setup.booking.flight_order('123').delete(a='b')
338 | client_setup.delete.assert_called_with(
339 | '/v1/booking/flight-orders/123', a='b'
340 | )
341 |
342 |
343 | def test_shopping_booking_hotel_bookings_post(client_setup):
344 | client_setup.booking.hotel_bookings.post('123',
345 | {'foo': 'bar'},
346 | {'bar': 'foo'})
347 | client_setup.post.assert_called_with(
348 | '/v1/booking/hotel-bookings',
349 | {'data': {'offerId': '123',
350 | 'guests': [{'foo': 'bar'}],
351 | 'payments': [{'bar': 'foo'}]
352 | }}
353 | )
354 |
355 |
356 | def test_shopping_booking_hotel_bookings_post_list(client_setup):
357 | client_setup.booking.hotel_bookings.post('123',
358 | [{'foo': 'bar'}],
359 | [{'bar': 'foo'}])
360 | client_setup.post.assert_called_with(
361 | '/v1/booking/hotel-bookings',
362 | {'data': {'offerId': '123',
363 | 'guests': [{'foo': 'bar'}],
364 | 'payments': [{'bar': 'foo'}]
365 | }}
366 | )
367 |
368 |
369 | def test_booking_hotel_orders_post(client_setup):
370 | client_setup.booking.hotel_orders.post({'foo': 'bar'},
371 | {'bar': 'foo'})
372 | client_setup.post.assert_called_with(
373 | '/v2/booking/hotel-orders',
374 | {'data': {'type': 'hotel-order',
375 | 'guests': [{'foo': 'bar'}],
376 | 'travelAgent': {'bar': 'foo'},
377 | 'roomAssociations': [],
378 | 'payment': {},
379 | 'arrivalInformation': {}}}
380 | )
381 |
382 |
383 | def test_booking_hotel_orders_post_list(client_setup):
384 | client_setup.booking.hotel_orders.post([{'foo': 'bar'}],
385 | {'bar': 'foo'},
386 | [{'a': 'b'}],)
387 | client_setup.post.assert_called_with(
388 | '/v2/booking/hotel-orders',
389 | {'data': {'type': 'hotel-order',
390 | 'guests': [{'foo': 'bar'}],
391 | 'travelAgent': {'bar': 'foo'},
392 | 'roomAssociations': [{'a': 'b'}],
393 | 'payment': {},
394 | 'arrivalInformation': {}}}
395 | )
396 |
397 |
398 | def test_schedule_flights_get(client_setup):
399 | client_setup.schedule.flights.get(a='b')
400 | client_setup.get.assert_called_with(
401 | '/v2/schedule/flights', a='b'
402 | )
403 |
404 |
405 | def test_shopping_activities_get(client_setup):
406 | client_setup.shopping.activities.get(a='b')
407 | client_setup.get.assert_called_with(
408 | '/v1/shopping/activities', a='b'
409 | )
410 |
411 |
412 | def test_shopping_activities_by_square_get(client_setup):
413 | client_setup.shopping.activities.by_square.get(a='b')
414 | client_setup.get.assert_called_with(
415 | '/v1/shopping/activities/by-square', a='b'
416 | )
417 |
418 |
419 | def test_shopping_activity_get(client_setup):
420 | client_setup.shopping.activity('XXX').get(a='b')
421 | client_setup.get.assert_called_with(
422 | '/v1/shopping/activities/XXX', a='b'
423 | )
424 |
425 |
426 | def test_analytics_itinerary_price_metrics_get(client_setup):
427 | client_setup.analytics.itinerary_price_metrics.get(a='b')
428 | client_setup.get.assert_called_with(
429 | '/v1/analytics/itinerary-price-metrics', a='b'
430 | )
431 |
432 |
433 | def test_reference_data_locations_hotels_by_hotels_get(client_setup):
434 | client_setup.reference_data.locations.hotels.by_hotels.get(a='b',
435 | c=['d', 'e'])
436 | client_setup.get.assert_called_with(
437 | '/v1/reference-data/locations/hotels/by-hotels', a='b', c='d,e'
438 | )
439 |
440 |
441 | def test_reference_data_locations_hotels_by_city_get(client_setup):
442 | client_setup.reference_data.locations.hotels.by_city.get(a='b')
443 | client_setup.get.assert_called_with(
444 | '/v1/reference-data/locations/hotels/by-city', a='b'
445 | )
446 |
447 |
448 | def test_reference_data_locations_hotels_by_geocode_get(client_setup):
449 | client_setup.reference_data.locations.hotels.by_geocode.get(a='b')
450 | client_setup.get.assert_called_with(
451 | '/v1/reference-data/locations/hotels/by-geocode', a='b'
452 | )
453 |
454 |
455 | def test_reference_data_locations_hotel_get(client_setup):
456 | client_setup.reference_data.locations.hotel.get(a='b')
457 | client_setup.get.assert_called_with(
458 | '/v1/reference-data/locations/hotel', a='b'
459 | )
460 |
461 |
462 | def test_reference_data_locations_cities_get(client_setup):
463 | client_setup.reference_data.locations.cities.get(a='b')
464 | client_setup.get.assert_called_with(
465 | '/v1/reference-data/locations/cities', a='b'
466 | )
467 |
468 |
469 | def test_airline_destinations_get(client_setup):
470 | client_setup.airline.destinations.get(a='b')
471 | client_setup.get.assert_called_with(
472 | '/v1/airline/destinations', a='b'
473 | )
474 |
475 |
476 | def test_shopping_transfer_offers_post(client_setup):
477 | client_setup.shopping.transfer_offers.post({'foo': 'bar'})
478 | client_setup.post.assert_called_with(
479 | '/v1/shopping/transfer-offers', {'foo': 'bar'}
480 | )
481 |
482 |
483 | def test_ordering_transfer_orders_post(client_setup):
484 | client_setup.ordering.transfer_orders.post(
485 | {'foo': 'bar'}, offerId='1')
486 | client_setup.post.assert_called_with(
487 | '/v1/ordering/transfer-orders?'+'offerId=1', {'foo': 'bar'}
488 | )
489 |
490 |
491 | def test_ordering_transfer_order_transfers_cancellation_post(client_setup):
492 | client_setup.ordering.transfer_order('XXX').transfers.cancellation.post(
493 | {'foo': 'bar'}, confirmNbr=123)
494 | client_setup.post.assert_called_with(
495 | '/v1/ordering/transfer-orders/XXX/transfers/cancellation?confirmNbr=123',
496 | {'foo': 'bar'}
497 | )
498 |
--------------------------------------------------------------------------------
/specs/test_client.py:
--------------------------------------------------------------------------------
1 | from amadeus import Client, Location, Hotel
2 |
3 |
4 | def test_client_exists():
5 | assert Client is not None
6 |
7 |
8 | def test_client_has_helper_locations():
9 | assert Location is not None
10 | assert Location.ANY == 'AIRPORT,CITY'
11 |
12 |
13 | def test_client_has_helper_hotels():
14 | assert Hotel is not None
15 | assert Hotel.HOTEL_GDS == 'HOTEL_GDS'
16 | assert Hotel.HOTEL_LEISURE == 'HOTEL_LEISURE'
17 |
--------------------------------------------------------------------------------
/specs/test_version.py:
--------------------------------------------------------------------------------
1 | from amadeus import version
2 |
3 |
4 | def test_version():
5 | assert version is not None
6 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py38,python3.9,python10
3 |
4 | [testenv]
5 | commands =
6 | flake8 amadeus specs setup.py
7 | pytest specs/ --cov --cov-report=html
8 |
9 | deps =
10 | flake8==3.8.0
11 | flake8-quotes==2.1.1
12 | pytest==7.2.0
13 | mock==5.0.0
14 | pytest-cov==4.0.0
15 | pytest-html==3.2.0
16 | usedevelop=True
17 |
18 | [gh-actions]
19 | python =
20 | 3.8: py38
21 | 3.9: python3.9
22 | 3.10: python10
23 |
24 | [flake8]
25 | max-complexity = 5
26 | inline-quotes = single
27 | multiline-quotes = single
28 | max-line-length = 82
29 | ignore = Q002, E126, E123, W504, E226
30 |
--------------------------------------------------------------------------------