gdc_cmod/script.php
mahdahar 0b569c58d9 fix: add progress output and better timeout handling for batch PDF script
- Add flush() after each echo to show progress in real-time
- Increase curl timeout to 30 seconds with 10 second connect timeout
- Add curl error handling to diagnose connection issues
- Add progress messages (generating HTML, sending to spooler)
2026-03-11 08:54:49 +07:00

295 lines
9.1 KiB
PHP

<?php
/**
* Batch PDF Generator CLI Script
*
* Usage: php script.php accessnumbers.txt
*
* Input file format: One accessnumber per line
* Example:
* 202403110001
* 202403110002
* 202403110003
*/
// Check command line arguments
if ($argc < 2) {
echo "Usage: php script.php <accessnumbers_file>\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'];
}