551 lines
20 KiB
Plaintext
551 lines
20 KiB
Plaintext
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, <Variable Interval>, <User Schedule>, 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 ];
|
|
|
|
|
|
//================================================================
|
|
// <User Schedule> FREQUENCY
|
|
//================================================================
|
|
if order_med_frequency = "<User Schedule>"
|
|
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 };
|
|
|
|
|
|
//================================================================
|
|
// <Variable Interval> FREQUENCY
|
|
//================================================================
|
|
elseif order_med_frequency = "<Variable Interval>"
|
|
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;
|
|
|
|
//==================================================================================
|
|
// <User Schedule> Frequency
|
|
//==================================================================================
|
|
if order_med_frequency = "<User Schedule>"
|
|
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
|
|
|
|
|
|
//=============================================
|
|
// <User Schedule> -- 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
|
|
|
|
|
|
//=============================================
|
|
// <User Schedule> -- 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 <Variable Interval> FREQUENCY
|
|
//==================================================================================
|
|
else
|
|
//------------------------------------
|
|
// Frequency <QxH> and <QxM> Template
|
|
//------------------------------------
|
|
// Handle frequency templates <qxh> and <qxm> 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 = "<User Schedule>"
|
|
|
|
//-----------------
|
|
// 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:
|