-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Start work on connection using Faraday for now * Update to use post * Add command to call google api for route info * remove constraints around null values for rides * Implement pojo to return data for given rides * Update to include ride in return objects * Add functionality to get route duration for commuting the rides from address * Add functionality to compute the amount for a ride * fix tests. will add further model tests in another PR * fix rubocop errors
- Loading branch information
Showing
19 changed files
with
293 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# frozen_string_literal: true | ||
|
||
class BaseCommand | ||
def self.call(**args) | ||
new.call(**args) | ||
end | ||
|
||
def call(**args) | ||
raise NotImplementedError, "Must define #call method" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# frozen_string_literal: true | ||
|
||
module Client | ||
class Request | ||
CONNECTION = Faraday | ||
private_constant :CONNECTION | ||
|
||
def self.connection(url:, params: {}, headers: {}) | ||
new(url, params, headers) | ||
end | ||
|
||
def post(url, body, headers = nil) | ||
connection.post(url, body.to_json, headers) | ||
end | ||
|
||
attr_reader :connection | ||
private :connection | ||
|
||
private def initialize(url, params, headers) | ||
@connection = CONNECTION.new(url, params:, headers:) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# frozen_string_literal: true | ||
|
||
module Rides | ||
module Commands | ||
class ComputeAmount < BaseCommand | ||
BASE_PAY_AMOUNT = Money.new(12_00) | ||
MILEAGE_BONUS_AMOUNT = Money.new(1_50) | ||
MILEAGE_BONUS_CLIFF = 5.0 | ||
DURATION_BONUS_AMOUNT = Money.new(0.7) | ||
DURATION_BONUS_CLIFF = 0.25 | ||
|
||
def call(ride:) | ||
distance_bonus_amount = compute_distance_bonus(ride.distance_meters) | ||
duration_bonus_amount = compute_duration_bonus(ride.duration) | ||
|
||
BASE_PAY_AMOUNT + distance_bonus_amount + duration_bonus_amount | ||
end | ||
|
||
private def compute_distance_bonus(distance_meters) | ||
distance_in_miles = convert_distance_to_miles(distance_meters) | ||
|
||
amount = distance_in_miles > MILEAGE_BONUS_CLIFF ? MILEAGE_BONUS_AMOUNT * distance_in_miles : 0 | ||
Money.new(amount) | ||
end | ||
|
||
private def compute_duration_bonus(duration) | ||
duration_in_hours = convert_duration_to_hours(duration) | ||
|
||
amount = duration_in_hours > DURATION_BONUS_CLIFF ? DURATION_BONUS_AMOUNT * duration_in_hours : 0 | ||
Money.new(amount) | ||
end | ||
|
||
private def convert_distance_to_miles(distance_meters) | ||
# 1 mile = 1609.34 meters | ||
distance_meters / 1609.34 | ||
end | ||
|
||
private def convert_duration_to_hours(duration) | ||
# Since there are 3,600 seconds in one hour, that's the conversion ratio used in the formula | ||
duration.to_f / 3600 | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# frozen_string_literal: true | ||
|
||
module Rides | ||
module Commands | ||
# Computes the duration of the commute for the ride. | ||
# This is used in the ranking for the rides that the driver will | ||
# choose from. | ||
# Returns a list of objects where the origin is the driver's current address(home) | ||
class GetCommuteDuration < BaseCommand | ||
def call(rides:, driver:) | ||
commute_rides = convert_rides(rides, driver) | ||
GetRoutesData.call(rides: commute_rides) | ||
end | ||
|
||
# Converts the Driver's current home address and | ||
# the Ride#from_address into structs that can be used to | ||
# obtain the route information | ||
private def convert_rides(rides, driver) | ||
rides.each_with_object([]) do |ride, acc| | ||
acc << OpenStruct.new(origin_place_id: driver.origin_place_id, destination_place_id: ride.origin_place_id) | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# frozen_string_literal: true | ||
|
||
module Rides | ||
module Commands | ||
# Makes a request to the Google API to obtain the route information | ||
class GetRoutesData < BaseCommand | ||
DIRECTIONS_API_URL = "https://routes.googleapis.com/distanceMatrix/v2:computeRouteMatrix" | ||
DEFAULT_HEADERS = { | ||
"X-Goog-FieldMask" => "originIndex,destinationIndex,status,condition,distanceMeters,duration", | ||
"X-goog-api-key" => ENV["GOOGLE_API_KEY"], | ||
"Content-Type" => "application/json" | ||
}.freeze | ||
DEFAULT_REQUEST_PARAMS = { routingPreference: "TRAFFIC_AWARE", travelMode: "DRIVE" }.freeze | ||
|
||
def call(rides:) | ||
data = get_direction_data_for_ride(rides) | ||
results(data, rides) | ||
end | ||
|
||
# Returns a list of objects, with attributes of | ||
# @param[:distance_meters] = Integer | ||
# @param[:duration] = String, e.g., "577s" | ||
# Duration is in seconds | ||
private def results(data, rides) | ||
# The response keeps the array positioning on the return. Since we're getting a matrix | ||
# of routes back, we only want the ones where we explicitly have a 'Ride'. This means that | ||
# we want the computations where the indicies match. | ||
data = data.select { _1[:originIndex] == _1[:destinationIndex] } | ||
data = transform_keys!(data) | ||
|
||
data.map.with_index { OpenStruct.new(ride: rides[_2], **_1) } | ||
end | ||
|
||
private def connection | ||
@connection ||= Client::Request.connection( | ||
url: DIRECTIONS_API_URL, | ||
headers: DEFAULT_HEADERS | ||
) | ||
end | ||
|
||
private def get_direction_data_for_ride(rides) | ||
body = build_request_body(rides) | ||
|
||
response = connection.post( | ||
DIRECTIONS_API_URL, | ||
body.merge(routingPreference: "TRAFFIC_AWARE", travelMode: "DRIVE") | ||
) | ||
|
||
JSON.parse(response.body, symbolize_names: true) | ||
end | ||
|
||
private def build_request_body(rides) | ||
rides.each_with_object({}) do |ride, acc| | ||
acc[:origins] ||= [] | ||
acc[:destinations] ||= [] | ||
|
||
acc[:origins] << { waypoint: { placeId: ride.origin_place_id } } | ||
acc[:destinations] << { waypoint: { placeId: ride.destination_place_id } } | ||
end | ||
end | ||
|
||
private def transform_keys!(data) | ||
data.map { |d| d.transform_keys { |k| k.to_s.underscore.to_sym } } | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe Rides::Commands::ComputeAmount do | ||
let(:duration) { "577s" } | ||
let(:distance_meters) { 3105 } | ||
subject { described_class.call(ride:) } | ||
|
||
describe "CSU to The Still" do | ||
let(:ride) { OpenStruct.new(duration:, distance_meters:) } | ||
it "computes the amount" do | ||
result = subject | ||
expect(result.format).to eq("$12.00") | ||
end | ||
end | ||
|
||
describe "Fort Collins to Denver" do | ||
let(:distance_meters) { 100_262.1 } | ||
let(:duration) { "3600s" } | ||
let(:ride) { OpenStruct.new(duration:, distance_meters:) } | ||
it "computes the amount" do | ||
result = subject | ||
expect(result.format).to eq("$105.46") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe Rides::Commands::GetCommuteDuration do | ||
it "gets data for an address" do | ||
VCR.use_cassette("first_commute") do | ||
from_address = create( | ||
:address, :with_out_place_id, line_1: "4705 Weitzel Street", city: "Timnath", state: "CO", | ||
zip_code: "80547" | ||
) | ||
to_address = create( | ||
:address, :with_out_place_id, line_1: "151 N College Ave", city: "Fort Collins", state: "CO", | ||
zip_code: "80524" | ||
) | ||
create_list(:ride, 2, from_address:, to_address:) | ||
driver = create(:driver, current_address: from_address) | ||
rides = Ride.selectable | ||
data = described_class.call(rides:, driver:) | ||
|
||
expect(data.length).to eq(2) | ||
expect(data.all? { _1.duration == "577s" }) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe Rides::Commands::GetRoutesData do | ||
it "gets data for an address" do | ||
VCR.use_cassette("first_ride_directions") do | ||
from_address = create( | ||
:address, :with_out_place_id, line_1: "711 Oval Drive", city: "Fort Collins", state: "CO", | ||
zip_code: "80521" | ||
) | ||
to_address = create( | ||
:address, :with_out_place_id, line_1: "151 N College Ave", city: "Fort Collins", state: "CO", | ||
zip_code: "80524" | ||
) | ||
create_list(:ride, 2, from_address:, to_address:) | ||
rides = Ride.selectable | ||
data = described_class.call(rides:) | ||
|
||
expect(data.length).to eq(2) | ||
expect(data.all? { _1.distance_in_meters == 3105 && _1.duration == "577s" }) | ||
end | ||
end | ||
end |
Oops, something went wrong.