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