Third step in refactoring/integrating the amc rules for MU2.
[openemr.git] / library / classes / rulesets / Amc / library / AbstractAmcReport.php
blob4101f96e913a6f71f2e5b9dedb81767742d2f52f
1 <?php
2 /**
3 * AbstractAmcReport class
5 * Copyright (C) 2011 Ken Chapple <ken@mi-squared.com>
6 * Copyright (C) 2015 Brady Miller <brady@sparmy.com>
8 * LICENSE: This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 3
11 * of the License, or (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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>;.
19 * @package OpenEMR
20 * @author Ken Chapple <ken@mi-squared.com>
21 * @author Brady Miller <brady@sparmy.com>
22 * @link http://www.open-emr.org
25 require_once( 'AmcFilterIF.php' );
26 require_once( dirname(__FILE__)."/../../../../clinical_rules.php" );
27 require_once( dirname(__FILE__)."/../../../../amc.php" );
29 abstract class AbstractAmcReport implements RsReportIF
31 protected $_amcPopulation;
33 protected $_resultsArray = array();
35 protected $_rowRule;
36 protected $_ruleId;
37 protected $_beginMeasurement;
38 protected $_endMeasurement;
40 protected $_manualLabNumber;
42 public function __construct( array $rowRule, array $patientIdArray, $dateTarget, $options )
44 // require all .php files in the report's sub-folder
45 $className = get_class( $this );
46 foreach ( glob( dirname(__FILE__)."/../reports/".$className."/*.php" ) as $filename ) {
47 require_once( $filename );
49 // require common .php files
50 foreach ( glob( dirname(__FILE__)."/../reports/common/*.php" ) as $filename ) {
51 require_once( $filename );
53 // require clinical types
54 foreach ( glob( dirname(__FILE__)."/../../../ClinicalTypes/*.php" ) as $filename ) {
55 require_once( $filename );
58 $this->_amcPopulation = new AmcPopulation( $patientIdArray );
59 $this->_rowRule = $rowRule;
60 $this->_ruleId = isset( $rowRule['id'] ) ? $rowRule['id'] : '';
61 // Parse measurement period, which is stored as array in $dateTarget ('dateBegin' and 'dateTarget').
62 $this->_beginMeasurement = $dateTarget['dateBegin'];
63 $this->_endMeasurement = $dateTarget['dateTarget'];
64 $this->_manualLabNumber = $options['labs_manual'];
67 public abstract function createNumerator();
68 public abstract function createDenominator();
69 public abstract function getObjectToCount();
71 public function getResults() {
72 return $this->_resultsArray;
75 public function execute()
78 // If itemization is turned on, then iterate the rule id iterator
80 // Note that when AMC rules suports different patient populations and
81 // numerator caclulation, then it will need to change placement of
82 // this and mimick the CQM rules mechanism
83 if ($GLOBALS['report_itemizing_temp_flag_and_id']) {
84 $GLOBALS['report_itemized_test_id_iterator']++;
87 $numerator = $this->createNumerator();
88 if ( !$numerator instanceof AmcFilterIF ) {
89 throw new Exception( "Numerator must be an instance of AmcFilterIF" );
92 $denominator = $this->createDenominator();
93 if ( !$denominator instanceof AmcFilterIF ) {
94 throw new Exception( "Denominator must be an instance of AmcFilterIF" );
97 $totalPatients = count( $this->_amcPopulation );
99 // Figure out object to be counted
100 // (patients, labs, transitions, visits, or prescriptions)
101 $object_to_count = $this->getObjectToCount();
102 if (empty($object_to_count)) {
103 $object_to_count="patients";
106 $numeratorObjects = 0;
107 $denominatorObjects = 0;
108 foreach ( $this->_amcPopulation as $patient )
110 // If begin measurement is empty, then make the begin
111 // measurement the patient dob.
112 $tempBeginMeasurement = "";
113 if (empty($this->_beginMeasurement)) {
114 $tempBeginMeasurement = $patient->dob;
116 else {
117 $tempBeginMeasurement = $this->_beginMeasurement;
120 // Count Denominators
121 if ($object_to_count == "patients") {
122 // Counting patients
123 if ( !$denominator->test( $patient, $tempBeginMeasurement, $this->_endMeasurement ) ) {
124 continue;
126 $denominatorObjects++;
128 else {
129 // Counting objects other than patients
130 // First, collect the pertinent objects
131 $objects = $this->collectObjects($patient,$object_to_count,$tempBeginMeasurement,$this->_endMeasurement);
133 // Second, test each object
134 $objects_pass=array();
135 foreach ($objects as $object) {
136 $patient->object=$object;
137 if ( $denominator->test( $patient, $tempBeginMeasurement, $this->_endMeasurement ) ) {
138 $denominatorObjects++;
139 array_push($objects_pass,$object);
144 // Count Numerators
145 if ($object_to_count == "patients") {
146 // Counting patients
147 if ( !$numerator->test( $patient, $tempBeginMeasurement, $this->_endMeasurement ) ) {
150 // If itemization is turned on, then record the "failed" item
151 if ($GLOBALS['report_itemizing_temp_flag_and_id']) {
152 insertItemReportTracker($GLOBALS['report_itemizing_temp_flag_and_id'], $GLOBALS['report_itemized_test_id_iterator'], 0, $patient->id);
155 continue;
157 else {
158 $numeratorObjects++;
160 // If itemization is turned on, then record the "passed" item
161 if ($GLOBALS['report_itemizing_temp_flag_and_id']) {
162 insertItemReportTracker($GLOBALS['report_itemizing_temp_flag_and_id'], $GLOBALS['report_itemized_test_id_iterator'], 1, $patient->id);
167 else {
168 // Counting objects other than patients
169 // test each object that passed the above denominator testing
170 foreach ($objects_pass as $object) {
171 $patient->object=$object;
172 if ( $numerator->test( $patient, $tempBeginMeasurement, $this->_endMeasurement ) ) {
173 $numeratorObjects++;
175 // If itemization is turned on, then record the "passed" item
176 if ($GLOBALS['report_itemizing_temp_flag_and_id']) {
177 insertItemReportTracker($GLOBALS['report_itemizing_temp_flag_and_id'], $GLOBALS['report_itemized_test_id_iterator'], 1, $patient->id);
181 else {
183 // If itemization is turned on, then record the "failed" item
184 if ($GLOBALS['report_itemizing_temp_flag_and_id']) {
185 insertItemReportTracker($GLOBALS['report_itemizing_temp_flag_and_id'], $GLOBALS['report_itemized_test_id_iterator'], 0, $patient->id);
194 // Deal with the manually added labs for the electronic labs AMC measure
195 if ($object_to_count == "labs") {
196 $denominatorObjects = $denominatorObjects + $this->_manualLabNumber;
199 $percentage = calculate_percentage( $denominatorObjects, 0, $numeratorObjects );
200 $result = new AmcResult( $this->_rowRule, $totalPatients, $denominatorObjects, 0, $numeratorObjects, $percentage );
201 $this->_resultsArray[]= $result;
204 private function collectObjects ($patient,$object_label,$begin,$end) {
206 $results = array();
207 $sqlBindArray = array();
209 switch ($object_label) {
210 case "transitions-in":
211 $sql = "SELECT amc_misc_data.map_id as `encounter`, amc_misc_data.date_completed as `completed`, form_encounter.date as `date` " .
212 "FROM `amc_misc_data`, `form_encounter` " .
213 "WHERE amc_misc_data.map_id = form_encounter.encounter " .
214 "AND amc_misc_data.map_category = 'form_encounter' " .
215 "AND amc_misc_data.pid = ? AND form_encounter.pid = ? " .
216 "AND amc_misc_data.amc_id = 'med_reconc_amc' " .
217 "AND form_encounter.date >= ? AND form_encounter.date <= ?";
218 array_push($sqlBindArray, $patient->id, $patient->id, $begin, $end);
219 break;
220 case "transitions-out":
221 $sql = "SELECT * " .
222 "FROM `transactions` " .
223 "WHERE `title` = 'LBTref' " .
224 "AND `pid` = ? " .
225 "AND `date` >= ? AND `date` <= ?";
226 array_push($sqlBindArray, $patient->id, $begin, $end);
227 break;
228 case "encounters":
229 $sql = "SELECT * " .
230 "FROM `form_encounter` " .
231 "WHERE `pid` = ? " .
232 "AND `date` >= ? AND `date` <= ?";
233 array_push($sqlBindArray, $patient->id, $begin, $end);
234 break;
235 case "encounters_office_visit":
236 $sql = "SELECT * " .
237 "FROM `form_encounter` LEFT JOIN `enc_category_map` ON (form_encounter.pc_catid = enc_category_map.main_cat_id) " .
238 "WHERE enc_category_map.rule_enc_id = 'enc_off_vis' " .
239 "AND `pid` = ? " .
240 "AND `date` >= ? AND `date` <= ?";
241 array_push($sqlBindArray, $patient->id, $begin, $end);
242 break;
243 case "prescriptions":
244 $sql = "SELECT * " .
245 "FROM `prescriptions` " .
246 "WHERE `patient_id` = ? " .
247 "AND `date_added` >= ? AND `date_added` <= ?";
248 array_push($sqlBindArray, $patient->id, $begin, $end);
249 break;
250 case "labs":
251 $sql = "SELECT procedure_result.result FROM " .
252 "procedure_order, " .
253 "procedure_report, " .
254 "procedure_result " .
255 "WHERE " .
256 "procedure_order.patient_id = ? AND " .
257 "procedure_order.procedure_order_id = procedure_report.procedure_order_id AND " .
258 "procedure_report.procedure_report_id = procedure_result.procedure_report_id AND " .
259 "procedure_report.date_collected >= ? AND procedure_report.date_collected <= ?";
260 array_push($sqlBindArray, $patient->id, $begin, $end);
261 break;
263 case "lab_radiology":
264 $sql = "SELECT IF( u.cpoe = '1', 'Yes', 'No') as cpoe_stat FROM procedure_order pr ".
265 "INNER JOIN procedure_order_code prc ON pr.procedure_order_id = prc.procedure_order_id ".
266 "LEFT JOIN procedure_providers pp ON pr.lab_id = pp.ppid ".
267 "LEFT JOIN users u ON u.id = pp.lab_director ".
268 "WHERE pr.patient_id = ? ".
269 "AND prc.procedure_order_title LIKE '%Radiology%' ".
270 "AND (pr.date_ordered BETWEEN ? AND ?)";
271 array_push($sqlBindArray, $patient->id, $begin, $end);
272 break;
274 case "cpoe_lab_orders":
275 $sql = "SELECT IF( u.cpoe = '1', 'Yes', 'No') as cpoe_stat FROM procedure_order pr ".
276 "INNER JOIN procedure_order_code prc ON pr.procedure_order_id = prc.procedure_order_id ".
277 "LEFT JOIN procedure_providers pp ON pr.lab_id = pp.ppid ".
278 "LEFT JOIN users u ON u.id = pp.lab_director ".
279 "WHERE pr.patient_id = ? ".
280 "AND prc.procedure_order_title LIKE '%Laboratory Test%' ".
281 "AND (pr.date_ordered BETWEEN ? AND ?)";
282 array_push($sqlBindArray, $patient->id, $begin, $end);
283 break;
285 case "med_orders":
286 // Still TODO
287 // AMC MU2 TODO :
288 // Note the cpoe_flag and functionality does not exist in OpenEMR official codebase.
290 $sql = "SELECT cpoe_flag as cpoe_stat " .
291 "FROM `prescriptions` " .
292 "WHERE `patient_id` = ? " .
293 "AND `date_added` BETWEEN ? AND ?";
294 array_push($sqlBindArray, $patient->id, $begin, $end);
295 break;
297 case "lab_orders":
298 $sql = "SELECT procedure_order_id FROM " .
299 "procedure_order " .
300 "WHERE " .
301 "patient_id = ? " .
302 "AND (date_ordered BETWEEN ? AND ?)";
303 array_push($sqlBindArray, $patient->id, $begin, $end);
304 break;
307 $rez = sqlStatement($sql, $sqlBindArray);
308 for($iter=0; $row=sqlFetchArray($rez); $iter++) {
309 if ('transitions-out' == $object_label) {
310 $fres = sqlStatement("SELECT field_id, field_value FROM lbt_data WHERE form_id = ?",
311 array($row['id']));
312 while ($frow = sqlFetchArray($fres)) {
313 $row[$frow['field_id']] = $frow['field_value'];
316 $results[$iter]=$row;
319 return $results;