From 2843ddd39218e54308f24c433a8034de313ccf20 Mon Sep 17 00:00:00 2001 From: mahdahar <89adham@gmail.com> Date: Tue, 3 Feb 2026 11:33:55 +0700 Subject: [PATCH] Migrate PDF generation from legacy spooler_db to CI4 + node_spooler BREAKING CHANGE: Remove public/spooler_db/ legacy system Changes: - Migrate validation preview from http://glenlis/spooler_db/main_dev.php to CI4 /report/{accessnumber} - Add ReportController::preview() for HTML preview in validation dialog - Add ReportController::generatePdf() to queue PDF generation via node_spooler at http://glenlis:3030 - Add ReportController::checkPdfStatus() to poll spooler job status - Add ReportController::postToSpooler() helper for curl requests to spooler API - Add routes: GET /report/(:num)/preview, GET /report/(:num)/pdf, GET /report/status/(:any) - Delete public/spooler_db/ directory (40+ legacy files) - Compact node_spooler/README.md from 577 to 342 lines Technical Details: - New architecture: CI4 Controller -> node_spooler (port 3030) -> Chrome CDP (port 42020) - API endpoints: POST /api/pdf/generate, GET /api/pdf/status/:jobId, GET /api/queue/stats - Features: Max 5 concurrent jobs, max 100 in queue, auto-cleanup after 60 min - Error handling: Chrome crash detection, manual error review in data/error/ - PDF infrastructure ready, frontend PDF buttons to be updated later in production Migration verified: - No external code references spooler_db - All assets duplicated in public/assets/report/ - Syntax checks passed for ReportController.php and Routes.php Refs: node_spooler/README.md --- .gitignore | 9 +- TODO.md | 7 +- app/Config/Routes.php | 4 + app/Controllers/ReportController.php | 89 ++ app/Views/shared/content_requests.php | 23 +- app/Views/shared/script_validation.php | 3 +- node_spooler/README.md | 428 +++++++++ node_spooler/admin.html | 339 +++++++ node_spooler/cleanup-config.json | 14 + node_spooler/cleanup.js | 208 ++++ node_spooler/package-lock.json | 869 +++++++++++++++++ node_spooler/package.json | 16 + node_spooler/spooler.js | 330 +++++++ public/spooler_db/_function.php | 705 -------------- public/spooler_db/_function_dev.php | 691 ------------- public/spooler_db/_function_zaka.php | 905 ------------------ public/spooler_db/_inc.php | 23 - public/spooler_db/assets/gleneaglesftr.png | Bin 57198 -> 0 bytes public/spooler_db/assets/gleneagleshdr.png | Bin 227868 -> 0 bytes public/spooler_db/assets/normalize.min.css | 2 - .../spooler_db/assets/normalize.min.css.map | 1 - public/spooler_db/assets/pdf.css | 34 - public/spooler_db/assets/pdf_qr.css | 33 - public/spooler_db/assets/style.css | 29 - public/spooler_db/assets/style_qr.css | 29 - public/spooler_db/config.php | 10 - public/spooler_db/config_glenlis.php | 10 - public/spooler_db/covidPCRnAG.php | 151 --- public/spooler_db/debug.php | 103 -- public/spooler_db/function.php.bak | 241 ----- public/spooler_db/gener_oru.php | 21 - public/spooler_db/gleneaglesftr.png | Bin 57198 -> 0 bytes public/spooler_db/gleneagleshdr.png | Bin 227868 -> 0 bytes public/spooler_db/main.php | 124 --- public/spooler_db/main2 -backup.php | 214 ----- public/spooler_db/main2.php | 229 ----- public/spooler_db/main2_debug.php | 214 ----- public/spooler_db/main2_zaka.php | 214 ----- public/spooler_db/main_dev.php | 208 ---- public/spooler_db/main_qr.php | 217 ----- public/spooler_db/normalize.min.css | 2 - public/spooler_db/normalize.min.css.map | 1 - public/spooler_db/oru.php | 96 -- public/spooler_db/package-lock.json | 376 -------- public/spooler_db/pdf.css | 34 - public/spooler_db/pdf.php | 116 --- public/spooler_db/pdf_qr.css | 33 - public/spooler_db/pdf_start.bat | 1 - public/spooler_db/preview.php | 137 --- public/spooler_db/spooler_db.7z | Bin 299753 -> 0 bytes public/spooler_db/spooler_pdf.js | 38 - public/spooler_db/style.css | 29 - public/spooler_db/style_qr.css | 29 - public/spooler_db/test.js | 29 - public/spooler_db/test.php | 8 - public/spooler_db/val2.php | 205 ---- public/spooler_db/val2_qr.php | 193 ---- 57 files changed, 2325 insertions(+), 5749 deletions(-) create mode 100644 node_spooler/README.md create mode 100644 node_spooler/admin.html create mode 100644 node_spooler/cleanup-config.json create mode 100644 node_spooler/cleanup.js create mode 100644 node_spooler/package-lock.json create mode 100644 node_spooler/package.json create mode 100644 node_spooler/spooler.js delete mode 100644 public/spooler_db/_function.php delete mode 100644 public/spooler_db/_function_dev.php delete mode 100644 public/spooler_db/_function_zaka.php delete mode 100644 public/spooler_db/_inc.php delete mode 100644 public/spooler_db/assets/gleneaglesftr.png delete mode 100644 public/spooler_db/assets/gleneagleshdr.png delete mode 100644 public/spooler_db/assets/normalize.min.css delete mode 100644 public/spooler_db/assets/normalize.min.css.map delete mode 100644 public/spooler_db/assets/pdf.css delete mode 100644 public/spooler_db/assets/pdf_qr.css delete mode 100644 public/spooler_db/assets/style.css delete mode 100644 public/spooler_db/assets/style_qr.css delete mode 100644 public/spooler_db/config.php delete mode 100644 public/spooler_db/config_glenlis.php delete mode 100644 public/spooler_db/covidPCRnAG.php delete mode 100644 public/spooler_db/debug.php delete mode 100644 public/spooler_db/function.php.bak delete mode 100644 public/spooler_db/gener_oru.php delete mode 100644 public/spooler_db/gleneaglesftr.png delete mode 100644 public/spooler_db/gleneagleshdr.png delete mode 100644 public/spooler_db/main.php delete mode 100644 public/spooler_db/main2 -backup.php delete mode 100644 public/spooler_db/main2.php delete mode 100644 public/spooler_db/main2_debug.php delete mode 100644 public/spooler_db/main2_zaka.php delete mode 100644 public/spooler_db/main_dev.php delete mode 100644 public/spooler_db/main_qr.php delete mode 100644 public/spooler_db/normalize.min.css delete mode 100644 public/spooler_db/normalize.min.css.map delete mode 100644 public/spooler_db/oru.php delete mode 100644 public/spooler_db/package-lock.json delete mode 100644 public/spooler_db/pdf.css delete mode 100644 public/spooler_db/pdf.php delete mode 100644 public/spooler_db/pdf_qr.css delete mode 100644 public/spooler_db/pdf_start.bat delete mode 100644 public/spooler_db/preview.php delete mode 100644 public/spooler_db/spooler_db.7z delete mode 100644 public/spooler_db/spooler_pdf.js delete mode 100644 public/spooler_db/style.css delete mode 100644 public/spooler_db/style_qr.css delete mode 100644 public/spooler_db/test.js delete mode 100644 public/spooler_db/test.php delete mode 100644 public/spooler_db/val2.php delete mode 100644 public/spooler_db/val2_qr.php diff --git a/.gitignore b/.gitignore index bcdad06..cdde22a 100644 --- a/.gitignore +++ b/.gitignore @@ -126,4 +126,11 @@ _modules/* /phpunit*.xml .claude/ -.serena/ \ No newline at end of file +.serena/ + +#------------------------- +# PDF Spooler Data +#------------------------- +node_spooler/data/ +node_spooler/node_modules/ +node_spooler/logs/ \ No newline at end of file diff --git a/TODO.md b/TODO.md index 0eddb22..2983151 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,8 @@ # Project Checklist: Glen RME & Lab Management System -**Last Updated:** 20260202 +**Last Updated:** 20260203 Pending: -- Restrict Print/Save-to-PDF to CS Role only (Lab can only preview, CS can print/save) -- Add Dedicated Print Button (Trigger browser/system print dialog) - Reprint Label (Add functionality to reprint labels) - Print Result Audit (Track when result reports are printed/exported, log user and timestamp) @@ -29,7 +27,8 @@ Completed: - 18 : Create Validate Page - 19 : Sync color with old gdc_cmod - 20 : Add Val1 Val2 on the result - +- 21 : Show Print / PDF button when val1 val2 done +- 22 : Restrict Print/Save-to-PDF to CS Role only (Admin, Lab, CS can print/save) Addition on dev : - adding init-isDev on index.php to set default date on dev dashboard \ No newline at end of file diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 3195bf9..948eb50 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -98,7 +98,11 @@ $routes->group('report', ['filter' => 'role:0,1,2,4'], function ($routes) { $routes->get('(:num)/eng', 'ReportController::generate/$1/1'); $routes->get('(:num)/print', 'ReportController::print/$1'); $routes->get('(:num)/print/eng', 'ReportController::print/$1/1'); + $routes->get('(:num)/preview', 'ReportController::preview/$1'); + $routes->get('(:num)/pdf', 'ReportController::generatePdf/$1'); }); +$routes->get('report/status/(:any)', 'ReportController::checkPdfStatus/$1'); + // Keep backward compatibility - updated filter $routes->get('print/(:num)', 'ReportController::generate/$1', ['filter' => 'role:0,1,2,3,4']); diff --git a/app/Controllers/ReportController.php b/app/Controllers/ReportController.php index c84e3fd..f858b8f 100644 --- a/app/Controllers/ReportController.php +++ b/app/Controllers/ReportController.php @@ -58,4 +58,93 @@ class ReportController extends BaseController VALUES(?, GETDATE(), 'PRINT', ?)"; $this->db->query($sql, [$accessnumber, $status]); } + + public function preview($accessnumber) + { + $eng = $this->request->getVar('eng') ?? 0; + return $this->renderReport($accessnumber, $eng, 0, false); + } + + public function generatePdf($accessnumber) + { + $userroleid = session()->get('userroleid'); + if (!in_array($userroleid, [0, 1, 2, 4])) { + return $this->response->setStatusCode(403)->setJSON(['success' => false, 'error' => 'Unauthorized']); + } + + $eng = $this->request->getVar('eng') ?? 0; + $data = $this->reportHelper->getReportData($accessnumber, $eng); + $data['eng'] = $eng; + $data['accessnumber'] = $accessnumber; + $data['ispdf'] = 1; + + $html = view('report/template', $data); + $filename = $accessnumber . '.pdf'; + + try { + $jobId = $this->postToSpooler($html, $filename); + return $this->response->setJSON([ + 'success' => true, + 'jobId' => $jobId, + 'message' => 'PDF queued for generation', + 'status' => 'queued' + ]); + } catch (\Exception $e) { + log_message('error', "PDF generation failed: " . $e->getMessage()); + return $this->response->setStatusCode(500)->setJSON([ + 'success' => false, + 'error' => 'Failed to queue PDF generation' + ]); + } + } + + public function checkPdfStatus($jobId) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, "http://glenlis:3030/api/pdf/status/$jobId"); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode !== 200) { + log_message('error', "Spooler status check returned HTTP $httpCode"); + return $this->response->setStatusCode(500)->setJSON([ + 'success' => false, + 'error' => 'Failed to check job status' + ]); + } + + return $this->response->setJSON($response); + } + + private function postToSpooler($html, $filename) + { + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, 'http://glenlis:3030/api/pdf/generate'); + curl_setopt($ch, CURLOPT_POST, 1); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ + 'html' => $html, + 'filename' => $filename + ])); + 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) { + log_message('error', "Spooler API returned HTTP $httpCode"); + throw new \Exception('Failed to queue PDF generation'); + } + + $data = json_decode($response, true); + return $data['jobId']; + } } diff --git a/app/Views/shared/content_requests.php b/app/Views/shared/content_requests.php index 4c90511..3241621 100644 --- a/app/Views/shared/content_requests.php +++ b/app/Views/shared/content_requests.php @@ -189,7 +189,8 @@ ResTo Val Result - + + @@ -197,10 +198,8 @@ - - + + @@ -227,10 +226,20 @@ -