@@ -6,7 +6,7 @@ module Agents |
||
6 | 6 |
|
7 | 7 |
|
8 | 8 |
description <<-MD |
9 |
- The QpxExpressAgent will tell you airline prices between a pair of cities. The api limit is 50 requests/day. |
|
9 |
+ The QpxExpressAgent will tell you the minimum airline prices between a pair of cities. The api limit is 50 requests/day. |
|
10 | 10 |
|
11 | 11 |
Follow their documentation here (https://developers.google.com/qpx-express/v1/prereqs#get-a-google-account) to retrieve an api key. |
12 | 12 |
After you get to the google developer console, created a project, enabled qpx express api then you can choose `api key` credential to be created. |
@@ -15,9 +15,9 @@ module Agents |
||
15 | 15 |
|
16 | 16 |
All the default options must exist. For `infantInSeatCount`, `infantInLapCount`, `seniorCount`, and `childCount`, leave them to the default value of `0` if its not necessary. |
17 | 17 |
|
18 |
- Set `refundable` to `true` if you want ticket to be refundable. Make sure `date` is in this type of date format `YYYY-MO-DAY`. |
|
18 |
+ Make sure `date` is in this type of date format `YYYY-MO-DAY`. |
|
19 | 19 |
|
20 |
- You can limit the number of `solutions` returned back. The first solution is the lowest priced ticket. Every detailed trip option details starts at `"slice"`. |
|
20 |
+ You can limit the number of `solutions` returned back. The first solution is the lowest priced ticket. |
|
21 | 21 |
MD |
22 | 22 |
|
23 | 23 |
event_description <<-MD |
@@ -46,17 +46,16 @@ module Agents |
||
46 | 46 |
|
47 | 47 |
def default_options |
48 | 48 |
{ |
49 |
- 'qpx_api_key': '', |
|
50 |
- 'adultCount': 1, |
|
51 |
- 'origin': 'BOS', |
|
52 |
- 'destination': 'SFO', |
|
53 |
- 'date': '2016-04-11', |
|
54 |
- 'childCount': 0, |
|
55 |
- 'infantInSeatCount': 0, |
|
56 |
- 'infantInLapCount': 0, |
|
57 |
- 'seniorCount': 0, |
|
58 |
- 'solutions': 3, |
|
59 |
- 'refundable': false |
|
49 |
+ 'qpx_api_key' => '', |
|
50 |
+ 'adultCount'=> 1, |
|
51 |
+ 'origin' => 'BOS', |
|
52 |
+ 'destination' => 'SFO', |
|
53 |
+ 'date' => '2016-04-11', |
|
54 |
+ 'childCount' => 0, |
|
55 |
+ 'infantInSeatCount' => 0, |
|
56 |
+ 'infantInLapCount'=> 0, |
|
57 |
+ 'seniorCount'=> 0, |
|
58 |
+ 'solutions'=> 3 |
|
60 | 59 |
} |
61 | 60 |
end |
62 | 61 |
|
@@ -71,7 +70,6 @@ module Agents |
||
71 | 70 |
errors.add(:base, "Infant In Lap Count") unless options['infantInLapCount'].present? |
72 | 71 |
errors.add(:base, "Senior Count must exist") unless options['seniorCount'].present? |
73 | 72 |
errors.add(:base, "Solutions must exist") unless options['solutions'].present? |
74 |
- errors.add(:base, "Refundable must exist") unless options['refundable'].present? |
|
75 | 73 |
end |
76 | 74 |
|
77 | 75 |
def working? |
@@ -79,7 +77,7 @@ module Agents |
||
79 | 77 |
end |
80 | 78 |
|
81 | 79 |
def check |
82 |
- post_params = {:request=>{:passengers=>{:kind=>"qpxexpress#passengerCounts", :adultCount=> interpolated["adultCount"], :childCount=> interpolated["childCount"], :infantInLapCount=>interpolated["infantInLapCount"], :infantInSeatCount=>interpolated['infantInSeatCount'], :seniorCount=>interpolated["seniorCount"]}, :slice=>[{:kind=>"qpxexpress#sliceInput", :origin=> interpolated["origin"].to_s , :destination=> interpolated["destination"].to_s , :date=> interpolated["date"].to_s }], :refundable=> interpolated["refundable"], :solutions=> interpolated["solutions"]}} |
|
80 |
+ post_params = {:request=>{:passengers=>{:kind=>"qpxexpress#passengerCounts", :adultCount=> interpolated["adultCount"], :childCount=> interpolated["childCount"], :infantInLapCount=>interpolated["infantInLapCount"], :infantInSeatCount=>interpolated['infantInSeatCount'], :seniorCount=>interpolated["seniorCount"]}, :slice=>[{:kind=>"qpxexpress#sliceInput", :origin=> interpolated["origin"].to_s , :destination=> interpolated["destination"].to_s , :date=> interpolated["date"].to_s }], :solutions=> interpolated["solutions"]}} |
|
83 | 81 |
body = JSON.generate(post_params) |
84 | 82 |
request = HTTParty.post(event_url, :body => body, :headers => {"Content-Type" => "application/json"}) |
85 | 83 |
events = JSON.parse request.body |
@@ -0,0 +1,184 @@ |
||
1 |
+{ |
|
2 |
+ "kind": "qpxExpress#tripsSearch", |
|
3 |
+ "trips": { |
|
4 |
+ "kind": "qpxexpress#tripOptions", |
|
5 |
+ "requestId": "a5KWNgUILMQXl0QjZ0O1Kj", |
|
6 |
+ "data": { |
|
7 |
+ "kind": "qpxexpress#data", |
|
8 |
+ "airport": [ |
|
9 |
+ { |
|
10 |
+ "kind": "qpxexpress#airportData", |
|
11 |
+ "code": "BOS", |
|
12 |
+ "city": "BOS", |
|
13 |
+ "name": "Boston Logan International" |
|
14 |
+ }, |
|
15 |
+ { |
|
16 |
+ "kind": "qpxexpress#airportData", |
|
17 |
+ "code": "SFO", |
|
18 |
+ "city": "SFO", |
|
19 |
+ "name": "San Francisco International" |
|
20 |
+ } |
|
21 |
+ ], |
|
22 |
+ "city": [ |
|
23 |
+ { |
|
24 |
+ "kind": "qpxexpress#cityData", |
|
25 |
+ "code": "BOS", |
|
26 |
+ "name": "Boston" |
|
27 |
+ }, |
|
28 |
+ { |
|
29 |
+ "kind": "qpxexpress#cityData", |
|
30 |
+ "code": "SFO", |
|
31 |
+ "name": "San Francisco" |
|
32 |
+ } |
|
33 |
+ ], |
|
34 |
+ "aircraft": [ |
|
35 |
+ { |
|
36 |
+ "kind": "qpxexpress#aircraftData", |
|
37 |
+ "code": "320", |
|
38 |
+ "name": "Airbus A320" |
|
39 |
+ } |
|
40 |
+ ], |
|
41 |
+ "tax": [ |
|
42 |
+ { |
|
43 |
+ "kind": "qpxexpress#taxData", |
|
44 |
+ "id": "ZP", |
|
45 |
+ "name": "US Flight Segment Tax" |
|
46 |
+ }, |
|
47 |
+ { |
|
48 |
+ "kind": "qpxexpress#taxData", |
|
49 |
+ "id": "AY_001", |
|
50 |
+ "name": "US September 11th Security Fee" |
|
51 |
+ }, |
|
52 |
+ { |
|
53 |
+ "kind": "qpxexpress#taxData", |
|
54 |
+ "id": "US_001", |
|
55 |
+ "name": "US Transportation Tax" |
|
56 |
+ }, |
|
57 |
+ { |
|
58 |
+ "kind": "qpxexpress#taxData", |
|
59 |
+ "id": "XF", |
|
60 |
+ "name": "US Passenger Facility Charge" |
|
61 |
+ } |
|
62 |
+ ], |
|
63 |
+ "carrier": [ |
|
64 |
+ { |
|
65 |
+ "kind": "qpxexpress#carrierData", |
|
66 |
+ "code": "B6", |
|
67 |
+ "name": "Jetblue Airways Corporation" |
|
68 |
+ } |
|
69 |
+ ] |
|
70 |
+ }, |
|
71 |
+ "tripOption": [ |
|
72 |
+ { |
|
73 |
+ "kind": "qpxexpress#tripOption", |
|
74 |
+ "saleTotal": "USD244.10", |
|
75 |
+ "id": "SD5atWDaIWFUAOehDmedl8001", |
|
76 |
+ "slice": [ |
|
77 |
+ { |
|
78 |
+ "kind": "qpxexpress#sliceInfo", |
|
79 |
+ "duration": 404, |
|
80 |
+ "segment": [ |
|
81 |
+ { |
|
82 |
+ "kind": "qpxexpress#segmentInfo", |
|
83 |
+ "duration": 404, |
|
84 |
+ "flight": { |
|
85 |
+ "carrier": "B6", |
|
86 |
+ "number": "833" |
|
87 |
+ }, |
|
88 |
+ "id": "Gv+0Syg7VaA8NyWp", |
|
89 |
+ "cabin": "COACH", |
|
90 |
+ "bookingCode": "Z", |
|
91 |
+ "bookingCodeCount": 7, |
|
92 |
+ "marriedSegmentGroup": "0", |
|
93 |
+ "leg": [ |
|
94 |
+ { |
|
95 |
+ "kind": "qpxexpress#legInfo", |
|
96 |
+ "id": "LXRlPXVU35MvdfE-", |
|
97 |
+ "aircraft": "320", |
|
98 |
+ "arrivalTime": "2016-04-11T23:10-07:00", |
|
99 |
+ "departureTime": "2016-04-11T19:26-04:00", |
|
100 |
+ "origin": "BOS", |
|
101 |
+ "destination": "SFO", |
|
102 |
+ "originTerminal": "C", |
|
103 |
+ "destinationTerminal": "I", |
|
104 |
+ "duration": 404, |
|
105 |
+ "onTimePerformance": 60, |
|
106 |
+ "mileage": 2697, |
|
107 |
+ "secure": true |
|
108 |
+ } |
|
109 |
+ ] |
|
110 |
+ } |
|
111 |
+ ] |
|
112 |
+ } |
|
113 |
+ ], |
|
114 |
+ "pricing": [ |
|
115 |
+ { |
|
116 |
+ "kind": "qpxexpress#pricingInfo", |
|
117 |
+ "fare": [ |
|
118 |
+ { |
|
119 |
+ "kind": "qpxexpress#fareInfo", |
|
120 |
+ "id": "Aw9SUGx6XiV/Eeby73yJz5cu1avySnHVCBCW01QV6wk2", |
|
121 |
+ "carrier": "B6", |
|
122 |
+ "origin": "BOS", |
|
123 |
+ "destination": "SFO", |
|
124 |
+ "basisCode": "ZH4AUEN" |
|
125 |
+ } |
|
126 |
+ ], |
|
127 |
+ "segmentPricing": [ |
|
128 |
+ { |
|
129 |
+ "kind": "qpxexpress#segmentPricing", |
|
130 |
+ "fareId": "Aw9SUGx6XiV/Eeby73yJz5cu1avySnHVCBCW01QV6wk2", |
|
131 |
+ "segmentId": "Gv+0Syg7VaA8NyWp" |
|
132 |
+ } |
|
133 |
+ ], |
|
134 |
+ "baseFareTotal": "USD213.95", |
|
135 |
+ "saleFareTotal": "USD213.95", |
|
136 |
+ "saleTaxTotal": "USD30.15", |
|
137 |
+ "saleTotal": "USD244.10", |
|
138 |
+ "passengers": { |
|
139 |
+ "kind": "qpxexpress#passengerCounts", |
|
140 |
+ "adultCount": 1 |
|
141 |
+ }, |
|
142 |
+ "tax": [ |
|
143 |
+ { |
|
144 |
+ "kind": "qpxexpress#taxInfo", |
|
145 |
+ "id": "US_001", |
|
146 |
+ "chargeType": "GOVERNMENT", |
|
147 |
+ "code": "US", |
|
148 |
+ "country": "US", |
|
149 |
+ "salePrice": "USD16.05" |
|
150 |
+ }, |
|
151 |
+ { |
|
152 |
+ "kind": "qpxexpress#taxInfo", |
|
153 |
+ "id": "AY_001", |
|
154 |
+ "chargeType": "GOVERNMENT", |
|
155 |
+ "code": "AY", |
|
156 |
+ "country": "US", |
|
157 |
+ "salePrice": "USD5.60" |
|
158 |
+ }, |
|
159 |
+ { |
|
160 |
+ "kind": "qpxexpress#taxInfo", |
|
161 |
+ "id": "XF", |
|
162 |
+ "chargeType": "GOVERNMENT", |
|
163 |
+ "code": "XF", |
|
164 |
+ "country": "US", |
|
165 |
+ "salePrice": "USD4.50" |
|
166 |
+ }, |
|
167 |
+ { |
|
168 |
+ "kind": "qpxexpress#taxInfo", |
|
169 |
+ "id": "ZP", |
|
170 |
+ "chargeType": "GOVERNMENT", |
|
171 |
+ "code": "ZP", |
|
172 |
+ "country": "US", |
|
173 |
+ "salePrice": "USD4.00" |
|
174 |
+ } |
|
175 |
+ ], |
|
176 |
+ "fareCalculation": "BOS B6 SFO 213.95ZH4AUEN USD 213.95 END ZP BOS XT 16.05US 4.00ZP 5.60AY 4.50XF BOS4.50", |
|
177 |
+ "latestTicketingTime": "2016-03-24T23:59-04:00", |
|
178 |
+ "ptc": "ADT" |
|
179 |
+ } |
|
180 |
+ ] |
|
181 |
+ } |
|
182 |
+ ] |
|
183 |
+ } |
|
184 |
+} |
@@ -3,7 +3,7 @@ require 'rails_helper' |
||
3 | 3 |
describe Agents::QpxAgent do |
4 | 4 |
before do |
5 | 5 |
|
6 |
- stub_request(:get, "https://www.googleapis.com/qpxExpress/v1/trips/search").to_return( |
|
6 |
+ stub_request(:post, "https://www.googleapis.com/qpxExpress/v1/trips/search?key=800deeaf-e285-9d62-bc90-j999c1973cc9").to_return( |
|
7 | 7 |
:body => File.read(Rails.root.join("spec/data_fixtures/qpx.json")), |
8 | 8 |
:status => 200, |
9 | 9 |
:headers => {"Content-Type" => "application/json"} |
@@ -19,7 +19,6 @@ describe Agents::QpxAgent do |
||
19 | 19 |
'infantInSeatCount' => 0, |
20 | 20 |
'infantInLapCount'=> 0, |
21 | 21 |
'seniorCount'=> 0, |
22 |
- 'refundable' => false, |
|
23 | 22 |
'solutions'=> 3 |
24 | 23 |
} |
25 | 24 |
|
@@ -35,7 +34,7 @@ describe Agents::QpxAgent do |
||
35 | 34 |
end |
36 | 35 |
|
37 | 36 |
describe "#that checker should be valid" do |
38 |
- it "should check that the aftership object is valid" do |
|
37 |
+ it "should check that the object is valid" do |
|
39 | 38 |
expect(@checker).to be_valid |
40 | 39 |
end |
41 | 40 |
|
@@ -88,16 +87,11 @@ describe Agents::QpxAgent do |
||
88 | 87 |
@checker.options['solutions'] = nil |
89 | 88 |
expect(@checker).not_to be_valid |
90 | 89 |
end |
91 |
- |
|
92 |
- it "should require Refundable" do |
|
93 |
- @checker.options['refundable'] = nil |
|
94 |
- expect(@checker).not_to be_valid |
|
95 |
- end |
|
96 | 90 |
end |
97 | 91 |
|
98 | 92 |
describe '#check' do |
99 | 93 |
it "should check that initial run creates an event" do |
100 |
- @checker.memory[:latestTicketingTime] = '2016-03-18T23:59-04:00' |
|
94 |
+ @checker.memory[:latestTicketingTime] = '2016-03-24T23:59-04:00' |
|
101 | 95 |
expect { @checker.check }.to change { Event.count }.by(1) |
102 | 96 |
end |
103 | 97 |
end |