376 lines
17 KiB
PHP
376 lines
17 KiB
PHP
<?php
|
|
|
|
class QuoteGroupApi extends Api
|
|
{
|
|
public $apiName = 'quotegroup';
|
|
const CACHE_TIMEOUT = 300; // 5 minutes
|
|
|
|
public function indexAction() {
|
|
$message = 'Unhandled exception';
|
|
$result = [];
|
|
$data = $this->requestParams["data"] ?? [];
|
|
error_log(json_encode( $this->requestParams));
|
|
try {
|
|
if (!is_array($data) || count($data)<1) {
|
|
throw new Exception('Data not found');
|
|
}
|
|
$db = new Db();
|
|
foreach ($data as $item) {
|
|
$id = $item['quote_group_id'];
|
|
if ($id<1) {
|
|
$item['quote_group_error'] = 'Missing or invalid quote group ID';
|
|
$item['quote_group'] = NULL;
|
|
$data[$key] = $item;
|
|
continue;
|
|
}
|
|
list($message,$code,$action,$data) = self::viewReal($db, $id);
|
|
if ($code==200) {
|
|
$item['quote_group_error'] = NULL;
|
|
$item['quote_group'] = $data;
|
|
} else {
|
|
$item['quote_group_error'] = $message;
|
|
$item['quote_group'] = NULL;
|
|
}
|
|
$result[] = $item;
|
|
}
|
|
return $this->response($result, 200);
|
|
} catch (Exception $e) {
|
|
$message = $e->getMessage();
|
|
error_log('EXCEPTION: '.$message);
|
|
}
|
|
return $this->response(
|
|
array(
|
|
'error' => $message
|
|
), 404);
|
|
}
|
|
|
|
/**
|
|
* Method GET
|
|
* Get single record (by id)
|
|
* http://DOMAIN/quote/1
|
|
* @return string
|
|
*/
|
|
public function viewAction() {
|
|
//id must be the first parameter after /trips/x
|
|
$id = array_shift($this->requestUri);
|
|
$db = new Db();
|
|
list($message,$code,$action,$data) = self::viewReal($db, $id);
|
|
if ($code==200) {
|
|
return $this->response($data, 200);
|
|
}
|
|
return $this->response(
|
|
array(
|
|
'error'=> $message
|
|
), $code);
|
|
}
|
|
|
|
public static function viewReal($db, $id) {
|
|
error_log('QuoteGroupApi::viewReal()');
|
|
$message = 'Data not found';
|
|
$code = 404;
|
|
$action = '';
|
|
$data = [];
|
|
try {
|
|
if(!$id || (int)$id<1) {
|
|
throw new Exception('Invalid quote group ID');
|
|
}
|
|
$quoteGroup = QuoteGroup::getById($db->getConnect(), (int)$id);
|
|
if(!is_array($quoteGroup) || count($quoteGroup)<1) {
|
|
throw new Exception('Quote group was not found');
|
|
}
|
|
// Check "completed"
|
|
if ($quoteGroup["completed"]!="" && (int)$quoteGroup["cost"]!=0) {
|
|
error_log('Quote group #'.$id.' was completed with the cost '.$quoteGroup["cost"]);
|
|
list($quotes,$err) = Quotes::getByQuoteGroupId($db->getConnect(), (int)$id);
|
|
if ((!is_array($quotes) || count($quotes)<1) && $quoteGroup["quote_id"]>0) {
|
|
$quotes = [];
|
|
array_push($quotes, Quotes::getById($db->getConnect(), $quoteGroup["quote_id"]));
|
|
}
|
|
$quoteGroup["quotes"] = $quotes; // Does not matter much if we have "cost"
|
|
$quoteGroup["deeplink"] = Quotes::deeplink($db->getConnect(), $quoteGroup);
|
|
return ["OK",200,"",$quoteGroup]; // OK!
|
|
} else {
|
|
error_log('Quote group #'.$id.' was completed yet...');
|
|
}
|
|
// Load quotes
|
|
list($quotes,$err) = Quotes::getByQuoteGroupId($db->getConnect(), (int)$id);
|
|
if (!is_array($quotes) || count($quotes)<1) {
|
|
if ($quoteGroup["quote_id"]>0) {
|
|
$quotes = [];
|
|
array_push($quotes, Quotes::getById($db->getConnect(), $quoteGroup["quote_id"]));
|
|
error_log(json_encode($quotes));
|
|
} else {
|
|
throw new Exception ($err!=''?$err:'Failed to load quotes');
|
|
}
|
|
} else {
|
|
error_log(json_encode($quotes));
|
|
}
|
|
// Check each quote
|
|
$completed = 0;
|
|
$lowestQuote = NULL;
|
|
$cost = PHP_INT_MAX;
|
|
foreach ($quotes as $quote) {
|
|
for ($i=0; $i<3; $i++) {
|
|
if ($quote["completed"]=="" && $quote["travel_date"]=="" && $quote["automation_id"]>0) {
|
|
if ($i>0) {
|
|
sleep(3); // wait...
|
|
}
|
|
// Call service if needed
|
|
list($quoteService, $err) = QuoteAPI::checkQuote($quote["automation_id"]);
|
|
// Update
|
|
if (is_array($quoteService) && $quoteService["complete"]!="") {
|
|
$quoteService["automation_id"] = $quoteService["id"];
|
|
$quoteService["id"] = (int)$quote["id"];
|
|
$quoteService["completed"] = $quoteService["complete"];
|
|
$timezone = Address::getTzByAddressId($db->getConnect(),$quote["location_start_id"]);
|
|
$quoteService["travel_date"] = Utilities::convertUtcToLocal($quoteService["complete"],$timezone);
|
|
$quoteService["created"] = $quote["created"];
|
|
$quoteService["location_start_id"] = $quote["location_start_id"];
|
|
$quoteService["location_end_id"] = $quote["location_end_id"];
|
|
$quoteService["member_id"] = $quote["member_id"];
|
|
$quoteService["deeplink"] = Quotes::deeplink($db->getConnect(), $quote);
|
|
list ($res, $err) = Quotes::update($db->getConnect(), $quoteService);
|
|
if ($quoteService["cost"]==0 &&
|
|
$quoteService["message"]=="android_automation_job_detail") {
|
|
// We must take the details
|
|
$quoteService = QuoteApi::quoteDetails($db, $quoteService);
|
|
}
|
|
$quote = $quoteService;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
// Check "completed"
|
|
if ($quote["completed"]!="" && $quote["travel_date"]!="" && (int)$quote["cost"]!=0) {
|
|
error_log('Completed!');
|
|
$completed++;
|
|
if ($quote["cost"] < $cost) {
|
|
$cost = $quote["cost"];
|
|
$lowestQuote = $quote;
|
|
}
|
|
}
|
|
}
|
|
$quoteGroup["cost"] = $cost==PHP_INT_MAX ? NULL : $cost;
|
|
$quoteGroup["transport_provider_id"] = is_array($lowestQuote) ? $lowestQuote["transport_provider_id"] : NULL;
|
|
$quoteGroup["quote_id"] = is_array($lowestQuote) ? $lowestQuote["id"] : NULL;
|
|
$quoteGroup["processed"] = $completed;
|
|
// All completed?
|
|
if ($completed>=$quoteGroup["transport_providers"]) {
|
|
// All quotes completed - hurray!
|
|
$quoteGroup["completed"] = date("Y-m-d H:i:s");
|
|
} else if (is_array($lowestQuote)) {
|
|
// We will update since we have at least one quote completed
|
|
$quoteGroup["completed"] = NULL;
|
|
} else {
|
|
// Do not update, as nothing has completed
|
|
$quoteGroup["quotes"] = $quotes;
|
|
return ["OK",200,"",$quoteGroup];
|
|
}
|
|
list($res,$err) = QuoteGroup::update($db->getConnect(), $quoteGroup);
|
|
if ($res==NULL || !array_key_exists("id",$res)) {
|
|
error_log('QuoteGroup::update() failed?');
|
|
error_log(json_encode($res));
|
|
$res = $quoteGroup;
|
|
}
|
|
$res["quotes"] = $quotes;
|
|
if (isset($res["transport_provider_id"]) && $res["transport_provider_id"]>0) {
|
|
$res["deeplink"] = Quotes::deeplink($db->getConnect(), $res);
|
|
}
|
|
error_log(json_encode($res));
|
|
return ["OK",200,"",$res];
|
|
} catch (Exception $e) {
|
|
$code = 500;
|
|
$message = $e->getMessage();
|
|
error_log('EXCEPTION: '.$message);
|
|
}
|
|
return [$message,$code,$action,$data];
|
|
}
|
|
|
|
public function createAction() {
|
|
$origin = $this->requestParams["origin"] ?? "";
|
|
$destination = $this->requestParams["destination"] ?? "";
|
|
$member_id = $this->requestParams["member_id"] ?? 0;
|
|
$country = $this->requestParams["country"] ?? GeocodeApi::DEFAULT_COUNTRY_CODE;
|
|
$transport_providers = $this->requestParams["transport_providers"] ?? [];
|
|
|
|
$db = new Db();
|
|
|
|
// DEBUG
|
|
$country = Geocode::mockGPSCountry($db->getConnect(), $member_id, $country, 'default');
|
|
|
|
list($message,$code,$action,$data) = QuoteGroupApi::createReal(
|
|
$db, $origin, $destination, $member_id, $transport_providers, $country
|
|
);
|
|
if ($code==200) {
|
|
if ($action=="view") {
|
|
$id = $data["id"];
|
|
array_unshift($this->requestUri, $id);
|
|
return $this->viewAction();
|
|
} else if ($action=="response") {
|
|
return $this->response($data, 200);
|
|
}
|
|
}
|
|
return $this->response(
|
|
array(
|
|
"error" => $message
|
|
), $code);
|
|
}
|
|
|
|
public static function createReal($db, $origin, $destination, $member_id, $transport_providers, $country=GeocodeApi::DEFAULT_COUNTRY_CODE) {
|
|
syslog(LOG_WARNING,"QuoteGroupApi::createReal(\$db, $origin, $destination, $member_id, \$transport_providers, $country)");
|
|
$message = "Failed to schedule quotes";
|
|
$code = 500;
|
|
$action = "";
|
|
$data = [];
|
|
$city = '';
|
|
try {
|
|
foreach ($transport_providers as $key=>$transport_provider_id) {
|
|
if (!in_array($transport_provider_id, [1,2,3,4,5,8])) {
|
|
unset($transport_providers[$key]);
|
|
}
|
|
if (in_array($transport_provider_id, [3,4,5])) {
|
|
$city = 'Singapore';
|
|
}
|
|
if (in_array($transport_provider_id, [1,2,8])) {
|
|
$city = 'Other';
|
|
}
|
|
}
|
|
if (count($transport_providers)<1) {
|
|
throw new Exception('Unsupported transport provider IDs');
|
|
}
|
|
if (!$destination || !$origin) {
|
|
throw new Exception('Invalid origin and/or destination address');
|
|
}
|
|
// Check if exists
|
|
list($total, $quoteGroups) = QuoteGroup::getByOriginDestinationId(
|
|
$db->getConnect(),
|
|
$origin, $destination, 0 /* transport_provider_id */,
|
|
1, 0); // LIMIT # OFFSET #
|
|
if (is_array($quoteGroups) && count($quoteGroups)>0) {
|
|
$quoteGroup = $quoteGroups[0];
|
|
$id = $quoteGroup["id"]; // Will return the last quote
|
|
$completed = $quoteGroup["completed"]; // Completed with the SELF::CACHE_TIMEOUT
|
|
if (time()-strtotime($completed)<SELF::CACHE_TIMEOUT) {
|
|
return ["Quote from cache",200,"view",["id"=>$id]];
|
|
}
|
|
}
|
|
// Geocode origin and destination
|
|
$headers = getallheaders();
|
|
$client_id = isset($headers['client_id'])?$headers['client_id']:"Unknown";
|
|
list($originData, $err) = GeocodeAPI::geocode($db, $origin, $country,$client_id);
|
|
$log = [
|
|
'message' => 'QuoteGroupApi::geocode origin',
|
|
'data' => ['client_id'=>$client_id, 'address_input'=>$origin],
|
|
'errror' => $err
|
|
];
|
|
Logger::debug($log);
|
|
if ($err || !isset($originData["lat"])) throw new Exception("Origin geocoding failed: ".$err);
|
|
list($destinationData, $err) = GeocodeAPI::geocode($db, $destination, $country, $client_id);
|
|
$log = [
|
|
'message' => 'QuoteGroupApi::geocode destination',
|
|
'data' => ['client_id'=>$client_id, 'address_input'=>$destination],
|
|
'errror' => $err
|
|
];
|
|
Logger::debug($log);
|
|
if ($err || !isset($destinationData["lat"])) throw new Exception("Destination geocoding failed: ".$err);
|
|
|
|
$isOriginWithin = false;
|
|
$isDestinationWithin = false;
|
|
// Check if we are within Singapore: for Grab, Gojek, CDG
|
|
|
|
if ($city=='Singapore') {
|
|
$sgLat = 1.352083;
|
|
$sgLng = 103.819836;
|
|
$radius = 25; // km
|
|
$isOriginWithin = GeocodeAPI::withinCity(
|
|
$originData["address"],$originData["lat"],$originData["lng"],
|
|
$city,$sgLat,$sgLng,$radius);
|
|
$isDestinationWithin = GeocodeAPI::withinCity(
|
|
$destinationData["address"],$destinationData["lat"],$destinationData["lng"],
|
|
$city,$sgLat,$sgLng,$radius);
|
|
}
|
|
// Uber & Lyft: no check required
|
|
if ($city=='Other') {
|
|
$isOriginWithin = true;
|
|
$isDestinationWithin = true;
|
|
}
|
|
|
|
if ($isOriginWithin && $isDestinationWithin) {
|
|
syslog(LOG_WARNING,"'".$originData["address"]."' and '".$destinationData["address"]."' are within Singapore");
|
|
} else {
|
|
throw new Exception("Origin and/or destination is not within the service area!");
|
|
}
|
|
// Create quote group
|
|
$requestData = [
|
|
"location_start_lat" => $originData["lat"],
|
|
"location_start_lng" => $originData["lng"],
|
|
"location_start" => $originData["address"] ?? $origin,
|
|
"location_start_country" => $originData["country"],
|
|
"location_end_lat" => $destinationData["lat"],
|
|
"location_end_lng" => $destinationData["lng"],
|
|
"location_end" => $destinationData["address"] ?? $destination,
|
|
"location_end_country" => $destinationData["country"],
|
|
"transport_providers" => $transport_providers
|
|
];
|
|
list($quoteGroup,$err) = QuoteGroup::create($db->getConnect(), $requestData);
|
|
if ($quoteGroup==NULL || !isset($quoteGroup["id"])) {
|
|
throw new Exception("Quote group creation failed: " . ($err ?? $message));
|
|
}
|
|
$id = $quoteGroup["id"];
|
|
syslog(LOG_WARNING,'New QuoteGroup ID: '.$id);
|
|
// Schedule the quotes
|
|
//$quoteApi = new QuoteApi($this->requestUri, false /* no encryption for internal call */);
|
|
$quotes = [];
|
|
foreach ($transport_providers as $transport_provider_id) {
|
|
//$quoteApi->requestParams["transport_provider_id"] = $transport_provider_id;
|
|
list($message,$code,$action,$data) = QuoteApi::createReal(
|
|
$db, $origin, $destination, $member_id, $transport_provider_id, $id, 0, $country, 'f'
|
|
);
|
|
$quote_id = 0;
|
|
if ($code==200 && ($action=="view" || $action=="response")) {
|
|
$quote_id = $data["id"];
|
|
$quotes[] = $quote_id;
|
|
} else {
|
|
syslog(LOG_WARNING,'ERROR: '.$message);
|
|
}
|
|
syslog(LOG_WARNING,'QUOTE: '.json_encode($data));
|
|
}
|
|
$quoteGroup["cost"] = NULL;
|
|
$quoteGroup["transport_provider_id"] = count($quotes)==1 ? $transport_provider_id : NULL;
|
|
$quoteGroup["quote_id"] = count($quotes)==1 ? $quotes[0] : NULL;
|
|
$quoteGroup["completed"] = NULL;
|
|
if (count($quotes)<1) {
|
|
$quoteGroup["completed"] = date("Y-m-d H:i:s");
|
|
$quoteGroup["processed"] = 0;
|
|
}
|
|
list($res,$err) = QuoteGroup::update($db->getConnect(), $quoteGroup);
|
|
if ($res==NULL || !isset($res["id"])) {
|
|
$res = $quoteGroup;
|
|
}
|
|
$res["quotes"] = $quotes;
|
|
return ["Group quote scheduled",200,"response",$res];
|
|
} catch (Exception $e) {
|
|
syslog(LOG_WARNING,json_encode($e));
|
|
$message = $e->getMessage();
|
|
syslog(LOG_WARNING,'EXCEPTION: '.$message);
|
|
}
|
|
return [$message,$code,$action,$data];
|
|
}
|
|
|
|
public function updateAction() {
|
|
return $this->response(
|
|
array(
|
|
"error" => "Update error"
|
|
), 400);
|
|
}
|
|
|
|
public function deleteAction() {
|
|
return $this->response(
|
|
array(
|
|
"error" => "Delete error"
|
|
), 500);
|
|
}
|
|
|
|
}
|