Added Other AP

This commit is contained in:
dev-chiefworks
2022-04-26 11:30:34 -04:00
parent 5e006a6a21
commit 47f4fad75c
251 changed files with 29298 additions and 4 deletions
+27
View File
@@ -0,0 +1,27 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /SAVVY/booking/
#RewriteBase /
#Checks to
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [L]
</IfModule>
<IfModule !mod_rewrite.c>
# If we don't have mod_rewrite installed, all 404's
# can be sent to index.php, and everything works as normal.
# Submitted by: ElliotHaughin
ErrorDocument 404 /index.php
</IfModule>
#Header add Access-Control-Allow-Origin "*"
#Header add Access-Control-Expose-Headers "Access-Control-Allow-Origin"
#Header add Access-Control-Allow-Headers "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With"
#Header add Access-Control-Allow-Methods "POST, GET, PUT, DELETE, OPTIONS"
#Header add Content-type "application/json"
File diff suppressed because it is too large Load Diff
+297
View File
@@ -0,0 +1,297 @@
<?php
class Booking {
const STATUS_ERROR = -1;
const STATUS_INIT = 1;
const STATUS_BOOKED = 2;
const STATUS_CANCELED = 3;
const STATUS_VENDOR_CANCELED = 4;
const STATUS_VEHICLE_ARRIVED = 5;
const STATUS_COMPLETED = 6;
const STATUS_IN_PROGRESS = 7;
const STATUS_DISPATCHED = 8;
const STATUS_LOCATION_UPDATE = 9;
/*
CREATE TABLE booking (
id serial not null constraint booking_pkey primary key,
quote_id bigint not null,
provider_booking_ref varchar(200) not null,
details json,
created timestamp default now(),
updated timestamp,
completed timestamp,
status smallint default 0,
message text,
cost numeric default 0
);
alter table booking
add constraint booking_quotes_id_fk
foreign key (quote_id) references quotes;
CREATE TABLE booking_details (
id serial not null constraint booking_details_pkey primary key,
booking_id bigint not null,
action varchar(64),
details json,
created timestamp default now(),
message text,
request json
);
alter table booking_details
add constraint booking_details_booking_id_fk
foreign key (booking_id) references booking;*/
public static function getById($db, $id) {
syslog( LOG_WARNING, "Booking::getById(\$db, $id)" );
Logger::debug( "Booking::getById(\$db, $id)" );
$result = [];
// $q = "SELECT * FROM booking WHERE id=${id}";
$q = "SELECT b.*,
a_s.address as location_start, a_s.latitude as location_start_lat, a_s.longitude as location_start_lng,
a_e.address as location_end, a_e.latitude as location_end_lat, a_e.longitude as location_end_lng,
a_s.country as location_country
FROM booking b
LEFT JOIN quotes q on b.quote_id = q.id
LEFT JOIN address a_s on q.location_start_id = a_s.id
LEFT JOIN address a_e on q.location_end_id = a_e.id
WHERE b.id=${id}";
$r = pg_query( $db, $q );
//syslog(LOG_WARNING,$q);
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r ) ) {
$result = $f;
}
return $result;
}
public static function getAll($db, $params = []) {
syslog( LOG_WARNING, "Booking::getAll(\$db, $params)" );
Logger::debug( "Booking::getAll(\$db, $params)" );
$result = [];
// $q = "SELECT * FROM booking ";
$q = "SELECT b.*,
a.address as location_end, a.latitude as location_end_lat, a.longitude as location_end_lng,
a.country as location_country
FROM booking b
LEFT JOIN quotes q on b.quote_id = q.id
LEFT JOIN address a on q.location_end_id = a.id ";
if (count($params)) {
if (array_key_exists('active', $params) && (bool)$params['active'] == true) {
$q .= " WHERE b.status = ". Booking::STATUS_BOOKED. " OR b.status = ". Booking::STATUS_IN_PROGRESS. " OR b.status = ". Booking::STATUS_DISPATCHED ;
} else if (array_key_exists('status', $params) && isset($params['status'])) {
//$statuses = explode(',', $params['status']);
$q .= " WHERE b.status IN(". $params['status'] .")";
}
if (array_key_exists('limit', $params) && array_key_exists('offset', $params)) {
$limit = $params['limit'];
$offset = $params['offset'];
$q .= " ORDER BY created DESC LIMIT ${limit} OFFSET ${offset} ";
} else {
$q .= " ORDER BY created DESC LIMIT 10 OFFSET 0";
}
}
$r = pg_query( $db, $q );
$result = [];
if ( $r && pg_num_rows( $r ) ) {
while ($row = pg_fetch_assoc($r)) {
array_push($result, $row);
}
}
return $result;
}
public static function getByBookingRef($db, $bookingRef) {
syslog( LOG_WARNING, "Booking::getByBookingRef(\$db, $bookingRef)" );
Logger::debug( "Booking::getById(\$db, $bookingRef)" );
$result = [];
$q = "SELECT * FROM booking WHERE provider_booking_ref='${bookingRef}'";
$r = pg_query( $db, $q );
//syslog(LOG_WARNING,$q);
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r ) ) {
$result = $f;
}
return $result;
}
public static function getByMemberId($db, $id, $params = []) {
syslog( LOG_WARNING, "Booking::getByMemberId(\$db, $id)" );
Logger::debug( "Booking::getByMemberId(\$db, $id)" );
$result = [];
// $q = "SELECT * FROM booking WHERE member_id=${id} ";
$q = "SELECT b.*,
a.address as location_end, a.latitude as location_end_lat, a.longitude as location_end_lng,
a.country as location_country
FROM booking b
LEFT JOIN quotes q on b.quote_id = q.id
LEFT JOIN address a on q.location_end_id = a.id
WHERE b.member_id=${id}";
if (count($params)) {
if (array_key_exists('active', $params) && (bool)$params['active'] == true) {
$q .= " AND b.status = ". Booking::STATUS_BOOKED. " OR b.status = ". Booking::STATUS_IN_PROGRESS. " OR b.status = ". Booking::STATUS_DISPATCHED ;
} else if (array_key_exists('status', $params) && isset($params['status'])) {
//$statuses = explode(',', $params['status']);
$q .= " AND b.status IN(". $params['status'] .")";
}
if (array_key_exists('limit', $params) && array_key_exists('offset', $params)) {
$limit = $params['limit'];
$offset = $params['offset'];
$q .= " ORDER BY created DESC LIMIT ${limit} OFFSET ${offset} ";
} else {
$q .= " ORDER BY created DESC LIMIT 10 OFFSET 0";
}
}
$r = pg_query( $db, $q );
$result = [];
if ( $r && pg_num_rows( $r ) ) {
while ($row = pg_fetch_assoc($r)) {
array_push($result, $row);
}
}
return $result;
}
public static function getLastNotCompletedBookingForMember($db, $member_id) {
syslog( LOG_WARNING, "Booking::getLastNotCompletedBookingForMember(\$db, $member_id)" );
Logger::debug( "Booking::getLastNotCompletedBookingForMember(\$db, $member_id)" );
$result = [];
$q = "SELECT * FROM booking WHERE member_id=${member_id} AND completed IS NULL AND status = ". Booking::STATUS_BOOKED ." AND cost > 0";
$r = pg_query( $db, $q );
//syslog(LOG_WARNING,$q);
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r ) ) {
$result = $f;
}
return $result;
}
public static function create($db, $data) {
syslog(LOG_WARNING,'Booking::create($db, $data)');
$quote_id = $data["quote_id"];
$provider_booking_ref = $data["provider_booking_ref"];
$details = $data["details"];
$status = (int)$data["status"];
$memberId = (int)$data["member_id"];
$cost = (int)$data["cost"];
$q = "INSERT INTO booking(quote_id, provider_booking_ref, details, status, member_id, cost) VALUES (${quote_id}, '${provider_booking_ref}', '${details}', ${status}, ${memberId}, ${cost}) RETURNING id";
$log = [
'message' => 'Insert Booking',
'data' =>$data,
'query' =>$q
];
Logger::debug($log);
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
return [self::getById($db, $f["id"]),NULL];
}
syslog(LOG_WARNING,pg_last_error($db));
return [NULL,pg_last_error($db)];
}
public static function update($db, $data) {
syslog(LOG_WARNING,'Booking::update(): '.json_encode($data));
$q = self::buildUpdateSQL($data);
syslog(LOG_WARNING,$q);
$r = pg_query($db, $q);
$log = [
'affected_row' => pg_affected_rows($r),
'message' => 'Update Booking',
'data' =>$data,
'query' =>$q
];
Logger::debug($log);
if ($r && pg_affected_rows($r)) {
return array(self::getById($db, $data["id"]),NULL);
}
return array(NULL, pg_last_error($db));
}
public static function buildUpdateSQL($data) {
$q = "UPDATE booking SET";
foreach ($data as $key => $value) {
if ($key != 'id') {
if (is_int($value) || "now()" == $value) {
$q .= " ${key}=${value},";
} else {
$q .= " ${key}='".pg_escape_string($value)."',";
}
}
}
$q = substr($q, 0, -1);
$q .= " WHERE id=".$data["id"];
return $q;
}
public static function createDetails($db, $data) {
syslog(LOG_WARNING,'Booking::createDetails($db, $data)');
$booking_id = $data['booking_id'];
$action = $data['action'];
$details = $data['details'];
$message = $data['message'];
$request = $data['request'];
$q = "INSERT INTO booking_details(booking_id, action, details, message, request) VALUES (${booking_id}, '${action}', '${details}', '${message}', '${request}') RETURNING id";
$log = [
'message' => 'Insert Booking Details',
'data' =>$data,
'query' =>$q
];
Logger::debug($log);
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
return [self::getDetailsById($db, $f["id"]),NULL];
}
syslog(LOG_WARNING,pg_last_error($db));
return [NULL,pg_last_error($db)];
}
public static function getDetailsById($db, $id) {
syslog( LOG_WARNING, "Booking::getDetailsById(\$db, $id)" );
Logger::debug( "Booking::getDetailsById(\$db, $id)" );
$result = [];
$q = "SELECT id, booking_id, action, details, message FROM booking_details WHERE id=${id}";
$r = pg_query( $db, $q );
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r ) ) {
$result = $f;
}
return $result;
}
public static function getDetailsByBookingId($db, $booking_id) {
syslog( LOG_WARNING, "Booking::getDetailsByBookingId(\$db, $booking_id)" );
Logger::debug( "Booking::getDetailsByBookingId(\$db, $booking_id)" );
$result = [];
$q = "SELECT id, booking_id, action, details, message FROM booking_details WHERE booking_id=${booking_id} ORDER BY id";
$r = pg_query( $db, $q );
$result = [];
if ( $r && pg_num_rows( $r ) ) {
while ($row = pg_fetch_assoc($r)) {
array_push($result, $row);
}
}
return $result;
}
public static function getMemberBySessionId($db, $sessionId) {
syslog( LOG_WARNING, "Booking::getMemberBySessionId(\$db, $sessionId)" );
Logger::debug( "Booking::getMemberBySessionId(\$db, $sessionId)" );
$result = [];
$q = "SELECT * FROM public.members_session WHERE session = ${sessionId} LIMIT 1 OFFSET 0";
$r = pg_query( $db, $q );
if ( $r && pg_num_rows( $r ) && $f = pg_fetch_assoc( $r )) {
return [$f, NULL];
}
syslog(LOG_WARNING,pg_last_error($db));
return [NULL,pg_last_error($db)];
}
}
+433
View File
@@ -0,0 +1,433 @@
<?php
class Options {
public static function getTransportOptions($db, $origin, $destination) {
return Options::getServicesByLatitudeLongitude($db, $origin["lat"],$origin["lng"]);
}
public static function getServicesByLatitudeLongitude($db, $lat, $lng) {
// $lat,$lng => geofence_area_country
$geofence_area_country = [];
$q = "SELECT * FROM (SELECT *,ST_DistanceSphere(location::geometry,ST_SetSRID(ST_MakePoint(";
$q.= $lng.",".$lat."),4326)::geometry) AS distance FROM geofence_area_country ";
$q.= " WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(";
$q.= $lng.",".$lat."),4326)::geography,radius)) AS a ORDER BY a.distance ASC LIMIT 1";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
$geofence_area_country = $f;
} else {
// Fatal
$err = pg_last_error($db);
$err = $err ? $err : $q;
error_log('Options::getTransportOptions => '.$err);
throw new Exception("Failed to match input origin to a country");
}
// geofence_area_country => country_servies
$country_services = [];
if (array_key_exists("id",$geofence_area_country) && $geofence_area_country["id"]>0) {
$q = "SELECT * FROM country_services WHERE country_id=".$geofence_area_country["id"]." AND status=1";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r)) {
while ($f=pg_fetch_assoc($r)) {
$country_services[] = $f;
}
} else {
// Non-fatal, try to get city services next
$err = pg_last_error($db);
$err = $err ? $err : $q;
error_log('Options::getTransportOptions => '.$err);
}
}
// $lat,$lng => geofence_area_city
$geofence_area_city = [];
$q = "SELECT * FROM (SELECT *,ST_DistanceSphere(location::geometry,ST_SetSRID(ST_MakePoint(";
$q.= $lng.",".$lat."),4326)::geometry) AS distance FROM geofence_area_city ";
$q.= " WHERE ST_DWithin(location, ST_SetSRID(ST_MakePoint(";
$q.= $lng.",".$lat."),4326)::geography,radius)) AS a ORDER BY a.distance ASC LIMIT 1";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
$geofence_area_city = $f;
} else {
// Non-fatal, at least we know the country
$err = pg_last_error($db);
$err = $err ? $err : $q;
error_log('Options::getTransportOptions => '.$err);
}
// geofence_area_city => city_services
$city_services = [];
if (array_key_exists("id",$geofence_area_city) && $geofence_area_city["id"]>0) {
$q = "SELECT * FROM city_services WHERE city_id=".$geofence_area_city["id"]." AND status=1";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r)) {
while ($f=pg_fetch_assoc($r)) {
$city_services[] = $f;
}
} else {
// Non-fatal, if we have country services (let upstream decide what to do)
$err = pg_last_error($db);
$err = $err ? $err : $q;
error_log('Options::getTransportOptions => '.$err);
}
}
if (is_array($country_services) && count($country_services)>0 && (!is_array($city_services) || count($city_services)<1)) {
$services = $country_services;
} else if (is_array($city_services) && count($city_services)>0 && (!is_array($country_services) || count($country_services)<1)) {
$services = $city_services;
} else {
$services = $city_services;
foreach ($country_services as $country_service) {
$exists = false;
foreach($services as $service) {
if ($service["transport_provider_id"]>0
&& $service["transport_provider_id"]==$country_service["transport_provider_id"]) {
// The service is available on city level - skip
$exists = true;
break;
}
}
if (!$exists) {
// Add country service
$services[] = $country_service;
}
}
}
return [
$services,
[
'geofence_area_country' => $geofence_area_country,
'country_services' => $country_services,
'geofence_area_city' => $geofence_area_city,
'city_services' => $city_services
]
];
}
public static function processGooglePlace($db, $data) {
// data.address
// data.place
// data.lat
// data.lng
// data.utc_offset_minutes
// data.postal
// data.vicinity
// data.city
// data.country
$db_address = pg_escape_string(html_entity_decode($data["address"],ENT_QUOTES|ENT_HTML5,"UTF-8"));
$db_lat = floatval($data["lat"]);
$db_lng = floatval($data["lng"]);
$db_city = pg_escape_string($data["city"]);
$db_postal = pg_escape_string($data["postal"]);
$db_country = pg_escape_string($data["country"]);
// Step 1: Check address
$q = "SELECT a.*,b.timezone AS \"timeZoneId\" FROM address a LEFT JOIN address_timezone b ON (b.id=a.timezone) ";
$q .= " WHERE LOWER(a.address)=LOWER('${db_address}')";
$q .= "AND latitude<>0 AND longitude<>0 AND geocoding_date IS NOT NULL AND postal IS NOT NULL ";
$q .= "ORDER BY geocoding_date DESC LIMIT 1";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f = pg_fetch_assoc($r)) {
$f["address"] = html_entity_decode ($f["address"],ENT_QUOTES|ENT_HTML5,"UTF-8");
foreach ($f as $key=>$val) {
$data[$key] = $val;
}
return $data;
}
// Step 2: Get timezone
date_default_timezone_set('UTC');
$seconds = $data["utc_offset_minutes"] * 60;
$timezone = timezone_name_from_abbr('', $seconds, 1);
$q = "SELECT * FROM address_timezone WHERE LOWER(timezone)=LOWER('${timezone}')";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
$data["timezone"] = $f["id"];
$data["timeZoneId"] = $f["timezone"];
} else {
$q = "INSERT INTO address_timezone (timezone) VALUES('".pg_escape_string($timezone)."') RETURNING *";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
$data["timezone"] = $f["id"];
$data["timeZoneId"] = $f["timezone"];
} else { // Most likely this is very wrong...
$data["timezone"] = 1; // Singapore
$data["timeZoneId"] = 'Asia/Singapore';
}
}
// Step 3: Get city
$q = "SELECT id FROM geofence_area_city WHERE LOWER(city) ILIKE '" . strtolower($db_city) . "' AND country='${db_country}' LIMIT 1";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f = pg_fetch_row($r)) {
$data["city_id"] = $f[0];
} else {
$q = "INSERT INTO geofence_area_city (city,country,latitude,longitude,location) VALUES (";
$q.= "'${db_city}','${db_country}',${db_lat},${db_lng},ST_SetSRID(ST_MakePoint(${db_lng},${db_lat}), 4326)) RETURNING id";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f = pg_fetch_row($r)) {
$data["city_id"] = $f[0];
} else {
// Most likely this is very wrong...
$data["city_id"] = 1; // Singapore
}
}
// Step 4: Get closest postal if missing...
$db_tz = $data["timezone"];
if (empty($db_postal)) {
$distance = 50000; // 50 km
$q = "SELECT postal,
ST_DistanceSphere(geometry::geometry, ST_SetSRID(ST_MakePoint(${db_lng},${db_lat}),4326)) AS distance
FROM geoname_postal_code
WHERE ST_DistanceSphere(geometry::geometry, ST_SetSRID(ST_MakePoint(${db_lng},${db_lat}),4326)) BETWEEN 0 AND ${distance} AND country='${db_country}'
ORDER BY distance
LIMIT 1;";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f = pg_fetch_assoc($r)) {
$db_postal = $f["postal"];
$data["postal"] = $f["postal"];
}
}
// Step 5: Save address
$q = "INSERT INTO address (address,latitude,longitude,timezone,geocoding_date,postal,country,geometry, city_id) VALUES (";
$q.= "'${db_address}',${db_lat},${db_lng}," . ($db_tz == null ? "NULL" : $db_tz);
$q.= ",now(),'${db_postal}','${db_country}',ST_SetSRID(ST_MakePoint(${db_lng},${db_lat}), 4326),'".$data["city_id"]."')";
$q.= " RETURNING id, address, latitude as lat, longitude as lng, postal, country";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f = pg_fetch_assoc($r)) {
$data["id"] = $f["id"];
$data["address"] = $f["address"];
$data["lat"] = $f["lat"];
$data["lng"] = $f["lng"];
$data["geometry"] = $f["geometry"];
$data["postal"] = $f["postal"];
}
return $data;
}
public static function checkValidLocations($db, $origin, $destination) {
// Check for Google places result
if (is_array($origin) && array_key_exists('place',$origin) && is_array($origin['place'])) {
$origin = Options::processGooglePlace($db, $origin);
}
// Basic input sanity check: origin
if (!is_array($origin) || !array_key_exists('address',$origin)
|| !array_key_exists('lat',$origin) || !array_key_exists('lng',$origin)
|| !array_key_exists('id',$origin) || $origin['id']<1) {
throw new Exception('Invalid input origin');
}
// Check for Google places result
if (is_array($destination) && array_key_exists('place',$destination) && is_array($destination['place'])) {
$destination = Options::processGooglePlace($db, $destination);
}
// Basic input sanity check: destination
if (!is_array($destination) || !array_key_exists('address',$destination)
|| !array_key_exists('lat',$destination) || !array_key_exists('lng',$destination)
|| !array_key_exists('id',$destination) || $destination['id']<1) {
throw new Exception('Invalid input destination');
}
// Basic input sanity check: GPS coordinates
if ($origin['lat']==null || $origin['lng']==null || $destination['lat']==null || $destination['lng']==null
|| ($origin['lat']==0 && $origin['lng']==0) || ($destination['lat']==0 && $destination['lng']==0)) {
throw new Exception('Invalid origin and/or destination coordinates');
}
return [$origin, $destination];
}
public static function checkValidOption($db, $option) {
// Basic input sanity check: option
if (!is_array($option) || !array_key_exists('name',$option)
|| !array_key_exists('transport_provider_id',$option) || $option['transport_provider_id']<1
|| !array_key_exists('id',$option) || $option['id']<1) {
throw new Exception('Invalid transport option');
}
}
public static function checkValidTrip($db, $trip) {
// TODO!
}
public static function createTrip($db, $country, $duration, $distance, $location_start_id, $location_end_id) {
error_log("Options::createTrip(\$db, $country, $duration, $distance, $location_start_id, $location_end_id)");
$data = [
'travel_date' => Options::getDate($country),
'duration' => ((int)($duration/60)),
'cost_raw' => NULL,
'trackedemail_item_id' => NULL,
'cost' => NULL,
'distance' => sprintf("%0.02f",$distance/1000.0),
'transport_provider_id' => NULL,
'scheduled' => NULL,
'travel_date_end' => Options::getDate($country),
'dup_id' => NULL,
'location_start_id' => $location_start_id,
'location_end_id' => $location_end_id,
'private' => 't', /* this is a private entry without the source e-mail */
'booking' => 't' /* this is a booking entry */
];
list($id, $err) = Options::saveTrip($db->getConnect(), $data);
if ($id && $id>0) {
$trip = Options::getTripById($db->getConnect(), $id);
return $trip;
}
return NULL; // WTF?
}
public static function getTripById($db, $id) {
$result = array();
$q = "SELECT a.*,
b.address AS location_start, b.latitude AS location_start_lat, b.longitude AS location_start_lng,
c.address AS location_end, c.latitude AS location_end_lat, c.longitude AS location_end_lng,
a.id AS parsedemail_item_id
FROM parsedemail_item a
LEFT JOIN address b ON (b.id=a.location_start_id)
LEFT JOIN address c ON (c.id=a.location_end_id)
WHERE a.id=${id}";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
$f["location_start"] = html_entity_decode ($f["location_start"],ENT_QUOTES|ENT_HTML5,"UTF-8");
$f["location_end"] = html_entity_decode ($f["location_end"],ENT_QUOTES|ENT_HTML5,"UTF-8");
$result = $f;
}
return $result;
}
public static function saveTrip($db, $data) {
$fields = array(
'travel_date' => false,
'duration' => false,
'cost_raw' => false,
'trackedemail_item_id' => false,
'cost' => false,
'updated' => false,
'distance' => false,
'transport_provider_id' => false,
'scheduled' => false,
'travel_date_end' => false,
'dup_id' => false,
'location_start_id' => true,
'location_end_id' => true,
'private' => false,
'booking' => false
);
$field_key_list = [];
$field_val_list = [];
foreach ($fields as $key=>$required) {
if ($required && !isset($data[$key])) {
return [NULL, "Missing required field '${key}'"];
}
if (!isset($data[$key])) continue;
$field_key_list[] = "${key}";
$field_val_list[] = (($data[$key]==NULL || $data[$key]=="")?"NULL":"'".pg_escape_string($data[$key])."'");
}
$q = "INSERT INTO parsedemail_item (".implode(",",$field_key_list).")
VALUES(".implode(",",$field_val_list).") RETURNING id";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r)) {
$f = pg_fetch_row($r);
return [$f[0], NULL];
}
return [NULL, pg_last_error($db)];
}
public static function validateTrip($db, $trip) {
if (!is_array($trip) || !array_key_exists("id",$trip) || $trip["id"]<1) {
return false;
}
if (!array_key_exists("options",$trip) || !is_array($trip["options"]) || !array_key_exists("legs",$trip["options"])
|| !is_array($trip["options"]["legs"]) || count($trip["options"]["legs"])<1
|| !array_key_exists("id",$trip["options"]["legs"][0]) || $trip["options"]["legs"][0]["id"]<1) {
return false;
}
return true;
}
public static function saveTripAdvice($db, $trip) {
error_log('Options::saveTripAdvice($db, $trip)');
if (!is_array($trip) || !array_key_exists("id",$trip) || $trip["id"]<1) {
return NULL;
}
$q = "INSERT INTO parsedemail_item_advice_google (parsedemail_item_id,routes,source) VALUES (".$trip["id"].",1,'".$trip["source"]."') RETURNING id";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
return $f[0];
}
return NULL;
}
public static function saveTripAdviceLeg($db, $trip, $leg) {
error_log('Options::saveTripAdviceLeg($db, $trip, $leg)');
$fare_raw = "NULL";
if (array_key_exists("fare_raw",$leg) && $leg["fare_raw"]>0) {
$fare_raw = (int)$leg["fare_raw"];
}
$q = "INSERT INTO google_directions_legs (parsedemail_item_advice_google_id,arrival_time,arrival_time_zone,";
$q.= "departure_time,departure_time_zone,distance,duration,steps,fare_raw,polyline) VALUES(";
$q.= $leg["parsedemail_item_advice_google_id"].",'".pg_escape_string($leg["arrival_time"])."',".((int)$leg["arrival_time_zone"]);
$q.= ",'".pg_escape_string($leg["departure_time"])."',".((int)$leg["departure_time_zone"]).",";
$q.= ((int)$leg["distance"]).",".((int)$leg["duration"]).",1,${fare_raw},'".pg_escape_string($leg["polyline"])."') RETURNING id";
$r = pg_query($db, $q);
error_log($q);
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
return $f[0];
}
return NULL;
}
public static function saveTripAdviceLegStep($db, $trip, $leg, $step) {
error_log('Options::saveTripAdviceLegStep($db, $trip, $leg, $step)');
$q = "INSERT INTO google_directions_leg_steps (google_directions_leg_id,distance,duration,travel_mode,";
$q.= "location_start_lat,location_start_lng,location_end_lat,location_end_lng,html_instructions,polyline) VALUES (";
$q.= $leg["id"].",".((int)$step["distance"]).",".((int)$step["duration"]).",";
$q.= "'".pg_escape_string($step["travel_mode"])."',".$step["location_start_lat"].",".$step["location_start_lng"].",";
$q.= $step["location_end_lat"].",".$step["location_end_lng"].",'".pg_escape_string($step["html_instructions"])."',";
$q.= "'".pg_escape_string($step["polyline"])."') RETURNING id";
$r = pg_query($db, $q);
error_log($q);
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
return $f[0];
}
return NULL;
}
public static function saveTripAdviceLegStepQuote($db, $trip, $leg, $step) {
$fare_raw = NULL;
if (array_key_exists("fare_raw",$step) && $step["fare_raw"]>0) {
$fare_raw = (int)$step["fare_raw"];
}
if ($fare_raw>0) {
$q = "INSERT INTO leg_step_quote (google_directions_leg_step_id,name,service,board,alight,";
$q.= "distance,fare,fare_raw,distance_raw) VALUES (".$step["id"].",";
$q.= "'".pg_escape_string($trip["source"])."','".pg_escape_string($trip["source"])."',";
$q.= "'".pg_escape_string($trip["location_start"])."','".pg_escape_string($trip["location_end"])."',";
$q.= "'".sprintf("%0.02f",$step["distance"]/1000.0)."','".sprintf("%0.02f",$fare_raw/100.0)."',";
$q.= ((int)$fare_raw).",".((int)$step["distance"]).") RETURNING id";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
return $f[0];
}
}
return NULL;
}
public static function getDate($country) {
$tz = 'Asia/Singapore';
if ($country=='US') {
$tz = 'America/Los_Angeles';
}
$timestamp = time();
$dt = new DateTime("now", new DateTimeZone($tz)); //first argument "must" be a string
$dt->setTimestamp($timestamp); //adjust the object to correct timestamp
return $dt->format('Y-m-d, H:i:s');
}
public static function getTimezoneById($db, $id, $def='Asia/Singapore') {
$q = "SELECT timezone FROM address_timezone WHERE id=".((int)$id);
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
return $f['timezone'];
}
return $def; //$id;
}
}
// vi:ts=2
+110
View File
@@ -0,0 +1,110 @@
<?php
class OptionsApi extends Api
{
public $apiName = 'options';
public function indexAction()
{
$message = "Unexpected options error";
$latitude = $this->requestParams["latitude"] ?? 0;
$longitude = $this->requestParams["longitude"] ?? 0;
try {
if ($latitude==null || $longitude==null || ($latitude==0 || $longitude==0)) {
throw new Exception('Invalid location: '.$latitude.",".$longitude);
}
$db = new Db();
list ($res, $details) = Options::getServicesByLatitudeLongitude($db->getConnect(), $latitude, $longitude);
if (is_array($res)) {
return $this->response([
'services' => $res,
'details' => $details
], 200);
} else {
throw new Exception("Failed to get transport options for origin");
}
} catch (Exception $e) {
$message = $e->getMessage();
}
return $this->response(
array(
"error" => $message
), 500);
}
/**
* Method GET
* Get single record (by id)
* http://DOMAIN/remove/1
* @return string
*/
public function viewAction()
{
//id must be the first parameter after /remove/x
return $this->response(
array(
'error'=> 'Data not found'
), 404);
}
public function createAction()
{
$message = "Unexpected options error";
$origin = $this->requestParams["origin"] ?? [];
$destination = $this->requestParams["destination"] ?? [];
$create = $this->requestParams["create"] ?? false;
$country = $this->requestParams["country"] ?? NULL;
$duration = $this->requestParams["duration"] ?? 0;
$distance = $this->requestParams["distance"] ?? 0;
$member_id = $this->requestParams["member_id"] ?? 0;
try {
$db = new Db();
list ($origin, $destination) = Options::checkValidLocations($db->getConnect(), $origin, $destination);
list ($res, $details) = Options::getTransportOptions($db->getConnect(), $origin, $destination);
if (is_array($res)) {
$trip = NULL;
if ($create) {
$country = $origin["country"];
$trip = Options::createTrip($db, $country, $duration, $distance, $origin["id"], $destination["id"]);
}
return $this->response([
'services' => $res,
'details' => $details,
'trip' => $trip
], 200);
} else {
throw new Exception("Failed to get transport options for origin");
}
} catch (Exception $e) {
$message = $e->getMessage();
}
return $this->response(
array(
"error" => $message
), 500);
}
public function updateAction()
{
return $this->response(
array(
"error" => "Update error"
), 400);
}
public function deleteAction()
{
return $this->response(
array(
"error" => "Delete error"
), 500);
}
}
+643
View File
@@ -0,0 +1,643 @@
<?php
class Quote {
const QUOTE_CHECK_RETRIES = 10;
const QUOTE_CHECK_TIMEOUT = 3;
public static function getTransportQuote($db, $origin, $destination, $option, $trip, $route) {
$quote = [];
switch ($option["transport_provider_id"]) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 8:
list($option, $trip, $quote) = Quote::rideshareQuote($db, $origin, $destination, $option, $trip);
break;
case 11: // BART
list($option, $trip, $quote) = Quote::bartQuote($db, $origin, $destination, $option, $trip);
break;
case 12: // Muni
list($option, $trip, $quote) = Quote::muniQuote($db, $origin, $destination, $option, $trip);
break;
case 13: // LTA
list($option, $trip, $quote) = Quote::ltaQuote($db, $origin, $destination, $option, $trip);
break;
case 14: // MARTA
list($option, $trip, $quote) = Quote::martaQuote($db, $origin, $destination, $option, $trip);
break;
}
return [$option, $trip, $quote];
}
public static function rideshareQuote($db, $origin, $destination, $option, $trip) {
$res = [];
$input = [
"origin" => $origin,
"destination" => $destination,
"member_id" => $option["member_id"],
"country" => $origin["country"],
"transport_provider_id" => $option["transport_provider_id"],
"group_quote_id" => 0,
"trackedemail_item_id" => 0,
"prefill" => 'f'
];
list($res,$body) = Quote::postServiceCall("/trips/api/quote", json_encode($input));
$log = [
'message' => 'BookingQuote::rideshareQuote',
'function' =>'Quote::postServiceCall()',
'data' =>$input,
'response' => $res
];
Logger::debug($log);
if (!is_array($res) || !array_key_exists("id",$res) || $res["id"]<1) {
if (!is_array($res)) {
error_log($res);
}
error_log($body);
throw new Exception("Failed to schedule quote");
}
if (array_key_exists("complete",$res) && $res["complete"]!=NULL && $res["complete"]!="") {
if (array_key_exists("cost",$res) && $res["cost"]>0) {
$trip = Quote::patchRideshareTrip($trip, $res["cost"]);
return [$option, $trip, $res];
} else {
throw new Exception("Failed to get the quote");
// $res["message"] ?
// return [$option, $trip, $res]; // Do we want to recover?
}
}
// Will try for 30 seconds?
$endpoint = "/trips/api/quote/".$res["id"];
$i = 0;
while ($i<Quote::QUOTE_CHECK_RETRIES) {
sleep(Quote::QUOTE_CHECK_TIMEOUT);
list($res,$body) = Quote::getServiceCall($endpoint);
$log = [
'message' => 'BookingQuote::rideshareQuote',
'function' =>'Quote::getServiceCall()',
'data' =>$endpoint,
'response' => $res
];
Logger::debug($log);
if (!is_array($res) || !array_key_exists("id",$res) || $res["id"]<1) {
continue; // Try again...
}
if (array_key_exists("complete",$res) && $res["complete"]!=NULL && $res["complete"]!="") {
break; // No mater the result, maybe client will want to recover
}
$i++;
}
if (!is_array($res) || !array_key_exists("cost",$res) || !isset($res["cost"]) || $res["cost"] == 0) {
$avgPriceInfo = Quote::averagePrice($db, $origin, $destination, $option["transport_provider_id"]);
if (isset($avgPriceInfo) && array_key_exists('cost', $avgPriceInfo)) {
$res["cost"] = $avgPriceInfo['cost'];
$res["is_average_cost"] = true;
};
}
$trip = Quote::patchRideshareTrip($trip, $res["cost"]);
Quote::updateRideshareCosts($db, $trip);
return [$option, $trip, $res]; // last check quote result
}
public static function patchRideshareTrip($trip, $cost) {
// Do we save the quote?
$fare_raw = ((int)(100.0*$cost));
if (array_key_exists("options",$trip)) {
$gid = $trip["options"]["gid"];
$trip["options"]["legs"][0]["fare_raw"] = $fare_raw;
$trip["options"]["leg_fare"][$gid] = $fare_raw;
$trip["options"]["leg_steps"][$gid][0]["fare_raw"] = $fare_raw;
} else {
$trip["options"] = [
"gid" => "1",
"routes" => 1,
"legs" => [
[
"id" => 1,
"fare_raw" => $fare_raw
]
],
"leg_steps" => [
1 => [
"fare_raw" => $fare_raw
]
],
"leg_fare" => [
1 => $fare_raw
]
];
}
return $trip;
}
public static function updateRideshareCosts($db, $trip) {
if (array_key_exists("options",$trip)) {
$gid = $trip["options"]["legs"][0]["id"];
if ($gid>1 && $trip["options"]["legs"][0]["fare_raw"]>0) {
$q = "UPDATE google_directions_legs SET fare_raw=".((int)$trip["options"]["legs"][0]["fare_raw"]);
$q.= " WHERE id=".((int)$gid);
$r = pg_query($db, $q);
Options::saveTripAdviceLegStepQuote(
$db, $trip, $trip["options"]["legs"][0], $trip["options"]["leg_steps"][$gid][0]);
}
}
}
/*
{
"id": "224583",
"transport_provider_id": "4",
"automation_id": "206441",
"cost_raw": null,
"cost": null,
"created": "2020-01-09 13:09:01.072305",
"completed": null,
"member_id": "13",
"location_start_id": "4426",
"location_end_id": "4386",
"quote_group_id": "0",
"prefill": "f",
"location_start": "97 Meyer Rd, 93, Singapore 437918",
"location_start_lat": "1.2970849",
"location_start_lng": "103.8925712",
"location_start_tz": "1",
"location_geocoding_date": "2019-10-11",
"location_end": "1 Fusionopolis Way, #01-07 & #02-14, Connexis, Singapore 138632",
"location_end_lat": "1.2987049",
"location_end_lng": "103.7875699",
"deeplink": "ComfortDelGroTaxi:\/\/\/?action=setBooking&endingLat=1.298705&endingLong=103.787570&startingLat=1.297085&startingLong=103.892571"
}
*/
/*
{
"id": 224583,
"location_start": "97 Meyer Rd, 93, Singapore 437918",
"location_end": "1 Fusionopolis Way, #01-07 & #02-14, Connexis, Singapore 138632",
"cost_raw": "14.6",
"trackedemail_item_id": 0,
"cost": "14.60",
"transport_provider_id": 4,
"location_start_lat": "1.2970576",
"location_start_lng": "103.8925856",
"location_end_lat": "1.2987049",
"location_end_lng": "103.7875699",
"request_date": "2020-01-09T13:09:00.955Z",
"started": "2020-01-09T13:09:04.203Z",
"complete": "2020-01-09T13:09:04.743Z",
"status": 0,
"message": "android_automation_job_detail",
"attempts": 0,
"automation_id": 206441,
"completed": "2020-01-09T13:09:04.743Z",
"created": "2020-01-09 13:09:01.072305",
"location_start_id": "4426",
"location_end_id": "4386",
"member_id": "13",
"deeplink": "ComfortDelGroTaxi:\/\/\/?action=setBooking&endingLat=1.298705&endingLong=103.787570&startingLat=1.297085&startingLong=103.892571"
}
*/
public static function bartQuote($db, $origin, $destination, $option, $trip) {
$quote = [];
$quote["cost_raw"] = NULL;
$quote["transport_provider_id"] = $option["transport_provider_id"];
$quote["deeplink"] = NULL;
//$quote["leg_fare"] = $trip["options"]["leg_fare"];
$leg = [
"fare_raw" => 1
];
// Select leg
//error_log(">>>>>>>>> ".json_encode($trip));
foreach ($trip["options"]["legs"] as $leg) {
break;
}
$quote["cost"] = sprintf("%0.02f",$leg["fare_raw"]/100.0);
return [$option, $trip, $quote];
}
public static function muniQuote($db, $origin, $destination, $option, $trip) {
$quote = [];
$quote["cost_raw"] = NULL;
$quote["transport_provider_id"] = $option["transport_provider_id"];
$quote["deeplink"] = NULL;
//$quote["leg_fare"] = $trip["options"]["leg_fare"];
$leg = [
"fare_raw" => 1
];
// Select leg
//error_log(">>>>>>>>> ".json_encode($trip));
foreach ($trip["options"]["legs"] as $leg) {
break;
}
$quote["cost"] = sprintf("%0.02f",$leg["fare_raw"]/100.0);
return [$option, $trip, $quote];
}
public static function ltaQuote($db, $origin, $destination, $option, $trip) {
$quote = [];
$cost = 0;
$quote["cost_raw"] = NULL;
$quote["transport_provider_id"] = $option["transport_provider_id"];
$quote["deeplink"] = NULL;
$legs = $trip["options"]["legs"];
$leg_steps = $trip["options"]["leg_steps"];
$leg_fare = $trip["options"]["leg_fare"];
foreach ($legs as $i=>$leg) {
$leg_id = $leg["id"];
if (array_key_exists($leg_id,$leg_fare) && $leg_fare[$leg_id]>0) {
if ($cost==0) $cost = $leg["fare_raw"];
continue;
}
if (array_key_exists("fare_raw",$leg) && $leg["fare_raw"]>0) {
if ($cost==0) $cost = $leg["fare_raw"];
continue;
}
list ($leg, $leg_steps) = Quote::legStepsQuote($db, $origin, $destination, $option, $trip, $leg, $leg_steps);
$leg_fare[$leg_id] = $leg["fare_raw"];
if ($cost==0 && $leg["fare_raw"]>0) $cost = $leg["fare_raw"];
$legs[$i] = $leg;
}
$trip["options"]["legs"] = $legs;
$trip["options"]["leg_steps"] = $leg_steps;
$trip["options"]["leg_fare"] = $leg_fare;
$quote["cost"] = $cost;
//$quote["leg_fare"] = $leg_fare;
// Update model to save the quote
Quote::updateQuotes($db, $trip);
return [$option, $trip, $quote];
}
public static function martaQuote($db, $origin, $destination, $option, $trip) {
$quote = [];
$quote["cost_raw"] = NULL;
$quote["transport_provider_id"] = $option["transport_provider_id"];
$quote["deeplink"] = NULL;
//$quote["leg_fare"] = $trip["options"]["leg_fare"];
$leg = [
"fare_raw" => 1
];
// Select leg
//error_log(">>>>>>>>> ".json_encode($trip));
foreach ($trip["options"]["legs"] as $leg) {
break;
}
$quote["cost"] = sprintf("%0.02f",$leg["fare_raw"]/100.0);
return [$option, $trip, $quote];
}
public static function updateQuotes($db, $trip) {
$legs = $trip["options"]["legs"];
$leg_steps = $trip["options"]["leg_steps"];
$leg_fare = $trip["options"]["leg_fare"];
foreach ($legs as $leg) {
$leg_id = $leg["id"];
$steps = $leg_steps[$leg_id];
foreach ($steps as $step) {
if (array_key_exists("fare_raw",$step) && $step["fare_raw"]>0) {
$q = "SELECT id FROM leg_step_quote WHERE google_directions_leg_step_id=".$step["id"];
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_row($r)) {
$q = "UPDATE leg_step_quote SET fare_raw=".((int)$step["fare_raw"])." WHERE id=".$f[0];
$r = pg_query($db, $q);
} else {
$q = "INSERT INTO leg_step_quote (google_directions_leg_step_id,name,service,board,alight,distance,fare,fare_raw,distance_raw) VALUES(";
$q.= $step["id"].",'".pg_escape_string($step["short_line"])."','".pg_escape_string($step["line"])."',";
$q.= "'".pg_escape_string($step["departure_stop"])."','".pg_escape_string($step["arrival_stop"])."',";
$q.= "'".sprintf("%0.02f",$step["distance"]/100.0)."','".sprintf("%0.02f",$step["fare_raw"]/100.0)."',";
$q.= ((int)$step["fare_raw"]).",".((int)$step["distance"]).")";
$r = pg_query($db, $q);
}
}
}
if (array_key_exists("fare_raw",$leg) && $leg["fare_raw"]>0) {
$q = "UPDATE google_directions_legs SET fare_raw=".((int)$leg["fare_raw"])." WHERE id=${leg_id}";
$r = pg_query($db, $q);
continue;
}
if (array_key_exists($leg_id,$leg_fare) && $leg_fare[$leg_id]>0) {
$q = "UPDATE google_directions_legs SET fare_raw=".((int)$leg["fare_raw"])." WHERE id=${leg_id}";
$r = pg_query($db, $q);
}
}
}
public static function legStepsQuote($db, $origin, $destination, $option, $trip, $leg, $leg_steps) {
$leg_id = $leg["id"];
$steps = $leg_steps[$leg_id];
$fare_raw = 0;
foreach ($steps as $i=>$step) {
if (array_key_exists("fare_raw",$step) && $step["fare_raw"]>0) {
$fare_raw += $step["fare_raw"];
continue;
}
$step = Quote::stepQuote($db, $origin, $destination, $option, $trip, $step);
if (array_key_exists("fare_raw",$step) && $step["fare_raw"]>0) {
$fare_raw += $step["fare_raw"];
}
$steps[$i] = $step;
}
$leg["fare_raw"] = $fare_raw;
$leg_steps[$leg_id] = $steps;
return [$leg, $leg_steps];
}
public static function stepQuote($db, $origin, $destination, $option, $trip, $step) {
switch ($step["travel_mode"]) {
case "": break;
case "BIKE":
$bike_time = $step['duration'];
$country = $origin['country'];
list($price,$err) = SGBikeApi::quote($db, $bike_time, $country,$step);
if (!empty($price) && $price>0) {
$step["fare_raw"] = $price;
}
break;
case "WALKING": break;
case "NUS_TRANSIT":
// NUS Internal Shuttle Buses are free of charge
// http://www.nus.edu.sg/celc/symposium/Symposium%20Documents/Symposium%202016%20Visitors'%20Guide.pdf
break;
case "PARK": break;
case "SCOOTER":
// No scooter quotes right now
// list($res,$err) = ScooterApi::quote($db, $scooter_time, $country);
break;
case "TAXI":
// We do not know how to quote "TAXI" in another countries
if ($origin["country"]=="SG") {
// Reverse geocode the "TAXI" step
$input = [
"latitude" => $step["location_start_lat"],
"longitude" => $step["location_start_lng"],
"country" => $origin["country"]
];
list($originTaxi, $body) = Quote::putServiceCall("/trips/api/geocode",$input);
$input = [
"latitude" => $step["location_end_lat"],
"longitude" => $step["location_end_lng"],
"country" => $destination["country"]
];
list($destinationTaxi, $body) = Quote::putServiceCall("/trips/api/geocode",$input);
if (!is_array($originTaxi) || !array_key_exists("address",$originTaxi)
|| !is_array($destinationTaxi) || !array_key_exists("address",$destinationTaxi)) {
break;
}
$option["transport_provider_id"] = 4; // ComfortDelGro
list($optionTaxi, $tripTaxi, $quoteTaxi) =
Quote::rideshareQuote($db, $originTaxi, $destinationTaxi, $option, $trip);
if (is_array($quoteTaxi) && array_key_exists("cost",$quoteTaxi) && $quoteTaxi["cost"]>0) {
$step["fare_raw"] = (int)(100.0*$quoteTaxi["cost"]); // fare_raw is in cents
}
}
break;
case "TRANSIT":
// We do not know how to quote "TRANSIT" in another countries from here (must be specific)
if ($origin["country"]=="SG") {
$step_id = $step["id"];
$result = Quote::mytansportsgServiceStepQuote($step_id);
error_log('***********************************************************************');
error_log(json_encode($result));
error_log('***********************************************************************');
// {"code":0,"data":{"307575":{"total":92}}}
if (is_array($result) && array_key_exists("code",$result) && $result["code"]===0 && array_key_exists("data",$result)) {
$data = $result["data"];
if (is_array($data) && array_key_exists($step_id,$data) && is_array($data[$step_id]) && array_key_exists('total',$data[$step_id]) && $data[$step_id]['total']>0) {
// Conversion ???
$step["fare_raw"] = (int)$data[$step_id]['total']; // fare_raw is in cents
}
}
}
break;
}
return $step;
}
/*
BIKE | 4743
NUS_TRANSIT | 367
PARK | 1796
SCOOTER | 1023
TAXI | 2060
TRANSIT | 88781
WALKING | 154766
| 3
*/
public static function postServiceCall($endpoint, $payload) {
global $savvyext;
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
$api_url = $savvyext->cfgReadChar('system.api_url');
$encrypted_payload = bin2hex(
openssl_encrypt(
$payload, $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV
));
$postdata = "{\"encrypted_payload\": \"${encrypted_payload}\"}";
$url = $api_url . $endpoint;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Authorization: Server-Token ' . $httpAuthToken)
);
$body = curl_exec($ch);
$result = json_decode($body, true);
$payload = openssl_decrypt(
hex2bin(
$result['payload']
), $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV
);
$payload = json_decode($payload, true);
return [$payload, $body];
}
public static function putServiceCall($endpoint, $payload) {
global $savvyext;
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
$api_url = $savvyext->cfgReadChar('system.api_url');
/* $encrypted_payload = bin2hex(
openssl_encrypt(
$payload, $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV
));
$postdata = "{\"encrypted_payload\": \"${encrypted_payload}\"}"; */
$postdata = http_build_query($payload);
$url = $api_url . $endpoint;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded',
'Authorization: Server-Token ' . $httpAuthToken)
);
$body = curl_exec($ch);
$result = json_decode($body, true);
$payload = openssl_decrypt(
hex2bin(
$result['payload']
), $encryptionAlg, $encryptionKey, OPENSSL_RAW_DATA, $encryptionIV
);
$payload = json_decode($payload, true);
return [$payload, $body];
}
public static function getServiceCall($endpoint, $shouldDecrypt = true) {
global $savvyext;
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
$encryptionAlg = $savvyext->cfgReadChar('encryption.algorithm');
$encryptionKey = $savvyext->cfgReadChar('encryption.key');
$encryptionIV = $savvyext->cfgReadChar('encryption.iv');
$api_url = $savvyext->cfgReadChar('system.api_url');
$url = $api_url . $endpoint;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_VERBOSE, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/json',
'Authorization: Server-Token ' . $httpAuthToken)
);
$body = curl_exec($ch);
if ($shouldDecrypt) {
$result = json_decode($body,true);
$payload = openssl_decrypt(
hex2bin(
$result['payload']
),
$encryptionAlg,
$encryptionKey,
OPENSSL_RAW_DATA,
$encryptionIV
);
} else {
$payload = $body;
}
$payload = json_decode($payload, true);
return [$payload, $body];
}
public static function mytansportsgServiceStepQuote($id) {
global $savvyext;
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
$url = $oauth2_url."mytransportsg/?step_id=" . $id;
$opts = array(
'http' => array(
'method' => "GET",
'timeout' => 60, /* 1 minute */
'header' =>
"Content-Type: application/x-www-form-urlencoded\r\n" .
"Accept: application/json\r\n" .
"Authorization: Server-Token ${httpAuthToken}\r\n",
),
"ssl" => array(
"verify_peer"=>false,
"verify_peer_name"=>false,
)
);
$context = stream_context_create($opts);
$body = file_get_contents($url, false, $context);
$advice = json_decode($body,true);
return $advice;
}
public static function averagePrice($db, $origin, $destination, $providerId) {
if (!array_key_exists('id', $origin) || !isset($origin['id'])
|| !array_key_exists('country', $origin) || !isset($origin['country'])
|| !array_key_exists('timeZoneId', $origin) || !isset($origin['timeZoneId'])
|| !array_key_exists('id', $destination) || !isset($destination['id']) ) {
throw new Exception('Quote::averagePrices. Invalid input origin or destination');
}
$result = ['cost'=>0];
$timeZoneId = $origin['timeZoneId'];
$country = $origin['country'];
$originId = $origin['id'];
$destinationId = $destination['id'];
$endpoint = "/geofencearea/api/addresstoarea/?country=${country}&address_id=";
list($res,$body) = Quote::getServiceCall($endpoint.$originId, false);
if(count($res) == 0 || array_key_exists('error', $res) && isset($res['error'])) {
return $result;
}
$originAreaId = $res['areas'][0]['id'];
list($res,$body) = Quote::getServiceCall($endpoint.$destinationId, false);
if(count($res) == 0 || array_key_exists('error', $res) && isset($res['error'])) {
return $result;
}
$destinationAreaId = $res['areas'][0]['id'];
$dt = new DateTime('now', new DateTimeZone($timeZoneId));
$currentHour = $dt->format('G');
$q = "SELECT average_cost, hour, last_quotes_id, last_updated FROM geofence_area_average_quotes
WHERE transport_provider_id=${providerId}
AND area_start_id=${originAreaId}
AND area_end_id=${destinationAreaId}
AND hour=${currentHour}
";
$r = pg_query($db, $q);
if ($r && pg_num_rows($r) && $f=pg_fetch_assoc($r)) {
$result['cost'] = $f['average_cost'];
}
return $result;
}
}
// vi:ts=2
+110
View File
@@ -0,0 +1,110 @@
<?php
class QuoteApi extends Api
{
public $apiName = 'quote';
protected $hasPrice = true;
public function __construct($requestUri, $encryption=true) {
$this->cacheWhitelist = [
"createAction" => ['ttl' => 300] // 300 sec. = 5 min.
];
parent::__construct($requestUri, $encryption);
}
public function indexAction()
{
return $this->response(
array(
'error' => 'Data not found'
), 404);
}
/**
* Method GET
* Get single record (by id)
* http://DOMAIN/remove/1
* @return string
*/
public function viewAction()
{
//id must be the first parameter after /remove/x
return $this->response(
array(
'error'=> 'Data not found'
), 404);
}
public function createAction()
{
$message = "Unexpected options error";
$origin = $this->requestParams["origin"] ?? [];
$destination = $this->requestParams["destination"] ?? [];
$option = $this->requestParams["option"] ?? [];
$trip = $this->requestParams["trip"] ?? [];
$route = $this->requestParams["route"] ?? [];
$member_id = $this->requestParams["member_id"] ?? 0;
try {
$db = new Db();
list ($origin, $destination) = Options::checkValidLocations($db->getConnect(), $origin, $destination);
Options::checkValidOption($db->getConnect(), $option);
Options::checkValidTrip($db->getConnect(), $trip);
Route::checkValidRoute($db->getConnect(), $route);
$option["member_id"] = $member_id;
$trip["member_id"] = $member_id;
list ($option, $trip, $quote) = Quote::getTransportQuote($db->getConnect(), $origin, $destination, $option, $trip, $route);
if (is_array($quote)) {
$this->hasPrice = array_key_exists('cost', $quote) && isset($quote['cost']) && intval($quote['cost']) > 0;
return $this->response([
'option' => $option,
'trip' => $trip,
'quote' => $quote,
'route' => $route
], 200);
} else {
throw new Exception("Failed to get transport quote for origin");
}
} catch (Exception $e) {
$message = $e->getMessage();
}
return $this->response(
array(
"error" => $message
), 500);
}
public function updateAction()
{
return $this->response(
array(
"error" => "Update error"
), 400);
}
public function deleteAction()
{
return $this->response(
array(
"error" => "Delete error"
), 500);
}
protected function storeInCache() {
return http_response_code() === 200 && $this->hasPrice;
}
protected function getCacheKey() {
$origin = json_encode($this->requestParams["origin"]);
$destination = json_encode($this->requestParams["destination"]);
$option = $this->requestParams["option"];
$transport_provider_id = $option["transport_provider_id"];
return hash('md5', $this->method.'|'.$this->apiName.'|'.$this->action.'|'.$origin.'|'.$destination.'|'.$transport_provider_id);
}
}
+633
View File
@@ -0,0 +1,633 @@
<?php
class Route {
/*
savvy=> select id,name from transport_providers;
id | name
----+---------------
1 | Uber
2 | Lyft
3 | Grab
4 | ComfortDelGro
5 | GOJEK
6 | Turo
7 | Getaround
10 | GrabWheels
11 | BART
12 | Muni
13 | LTA
(11 rows)
*/
public static function getTransportRoute($db, $origin, $destination, $option, $trip) {
$route = [];
switch ($option["transport_provider_id"]) {
case 1:
case 2:
case 3:
case 4:
case 5:
case 8:
list($option, $trip, $route) = Route::drivingDirections($db, $origin, $destination, $option, $trip);
break;
case 11: // BART
list($option, $trip, $route) = Route::bartDirections($db, $origin, $destination, $option, $trip);
break;
case 12: // Muni
list($option, $trip, $route) = Route::muniDirections($db, $origin, $destination, $option, $trip);
break;
case 13: // LTA
list($option, $trip, $route) = Route::ltaDirections($db, $origin, $destination, $option, $trip);
break;
case 14: // MARTA
list($option, $trip, $route) = Route::martaDirections($db, $origin, $destination, $option, $trip);
break;
}
error_log(json_encode($trip));
return [$option, $trip, $route];
}
public static function drivingDirections($db, $origin, $destination, $option, $trip) {
list($option, $trip, $route) = Route::processRideshareOption($db, $option, $origin, $destination, $trip);
$option["travel_time"] = (int)$trip["duration"];
$option["travel_distance"] = (int)$trip["distance"];
$trip["source"] = strtolower($option["name"]);
return [$option, $trip, $route];
}
public static function drivingDirectionsOriginal($db, $origin, $destination, $option, $trip) {
$options = [];
$trip = [];
$option["travel_time"] = 0;
$option["travel_distance"] = 0;
$fromLat = $origin['lat'];
$fromLng = $origin['lng'];
$toLat = $destination['lat'];
$toLng = $destination['lng'];
// This is paranoid redudant check, but since the function is "public"...
if ($fromLat==null || $fromLng==null || $toLat==null || $toLng==null
|| ($fromLat==0 && $fromLng==0) || ($toLat==0 && $toLng==0)) {
throw new Exception('Invalid origin and/or destination coordinates');
}
list($res, $err) = Route::serviceRoute($db, $fromLat, $fromLng, $toLat, $toLng);
if ($err!=NULL || !is_array($res) || !isset($res["routes"]) || count($res["routes"])<1) {
$options['error'] = $err!=NULL ? $err : 'No available routes';
return [$option, $trip, $options];
}
$options["route_overlay"] = [];
$travel_time = PHP_INT_MAX;
$travel_distance = PHP_INT_MAX;
foreach ($res["routes"] as $route) {
$r = Route::processRoute($route);
if ($travel_time>$r['duration']) {
$travel_time = $r['duration'];
$travel_distance = $r['distance'];
}
$options["route_overlay"][] = $r;
}
$options["travel_time"] = $travel_time==PHP_INT_MAX ? NULL : $travel_time;
$options["travel_distance"] = $travel_distance==PHP_INT_MAX ? NULL : $travel_distance;
$option["travel_time"] = (int)$options["travel_time"];
$option["travel_distance"] = (int)$options["travel_distance"];
//https://developers.google.com/maps/documentation/utilities/polylinealgorithm
//overview_polyline
//bounds contains the viewport bounding box of the overview_polyline.
return [$option, $trip, $options];
}
public static function bartDirections($db, $origin, $destination, $option, $trip) {
$route = [];
$option["travel_time"] = 0;
$option["travel_distance"] = 0;
$route["travel_time"] = 0;
$route["travel_distance"] = 0;
$route["route_overlay"] = [];
$country = $origin["country"];
$member_id = $option["member_id"];
// New directions
$tripService = Route::tripService($trip["id"], $country, 'BART');
//error_log(json_encode($tripService));
if (is_array($tripService) && isset($tripService["travel_date"])) {
$tripService["member_id"] = $member_id; // Pass along
$tripService["country"] = $country;
$tripService = MultiModalFilter::byDuration($tripService);
//$tripService = MultiModalFilter::byCost($tripService);
$trip = $tripService;
$option["travel_time"] = (int)$trip["options"]["legs"][0]["duration"];
$option["travel_distance"] = (int)$trip["options"]["legs"][0]["distance"];
$route = Route::routeFromTrip($db, $origin, $destination, $option, $trip);
}
return [$option, $trip, $route];
}
public static function muniDirections($db, $origin, $destination, $option, $trip) {
$route = [];
$option["travel_time"] = 0;
$option["travel_distance"] = 0;
$country = $origin["country"];
$member_id = $option["member_id"];
// New directions
$tripService = Route::tripService($trip["id"], $country, 'MUNI');
//error_log(json_encode($tripService));
if (is_array($tripService) && isset($tripService["travel_date"])) {
$tripService["member_id"] = $member_id; // Pass along
$tripService["country"] = $country;
$tripService = MultiModalFilter::byDuration($tripService);
//$tripService = MultiModalFilter::byCost($tripService);
$trip = $tripService;
$option["travel_time"] = (int)$trip["options"]["legs"][0]["duration"];
$option["travel_distance"] = (int)$trip["options"]["legs"][0]["distance"];
$route = Route::routeFromTrip($db, $origin, $destination, $option, $trip);
}
return [$option, $trip, $route];
}
public static function ltaDirections($db, $origin, $destination, $option, $trip) {
$route = [];
$option["travel_time"] = 0;
$option["travel_distance"] = 0;
$country = $origin["country"];
$member_id = $option["member_id"];
// New directions
$tripService = Route::tripService($trip["id"], $country, 'LTA');
//error_log(json_encode($tripService));
if (is_array($tripService) && isset($tripService["travel_date"])) {
$tripService["member_id"] = $member_id; // Pass along
$tripService["country"] = $country;
$tripService = MultiModalFilter::byDuration($tripService);
//$tripService = MultiModalFilter::byCost($tripService);
$trip = $tripService;
$option["travel_time"] = (int)$trip["options"]["legs"][0]["duration"];
$option["travel_distance"] = (int)$trip["options"]["legs"][0]["distance"];
$route = Route::routeFromTrip($db, $origin, $destination, $option, $trip);
}
return [$option, $trip, $route];
}
public static function martaDirections($db, $origin, $destination, $option, $trip) {
$route = [];
$option["travel_time"] = 0;
$option["travel_distance"] = 0;
$country = $origin["country"];
$member_id = $option["member_id"];
// New directions
$tripService = Route::tripService($trip["id"], $country, 'MARTA');
//error_log(json_encode($tripService));
if (is_array($tripService) && isset($tripService["travel_date"])) {
$tripService["member_id"] = $member_id; // Pass along
$tripService["country"] = $country;
$tripService = MultiModalFilter::byDuration($tripService);
$trip = $tripService;
$option["travel_time"] = (int)$trip["options"]["legs"][0]["duration"];
$option["travel_distance"] = (int)$trip["options"]["legs"][0]["distance"];
$route = Route::routeFromTrip($db, $origin, $destination, $option, $trip);
}
return [$option, $trip, $route];
}
public static function routeFromTrip($db, $origin, $destination, $option, $trip) {
$leg = [
"distance" => 0,
"duration" => 0,
"polyline" => NULL
];
// Select leg
foreach ($trip["options"]["legs"] as $leg) {
break;
}
$route["travel_time"] = (int)$leg["duration"];
$route["travel_distance"] = (int)$leg["distance"];
// TODO: BART fix polyline!
$route["route_overlay"][] = [
"polyline" => [],
"distance" => $leg["distance"],
"duration" => $leg["duration"],
"summary" => "Generated option",
"overview_polyline" => [
"points" => Route::processPolyline($leg["polyline"])
],
"bounds" => [
"northeast" => [
"lat" => $origin["lat"],
"lng" => $origin["lng"]
],
"southwest" => [
"lat" => $destination["lat"],
"lng" => $destination["lng"]
]
]
];
return $route;
}
public static function checkValidRoute($db, $route) {
// Basic input sanity check: route
if (!is_array($route) || !array_key_exists('travel_time',$route) || !is_numeric($route['travel_time'])
|| !array_key_exists('travel_distance',$route) || !is_numeric($route['travel_distance'])
|| !array_key_exists('route_overlay',$route) || !is_array($route['route_overlay'])) {
throw new Exception('Invalid transport route');
}
}
public static function serviceRoute($db, $fromLat, $fromLng, $toLat, $toLng, $mode='driving',$waypoints=[]) {
global $savvyext;
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
error_log("Route::serviceRoute(\$db, $fromLat, $fromLng, $toLat, $toLng, $mode, \$waypoints=[])");
// Call geocoding service
$data = http_build_query(
array(
'gps' => implode(",",array($fromLat, $fromLng, $toLat, $toLng)),
'mode' => $mode,
'waypoints' => implode(",",$waypoints)
)
);
$url = $oauth2_url."route?" . $data;
$opts = array(
'http' => array(
'method' => "GET",
'timeout' => 60, /* 1 minute */
'header' =>
"Content-Type: application/x-www-form-urlencoded\r\n" .
"Accept: application/json\r\n" .
"Authorization: Server-Token ${httpAuthToken}\r\n",
),
"ssl" => array(
"verify_peer"=>false,
"verify_peer_name"=>false,
)
);
$context = stream_context_create($opts);
$body = file_get_contents($url, false, $context);
$geocoded = json_decode($body,true);
if (is_array($geocoded) && is_array($geocoded["data"]) && !isset($geocoded["error"])) {
return array($geocoded["data"], NULL);
} else if (is_array($geocoded) && isset($geocoded["error"])) {
$body = $geocoded["error"];
}
return array(NULL, "Routing service call error: ".$body);
}
public static function processRoute($route) {
require_once('../common/Polyline.php');
$result = [
'polyline' => array(),
'distance' => NULL,
'duration' => NULL,
'summary' => $route["summary"],
'overview_polyline' => $route["overview_polyline"],
'bounds' => $route["bounds"]
];
$distance = 0;
$duration = 0;
foreach ($route["legs"] as $leg) {
$distance += $leg["distance"]["value"];
$duration += $leg["duration"]["value"];
foreach ($leg["steps"] as $step) {
$points = Polyline::decode($step["polyline"]["points"]);
$result['polyline'] = array_merge($result['polyline'],Polyline::pair($points));
}
}
$result['distance'] = $distance;
$result['duration'] = $duration;
return $result;
}
public static function processRideshareOption($db, $option, $origin, $destination, $trip) {
error_log("Options::processRideshareOption(\$db, \$option, \$origin, \$destination, \$trip)");
$route = [];
$route["travel_time"] = 0;
$route["travel_distance"] = 0;
$route["route_overlay"] = [];
$member_id = $option["member_id"];
$country = $origin["country"];
$rideshare = $option["name"];
$transport_provider_id = $option["transport_provider_id"];
if (array_key_exists("timeZoneId",$origin) && $origin["timeZoneId"]!="") {
$tz = $origin["timeZoneId"];
} else if (array_key_exists("timeZoneId",$destination) && $destination["timeZoneId"]!="") {
$tz = $destination["timeZoneId"];
} else {
$tz = Options::getTimezoneById($db, $origin["timezone"], "Asia/Singapore");
}
//"trips"=>$trips;
if (!is_array($trip) && count($trip)<1) {
syslog(LOG_WARNING,"Invalid trip");
return [$option, $trip, $route]; // No idea how to handle it
}
/*if (!array_key_exists('trip',$trip) || !is_array($trip['trip']) || count($trip['trip'])<10) {
syslog(LOG_WARNING,"Invalid trip[0]!");
return $trips; // No idea how to handle it
} //*/
$oldTrip = $trip; //['trip'];
$origin['latitude'] = $origin['lat'];
$origin['longitude'] = $origin['lng'];
$destination['latitude'] = $destination['lat'];
$destination['longitude'] = $destination['lng'];
list($res, $err) = Route::serviceRoute($db,
$origin['lat'], $origin['lng'],
$destination['lat'], $destination['lng'], 'driving'
);
if ($err!=NULL || !is_array($res) || !isset($res["routes"]) || count($res["routes"])<1) {
error_log('No available routes!');
error_log($err);
return [$option, $trip, $route]; // We cannot route driving!
}
$route_overlay = [];
$travel_time = PHP_INT_MAX;
$travel_distance = PHP_INT_MAX;
foreach ($res["routes"] as $item) {
$r = Route::processRoute($item);
if ($travel_time>$r['duration']) {
$travel_time = $r['duration'];
$travel_distance = $r['distance'];
}
$route_overlay[] = $r;
}
$oldTrip["duration"] = $travel_time==PHP_INT_MAX ? NULL : $travel_time;
$oldTrip["distance"] = $travel_distance==PHP_INT_MAX ? NULL : $travel_distance;
$route["travel_time"] = (int)$oldTrip["duration"];
$route["travel_distance"] = (int)$oldTrip["distance"];
$route["route_overlay"] = $route_overlay;
/*
$result = [
'polyline' => array(),
'distance' => NULL,
'duration' => NULL,
'summary' => $route["summary"],
'overview_polyline' => $route["overview_polyline"],
'bounds' => $route["bounds"]
];
*/
//syslog(LOG_WARNING,json_encode($res));
$route_0 = $res["routes"][0];
$route_leg = $route_0["legs"][0];
$route_leg_steps = $route_leg["steps"];
$overview_polyline = Route::processPolyline($route_0["overview_polyline"]["points"]);
$leg_steps = [];
$distance = 0;
$duration = 0;
$location_start_lat = 0;
$location_start_lng = 0;
$location_end_lat = 0;
$location_end_lng = 0;
$polyline = [];
$i = 0; $j=1; $n = count($route_leg_steps);
for (; $i<$n; $i++) {
$step = $route_leg_steps[$i];
if ($step["travel_mode"] == "DRIVING") {
$location_start_lat = $step["start_location"]["lat"];
$location_start_lng = $step["start_location"]["lng"];
break;
}
$leg_steps[] = [
'distance' => $step["distance"]["value"],
'duration' => $step["duration"]["value"],
'html_instructions' => $step["html_instructions"],
'location_end_lat' => $step["end_location"]["lat"],
'location_end_lng' => $step["end_location"]["lng"],
'location_start_lat' => $step["start_location"]["lat"],
'location_start_lng' => $step["start_location"]["lng"],
'polyline' => $step["polyline"]["points"], /*"yz|Fm_nxRi@e@_@P",*/
'sid' => $j++,
'travel_mode' => $step["travel_mode"]
];
}
for (;$i<$n; $i++) {
$step = $route_leg_steps[$i];
if ($step["travel_mode"] != "DRIVING") {
break;
}
$distance += $step["distance"]["value"];
$duration += $step["duration"]["value"];
$location_end_lat = $step["end_location"]["lat"];
$location_end_lng = $step["end_location"]["lng"];
$polyline[] = $step["polyline"]["points"];
}
$leg_steps[] = [
'distance' => $distance,
'duration' => $duration,
'fare_quote' => null,
'fare_raw' => null,
'html_instructions' => "Take ".$rideshare." to ".$destination["address"],
'location_end_lat' => $location_end_lat,
'location_end_lng' => $location_end_lng,
'location_start_lat' => $location_start_lat,
'location_start_lng' => $location_start_lng,
'polyline' => Route::processPolyline($polyline),
'quote_group_id' => null,
'sid' => $j++,
'travel_mode' => strtoupper($rideshare)
];
for (;$i<$n; $i++) {
$step = $route_leg_steps[$i];
$leg_steps[] = [
'distance' => $step["distance"]["value"],
'duration' => $step["duration"]["value"],
'html_instructions' => $step["html_instructions"],
'location_end_lat' => $step["end_location"]["lat"],
'location_end_lng' => $step["end_location"]["lng"],
'location_start_lat' => $step["start_location"]["lat"],
'location_start_lng' => $step["start_location"]["lng"],
'polyline' => $step["polyline"]["points"], /*"yz|Fm_nxRi@e@_@P",*/
'sid' => $j++,
'travel_mode' => $step["travel_mode"]
];
}
$cost = null;
$rideshare = [
'cost' => $cost,
'cost_raw' => $cost,
'count' => 1,
'country' => $country,
'distance' => $oldTrip['distance'],
'dup_id' => null,
'duration' => $oldTrip['duration'],
'id' => $oldTrip['id'],
'location_end' => $destination['address'],
'location_end_id' => $oldTrip['location_end_id'],
'location_end_lat' => $destination['latitude'],
'location_end_lng' => $destination['longitude'],
'location_end_tz' => $destination['timezone'],
'location_start' => $origin['address'],
'location_start_id' => $oldTrip['location_start_id'],
'location_start_lat' => $origin['latitude'],
'location_start_lng' => $origin['longitude'],
'location_start_tz' => $origin['timezone'],
'member_id' => $member_id,
'options' => [],
'parsedemail_item_id' => null, /* $trip['id'] ??? */
'private' => 't',
'scheduled' => null,
'trackedemail_item_id' => null,
'transport_provider_id' => $transport_provider_id,
'travel_date' => $oldTrip['travel_date'],
'travel_date_end' => $oldTrip['travel_date_end'],
'updated' => $oldTrip['updated']
];
// Create trip advice
$rideshare["source"] = strtolower($option["name"]);
$parsedemail_item_advice_google_id = Options::saveTripAdvice($db, $rideshare);
error_log(json_encode($parsedemail_item_advice_google_id));
$leg_fare = $cost;
// Get departure and arrival times...
$date = new DateTime("now", new DateTimeZone($tz));
$date->add(new DateInterval('PT15M'));
$departure_time = $date->getTimestamp();
$duration = Route::getDuration(array_key_exists('duration',$oldTrip)?$oldTrip['duration']:900);
$date->add(new DateInterval('PT'.$duration.'M'));
$arrival_time = $date->getTimestamp();
$leg = [
'id' => NULL,
'parsedemail_item_advice_google_id' => $parsedemail_item_advice_google_id,
'arrival_time' => $arrival_time,
'arrival_time_zone' => $destination['timezone'],
'arrival_timezone' => $tz,
'departure_time' => $departure_time,
'departure_time_zone' => $origin['timezone'],
'departure_timezone' => $tz,
'distance' => $oldTrip['distance'],
'duration' => $oldTrip['duration'],
'fare_raw' => $cost,
'polyline' => $overview_polyline,
'quote' => $cost,
'steps' => count($leg_steps),
];
// Create leg
$google_directions_leg_id = Options::saveTripAdviceLeg($db, $rideshare, $leg);
$leg['id'] = $google_directions_leg_id;
foreach ($leg_steps as $i=>$step) {
$step['google_directions_leg_id'] = $google_directions_leg_id;
$google_directions_leg_step_id = Options::saveTripAdviceLegStep($db, $rideshare, $leg, $step);
$step['id'] = $google_directions_leg_step_id;
$leg_steps[$i] = $step;
}
$options = [
'gid' => $google_directions_leg_id, /* ??? */
'leg_fare' => [$google_directions_leg_id => $leg_fare],
'leg_steps' => [$google_directions_leg_id => $leg_steps],
'legs' => [$leg],
'routes' => 1
];
$rideshare['options'] = $options;
return [$option, $rideshare, $route];
}
protected static function getDuration($duration=900) {
if ($duration==NULL) {
return 15;
}
if ($duration>60) {
return (int)($duration/60);
}
return 1;
}
public static function processPolyline($data) {
if (!is_array($data)) {
return $data;
}
require_once('../common/Polyline.php');
$points = [];
foreach ($data as $item) {
$points = array_merge($points, Polyline::decode($item));
//$points = array_merge(Polyline::decode($item),$points);
break;
}
return Polyline::encode($points);
}
public static function tripService($id, $country, $mode) {
global $savvyext;
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
$oauth2_url = $savvyext->cfgReadChar('system.oauth2_url');
error_log("Route::tripService($id)");
if ($country!='SG' && $country!='US') {
throw new Exception('Unsupported country: '.$country);
}
// curl -d 'id=44254' -d 'mode=BART' -d 'country=US' -H 'Authorization: Server-Token 99dfe35fcb7de1ee' -X POST {$oauth2_url}modetrips
$postdata = http_build_query(
array(
'id'=> $id,
'mode' => $mode,
'country' => $country
)
);
$url = $oauth2_url."modetrips";
$opts = array(
'http' => array(
'method' => "POST",
'timeout' => 60, /* 1 minute */
'header' =>
"Content-Type: application/x-www-form-urlencoded\r\n" .
"Accept: application/json\r\n" .
"Authorization: Server-Token ${httpAuthToken}\r\n",
'content' => $postdata
),
"ssl" => array(
"verify_peer"=>false,
"verify_peer_name"=>false,
)
);
$context = stream_context_create($opts);
error_log("started trips service call");
$t = time();
$body = file_get_contents($url, false, $context);
error_log("complete trips service call: ".(time()-$t)." seconds");
$trip = json_decode($body,true);
return $trip;
}
}
/*
["id"]=>
string(1) "5"
["city_id"]=>
string(1) "2"
["name"]=>
string(4) "Uber"
["transport_provider_id"]=>
string(1) "1"
["custom"]=>
NULL
*/
// vi:ts=2
+83
View File
@@ -0,0 +1,83 @@
<?php
class RouteApi extends Api
{
public $apiName = 'route';
public function indexAction()
{
return $this->response(
array(
'error' => 'Data not found'
), 404);
}
/**
* Method GET
* Get single record (by id)
* http://DOMAIN/remove/1
* @return string
*/
public function viewAction()
{
//id must be the first parameter after /remove/x
return $this->response(
array(
'error'=> 'Data not found'
), 404);
}
public function createAction()
{
$message = "Unexpected options error";
$origin = $this->requestParams["origin"] ?? [];
$destination = $this->requestParams["destination"] ?? [];
$option = $this->requestParams["option"] ?? [];
$trip = $this->requestParams["trip"] ?? [];
$member_id = $this->requestParams["member_id"] ?? 0;
try {
$db = new Db();
list ($origin, $destination) = Options::checkValidLocations($db->getConnect(), $origin, $destination);
Options::checkValidOption($db->getConnect(), $option);
Options::checkValidTrip($db->getConnect(), $trip);
$option["member_id"] = $member_id;
$trip["member_id"] = $member_id;
list ($res, $trip, $route) = Route::getTransportRoute($db->getConnect(), $origin, $destination, $option, $trip);
if (is_array($res)) {
return $this->response([
'option' => $res,
'trip' => $trip,
'route' => $route
], 200);
} else {
throw new Exception("Failed to get transport route for origin");
}
} catch (Exception $e) {
$message = $e->getMessage();
}
return $this->response(
array(
"error" => $message
), 500);
}
public function updateAction()
{
return $this->response(
array(
"error" => "Update error"
), 400);
}
public function deleteAction()
{
return $this->response(
array(
"error" => "Delete error"
), 500);
}
}
@@ -0,0 +1,431 @@
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Psr7;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
class Autocab {
const TIMEOUT = 60;
const DEBUG = false;
const SSL_VERIFY = false;
const REASON_NOT_REQUIRED = "NotRequired";
const REASON_PRICE_CHANGED = "PriceChanged";
private $httpClient;
private $agentId;
private $agentPassword;
private $currency;
private $vendorId;
private $templatesDir;
private $config = [];
private $templates = [];
public function __construct($config) {
if (empty($config)) {
throw new Exception("Configuration is not provided");
}
$this->setConfig($config);
if (!isset($config["baseUrl"]) || empty($config["baseUrl"])) {
throw new Exception("The API URL is not specified");
}
$this->httpClient = new Client(['base_uri' => $config["baseUrl"]]);
}
public function setConfig($config) {
global $savvyext;
$this->config = $config;
$this->agentId = $config["agentId"] ;
$this->agentPassword = $config["agentPassword"];
$this->currency = $config["currency"];
$this->templatesDir = $config["templatesDir"];
$this->loadTemplates($config);
}
public function loadTemplates($config) {
if ( !array_key_exists('templatesDir', $config) || empty($config["templatesDir"])) {
throw new Exception("The templates dir is not specified");
}
$files = scandir($this->templatesDir);
if ($files) {
foreach ($files as $templateFile) {
$template = file_get_contents($this->templatesDir."/".$templateFile);
if ($template) {
$this->templates[$templateFile] = $template;
}
}
}
if (count($this->templates) == 0) {
throw new Exception("Any one template is not initialized");
}
}
public function getTemplate($name, $oldRootTag=NULL, $newRootTag=NULL) {
if (!isset($this->templates[$name])) {
return false;
}
$template = $this->templates[$name];
if (isset($oldRootTag) && isset($newRootTag)) {
$template = str_replace($oldRootTag, $newRootTag, $template);
}
$xmlElement = new SimpleXMLElement($template);
$xmlElement->Agent->attributes()->Id = $this->config["agentId"];
$xmlElement->Agent->Password = $this->config["agentPassword"];
return $xmlElement;
}
public function getHeaders($extraHeaders = []) {
$headers = [
'content-type' => 'text/xml',
];
return array_merge($headers,$extraHeaders);
}
public function getAvailability($source, $destination) {
if (!isset($this->templates['autocab_agent_bid_request.xml'])) {
throw new Exception("Bid request template is not initialized");
}
$uri = '/api/agent';
$xmlElement = $this->getTemplate('autocab_agent_bid_request.xml');
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
$xmlElement->BidParameters->Journey->From->Data = $source["address"];
$xmlElement->BidParameters->Journey->From->Coordinate->Latitude = $source["latitude"];
$xmlElement->BidParameters->Journey->From->Coordinate->Longitude = $source["longitude"];
$xmlElement->BidParameters->Journey->To->Data = $destination["address"];
$xmlElement->BidParameters->Journey->To->Coordinate->Latitude = $destination["latitude"];
$xmlElement->BidParameters->Journey->To->Coordinate->Longitude = $destination["longitude"];
$xml = $xmlElement->asXML();
$result = $this->genericPost($uri, $xml);
if ($result["error"]) {
return $result;
}
if ($result["code"] != 200) {
var_dump($result["data"]);
}
$xmlResponse = new SimpleXMLElement($result["data"]);
$error = self::processError($xmlResponse);
if($error) {
return ["error"=>1, "data" => $error, "request"=>$xmlElement];
}
$costs = [];
foreach ($xmlResponse->Offers->Offer as $offer) {
$item = self::processBidOffer($offer);
array_push($costs, $item);
}
return array("error"=>0, "data"=>$costs, "raw_data"=>$xmlResponse, "request"=>$xmlElement);
}
public function getBookingAvailability($source, $destination, $vendor) {
if (!isset($this->templates['autocab_agent_booking_availability_request.xml'])) {
throw new Exception("Booking Availability request template is not initialized");
}
$uri = '/api/agent';
$xmlElement = $this->getTemplate('autocab_agent_booking_availability_request.xml');
$xmlElement->Vendor->attributes()->Id = $vendor;
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
$xmlElement->BookingParameters->Journey->From->Data = $source["address"] ?? "No Address";
$xmlElement->BookingParameters->Journey->From->Coordinate->Latitude = $source["latitude"];
$xmlElement->BookingParameters->Journey->From->Coordinate->Longitude = $source["longitude"];
$xmlElement->BookingParameters->Journey->To->Data = $destination["address"] ?? "No Address";
$xmlElement->BookingParameters->Journey->To->Coordinate->Latitude = $destination["latitude"];
$xmlElement->BookingParameters->Journey->To->Coordinate->Longitude = $destination["longitude"];
$xml = $xmlElement->asXML();
$result = $this->genericPost($uri, $xml);
if ($result["error"]) {
return $result;
}
if ($result["code"] != 200) {
var_dump($result["data"]);
}
$xmlResponse = new SimpleXMLElement($result["data"]);
$error = self::processError($xmlResponse);
if($error) {
return ["error"=>1, "data" => $error, "request"=>$xmlElement];
}
$cost = null;
if(isset($xmlResponse->Pricing)) {
$cost = [
'currency' => (string)$xmlResponse->Pricing->Currency,
'availability_ref' => (string)$xmlResponse->AvailabilityReference,
'display_name' => (string)$xmlResponse->VendorDetails->Name,
'high_estimate' => (int)$xmlResponse->Pricing->Price,
'low_estimate' => (int)$xmlResponse->Pricing->Price,
'distance_estimate' => (int) $xmlResponse->EstimatedJourney->Distance,
'duration_estimate' => (int) $xmlResponse->EstimatedJourney->Duration * 60,
'vendor_id' => (string) $xmlResponse->Vendor->attributes()->Id,
];
}
return ["error"=>0, "data"=>$cost, "raw_data"=>$xmlResponse, "request"=>$xmlElement];
}
public function cancelAvailability($availabilityRef, $vendor, $reason = self::REASON_NOT_REQUIRED) {
if (!isset($this->templates['autocab_agent_booking_not_authorized_request.xml'])) {
throw new Exception("BookingNotAuthorizedRequest template is not initialized");
}
if (!isset($availabilityRef)) {
throw new Exception("Invalid availability reference");
}
$uri = '/api/agent';
$xmlElement = $this->getTemplate('autocab_agent_booking_not_authorized_request.xml');
$xmlElement->Vendor->attributes()->Id = $vendor;
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
$xmlElement->AvailabilityReference = $availabilityRef;
$xmlElement->Reason = $reason;
$xml = $xmlElement->asXML();
$result = $this->genericPost($uri, $xml);
if ($result["error"]) {
return $result;
}
if ($result["code"] != 200) {
var_dump($result["data"]);
}
$xmlResponse = new SimpleXMLElement($result["data"]);
$error = self::processError($xmlResponse);
if($error) {
return ["error"=>1, "data" => $error, "request"=>$xmlElement];
}
return ["error"=>0, "data"=>[], "raw_data"=>$xmlResponse, "request"=>$xmlElement];
}
public function booking($availabilityRef, $passengers=[], $vendor) {
if (!isset($this->templates['autocab_agent_booking_athorization_request.xml'])) {
throw new Exception("BookingAuthorization template is not initialized");
}
if (!isset($availabilityRef)) {
throw new Exception("Invalid availability reference");
}
if (!is_array($passengers) || count($passengers) < 1
|| !array_key_exists("name", $passengers[0]) || empty($passengers[0]["name"])
) {
throw new Exception("Invalid passengers information");
}
$uri = '/api/agent';
$xmlElement = $this->getTemplate('autocab_agent_booking_athorization_request.xml');
$xmlElement->Vendor->attributes()->Id = $vendor;
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
$xmlElement->AvailabilityReference = $availabilityRef;
foreach ($passengers as $key => $passenger) {
$xmlPassenger = $xmlElement->Passengers->addChild("PassengerDetails");
$isLead = ($key === array_keys($passengers)[0]) ? "true" : "false";
$xmlPassenger->addAttribute("IsLead", "$isLead");
$xmlPassenger->addChild("Name", $passenger["name"]);
if (array_key_exists("phone", $passenger) && isset($passenger["phone"])) {
$xmlPassenger->addChild( "TelephoneNumber", $passenger["phone"] );
}
if (array_key_exists("email", $passenger) && isset($passenger["email"])) {
$xmlPassenger->addChild("EmailAddress", $passenger["email"]);
}
}
$xml = $xmlElement->asXML();
$result = $this->genericPost($uri, $xml);
if ($result["error"]) {
return $result;
}
if ($result["code"] != 200) {
var_dump($result["data"]);
}
$xmlResponse = new SimpleXMLElement($result["data"]);
$error = self::processError($xmlResponse);
if($error) {
return ["error"=>1, "data" => $error];
}
$resp = [];
if(isset($xmlResponse->AuthorizationReference)) {
$resp = [
'authorization_ref' => (string)$xmlResponse->AuthorizationReference,
'booking_ref' => (string)$xmlResponse->BookingReference,
];
}
return ["error"=>0, "data"=>$resp, "raw_data"=>$xmlResponse, "request"=>$xmlElement];
}
public function bookingStatus($authorizationRef, $vendor) {
if (!isset($this->templates['autocab_agent_booking_status_request.xml'])) {
throw new Exception("BookingStatusRequest template is not initialized");
}
if (!isset($authorizationRef)) {
throw new Exception("Invalid authorization reference");
}
$uri = '/api/agent';
$xmlElement = $this->getTemplate('autocab_agent_booking_status_request.xml');
$xmlElement->Vendor->attributes()->Id = $vendor;
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
$xmlElement->AuthorizationReference = $authorizationRef;
$xml = $xmlElement->asXML();
$result = $this->genericPost($uri, $xml);
if ($result["error"]) {
return $result;
}
if ($result["code"] != 200) {
var_dump($result["data"]);
}
$xmlResponse = new SimpleXMLElement($result["data"]);
$error = self::processError($xmlResponse);
if($error) {
return ["error"=>1, "data" => $error, "request"=>$xmlElement];
}
$resp = [
'booking_ref' => (string)$xmlResponse->BookingReference,
'status' => (string)$xmlResponse->Status,
'cancellation_reason' => (string)$xmlResponse->CancellationReason
];
return ["error"=>0, "data"=>$resp, "raw_data"=>$xmlResponse, "request"=>$xmlElement];
}
public function bookingCancellation($authorizationRef, $vendor) {
if (!isset($this->templates['autocab_agent_booking_cancellation_request.xml'])) {
throw new Exception("BookingCancellationRequest template is not initialized");
}
if (!isset($authorizationRef)) {
throw new Exception("Invalid authorization reference");
}
$uri = '/api/agent';
$xmlElement = $this->getTemplate('autocab_agent_booking_cancellation_request.xml');
$xmlElement->Vendor->attributes()->Id = $vendor;
$xmlElement->Agent->Time = gmdate("Y-m-d\TH:i:s.v\Z");
$xmlElement->AuthorizationReference = $authorizationRef;
$xml = $xmlElement->asXML();
$result = $this->genericPost($uri, $xml);
if ($result["error"]) {
return $result;
}
if ($result["code"] != 200) {
var_dump($result["data"]);
}
$xmlResponse = new SimpleXMLElement($result["data"]);
$error = self::processError($xmlResponse);
if($error) {
return ["error"=>1, "data" => $error, "code" => 500, "request"=>$xmlElement];
}
$resp = [];
return ["error"=>0, "data"=>$resp, "code" => 200, "raw_data"=>$xmlResponse, "request"=>$xmlElement];
}
private function genericPost($uri, $xml, $extraHeaders=[]) {
$code = 0;
$data = [];
$error = 0;
try {
$json = json_encode($data);
$length = strlen($json);
$response = $this->httpClient->request('POST', $uri, [
'decode_content' => true,
'connect_timeout' => SELF::TIMEOUT,
'debug' => SELF::DEBUG,
'verify' => SELF::SSL_VERIFY,
'headers' => $this->getHeaders(),
'body' => $xml
]);
$code = $response->getStatusCode();
$data = $response->getBody();
} catch (RequestException $e) {
if (SELF::DEBUG) {
echo Psr7\str($e->getRequest());
if ($e->hasResponse()) {
echo Psr7\str($e->getResponse());
}
}
if ($e->hasResponse()) {
$code = (int) $e->getResponse()->getStatusCode();
}
$data = ["error" => "exception", "error_description" => $e->getMessage()];
$error = 1;
} catch (ClientException $e) {
if (SELF::DEBUG) {
echo Psr7\str($e->getRequest());
echo Psr7\str($e->getResponse());
}
$code = (int)$e->getResponse()->getStatusCode();
$data = ["error" => "exception", "error_description" => $e->getMessage()];
$error = 1;
} catch (Exception $e) {
if (SELF::DEBUG) {
echo Psr7\str($e->getRequest());
echo Psr7\str($e->getResponse());
}
$code = (int)$e->getResponse()->getStatusCode();
$data = ["error" => "exception", "error_description" => $e->getMessage()];
$error = 1;
}
return ["error" => $error, "data" => $data, "code" => $code];
}
public static function processError($xmlResponse) {
if ((string)$xmlResponse->Result->Success == "true") {
return false;
}
return ["error"=>"API error", "error_description" => (string) $xmlResponse->Result->FailureReason, "error_code" => (string) $xmlResponse->Result->FailureCode];
}
public static function processBidOffer($offer) {
$item = [
'currency' => (string)$offer->Pricing->Currency,
'transport_product' => (string)$offer['Reference'],
'display_name' => (string)$offer->VendorDetails->Name,
'high_estimate' => (int)$offer->Pricing->Price,
'low_estimate' => (int)$offer->Pricing->Price,
'distance_estimate' => (int) $offer->EstimatedJourney->Distance,
'duration_estimate' => (int) $offer->EstimatedJourney->Duration * 60,
];
return $item;
}
}
@@ -0,0 +1,33 @@
<AgentBidRequest>
<Agent Id="{{agent_id}}">
<Password>{{agent_password}}</Password>
<Reference>AgentRef</Reference>
<Time>{{agent_time}}</Time>
</Agent>
<BidParameters>
<Pricing>
<Currency>USD</Currency>
<PaymentType>Cash</PaymentType>
</Pricing>
<Journey>
<From>
<Type>Address</Type>
<Data>1563 Van Dyke Ave, San Francisco, CA 94124, USA</Data>
<Coordinate>
<Latitude>37.7286068</Latitude>
<Longitude>-122.3919092</Longitude>
</Coordinate>
</From>
<To>
<Type>Address</Type>
<Data>536 Edinburgh St, San Francisco, CA 94112, USA</Data>
<Coordinate>
<Latitude>37.7206606</Latitude>
<Longitude>-122.4326099</Longitude>
</Coordinate>
</To>
</Journey>
<Ride Type="Passenger">
</Ride>
</BidParameters>
</AgentBidRequest>
@@ -0,0 +1,16 @@
<AgentBookingAuthorizationRequest>
<Agent Id="{{agent_id}}">
<Password>{{agent_password}}</Password>
<Reference>AgentRef</Reference>
<Time>{{agent_time}}</Time>
</Agent>
<Vendor Id="{{vendor_id}}" />
<AvailabilityReference>{{availability_reference}}</AvailabilityReference>
<Passengers>
</Passengers>
<Notifications>
<VendorEvents>BookingDispatched NoFare BookingCompleted BookingCancelled</VendorEvents>
<AlertMethod>None</AlertMethod>
<AgentEvents>BookingDispatched NoFare BookingCompleted BookingCancelled LocationUpdate VehicleArrived PassengerOnBoard</AgentEvents>
</Notifications>
</AgentBookingAuthorizationRequest>
@@ -0,0 +1,34 @@
<AgentBookingAvailabilityRequest>
<Agent Id="{{agent_id}}">
<Password>{{agent_password}}</Password>
<Reference>AgentRef</Reference>
<Time>{{agent_time}}</Time>
</Agent>
<Vendor Id="{{vendor_id}}" />
<BookingParameters>
<Pricing>
<Currency>USD</Currency>
<PaymentType>Cash</PaymentType>
</Pricing>
<Journey>
<From>
<Type>Address</Type>
<Data>1563 Van Dyke Ave, San Francisco, CA 94124, USA</Data>
<Coordinate>
<Latitude>37.7286068</Latitude>
<Longitude>-122.3919092</Longitude>
</Coordinate>
</From>
<To>
<Type>Address</Type>
<Data>536 Edinburgh St, San Francisco, CA 94112, USA</Data>
<Coordinate>
<Latitude>37.7206606</Latitude>
<Longitude>-122.4326099</Longitude>
</Coordinate>
</To>
</Journey>
<Ride Type="Passenger">
</Ride>
</BookingParameters>
</AgentBookingAvailabilityRequest>
@@ -0,0 +1,9 @@
<AgentBookingCancellationRequest>
<Agent Id="{{agent_id}}">
<Password>{{agent_password}}</Password>
<Reference>AgentRef</Reference>
<Time>{{agent_time}}</Time>
</Agent>
<Vendor Id="{{vendor_id}}" />
<AuthorizationReference>{{authorization_reference}}</AuthorizationReference>
</AgentBookingCancellationRequest>
@@ -0,0 +1,13 @@
<AgentBookingCancelledEventResponse>
<Vendor Id="{{vendor_id}}">
<Reference>{{vendor_ref}}</Reference>
<Time>{{vendor_time}}</Time>
</Vendor>
<Agent Id="{{agent_id}}"/>
<Result>
<Success>true</Success>
<FailureCode/>
<FailureReason/>
</Result>
<AcknowledgementReference>{{acknowledgement_ref}}</AcknowledgementReference>
</AgentBookingCancelledEventResponse>
@@ -0,0 +1,10 @@
<AgentBookingNotAuthorizedRequest>
<Agent Id="{{agent_id}}">
<Password>{{agent_password}}</Password>
<Reference>AgentRef</Reference>
<Time>{{agent_time}}</Time>
</Agent>
<Vendor Id="{{vendor_id}}" />
<AvailabilityReference>{{availability_reference}}</AvailabilityReference>
<Reason>NotRequired</Reason>
</AgentBookingNotAuthorizedRequest>
@@ -0,0 +1,9 @@
<AgentBookingStatusRequest>
<Agent Id="{{agent_id}}">
<Password>{{agent_password}}</Password>
<Reference>AgentRef</Reference>
<Time>{{agent_time}}</Time>
</Agent>
<Vendor Id="{{vendor_id}}" />
<AuthorizationReference>{{authorization_reference}}</AuthorizationReference>
</AgentBookingStatusRequest>
@@ -0,0 +1,13 @@
<AgentVehicleArrivedEventResponse>
<Vendor Id="{{vendor_id}}">
<Reference>{{vendor_ref}}</Reference>
<Time>{{vendor_time}}</Time>
</Vendor>
<Agent Id="{{agent_id}}"/>
<Result>
<Success>true</Success>
<FailureCode/>
<FailureReason/>
</Result>
<AcknowledgementReference>{{acknowledgement_ref}}</AcknowledgementReference>
</AgentVehicleArrivedEventResponse>
@@ -0,0 +1,13 @@
<AgentEventResponse>
<Vendor Id="{{vendor_id}}">
<Reference>{{vendor_ref}}</Reference>
<Time>{{vendor_time}}</Time>
</Vendor>
<Agent Id="{{agent_id}}"/>
<Result>
<Success>true</Success>
<FailureCode/>
<FailureReason/>
</Result>
<AcknowledgementReference>{{acknowledgement_ref}}</AcknowledgementReference>
</AgentEventResponse>
+80
View File
@@ -0,0 +1,80 @@
<?php
$profile = getenv("SAVVY_PROFILE");
if ($profile) {
require_once("../../core/backend.${profile}.php");
} else {
require_once( '../../core/backend.php' );
}
include_once '../config.php';
require_once('../constants.php');
require_once('../common/vendor/autoload.php');
require_once('../common/Api.php');
require_once('../common/Db.php');
require_once('../common/GoogleKMS.php');
require_once('../common/Logger.php');
require_once('../trips/MultiModalFilter.php');
require_once('Options.php');
require_once('OptionsApi.php');
require_once('Quote.php');
require_once('QuoteApi.php');
require_once('Route.php');
require_once('RouteApi.php');
require_once('AutocabApi.php');
require_once('autocab/Autocab.php');
require_once('Booking.php');
require_once('../trips/SGBikeApi.php');
$httpAuthToken = $savvyext->cfgReadChar('system.oauth2_token');
header("Access-Control-Allow-Origin: *");
header("Access-Control-Expose-Headers: Access-Control-Allow-Origin");
header("Access-Control-Allow-Headers: Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, client_id");
header("Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS");
header('Content-type: application/json');
if ("OPTIONS" === $_SERVER['REQUEST_METHOD']) {
exit();
}
$headers = getallheaders();
if ((!isset($headers["Authorization"]) || substr($headers["Authorization"],-strlen($httpAuthToken))!=$httpAuthToken) &&
(!isset($headers["authorization"]) || substr($headers["authorization"],-strlen($httpAuthToken))!=$httpAuthToken)) {
header('HTTP/1.1 401 Unauthorized');
header('Status: 401 Unauthorized');
echo "{\"status\":\"Missing authorization\"}";
exit();
}
try {
if (strpos($_SERVER['REQUEST_URI'],'/api/')===false) {
throw new Exception("Invalid API request");
}
$requestUri = explode('/', trim($_SERVER['REQUEST_URI'],'/'));
while (array_shift($requestUri) !== 'api') {
};
if ($requestUri[0]=='options') {
$api = new OptionsApi($requestUri);
}
else if ($requestUri[0]=='route') {
$api = new RouteApi($requestUri);
}
else if ($requestUri[0]=='quote') {
$api = new QuoteApi($requestUri);
}
else if ($requestUri[0]=='autocab') {
$api = new AutocabApi($requestUri);
}
else {
echo json_encode(Array('error' => 'Invalid API request'));
}
echo $api->run();
}
catch (Exception $e) {
echo json_encode(Array('error' => $e->getMessage()));
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

@@ -0,0 +1,60 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="swagger-ui.css" >
<link rel="icon" type="image/png" href="favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="favicon-16x16.png" sizes="16x16" />
<style>
html
{
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}
*,
*:before,
*:after
{
box-sizing: inherit;
}
body
{
margin:0;
background: #fafafa;
}
</style>
</head>
<body>
<div id="swagger-ui"></div>
<script src="swagger-ui-bundle.js"> </script>
<script src="swagger-ui-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "../swagger.php",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout"
})
// End Swagger UI call region
window.ui = ui
}
</script>
</body>
</html>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
<?php
require('../../../adminsavvy/vendor/autoload.php');
$openapi = \OpenApi\scan('.');
header('Content-Type: application/json');
echo $openapi->toJson();
?>