maintenance: title: : Creates the Estimated Dose Administrations;; mlmname: STD_FUNC_DOSAGE_ADMIN_TIMES;; arden: version 2.5;; version: 18.4;; institution: Allscripts, Standard MLM;; author: Allscripts Healthcare Solutions, Inc.;; specialist: ;; date: 2018-10-26;; validation: testing;; /* P r o p r i e t a r y N o t i c e */ /* Unpublished (c) 2013 - 2018 Allscripts Healthcare, LLC. and/or its affiliates. All Rights Reserved. P r o p r i e t a r y N o t i c e: This software has been provided pursuant to a License Agreement, with Allscripts Healthcare, LLC. and/or its affiliates, containing restrictions on its use. This software contains valuable trade secrets and proprietary information of Allscripts Healthcare, LLC. and/or its affiliates and is protected by trade secret and copyright law. This software may not be copied or distributed in any form or medium, disclosed to any third parties, or used in any manner not provided for in said License Agreement except with prior written authorization from Allscripts Healthcare, LLC. and/or its affiliates. Notice to U.S. Government Users: This software is {{{SINGLE-QUOTE}}}Commercial Computer Software{{{SINGLE-QUOTE}}}. All product names are the trademarks or registered trademarks of Allscripts Healthcare, LLC. and/or its affiliates. */ /* P r o p r i e t a r y N o t i c e */ library: purpose: The purpose of this MLM is to create the estimated Dose Administration Date-Times for Frequencies such as BID, Q18H, , , etc. ;; explanation: Using the data that is passed into the MLM, plus the Frequency dictionary and the Units of Measure dictionary, the ESTIMATED Dose Administrations date-times are created, put into a list, and then returned to the calling MLM. ;; keywords: single dose; average daily dose; total daily dose; dosage range ;; knowledge: type: data-driven;; data: (order_med_frequency, start_dtm, stop_dtm, stop_after_value, stop_after_option_type, freq_from_time, from_uom, order_TaskScheduleDefinition_obj ):= ARGUMENT; // Set to true if logging is needed. log_execution_info := false; // The facility must map its Dictionary Codes to the Core UOM in the // Units of Measure Dictionary. The MLM converts the facility-defined units of measure // to the system-defined values in the Unit of Measure Dictionary called CoreUOM. day_string := "day"; hour_string := "hr"; minute_string := "min"; month_string := "month"; ounce_string := "oz"; second_string := "s"; week_string := "week"; year_string := "year"; shift_string := "shift"; // Declare C functions to parse the frequency string func_get_token := interface {char* msvcrt:strtok(char*, char*)}; func_get_str := interface {char* msvcrt:strstr(char*, char*)}; // Declare Task_Schedule_Definition_Object Task_Schedule_Definition_Object := OBJECT [ scheduled_dtm, scheduled_time, day_of_week, parent_GUID, scheduled_duration ]; //================================================================ // FREQUENCY //================================================================ if order_med_frequency = "" then //Task_Schedule_Definition_Object := OBJECT //[ scheduled_dtm, scheduled_time, day_of_week, parent_GUID, scheduled_duration ]; task_schedule_definition_list := read AS Task_Schedule_Definition_Object { TaskScheduleDefinition: ScheduledDtm, ScheduledTime, DayOfWeek, ParentGUID REFERENCING order_TaskScheduleDefinition_obj }; //================================================================ // FREQUENCY //================================================================ elseif order_med_frequency = "" then frequency_type := 3; time_from_value := freq_from_time as number; time_core_uom := read last {"SELECT CoreUOM " || " FROM CV3UnitOfMeasure" || " WHERE Code = " || SQL (from_uom) || " AND Active = 1 " }; //================================================================ // REGULAR FREQUENCY //================================================================ else // Gets the Frequency information from the Enterprise data // Only gets the Active Frequencies and Active Units of Measures ( frequency_type, time_from_value, time_core_uom):= read last {"SELECT f.DefinitionType, f.TimeFromValue, u.CoreUOM " || " FROM CV3Frequency AS f " || " LEFT OUTER JOIN CV3UnitOfMeasure AS u" || " ON (f.TimeUom = u.Code and u.Active=1) " || " WHERE f.Code = " || SQL (order_med_frequency) || " AND f.Active = 1 " }; endif; // if order_med_frequency ;; evoke: ;; logic: // If the frequency is shift based then do not // do any calculations. Return an empty list // of admin times. if time_core_uom = shift_string then isShiftFrequency := true; next_startDtm := start_dtm + 24 hours; conclude true; endif; isShiftFrequency := false; //================================================================================== // Frequency //================================================================================== if order_med_frequency = "" then //--------------------------------- // Set Begin_DTM //--------------------------------- //Calculate Begin_DTM begin_dtm := start_dtm; //--------------------------------- // Calculate End_DTM //--------------------------------- if stop_after_option_type = 1 //days then end_dtm := begin_dtm + stop_after_value DAYS; elseif stop_after_option_type = 2 //hours then end_dtm := begin_dtm + stop_after_value HOURS; elseif stop_after_option_type = 3 //minutes then end_dtm := begin_dtm + stop_after_value MINUTES; elseif stop_after_option_type = 4 //times then //end_dtm is unknown until the DTMs are generated. //the end_dtm will be set after the DTMs are generated. end_dtm := null; elseif exist stop_dtm then end_dtm := stop_dtm ; endif; //if stop_after_option_type //============================================= // -- DAILY or WEEKLY //============================================= if from_uom is in ( "day", "week") then //---------------------------------------------- // Convert Scheduled_Time to Scheduled_Duration //---------------------------------------------- duration_list := (); for task_schedule_definition in task_schedule_definition_list do //Convert the Number to a String //Extract the Characters from the String temp_string := (""|| task_schedule_definition.scheduled_time); temp_characters := extract characters temp_string; //Convert the front characters to the number of hours //Convert the back character to the number of minutes if count (temp_characters) = 4 then num_hours := (temp_characters[1] || temp_characters[2]) AS NUMBER ; num_minutes := (temp_characters[3] || temp_characters[4]) AS NUMBER ; elseif count (temp_characters) = 3 then num_hours := (temp_characters[1]) AS NUMBER ; num_minutes := (temp_characters[2] || temp_characters[3]) AS NUMBER ; elseif count (temp_characters) = 2 then num_hours := 0; num_minutes := (temp_characters[1] || temp_characters[2]) AS NUMBER ; elseif count (temp_characters) = 1 then num_hours := 0 ; num_minutes := ("" || temp_characters[1]) AS NUMBER ; endif; //Add the Hours and Minutes and put them in a list duration_list := duration_list, ( num_hours HOUR) + (num_minutes MINUTE) ; enddo; //for task_schedule_definition //Set the Scheduled_Duration in the Task_Schedule_Definition_Object task_schedule_definition_list.scheduled_duration := duration_list; //-------------------------------------------------------------- // Determine how to increment "Every X Days" or "Every Y Weeks" //-------------------------------------------------------------- if from_uom = "day" then increment_duration := freq_from_time DAY; elseif from_uom = "week" then increment_duration := freq_from_time WEEK; endif; //if from_uom = "day" //----------------------------------------------------- // Generate the first set of DTMs based on the schedule //----------------------------------------------------- //task_schedule_definition_list.day_of_week values // 0=Sun, 1=Mon, 2=Tue, 3=Wed, 4=Thur, 5=Fri, 6=Sat // 7=DayOnly //Initialize Variables midnight_begin_dtm := day floor of begin_dtm; original_schedule_dtm_list := (); for task_schedule_definition in task_schedule_definition_list do if task_schedule_definition.day_of_week < 7 then //Figure Out Which Day of the Week to Create from_day_of_week := (day floor of begin_dtm - week floor of begin_dtm ) / 1 day; to_day_of_week := task_schedule_definition.day_of_week; if to_day_of_week >= from_day_of_week then //it is this week number_of_days := to_day_of_week - from_day_of_week ; else //it is next week number_of_days := (7 - from_day_of_week) + to_day_of_week; endif; //if to_day_of_week temp_dtm1 := midnight_begin_dtm + (number_of_days day) + task_schedule_definition.scheduled_duration ; else //It is not a day of the week. It is the initial day. temp_dtm1 := midnight_begin_dtm + task_schedule_definition.scheduled_duration ; endif; //if task_schedule_definition.day_of_week < 7 //Append DTM to List original_schedule_dtm_list := original_schedule_dtm_list, temp_dtm1; enddo; //for task_schedule_definition //-------------------------------------------- // Create a Valid Set of Sorted Scheduled DTMs //-------------------------------------------- original_schedule_dtm_list := Sort DATA original_schedule_dtm_list; //----------------------------------------------------------------------- // For DAYS, move any DTMs forward when they are less than the Begin_DTM //----------------------------------------------------------------------- if from_uom = "day" then temp_past_dtm_list := original_schedule_dtm_list where it < begin_dtm; temp_current_dtm_list := original_schedule_dtm_list where it >= begin_dtm ; updated_schedule_dtm_list := temp_current_dtm_list; for past_dtm in temp_past_dtm_list do temp_future_dtm := past_dtm + 1 day; updated_schedule_dtm_list := updated_schedule_dtm_list, temp_future_dtm; enddo; //------------------------------------------------------------------------------- // For WEEKS, do not move any DTMs forward when they are less than the Begin_DTM //------------------------------------------------------------------------------- elseif from_uom = "week" then updated_schedule_dtm_list := original_schedule_dtm_list; endif; //if from_uom //----------------------------------------- // Finish Creating all the rest of the DTM //----------------------------------------- //Get the last DTM in the list new_dtm := last updated_schedule_dtm_list; //Initialize Variables admin_dtm_list := updated_schedule_dtm_list; multiplier := 0; if stop_after_option_type = 4 //4 = TIMES then //Use the TIMES to stop generating DTMs admin_dtm_list := updated_schedule_dtm_list where it >= begin_dtm; count_of_dtm := count admin_dtm_list; //If there aren{{{SINGLE-QUOTE}}}t enough DTM{{{SINGLE-QUOTE}}}s then generate more if stop_after_value > count_of_dtm then num_of_dtm := count_of_dtm; While num_of_dtm <= stop_after_value do //Increment num_of_dtm num_of_dtm := num_of_dtm + (count of updated_schedule_dtm_list); //Increment the multiplier multiplier := multiplier + 1; //Generate new DTM by looping through the list DTMS and //increasing the duration of each one for temp_dtm2 in (updated_schedule_dtm_list) do new_dtm := temp_dtm2 + (increment_duration * multiplier) ; admin_dtm_list := admin_dtm_list, new_dtm; enddo; //for temp_dtm2 enddo; //While num_of_dtm endif; //if stop_after_value //only keep the number of DTM{{{SINGLE-QUOTE}}}s needed admin_dtm_list := first stop_after_value FROM admin_dtm_list; //set the end_dtm end_dtm := last admin_dtm_list; else //Use the End_Dtm to stop generating DTMs While new_dtm <= end_dtm do //Increment the multiplier multiplier := multiplier + 1; //Generate new DTM by looping through the list DTMS and //increasing the duration of each one for temp_dtm2 in (updated_schedule_dtm_list) do new_dtm := temp_dtm2 + (increment_duration * multiplier) ; if new_dtm <= end_dtm then admin_dtm_list := admin_dtm_list, new_dtm; endif; //if new_dtm enddo; //for temp_dtm2 enddo; //While new_dtm endif; //if stop_after_option_type = 4 //============================================= // -- IRREGULAR //============================================= else //Use the ScheduledDtm to set the Admin DTMS if exist task_schedule_definition_list.scheduled_dtm then admin_dtm_list := Sort DATA task_schedule_definition_list.scheduled_dtm; else admin_dtm_list := null; endif; //if exist task_schedule_definition_list.scheduled_dtm; endif; //if from_uom //--------------------------------------------------- // Remove any dates outside of Begin_DTM and End_DTM //--------------------------------------------------- admin_dtm_list := admin_dtm_list where (it >= begin_dtm and it <= end_dtm); //================================================================================== // REGULAR OR FREQUENCY //================================================================================== else //------------------------------------ // Frequency and Template //------------------------------------ // Handle frequency templates and by parsing the frequency string // If changes are made to this code, also change them in SYS_CALC_FREQMULT_AVERAGE // This is a temporary workaround for frequency templates If NOT Exist frequency_type then // Declare characters used for delimiting the string // Delimiters are Case Sensitive q_delim:= "Q"; h_delim:= "H"; m_delim:= "M"; // Determine if the letter in at the back of the string is H or M get_H:= call func_get_str with (order_med_frequency, h_delim); get_M:= call func_get_str with (order_med_frequency, m_delim); // Remove the front Q, so xH or xM is left trim_Q:= call func_get_token with (order_med_frequency, q_delim); // Set the time_core_uom // Remove the H or the M, leaving a string representation of a number if exist get_H then time_core_uom := hour_string; freq_num_str := call func_get_token with (trim_Q, h_delim); elseif exist get_M then time_core_uom := minute_string; freq_num_str := call func_get_token with (trim_Q, m_delim); endif; // if exist get_H // Convert string to number time_from_value := freq_num_str as number; endif; // If NOT Exist frequency_type //-------------------------------- // Convert Frequency to Durations //-------------------------------- if time_core_uom = day_string then if frequency_type = 1 // y times per day then time_interval:= 1 DAY /time_from_value; else time_interval:= time_from_value DAY ; endif; elseif time_core_uom = hour_string then if frequency_type = 1 // y times per hour then time_interval:= 1 HOUR /time_from_value; else time_interval:= time_from_value HOUR ; endif; elseif time_core_uom = minute_string then if frequency_type = 1 // y times per minute then time_interval:= 1 MINUTE /time_from_value; else time_interval:= time_from_value MINUTE ; endif; elseif time_core_uom = second_string then if frequency_type = 1 // y times per second then time_interval:= 1 SECOND /time_from_value; else time_interval:= time_from_value SECOND ; endif; elseif time_core_uom = week_string then if frequency_type = 1 // y times per week then time_interval:= 1 WEEK /time_from_value; else time_interval:= time_from_value WEEK ; endif; elseif time_core_uom = month_string then if frequency_type = 1 // y times per month then time_interval:= 1 MONTH /time_from_value; else time_interval:= time_from_value MONTH ; endif; elseif time_core_uom = year_string then if frequency_type = 1 // y times per year then time_interval:= 1 YEAR /time_from_value; else time_interval:= time_from_value YEAR ; endif; endif; // if time_core_uom // Calculate the Number of Administrations // Calculation uses CEILING to round the calculation up to // the next whole number. if NOT exist order_med_frequency OR frequency_type = 0 then // Assume that one dose is administered if the frequency is NULL // or NONE regardless of the values for StopDtm or a StopAfter. number_of_admins := 1; elseif stop_after_option_type = 1 //days then total_time := 1 day * stop_after_value; number_of_admins := ceiling (total_time/time_interval); elseif stop_after_option_type = 2 //hours then total_time := 1 hour * stop_after_value; number_of_admins := ceiling (total_time/time_interval); elseif stop_after_option_type = 3 //minutes then total_time := 1 minute * stop_after_value; number_of_admins := ceiling (total_time/time_interval); elseif stop_after_option_type = 4 //times then number_of_admins := truncate (stop_after_value); elseif exist stop_dtm and exist order_med_frequency then //Calculate the amount of time between the start_dtm and stop_dtm. //Adjust by adding 1 minute because the orders application calculates the //stop_dtm by multiplying the number of days by 24 hours and subtracting 1 minute. //We have to restore the 1 minute because this algorithm truncates, rather than rounds up. //Example: 1 day is 2005-11-05T00:00:00 to 2005-11-05T23:59:00, //rather than 2005-11-06T00:00:00 total_time := stop_dtm - start_dtm + 1 minute; number_of_admins := ceiling (total_time/time_interval); // rounds up else //If the time_interval greater than 24 hours If (time_interval / 24 hours) > 1 then //There is only 1 administration during the time_interval number_of_admins := 1; else //Calculate the number of administrations in 24 hours // by Rounding Up to the full day if there is a decimal number_of_admins := ceiling (24 HOURS/ time_interval); endif; //If (time_interval / 24 hours) endif; //if NOT exist order_med_frequency //---------------------------------------------- // Generate the Administration Date-Times (DTM) //---------------------------------------------- //Initialize the list to append date-times admin_dtm_list := (); //Generate the date-times for JJ in (1 seqto number_of_admins) do if JJ = 1 then temp_date := start_dtm; else temp_date := temp_date + time_interval; endif; admin_dtm_list := admin_dtm_list, temp_date; enddo; // Generate an end_dtm that can be used as // the start date of the next component if needed. next_startDtm := temp_date + time_interval; endif; //if order_med_frequency = "" //----------------- // Debug Variables //----------------- DEBUG_Number_Of_Days_In_Time_Interval := time_interval/ 1 DAY; DEBUG_Number_Of_Hours_In_Time_Interval := time_interval/ 1 HOUR; DEBUG_Number_Of_Minutes_In_Time_Interval := time_interval/ 1 MINUTE; DEBUG_Number_Of_Seconds_In_Time_Interval := time_interval/ 1 SECOND; DEBUG_admin_dtm_list := admin_dtm_list; // If no next_startDtm at this point, take the last administration time // and add 24 hours. This can be used as the start of next component // for a complex order. if next_startDtm is null then next_startDtm := last admin_dtm_list + 24 hours; endif; // Always Conclude True Conclude true; ;; action: return admin_dtm_list, next_startDtm, isShiftFrequency; ;; end: