Fix for issue #291
[openemr.git] / library / edihistory / edih_archive.php
blobc2484d904a7994641fdcee38883aa2e72eff2dcc
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 = '') {
54 $str_html = '';
55 $chkdt = '';
56 $strdt = '';
57 // edih_archive_date returns empty string if no period
58 $tper = edih_archive_date($period);
59 $chkdt = ($tper) ? $tper : 'None';
60 $strdt = ($tper) ? substr($chkdt,0,4).'-'.substr($chkdt,4,2).'-'.substr($chkdt,6,2) : 'None';
62 csv_edihist_log("edih_archive_report: creating archive report with date $chkdt");
64 $bdir = csv_edih_basedir();
65 $params = csv_parameters('ALL');
66 if ( !is_array($params) && count($params) ) {
67 csv_edihist_log("edih_archive_report: invalid csv_parameters");
68 return "<p>There was an error creating the report.</p>";
71 $str_html .= "<h3>Report on edi files using archive date $strdt</h3>".PHP_EOL;
72 foreach($params as $key=>$param) {
73 $old_ct = 0;
74 $clm_ct = 0;
75 $dir_ct = 0;
76 $dir_sz = 0;
77 $row_ct = 0;
78 $dir_kb = '';
79 $subdir_ct = 0;
80 $fntp = '';
81 $fntp_ct = 0;
83 $tp = $param['type'];
84 $fdir = $param['directory'];
86 if ( is_dir($fdir) ) {
87 $dir_ar = scandir($fdir);
88 if (is_array($dir_ar) && ((count($dir_ar)-2) > 0)) {
89 $str_html .= "<H3><em>Type</em> ".$tp."</H3>".PHP_EOL;
90 $str_html .= "<ul>".PHP_EOL;
91 $dir_ct = count($dir_ar);
92 foreach($dir_ar as $fn) {
93 if ($fn == 'README.txt') { $dir_ct--; continue; }
94 if (substr($fn, 0, 1) == '.') { $dir_ct--; continue; }
95 if (is_dir($fdir.DS.$fn)) { $subdir_ct++; $dir_ct--; continue; }
96 $dir_sz += filesize($fdir.DS.$fn);
99 $csv_ar = csv_assoc_array($tp, 'file');
100 $row_ct = (is_array($csv_ar)) ? count($csv_ar) : 0;
101 if ($row_ct) {
102 foreach($csv_ar as $row) {
103 // tally amount of claim transactions in the files
104 if ($tp == 'f837') { $clm_ct += $row['Claim_ct']; }
105 if ($tp == 'f277') { $clm_ct += ($row['Accept'] + $row['Reject']); }
106 if ($tp == 'f835') { $clm_ct += $row['Claim_ct']; }
107 // check for duplicates
108 // here assume that files with multiple rows are consecutive
109 if ($fntp !== $row['FileName']) {
110 $fntp = $row['FileName'];
111 // count files that would be archived
112 if (($chkdt != 'None') && strcmp($row['Date'], $chkdt) < 0) { $old_ct++; }
113 $fntp_ct++;
117 $dir_kb = csv_convert_bytes($dir_sz);
118 } else {
119 csv_edihist_log("edih_archive_report: error $tp file count $dir_ct with csv rows $row_ct");
122 $mis = ($fntp_ct == $dir_ct ) ? "(check/eft count or ISA--IEA count)" : "(mismatch csv $fntp_ct dir $dir_ct)";
124 if ($tp == 'f837') {
125 $str_html .= "<li><em>Note:</em> 837 Claims files are not archived </li>".PHP_EOL;
128 $str_html .= "<li>files directory has $dir_ct files, using $dir_kb </li>".PHP_EOL;
129 $str_html .= ($subdir_ct && ($tp != 'f837')) ? "<li> <em>warning</em> found $subdir_ct sub-directories</li>".PHP_EOL : "";
130 $str_html .= "<li>files csv table has $row_ct rows $mis</li>".PHP_EOL;
131 $str_html .= ($clm_ct) ? "<li>there are $clm_ct claim transactions counted</li>".PHP_EOL : "";
132 $str_html .= ($old_ct) ? "<li>Archive date $strdt would archive $old_ct files </li>".PHP_EOL : "";
133 $str_html .= "</ul>".PHP_EOL;
134 } else {
135 $str_html .= "<p><em>Type</em> <b>$tp</b> <br> -- empty $tp file directory</p>".PHP_EOL;
137 } else {
138 $str_html .= "<p><em>warning</em> <b>$tp</b> file directory does not exist</p>".PHP_EOL;
141 $str_html .= "<p>Report end</p>".PHP_EOL;
143 return $str_html;
148 * Format the date used in comparisons
150 * @param string period from select list e.g. 6m, 12m
152 * @return string archive date in CCYYMMDD format
154 function edih_archive_date($period) {
156 $dtpd2 = '';
157 if (!$period) { return $dtpd2; }
158 $is_period = preg_match('/\d{1,2}(?=m)/', $period, $matches);
160 if ( count($matches) ) {
161 $gtdt = getdate();
163 if ( strpos($period, 'm') ) {
164 // take the number part of 'period'
165 // so modstr will be '-N month'
166 $modstr = '-'.$matches[0].' month';
167 $dtstr1 = $gtdt['mon'].'/01/'.$gtdt['year'];
168 } elseif ( strpos($period, 'y') ) {
169 $modstr = '-'.$matches[0].' year';
170 $dtstr1 = $gtdt['mon'].'/01/'.$gtdt['year'];
171 } else {
172 csv_edihist_log("edih_archive_date: incorrect date period $period");
173 return false;
176 if ($modstr) {
177 $dtpd1 = date_create( $dtstr1 );
178 $dtm = date_modify($dtpd1, $modstr);
179 $dtpd2 = $dtm->format('Ymd');
180 } else {
181 csv_edihist_log("edih_archive_date: failed to parse $period");
182 return false;
184 } else {
185 csv_edihist_log("edih_archive_date: invalid argment $period");
186 return false;
188 // the testing date in CCYYMMDD format
189 return $dtpd2;
194 * Create an array of file names to be archived
195 * The 'Date' column in the files_[type].csv file
196 * is compared to the archive date. If the date is less
197 * than the archive date, the "FileName' value is copied
199 * @param array csv file rows array
200 * @param string archive date in CCYYMMDD format
202 * @return array array[i] = filename
204 function edih_archive_filenames($csv_ar, $archive_date) {
206 if ( $archive_date && strlen($archive_date) == 8 && is_numeric($archive_date) ) {
207 $testdate = (string)$archive_date;
208 } else {
209 csv_edihist_log("edih_archive_filenames: invalid archive date $archive_date");
210 return false;
213 if ( !is_array($csv_ar) || !count($csv_ar) ) {
214 csv_edihist_log("edih_archive_filenames: failed to get csv file array $file_type");
215 return false;
218 $fn_ar = array();
219 foreach($csv_ar as $row) {
220 if ( strcmp($row['Date'], $archive_date) < 0 ) {
221 $fn_ar[] = $row['FileName'];
224 $ret_ar = (count($fn_ar)) ? array_values(array_unique($fn_ar)) : $fn_ar;
226 return $ret_ar;
230 * Create a new csv array by omitting rows which reference
231 * a file name that is to be archived
233 * @uses csv_file_type()
234 * @uses csv_assoc_array()
236 * @param string the file type
237 * @param string the csv type file or claim
238 * @param array the array of archived file names and retained file names
240 * @return array
242 function edih_archive_csv_split($csv_ar, $filename_array) {
244 if ( !is_array($filename_array) || !count($filename_array) ) {
245 csv_edihist_log('csv_archive_table; invalid filename array');
246 return false;
249 if (is_array($csv_ar) && count($csv_ar)) {
250 csv_edihist_log("edih_archive_csv_split: csv rows ".count($csv_ar)." old files ".count($filename_array));
251 } else {
252 csv_edihist_log("edih_archive_csv_split: failed to get csv file array");
253 return false;
256 // if the to be archived file name is in the row,
257 // do not copy it to the new csv array
258 $arch_ar = array();
259 $arch_ar['arch'] = array();
260 $arch_ar['keep'] = array();
262 foreach($csv_ar as $row) {
263 if ( in_array($row['FileName'], $filename_array) ) {
264 $arch_ar['arch'][] = $row;
265 } else {
266 $arch_ar['keep'][] = $row;
270 csv_edihist_log("edih_archive_csv_split: 'arch' array rows ".count($arch_ar['arch']));
271 csv_edihist_log("edih_archive_csv_split: 'keep' array rows ".count($arch_ar['keep']));
273 return $arch_ar;
278 * Creates a zip archive of the files in the $filename_ar array and
279 * returns the path/name of the archive or FALSE on error
281 * @param array $parameters array for file type from csv_parameters
282 * @param array $filename_ar array of filenames to be archived
283 * @param string $archive_date date of archive to be incorporated in archive file name
285 * @return bool result of zipArchive functions
287 function edih_archive_create_zip($parameters, $filename_ar, $archive_date, $archive_filename) {
288 // we deal with possible maximum files issues by chunking the $fn_ar array
290 $ft = $parameters['type'];
291 $fdir = $parameters['directory'];
292 $tmp_dir = csv_edih_tmpdir();
293 // archive csv rows -- same name as from edih_archive_main
294 // $fn_files_arch = $tmp_dir.DS.'arch_'.basename($files_csv);
295 $files_csv_arch = 'arch_'.basename($parameters['files_csv']);
296 // $fn_claims_arch = $tmp_dir.DS.'arch_'.basename($claim_csv);
297 $claims_csv_arch = 'arch_'.basename($parameters['claims_csv']);
299 $f_max = 200;
300 $fn_ar2 = array();
301 // to handle possibility of more than 200 files in the archive
302 // use the 'chunk' method
303 if ( count($filename_ar) > $f_max) {
304 $fn_ar2 = array_chunk($filename_ar, $f_max);
305 } else {
306 $fn_ar2[] = $filename_ar;
309 $zip_name = $tmp_dir.DS.$archive_filename;
310 csv_edihist_log("edih_archive_create_zip: using $zip_name");
312 $zip_obj = new ZipArchive();
313 csv_edihist_log("edih_archive_create_zip: now opening archive $archive_filename");
314 if (is_file($zip_name) ) {
315 $isOK = $zip_obj->open($zip_name, ZIPARCHIVE::CHECKCONS);
316 if ($isOK) {
317 if ($zip_obj->locateName($ft) === false) {
318 $isOK = $zip_obj->addEmptyDir($ft);
319 if (!$isOK) {
320 csv_edihist_log("edih_archive_create_zip: adding $ft ZipArchive error $msg");
321 return $isOK;
324 } else {
325 $msg = $zip_obj->getStatusString();
326 csv_edihist_log("edih_archive_create_zip: $ft ZipArchive error $msg");
327 return $isOK;
329 } else {
330 $isOK = $zip_obj->open($zip_name, ZIPARCHIVE::CREATE);
331 $isOK = $zip_obj->addEmptyDir('csv');
332 $isOK = $zip_obj->addEmptyDir($ft);
333 $zip_obj->setArchiveComment("edi_history archive prior to $archive_date");
335 // we are working with the open archive
336 // now add the old csv files to the archive
337 if ( is_file($tmp_dir.DS.$files_csv_arch) ) {
338 csv_edihist_log("edih_archive_create_zip: now adding $files_csv_arch to archive");
339 $isOK = $zip_obj->addFile($tmp_dir.DS.$files_csv_arch, 'csv'.DS.$files_csv_arch);
341 if ( is_file($tmp_dir.DS.$claims_csv_arch) ) {
342 csv_edihist_log("edih_archive_create_zip: now adding $claims_csv_arch to archive");
343 $isOK = $zip_obj->addFile($tmp_dir.DS.$claims_csv_arch, 'csv'.DS.$claims_csv_arch);
345 // close zip archive
346 csv_edihist_log("edih_archive_create_zip: now closing archive");
347 $isOK = $zip_obj->close();
348 if ($isOK !== true) {
349 $msg = $zip_obj->getStatusString();
350 csv_edihist_log("edih_archive_create_zip: $ft ZipArchive error $msg");
351 return $isOK;
353 // $fn_ar2[i][j]
354 csv_edihist_log("edih_archive_create_zip: with file name groups ".count($fn_ar2));
355 foreach($fn_ar2 as $fnz) {
356 // reopen the zip archive on each loop so the open file count is controlled
357 if (is_file($zip_name) ) {
358 csv_edihist_log("edih_archive_create_zip: now opening archive");
359 $isOK = $zip_obj->open($zip_name, ZIPARCHIVE::CHECKCONS);
362 if ($isOK === true) {
363 // we are working with the open archive
364 // now add the old x12 files to the archive
365 csv_edihist_log("edih_archive_create_zip: now adding $ft files to archive");
366 foreach($fnz as $fz) {
367 if ($fz == '.' || $fz == '..') { continue; }
368 if (is_file($fdir.DS.$fz) && is_readable($fdir.DS.$fz) ) {
369 $isOK = $zip_obj->addFile($fdir.DS.$fz, $ft.DS.$fz);
370 } else {
371 // possible that file is in csv table, but not in directory?
372 $msg = $zip_obj->getStatusString();
373 csv_edihist_log("edih_archive_create_zip: error adding file $fz zipArchive: $msg");
375 } // end foreach($fnz as $fz)
376 // close zip object for next iteration of chunked array
377 csv_edihist_log("edih_archive_create_zip: now closing archive");
378 $isOK = $zip_obj->close();
379 // errors on close would be non-existing file added or something else
380 if ($isOK !== true) {
381 $msg = $zip_obj->getStatusString();
382 csv_edihist_log("edih_archive_create_zip: $ft ZipArchive error $msg");
384 return $isOK;
386 } else {
387 // ZipArchive open() failed -- try to get the error message and return false
388 $msg = $zip_obj->getStatusString();
389 csv_edihist_log("edih_archive_create_zip: $ft ZipArchive failed $msg");
390 return $isOK;
391 }// end if ($isOK)
394 } // end foreach($fn_ar2 as $fnz)
396 return $isOK;
400 * Archived files have been included in archive file
401 * so we move the files to the archive tmp directory, for later deletion
403 * @param array parameters array for type
404 * @param array filename array
406 * @return int count of moved files
408 function edih_archive_move_old($parameters, $filename_ar) {
410 if ( !is_array($filename_ar) || !count($filename_ar) ) { return false; }
411 if ( !is_array($parameters) || !count($parameters) ) { return false; }
413 clearstatcache(true);
415 $fnct = 0;
416 $fn_ar_ct = count($filename_ar);
417 $ft = $parameters['type'];
418 $fdir = $parameters['directory'];
419 $fdir = realpath($fdir);
420 $rndir = csv_edih_tmpdir().DS.$ft;
422 if (is_dir($fdir)) {
423 csv_edihist_log("edih_archive_delete_old: $ft dir OK");
424 if (is_dir($rndir) || mkdir($rndir)) {
425 $rndir = realpath($rndir);
426 csv_edihist_log("edih_archive_delete_old: $ft move dir OK");
427 $isOK = true;
428 } else {
429 csv_edihist_log("edih_archive_delete_old: $ft move dir error");
430 $isOK = false;
432 } else {
433 csv_edihist_log("edih_archive_delete_old: $ft dir error");
434 $isOK = false;
437 if ($isOK) {
438 csv_edihist_log("edih_archive_delete_old: $ft old file count $fn_ar_ct");
439 foreach($filename_ar as $fn) {
440 // if we have added the file to the archive, remove it from the storage directory
441 // but keep the /history/tmp file copy for now
442 if (is_file($fdir.DS.$fn)) {
443 $isrn = rename($fdir.DS.$fn, $rndir.DS.$fn);
446 if ($isrn) {
447 $fnct++;
448 } else {
449 csv_edihist_log("edih_archive_delete_old: $ft failed to move $fn");
452 } else {
453 csv_edihist_log("edih_archive_delete_old: $ft directory error for files or tmp");
456 return $fnct;
461 * create associative array from archive csv file
463 * @uses edih_archive_csv_array()
465 * @param string
466 * @param string
467 * @param string optional filepath
469 * @return array
471 function edih_archive_csv_array($filetype, $csv_type, $filepath='') {
473 $str_out = '';
474 $csv_ar = array();
475 $tmpdir = csv_edih_tmpdir();
476 $tmpcsv = $tmpdir.DS.'csv';
478 $csvtp = (strpos($csv_type, 'aim')) ? 'claims' : 'files';
480 if (is_file($filepath)) {
481 $csv_arch_path = $filepath;
482 } else {
483 $csv_arch_path = $tmpcsv.DS.'arch_'.$csvtp.'_'.$filetype.'.csv';
486 $ct = 0;
487 $row = 0;
488 $ky = -1;
489 // relies on first row being header or column names
490 if (($fh = fopen($csv_arch_path, "rb")) !== false) {
491 while (($data = fgetcsv($fh, 2048, ",")) !== false) {
492 if ( is_null($data) ) { continue; }
493 if ($row) {
494 for($i=0; $i<$ct; $i++) {
495 $csv_ar[$ky][$h[$i]] = $data[$i];
497 } else {
498 $ct = count($data);
499 $h = $data;
501 $row++;
502 $ky++;
504 fclose($fh);
505 } else {
506 // invalid file path
507 csv_edihist_log('edih_archive_csv_array; invalid file path '.$csv_arch_path);
508 return false;
511 return $csv_ar;
515 * combine the csv file in the archive with the current csv file
517 * @uses edih_archive_csv_array()
519 * @param string
520 * @param string
522 * @return string
524 function edih_archive_csv_combine($filetype, $csvtype) {
526 $str_out = '';
527 $hdr_ar = array();
528 $bdir = csv_edih_basedir();
529 $tmpdir = csv_edih_tmpdir();
530 $tmpcsv = $tmpdir.DS.'csv';
532 $csvtp = (strpos($csvtype, 'aim')) ? 'claims' : 'files';
533 $csv_arch_file = $tmpcsv.DS.'arch_'.$csvtp.'_'.$filetype.'.csv';
534 $csv_new_file = $tmpdir.DS.'cmb_'.$csvtp.'_'.$filetype.'.csv';
536 // arrays used to eliminate duplicate rows
537 $dup_ar = $dup_unique = $dup_keys = array();
538 // combine files by combining arrays and writing a tmp file
539 // get the present csv file contents
540 $car1 = csv_assoc_array($filetype, $csvtp);
541 // get the archived csv contents
542 if (is_file($csv_arch_file)) {
543 $car2 = edih_archive_csv_array($filetype, $csvtp, $csv_arch_file);
545 // possibility of empty arrays if no data rows in a csv file
546 $hdrc1 = (is_array($car1) && count($car1)) ? array_keys($car1[0]) : array();
547 $hdrc2 = (is_array($car2) && count($car2)) ? array_keys($car2[0]) : array();
548 if (count($hdrc1) && ($hdrc1 === $hdrc2)) {
549 $hdr_ar = $hdrc1;
550 } elseif (empty($hdrc1) && count($hdrc2)) {
551 $hdr_ar = $hdrc2;
552 } elseif (empty($hdrc2) && count($hdrc1)) {
553 $hdr_ar = $hdrc1;
554 } else {
555 // array mismatch error (impossible?)
556 csv_edihist_log("edih_archive_csv_combine: $filetype $csvtp array header mismatch");
557 // just use the current csv file
558 $hdr_ar = csv_table_header($filetype, $csvtp);
559 $car_cmb_unique = $car1;
560 // debug
561 if (count($hdrc1)) {
562 $dbg_str = '';
563 foreach($hdrc1 as $h) { $dbg_str .= $h.' '; }
564 csv_edihist_log("edih_archive_csv_combine: $csvtp car1 header $dbg_str");
565 } else {
566 csv_edihist_log("edih_archive_csv_combine: $csvtp car1 header empty");
568 if (count($hdrc2)) {
569 $dbg_str = '';
570 foreach($hdrc2 as $h) { $dbg_str .= $h.' '; }
571 csv_edihist_log("edih_archive_csv_combine: $csvtp car2 header $dbg_str");
572 } else {
573 csv_edihist_log("edih_archive_csv_combine: $csvtp car2 header empty");
577 // if the arrays checked out
578 if (!isset($car_cmb_unique)) {
579 // if we have archive csv rows
580 if (is_array($car1) && is_array($car2)) {
581 // put the archive rows first
582 $car_cmb = array_merge($car2, $car1);
583 // now eliminate duplicates
584 if ($csvtp == 'files') {
585 if ($filetype == 'f835') {
586 $ky = 'Trace';
587 } else {
588 $ky = 'Control';
590 // array_column() php v5.5
591 foreach($car_cmb as $idx=>$row) {
592 $dup_ar[$idx] = $row[$ky];
594 csv_edihist_log("edih_archive_csv_combine: $csvtp array row count ".count($dup_ar));
595 $dup_unique = array_unique($dup_ar);
596 $dup_keys = array_keys($dup_unique);
597 csv_edihist_log("edih_archive_csv_combine: $csvtp index row count ".count($dup_keys));
598 foreach($dup_keys as $k) {
599 $car_cmb_unique[] = $car_cmb[$k];
601 csv_edihist_log("edih_archive_csv_combine: $csvtp combined row count ".count($car_cmb_unique));
602 } elseif ($csvtp == 'claims') {
603 $ct = count($hdr_ar);
604 $ftxt = $csvtp.' array'.PHP_EOL;
605 foreach($car_cmb as $idx=>$row) {
606 $r_str = '';
607 for($i=0; $i<$ct; $i++) {
608 $r_str .= $row[$hdr_ar[$i]];
610 $dup_ar[$idx] = $r_str;
611 $ftxt .= $r_str.PHP_EOL;
613 csv_edihist_log("edih_archive_csv_combine: $csvtp array row count ".count($dup_ar));
614 file_put_contents($tmpdir.DS.'archive'.DS.'claimstr.txt', $ftxt);
616 $dup_unique = array_unique($dup_ar);
617 $dup_keys = array_keys($dup_unique);
618 csv_edihist_log("edih_archive_csv_combine: $csvtp index row count ".count($dup_keys));
619 foreach($dup_keys as $k) {
620 $car_cmb_unique[] = $car_cmb[$k];
622 csv_edihist_log("edih_archive_csv_combine: $csvtp combined row count ".count($car_cmb_unique));
623 } else {
624 $car_cmb_unique = $car_cmb;
626 } else {
627 csv_edihist_log("edih_archive_csv_combine: array keys mismatch $filetype");
630 } else {
631 csv_edihist_log("edih_archive_csv_combine: error reading archived csv ".$csvtp."_".$filetype.".csv");
633 $rwct = 0;
634 $fh = fopen($csv_new_file, 'wb');
635 if ($fh) {
636 fputcsv($fh, $hdr_ar);
637 $rwct++;
639 foreach ($car_cmb_unique as $row) {
640 fputcsv($fh, $row);
641 $rwct++;
643 // close new csv file
644 fclose($fh);
645 } else {
646 csv_edihist_log("edih_archive_csv_combine: failed to open $filetype new csv file");
648 return $rwct;
652 * Unpack an existing archive and restore it to current csv records
653 * and replace the files in the respective directories
655 * @uses edih_archive_csv_combine
656 * @param string
658 * @return string
660 function edih_archive_restore($archive_name) {
662 $str_out = '';
663 $bdir = csv_edih_basedir();
664 $tmpdir = csv_edih_tmpdir();
665 $archdir = $bdir.DS.'archive';
667 if (is_file($archdir.DS.$archive_name)) {
668 $arch = realpath($archdir.DS.$archive_name);
669 $str_out .= "Archive: restoring $archive_name<br>";
670 csv_edihist_log("edih_archive_restore: restoring $archive_name");
671 } else {
672 $str_out = "Archive: restore archive bad file name $archive_name <br>";
673 csv_edihist_log("edih_archive_restore: restore archive bad file name $archive_name");
674 return $str_out;
677 $zip_obj = new ZipArchive();
678 // open archive (ZIPARCHIVE::CHECKCONS the ZIPARCHIVE::CREATE is supposedly necessary for microsoft)
679 //$res = $zip_obj->open($arch, ZIPARCHIVE::CHECKCONS);
680 if ($zip_obj->open($arch, ZIPARCHIVE::CHECKCONS) === true) {
681 $f_ct = $zip_obj->numFiles;
682 $str_out .= "Extracting $f_ct items from $archive_name <br>";
683 csv_edihist_log("edih_archive_restore: Extracting $f_ct items from $archive_name");
684 $isOK = $zip_obj->extractTo($tmpdir);
685 if (!$isOK) {
686 $msg = $zip_obj->getStatusString();
687 csv_edihist_log("edih_archive_restore: error extracting archive");
688 $str_out .= "Archive: error extracting archive $archive_name <br>";
689 $str_out .= "zipArchive: $msg <br>";
690 return $str_out;
692 } else {
693 $msg = $zip_obj->getStatusString();
694 csv_edihist_log("edih_archive_restore: error opening archive");
695 $str_out .= "Archive: error opening archive <br>".PHP_EOL;
696 $str_out .= "zipArchive: $msg <br>";
697 return $str_out;
699 // now traverse the tmpdir and replace things
700 // we should have tmp/csv/files_[ftype].csv claims_[ftype].csv
701 // tmp/[ftype]/x12_filenames
702 $arch_ar = scandir($tmpdir);
703 $tpstr = '';
704 foreach($arch_ar as $fa) {
705 if ($fa == '.' || $fa == '..') { continue; }
706 if (is_dir($tmpdir.DS.$fa)) {
707 if ($fa == 'csv') { continue; }
708 // if a /history/ftype dir exists
709 if (is_dir($bdir.DS.$fa)) {
710 $type_ar[] = $fa;
711 $tpstr .= "$fa ";
713 } else {
714 continue;
718 csv_edihist_log("edih_archive_restore: types in archive $tpstr");
719 $str_out .= "Archive: types in archive $tpstr <br>".PHP_EOL;
721 foreach($type_ar as $ft) {
722 $str_out .= "Archive: now restoring $ft<br>".PHP_EOL;
723 csv_edihist_log("edih_archive_restore: now restoring $ft");
725 $frows = edih_archive_csv_combine($ft, 'file');
726 csv_edihist_log("edih_archive_restore: files_$ft csv combined rows $frow");
727 $crows = edih_archive_csv_combine($ft, 'claim');
728 csv_edihist_log("edih_archive_restore: claims_$ft csv combined rows $frow");
730 $file_ar = scandir($tmpdir.DS.$ft);
731 foreach($file_ar as $fn) {
732 if ($fn == '.' || $fn == '..') { continue; }
733 if (is_file($tmpdir.DS.$ft.DS.$fn)) {
734 $rn = rename($tmpdir.DS.$ft.DS.$fn, $bdir.DS.$ft.DS.$fn);
735 if (!$rn) {
736 $str_out .= " -- error restoring ".$ft.DS.$fn."<br>".PHP_EOL;
737 csv_edihist_log("edih_archive_restore: error restoring ".$ft.DS.$fn);
741 // this will catch the csv files for the particulat type
742 $str_out .= "Archive: now replacing csv tables for $ft<br>".PHP_EOL;
743 csv_edihist_log("edih_archive_restore: now replacing csv tables for $ft");
745 $rnf = rename($tmpdir.DS.'cmb_files_'.$ft.'.csv', $bdir.DS.'csv'.DS.'files_'.$ft.'.csv');
746 $rnc = rename($tmpdir.DS.'cmb_claims_'.$ft.'.csv', $bdir.DS.'csv'.DS.'claims_'.$ft.'.csv');
747 $str_out .= ($rnf) ? "" : " -- error restoring files_$ft.csv <br>".PHP_EOL;
748 $str_out .= ($rnc) ? "" : " -- error restoring claims_$ft.csv <br>".PHP_EOL;
751 csv_edihist_log("edih_archive_restore: now removing archive file");
752 $str_out .= "Archive: now removing archive file <br>".PHP_EOL;
753 $rm = unlink($arch);
754 if (!$rm) {
755 csv_edihist_log("edih_archive_restore: error removing $archdir.DS.$archive_name");
756 $str_out .= ($rnf) ? "" : " -- error removing $archdir.DS.$archive_name".PHP_EOL;
759 //edih_archive_cleanup($arch_fn, $tp_ar);
760 csv_edihist_log("edih_archive_restore: now removing temporary files");
761 $str_out .= "Archive: now removing temporary files <br>".PHP_EOL;
764 $is_clear = csv_clear_tmpdir();
765 if ($is_clear) {
766 $str_out .= "Archive: temporary files removed. Process complete.<br>" .PHP_EOL;
767 } else {
768 $str_out .= "Archive: still some files in /history/tmp/. Process complete.<br>" .PHP_EOL;
771 return $str_out;
776 * restores files from the tmp dir if the archive process needs to be aborted
778 * @uses csv_edih_basedir()
779 * @uses csv_edih_tmpdir()
780 * @uses csv_parameters()
782 * @return string
784 function edih_archive_undo() {
786 // archive process creates files in /history/tmp
787 // /tmp/old_files_[type].csv copy of pre-archive csv record
788 // /tmp/old_claims_[type].csv copy of pre-archive csv record
789 // /tmp/new_files_[type].csv csv record of non-archived files
790 // /tmp/new_claims_[type].csv csv record of non-archived files
791 // /tmp/arch_files_[type].csv csv record of archived files (to be put in zip file)
792 // /tmp/arch_claims_[type].csv csv record of archived files (to be put in zip file)
793 // /tmp/[type]/filename_to_be_archived all the archived files for [type]
795 $str_out = '';
796 $bdir = csv_edih_basedir();
797 $tmpdir = csv_edih_tmpdir();
798 $archdir = $bdir.DS.'archive';
800 $params = csv_parameters("ALL");
801 $types_ar = array_keys($params);
803 csv_edihist_log("edih_archive_undo: restoring prior csv files files");
804 foreach($types_ar as $ft) {
805 if (is_file($tmpdir.DS.'old_files_'.$ft.'.csv')) {
806 $rn = rename($tmpdir.DS.'old_files_'.$ft.'.csv', $bdir.DS.'csv'.DS.'files_'.$ft.'.csv');
807 if ($rn) {
808 csv_edihist_log("edih_archive_undo: restored prior files_$ft ");
809 } else {
810 csv_edihist_log("edih_archive_undo: restore failed for prior files_$ft ");
813 if (is_file($tmpdir.DS.'old_claims_'.$ft.'.csv')) {
814 $rn = rename($tmpdir.DS.'old_claims_'.$ft.'.csv', $bdir.DS.'csv'.DS.'claims_'.$ft.'.csv');
815 if ($rn) {
816 csv_edihist_log("edih_archive_undo: restored prior claimss_$ft ");
817 } else {
818 csv_edihist_log("edih_archive_undo: restore failed for prior claims_$ft ");
822 $arch_ar = scandir($tmpdir);
823 foreach($arch_ar as $fa) {
824 if ($fa == "." && $fa == "..") { continue; }
825 if (is_dir($tmpdir.DS.$fa)) {
826 if (in_array($fa, $types_ar)) {
827 $fpath = $params[$fa]['directory'];
828 if ($dh = opendir($tmpdir.DS.$fa)) {
829 $str_out .= "Archive: undo restoring $fa files<br>".PHP_EOL;
830 csv_edihist_log("edih_archive_undo: restoring $fa files");
831 while (false !== ($entry = readdir($dh))) {
832 if ($entry != "." && $entry != "..") {
833 if (is_file($fpath.DS.$entry)) {
834 // file was not moved
835 } else {
836 rename($tmpdir.DS.$fa.DS.$entry, $fpath.DS.$entry);
840 closedir($dh);
845 return $str_out;
850 * After the archive is created, the csv record needs to be re-written so the archived
851 * files are not in the csv file and hence, not searched for.
853 * @uses csv_table_header()
855 * @param string $csv_path the tmp csv file path is expected
856 * @param array $row_array the data rows to be written (an associative array)
858 * @return integer count the rows written
860 function edih_archive_rewrite_csv($csv_path, $csv_keys, $row_array) {
861 // @param string $csv_path -- the tmp csv file path is expected
862 // @param array $heading_ar -- the column heading for the csv file
863 // @param array $row_array -- the data rows to be written
865 // count characters written -- returned by fputcsv
866 $ocwct = 0;
867 $rwct = 0;
869 if (is_array($row_array)) {
870 csv_edihist_log("edih_archive_rewrite_csv: row array count ".count($row_array));
871 } else {
872 csv_edihist_log("edih_archive_rewrite_csv: row array not array");
875 if (is_array($row_array) && is_array($csv_keys)) {
876 if (count($csv_keys)) {
877 $h_ar = $csv_keys;
879 } else {
880 csv_edihist_log("edih_archive_rewrite_csv: invalid row array");
881 return $rwct;
883 //$csv_path should end with /history/tmp/[arch|keep]_[files|claims]_[type].csv
884 // with 'w' flag, place the file pointer at the beginning of the file
885 // and truncate the file to zero length.
886 // If the file does not exist, attempt to create it.
887 $fh3 = fopen($csv_path, 'wb');
888 if ($fh3) {
889 // write the heading row first
890 $ocwct += fputcsv($fh3, $h_ar);
891 // wrote heading, now add rows
892 foreach($row_array as $row) {
893 $ocwct += fputcsv($fh3, $row);
894 $rwct++;
896 fclose($fh3);
897 csv_edihist_log("edih_archive_rewrite_csv: wrote ".count($row_array)." rows to ".basename($csv_path));
898 } else {
899 csv_edihist_log("edih_archive_rewrite_csv: failed to open $csv_path");
901 return $rwct;
906 * cleanup archived files after archive created
908 * @param string name of archive file
909 * @param array array of types included in archive
911 * @return string
913 function edih_archive_cleanup($archivename, $types_ar) {
915 $str_out = '';
917 if (is_array($types_ar) && count($types_ar)) {
918 $tdirs = $types_ar;
919 } else {
920 csv_edihist_log("edih_archive_cleanup: no types in file types list");
921 $str_out = "no types in file types list".PHP_EOL;
922 return $str_out;
924 $bdir = csv_edih_basedir();
925 $tmpdir = csv_edih_tmpdir();
926 $archivedir = $bdir.DS.'archive';
927 $fct = 0;
928 // move archive file to archive directory
929 csv_edihist_log("edih_archive_cleanup: now clearing temporary files");
930 $str_out .= "Archive: now clearing temporary files<br>".PHP_EOL;
931 // delete archived files from edih tmp dir
932 foreach($tdirs as $td) {
933 csv_edihist_log("edih_archive_cleanup: cleaning up for $td");
934 if (is_dir($tmpdir.DS.$td)) {
935 $fn_ar = scandir($tmpdir.DS.$td);
936 foreach($fn_ar as $fn) {
937 if ($fn == '.' || $fn == '..') { continue; }
938 if (is_file($tmpdir.DS.$td.DS.$fn)) {
939 $ul = unlink($tmpdir.DS.$td.DS.$fn);
940 if (!$ul) {
941 csv_edihist_log("edih_archive_cleanup: error removing file $fn");
942 $str_out .= "<p>edih_archive_cleanup: error removing file ".$td.DS.$fn."</p>";
943 } else {
944 $fct++;
948 // try to remove the now empty directory
949 csv_edihist_log("edih_archive_cleanup: removing tmp $td");
950 rmdir($tmpdir.DS.$td);
952 csv_edihist_log("edih_archive_cleanup: removed $fct files from $td");
954 return $str_out;
959 * The main function in this edih_archive.php script. This function gets the parameters array
960 * from csv_parameters() and calls the archiving functions on each type of file
961 * in the parameters array.
963 * @uses edih_archive_date()
964 * @uses csv_edih_basedir()
965 * @uses csv_parameters()
966 * @uses csv_edih_tmpdir()
967 * @uses csv_table_header()
968 * @uses edih_archive_filenames()
969 * @uses edih_archive_rewrite_csv()
970 * @uses edih_archive_csv_split()
971 * @uses edih_archive_create_zip()
973 * @param string from select drop-down 6m, 12m, 18m, etc
975 * @return string descriptive message in html format
977 function edih_archive_main($period) {
979 $out_html = '';
980 if ($period) {
981 $archive_date = edih_archive_date($period);
982 if ($archive_date) {
983 $archive_dir = csv_edih_basedir().DS.'archive';
984 $tmp_dir = csv_edih_tmpdir();
985 $arch_fn = $archive_date.'_archive.zip';
986 $params = csv_parameters();
987 } else {
988 csv_edihist_log("edih_archive_main: error creating archive date from $period");
989 $out_html = "Error creating archive date from $period<br />" .PHP_EOL;
991 } else {
992 $out_html = "Archive period invalid.<br />" .PHP_EOL;
993 return $out_html;
996 if (is_dir($archive_dir) ) {
997 if (is_file($archive_dir.DS.$arch_fn)) {
998 csv_edihist_log("edih_archive_main: archive file $arch_fn already exists");
999 $out_html = "Archive: archive file $arch_fn already exists<br>" .PHP_EOL;
1000 return $out_html;
1002 } else {
1003 // should have been created at setup
1004 if (!mkdir ($archive_dir, 0755)) {
1005 csv_edihist_log("edih_archive_main: archive directory does not exist");
1006 $out_html = "Archive: archive directory does not exist<br>" .PHP_EOL;
1007 return $out_html;
1011 foreach($params as $k=>$p) {
1013 $ft = $p['type']; // could be $k
1015 if ($ft == 'f837') {
1016 csv_edihist_log("edih_archive_main: 837 Claims files are not archived");
1017 continue;
1019 $fdir = $p['directory'];
1020 $scan = scandir($fdir);
1021 if ( !$scan || count($scan) < 3 ) { continue; }
1023 $files_csv = $p['files_csv'];
1024 $claims_csv = $p['claims_csv'];
1025 $date_col = $p['filedate'];
1026 $fncol = 'FileName';
1028 // create three csv file paths 'old_', 'arch_', and 'keep_'
1029 // files csv temporary names
1030 $fn_files_old = $tmp_dir.DS.'old_'.basename($files_csv);
1031 $fn_files_arch = $tmp_dir.DS.'arch_'.basename($files_csv);
1032 $fn_files_keep = $tmp_dir.DS.'keep_'.basename($files_csv);
1033 // claims csv temporary names
1034 $fn_claims_old = $tmp_dir.DS.'old_'.basename($claims_csv);
1035 $fn_claims_arch = $tmp_dir.DS.'arch_'.basename($claims_csv);
1036 $fn_claims_keep = $tmp_dir.DS.'keep_'.basename($claims_csv);
1037 // table headings
1038 $fh_ar = csv_table_header($ft, 'file');
1039 $ch_ar = csv_table_header($ft, 'claim');
1040 // copy existing csv files -- continue to next type if no files_csv
1041 $iscpc = $iscpf = false;
1042 if (is_file($files_csv)) {
1043 $iscpf = copy ($files_csv, $fn_files_old);
1044 csv_edihist_log("edih_archive_main: copy $ft files csv to tmp dir");
1045 } else {
1046 csv_edihist_log("edih_archive_main: $ft files csv does not exist");
1047 continue;
1049 if (is_file($claims_csv)) {
1050 $iscpc = copy ($claims_csv, $fn_claims_old);
1051 csv_edihist_log("edih_archive_main: copy $ft claims csv to tmp dir");
1052 } else {
1053 if ($ft == 'f997') {
1054 // there may be no 997 type claims records, so create a dummy file
1055 $fh = fopen($fn_claims_old, 'wb');
1056 if ($fh) { fputcsv($fh, $ch_ar); fclose($fh); }
1057 } else {
1058 csv_edihist_log("edih_archive_main: $ft claims csv does not exist");
1059 continue;
1063 if (!$iscpf || !$iscpc) {
1064 csv_edihist_log("edih_archive_csv_old: copy to tmp dir failed for csv file $ft");
1065 $out_html = "Archive temporary files operation failed ... aborting <br />" .PHP_EOL;
1066 // need to call archive_undo()
1067 $out_html .= edih_archive_undo();
1068 return $out_html;
1070 // get the csv data
1071 $csv_files_ar = csv_assoc_array($ft, 'file');
1072 $csv_claims_ar = csv_assoc_array($ft, 'claim');
1073 // get filenames to be archived
1074 $fn_ar = array();
1075 $tp_ar = array();
1076 $fn_ar = edih_archive_filenames($csv_files_ar, $archive_date);
1077 if (count($fn_ar)) {
1078 // add type to list
1079 $tp_ar[] = $ft;
1080 // get the old and new csv row arrays for files_csv
1081 $arch_new = edih_archive_csv_split($csv_files_ar, $fn_ar);
1082 if ($arch_new) {
1083 if (isset($arch_new['keep'])) {
1084 // write the new
1085 $frws = edih_archive_rewrite_csv($fn_files_keep, $fh_ar, $arch_new['keep']);
1086 $out_html .= "type $ft keep files_csv file with $frws rows<br>";
1088 if ( isset($arch_new['arch'])) {
1089 // write the old
1090 $frws2 = edih_archive_rewrite_csv($fn_files_arch, $fh_ar, $arch_new['arch']);
1091 $out_html .= "type $ft archive files_csv file with $frws2 rows<br>";
1093 } else {
1094 $out_html .= "type $ft error creating new files_csv tables";
1096 // repeat for claims_csv
1097 $arch_new = edih_archive_csv_split($csv_claims_ar, $fn_ar);
1098 if ($arch_new) {
1099 if (isset($arch_new['keep'])) {
1100 // write the new
1101 $crws = edih_archive_rewrite_csv($fn_claims_keep, $ch_ar, $arch_new['keep']);
1102 $out_html .= "type $ft keep claims_csv file with $crws rows<br>";
1104 if (isset($arch_new['arch'])) {
1105 // write the old
1106 $crws = edih_archive_rewrite_csv($fn_claims_arch, $ch_ar, $arch_new['arch']);
1107 $out_html .= "type $ft archive claims_csv file with $crws rows<br>";
1109 } else {
1110 $out_html .= "type $ft error creating new claims csv tables<br>";
1112 // now the csv_records are in files
1113 // file records in $fn_files_arch $fn_files_keep
1114 // claim records in $fn_claims_arch $fn_claims_new
1116 // create a zip archive
1117 // zf is result of zipArchive functions true or false
1118 $zf = edih_archive_create_zip($p, $fn_ar, $archive_date, $arch_fn);
1120 // delete archived files
1121 if ($zf) {
1122 // replace the csv files
1123 $rn = rename($fn_files_keep, $files_csv);
1124 if ($rn) {
1125 csv_edihist_log("edih_archive_main: replaced $files_csv");
1126 } else {
1127 csv_edihist_log("edih_archive_main: error trying to replace $files_csv");
1129 $rn = rename($fn_claims_keep, $claims_csv);
1130 if ($rn) {
1131 csv_edihist_log("edih_archive_main: replaced $claims_csv");
1132 } else {
1133 csv_edihist_log("edih_archive_main: error trying to replace $claims_csv");
1135 // move archive files to tmpdir/ftype
1136 // $rndir = mkdir($tmpdir.DS.$fdir);
1137 csv_edihist_log("edih_archive_main: $ft now moving old files ");
1138 $del = edih_archive_move_old($p, $fn_ar);
1139 $out_html .= "Archive moved $del $ft type files<br>".PHP_EOL;
1141 } else {
1142 csv_edihist_log("edih_archive_main: type $ft error in creating archive");
1143 $out_html .= "type $ft error in creating archive<br>" .PHP_EOL;
1146 } else {
1147 csv_edihist_log("edih_archive_main: search found no type $ft files older than $period");
1148 $out_html .= "Archive: type $ft archive found no files older than $period<br>" .PHP_EOL;
1150 } // end foreach($params as $k=>$p)
1152 if (is_file($tmp_dir.DS.$arch_fn)) {
1153 $rn = rename($tmp_dir.DS.$arch_fn, $archive_dir.DS.$arch_fn);
1154 $cm = chmod($archive_dir.DS.$arch_fn, 0400);
1155 if ($rn) {
1156 csv_edihist_log("edih_archive_main: moved $arch_fn to archive directory");
1157 } else {
1158 csv_edihist_log("edih_archive_main: error moving archive file $arch_fn");
1159 $out_html .= "<p>edih_archive_main: error moving archive file $arch_fn</p>";
1161 } else {
1162 csv_edihist_log("edih_archive_main: is_file false $tmp_dir.DS.$arch_fn");
1164 //edih_archive_cleanup($arch_fn, $tp_ar);
1165 $is_clear = csv_clear_tmpdir();
1166 if ($is_clear) {
1167 $out_html .= "Archive: temporary files removed. Process complete.<br>" .PHP_EOL;
1168 } else {
1169 $out_html .= "Archive: still some files in /history/tmp/. Process complete.<br>" .PHP_EOL;
1172 return $out_html;