Skip to content

arkadiysudarikov/hopskipdrive

Folders and files

NameName
Last commit message
Last commit date

Latest commit

fcb0da1 · Jun 4, 2024

History

63 Commits
Feb 16, 2024
Feb 12, 2024
Jun 4, 2024
Feb 15, 2024
Feb 29, 2024
Feb 12, 2024
Feb 29, 2024
Feb 29, 2024
Feb 12, 2024
Feb 29, 2024
Feb 12, 2024
Feb 12, 2024
Feb 12, 2024
Feb 12, 2024
Feb 12, 2024
Feb 13, 2024
Feb 15, 2024
Feb 13, 2024
Feb 12, 2024
Feb 15, 2024
Feb 15, 2024
Feb 12, 2024
Jun 4, 2024
Feb 12, 2024
Feb 12, 2024

Repository files navigation

HopSkipDrive Drivers API

HopSkipDrive

HopSkipDrive API for drivers.

Description

Unlike many other rideshares, HopSkipDrive is a scheduled ride service and not an on-demand ride service.
In our app for drivers, we show them a list of upcoming rides and they can pick the ones they want.
Our goal is to show them the best rides for each driver first, so to that end we have an internal scoring system.
In this exercise you will be implementing a slimmed down version of it

Definitions

The driving distance between two addresses is the distance in miles that it would take to drive a reasonably efficient route between them. It is not the straight line distance. It can be calculated by using a routing service

The driving duration between two addresses is the amount of time in hours it would take to drive the driving distance under realistic driving conditions. It can be calculated by using a routing service

The commute distance for a ride is the driving distance from the driver’s home address to the start of the ride, in miles

The commute duration for a ride is the amount of time it is expected to take to drive the commute distance, in hours.

The ride distance for a ride is the driving distance from the start address of the ride to the destination address, in miles

The ride duration for a ride is the amount of time it is expected to take to drive the ride distance, in hours

The ride earnings is how much the driver earns by driving the ride. It takes into account both the amount of time the ride is expected to take and the distance.

For the purposes of this exercise, it is calculated as: $12 + $1.50 per mile beyond 5 miles + (ride duration) * $0.70 per minute beyond 15 minutes

Prompt

The primary goal of this exercise is for you to demonstrate how you think about testing,
readability, and structuring a Rails application. We are also evaluating your ability to
understand and implement requirements.

Specification

  • Create a Rails 7 application, using Ruby 3+

  • Include the following entities:

    • Ride
      • Has an id, a start address and a destination address. You may end up adding additional information
    • Driver
      • Has an id and a home address
  • Build a RESTful API endpoint that returns a paginated JSON list of rides in descending score order for a given driver

  • Please write up API documentation for this endpoint in MarkDown or alternative

  • Calculate the score of a ride in $ per hour as: (ride earnings) / (commute duration + ride duration). Higher is better

  • Google Maps is expensive. Consider how you can reduce duplicate API calls

  • Include RSpec tests

Features

  • Implements the upcoming rides endpoint to be used by the driver's app.

Design Decisions

For this project I ended up making some design decisions to keep the project on track while developing the application:

  • I decided to use UUID for all IDs to increase information security.

  • Address table holds addresses for drivers and rides

    • Address is a string that is valid for Google Directions API
      • Location ID should be used in production to accurately query the same locations
        • Google Place ID is an example of an ID that can be used to identify addresses

    Similar addresses that share a location ID (potentially stored in a long-lived, distributed Solid Cache) can be used by both Driver and Ride and used in the API call to get commute and ride distance and duration.

  • I use the Rails Credentials to keep the Google Directions API Key secure in both development and production.

  • Because I'm working with two entities, Driver and Ride, I decided to use a module for the methods.

  • I decided to create a client class to call the Google Directions API.

  • I decided to use RSwag to provide OpenAPI UI compliance for the endpoint.

  • I use Pagy gem for pagination.

    • It handles pagination via the query parameter and headers.
      • I set the default to be 5 items per page.
  • I decided to cache the data returned for 5 minutes at a time.

    • I think the new Solid Cache can a great candidate for storing data returned from the Google API.
      • I think cache can be kept warm between the server restarts to continue to provide fast response from the server.
  • I considered adding the "pickup time" to the upcoming rides but for demo purposes I decided to leave it out so that rides can be retrieved at any time in the future.

Assumptions

I tried to make few assumptions but here they are:

  • Ride start and destination addresses cannot be the same.

Gems for calling APIs

Libraries provide benefits such as retries, improved error-handling and backoff and should be used in production environments.

I considered using various libraries but ultimately decided to make a simple call instead for this project.

