Files
dev-chiefworks 47f4fad75c Added Other AP
2022-04-26 11:30:34 -04:00

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);
}
}