From: bradymiller Date: Sat, 9 Feb 2013 08:52:36 +0000 (-0800) Subject: Code type module improvements: X-Git-Tag: whats-been-changed~409 X-Git-Url: https://repo.or.cz/w/openemr.git/commitdiff_plain/4c2e8f116ca989f714642e2cbbaa8b2ae25ab353 Code type module improvements: -Added wrappers to the code set search module functions to ease use. -Full support for multiple code type searching. -Support of short descriptions. -Support for new functionality in Administration->Services. -Added test calls in the Testing scripts -Consolidated the internal/external code search methods in code_set_search() function -Added ability to search code sets by category --- diff --git a/Tests/code_types/CodesTests.php b/Tests/code_types/CodesTests.php index 5e9d402dd..82b580b26 100644 --- a/Tests/code_types/CodesTests.php +++ b/Tests/code_types/CodesTests.php @@ -6,13 +6,13 @@ require_once("library/htmlspecialchars.inc.php"); require_once("library/translation.inc.php"); require_once("custom/code_types.inc.php"); -function search_test($type,$string,$mode='default') +function search_test($type,$string,$mode='default',$return_only_one=false) { echo "
    "; - $res=code_set_search($type,$string,false,true,false,0,10,array(),null,$mode); - foreach($res->GetArray() as $code) + $res=code_set_search($type,$string,false,true,$return_only_one,0,10,array(),null,$mode); + while ($code = sqlFetchArray($res)) { - echo "
  1. ". $code['code_type_name'].":".$code['code'].":".$code['code_text']."
  2. "; + echo "
  3. ". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."
  4. "; } echo "
