Import Excel into mySQL taking too long - php

So, I have a form that allows users to upload an excel sheet and then the system will import that excel data into mySQL.
However, every time I submit the form using AJAX, it starts the process, saves about half of the data, and then gives me a 504 gateway error. I have already changed the PHP config timeout to 300 but it still gives in half way through. I do not think that an excel sheet with a little under 1000 rows should be taking 5+ minutes?
Here is my code:
public function postImportGroup(Request $request)
{
if($request->hasFile('import_numbers')) {
$file = $request->file('import_numbers');
$file_extension = Input::file('import_numbers')->getClientOriginalExtension();
$supportedExt = array('csv', 'xls', 'xlsx');
if (!in_array_r($file_extension, $supportedExt)) {
return response()->json([
'status' => 'error',
'msg' => 'Please make sure that the uploaded file is a valid CSV, XLS, XLSX sheet.',
]);
}
}
$results = Excel::load($file)->get();
$results = json_decode($results[0], true);
$class = new DailyGroup();
$class->title = $request->group_name;
$class->user_id = Auth::guard('client')->user()->id;
$class->entries = count($results);
$class->save();
foreach ($results as $r => $value) {
//$data = array_values($value);
//return $value["employee_number"];
$group = new DailyGroupLocations();
$address = $value["address"] . ',' . $value["city"] . ',' . $value["state"] . ',' . $value["zip"];
$c = $value["country"];
$file_contents = file_get_contents('https://maps.googleapis.com/maps/api/geocode/json?address=' . urlencode($address) . '&components=country:' . urlencode($c) .'&sensor=false&key=xxx');
$json_decode = json_decode($file_contents);
if (isset($json_decode->results[0])) {
$group->lat = $json_decode->results[0]->geometry->location->lat;
$group->lng = $json_decode->results[0]->geometry->location->lng;
}
$phone = preg_replace('/\D+/', '', $value["ph"]);
$phone = '1'.$phone;
$group->user_id = Auth::guard('client')->user()->id;
$group->group_id = $class->id;
$group->employee_number = $value["employee_number"];
$group->work_date = $value["work_date"]["date"];
$group->first_name = $value["name"];
$group->last_name = $value["lastname"];
$group->phone = $phone;
$group->email = $value["email"];
$group->job_number = $value["job_number"];
$group->address = $value["address"];
$group->city = $value["city"];
$group->state = $value["state"];
$group->zip = $value["zip"];
$group->country = $value["country"];
$group->job_name = $value["job_name"];
$group->location = $value["location"];
$group->shift_description = $value["shift_description"];
$group->shift_start = $value["shift_start_time"];
$group->shift_end = $value["shift_end_time"];
$group->post_hours = $value["post_hours"];
$group->save();
}
return response()->json([
'status' => 'success',
'msg' => 'All data uploaded successfully. Please wait for tables to refresh.',
//'url' => '/user/location/location-areas'
]);
}
Is there anything I can do to optimize this? Am I running to many things in the foreach statement? Any tips or tricks I can use?

its Not the excel file taking its time... its the google geocode call which is blocking your code from executing.
you can get yourself an google api key to speed up your process.
reference: https://developers.google.com/maps/documentation/javascript/get-api-key
you should also Check for the response status of your geocode calls
$status = json_decode->results[0]->status;
possible status values:
OK
ZERO_RESULTS
OVER_QUERY_LIMIT
INVALID_REQUEST
UNKNOWN_ERROR
if its possible in your case you could consider pre-geocoding your dataset, store the lat and lng values with the address so you do Not have to geocode on the fly if you expect or need a fast execution.

Related

PHP wordpress wp_safe_remote_post write server response to text file

In Wordpress (PHP) i'm using Contact Form 7 (CF7) as the UI and sending the data via an API with the function on_submit below. Would like to have the success or fail response data logged to make sure everything is working so thought i'd write that to a simple text file using the function custom_logs. It does write to the text file but then the process never continues on to the CF7 plugin to indicate the POST was a success or fail, it just hangs after hitting submit on the UI side. I believe something about writing to the file is interrupting the flow and then is just stops from CF7's POV. I rarely write anything in PHP so not 100% sure what the issue is? Anyone have an idea how to solve it, I can't find anything. Thanks
function on_submit( $form, &$abort, $submission )
{
$data = $submission->get_posted_data();
$firstname = sanitize_text_field($data['first-name']);
$response = wp_safe_remote_post("https://www.api.com", [
'body' => json_encode([
'firstname' => $firstname,
]),
]);
if ( is_wp_error($response) ) {
$abort = TRUE;
$body = wp_remote_retrieve_body($response);
$result = json_decode($body);
$submission->set_response($result->error);
$submission->set_status('api_failed');
} else {
$abort = FALSE;
$body_success = wp_remote_retrieve_body($response);
$result_success = json_decode($body_success);
custom_logs("WP API: " . $result_success);
}
}
add_action('wpcf7_before_send_mail', 'on_submit', 10, 3);
function custom_logs($message) {
if(is_array($message)) {
$message = json_encode($message);
}
$file = fopen("custom_logs.log","a");
echo fwrite($file, "\n" . date('Y-m-d h:i:s') . " :: " . $message);
fclose($file);
}

