From f35d6647c51eac9ff9401cea2e6ba3e5a856793b Mon Sep 17 00:00:00 2001 From: mahdahar <89adham@gmail.com> Date: Wed, 11 Mar 2026 08:52:06 +0700 Subject: [PATCH] feat: add batch PDF generator CLI script Add script.php for batch generating PDFs from a list of accessnumbers. Features: - Reads accessnumbers from text file (one per line) - Validates requests exist and are validated (ISVAL2=1) - Uses REPORT_LANG to determine language - Sends to PDF spooler and logs to AUDIT_REQUESTS - Creates ORU files for HL7 integration - Outputs progress and saves detailed log Usage: php script.php accessnumbers.txt --- script.php | 283 +++++++++++++++++++++++++++++++++++++++++++++++++ test_batch.txt | 2 + 2 files changed, 285 insertions(+) create mode 100644 script.php create mode 100644 test_batch.txt diff --git a/script.php b/script.php new file mode 100644 index 0000000..dce2ca1 --- /dev/null +++ b/script.php @@ -0,0 +1,283 @@ +\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... "; + + 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 + $html = view('report/template', $data); + $filename = $accessnumber . ($eng == 1 ? '_eng' : '') . '.pdf'; + $collectionDate = $data['collectionDate'] ?? ''; + $hostnumber = $data['hostnumber'] ?? ''; + + // Send to PDF spooler + $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, 10); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + 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']; +} diff --git a/test_batch.txt b/test_batch.txt new file mode 100644 index 0000000..00494aa --- /dev/null +++ b/test_batch.txt @@ -0,0 +1,2 @@ +202403110001 +202403110002