Do not close this window. The system is currently creating your PDFs!

Stand by!


 patient_focus logo
Captain's Log | bIQ What you're looking at is a portion of the notes taken as part of working on the "bIQ" application of Applied Health Analtyics. For the sake of security, most of the content has been removed with the exception of a couple of scenarios that demonstrate my problem solving skils in the context of PHP.
Table of Contents: A permanent remedy to the problem of reports and forms not fully printing as a result of timing out. A) Prototype 1) print.php 2) hello.php API 3) pap.class_print.php 4) hello_again.php A permanent remedy to the problem of reports and forms not fully printing as a result of timing out. The "Personal Health Survey" is a large PDF document. In some instances, depending on the number of employees, a large number of these PDFs need to be generated which can cause the system to time out. This solution fixed that problem by introducing a mechanism that printed off the total number of PDFs in "chunks." A) Prototype (back to top...) 1) print.php page (back to top...) Here's the way this works: You click on a button that opens up a popup window that has an animated "gear" icon displayed that gives the user the assurance that something is being processed. Below that icon is a "counter" that displays the number of "laps" that are being accomplished. What the user doesn't know is that each "lap" is a bundle of 10 rows. That number of "laps" is the number of times the code will retrieve 10 rows before the entire dataset has been processed. In between each lap, the connection to the database is severed while simultaneously routing the process back to the beginning with the ID of the last row that was processed in tow so the cycle knows where to start when the next "lap" is begun. Here's the code I used to make a prototype that includes some "markers" (, , ) to better explain the logic behind the code. First, here's the "print.php" page. This is where the button that initates the process resides...
//first thing is establish the number of laps we're doing by dividing the number of copies by the number of records we retrieve with each lap <?php $laps=round(111/10)+1; ?> //here's my style sheet that has my centered / embossed div where we'll print the status of the process and the final link <style> body { font-family:arial; font-size:10pt; } .centered { position: fixed; top: 50%; left: 50%; /* bring your own prefixes */ transform: translate(-50%, -50%); border:1px solid #cccccc; width:320px; height:175px; background-color:#ffffff; box-shadow:6px 6px 6px #656464; border-radius:10pt; padding:0; z-index:10; display:none; } .embossed { width:285px; height:135px; margin:auto; box-shadow:3px 3px 1px #ccc inset; border:1px solid #ccc; border-radius:10pt; margin-top:10px; padding:10px; } .gears { width:90%; margin:auto; height:90%; margin-top:7px; font-family:arial; } .gears table tr td { border:none; } </style> here's the actual div where I'm printing my status and my final link <div class="centered"> <div class="embossed"> <div class="gears"> <br> <table> <tr> <td><img src="loading_gears.gif"></td> <td style="font-size:9pt;" id="gear_text">Do not close this window. The system is currently creating your PDFs! <br><br>Stand by! </td> </tr> </table> </div> </div> </div> here's the link with the necessary data embedded as "data" tags Click <a href="#" class="print_forms" data-empid="1547" data-orderid="2750" data-quantity="111" data-laps="<?php echo $laps;?>">here</a> to print something... here's my JQuery <script> $(document).ready(function() { $('.print_forms').click(function() { $('.centered').show(); var empID = $(this).data('empid'); var quantity=$(this).data('quantity'); var primeID=$(this).data('prime_id'); var laps=$(this).data('laps'); $.get('hello.php?empID='+empID+'&hID='+orderID+'&quantity='+quantity+' &PrimeID='+primeID+'&laps='+laps+'&reps=1', function(data) { $('#gear_text').html(data); }); }); }); </script>
the number of records to be processed was displayed in the UI. We're just grabbing that number and dividing by 10 and adding 1 so our first round of processing, which subtracts "1" from the total by default, doesn't stop short of processing the whole dataset by 10 records. the class that is typically "hidden" which contains the "gear" graphic / popup that affords the user the opportunity to "see" that things are being processed the initial query that's retrieving our dataset in groups of 10. the "data" that's being displayed is the number of laps that are being done as things are being processed. 2) hello.php API (back to top...) So, you click a button that has the "print_forms" class attached to it and you get a little popup graphic that is also triggering the functionality represented in the "hello.php" page. "hello.php" is the API that's doing all of the heavy lifting.
<?php require_once('pap.class_print.php'); retrieve all of your necessary variables. Notice your "laps" variable has been decreased by "1" $empID=$_GET['empID']; $orderID=$_GET['orderID']; $quantity=$_GET['quantity']; $PrimeID=$_GET['PrimeID']; $laps=$_GET['laps']-1; $reps=$_GET['reps']; $attempt=new PrintProduction($database_connection); $bob=$attempt->init_genPSM($empID, $hID, $item_sel, $cpID, $orderID, $quantity, $PrimeID, $reps); if($laps>0) { echo "<script> $(document).ready(function() { $.get('hello_again.php?empID=$empID&orderID=$orderID&quantity=$quantity&PrimeID=$bob &laps=$laps&reps=$reps', function(data) { $('.centered').show(); $('#gear_text').html(data); }); }); </script>"; } else { When there are no more laps to do, then you trigger the "SecureFileZipper.php" page which returns the link the user will click on to access the completed zip file $printDate = date('m_d_Y'); $linkPath = '/SecureFileZipper.php?zipLoc=internal/production/' . $printDate . '/primed/' . $orderID; echo "<script> $('.centered').show(); $('#gear_text').html('You are all set! <br><br>To access the PDFs you just created, click <a href=\"$linkPath\" target=\"_blank\" style=\"font-size:9pt;\">here</a>.<br><br> Click <a id=\"close_print_window\" href=\"#\" style=\"font-size:9pt;\">here</a> to close this window!'); $('#close_print_window').click(function() { $('.centered').hide(); }); </script>"; } ?>
"pap.class_print.php" is the code that has the "init_genPSM" method which is what's being called by "$bob." here's where you're subtracting "1" from the number of laps the process is stated to complete meet, "$bob." $bob is going to trigger the functionality that's generating the PDFs, store them in a directory and the provide the needed ID of the last row that was processed so the system knows where to start in the context of the next "lap." the actual method located on "pap.class_print.php." It's going to generate 10 PDFs and then return the ID of the last row that was processed. $laps is the value that originally came from the value in the "print_forms" function. Once this process is initiated though, the "hello_again.php" page will decrease that value by 1. at this point, $bob has fired and it's going to send the id of the last row processed to "hello_again.php." The sole purpose of that page is to simply take an inventory of the number of rows / laps that have yet to be completed, update the text in the "gear" graphic and route the whole process back to "hello.php." here's the value of $bob" embedded within the URL that's routing the process to "hello_again.php." when there are no more laps, you then send the appropriate text alerting the user the process has completed and you include the link to the zip file. As long as there are laps, you'll display the text that's coming from the "hello_again.php" file which communicates the number of laps that been completed. the text that will be displayed on your popup window when the process has completed the close button for the popup window 3) pap.class_print.php (back to top...) This is the "pap.class_print.php" page. This is where all of the heavy lifting is happening, as far as the query that grabs the IDs that need to be processed, then generates the PDFs and finally returns the PrimeID of the last row that was processed. It's here, too, that the connection to the database will be severed in order to prevent the process from timing out.
<?php class PrintProduction { private $database; public function __construct($connection){ $this->database = $connection; } function init_genPSM($empID_sel=NULL,$hID_sel=NULL,$item_sel=NULL,$cpID_sel=NULL, $orderID=NULL, $quantity=500, $PrimeID, $reps) { $boom=0; $stmt = $this->cn->prepare('SELECT Top 10 PrimeID, rhID FROM AppropriateTable WHERE orderID = ? AND PrimeID>? ORDER BY PrimeID'); $stmt->execute(array($orderID, $PrimeID)); if($table = $stmt->fetchall(PDO::FETCH_ASSOC)) { $userTable = array(); foreach($table as $value) { $lastPrimeID=$value['PrimeID']; //here's the "last Prime ID." It will be exactly what we need it to be when it iterates for the last time $rhID = $value['rhID']; $stmt = $this->cn->prepare('SELECT cp.cpID, RIGHT(u.usrSSN, 4) as usrSSN_L4, u.*, r.rhID, e.empName, h.hPID, h.hID, h.hName, c.cmpLogoLrg, c.cmpLogoMed, c.cmpSignatureImg, c.cmpSignatoryName, c.cmpSignatoryTitle, c.cmpPHRLetter, c.cmpPostSurveyTxt, c.cmpSurveyLegalTxt, c.cmpBioSurveyLegalTxt, c.cmpSurveyLetter_sp, c.cmpSignatoryTitle_sp, c.cmpSurveyLetter FROM AppropriateTable r INNER JOIN anotherTable u ON r.usrID = u.usrID INNER JOIN yetAnotherTable e ON u.empID = e.empID INNER JOIN WHERE ($importantId= ?)'); $stmt->execute(array($importantId)); $userTable[]= $stmt->fetchall(PDO::FETCH_ASSOC); } //Date for PDF Folder $pDate = date('m_d_Y'); $orderCount = sizeof($userTable); //Determine the number of orders in the dataset you just retrieved $orderCountCharCount = strlen($orderCount); if($orderCount>0) { $new_count=$reps; for ($i=0; $i < $orderCount; $i++) { //the order count governs the number of times we loop through the list / dataset we generated in the above SELECT $incrFileName = $orderID.'_'.str_pad($new_count, $orderCountCharCount, '0', STR_PAD_LEFT).'_'; $pdfPath = $this->exe_primeGenPSM($item_sel, $userTable[$i][0], $incrFileName, $orderID, $pDate); $new_count=$new_count+1; } $out = array('zipLink' => $pdfPath); $stmt=null; $this->cn=null; return $lastPrimeID; //return to the last Prime ID } else { //you've reached the end of the line and all of the docs have been processed } } else { // generate an error message } } function exe_primeGenPSM($ordItem, $usrTable, $increment, $orderID, $pDate) { // content removed. This is the code that puts the actual PDF together and generates the $pdfPath } } ?>
this is the method that was triggered a monent ago on the "hello.php" page (see ). This is going to collect the 10 PrimeIDs that need to be funneled into the method that will created the PDFs. And this will only gather 10 of those PrimeIDs that have a value that is greater than the last PrimeID that was processed in the last "lap." the actual SELECT that gathers the correct PrimeIDs based on the incoming arguments the count / reps value is keeping track of how many PDFs have been successfully generated the $pdfPath is the directory where all of the PDFs that are being manufactured are being stored this is the method that's actually assembling and creating the actual PDF the URL / Zip File that the user will be able to access that contains all of the PDFs kill the database connection so it doesn't time out return the last PrimeID value 4) hello_again.php (back to top...) ...and finally, here's the "hello_again.php" page. This is the text as well as the piece of the code that's "bouncing" the process back to the "hello.php page. The text will change and display the link to the zip file when the process arrives at the last lap:
<?php $empID=$_GET['empID']; $hID=$_GET['hID']; $item_sel=$_GET['item_sel']; $cpID=$_GET['cpID']; $orderID=$_GET['orderID']; $quantity=$_GET['quantity']; $PrimeID=$_GET['PrimeID']; $laps=$_GET['laps']; $reps=$_GET['reps']+10; if($laps>1) { echo "Stand by! You've only got $laps more laps to go!"; } else { echo "Stand by! You've only got $laps more lap to go!"; } echo "<script> $(document).ready(function() { setTimeout(function() { $.get('hello.php?empID=$empID&hID=$hID&item_sel=$item_sel &cpID=$cpID&orderID=$orderID&quantity=$quantity &PrimeID=$PrimeID&laps=$laps&reps=$reps', function(data) { $('#gear_text').html(data); }); }, 3000); }); </script>"; ?>
here's where the process is routed back to the "hello.php" API