"; } @@ -21,11 +21,6 @@ function search_test($type,$string,$mode='default') diff --git a/Tests/code_types/DescriptionTests.php b/Tests/code_types/DescriptionTests.php index b32bfeb5c..9d63fcf62 100644 --- a/Tests/code_types/DescriptionTests.php +++ b/Tests/code_types/DescriptionTests.php @@ -6,20 +6,30 @@ require_once("library/htmlspecialchars.inc.php"); require_once("library/translation.inc.php"); require_once("custom/code_types.inc.php"); -function description_test($codes) +function description_test($codes,$mode) { - $descriptions=lookup_code_descriptions($codes); + $descriptions=lookup_code_descriptions($codes,$mode); echo $descriptions."
".PHP_EOL; } echo PHP_EOL; description_test("ICD9:401.1"); +description_test("ICD9:401.1","code_text_short"); + description_test("CVX:1"); +description_test("CVX:1","code_text_short"); + description_test("ICD10:I10"); +description_test("ICD10:I10","code_text_short"); + description_test("SNOMED:1201005"); -description_test("SNOMED-CT:1201005"); -description_test("SNOMED-PR:285008"); +description_test("SNOMED:1201005","code_text_short"); +description_test("SNOMED-CT:1201005"); +description_test("SNOMED-CT:1201005","code_text_short"); +description_test("SNOMED-PR:285008"); +description_test("SNOMED-PR:285008","code_text_short"); description_test("ICD9:401.1;CVX:1;ICD10:I10;SNOMED:1201005;SNOMED-CT:1201005;SNOMED-PR:285008"); +description_test("ICD9:401.1;CVX:1;ICD10:I10;SNOMED:1201005;SNOMED-CT:1201005;SNOMED-PR:285008","code_text_short"); ?> diff --git a/Tests/code_types/MainCodesTests.php b/Tests/code_types/MainCodesTests.php new file mode 100644 index 000000000..9bf9094b6 --- /dev/null +++ b/Tests/code_types/MainCodesTests.php @@ -0,0 +1,82 @@ +"; + $res=main_code_set_search($type,$string,$limit,$category,true,$modes,$count); + if ($count) { + echo "
  • " . $res . "
  • "; + } + else { + while ($code = sqlFetchArray($res)) + { + echo "
  • ". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."
  • "; + } + } + echo ""; +} + +function return_code_info_test($type,$string,$limit=20,$modes=NULL,$count=false) +{ + echo "
      "; + $res=return_code_information($type,$string); + while ($code = sqlFetchArray($res)) + { + echo "
    1. ". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."
    2. "; + } + echo "
    "; +} + +main_search_test("ICD9","hyperchol"); +main_search_test("ICD9","401"); +main_search_test(array("ICD9"),"401"); + +main_search_test("ICD10","hypert"); +main_search_test("ICD10","I1"); +main_search_test(array("ICD10"),"I1"); + +main_search_test("CPT4","99"); + +main_search_test("SNOMED","hypert"); +main_search_test("SNOMED","1201005"); + +main_search_test("SNOMED-CT","hypert"); +main_search_test("SNOMED-CT","1201005"); + +main_search_test("SNOMED-PR","Incision Drai"); + +main_search_test(array("ICD9","CVX","ICD10"),"100"); +main_search_test(array("ICD9","CVX"),"100"); +main_search_test(array("CVX","ICD10"),"100"); + +main_search_test(array("ICD9","ICD10","SNOMED"),"colon benign"); +main_search_test(array("ICD9","SNOMED"),"colon benign"); + +main_search_test("","polio",40,NULL,false,"active"); +main_search_test("","polio",40,NULL,false,"diagnosis"); +main_search_test("","polio",40,NULL,false,"procedure"); +main_search_test("","polio",40,NULL,false,"clinical_term"); + +main_search_test("ICD9","401",NULL,array('code','description'),true); +main_search_test("ICD9","401",NULL,array('description','code'),true); +main_search_test("ICD9","401",NULL,array('code'),true); +main_search_test("ICD9","401",NULL,array('description'),true); +main_search_test("ICD9","chol",NULL,array('code','description'),true); +main_search_test("ICD9","chol",NULL,array('description','code'),true); +main_search_test("ICD9","chol",NULL,array('code'),true); +main_search_test("ICD9","chol",NULL,array('description'),true); + +return_code_info_test("ICD9","045.10"); +return_code_info_test("CVX","2"); +return_code_info_test("ICD10","A80.2"); +return_code_info_test("SNOMED","172672006"); +return_code_info_test("SNOMED-CT","14535005"); +return_code_info_test("SNOMED-PR","170420002"); + +?> diff --git a/Tests/code_types/MultipleTests.php b/Tests/code_types/MultipleTests.php new file mode 100644 index 000000000..5a38fa4a9 --- /dev/null +++ b/Tests/code_types/MultipleTests.php @@ -0,0 +1,58 @@ +"; + $res=multiple_code_set_search($type,$string,$limit,$modes,$count); + if ($count) { + echo "
  • " . $res . "
  • "; + } + else { + while ($code = sqlFetchArray($res)) + { + echo "
  • ". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."
  • "; + } + } + echo ""; +} + + +mult_search_test(array("ICD9"),"hyperchol"); +mult_search_test(array("ICD9"),"401"); + +mult_search_test(array("ICD10"),"hypert"); +mult_search_test(array("ICD10"),"I1"); + +mult_search_test(array("CPT4"),"99"); + +mult_search_test(array("SNOMED"),"hypert"); +mult_search_test(array("SNOMED"),"1201005"); + +mult_search_test(array("SNOMED-CT"),"hypert"); +mult_search_test(array("SNOMED-CT"),"1201005"); + +mult_search_test(array("SNOMED-PR"),"Incision Drai"); + +mult_search_test(array(),"100"); +mult_search_test(array("ICD9","CVX"),"100"); +mult_search_test(array("CVX","ICD10"),"100"); + +mult_search_test(array(),"colon benign"); +mult_search_test(array("ICD9","SNOMED"),"colon benign"); + +mult_search_test(array("ICD9"),"401",NULL,array('code','description'),true); +mult_search_test(array("ICD9"),"401",NULL,array('description','code'),true); +mult_search_test(array("ICD9"),"401",NULL,array('code'),true); +mult_search_test(array("ICD9"),"401",NULL,array('description'),true); +mult_search_test(array("ICD9"),"chol",NULL,array('code','description'),true); +mult_search_test(array("ICD9"),"chol",NULL,array('description','code'),true); +mult_search_test(array("ICD9"),"chol",NULL,array('code'),true); +mult_search_test(array("ICD9"),"chol",NULL,array('description'),true); + +?> diff --git a/Tests/code_types/SequentialTests.php b/Tests/code_types/SequentialTests.php index fd9fa4e96..f1967af9a 100644 --- a/Tests/code_types/SequentialTests.php +++ b/Tests/code_types/SequentialTests.php @@ -16,7 +16,7 @@ function seq_search_test($type,$string,$limit=20,$modes=NULL,$count=false) else { while ($code = sqlFetchArray($res)) { - echo "
  • ". $code['code_type_name'].":".$code['code'].":".$code['code_text']."
  • "; + echo "
  • ". $code['code_type_name'].":".$code['code'].":".$code['code_text'].":".$code['code_text_short']."
  • "; } } echo ""; @@ -41,7 +41,6 @@ seq_search_test("SNOMED-CT","1201005"); seq_search_test("SNOMED-PR","Incision Drai"); - seq_search_test("ICD9","401",NULL,array('code','description'),true); seq_search_test("ICD9","401",NULL,array('description','code'),true); seq_search_test("ICD9","401",NULL,array('code'),true); diff --git a/custom/code_types.inc.php b/custom/code_types.inc.php index 152b3eb0e..4122952e5 100644 --- a/custom/code_types.inc.php +++ b/custom/code_types.inc.php @@ -85,6 +85,7 @@ while ($ctrow = sqlFetchArray($ctres)) { $code_external_tables=array(); define('EXT_COL_CODE','code'); define('EXT_COL_DESCRIPTION','description'); +define('EXT_COL_DESCRIPTION_BRIEF','description_brief'); define('EXT_TABLE_NAME','table'); define('EXT_FILTER_CLAUSES','filter_clause'); define('EXT_VERSION_ORDER','filter_version_order'); @@ -101,15 +102,17 @@ define('DISPLAY_DESCRIPTION',"display_description"); * @param type $table_name The name of the table which stores the code informattion (e.g. icd9_dx_code * @param type $col_code The name of the column which is the code * @param type $col_description The name of the column which is the description + * @param type $col_description_brief The name of the column which is the brief description * @param type $filter_clauses An array of clauses to be included in the search "WHERE" clause that limits results * @param type $version_order How to choose between different revisions of codes * @param type $joins An array which describes additional tables to join as part of a code search. */ -function define_external_table(&$results, $index, $table_name,$col_code, $col_description,$filter_clauses=array(),$version_order="",$joins=array(),$display_desc="") +function define_external_table(&$results, $index, $table_name,$col_code, $col_description,$col_description_brief,$filter_clauses=array(),$version_order="",$joins=array(),$display_desc="") { $results[$index]=array(EXT_TABLE_NAME=>$table_name, EXT_COL_CODE=>$col_code, EXT_COL_DESCRIPTION=>$col_description, + EXT_COL_DESCRIPTION_BRIEF=>$col_description_brief, EXT_FILTER_CLAUSES=>$filter_clauses, EXT_JOINS=>$joins, EXT_VERSION_ORDER=>$version_order, @@ -117,18 +120,18 @@ function define_external_table(&$results, $index, $table_name,$col_code, $col_de ); } // In order to treat all the code types the same for lookup_code_descriptions, we include metadata for the original codes table -define_external_table($code_external_tables,0,'codes','code','code_text',array(),'id'); +define_external_table($code_external_tables,0,'codes','code','code_text','code_text_short',array(),'id'); // ICD9 External Definitions -define_external_table($code_external_tables,4,'icd9_dx_code','formatted_dx_code','long_desc',array("active='1'"),'revision DESC'); -define_external_table($code_external_tables,5,'icd9_sg_code','formatted_sg_code','long_desc',array("active='1'"),'revision DESC'); +define_external_table($code_external_tables,4,'icd9_dx_code','formatted_dx_code','long_desc','short_desc',array("active='1'"),'revision DESC'); +define_external_table($code_external_tables,5,'icd9_sg_code','formatted_sg_code','long_desc','short_desc',array("active='1'"),'revision DESC'); //**** End ICD9 External Definitions // SNOMED Definitions // For generic SNOMED-CT, there is no need to join with the descriptions table to get a specific description Type // For generic concepts, use the fully specified description (DescriptionType=3) so we can tell the difference between them. -define_external_table($code_external_tables,7,'sct_descriptions','ConceptId','Term',array("DescriptionStatus=0","DescriptionType=3"),""); +define_external_table($code_external_tables,7,'sct_descriptions','ConceptId','Term','Term',array("DescriptionStatus=0","DescriptionType=3"),""); // To determine codes, we need to evaluate data in both the sct_descriptions table, and the sct_concepts table. @@ -136,12 +139,12 @@ define_external_table($code_external_tables,7,'sct_descriptions','ConceptId','Te $SNOMED_joins=array(JOIN_TABLE=>"sct_concepts",JOIN_FIELDS=>array("sct_descriptions.ConceptId=sct_concepts.ConceptId")); // For disorders, use the preferred term (DescriptionType=1) -define_external_table($code_external_tables,2,'sct_descriptions','ConceptId','Term',array("DescriptionStatus=0","DescriptionType=1"),"",array($SNOMED_joins)); +define_external_table($code_external_tables,2,'sct_descriptions','ConceptId','Term','Term',array("DescriptionStatus=0","DescriptionType=1"),"",array($SNOMED_joins)); // Add the filter to choose only disorders. This filter happens as part of the join with the sct_concepts table array_push($code_external_tables[2][EXT_JOINS][0][JOIN_FIELDS],"FullySpecifiedName like '%(disorder)'"); // SNOMED-PR definition -define_external_table($code_external_tables,9,'sct_descriptions','ConceptId','Term',array("DescriptionStatus=0","DescriptionType=1"),"",array($SNOMED_joins)); +define_external_table($code_external_tables,9,'sct_descriptions','ConceptId','Term','Term',array("DescriptionStatus=0","DescriptionType=1"),"",array($SNOMED_joins)); // Add the filter to choose only procedures. This filter happens as part of the join with the sct_concepts table array_push($code_external_tables[9][EXT_JOINS][0][JOIN_FIELDS],"FullySpecifiedName like '%(procedure)'"); @@ -149,8 +152,8 @@ array_push($code_external_tables[9][EXT_JOINS][0][JOIN_FIELDS],"FullySpecifiedNa //**** End SNOMED Definitions // ICD 10 Definitions -define_external_table($code_external_tables,1,'icd10_dx_order_code','formatted_dx_code','long_desc',array("active='1'","valid_for_coding = '1'"),'revision DESC'); -define_external_table($code_external_tables,6,'icd10_pcs_order_code','pcs_code','long_desc',array("active='1'","valid_for_coding = '1'"),'revision DESC'); +define_external_table($code_external_tables,1,'icd10_dx_order_code','formatted_dx_code','long_desc','short_desc',array("active='1'","valid_for_coding = '1'"),'revision DESC'); +define_external_table($code_external_tables,6,'icd10_pcs_order_code','pcs_code','long_desc','short_desc',array("active='1'","valid_for_coding = '1'"),'revision DESC'); //**** End ICD 10 Definitions /** @@ -241,7 +244,7 @@ function convert_type_id_to_key($id) { * as 1) an array and as 2) a comma-separated lists that has been * process by urlencode() in order to place into URL address safely. * - * @param string $category category of code types('diagnosis', 'procedure' or 'clinical_term') + * @param string $category category of code types('diagnosis', 'procedure', 'clinical_term' or 'active') * @param string $return_format format or returned code types ('array' or 'csv') * @return string/array */ @@ -268,6 +271,11 @@ function collect_codetypes($category,$return_format="array") { array_push($return,$ct_key); } } + else if ($category == "active") { + if ($ct_arr['active']) { + array_push($return,$ct_key); + } + } else { //return nothing since no supported category was chosen } @@ -284,13 +292,69 @@ function collect_codetypes($category,$return_format="array") { } /** - * Main code set searching function. + * Return the code information for a specific code. + * + * Function is able to search a variety of code sets. See the code type items in the comments at top + * of this page for a listing of the code sets supported. + * + * @param string $form_code_type code set key + * @param string $code code + * @param boolean $active if true, then will only return active entries (not pertinent for PROD code sets) + * @return recordset will contain only one item (row). + */ +function return_code_information($form_code_type,$code,$active=true) { + return code_set_search($form_code_type,$code,false,$active,true); +} + +/** +* The main code set searching function. +* +* It will work for searching one or numerous code sets simultaneously. +* Note that when searching numerous code sets, you CAN NOT search the PROD +* codes; the PROD codes can only be searched by itself. +* +* @param string/array $form_code_type code set key(s) (can either be one key in a string or multiple/one key(s) in an array +* @param string $search_term search term +* @param integer $limit Number of results to return (NULL means return all) +* @param string $category Category of code sets. This WILL OVERRIDE the $form_code_type setting (category options can be found in the collect_codetypes() function above) +* @param boolean $active if true, then will only return active entries +* @param array $modes Holds the search modes to process along with the order of processing (if NULL, then default behavior is sequential code then description search) +* @param boolean $count if true, then will only return the number of entries +* @param integer $start Query start limit (for pagination) (Note this setting will override the above $limit parameter) +* @param integer $number Query number returned (for pagination) (Note this setting will override the above $limit parameter) +* @param array $filter_elements Array that contains elements to filter +* @return recordset/integer Will contain either a integer(if counting) or the results (recordset) +*/ +function main_code_set_search($form_code_type,$search_term,$limit=NULL,$category=NULL,$active=true,$modes=NULL,$count=false,$start=NULL,$number=NULL,$filter_elements=array()) { + + // check for a category + if (!empty($category)) { + $form_code_type = collect_codetypes($category,"array"); + } + + // do the search + if (!empty($form_code_type)) { + if ( is_array($form_code_type) && (count($form_code_type) > 1) ) { + // run the multiple code set search + return multiple_code_set_search($form_code_type,$search_term,$limit,$modes,$count,$active,$start,$number,$filter_elements); + } + if ( is_array($form_code_type) && (count($form_code_type) == 1) ) { + // prepare the variable (ie. convert the one array item to a string) for the non-multiple code set search + $form_code_type = $form_code_type[0]; + } + // run the non-multiple code set search + return sequential_code_set_search($form_code_type,$search_term,$limit,$modes,$count,$active,$start,$number,$filter_elements); + } +} + +/** + * Main "internal" code set searching function. * * Function is able to search a variety of code sets. See the 'external' items in the comments at top * of this page for a listing of the code sets supported. Also note that Products (using PROD as code type) - * is also supported. + * is also supported. (This function is not meant to be called directly) * - * @param string $form_code_type code set key (special keywords are PROD and --ALL--) + * @param string $form_code_type code set key (special keywords are PROD) (Note --ALL-- has been deprecated and should be run through the multiple_code_set_search() function instead) * @param string $search_term search term * @param boolean $count if true, then will only return the number of entries * @param boolean $active if true, then will only return active entries (not pertinent for PROD code sets) @@ -298,38 +362,22 @@ function collect_codetypes($category,$return_format="array") { * @param integer $start Query start limit * @param integer $number Query number returned * @param array $filter_elements Array that contains elements to filter - * @param array $limit Number of results to return (NULL means return all); note this is ignored if set $start/number - * @param array $mode 'default' mode searches code and description, 'code' mode only searches code, 'description' mode searches description (and separates words); note this is ignored if set $return_only_one to TRUE - * @return recordset/integer + * @param integer $limit Number of results to return (NULL means return all); note this is ignored if set $start/number + * @param array $mode 'default' mode searches code and description, 'code' mode only searches code, 'description' mode searches description (and separates words); note this is ignored if set $return_only_one to TRUE + * @param array $return_query This is a mode that will only return the query (everything except for the LIMIT is included) (returned as an array to include the query string and binding array) + * @return recordset/integer/array */ -function code_set_search($form_code_type,$search_term="",$count=false,$active=true,$return_only_one=false,$start=NULL,$number=NULL,$filter_elements=array(),$limit=NULL,$mode='default') { +function code_set_search($form_code_type,$search_term="",$count=false,$active=true,$return_only_one=false,$start=NULL,$number=NULL,$filter_elements=array(),$limit=NULL,$mode='default',$return_query=false) { global $code_types,$code_external_tables; // Figure out the appropriate limit clause - if ( !is_null($start) && !is_null($number) ) { - // For pagination of results - $limit_query = " LIMIT $start, $number "; - } - else if (!is_null($limit)) { - $limit_query = " LIMIT $limit "; - } - else { - // No pagination and no limit - $limit_query = ''; - } - - if ($return_only_one) { - // Only return one result (this is where only matching for exact code match) - // Note this overrides the above limit settings - $limit_query = " LIMIT 1 "; - } - // End determining limit clause + $limit_query = limit_query_string($limit,$start,$number,$return_only_one); // build the filter_elements sql code $query_filter_elements=""; if (!empty($filter_elements)) { foreach ($filter_elements as $key => $element) { - $query_filter_elements .= " AND c." . add_escape_custom($key) . "=" . "'" . add_escape_custom($element) . "' "; + $query_filter_elements .= " AND codes." . add_escape_custom($key) . "=" . "'" . add_escape_custom($element) . "' "; } } @@ -350,56 +398,12 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr else { // Start a codes search // We are looking up the external table id here. An "unset" value gets treated as 0(zero) without this test. This way we can differentiate between "unset" and explicitly zero. $table_id=isset($code_types[$form_code_type]['external']) ? intval(($code_types[$form_code_type]['external'])) : -9999 ; - if(($table_id>=0) || ($form_code_type == '--ALL--')) // Either we found a definition for the given code search or we are doing an "--ALL--" search, so start building the query + if($table_id>=0) // We found a definition for the given code search, so start building the query { - - // Place the common columns variable here since all check codes table - $common_columns=" c.id, c.code_type, c.modifier, c.units, c.fee, " . - "c.superbill, c.related_code, c.taxrates, c.cyp_factor, c.active, c.reportable, c.financial_reporting, "; - - if ( $table_id==0 || ($form_code_type == '--ALL--') ) { // Search from default codes table. --ALL-- only means all codes in the default tables - if($table_id==0){ $table_info[EXT_FILTER_CLAUSES]=array("code_type=".$code_types[$form_code_type]['id']); } // Add a filter for the code type - else {$table_info[EXT_FILTER_CLAUSES]=array("ct.ct_external=0");} // define filter array for "--ALL--" - $table_dot="c."; // $table_dot is used to prevent awkward looking concatenations when referring to columns in - $code_col="code"; - $code_text_col="code_text"; - - $active_query = ''; - if ($active) { - // Only filter for active codes - $active_query=" AND c.active = 1 "; - } - $sql_bind_array = array(); - $columns = " c.code_text, c.code_text_short, c.code, ".$common_columns; - if($table_id==0) - { - // If code type is specified, then include the "constant" as part of the query results for consistency - $columns .= " '" . add_escape_custom($form_code_type) . "' as code_type_name "; - if ($count) { - $query = "SELECT count(c.code) as count "; - } - else { - $query = "SELECT ".$columns; - } - $query .= " FROM `codes` as c "; - - } - else if($form_code_type=='--ALL--') - { - // For an "--ALL--" search we need to join with the code_types table to get the string representation of each returned code. - $columns .= " ct.ct_key as code_type_name "; - if ($count) { - $query = "SELECT count(c.code) as count "; - } - else { - $query = "SELECT ".$columns; - } - $query .= " FROM `codes` as c " . - " LEFT OUTER JOIN `code_types` as ct " . - " ON c.code_type = ct.ct_id "; - } - } - else if ($code_types[$form_code_type]['external'] > 0 ) { // Search from an external table with defined metadata + // Place the common columns variable here since all check codes table + $common_columns=" codes.id, codes.code_type, codes.modifier, codes.units, codes.fee, " . + "codes.superbill, codes.related_code, codes.taxrates, codes.cyp_factor, " . + "codes.active, codes.reportable, codes.financial_reporting, "; $columns .= $common_columns . "'" . add_escape_custom($form_code_type) . "' as code_type_name "; $active_query = ''; @@ -411,25 +415,39 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr // - Kevin Yeh // If there is no entry in codes sql table, then default to active // (this is reason for including NULL below) - $active_query=" AND (c.active = 1 || c.active IS NULL) "; + if ($table_id==0) { + // Search from default codes table + $active_query=" AND codes.active = 1 "; + } + else { + // Search from external tables + $active_query=" AND (codes.active = 1 || codes.active IS NULL) "; + } } - // Get the basic metadata information + // Get/set the basic metadata information $table_info=$code_external_tables[$table_id]; $table=$table_info[EXT_TABLE_NAME]; $table_dot=$table."."; $code_col=$table_info[EXT_COL_CODE]; $code_text_col=$table_info[EXT_COL_DESCRIPTION]; - + $code_text_short_col=$table_info[EXT_COL_DESCRIPTION_BRIEF]; + if ($table_id==0) { + $table_info[EXT_FILTER_CLAUSES]=array("code_type=".$code_types[$form_code_type]['id']); // Add a filter for the code type + } + $code_external = $code_types[$form_code_type]['external']; + // If the description is supposed to come from "joined" table instead of the "main", // the metadata defines a DISPLAY_DESCRIPTION element, and we use that to build up the query if($table_info[DISPLAY_DESCRIPTION]!="") { $display_description=$table_info[DISPLAY_DESCRIPTION]; + $display_description_brief=$table_info[DISPLAY_DESCRIPTION]; } else { $display_description=$table_dot.$code_text_col; + $display_description_brief=$table_dot.$code_text_short_col; } // Ensure the external table exists $check_table = sqlQuery("SHOW TABLES LIKE '".$table."'"); @@ -441,14 +459,23 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr $query = "SELECT count(".$table_dot.$code_col . ") as count "; } else { - $query = "SELECT ".$table_dot.$code_col . " as code, " . + $query = "SELECT '" . $code_external ."' as code_external, " . + $table_dot.$code_col . " as code, " . $display_description . " as code_text, " . + $display_description_brief . " as code_text_short, " . $columns . " "; } - $query .= " FROM ".$table. - " LEFT OUTER JOIN `codes` as c " . - " ON ".$table_dot.$code_col." = c.code AND c.code_type = ? "; - array_push($sql_bind_array,$code_types[$form_code_type]['id']); + if ($table_id==0) { + // Search from default codes table + $query .= " FROM ".$table." "; + } + else { + // Search from external tables + $query .= " FROM ".$table. + " LEFT OUTER JOIN `codes` " . + " ON ".$table_dot.$code_col." = codes.code AND codes.code_type = ? "; + array_push($sql_bind_array,$code_types[$form_code_type]['id']); + } foreach($table_info[EXT_JOINS] as $join_info) { @@ -468,7 +495,6 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr $not_first=true; } } - } // End of block for handling external_id>0 // Setup the where clause based on MODE $query.= " WHERE "; @@ -513,6 +539,15 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr $query .=$active_query . $query_filter_elements; $query .= " ORDER BY ".$table_dot.$code_col."+0,".$table_dot.$code_col; + + if ($return_query) { + // Just returning the actual query without the LIMIT information in it. This + // information can then be used to combine queries of different code types + // via the mysql UNION command. Returning an array to contain the query string + // and the binding parameters. + return array('query'=>$query,'binds'=>$sql_bind_array); + } + $query .= $limit_query; $res = sqlStatement($query,$sql_bind_array); @@ -543,10 +578,17 @@ function code_set_search($form_code_type,$search_term="",$count=false,$active=tr * items in the comments at top of this page for a listing of the code sets supported. * * @param string $codes Is of the form "type:code;type:code; etc.". + * @param string $desc_detail Can choose either the normal description('code_text') or the brief description('code_text_short'). * @return string Is of the form "description;description; etc.". */ -function lookup_code_descriptions($codes) { +function lookup_code_descriptions($codes,$desc_detail="code_text") { global $code_types, $code_external_tables; + + // ensure $desc_detail is set properly + if ( ($desc_detail != "code_text") && ($desc_detail != "code_text_short") ) { + $desc_detail="code_text"; + } + $code_text = ''; if (!empty($codes)) { $relcodes = explode(';', $codes); @@ -560,8 +602,9 @@ function lookup_code_descriptions($codes) { $table_name=$table_info[EXT_TABLE_NAME]; $code_col=$table_info[EXT_COL_CODE]; $desc_col= $table_info[DISPLAY_DESCRIPTION]=="" ? $table_info[EXT_COL_DESCRIPTION] : $table_info[DISPLAY_DESCRIPTION]; + $desc_col_short= $table_info[DISPLAY_DESCRIPTION]=="" ? $table_info[EXT_COL_DESCRIPTION_BRIEF] : $table_info[DISPLAY_DESCRIPTION]; $sqlArray = array(); - $sql = "SELECT ".$desc_col." as code_text FROM ".$table_name; + $sql = "SELECT ".$desc_col." as code_text,".$desc_col_short." as code_text_short FROM ".$table_name; // include the "JOINS" so that we get the preferred term instead of the FullySpecifiedName when appropriate. foreach($table_info[EXT_JOINS] as $join_info) @@ -609,9 +652,9 @@ function lookup_code_descriptions($codes) { $sql .= " LIMIT 1"; $crow = sqlQuery($sql,$sqlArray); - if (!empty($crow["code_text"])) { + if (!empty($crow[$desc_detail])) { if ($code_text) $code_text .= '; '; - $code_text .= $crow["code_text"]; + $code_text .= $crow[$desc_detail]; } } @@ -624,25 +667,27 @@ function lookup_code_descriptions($codes) { } /** -* Sequential code set searching function +* Sequential code set "internal" searching function * * Function is basically a wrapper of the code_set_search() function to support * a optimized searching models. The default mode will: * Searches codes first; then if no hits, it will then search the descriptions * (which are separated by each word in the code_set_search() function). +* (This function is not meant to be called directly) * -* @param string $form_code_type code set key (special keywords are PROD and --ALL--) +* @param string $form_code_type code set key (special keyword is PROD) (Note --ALL-- has been deprecated and should be run through the multiple_code_set_search() function instead) * @param string $search_term search term -* @param array $limit Number of results to return (NULL means return all) +* @param integer $limit Number of results to return (NULL means return all) * @param array $modes Holds the search modes to process along with the order of processing (default behavior is described in above function comment) * @param boolean $count if true, then will only return the number of entries * @param boolean $active if true, then will only return active entries * @param integer $start Query start limit (for pagination) * @param integer $number Query number returned (for pagination) * @param array $filter_elements Array that contains elements to filter -* @return recordset/integer +* @param string $is_hit_mode This is a mode that simply returns the name of the mode if results were found +* @return recordset/integer/string */ -function sequential_code_set_search($form_code_type,$search_term,$limit=NULL,$modes=NULL,$count=false,$active=true,$start=NULL,$number=NULL,$filter_elements=array()) { +function sequential_code_set_search($form_code_type,$search_term,$limit=NULL,$modes=NULL,$count=false,$active=true,$start=NULL,$number=NULL,$filter_elements=array(),$is_hit_mode=false) { // Set the default behavior that is described in above function comments if (empty($modes)) { $modes=array('code','description'); @@ -652,9 +697,132 @@ function sequential_code_set_search($form_code_type,$search_term,$limit=NULL,$mo foreach ($modes as $mode) { $res = code_set_search($form_code_type,$search_term,$count,$active,false,$start,$number,$filter_elements,$limit,$mode); if ( ($count && $res>0) || (!$count && sqlNumRows($res)>0) ) { - // returns the count number if count is true or returns the data if count is false - return $res; + if ($is_hit_mode) { + // just return the mode + return $mode; + } + else { + // returns the count number if count is true or returns the data if count is false + return $res; + } + } + } +} + +/** +* Code set searching "internal" function for when searching multiple code sets. +* +* It will also work for one code set search, although not meant for this. +* (This function is not meant to be called directly) +* +* @param array $form_code_types code set keys (will default to checking all active code types if blank) +* @param string $search_term search term +* @param integer $limit Number of results to return (NULL means return all) +* @param array $modes Holds the search modes to process along with the order of processing (default behavior is described in above function comment) +* @param boolean $count if true, then will only return the number of entries +* @param boolean $active if true, then will only return active entries +* @param integer $start Query start limit (for pagination) +* @param integer $number Query number returned (for pagination) +* @param array $filter_elements Array that contains elements to filter +* @return recordset/integer +*/ +function multiple_code_set_search($form_code_types=array(),$search_term,$limit=NULL,$modes=NULL,$count=false,$active=true,$start=NULL,$number=NULL,$filter_elements=array()) { + + if (empty($form_code_types)) { + // Collect the active code types + $form_code_types = collect_codetypes("active","array"); + } + + if ($count) { + //start the counter + $counter = 0; + } + else { + // Figure out the appropriate limit clause + $limit_query = limit_query_string($limit,$start,$number); + + // Prepare the sql bind array + $sql_bind_array = array(); + + // Start the query string + $query = "SELECT * FROM (("; + } + + // Loop through each code type + $flag_first = true; + $flag_hit = false; //ensure there is a hit to avoid trying an empty query + foreach ($form_code_types as $form_code_type) { + // see if there is a hit + $mode_hit = NULL; + // only use the count method here, since it's much more efficient than doing the actual query + $mode_hit = sequential_code_set_search($form_code_type,$search_term,NULL,$modes,true,$active,NULL,NULL,$filter_elements,true); + if ($mode_hit) { + if ($count) { + // count the hits + $count_hits = code_set_search($form_code_type,$search_term,$count,$active,false,NULL,NULL,$filter_elements,NULL,$mode_hit); + // increment the counter + $counter += $count_hits; + } + else { + $flag_hit = true; + // build the query + $return_query = code_set_search($form_code_type,$search_term,$count,$active,false,NULL,NULL,$filter_elements,NULL,$mode_hit,true); + if (!empty($sql_bind_array)) { + $sql_bind_array = array_merge($sql_bind_array,$return_query['binds']); + } + else { + $sql_bind_array = $return_query['binds']; + } + if (!$flag_first) { + $query .= ") UNION ALL ("; + } + $query .= $return_query['query']; + } + $flag_first = false; + } + } + + if ($count) { + //return the count + return $counter; + } + else { + // Finish the query string + $query .= ")) as atari $limit_query"; + + // Process and return the query (if there was a hit) + if ($flag_hit) { + return sqlStatement($query,$sql_bind_array); } } } + +/** +* Returns the limit to be used in the sql query for code set searches. +* +* @param integer $limit Number of results to return (NULL means return all) +* @param integer $start Query start limit (for pagination) +* @param integer $number Query number returned (for pagination) +* @param boolean $return_only_one if true, then will only return one perfect matching item +* @return recordset/integer +*/ +function limit_query_string($limit=NULL,$start=NULL,$number=NULL,$return_only_one=false) { + if ( !is_null($start) && !is_null($number) ) { + // For pagination of results + $limit_query = " LIMIT $start, $number "; + } + else if (!is_null($limit)) { + $limit_query = " LIMIT $limit "; + } + else { + // No pagination and no limit + $limit_query = ''; + } + if ($return_only_one) { + // Only return one result (this is where only matching for exact code match) + // Note this overrides the above limit settings + $limit_query = " LIMIT 1 "; + } + return $limit_query; +} ?> diff --git a/interface/forms/fee_sheet/new.php b/interface/forms/fee_sheet/new.php index c26ee89b0..cc52fc8c1 100644 --- a/interface/forms/fee_sheet/new.php +++ b/interface/forms/fee_sheet/new.php @@ -860,7 +860,7 @@ echo " \n" // $numrows = 0; if ($_POST['bn_search'] && $_POST['search_term']) { - $res = sequential_code_set_search($search_type,$_POST['search_term']); + $res = main_code_set_search($search_type,$_POST['search_term']); if (!empty($res)) { $numrows = sqlNumRows($res); } diff --git a/interface/patient_file/encounter/find_code_popup.php b/interface/patient_file/encounter/find_code_popup.php index 818373954..f67a7e739 100644 --- a/interface/patient_file/encounter/find_code_popup.php +++ b/interface/patient_file/encounter/find_code_popup.php @@ -159,7 +159,7 @@ else { = $count) $fstart -= $pagesize; if ($fstart < 0) $fstart = 0; $fend = $fstart + $pagesize; @@ -518,31 +522,11 @@ if ($taxline) { - $value) { echo "\n"; } ?> @@ -587,6 +571,7 @@ foreach ($code_types as $key => $value) { + @@ -601,17 +586,9 @@ while ($prow = sqlFetchArray($pres)) { \n"; echo " " . text($iter["code"]) . "\n"; echo " " . text($iter["modifier"]) . "\n"; - if ($is_external_set) { + if ($iter["code_external"] > 0) { // If there is no entry in codes sql table, then default to active // (this is reason for including NULL below) echo " " . ( ($iter["active"] || $iter["active"]==NULL) ? xlt('Yes') : xlt('No')) . "\n"; @@ -644,6 +621,7 @@ if (!empty($all)) { echo " " . ($iter["financial_reporting"] ? xlt('Yes') : xlt('No')) . "\n"; echo " " . text($iter['code_type_name']) . "\n"; echo " " . text($iter['code_text']) . "\n"; + echo " " . text($iter['code_text_short']) . "\n"; if (related_codes_are_used()) { // Show related codes. @@ -665,7 +643,7 @@ if (!empty($all)) { echo "" . text(bucks($prow['pr_price'])) . "\n"; } - if ($is_external_set) { + if ($iter["code_external"] > 0) { echo " [" . xlt('Modify') . "]\n"; } else {