PHP-EWS fails on more than one attachment

I use James Armes's PHP-EWS library.
The following code works fine with single attachments, but fails with multiply files.
<?php
$msgRequest->MessageDisposition = 'SaveOnly';
$msgResponse = $ews->CreateItem($msgRequest);
$msgResponseItems = $msgResponse->ResponseMessages->CreateItemResponseMessage->Items;
// Create attachment(s)
$attachments = array();
$i = 0;
foreach ($message_details['attachment'] as $attachment) {
$attachments[$i] = new EWSType_FileAttachmentType();
$attachments[$i]->Content = file_get_contents($attachment['path'] . '/' . $attachment['file']);
$attachments[$i]->Name = $attachment['file'];
$i++;
}
//
// Attach files to message
$attRequest = new EWSType_CreateAttachmentType();
$attRequest->ParentItemId = $msgResponseItems->Message->ItemId;
$attRequest->Attachments = new EWSType_NonEmptyArrayOfAttachmentsType();
$attRequest->Attachments->FileAttachment = $attachments;
$attResponse = $ews->CreateAttachment($attRequest);
$attResponseId = $attResponse->ResponseMessages->CreateAttachmentResponseMessage->Attachments->FileAttachment->AttachmentId;
// Save message id from create attachment response
$msgItemId = new EWSType_ItemIdType();
$msgItemId->ChangeKey = $attResponseId->RootItemChangeKey;
$msgItemId->Id = $attResponseId->RootItemId;
// Send and save message
$msgSendRequest = new EWSType_SendItemType();
$msgSendRequest->ItemIds = new EWSType_NonEmptyArrayOfBaseItemIdsType();
$msgSendRequest->ItemIds->ItemId = $msgItemId;
$msgSendRequest->SaveItemToFolder = true;
$msgSendResponse = $ews->SendItem($msgSendRequest);
$response = $msgSendResponse->ResponseMessages->SendItemResponseMessage;
?>
$ews->SendItem() returns this error:
Uncaught SoapFault exception: [a:ErrorSchemaValidation] The request
failed schema validation: The required attribute 'Id' is missing.
What do I miss here?
Found the answer here:
https://github.com/jamesiarmes/php-ews/issues/132
Basically Exchange does not use an array if there is only one attachment, so an additional check is required to determine where to get the ID from.
if(!is_array($attResponse->ResponseMessages->CreateAttachmentResponseMessage))
$attResponseId = $attResponse->ResponseMessages->CreateAttachmentResponseMessage->Attachments->FileAttachment->AttachmentId;
else {
$attResponseId = $attResponse->ResponseMessages->CreateAttachmentResponseMessage[0]->Attachments->FileAttachment->AttachmentId;
}
Exchange uses the same structure with recipients. I find this inconsistent, however I am sure there is a reason behind it.
I hope someone will benefit from raising this.

Is this conversion script efficient?

I wrote this piece of script to get the conversion rates from Yahoo API . What it does is I have a config file which has currency details I have to make an array of rates of all the currency present in the config here is my script .
Please review it and let me know if there is a better way to do this .
$cur = $this->parent->parent->config->conversionCurrencies;
//$cur=array(11=>'USD/CRC');
foreach($cur as $key=>$val){
$get_cur = str_replace("/","",$val);echo "<br>";
$code=file_get_contents('http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20yahoo.finance.xchange%20where%20pair%20in%20(%22'.$get_cur.'%22)&format=json&env=store://datatables.org/alltableswithkeys&callback=');
var_dump($array1 = json_decode($code, true));
$rates_array = array();
$myaskRate = $array1['query']['results']['rate']['Ask'];
$mybidRate = $array1['query']['results']['rate']['Bid'];
$rates_array[$key] = round(($myaskRate+$mybidRate)/2,2);
}
var_dump( $rates_array);

Facebook PHP SDK "Too many requests in batch message. Maximum batch size is 50"

