resq/src/Command/LoadWarrantySerialCommand.php
2022-09-15 09:51:07 +00:00

322 lines
10 KiB
PHP

<?php
namespace App\Command;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\WarrantySerial;
use App\Entity\WarrantySerialUploadLog;
use App\Entity\WarrantySerialLoadLog;
use App\Service\WarrantySerialUploadLogger;
use App\Service\WarrantySerialLoadLogger;
use PDO;
use DateTime;
class LoadWarrantySerialCommand extends Command
{
protected $em;
protected $upload_logger;
protected $load_logger;
protected $project_dir;
public function __construct(EntityManagerInterface $em, WarrantySerialUploadLogger $upload_logger,
WarrantySerialLoadLogger $load_logger, KernelInterface $kernel)
{
$this->em = $em;
$this->upload_logger = $upload_logger;
$this->load_logger = $load_logger;
$this->project_dir = $kernel->getProjectDir();
parent::__construct();
}
protected function configure()
{
$this->setName('warrantyserial:load')
->setDescription('Load warranty serials from file.')
->setHelp('Load warranty serials from file.');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$em = $this->em;
$status = 'pending';
// get the filenames from the queue table with status pending
$db = $em->getConnection();
$ws_query_sql = 'SELECT id, file_serial, api_user FROM warranty_serial_queue
WHERE status = :status';
$ws_query_stmt = $db->prepare($ws_query_sql);
$ws_query_stmt->bindValue('status', $status);
$ws_results = $ws_query_stmt->executeQuery();
$output_info = [];
while ($row = $ws_results->fetchAssociative())
{
$filename = $row['file_serial'];
$user_id = $row['api_user'];
$ws_file_id = $row['id'];
$fname = $this->project_dir . '/public/warranty_serial_uploads/' . $filename;
// TODO: need to consider what happens if file cannot be read.
// if file can be read, output_info will have an array of objects
// which can be used to transform into data
// if file cannot be read, output_info will have an array of strings
$output_info[] = $this->processWarrantySerialFile($fname, $user_id);
$this->updateWarrantySerialQueue($ws_file_id);
}
return 0;
}
protected function processWarrantySerialFile($csv_file, $user_id)
{
$output_info = [];
// attempt to open file
try
{
$fh = fopen($csv_file, "r");
}
catch (Exception $e)
{
$error = 'The file ' . $csv_file . 'could not be read.';
$log_data = [
'user_id' => $user_id,
'is_uploaded' => false,
'error' => $error,
];
$this->upload_logger->logWarrantySerialUploadInfo($log_data);
$output_info = $this->setOutputInfo('', 'not_uploaded', true, $error);
return $output_info;
}
while(($row = fgetcsv($fh)) !== false)
{
$validation_result = $this->validateRow($row, $user_id);
if (!empty($validation_result))
{
$output_info[] = $validation_result;
continue;
}
// valid entry, we parse and insert
$serial = trim(strtoupper($row[0]));
$sku = trim(strtoupper($row[1]));
$dispatch_status = trim($row[2]);
$str_date_create = trim($row[3]);
$inventory_status = trim($row[4]);
$cat_id = trim($row[5]);
$cat_name = trim(strtoupper($row[6]));
// we are sure that this is a valid date at this point
$created_date = $this->convertDateCreate($str_date_create);
$meta_info = [
'dispatch_status' => $dispatch_status,
'inventory_status' => $inventory_status,
'category_id' => $cat_id,
'category_name' => $cat_name,
];
$info = json_encode($meta_info);
// prepare the data
$source = 'motiv';
if ($sku == 'N/A')
$sku = null;
// prepared statement
$db = $this->em->getConnection();
$insert_stmt = $db->prepare('INSERT INTO warranty_serial (id, sku, date_create, source, meta_info)
VALUES (:serial, :sku, :date_create, :source, :meta_info)');
$res = $insert_stmt->execute([
':serial' => $serial,
':sku' => $sku,
':date_create' => $created_date,
':source' => $source,
':meta_info' => $info,
]);
if (!$res)
{
// log the not successful insert
$err = $insert_stmt->errorInfo();
$error = $err[2];
$this->logLoadInfo($user_id, false, $serial, $error);
$output_info = $this->setOutputInfo($serial, 'not uploaded', true, $error);
}
else
{
// log the successful insert
$this->logLoadInfo($user_id, true, $serial, '');
$output_info = $this->setOutputInfo($serial, 'uploaded', false, '');
}
}
return $output_info;
}
protected function validateRow($row, $user_id)
{
$data = [];
// possible lines:
// (1) header in csv file - ignore
// SerialNumber,Sku,DispatchStatus,CreatedDate,InventoryStatus,CategoryID,CategoryName
// (2) No available data - ignore
// (3) CH2000012071,WCHD23BL-CPN00-LX,0,2020-08-11 04:05:27.090,0,4,CHAMPION MF - valid
// (4) MG2000313690,N/A,1,2021-05-14T23:47:30.6430000+08:00,0,10,GOLD - valid
// (5) Empty line - ignore
// (6) empty sku - log
// check if the line is a header
if ($row[0] == 'SerialNumber')
{
// no need to log, just ignore and skip
return false;
}
// check if empty line
if ($row == array(null))
{
// no need to log, just ignore and skip
return false;
}
// check if empty serial
if (empty($row[0]))
{
// this one, we log
$error = 'Empty serial';
$this->logLoadInfo($user_id, false, '', $error);
$data = $this->setOutputInfo('', 'not_uploaded', true, $error);
return $data;
}
// validate the date created
$serial = trim($row[0]);
$str_date_create = trim($row[3]);
$date_create = $this->convertDateCreate($str_date_create);
if ($date_create == null)
{
// log
$error = 'Invalid date create';
$this->logLoadInfo($user_id, false, $serial, $error);
$data = $this->setOutputInfo($serial, 'not_uploaded', true, $error);
return $data;
}
// check if serial is a dupe
$existing_serial = $this->em->getRepository(WarrantySerial::class)->find($serial);
if ($existing_serial != null)
{
// log
$error = 'Duplicate serial';
$this->logLoadInfo($user_id, false, $serial, $error);
$data = $this->setOutputInfo($serial, 'not_uploaded', true, $error)
return $data;
}
// valid entry, return empty
return $data;
}
protected function convertDateCreate($str_date_create)
{
// since some people cannot follow simple instructions...
// check the date format on the string
// try 2021-05-15T08:35:46+08:00 format on str_date_create
$date_create = DateTime::createFromFormat('Y-m-d\TH:i:sP', $str_date_create);
if ($date_create == false)
{
// try this format: 2021-05-15T08:47:20.3330000+08:00
// get the date, time and timezone from str_date_create
$str_date_time = substr($str_date_create, 0, 19);
$str_timezone = substr($str_date_create, 27);
$str_datetime_tz = $str_date_time . $str_timezone;
// create DateTime object
// sample: 2021-05-15T12:16:06+08:00
$date_create = DateTime::createFromFormat('Y-m-d\TH:i:sP', $str_datetime_tz);
// check if datetime object was created
// if not, someone f*cked up and we have no date create
if ($date_create == false)
{
return null;
}
}
// if you reach this part, then date string is valid. Since we'll be using
// sql to insert the entries, we return the string
$created_date = $date_create->format('Y-m-d H:i:s');
// $created_date = DateTime::createFromFormat('Y-m-d H:i:s', $str_created_date);
return $created_date;
}
protected function logLoadInfo($user_id, $is_loaded, $serial, $error)
{
$log_data = [
'user_id' => $user_id,
'is_loaded' => $is_loaded,
'serial' => $serial,
'error' => $error,
];
$this->load_logger->logWarrantySerialLoadInfo($log_data);
}
protected function updateWarrantySerialQueue($id)
{
// prepared statement
$db = $this->em->getConnection();
$update_stmt = $db->prepare('UPDATE warranty_serial_queue SET status = :status
WHERE id = :id');
$res = $update_stmt->execute([
':status' => 'done',
':id' => $id,
]);
}
protected function setOutputInfo($serial, $status, $has_error, $error_message)
{
$info = [
'serial' => $serial,
'status' => $status,
'has_error' => $has_error,
'error_message' => $error_message
];
return $info;
}
}