11from .classes .parser import *
2- from . import method , dose , strength , route , frequency , when , duration , indication , additional_info
2+ from . import method , dose , strength , route , frequency , when , duration , indication , max , additional_info
33import csv
44
55# TODO: need to move all this to the main app and re-purpose the sig.py parser
@@ -19,11 +19,12 @@ class SigParser(Parser):
1919 'when' : when .parsers ,
2020 'duration' : duration .parsers ,
2121 'indication' : indication .parsers ,
22+ 'max' : max .parsers ,
2223 'additional_info' : additional_info .parsers ,
2324 }
2425 # TODO: make this match_keys assignment more elegant
25- #match_keys = ['original_sig_text'] + ['sig_text', 'sig_readable'] + method.parsers[0].match_keys + dose.parsers[0].match_keys + strength.parsers[0].match_keys + route.parsers[0].match_keys + frequency.parsers[0].match_keys + when.parsers[0].match_keys + duration.parsers[0].match_keys + indication.parsers[0].match_keys + additional_info.parsers[0].match_keys
26- match_keys = ['sig_text' , 'sig_readable' ] + method .parsers [0 ].match_keys + dose .parsers [0 ].match_keys + strength .parsers [0 ].match_keys + route .parsers [0 ].match_keys + frequency .parsers [0 ].match_keys + when .parsers [0 ].match_keys + duration .parsers [0 ].match_keys + indication .parsers [0 ].match_keys + additional_info .parsers [0 ].match_keys
26+ #match_keys = ['original_sig_text'] + ['sig_text', 'sig_readable'] + method.parsers[0].match_keys + dose.parsers[0].match_keys + strength.parsers[0].match_keys + route.parsers[0].match_keys + frequency.parsers[0].match_keys + when.parsers[0].match_keys + duration.parsers[0].match_keys + indication.parsers[0].match_keys + max.parsers[0].match_keys + additional_info.parsers[0].match_keys
27+ match_keys = ['sig_text' , 'sig_readable' ] + method .parsers [0 ].match_keys + dose .parsers [0 ].match_keys + strength .parsers [0 ].match_keys + route .parsers [0 ].match_keys + frequency .parsers [0 ].match_keys + when .parsers [0 ].match_keys + duration .parsers [0 ].match_keys + indication .parsers [0 ].match_keys + max . parsers [ 0 ]. match_keys + additional_info .parsers [0 ].match_keys
2728 parser_type = 'sig'
2829
2930 def get_normalized_sig_text (self , sig_text ):
@@ -38,27 +39,85 @@ def get_normalized_sig_text(self, sig_text):
3839 sig_text = ' ' .join (sig_text .split ())
3940 return sig_text
4041
41- def get_readable (self , method = None , dose = None , strength = None , route = None , frequency = None , when = None , duration = None , indication = None , additional_info = None ):
42- method = method if method else ''
43- dose = dose if dose else ''
44- strength = strength if strength else ''
45- route = route if route else ''
46- frequency = frequency if frequency else ''
47- when = when if when else ''
48- duration = duration if duration else ''
49- indication = indication if indication else ''
50- additional_info = additional_info if additional_info else ''
42+ def get_readable (self , match_dict ):
43+ method = match_dict ['method_readable' ] if match_dict ['method_readable' ] else ''
44+ dose = match_dict ['dose_readable' ] if match_dict ['dose_readable' ] else ''
45+ strength = match_dict ['strength_readable' ] if match_dict ['strength_readable' ] else ''
46+ route = match_dict ['route_readable' ] if match_dict ['route_readable' ] else ''
47+ frequency = match_dict ['frequency_readable' ] if match_dict ['frequency_readable' ] else ''
48+ when = match_dict ['when_readable' ] if match_dict ['when_readable' ] else ''
49+ duration = match_dict ['duration_readable' ] if match_dict ['duration_readable' ] else ''
50+ indication = match_dict ['indication_readable' ] if match_dict ['indication_readable' ] else ''
51+ max = match_dict ['max_readable' ] if match_dict ['max_readable' ] else ''
52+ additional_info = match_dict ['additional_info_readable' ] if match_dict ['additional_info_readable' ] else ''
5153
5254 if dose != '' and strength != '' :
5355 strength = '(' + strength + ')'
54- sig_elements = [method , dose , strength , route , frequency , when , duration , indication , additional_info ]
56+ sig_elements = [method , dose , strength , route , frequency , when , duration , indication , max , additional_info ]
5557 # join sig elements with spaces
5658 readable = ' ' .join (sig_elements )
5759 # remove duplicate spaces, and in doing so, also trim whitespaces from around sig
5860 # this accounts for empty sig elements
5961 readable = ' ' .join (readable .split ())
6062 return readable
6163
64+ def get_period_per_day (self , period , period_unit ):
65+ if not period :
66+ return None
67+
68+ if period_unit == 'hour' :
69+ return 24 / period
70+ elif period_unit == 'day' :
71+ return 1 / period
72+ elif period_unit == 'week' :
73+ return 1 / (7 * period )
74+ elif period_unit == 'month' :
75+ return 1 / (30 * period )
76+ else :
77+ return None
78+
79+ def get_max_dose_per_day (self , match_dict ):
80+ # calculate max per day from sig instructions
81+ frequency = match_dict ['frequency_max' ] or match_dict ['frequency' ]
82+ period = match_dict ['period' ]
83+ period_unit = get_normalized (PERIOD_UNIT , match_dict ['period_unit' ]) if match_dict ['period_unit' ] else match_dict ['period_unit' ]
84+ # period_per_day can be null if period_unit doesn't match hour / day / week / month
85+ period_per_day = self .get_period_per_day (period , period_unit )
86+
87+ dose = match_dict ['dose_max' ] or match_dict ['dose' ]
88+ dose_unit = match_dict ['dose_unit' ]
89+
90+ max_dose_per_day_sig = None
91+ if frequency and period_per_day and dose :
92+ max_dose_per_day_sig = frequency * period_per_day * dose
93+
94+ # calculate max per day from max dose (i.e. "max daily dose = 3" or "no more than 2 per week")
95+ frequency_max = 1
96+ period_max = match_dict ['max_denominator_value' ]
97+ period_unit_max = match_dict ['max_denominator_unit' ]
98+ # can be null if period_unit doesn't match
99+ period_per_day_max = self .get_period_per_day (period_max , period_unit_max )
100+
101+ dose_max = match_dict ['max_numerator_value' ]
102+ dose_unit_max = match_dict ['max_numerator_unit' ]
103+
104+ max_dose_per_day_max = None
105+ if frequency_max and period_per_day_max and dose_max :
106+ max_dose_per_day_max = frequency_max * period_per_day_max * dose_max
107+
108+ max_dose_per_day = None
109+ # if we are dealing with a complex dose unit, don't return a max_dose_per_day
110+ if dose_unit in EXCLUDED_MDD_DOSE_UNITS or dose_unit_max in EXCLUDED_MDD_DOSE_UNITS :
111+ return max_dose_per_day
112+ # if (at least one max dose is not null) and (the dose units match or one of the dose units is null)
113+ if (max_dose_per_day_sig or max_dose_per_day_max ) and (dose_unit == dose_unit_max or not dose_unit or not dose_unit_max ):
114+ # originally wrote this to choose the lowest dose per day
115+ # max_dose_per_day = min(d for d in [max_dose_per_day_sig, max_dose_per_day_max] if d is not None)
116+ # however, requirements changed to always prefer max over sig
117+ max_dose_per_day = max_dose_per_day_max or max_dose_per_day_sig
118+
119+ return max_dose_per_day
120+
62121 def parse (self , sig_text ):
63122 match_dict = dict (self .match_dict )
64123 #match_dict['original_sig_text'] = sig_text
@@ -82,17 +141,9 @@ def parse(self, sig_text):
82141 for k , v in match .items ():
83142 match_dict [k ] = v
84143 #elif len(matches) == 0:
85- match_dict ['sig_readable' ] = self .get_readable (
86- method = match_dict ['method_readable' ],
87- dose = match_dict ['dose_readable' ],
88- strength = match_dict ['strength_readable' ],
89- route = match_dict ['route_readable' ],
90- frequency = match_dict ['frequency_readable' ],
91- when = match_dict ['when_readable' ],
92- duration = match_dict ['duration_readable' ],
93- indication = match_dict ['indication_readable' ],
94- additional_info = match_dict ['additional_info_readable' ],
95- )
144+ match_dict ['sig_readable' ] = self .get_readable (match_dict )
145+ match_dict ['max_dose_per_day' ] = self .get_max_dose_per_day (match_dict )
146+
96147 # calculate admin instructions based on leftover pieces of sig
97148 # would need to calculate overlap in each of the match_dicts
98149 # in doing so, maybe also return a map of the parsed parts of the sig for use in frontend highlighting
0 commit comments