\n"; echo "Example: php script.php batch.txt\n\n"; echo "File format: One accessnumber per line\n"; exit(1); } $inputFile = $argv[1]; // Convert to absolute path if relative if (!str_starts_with($inputFile, '/') && !preg_match('/^[A-Za-z]:/', $inputFile)) { $inputFile = getcwd() . DIRECTORY_SEPARATOR . $inputFile; } if (!file_exists($inputFile)) { echo "Error: File not found: $inputFile\n"; exit(1); } // We want errors to be shown when using it from the CLI. error_reporting(E_ALL); ini_set('display_errors', '1'); // Define essential paths define('FCPATH', __DIR__ . DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR); define('ROOTPATH', realpath(__DIR__) . DIRECTORY_SEPARATOR); define('WRITEPATH', ROOTPATH . 'writable' . DIRECTORY_SEPARATOR); define('SYSTEMPATH', realpath(__DIR__ . '/vendor/codeigniter4/framework/system') . DIRECTORY_SEPARATOR); define('APPPATH', realpath(__DIR__ . '/app') . DIRECTORY_SEPARATOR); // Store original directory $originalDir = getcwd(); // Change to public directory for CI4 boot chdir(FCPATH); // Load composer autoloader require_once ROOTPATH . 'vendor/autoload.php'; // Load .env file first to get environment settings $dotenv = new \CodeIgniter\Config\DotEnv(ROOTPATH); $dotenv->load(); // Define ENVIRONMENT constant define('ENVIRONMENT', $_ENV['CI_ENVIRONMENT'] ?? 'production'); // Load CI4's Boot class require_once SYSTEMPATH . 'Boot.php'; // Create paths configuration $paths = new \Config\Paths(); // Boot the console (non-spark CLI mode) \CodeIgniter\Boot::bootConsole($paths); // Change back to original directory chdir($originalDir); // Get database connection $db = \Config\Database::connect(); // Read accessnumbers from file $lines = file($inputFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); if ($lines === false) { echo "Error: Could not read file: $inputFile\n"; exit(1); } $accessnumbers = array_filter(array_map('trim', $lines)); $total = count($accessnumbers); if ($total === 0) { echo "Error: No accessnumbers found in file\n"; exit(1); } echo "Processing $inputFile\n"; echo "Found $total access number(s)\n"; echo str_repeat("-", 60) . "\n"; // Statistics $stats = [ 'success' => 0, 'skipped' => 0, 'failed' => 0, 'details' => [] ]; // Process each accessnumber foreach ($accessnumbers as $index => $accessnumber) { $current = $index + 1; echo "[$current/$total] $accessnumber... "; flush(); try { // Check if request exists and get language $sql = "SELECT ACCESSNUMBER, ISVAL2, REPORT_LANG FROM GDC_CMOD.dbo.CM_REQUESTS WHERE ACCESSNUMBER = ?"; $row = $db->query($sql, [$accessnumber])->getRowArray(); if (!$row) { echo "SKIPPED (not found in CM_REQUESTS)\n"; $stats['skipped']++; $stats['details'][] = [ 'accessnumber' => $accessnumber, 'status' => 'skipped', 'message' => 'not found in CM_REQUESTS' ]; continue; } if ($row['ISVAL2'] != 1) { echo "SKIPPED (not validated - ISVAL2=0)\n"; $stats['skipped']++; $stats['details'][] = [ 'accessnumber' => $accessnumber, 'status' => 'skipped', 'message' => 'not validated (ISVAL2=0)' ]; continue; } $eng = (int) ($row['REPORT_LANG'] ?? 0); // Get report data $reportHelper = new \App\Libraries\ReportHelper($db); $data = $reportHelper->getReportData($accessnumber, $eng); $data['eng'] = $eng; $data['accessnumber'] = $accessnumber; $data['ispdf'] = 1; // Generate HTML echo "generating HTML... "; flush(); $html = view('report/template', $data); $filename = $accessnumber . ($eng == 1 ? '_eng' : '') . '.pdf'; $collectionDate = $data['collectionDate'] ?? ''; $hostnumber = $data['hostnumber'] ?? ''; // Send to PDF spooler echo "sending to spooler... "; flush(); $jobId = postToSpooler($html, $filename, $collectionDate, $accessnumber, $hostnumber); // Log to AUDIT_REQUESTS $sqlCheck = "SELECT COUNT(*) as cnt FROM GDC_CMOD.dbo.AUDIT_REQUESTS WHERE ACCESSNUMBER = ? AND STEPTYPE IN ('GEN_PDF', 'REGEN_PDF')"; $result = $db->query($sqlCheck, [$accessnumber])->getRowArray(); $stepType = ($result['cnt'] > 0) ? 'REGEN_PDF' : 'GEN_PDF'; $stepStatus = $eng == 1 ? 'English' : 'Indonesian'; $sqlLog = "INSERT INTO GDC_CMOD.dbo.AUDIT_REQUESTS(ACCESSNUMBER, STEPDATE, STEPTYPE, STEPSTATUS) VALUES (?, GETDATE(), ?, ?)"; $db->query($sqlLog, [$accessnumber, $stepType, $stepStatus]); // Create ORU file try { $oruDir = 'c:\inetpub\wwwroot\spooler_db\process_oru'; if (!is_dir($oruDir)) { mkdir($oruDir, 0777, true); } $oruFile = "$oruDir/$accessnumber.oru"; $date = date('Y-m-d H:i'); $status = $data['status'] ?? 'PENDING'; $file = fopen($oruFile, 'w+'); fwrite($file, "$accessnumber\r\n$hostnumber\r\n$date\r\n$status\r\n-"); fclose($file); $sqlOruLog = "INSERT INTO GDC_CMOD.dbo.AUDIT_REQUESTS(ACCESSNUMBER, STEPDATE, STEPTYPE, STEPSTATUS) VALUES (?, GETDATE(), 'ORU_FILE', 'Created')"; $db->query($sqlOruLog, [$accessnumber]); } catch (\Throwable $e) { // Log but don't fail if ORU creation fails error_log("ORU file creation failed for $accessnumber: " . $e->getMessage()); } echo "QUEUED (job: $jobId, lang: $stepStatus)\n"; $stats['success']++; $stats['details'][] = [ 'accessnumber' => $accessnumber, 'status' => 'success', 'jobId' => $jobId, 'language' => $stepStatus ]; } catch (\Exception $e) { echo "FAILED (" . $e->getMessage() . ")\n"; $stats['failed']++; $stats['details'][] = [ 'accessnumber' => $accessnumber, 'status' => 'failed', 'error' => $e->getMessage() ]; } } echo str_repeat("-", 60) . "\n"; echo "Complete: " . $stats['success'] . " queued, " . $stats['skipped'] . " skipped, " . $stats['failed'] . " failed\n"; // Save detailed log $logFile = 'batch_pdf_' . date('Ymd_His') . '.log'; $logContent = "Batch PDF Generation Log\n"; $logContent .= "Generated: " . date('Y-m-d H:i:s') . "\n"; $logContent .= "Input file: $inputFile\n"; $logContent .= "Total: $total, Success: {$stats['success']}, Skipped: {$stats['skipped']}, Failed: {$stats['failed']}\n"; $logContent .= str_repeat("-", 60) . "\n"; foreach ($stats['details'] as $detail) { $logContent .= $detail['accessnumber'] . " | " . $detail['status']; if (isset($detail['jobId'])) { $logContent .= " | job: " . $detail['jobId']; } if (isset($detail['error'])) { $logContent .= " | error: " . $detail['error']; } $logContent .= "\n"; } file_put_contents($logFile, $logContent); echo "Log saved to: $logFile\n"; exit($stats['failed'] > 0 ? 1 : 0); /** * Post HTML to PDF spooler */ function postToSpooler(string $html, string $filename, string $collectionDate = '', string $accessnumber = '', string $hostnumber = ''): string { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://glenlis:3000/api/pdf/generate'); curl_setopt($ch, CURLOPT_POST, 1); $payload = [ 'html' => $html, 'filename' => $filename ]; if ($collectionDate) { $payload['collectionDate'] = $collectionDate; } if ($accessnumber) { $payload['accessnumber'] = $accessnumber; } if ($hostnumber) { $payload['hostnumber'] = $hostnumber; } curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json' ]); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); if ($curlError) { throw new \Exception("cURL error: $curlError"); } if ($httpCode !== 200) { throw new \Exception("Spooler API returned HTTP $httpCode"); } $data = json_decode($response, true); if (!isset($data['jobId'])) { throw new \Exception("Invalid response from spooler: no jobId"); } return $data['jobId']; }