@@ -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 |