Skip to content

Commit a22f9ce

Browse files
author
Alvaro Navarro
committed
Add Flight Choice Prediction API
1 parent afb9366 commit a22f9ce

File tree

10 files changed

+80
-60
lines changed

10 files changed

+80
-60
lines changed

README.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,11 @@ List of supported endpoints
207207
208208
# Flight Low-fare Search
209209
amadeus.shopping.flight_offers.get(origin='MAD', destination='NYC', departureDate='2019-08-01')
210-
210+
211+
# Flight Choice Prediction
212+
result = amadeus.shopping.flight_offers.get(origin='MAD', destination='NYC', departureDate='2019-08-01').result
213+
amadeus.shopping.flight_offers.prediction.post(result)
214+
211215
# Flight Checkin Links
212216
amadeus.reference_data.urls.checkin_links.get(airlineCode='BA')
213217
@@ -245,6 +249,7 @@ List of supported endpoints
245249
amadeus.shopping.hotel_offers_by_hotel.get(hotelId = 'IALONCHO')
246250
# Confirm the availability of a specific offer
247251
amadeus.shopping.hotel_offer('D5BEE9D0D08B6678C2F5FAD910DC110BCDA187D21D4FCE68ED423426D0A246BB').get()
252+
248253
# Point of Interest
249254
# What are the popular places in Barcelona (based a geo location and a radius)
250255
amadeus.reference_data.locations.points_of_interest.get(latitude=41.397158, longitude=2.160873)

amadeus/client/request.py

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Support Python 2 and 3 API calls without importing
22
# a 3rd party library
3+
4+
import json
5+
36
try:
47
from urllib.request import Request as HTTPRequest
58
from urllib.parse import urlencode
@@ -54,16 +57,6 @@ class Request(object):
5457
'''
5558

5659
def __init__(self, options):
57-
self.http_request = None
58-
self.__initialize_options(options)
59-
self.__initialize_headers()
60-
self.__build_http_request()
61-
62-
# PRIVATE
63-
64-
# Initializes the Request object with all the passed in params. These
65-
# do not change once set
66-
def __initialize_options(self, options):
6760
self.host = options['host']
6861
self.port = options['port']
6962
self.ssl = options['ssl']
@@ -77,13 +70,16 @@ def __initialize_options(self, options):
7770
self.app_id = options['app_id']
7871
self.app_version = options['app_version']
7972

80-
# Initializes the basic headers
81-
def __initialize_headers(self):
8273
self.headers = {
8374
'User-Agent': self.__build_user_agent(),
8475
'Accept': 'application/json, application/vnd.amadeus+json'
8576
}
8677

78+
self.url = self.__build_url()
79+
self.http_request = self.__build_http_request()
80+
81+
# PRIVATE
82+
8783
# Determines the User Agent
8884
def __build_user_agent(self):
8985
user_agent = 'amadeus-python/{0}'.format(self.client_version)
@@ -92,30 +88,31 @@ def __build_user_agent(self):
9288
user_agent += ' {0}/{1}'.format(self.app_id, self.app_version)
9389
return user_agent
9490

95-
# Builds up a HTTP Request objectm if not akready set
91+
# Builds a HTTP Request object based on the path, params, and verb
9692
def __build_http_request(self):
97-
request = self.__request_for_verb()
98-
self.__add_post_data_header()
99-
self.__add_bearer_token_header()
100-
self.__apply_headers(request)
101-
self.http_request = request
93+
# Requests token in case has not been set
94+
if (self.bearer_token is None):
95+
self.headers['Content-Type'] = 'application/x-www-form-urlencoded'
96+
return HTTPRequest(self.url,
97+
data=urlencode(self.params).encode(),
98+
headers=self.headers)
10299

103-
# Builds a HTTP Request object based on the path, params, and verb
104-
def __request_for_verb(self):
105-
params = self._encoded_params().encode()
106-
path = self.__full_url()
100+
# Adds the authentication header since the bearer token has been set
101+
self.headers['Authorization'] = self.bearer_token
107102

108103
if (self.verb == 'GET'):
109-
return HTTPRequest(path)
104+
return HTTPRequest(self.url, headers=self.headers)
110105
else:
111-
return HTTPRequest(path, params)
106+
return HTTPRequest(self.url,
107+
data=json.dumps(self.params).encode(),
108+
headers=self.headers)
112109

113110
# Encodes the params before sending them
114111
def _encoded_params(self):
115112
return self._urlencode(self.params)
116113

117114
# Builds up the full URL based on the scheme, host, path, and params
118-
def __full_url(self):
115+
def __build_url(self):
119116
full_url = '{0}://{1}'.format(self.scheme, self.host)
120117
if not self.__port_matches_scheme():
121118
full_url = '{0}:{1}'.format(full_url, self.port)
@@ -128,20 +125,6 @@ def __port_matches_scheme(self):
128125
return ((self.ssl and self.port == 443) or
129126
(not self.ssl and self.port == 80))
130127

131-
# Adds an extra header if the verb is POST
132-
def __add_post_data_header(self):
133-
if (self.verb == 'POST'):
134-
self.headers['Content-Type'] = 'application/x-www-form-urlencoded'
135-
136-
# Adds the authentication header if the bearer token has been set
137-
def __add_bearer_token_header(self):
138-
if (self.bearer_token is not None):
139-
self.headers['Authorization'] = self.bearer_token
140-
141-
# Applies all the headers to the HTTP Request object
142-
def __apply_headers(self, http_request):
143-
http_request.headers = self.headers
144-
145128
# Helper method to prepare the parameter encoding
146129
def _urlencode(self, d):
147130
return urlencode(self._flatten_keys(d, '', {}))

amadeus/mixins/http.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ def get(self, path, **params):
4040
:rtype: amadeus.Response
4141
:raises amadeus.ResponseError: when the request fails
4242
'''
43-
return self.request('GET', path, **params)
43+
return self.request('GET', path, params)
4444

