Skip to content

Instantly share code, notes, and snippets.

@patmigliaccio
Last active November 30, 2018 19:54
Show Gist options
  • Save patmigliaccio/a24c2cd10a3a088c473f6fe2ea8c2e30 to your computer and use it in GitHub Desktop.
Save patmigliaccio/a24c2cd10a3a088c473f6fe2ea8c2e30 to your computer and use it in GitHub Desktop.
A Go script that pulls Uber ride history and fare details
/**
* A Go script that pulls Uber ride history and fare details
*
* Example:
* $ go run concurrent_http.go
*
* New York | Base: $1.16 -- Duration Cost: $2.32 -- Distance Cost (mile): $4.53 -- Service Fees: $2.35 -- Duration: 13.67min
* New Jersey | Base: $1.16 -- Duration Cost: $0.66 -- Distance Cost (mile): $1.83 -- Service Fees: $2.35 -- Duration: 3.88min
* New Jersey | Base: $1.10 -- Duration Cost: $0.86 -- Distance Cost (mile): $1.94 -- Service Fees: $2.20 -- Duration: 5.40min
* New Jersey | Base: $1.10 -- Duration Cost: $2.38 -- Distance Cost (mile): $1.47 -- Service Fees: $2.20 -- Duration: 14.87min
*
* Author: Pat Migliaccio <[email protected]>
* License: MIT
*/
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"strconv"
"time"
)
// AuthorizationCode is the Personal Access Token used to make requests to the Uber API
const AuthorizationCode = ""
func main() {
if AuthorizationCode == "" {
log.Fatalln("'AuthorizationCode' is empty. Please edit the script and provide a Personal Access Token (https://developer.uber.com/dashboard/app/)")
}
historyItems := GetHistory(15)
ch := make(chan ProductDetail)
for _, historyItem := range historyItems[1:] {
go func(item HistoryItem) {
ch <- GetProductDetails(item.ProductID)
}(historyItem)
}
for _, historyItem := range historyItems[1:] {
pd := <-ch
if pd.ProductGroup != "ubereats" {
fmt.Printf("%s | ", historyItem.StartCity.DisplayName)
totalServiceFee := CalculateServiceFees(pd.PriceDetails)
duration := CalculateTripTime(historyItem)
fmt.Printf("Base: $%.2f -- Duration Cost: $%.2f -- Distance Cost (%s): $%.2f -- Service Fees: $%.2f", pd.PriceDetails.Base, pd.PriceDetails.CostPerMinute*duration, pd.PriceDetails.DistanceUnit, pd.PriceDetails.CostPerDistance*historyItem.Distance, totalServiceFee)
fmt.Printf(" -- Duration: %.2fmin \n", duration)
}
}
}
type HistoryItem struct {
Status string `json:"status"`
Distance float64 `json:"distance"`
ProductID string `json:"product_id"`
StartTime int64 `json:"start_time"`
StartCity struct {
Latitude float64 `json:"latitude"`
DisplayName string `json:"display_name"`
Longitude float64 `json:"longitude"`
} `json:"start_city"`
EndTime int64 `json:"end_time"`
RequestID string `json:"request_id"`
RequestTime int64 `json:"request_time"`
}
type RideReceipt struct {
RequestID string `json:"request_id"`
Subtotal string `json:"subtotal"`
TotalCharged string `json:"total_charged"`
TotalOwed string `json:"total_owed"`
TotalFare string `json:"total_fare"`
CurrencyCode string `json:"currency_code"`
ChargeAdjustments []struct{} `json:"charge_adjustments"`
Duration string `json:"duration"`
Distance string `json:"distance"`
DistanceLabel string `json:"distance_label"`
}
type ProductDetail struct {
UpfrontFareEnabled bool `json:"upfront_fare_enabled"`
Capacity int `json:"capacity"`
ProductID string `json:"product_id"`
PriceDetails PriceDetail `json:"price_details"`
Image string `json:"image"`
CashEnabled bool `json:"cash_enabled"`
Shared bool `json:"shared"`
ShortDescription string `json:"short_description"`
DisplayName string `json:"display_name"`
ProductGroup string `json:"product_group"`
Description string `json:"description"`
}
type PriceDetail struct {
ServiceFees []struct {
Fee float64 `json:"fee"`
Name string `json:"name"`
} `json:"service_fees"`
CostPerMinute float64 `json:"cost_per_minute"`
DistanceUnit string `json:"distance_unit"`
Minimum float64 `json:"minimum"`
CostPerDistance float64 `json:"cost_per_distance"`
Base float64 `json:"base"`
CancellationFee float64 `json:"cancellation_fee"`
CurrencyCode string `json:"currency_code"`
}
// GetHistory retrieves a user's ride history
func GetHistory(limit int) []HistoryItem {
client := &http.Client{}
if limit > 50 {
limit = 50
}
req, err := http.NewRequest("GET", "https://api.uber.com/v1.2/history?limit="+strconv.Itoa(limit), nil)
if err != nil {
log.Fatal(err)
}
req.Header.Add("Authorization", "Bearer "+AuthorizationCode)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
body, _ := ioutil.ReadAll(resp.Body)
handleHTTPError(resp, body)
var historyResponse struct {
Count int `json:"count"`
History []HistoryItem `json:"history"`
}
if err := json.Unmarshal(body, &historyResponse); err != nil {
panic(err)
}
return historyResponse.History
}
/*
GetRideReceipt retrieves receipt information. Requires privileged scope access.
https://developer.uber.com/docs/riders/guides/scopes#privileged-scopes
*/
func GetRideReceipt(requestID string) RideReceipt {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.uber.com/v1.2/requests/"+requestID+"/receipt", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Add("Authorization", "Bearer "+AuthorizationCode)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
body, _ := ioutil.ReadAll(resp.Body)
handleHTTPError(resp, body)
var rideReceipt RideReceipt
if err := json.Unmarshal(body, &rideReceipt); err != nil {
panic(err)
}
return rideReceipt
}
// GetProductDetails retrieves the product details for a ride
func GetProductDetails(productID string) ProductDetail {
client := &http.Client{}
req, err := http.NewRequest("GET", "https://api.uber.com/v1.2/products/"+productID, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Add("Authorization", "Bearer "+AuthorizationCode)
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
body, _ := ioutil.ReadAll(resp.Body)
handleHTTPError(resp, body)
var productDetail ProductDetail
if err := json.Unmarshal(body, &productDetail); err != nil {
panic(err)
}
return productDetail
}
// CalculateServiceFees determines the total cost of the trip service fees
func CalculateServiceFees(priceDetail PriceDetail) float64 {
var totalServiceFee float64
if len(priceDetail.ServiceFees) > 0 {
for _, fee := range priceDetail.ServiceFees {
totalServiceFee += fee.Fee
}
}
return totalServiceFee
}
// CalculateTripTime determines the duration of the trip from the history
func CalculateTripTime(historyItem HistoryItem) float64 {
return time.Unix(historyItem.EndTime, 0).Sub(time.Unix(historyItem.StartTime, 0)).Minutes()
}
func handleHTTPError(resp *http.Response, body []byte) {
if resp.StatusCode < 200 || resp.StatusCode > 299 {
log.Fatal(struct {
StatusCode int
Message string
}{
resp.StatusCode,
string(body),
})
}
}
func printJSON(v interface{}) {
vj, err := json.Marshal(v)
if err == nil {
fmt.Println(string(vj))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment