psr fix to prior commit
[openemr.git] / library / edihistory / edih_archive.php
blob56556350b8ff8e67f9ff6ac70288cc3607eae575
1 <?php
2 /************* edih_archive.php
3 * Author: Kevin McCormick Longview Texas
4 *
5 * Copyright 2016 Kevin McCormick Longview, Texas
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301, USA.
23 * Purpose: to archive old entries in the csv files and old files
25 * @author Kevin McCormick
26 * @link: http://www.open-emr.org
27 * @package OpenEMR
28 * @subpackage ediHistory
31 // a security measure to prevent direct web access to this file
32 // must be accessed through the main calling script ibr_history.php
33 // from admin at rune-city dot com; found in php manual
34 //if (!defined('SITE_IN')) die('Direct access not allowed!');
37 // required functions
38 //require_once("$srcdir/edihistory/test_edih_csv_inc.php");
40 // constant DS = DIRECTORY_SEPARATOR
42 /**
43 * Report on edi_history
45 * @uses csv_parameters()
46 * @uses csv_assoc_array()
48 * @param string archive date in CCYYMMDD format
50 * @return array array[i] = filename
52 function edih_archive_report($period = '')
55 $str_html = '';
56 $chkdt = '';
57 $strdt = '';
58 // edih_archive_date returns empty string if no period
59 $tper = edih_archive_date($period);
60 $chkdt = ($tper) ? $tper : 'None';
61 $strdt = ($tper) ? substr($chkdt, 0, 4).'-'.substr($chkdt, 4, 2).'-'.substr($chkdt, 6, 2) : 'None';
63 csv_edihist_log("edih_archive_report: creating archive report with date $chkdt");
65 $bdir = csv_edih_basedir();
66 $params = csv_parameters('ALL');
67 if (!is_array($params) && count($params)) {
68 csv_edihist_log("edih_archive_report: invalid csv_parameters");
69 return "<p>There was an error creating the report.</p>";
73 $str_html .= "<h3>Report on edi files using archive date $strdt</h3>".PHP_EOL;
74 foreach ($params as $key => $param) {
75 $old_ct = 0;
76 $clm_ct = 0;
77 $dir_ct = 0;
78 $dir_sz = 0;
79 $row_ct = 0;
80 $dir_kb = '';
81 $subdir_ct = 0;
82 $fntp = '';
83 $fntp_ct = 0;
85 $tp = $param['type'];
86 $fdir = $param['directory'];
88 if (is_dir($fdir)) {
89 $dir_ar = scandir($fdir);
90 if (is_array($dir_ar) && ((count($dir_ar)-2) > 0)) {
91 $str_html .= "<H3><em>Type</em> ".$tp."</H3>".PHP_EOL;
92 $str_html .= "<ul>".PHP_EOL;
93 $dir_ct = count($dir_ar);
94 foreach ($dir_ar as $fn) {
95 if ($fn == 'README.txt') {
96 $dir_ct--;
97 continue;
100 if (substr($fn, 0, 1) == '.') {
101 $dir_ct--;
102 continue;
105 if (is_dir($fdir.DS.$fn)) {
106 $subdir_ct++;
107 $dir_ct--;
108 continue;
111 $dir_sz += filesize($fdir.DS.$fn);
115 $csv_ar = csv_assoc_array($tp, 'file');
116 $row_ct = (is_array($csv_ar)) ? count($csv_ar) : 0;
117 if ($row_ct) {
118 foreach ($csv_ar as $row) {
119 // tally amount of claim transactions in the files
120 if ($tp == 'f837') {
121 $clm_ct += $row['Claim_ct'];
124 if ($tp == 'f277') {
125 $clm_ct += ($row['Accept'] + $row['Reject']);
128 if ($tp == 'f835') {
129 $clm_ct += $row['Claim_ct'];
132 // check for duplicates
133 // here assume that files with multiple rows are consecutive
134 if ($fntp !== $row['FileName']) {
135 $fntp = $row['FileName'];
136 // count files that would be archived
137 if (($chkdt != 'None') && strcmp($row['Date'], $chkdt) < 0) {
138 $old_ct++;
141 $fntp_ct++;
146 $dir_kb = csv_convert_bytes($dir_sz);
147 } else {
148 csv_edihist_log("edih_archive_report: error $tp file count $dir_ct with csv rows $row_ct");
152 $mis = ($fntp_ct == $dir_ct ) ? "(check/eft count or ISA--IEA count)" : "(mismatch csv $fntp_ct dir $dir_ct)";
154 if ($tp == 'f837') {
155 $str_html .= "<li><em>Note:</em> 837 Claims files are not archived </li>".PHP_EOL;
159 $str_html .= "<li>files directory has $dir_ct files, using $dir_kb </li>".PHP_EOL;
160 $str_html .= ($subdir_ct && ($tp != 'f837')) ? "<li> <em>warning</em> found $subdir_ct sub-directories</li>".PHP_EOL : "";
161 $str_html .= "<li>files csv table has $row_ct rows $mis</li>".PHP_EOL;
162 $str_html .= ($clm_ct) ? "<li>there are $clm_ct claim transactions counted</li>".PHP_EOL : "";
163 $str_html .= ($old_ct) ? "<li>Archive date $strdt would archive $old_ct files </li>".PHP_EOL : "";
164 $str_html .= "</ul>".PHP_EOL;
165 } else {
166 $str_html .= "<p><em>Type</em> <b>$tp</b> <br> -- empty $tp file directory</p>".PHP_EOL;
168 } else {
169 $str_html .= "<p><em>warning</em> <b>$tp</b> file directory does not exist</p>".PHP_EOL;
173 $str_html .= "<p>Report end</p>".PHP_EOL;
175 return $str_html;
180 * Format the date used in comparisons
182 * @param string period from select list e.g. 6m, 12m
184 * @return string archive date in CCYYMMDD format
186 function edih_archive_date($period)
189 $dtpd2 = '';
190 if (!$period) {
191 return $dtpd2;
194 $is_period = preg_match('/\d{1,2}(?=m)/', $period, $matches);
196 if (count($matches)) {
197 $gtdt = getdate();
199 if (strpos($period, 'm')) {
200 // take the number part of 'period'
201 // so modstr will be '-N month'
202 $modstr = '-'.$matches[0].' month';
203 $dtstr1 = $gtdt['mon'].'/01/'.$gtdt['year'];
204 } elseif (strpos($period, 'y')) {
205 $modstr = '-'.$matches[0].' year';
206 $dtstr1 = $gtdt['mon'].'/01/'.$gtdt['year'];
207 } else {
208 csv_edihist_log("edih_archive_date: incorrect date period $period");
209 return false;
213 if ($modstr) {
214 $dtpd1 = date_create($dtstr1);
215 $dtm = date_modify($dtpd1, $modstr);
216 $dtpd2 = $dtm->format('Ymd');
217 } else {
218 csv_edihist_log("edih_archive_date: failed to parse $period");
219 return false;
221 } else {
222 csv_edihist_log("edih_archive_date: invalid argment $period");
223 return false;
226 // the testing date in CCYYMMDD format
227 return $dtpd2;
232 * Create an array of file names to be archived
233 * The 'Date' column in the files_[type].csv file
234 * is compared to the archive date. If the date is less
235 * than the archive date, the "FileName' value is copied
237 * @param array csv file rows array
238 * @param string archive date in CCYYMMDD format
240 * @return array array[i] = filename
242 function edih_archive_filenames($csv_ar, $archive_date)
245 if ($archive_date && strlen($archive_date) == 8 && is_numeric($archive_date)) {
246 $testdate = (string)$archive_date;
247 } else {
248 csv_edihist_log("edih_archive_filenames: invalid archive date $archive_date");
249 return false;
253 if (!is_array($csv_ar) || !count($csv_ar)) {
254 csv_edihist_log("edih_archive_filenames: failed to get csv file array $file_type");
255 return false;
259 $fn_ar = array();
260 foreach ($csv_ar as $row) {
261 if (strcmp($row['Date'], $archive_date) < 0) {
262 $fn_ar[] = $row['FileName'];
266 $ret_ar = (count($fn_ar)) ? array_values(array_unique($fn_ar)) : $fn_ar;
268 return $ret_ar;
272 * Create a new csv array by omitting rows which reference
273 * a file name that is to be archived
275 * @uses csv_file_type()
276 * @uses csv_assoc_array()
278 * @param string the file type
279 * @param string the csv type file or claim
280 * @param array the array of archived file names and retained file names
282 * @return array
284 function edih_archive_csv_split($csv_ar, $filename_array)
287 if (!is_array($filename_array) || !count($filename_array)) {
288 csv_edihist_log('csv_archive_table; invalid filename array');
289 return false;
293 if (is_array($csv_ar) && count($csv_ar)) {
294 csv_edihist_log("edih_archive_csv_split: csv rows ".count($csv_ar)." old files ".count($filename_array));
295 } else {
296 csv_edihist_log("edih_archive_csv_split: failed to get csv file array");
297 return false;
301 // if the to be archived file name is in the row,
302 // do not copy it to the new csv array
303 $arch_ar = array();
304 $arch_ar['arch'] = array();
305 $arch_ar['keep'] = array();
307 foreach ($csv_ar as $row) {
308 if (in_array($row['FileName'], $filename_array)) {
309 $arch_ar['arch'][] = $row;
310 } else {
311 $arch_ar['keep'][] = $row;
316 csv_edihist_log("edih_archive_csv_split: 'arch' array rows ".count($arch_ar['arch']));
317 csv_edihist_log("edih_archive_csv_split: 'keep' array rows ".count($arch_ar['keep']));
319 return $arch_ar;
324 * Creates a zip archive of the files in the $filename_ar array and
325 * returns the path/name of the archive or FALSE on error
327 * @param array $parameters array for file type from csv_parameters
328 * @param array $filename_ar array of filenames to be archived
329 * @param string $archive_date date of archive to be incorporated in archive file name
331 * @return bool result of zipArchive functions
333 function edih_archive_create_zip($parameters, $filename_ar, $archive_date, $archive_filename)
335 // we deal with possible maximum files issues by chunking the $fn_ar array
337 $ft = $parameters['type'];
338 $fdir = $parameters['directory'];
339 $tmp_dir = csv_edih_tmpdir();
340 // archive csv rows -- same name as from edih_archive_main
341 // $fn_files_arch = $tmp_dir.DS.'arch_'.basename($files_csv);
342 $files_csv_arch = 'arch_'.basename($parameters['files_csv']);
343 // $fn_claims_arch = $tmp_dir.DS.'arch_'.basename($claim_csv);
344 $claims_csv_arch = 'arch_'.basename($parameters['claims_csv']);
346 $f_max = 200;
347 $fn_ar2 = array();
348 // to handle possibility of more than 200 files in the archive
349 // use the 'chunk' method
350 if (count($filename_ar) > $f_max) {
351 $fn_ar2 = array_chunk($filename_ar, $f_max);
352 } else {
353 $fn_ar2[] = $filename_ar;
357 $zip_name = $tmp_dir.DS.$archive_filename;
358 csv_edihist_log("edih_archive_create_zip: using $zip_name");
360 $zip_obj = new ZipArchive();
361 csv_edihist_log("edih_archive_create_zip: now opening archive $archive_filename");
362 if (is_file($zip_name)) {
363 $isOK = $zip_obj->open($zip_name, ZIPARCHIVE::CHECKCONS);
364 if ($isOK) {
365 if ($zip_obj->locateName($ft) === false) {
366 $isOK = $zip_obj->addEmptyDir($ft);
367 if (!$isOK) {
368 csv_edihist_log("edih_archive_create_zip: adding $ft ZipArchive error $msg");
369 return $isOK;
372 } else {
373 $msg = $zip_obj->getStatusString();
374 csv_edihist_log("edih_archive_create_zip: $ft ZipArchive error $msg");
375 return $isOK;
377 } else {
378 $isOK = $zip_obj->open($zip_name, ZIPARCHIVE::CREATE);
379 $isOK = $zip_obj->addEmptyDir('csv');
380 $isOK = $zip_obj->addEmptyDir($ft);
381 $zip_obj->setArchiveComment("edi_history archive prior to $archive_date");
384 // we are working with the open archive
385 // now add the old csv files to the archive
386 if (is_file($tmp_dir.DS.$files_csv_arch)) {
387 csv_edihist_log("edih_archive_create_zip: now adding $files_csv_arch to archive");
388 $isOK = $zip_obj->addFile($tmp_dir.DS.$files_csv_arch, 'csv'.DS.$files_csv_arch);
391 if (is_file($tmp_dir.DS.$claims_csv_arch)) {
392 csv_edihist_log("edih_archive_create_zip: now adding $claims_csv_arch to archive");
393 $isOK = $zip_obj->addFile($tmp_dir.DS.$claims_csv_arch, 'csv'.DS.$claims_csv_arch);
396 // close zip archive
397 csv_edihist_log("edih_archive_create_zip: now closing archive");
398 $isOK = $zip_obj->close();
399 if ($isOK !== true) {
400 $msg = $zip_obj->getStatusString();
401 csv_edihist_log("edih_archive_create_zip: $ft ZipArchive error $msg");
402 return $isOK;
405 // $fn_ar2[i][j]
406 csv_edihist_log("edih_archive_create_zip: with file name groups ".count($fn_ar2));
407 foreach ($fn_ar2 as $fnz) {
408 // reopen the zip archive on each loop so the open file count is controlled
409 if (is_file($zip_name)) {
410 csv_edihist_log("edih_archive_create_zip: now opening archive");
411 $isOK = $zip_obj->open($zip_name, ZIPARCHIVE::CHECKCONS);
415 if ($isOK === true) {
416 // we are working with the open archive
417 // now add the old x12 files to the archive
418 csv_edihist_log("edih_archive_create_zip: now adding $ft files to archive");
419 foreach ($fnz as $fz) {
420 if ($fz == '.' || $fz == '..') {
421 continue;
424 if (is_file($fdir.DS.$fz) && is_readable($fdir.DS.$fz)) {
425 $isOK = $zip_obj->addFile($fdir.DS.$fz, $ft.DS.$fz);
426 } else {
427 // possible that file is in csv table, but not in directory?
428 $msg = $zip_obj->getStatusString();
429 csv_edihist_log("edih_archive_create_zip: error adding file $fz zipArchive: $msg");
431 } // end foreach($fnz as $fz)
432 // close zip object for next iteration of chunked array
433 csv_edihist_log("edih_archive_create_zip: now closing archive");
434 $isOK = $zip_obj->close();
435 // errors on close would be non-existing file added or something else
436 if ($isOK !== true) {
437 $msg = $zip_obj->getStatusString();
438 csv_edihist_log("edih_archive_create_zip: $ft ZipArchive error $msg");
440 return $isOK;
442 } else {
443 // ZipArchive open() failed -- try to get the error message and return false
444 $msg = $zip_obj->getStatusString();
445 csv_edihist_log("edih_archive_create_zip: $ft ZipArchive failed $msg");
446 return $isOK;
447 }// end if ($isOK)
449 } // end foreach($fn_ar2 as $fnz)
451 return $isOK;
455 * Archived files have been included in archive file
456 * so we move the files to the archive tmp directory, for later deletion
458 * @param array parameters array for type
459 * @param array filename array
461 * @return int count of moved files
463 function edih_archive_move_old($parameters, $filename_ar)
466 if (!is_array($filename_ar) || !count($filename_ar)) {
467 return false;
470 if (!is_array($parameters) || !count($parameters)) {
471 return false;
475 clearstatcache(true);
477 $fnct = 0;
478 $fn_ar_ct = count($filename_ar);
479 $ft = $parameters['type'];
480 $fdir = $parameters['directory'];
481 $fdir = realpath($fdir);
482 $rndir = csv_edih_tmpdir().DS.$ft;
484 if (is_dir($fdir)) {
485 csv_edihist_log("edih_archive_delete_old: $ft dir OK");
486 if (is_dir($rndir) || mkdir($rndir)) {
487 $rndir = realpath($rndir);
488 csv_edihist_log("edih_archive_delete_old: $ft move dir OK");
489 $isOK = true;
490 } else {
491 csv_edihist_log("edih_archive_delete_old: $ft move dir error");
492 $isOK = false;
494 } else {
495 csv_edihist_log("edih_archive_delete_old: $ft dir error");
496 $isOK = false;
500 if ($isOK) {
501 csv_edihist_log("edih_archive_delete_old: $ft old file count $fn_ar_ct");
502 foreach ($filename_ar as $fn) {
503 // if we have added the file to the archive, remove it from the storage directory
504 // but keep the /history/tmp file copy for now
505 if (is_file($fdir.DS.$fn)) {
506 $isrn = rename($fdir.DS.$fn, $rndir.DS.$fn);
510 if ($isrn) {
511 $fnct++;
512 } else {
513 csv_edihist_log("edih_archive_delete_old: $ft failed to move $fn");
516 } else {
517 csv_edihist_log("edih_archive_delete_old: $ft directory error for files or tmp");
521 return $fnct;
526 * create associative array from archive csv file
528 * @uses edih_archive_csv_array()
530 * @param string
531 * @param string
532 * @param string optional filepath
534 * @return array
536 function edih_archive_csv_array($filetype, $csv_type, $filepath = '')
539 $str_out = '';
540 $csv_ar = array();
541 $tmpdir = csv_edih_tmpdir();
542 $tmpcsv = $tmpdir.DS.'csv';
544 $csvtp = (strpos($csv_type, 'aim')) ? 'claims' : 'files';
546 if (is_file($filepath)) {
547 $csv_arch_path = $filepath;
548 } else {
549 $csv_arch_path = $tmpcsv.DS.'arch_'.$csvtp.'_'.$filetype.'.csv';
553 $ct = 0;
554 $row = 0;
555 $ky = -1;
556 // relies on first row being header or column names
557 if (($fh = fopen($csv_arch_path, "rb")) !== false) {
558 while (($data = fgetcsv($fh, 2048, ",")) !== false) {
559 if (is_null($data)) {
560 continue;
563 if ($row) {
564 for ($i=0; $i<$ct; $i++) {
565 $csv_ar[$ky][$h[$i]] = $data[$i];
567 } else {
568 $ct = count($data);
569 $h = $data;
572 $row++;
573 $ky++;
576 fclose($fh);
577 } else {
578 // invalid file path
579 csv_edihist_log('edih_archive_csv_array; invalid file path '.$csv_arch_path);
580 return false;
584 return $csv_ar;
588 * combine the csv file in the archive with the current csv file
590 * @uses edih_archive_csv_array()
592 * @param string
593 * @param string
595 * @return string
597 function edih_archive_csv_combine($filetype, $csvtype)
600 $str_out = '';
601 $hdr_ar = array();
602 $bdir = csv_edih_basedir();
603 $tmpdir = csv_edih_tmpdir();
604 $tmpcsv = $tmpdir.DS.'csv';
606 $csvtp = (strpos($csvtype, 'aim')) ? 'claims' : 'files';
607 $csv_arch_file = $tmpcsv.DS.'arch_'.$csvtp.'_'.$filetype.'.csv';
608 $csv_new_file = $tmpdir.DS.'cmb_'.$csvtp.'_'.$filetype.'.csv';
610 // arrays used to eliminate duplicate rows
611 $dup_ar = $dup_unique = $dup_keys = array();
612 // combine files by combining arrays and writing a tmp file
613 // get the present csv file contents
614 $car1 = csv_assoc_array($filetype, $csvtp);
615 // get the archived csv contents
616 if (is_file($csv_arch_file)) {
617 $car2 = edih_archive_csv_array($filetype, $csvtp, $csv_arch_file);
620 // possibility of empty arrays if no data rows in a csv file
621 $hdrc1 = (is_array($car1) && count($car1)) ? array_keys($car1[0]) : array();
622 $hdrc2 = (is_array($car2) && count($car2)) ? array_keys($car2[0]) : array();
623 if (count($hdrc1) && ($hdrc1 === $hdrc2)) {
624 $hdr_ar = $hdrc1;
625 } elseif (empty($hdrc1) && count($hdrc2)) {
626 $hdr_ar = $hdrc2;
627 } elseif (empty($hdrc2) && count($hdrc1)) {
628 $hdr_ar = $hdrc1;
629 } else {
630 // array mismatch error (impossible?)
631 csv_edihist_log("edih_archive_csv_combine: $filetype $csvtp array header mismatch");
632 // just use the current csv file
633 $hdr_ar = csv_table_header($filetype, $csvtp);
634 $car_cmb_unique = $car1;
635 // debug
636 if (count($hdrc1)) {
637 $dbg_str = '';
638 foreach ($hdrc1 as $h) {
639 $dbg_str .= $h.' ';
642 csv_edihist_log("edih_archive_csv_combine: $csvtp car1 header $dbg_str");
643 } else {
644 csv_edihist_log("edih_archive_csv_combine: $csvtp car1 header empty");
647 if (count($hdrc2)) {
648 $dbg_str = '';
649 foreach ($hdrc2 as $h) {
650 $dbg_str .= $h.' ';
653 csv_edihist_log("edih_archive_csv_combine: $csvtp car2 header $dbg_str");
654 } else {
655 csv_edihist_log("edih_archive_csv_combine: $csvtp car2 header empty");
661 // if the arrays checked out
662 if (!isset($car_cmb_unique)) {
663 // if we have archive csv rows
664 if (is_array($car1) && is_array($car2)) {
665 // put the archive rows first
666 $car_cmb = array_merge($car2, $car1);
667 // now eliminate duplicates
668 if ($csvtp == 'files') {
669 if ($filetype == 'f835') {
670 $ky = 'Trace';
671 } else {
672 $ky = 'Control';
675 // array_column() php v5.5
676 foreach ($car_cmb as $idx => $row) {
677 $dup_ar[$idx] = $row[$ky];
680 csv_edihist_log("edih_archive_csv_combine: $csvtp array row count ".count($dup_ar));
681 $dup_unique = array_unique($dup_ar);
682 $dup_keys = array_keys($dup_unique);
683 csv_edihist_log("edih_archive_csv_combine: $csvtp index row count ".count($dup_keys));
684 foreach ($dup_keys as $k) {
685 $car_cmb_unique[] = $car_cmb[$k];
688 csv_edihist_log("edih_archive_csv_combine: $csvtp combined row count ".count($car_cmb_unique));
689 } elseif ($csvtp == 'claims') {
690 $ct = count($hdr_ar);
691 $ftxt = $csvtp.' array'.PHP_EOL;
692 foreach ($car_cmb as $idx => $row) {
693 $r_str = '';
694 for ($i=0; $i<$ct; $i++) {
695 $r_str .= $row[$hdr_ar[$i]];
698 $dup_ar[$idx] = $r_str;
699 $ftxt .= $r_str.PHP_EOL;
702 csv_edihist_log("edih_archive_csv_combine: $csvtp array row count ".count($dup_ar));
703 file_put_contents($tmpdir.DS.'archive'.DS.'claimstr.txt', $ftxt);
705 $dup_unique = array_unique($dup_ar);
706 $dup_keys = array_keys($dup_unique);
707 csv_edihist_log("edih_archive_csv_combine: $csvtp index row count ".count($dup_keys));
708 foreach ($dup_keys as $k) {
709 $car_cmb_unique[] = $car_cmb[$k];
712 csv_edihist_log("edih_archive_csv_combine: $csvtp combined row count ".count($car_cmb_unique));
713 } else {
714 $car_cmb_unique = $car_cmb;
716 } else {
717 csv_edihist_log("edih_archive_csv_combine: array keys mismatch $filetype");
719 } else {
720 csv_edihist_log("edih_archive_csv_combine: error reading archived csv ".$csvtp."_".$filetype.".csv");
723 $rwct = 0;
724 $fh = fopen($csv_new_file, 'wb');
725 if ($fh) {
726 fputcsv($fh, $hdr_ar);
727 $rwct++;
729 foreach ($car_cmb_unique as $row) {
730 fputcsv($fh, $row);
731 $rwct++;
734 // close new csv file
735 fclose($fh);
736 } else {
737 csv_edihist_log("edih_archive_csv_combine: failed to open $filetype new csv file");
740 return $rwct;
744 * Unpack an existing archive and restore it to current csv records
745 * and replace the files in the respective directories
747 * @uses edih_archive_csv_combine
748 * @param string
750 * @return string
752 function edih_archive_restore($archive_name)
755 $str_out = '';
756 $bdir = csv_edih_basedir();
757 $tmpdir = csv_edih_tmpdir();
758 $archdir = $bdir.DS.'archive';
760 if (is_file($archdir.DS.$archive_name)) {
761 $arch = realpath($archdir.DS.$archive_name);
762 $str_out .= "Archive: restoring $archive_name<br>";
763 csv_edihist_log("edih_archive_restore: restoring $archive_name");
764 } else {
765 $str_out = "Archive: restore archive bad file name $archive_name <br>";
766 csv_edihist_log("edih_archive_restore: restore archive bad file name $archive_name");
767 return $str_out;
771 $zip_obj = new ZipArchive();
772 // open archive (ZIPARCHIVE::CHECKCONS the ZIPARCHIVE::CREATE is supposedly necessary for microsoft)
773 //$res = $zip_obj->open($arch, ZIPARCHIVE::CHECKCONS);
774 if ($zip_obj->open($arch, ZIPARCHIVE::CHECKCONS) === true) {
775 $f_ct = $zip_obj->numFiles;
776 $str_out .= "Extracting $f_ct items from $archive_name <br>";
777 csv_edihist_log("edih_archive_restore: Extracting $f_ct items from $archive_name");
778 $isOK = $zip_obj->extractTo($tmpdir);
779 if (!$isOK) {
780 $msg = $zip_obj->getStatusString();
781 csv_edihist_log("edih_archive_restore: error extracting archive");
782 $str_out .= "Archive: error extracting archive $archive_name <br>";
783 $str_out .= "zipArchive: $msg <br>";
784 return $str_out;
786 } else {
787 $msg = $zip_obj->getStatusString();
788 csv_edihist_log("edih_archive_restore: error opening archive");
789 $str_out .= "Archive: error opening archive <br>".PHP_EOL;
790 $str_out .= "zipArchive: $msg <br>";
791 return $str_out;
794 // now traverse the tmpdir and replace things
795 // we should have tmp/csv/files_[ftype].csv claims_[ftype].csv
796 // tmp/[ftype]/x12_filenames
797 $arch_ar = scandir($tmpdir);
798 $tpstr = '';
799 foreach ($arch_ar as $fa) {
800 if ($fa == '.' || $fa == '..') {
801 continue;
804 if (is_dir($tmpdir.DS.$fa)) {
805 if ($fa == 'csv') {
806 continue;
809 // if a /history/ftype dir exists
810 if (is_dir($bdir.DS.$fa)) {
811 $type_ar[] = $fa;
812 $tpstr .= "$fa ";
814 } else {
815 continue;
820 csv_edihist_log("edih_archive_restore: types in archive $tpstr");
821 $str_out .= "Archive: types in archive $tpstr <br>".PHP_EOL;
823 foreach ($type_ar as $ft) {
824 $str_out .= "Archive: now restoring $ft<br>".PHP_EOL;
825 csv_edihist_log("edih_archive_restore: now restoring $ft");
827 $frows = edih_archive_csv_combine($ft, 'file');
828 csv_edihist_log("edih_archive_restore: files_$ft csv combined rows $frow");
829 $crows = edih_archive_csv_combine($ft, 'claim');
830 csv_edihist_log("edih_archive_restore: claims_$ft csv combined rows $frow");
832 $file_ar = scandir($tmpdir.DS.$ft);
833 foreach ($file_ar as $fn) {
834 if ($fn == '.' || $fn == '..') {
835 continue;
838 if (is_file($tmpdir.DS.$ft.DS.$fn)) {
839 $rn = rename($tmpdir.DS.$ft.DS.$fn, $bdir.DS.$ft.DS.$fn);
840 if (!$rn) {
841 $str_out .= " -- error restoring ".$ft.DS.$fn."<br>".PHP_EOL;
842 csv_edihist_log("edih_archive_restore: error restoring ".$ft.DS.$fn);
847 // this will catch the csv files for the particulat type
848 $str_out .= "Archive: now replacing csv tables for $ft<br>".PHP_EOL;
849 csv_edihist_log("edih_archive_restore: now replacing csv tables for $ft");
851 $rnf = rename($tmpdir.DS.'cmb_files_'.$ft.'.csv', $bdir.DS.'csv'.DS.'files_'.$ft.'.csv');
852 $rnc = rename($tmpdir.DS.'cmb_claims_'.$ft.'.csv', $bdir.DS.'csv'.DS.'claims_'.$ft.'.csv');
853 $str_out .= ($rnf) ? "" : " -- error restoring files_$ft.csv <br>".PHP_EOL;
854 $str_out .= ($rnc) ? "" : " -- error restoring claims_$ft.csv <br>".PHP_EOL;
858 csv_edihist_log("edih_archive_restore: now removing archive file");
859 $str_out .= "Archive: now removing archive file <br>".PHP_EOL;
860 $rm = unlink($arch);
861 if (!$rm) {
862 csv_edihist_log("edih_archive_restore: error removing $archdir.DS.$archive_name");
863 $str_out .= ($rnf) ? "" : " -- error removing $archdir.DS.$archive_name".PHP_EOL;
867 //edih_archive_cleanup($arch_fn, $tp_ar);
868 csv_edihist_log("edih_archive_restore: now removing temporary files");
869 $str_out .= "Archive: now removing temporary files <br>".PHP_EOL;
872 $is_clear = csv_clear_tmpdir();
873 if ($is_clear) {
874 $str_out .= "Archive: temporary files removed. Process complete.<br>" .PHP_EOL;
875 } else {
876 $str_out .= "Archive: still some files in /history/tmp/. Process complete.<br>" .PHP_EOL;
880 return $str_out;
885 * restores files from the tmp dir if the archive process needs to be aborted
887 * @uses csv_edih_basedir()
888 * @uses csv_edih_tmpdir()
889 * @uses csv_parameters()
891 * @return string
893 function edih_archive_undo()
896 // archive process creates files in /history/tmp
897 // /tmp/old_files_[type].csv copy of pre-archive csv record
898 // /tmp/old_claims_[type].csv copy of pre-archive csv record
899 // /tmp/new_files_[type].csv csv record of non-archived files
900 // /tmp/new_claims_[type].csv csv record of non-archived files
901 // /tmp/arch_files_[type].csv csv record of archived files (to be put in zip file)
902 // /tmp/arch_claims_[type].csv csv record of archived files (to be put in zip file)
903 // /tmp/[type]/filename_to_be_archived all the archived files for [type]
905 $str_out = '';
906 $bdir = csv_edih_basedir();
907 $tmpdir = csv_edih_tmpdir();
908 $archdir = $bdir.DS.'archive';
910 $params = csv_parameters("ALL");
911 $types_ar = array_keys($params);
913 csv_edihist_log("edih_archive_undo: restoring prior csv files files");
914 foreach ($types_ar as $ft) {
915 if (is_file($tmpdir.DS.'old_files_'.$ft.'.csv')) {
916 $rn = rename($tmpdir.DS.'old_files_'.$ft.'.csv', $bdir.DS.'csv'.DS.'files_'.$ft.'.csv');
917 if ($rn) {
918 csv_edihist_log("edih_archive_undo: restored prior files_$ft ");
919 } else {
920 csv_edihist_log("edih_archive_undo: restore failed for prior files_$ft ");
924 if (is_file($tmpdir.DS.'old_claims_'.$ft.'.csv')) {
925 $rn = rename($tmpdir.DS.'old_claims_'.$ft.'.csv', $bdir.DS.'csv'.DS.'claims_'.$ft.'.csv');
926 if ($rn) {
927 csv_edihist_log("edih_archive_undo: restored prior claimss_$ft ");
928 } else {
929 csv_edihist_log("edih_archive_undo: restore failed for prior claims_$ft ");
934 $arch_ar = scandir($tmpdir);
935 foreach ($arch_ar as $fa) {
936 if ($fa == "." && $fa == "..") {
937 continue;
940 if (is_dir($tmpdir.DS.$fa)) {
941 if (in_array($fa, $types_ar)) {
942 $fpath = $params[$fa]['directory'];
943 if ($dh = opendir($tmpdir.DS.$fa)) {
944 $str_out .= "Archive: undo restoring $fa files<br>".PHP_EOL;
945 csv_edihist_log("edih_archive_undo: restoring $fa files");
946 while (false !== ($entry = readdir($dh))) {
947 if ($entry != "." && $entry != "..") {
948 if (is_file($fpath.DS.$entry)) {
949 // file was not moved
950 } else {
951 rename($tmpdir.DS.$fa.DS.$entry, $fpath.DS.$entry);
956 closedir($dh);
962 return $str_out;
967 * After the archive is created, the csv record needs to be re-written so the archived
968 * files are not in the csv file and hence, not searched for.
970 * @uses csv_table_header()
972 * @param string $csv_path the tmp csv file path is expected
973 * @param array $row_array the data rows to be written (an associative array)
975 * @return integer count the rows written
977 function edih_archive_rewrite_csv($csv_path, $csv_keys, $row_array)
979 // @param string $csv_path -- the tmp csv file path is expected
980 // @param array $heading_ar -- the column heading for the csv file
981 // @param array $row_array -- the data rows to be written
983 // count characters written -- returned by fputcsv
984 $ocwct = 0;
985 $rwct = 0;
987 if (is_array($row_array)) {
988 csv_edihist_log("edih_archive_rewrite_csv: row array count ".count($row_array));
989 } else {
990 csv_edihist_log("edih_archive_rewrite_csv: row array not array");
994 if (is_array($row_array) && is_array($csv_keys)) {
995 if (count($csv_keys)) {
996 $h_ar = $csv_keys;
998 } else {
999 csv_edihist_log("edih_archive_rewrite_csv: invalid row array");
1000 return $rwct;
1003 //$csv_path should end with /history/tmp/[arch|keep]_[files|claims]_[type].csv
1004 // with 'w' flag, place the file pointer at the beginning of the file
1005 // and truncate the file to zero length.
1006 // If the file does not exist, attempt to create it.
1007 $fh3 = fopen($csv_path, 'wb');
1008 if ($fh3) {
1009 // write the heading row first
1010 $ocwct += fputcsv($fh3, $h_ar);
1011 // wrote heading, now add rows
1012 foreach ($row_array as $row) {
1013 $ocwct += fputcsv($fh3, $row);
1014 $rwct++;
1017 fclose($fh3);
1018 csv_edihist_log("edih_archive_rewrite_csv: wrote ".count($row_array)." rows to ".basename($csv_path));
1019 } else {
1020 csv_edihist_log("edih_archive_rewrite_csv: failed to open $csv_path");
1023 return $rwct;
1028 * cleanup archived files after archive created
1030 * @param string name of archive file
1031 * @param array array of types included in archive
1033 * @return string
1035 function edih_archive_cleanup($archivename, $types_ar)
1038 $str_out = '';
1040 if (is_array($types_ar) && count($types_ar)) {
1041 $tdirs = $types_ar;
1042 } else {
1043 csv_edihist_log("edih_archive_cleanup: no types in file types list");
1044 $str_out = "no types in file types list".PHP_EOL;
1045 return $str_out;
1048 $bdir = csv_edih_basedir();
1049 $tmpdir = csv_edih_tmpdir();
1050 $archivedir = $bdir.DS.'archive';
1051 $fct = 0;
1052 // move archive file to archive directory
1053 csv_edihist_log("edih_archive_cleanup: now clearing temporary files");
1054 $str_out .= "Archive: now clearing temporary files<br>".PHP_EOL;
1055 // delete archived files from edih tmp dir
1056 foreach ($tdirs as $td) {
1057 csv_edihist_log("edih_archive_cleanup: cleaning up for $td");
1058 if (is_dir($tmpdir.DS.$td)) {
1059 $fn_ar = scandir($tmpdir.DS.$td);
1060 foreach ($fn_ar as $fn) {
1061 if ($fn == '.' || $fn == '..') {
1062 continue;
1065 if (is_file($tmpdir.DS.$td.DS.$fn)) {
1066 $ul = unlink($tmpdir.DS.$td.DS.$fn);
1067 if (!$ul) {
1068 csv_edihist_log("edih_archive_cleanup: error removing file $fn");
1069 $str_out .= "<p>edih_archive_cleanup: error removing file ".$td.DS.$fn."</p>";
1070 } else {
1071 $fct++;
1076 // try to remove the now empty directory
1077 csv_edihist_log("edih_archive_cleanup: removing tmp $td");
1078 rmdir($tmpdir.DS.$td);
1081 csv_edihist_log("edih_archive_cleanup: removed $fct files from $td");
1084 return $str_out;
1089 * The main function in this edih_archive.php script. This function gets the parameters array
1090 * from csv_parameters() and calls the archiving functions on each type of file
1091 * in the parameters array.
1093 * @uses edih_archive_date()
1094 * @uses csv_edih_basedir()
1095 * @uses csv_parameters()
1096 * @uses csv_edih_tmpdir()
1097 * @uses csv_table_header()
1098 * @uses edih_archive_filenames()
1099 * @uses edih_archive_rewrite_csv()
1100 * @uses edih_archive_csv_split()
1101 * @uses edih_archive_create_zip()
1103 * @param string from select drop-down 6m, 12m, 18m, etc
1105 * @return string descriptive message in html format
1107 function edih_archive_main($period)
1110 $out_html = '';
1111 if ($period) {
1112 $archive_date = edih_archive_date($period);
1113 if ($archive_date) {
1114 $archive_dir = csv_edih_basedir().DS.'archive';
1115 $tmp_dir = csv_edih_tmpdir();
1116 $arch_fn = $archive_date.'_archive.zip';
1117 $params = csv_parameters();
1118 } else {
1119 csv_edihist_log("edih_archive_main: error creating archive date from $period");
1120 $out_html = "Error creating archive date from $period<br />" .PHP_EOL;
1122 } else {
1123 $out_html = "Archive period invalid.<br />" .PHP_EOL;
1124 return $out_html;
1128 if (is_dir($archive_dir)) {
1129 if (is_file($archive_dir.DS.$arch_fn)) {
1130 csv_edihist_log("edih_archive_main: archive file $arch_fn already exists");
1131 $out_html = "Archive: archive file $arch_fn already exists<br>" .PHP_EOL;
1132 return $out_html;
1134 } else {
1135 // should have been created at setup
1136 if (!mkdir($archive_dir, 0755)) {
1137 csv_edihist_log("edih_archive_main: archive directory does not exist");
1138 $out_html = "Archive: archive directory does not exist<br>" .PHP_EOL;
1139 return $out_html;
1144 foreach ($params as $k => $p) {
1146 $ft = $p['type']; // could be $k
1148 if ($ft == 'f837') {
1149 csv_edihist_log("edih_archive_main: 837 Claims files are not archived");
1150 continue;
1153 $fdir = $p['directory'];
1154 $scan = scandir($fdir);
1155 if (!$scan || count($scan) < 3) {
1156 continue;
1160 $files_csv = $p['files_csv'];
1161 $claims_csv = $p['claims_csv'];
1162 $date_col = $p['filedate'];
1163 $fncol = 'FileName';
1165 // create three csv file paths 'old_', 'arch_', and 'keep_'
1166 // files csv temporary names
1167 $fn_files_old = $tmp_dir.DS.'old_'.basename($files_csv);
1168 $fn_files_arch = $tmp_dir.DS.'arch_'.basename($files_csv);
1169 $fn_files_keep = $tmp_dir.DS.'keep_'.basename($files_csv);
1170 // claims csv temporary names
1171 $fn_claims_old = $tmp_dir.DS.'old_'.basename($claims_csv);
1172 $fn_claims_arch = $tmp_dir.DS.'arch_'.basename($claims_csv);
1173 $fn_claims_keep = $tmp_dir.DS.'keep_'.basename($claims_csv);
1174 // table headings
1175 $fh_ar = csv_table_header($ft, 'file');
1176 $ch_ar = csv_table_header($ft, 'claim');
1177 // copy existing csv files -- continue to next type if no files_csv
1178 $iscpc = $iscpf = false;
1179 if (is_file($files_csv)) {
1180 $iscpf = copy($files_csv, $fn_files_old);
1181 csv_edihist_log("edih_archive_main: copy $ft files csv to tmp dir");
1182 } else {
1183 csv_edihist_log("edih_archive_main: $ft files csv does not exist");
1184 continue;
1187 if (is_file($claims_csv)) {
1188 $iscpc = copy($claims_csv, $fn_claims_old);
1189 csv_edihist_log("edih_archive_main: copy $ft claims csv to tmp dir");
1190 } else {
1191 if ($ft == 'f997') {
1192 // there may be no 997 type claims records, so create a dummy file
1193 $fh = fopen($fn_claims_old, 'wb');
1194 if ($fh) {
1195 fputcsv($fh, $ch_ar);
1196 fclose($fh);
1198 } else {
1199 csv_edihist_log("edih_archive_main: $ft claims csv does not exist");
1200 continue;
1205 if (!$iscpf || !$iscpc) {
1206 csv_edihist_log("edih_archive_csv_old: copy to tmp dir failed for csv file $ft");
1207 $out_html = "Archive temporary files operation failed ... aborting <br />" .PHP_EOL;
1208 // need to call archive_undo()
1209 $out_html .= edih_archive_undo();
1210 return $out_html;
1213 // get the csv data
1214 $csv_files_ar = csv_assoc_array($ft, 'file');
1215 $csv_claims_ar = csv_assoc_array($ft, 'claim');
1216 // get filenames to be archived
1217 $fn_ar = array();
1218 $tp_ar = array();
1219 $fn_ar = edih_archive_filenames($csv_files_ar, $archive_date);
1220 if (count($fn_ar)) {
1221 // add type to list
1222 $tp_ar[] = $ft;
1223 // get the old and new csv row arrays for files_csv
1224 $arch_new = edih_archive_csv_split($csv_files_ar, $fn_ar);
1225 if ($arch_new) {
1226 if (isset($arch_new['keep'])) {
1227 // write the new
1228 $frws = edih_archive_rewrite_csv($fn_files_keep, $fh_ar, $arch_new['keep']);
1229 $out_html .= "type $ft keep files_csv file with $frws rows<br>";
1232 if (isset($arch_new['arch'])) {
1233 // write the old
1234 $frws2 = edih_archive_rewrite_csv($fn_files_arch, $fh_ar, $arch_new['arch']);
1235 $out_html .= "type $ft archive files_csv file with $frws2 rows<br>";
1237 } else {
1238 $out_html .= "type $ft error creating new files_csv tables";
1241 // repeat for claims_csv
1242 $arch_new = edih_archive_csv_split($csv_claims_ar, $fn_ar);
1243 if ($arch_new) {
1244 if (isset($arch_new['keep'])) {
1245 // write the new
1246 $crws = edih_archive_rewrite_csv($fn_claims_keep, $ch_ar, $arch_new['keep']);
1247 $out_html .= "type $ft keep claims_csv file with $crws rows<br>";
1250 if (isset($arch_new['arch'])) {
1251 // write the old
1252 $crws = edih_archive_rewrite_csv($fn_claims_arch, $ch_ar, $arch_new['arch']);
1253 $out_html .= "type $ft archive claims_csv file with $crws rows<br>";
1255 } else {
1256 $out_html .= "type $ft error creating new claims csv tables<br>";
1259 // now the csv_records are in files
1260 // file records in $fn_files_arch $fn_files_keep
1261 // claim records in $fn_claims_arch $fn_claims_new
1263 // create a zip archive
1264 // zf is result of zipArchive functions true or false
1265 $zf = edih_archive_create_zip($p, $fn_ar, $archive_date, $arch_fn);
1267 // delete archived files
1268 if ($zf) {
1269 // replace the csv files
1270 $rn = rename($fn_files_keep, $files_csv);
1271 if ($rn) {
1272 csv_edihist_log("edih_archive_main: replaced $files_csv");
1273 } else {
1274 csv_edihist_log("edih_archive_main: error trying to replace $files_csv");
1277 $rn = rename($fn_claims_keep, $claims_csv);
1278 if ($rn) {
1279 csv_edihist_log("edih_archive_main: replaced $claims_csv");
1280 } else {
1281 csv_edihist_log("edih_archive_main: error trying to replace $claims_csv");
1284 // move archive files to tmpdir/ftype
1285 // $rndir = mkdir($tmpdir.DS.$fdir);
1286 csv_edihist_log("edih_archive_main: $ft now moving old files ");
1287 $del = edih_archive_move_old($p, $fn_ar);
1288 $out_html .= "Archive moved $del $ft type files<br>".PHP_EOL;
1290 } else {
1291 csv_edihist_log("edih_archive_main: type $ft error in creating archive");
1292 $out_html .= "type $ft error in creating archive<br>" .PHP_EOL;
1294 } else {
1295 csv_edihist_log("edih_archive_main: search found no type $ft files older than $period");
1296 $out_html .= "Archive: type $ft archive found no files older than $period<br>" .PHP_EOL;
1298 } // end foreach($params as $k=>$p)
1300 if (is_file($tmp_dir.DS.$arch_fn)) {
1301 $rn = rename($tmp_dir.DS.$arch_fn, $archive_dir.DS.$arch_fn);
1302 $cm = chmod($archive_dir.DS.$arch_fn, 0400);
1303 if ($rn) {
1304 csv_edihist_log("edih_archive_main: moved $arch_fn to archive directory");
1305 } else {
1306 csv_edihist_log("edih_archive_main: error moving archive file $arch_fn");
1307 $out_html .= "<p>edih_archive_main: error moving archive file $arch_fn</p>";
1309 } else {
1310 csv_edihist_log("edih_archive_main: is_file false $tmp_dir.DS.$arch_fn");
1313 //edih_archive_cleanup($arch_fn, $tp_ar);
1314 $is_clear = csv_clear_tmpdir();
1315 if ($is_clear) {
1316 $out_html .= "Archive: temporary files removed. Process complete.<br>" .PHP_EOL;
1317 } else {
1318 $out_html .= "Archive: still some files in /history/tmp/. Process complete.<br>" .PHP_EOL;
1322 return $out_html;