Technologies Used

  • Ruby on Rails
  • PostgreSQL
  • OpenAPI

Installation

  1. Clone the repository: git clone https://github.com/arkadiysudarikov/hopskipdrive.git
  2. Install dependencies: bundle install
  3. Set up the database: rails db:setup
  4. Run Rails Credentials to add google_api_key: rails credentials:edit
  5. Run RSpec: rspec
  6. Start the server: rails server

Documentation

Code coverage

UpcomingRides (RDoc)

GoogleDirectionsApiClient (RDoc)

APU Usage

Please refer to the project's OpenAPI specification for the project.

  • I seed the database with a Driver ID e76885d9-dc50-4616-830e-cd24beefd7d9 that can be used to try out the endpoint.

API Endpoints

I offer both /api/v1/drivers/{driver_id}/upcoming_rides and /api/v1/{driver_id}/upcoming_rides endpoints.

[
  {
    "id": "b2d27489-27ab-4334-9940-6ae9eccbe0f1",
    "start_address_id": "c23bfaad-5633-4d4b-a36b-9380d6ed0a69",
    "destination_address_id": "9bfa1d1b-f07d-44ba-9aa2-18cf8b72a6af",
    "created_at": "2024-02-16T04:05:16.517Z",
    "updated_at": "2024-02-16T04:05:16.517Z",
    "home_address": {
      "id": "8bc8c7dd-4977-4655-8892-14143dbc4895",
      "address": "1600 Amphitheatre Pkwy",
      "created_at": "2024-02-16T04:05:16.451Z",
      "updated_at": "2024-02-16T04:05:16.451Z"
    },
    "start_address": {
      "id": "c23bfaad-5633-4d4b-a36b-9380d6ed0a69",
      "address": "1588 E Thomspon Blvd",
      "created_at": "2024-02-16T04:05:16.435Z",
      "updated_at": "2024-02-16T04:05:16.435Z"
    },
    "commute_distance": 319.3271776007556,
    "commute_duration": 4.984444444444445,
    "destination_address": {
      "id": "9bfa1d1b-f07d-44ba-9aa2-18cf8b72a6af",
      "address": "2112 E Thompson Blvd",
      "created_at": "2024-02-16T04:05:16.455Z",
      "updated_at": "2024-02-16T04:05:16.455Z"
    },
    "ride_distance": 0.46789367069730453,
    "ride_duration": 0.015277777777777777,
    "ride_earnings": 12,
    "score": 2.4001333407411525
  },
  {
    "id": "0b3e3771-1556-4fa4-981a-693ba8aed182",
    "start_address_id": "9bfa1d1b-f07d-44ba-9aa2-18cf8b72a6af",
    "destination_address_id": "c23bfaad-5633-4d4b-a36b-9380d6ed0a69",
    "created_at": "2024-02-16T04:05:16.502Z",
    "updated_at": "2024-02-16T04:05:16.502Z",
    "home_address": {
      "id": "8bc8c7dd-4977-4655-8892-14143dbc4895",
      "address": "1600 Amphitheatre Pkwy",
      "created_at": "2024-02-16T04:05:16.451Z",
      "updated_at": "2024-02-16T04:05:16.451Z"
    },
    "start_address": {
      "id": "9bfa1d1b-f07d-44ba-9aa2-18cf8b72a6af",
      "address": "2112 E Thompson Blvd",
      "created_at": "2024-02-16T04:05:16.455Z",
      "updated_at": "2024-02-16T04:05:16.455Z"
    },
    "commute_distance": 319.79444989871627,
    "commute_duration": 4.999722222222222,
    "destination_address": {
      "id": "c23bfaad-5633-4d4b-a36b-9380d6ed0a69",
      "address": "1588 E Thomspon Blvd",
      "created_at": "2024-02-16T04:05:16.435Z",
      "updated_at": "2024-02-16T04:05:16.435Z"
    },
    "ride_distance": 0.46789367069730453,
    "ride_duration": 0.014722222222222222,
    "ride_earnings": 12,
    "score": 2.3930866385996015
  }
]

ride_distance is in miles. ride_duration is in hours. ride_earnings is in USD. score is in USD/hour.

Screenshot

Screenshot 2024-02-15 at 20 36 01 (2)

Screenshot 2024-02-15 at 20 24 48

Contributing

If you'd like to contribute to this project, please follow these steps:

  1. Fork the repository
  2. Create a new branch: git checkout -b feature/your-feature
  3. Make your changes and commit them: git commit -am 'Add some feature'
  4. Push to the branch: git push origin feature/your-feature
  5. Submit a pull request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contact