45-
def post(self, path, **params):
45+
def post(self, path, params={}):
4646
'''
4747
A helper function for making generic POST requests calls. It is used by
4848
every namespaced API POST method.
@@ -63,9 +63,9 @@ def post(self, path, **params):
6363
:rtype: amadeus.Response
6464
:raises amadeus.ResponseError: when the request fails
6565
'''
66-
return self.request('POST', path, **params)
66+
return self.request('POST', path, params)
6767

68-
def request(self, verb, path, **params):
68+
def request(self, verb, path, params):
6969
'''
7070
A helper function for making generic POST requests calls. It is used by
7171
every namespaced API method. It can be used to make any generic API

amadeus/mixins/pagination.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __page(self, name, response):
3030
return self.request(
3131
response.request.verb,
3232
response.request.path,
33-
**params
33+
params
3434
)
3535

3636
def __page_number_for(self, name, response):

amadeus/shopping/_flight_offers.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
from amadeus.client.decorator import Decorator
2+
from amadeus.shopping.flight_offers._prediction import FlightChoicePrediction
23

34

45
class FlightOffers(Decorator, object):
6+
def __init__(self, client):
7+
Decorator.__init__(self, client)
8+
self.prediction = FlightChoicePrediction(client)
9+
510
def get(self, **params):
611
'''
712
Find the cheapest bookable flights.
813
914
.. code-block:: python
1015
11-
amadeus.shopping.flight_destinations.get(
16+
amadeus.shopping.flight_offers.get(
1217
origin='MAD',
1318
destination='NYC',
1419
departureDate='2019-08-01'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from ._prediction import FlightChoicePrediction
2+
3+
__all__ = ['FlightChoicePrediction']
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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.get(origin='MAD',
13+
destination='NYC',
14+
departureDate='2019-08-01'
15+
).body
16+
)
17+
18+
:rtype: amadeus.Response
19+
:raises amadeus.ResponseError: if the request could not be completed
20+
'''
21+
return self.client.post('/v1/shopping/flight-offers/prediction', body)

specs/client/request_spec.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
expect(self.request.http_request.get_header('Accept')).to(
6565
equal('application/json, application/vnd.amadeus+json')
6666
)
67-
expect(self.request.http_request.get_header('User-Agent')).to(
67+
expect(self.request.http_request.get_header('User-agent')).to(
6868
equal('amadeus-python/1.2.3 python/2.3.4 amadeus-cli/3.4.5')
6969
)
7070

@@ -86,9 +86,8 @@
8686
expect(self.request.http_request).to(be_a(HTTPRequest))
8787
expect(self.request.http_request.get_full_url()).to(equal(
8888
'https://example.com/foo/bar'))
89-
expect(self.request.http_request.data).to(equal(b'foo=bar'))
90-
expect(self.request.http_request.get_header('Content-Type')).to(
91-
equal('application/x-www-form-urlencoded'))
89+
expect(self.request.http_request.data).to(
90+
equal(b'{\"foo\": \"bar\"}'))
9291

9392
with it('should handle a custom scheme and port'):
9493
self.request = Request({

specs/mixins/http_spec.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,23 @@
2626
response = self.client.get('/foo', foo='bar')
2727
expect(response).to(equal(self.response))
2828
expect(self.client.request).to(
29-
have_been_called_with('GET', '/foo', foo='bar')
29+
have_been_called_with('GET', '/foo', {'foo': 'bar'})
3030
)
3131

3232
with context('Client.post'):
3333
with it('should pass all details to the request method'):
3434
self.client.request = self.request_method
35-
response = self.client.post('/foo', foo='bar')
35+
response = self.client.post('/foo', {'foo': 'bar'})
3636
expect(response).to(equal(self.response))
3737
expect(self.client.request).to(
38-
have_been_called_with('POST', '/foo', foo='bar')
38+
have_been_called_with('POST', '/foo', {'foo': 'bar'})
3939
)
4040

4141
with context('Client.request'):
4242
with it('should pass on to the _unauthenticated_request method'):
4343
self.response.result = {'access_token': '123'}
4444
self.client._unauthenticated_request = self.request_method
45-
response = self.client.request('POST', '/foo', foo='bar')
45+
response = self.client.request('POST', '/foo', {'foo': 'bar'})
4646
expect(response).to(equal(self.response))
4747
expect(self.client._unauthenticated_request).to(
4848
have_been_called_with(
@@ -53,11 +53,11 @@
5353
with it('should use the same access token when cashed'):
5454
self.response.result = {'access_token': '123', 'expires_in': 2000}
5555
self.client._unauthenticated_request = self.request_method
56-
self.client.request('POST', '/foo', foo='bar')
56+
self.client.request('POST', '/foo', {'foo': 'bar'})
5757

5858
self.response.result = {'access_token': '234', 'expires_in': 2000}
5959
self.client._unauthenticated_request = self.request_method
60-
self.client.request('POST', '/foo', foo='bar')
60+
self.client.request('POST', '/foo', {'foo': 'bar'})
6161

6262
expect(self.client._unauthenticated_request).not_to(
6363
have_been_called_with(

specs/mixins/pagination_spec.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131

3232
next_response = self.pagination.previous(self.response)
3333
expect(self.client.request).to(
34-
have_been_called_with('GET', '/a', a='b', page={'offset': '1'})
34+
have_been_called_with('GET', '/a',
35+
{'a': 'b', 'page': {'offset': '1'}})
3536
)
3637
expect(next_response).to(equal(self.next_response))
3738

@@ -48,7 +49,8 @@
4849

4950
next_response = self.pagination.next(self.response)
5051
expect(self.client.request).to(
51-
have_been_called_with('GET', '/a', a='b', page={'offset': '1'})
52+
have_been_called_with('GET', '/a',
53+
{'a': 'b', 'page': {'offset': '1'}})
5254
)
5355
expect(next_response).to(equal(self.next_response))
5456

@@ -65,7 +67,8 @@
6567

6668
next_response = self.pagination.first(self.response)
6769
expect(self.client.request).to(
68-
have_been_called_with('GET', '/a', a='b', page={'offset': '1'})
70+
have_been_called_with('GET', '/a',
71+
{'a': 'b', 'page': {'offset': '1'}})
6972
)
7073
expect(next_response).to(equal(self.next_response))
7174

@@ -83,7 +86,8 @@
8386

8487
next_response = self.pagination.last(self.response)
8588
expect(self.client.request).to(
86-
have_been_called_with('GET', '/a', a='b', page={'offset': '1'})
89+
have_been_called_with('GET', '/a',
90+
{'a': 'b', 'page': {'offset': '1'}})
8791
)
8892
expect(next_response).to(equal(self.next_response))
8993

0 commit comments

Comments
 (0)