I'm trying to get the profiles of a large group of friends and I'm getting the error:
Too many requests in batch message. Maximum batch size is 50
From the API. Now I understand the error message but I thought I built the function to mitigate this error. I specifically make the calls in chunks of 50. I don't change $chunk_size in any of the methods that call it so I don't really know what is going on here.
This is the function that is spitting out the error:
protected function getFacebookProfiles($ids, array $fields = array('name', 'picture'), $chunk_size = 50)
{
$facebook = App::make('Facebook');
$fields = implode(',', $fields);
$requests = array();
foreach ($ids as $id) {
$requests[] = array('method' => 'GET', 'relative_url' => "{$id}?fields={$fields}");
}
$responses = array();
$chunks = array_chunk($requests, $chunk_size);
foreach ($chunks as $chunk) {
$batch = json_encode($requests);
$response = $facebook->api("?batch={$batch}", 'POST');
foreach ($response as &$profile) {
$profile = json_decode($profile['body']);
if (empty($profile->picture->data)) {
// something has gone REALLY wrong, this should never happen but if it does we'll have more debug information
if (empty($profile->error->message)) {
throw new Exception('Unexpected error when retrieving user information for IDs:' . implode(', ', $ids));
}
$profile->error = (array) $profile->error;
throw new FacebookApiException((array) $profile);
}
$profile->picture = $profile->picture->data;
}
$responses = array_merge($responses, $response);
}
return $responses;
}
You are not using your $chunk variable in the JSON you generate for your API call, but still the original, unmodified $requests.
Happy slamming :-)

'Node no longer exists' error in PHP

I'm using the following code to turn user's IP into latitude/longitude information using the hostip web service:
//get user's location
$ip=$_SERVER['REMOTE_ADDR'];
function get_location($ip) {
$content = file_get_contents('http://api.hostip.info/?ip='.$ip);
if ($content != FALSE) {
$xml = new SimpleXmlElement($content);
$coordinates = $xml->children('gml', TRUE)->featureMember->children('', TRUE)->Hostip->ipLocation->children('gml', TRUE)->pointProperty->Point->coordinates;
$longlat = explode(',', $coordinates);
$location['longitude'] = $longlat[0];
$location['latitude'] = $longlat[1];
$location['citystate'] = '==>'.$xml->children('gml', TRUE)->featureMember->children('', TRUE)->Hostip->children('gml', TRUE)->name;
$location['country'] = '==>'.$xml->children('gml', TRUE)->featureMember->children('', TRUE)->Hostip->countryName;
return $location;
}
else return false;
}
$data = get_location($ip);
$center_long=$data['latitude'];
$center_lat=$data['longitude'];
This works fine for me, using $center_long and $center_lat the google map on the page is centered around my city, but I have a friend in Thailand who tested it from there, and he got this error:
Warning: get_location() [function.get-location]: Node no longer exists in /home/bedbugs/registry/index.php on line 21
So I'm completely confused by this, how could he be getting an error if I don't? I tried googling it and it has something to do with parsing XML data, but the parsing process is the same for me and him. Note that line 21 is the one that starts with '$coordinates =' .
You need to check the service actually has an <ipLocation> listed, you're doing:
$xml->children('gml', TRUE)->featureMember->children('', TRUE)->Hostip->ipLocation
->children('gml', TRUE)->pointProperty->Point->coordinates
but the XML output for my IP is:
<HostipLookupResultSet version="1.0.1" xsi:noNamespaceSchemaLocation="http://www.hostip.info/api/hostip-1.0.1.xsd">
<gml:description>This is the Hostip Lookup Service</gml:description>
<gml:name>hostip</gml:name>
<gml:boundedBy>
<gml:Null>inapplicable</gml:Null>
</gml:boundedBy>
<gml:featureMember>
<Hostip>
<ip>...</ip>
<gml:name>(Unknown City?)</gml:name>
<countryName>(Unknown Country?)</countryName>
<countryAbbrev>XX</countryAbbrev>
<!-- Co-ordinates are unavailable -->
</Hostip>
</gml:featureMember>
</HostipLookupResultSet>
The last part ->children('gml', TRUE)->pointProperty->Point->coordinates gives the error because it has no children (for some IPs).
You can add a basic check to see if the <ipLocation> node exists like this (assuming the service always returns at least up to the <hostIp> node):
function get_location($ip) {
$content = file_get_contents('http://api.hostip.info/?ip='.$ip);
if ($content === FALSE) return false;
$location = array('latitude' => 'unknown', 'longitude' => 'unknown');
$xml = new SimpleXmlElement($content);
$hostIpNode = $xml->children('gml', TRUE)->featureMember->children('', TRUE)->Hostip;
if ($hostIpNode->ipLocation) {
$coordinates = $hostIpNode->ipLocation->children('gml', TRUE)->pointProperty->Point->coordinates;
$longlat = explode(',', $coordinates);
$location['longitude'] = $longlat[0];
$location['latitude'] = $longlat[1];
}
$location['citystate'] = '==>'.$hostIpNode->children('gml', TRUE)->name;
$location['country'] = '==>'.$hostIpNode->countryName;
return $location;
}

Categories