Chris McMaster commited on
Commit
04e78e6
·
1 Parent(s): 8b42c1d

Improved DBI

Browse files
Files changed (3) hide show
  1. app.py +48 -2
  2. dbi_mcp.py +247 -19
  3. dbi_reference_by_route.csv +4 -0
app.py CHANGED
@@ -3,7 +3,7 @@ from typing import Dict, Any
3
  from datetime import datetime
4
 
5
  from brand_to_generic import brand_lookup
6
- from dbi_mcp import dbi_mcp
7
  from clinical_calculators import (
8
  cockcroft_gault_creatinine_clearance,
9
  ckd_epi_egfr,
@@ -53,6 +53,12 @@ def _dbi_mcp_gradio(text_block: str, route: str = "oral"):
53
  return standardize_response(result, "dbi_calculator")
54
 
55
 
 
 
 
 
 
 
56
  @with_error_handling
57
  def _cockcroft_gault_gradio(
58
  age: int, weight_kg: float, serum_creatinine: float, is_female: bool
@@ -249,6 +255,29 @@ def calculate_drug_burden_index_mcp(drug_list: str, route: str = "oral") -> str:
249
  return format_json_output(result)
250
 
251
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  @with_error_handling
253
  def calculate_creatinine_clearance_mcp(
254
  age: str, weight_kg: str, serum_creatinine: str, is_female: str
@@ -739,11 +768,26 @@ dbi_calculator_ui = gr.Interface(
739
  gr.Text(label="Route of Administration", value="oral"),
740
  ],
741
  outputs=gr.JSON(label="DBI Calculation"),
742
- title="DBI Calculator",
743
  api_name="dbi_calculator",
744
  description="Calculate Drug Burden Index (DBI) from a list of medications. Supports PRN and various dose formats.",
745
  )
746
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
747
  cockcroft_gault_ui = gr.Interface(
748
  fn=calculate_creatinine_clearance_mcp,
749
  inputs=[
@@ -850,6 +894,7 @@ demo = gr.TabbedInterface(
850
  livertox_ui,
851
  brand_generic_ui,
852
  dbi_calculator_ui,
 
853
  cockcroft_gault_ui,
854
  ckd_epi_ui,
855
  child_pugh_ui,
@@ -868,6 +913,7 @@ demo = gr.TabbedInterface(
868
  "LiverTox",
869
  "Brand to Generic",
870
  "DBI Calculator",
 
871
  "Creatinine CL",
872
  "eGFR",
873
  "Child-Pugh",
 
3
  from datetime import datetime
4
 
5
  from brand_to_generic import brand_lookup
6
+ from dbi_mcp import dbi_mcp, dbi_mcp_mixed_routes
7
  from clinical_calculators import (
8
  cockcroft_gault_creatinine_clearance,
9
  ckd_epi_egfr,
 
53
  return standardize_response(result, "dbi_calculator")
54
 
55
 
56
+ @with_error_handling
57
+ def _dbi_mcp_mixed_routes_gradio(text_block: str):
58
+ result = dbi_mcp_mixed_routes(text_block, ref_csv="dbi_reference_by_route.csv")
59
+ return standardize_response(result, "dbi_calculator_mixed_routes")
60
+
61
+
62
  @with_error_handling
63
  def _cockcroft_gault_gradio(
64
  age: int, weight_kg: float, serum_creatinine: float, is_female: bool
 
255
  return format_json_output(result)
256
 
257
 
258
+ @with_error_handling
259
+ def calculate_drug_burden_index_mixed_routes_mcp(drug_list: str) -> str:
260
+ """
261
+ Calculate Drug Burden Index (DBI) from a list of medications with automatic route detection.
262
+
263
+ This enhanced version automatically detects the route of administration for each medication
264
+ (oral, transdermal patches, parenteral injections, etc.) and uses the appropriate reference
265
+ data for each route. Perfect for mixed medication lists.
266
+
267
+ Args:
268
+ drug_list (str): Drug list (one per line, include dose and frequency - also write "prn" if the drug is a PRN medication)
269
+ Examples:
270
+ - "Fentanyl 25mcg/hr patch daily"
271
+ - "Amitriptyline 25mg tablet twice daily"
272
+ - "Morphine 10mg injection PRN"
273
+
274
+ Returns:
275
+ str: JSON string with DBI calculation results broken down by route and individual drug contributions
276
+ """
277
+ result = _dbi_mcp_mixed_routes_gradio(drug_list)
278
+ return format_json_output(result)
279
+
280
+
281
  @with_error_handling
282
  def calculate_creatinine_clearance_mcp(
283
  age: str, weight_kg: str, serum_creatinine: str, is_female: str
 
768
  gr.Text(label="Route of Administration", value="oral"),
769
  ],
770
  outputs=gr.JSON(label="DBI Calculation"),
771
+ title="DBI Calculator (Single Route)",
772
  api_name="dbi_calculator",
773
  description="Calculate Drug Burden Index (DBI) from a list of medications. Supports PRN and various dose formats.",
774
  )
775
 
776
+ dbi_mixed_routes_ui = gr.Interface(
777
+ fn=calculate_drug_burden_index_mixed_routes_mcp,
778
+ inputs=[
779
+ gr.Textbox(
780
+ label="Drug List (one per line, include dose and frequency)",
781
+ lines=10,
782
+ placeholder="e.g., Fentanyl 25mcg/hr patch daily\nAmitriptyline 25mg tablet twice daily\nMorphine 10mg injection PRN",
783
+ ),
784
+ ],
785
+ outputs=gr.JSON(label="DBI Calculation with Route Detection"),
786
+ title="DBI Calculator (Mixed Routes)",
787
+ api_name="dbi_calculator_mixed_routes",
788
+ description="Enhanced DBI calculator that automatically detects routes (oral, patches, injections, etc.) and uses appropriate reference data for each medication.",
789
+ )
790
+
791
  cockcroft_gault_ui = gr.Interface(
792
  fn=calculate_creatinine_clearance_mcp,
793
  inputs=[
 
894
  livertox_ui,
895
  brand_generic_ui,
896
  dbi_calculator_ui,
897
+ dbi_mixed_routes_ui,
898
  cockcroft_gault_ui,
899
  ckd_epi_ui,
900
  child_pugh_ui,
 
913
  "LiverTox",
914
  "Brand to Generic",
915
  "DBI Calculator",
916
+ "DBI Mixed Routes",
917
  "Creatinine CL",
918
  "eGFR",
919
  "Child-Pugh",
dbi_mcp.py CHANGED
@@ -18,9 +18,13 @@ except ImportError:
18
 
19
  __all__ = [
20
  "load_reference",
 
21
  "load_patient_meds",
22
  "calculate_dbi",
23
  "print_report",
 
 
 
24
  ]
25
 
26
  PatientInput = Union[
@@ -29,12 +33,69 @@ PatientInput = Union[
29
  Mapping[str, float],
30
  ]
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  def _normalise_name(name: str) -> str:
34
  """Strip/-lower a drug name for key matching."""
35
  return name.strip().lower()
36
 
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  def load_reference(
39
  ref_path: Path,
40
  *,
@@ -72,6 +133,51 @@ def load_reference(
72
 
73
  return ref
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  def calculate_dbi(
76
  patient_meds: Mapping[str, float],
77
  reference: Mapping[str, Tuple[float, str]],
@@ -141,7 +247,7 @@ def _freq_to_per_day(token: str) -> float:
141
  return 24 / hrs if hrs else 1
142
  return 1
143
 
144
- Parsed = Tuple[str, float, bool]
145
 
146
  @functools.lru_cache(maxsize=2048)
147
  def _parse_line(line: str) -> Optional[Parsed]:
@@ -150,13 +256,15 @@ def _parse_line(line: str) -> Optional[Parsed]:
150
  return None
151
 
152
  is_prn = "prn" in original.lower()
 
153
 
154
  m_patch = PATCH_PAT.search(original)
155
  if m_patch:
156
  mcg_hr = float(m_patch.group("val").replace(",", "."))
157
  mg_day = (mcg_hr * 24) / 1_000 # µg/hr → mg/day
158
  name_part = PATCH_PAT.sub("", original).split()[0]
159
- return (name_part, mg_day, is_prn)
 
160
 
161
  m_conc = CONC_PAT.search(original)
162
  m_vol = VOL_PAT.search(original)
@@ -175,7 +283,7 @@ def _parse_line(line: str) -> Optional[Parsed]:
175
  freq = _freq_to_per_day(m_freq.group(0))
176
  mg_day = mg_per_dose * freq
177
  name_part = CONC_PAT.split(original)[0].strip()
178
- return (name_part, mg_day, is_prn)
179
 
180
  m = UNIT_PAT.search(original)
181
  if m:
@@ -192,30 +300,35 @@ def _parse_line(line: str) -> Optional[Parsed]:
192
  name_part = original[:m.start()].strip()
193
  name_part = re.sub(r"[^A-Za-z0-9\s]", " ", name_part)
194
  name_part = re.sub(r"\s+", " ", name_part).strip()
195
- return (name_part, mg_day, is_prn)
196
 
197
  logger.debug("unhandled line: %s", original)
198
  return None
199
 
200
- def _smart_drug_lookup(raw_name: str, reference_data: dict) -> str:
201
  """
202
  Smart drug name resolution that avoids unnecessary API calls.
 
203
 
204
- 1. First checks if the name (or close variant) exists in reference data
205
  2. Only calls brand_lookup API if not found in reference
206
  3. Returns the best generic name match
207
  """
208
  clean_name = raw_name.strip().lower()
209
 
210
- if clean_name in reference_data:
211
- logger.debug(f"Direct match found for '{raw_name}' in reference data")
212
- return clean_name
 
 
213
 
214
- for ref_name in reference_data.keys():
215
- if len(clean_name) >= 4 and len(ref_name) >= 4:
216
- if clean_name in ref_name or ref_name in clean_name:
217
- logger.debug(f"Partial match found: '{raw_name}' -> '{ref_name}' in reference data")
218
- return ref_name
 
 
219
 
220
  common_variations = {
221
  'acetaminophen': 'paracetamol',
@@ -228,9 +341,11 @@ def _smart_drug_lookup(raw_name: str, reference_data: dict) -> str:
228
 
229
  if clean_name in common_variations:
230
  alt_name = common_variations[clean_name]
231
- if alt_name in reference_data:
232
- logger.debug(f"Found common variation: '{raw_name}' -> '{alt_name}' in reference data")
233
- return alt_name
 
 
234
 
235
  logger.debug(f"'{raw_name}' not found in reference data, trying brand lookup API")
236
  try:
@@ -263,8 +378,11 @@ def dbi_mcp(text_block: str, *, ref_csv: Union[str, Path] = "dbi_reference_by_ro
263
  meds_with: Dict[str, float] = {}
264
  meds_without: Dict[str, float] = {}
265
 
266
- for raw_name, mg_day, is_prn in parsed:
267
- generic = _smart_drug_lookup(raw_name, ref)
 
 
 
268
 
269
  meds_with[generic] = meds_with.get(generic, 0.0) + mg_day
270
  if not is_prn:
@@ -286,6 +404,116 @@ def dbi_mcp(text_block: str, *, ref_csv: Union[str, Path] = "dbi_reference_by_ro
286
  }
287
 
288
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  if __name__ == "__main__":
290
  import sys
291
  import pprint
 
18
 
19
  __all__ = [
20
  "load_reference",
21
+ "load_all_routes_reference",
22
  "load_patient_meds",
23
  "calculate_dbi",
24
  "print_report",
25
+ "detect_route_from_text",
26
+ "dbi_mcp",
27
+ "dbi_mcp_mixed_routes",
28
  ]
29
 
30
  PatientInput = Union[
 
33
  Mapping[str, float],
34
  ]
35
 
36
+ # Route detection patterns
37
+ ROUTE_PATTERNS = {
38
+ 'transdermal': [
39
+ r'\bpatch(es)?\b',
40
+ r'\btransdermal\b',
41
+ r'\bmcg/hr\b',
42
+ r'\bμg/hr\b',
43
+ r'\bmicrograms?/hr\b',
44
+ r'\bmicrograms?/hour\b',
45
+ ],
46
+ 'parenteral': [
47
+ r'\binjection\b',
48
+ r'\biv\b',
49
+ r'\bim\b',
50
+ r'\bsc\b',
51
+ r'\bsubcut\b',
52
+ r'\bsubcutaneous\b',
53
+ r'\bintravenous\b',
54
+ r'\bintramuscular\b',
55
+ r'\bparenteral\b',
56
+ ],
57
+ 'sublingual_buccal': [
58
+ r'\bsublingual\b',
59
+ r'\bbuccal\b',
60
+ r'\bsl\b',
61
+ r'\bunder.?tongue\b',
62
+ ],
63
+ 'oral': [
64
+ r'\btab(let)?s?\b',
65
+ r'\bcap(sule)?s?\b',
66
+ r'\boral\b',
67
+ r'\bpo\b',
68
+ r'\bby.?mouth\b',
69
+ r'\bliquid\b',
70
+ r'\bsyrup\b',
71
+ r'\bsolution\b',
72
+ r'\bsuspension\b',
73
+ ]
74
+ }
75
+
76
 
77
  def _normalise_name(name: str) -> str:
78
  """Strip/-lower a drug name for key matching."""
79
  return name.strip().lower()
80
 
81
 
82
+ def detect_route_from_text(text: str) -> str:
83
+ """
84
+ Detect the most likely route of administration from medication text.
85
+ Returns the detected route or 'oral' as default.
86
+ """
87
+ text_lower = text.lower()
88
+
89
+ # Check each route pattern
90
+ for route, patterns in ROUTE_PATTERNS.items():
91
+ for pattern in patterns:
92
+ if re.search(pattern, text_lower):
93
+ return route
94
+
95
+ # Default to oral if no specific route detected
96
+ return 'oral'
97
+
98
+
99
  def load_reference(
100
  ref_path: Path,
101
  *,
 
133
 
134
  return ref
135
 
136
+
137
+ def load_all_routes_reference(
138
+ ref_path: Path,
139
+ *,
140
+ use_pandas: bool | None = None,
141
+ ) -> Dict[str, Dict[str, Tuple[float, str]]]:
142
+ """
143
+ Load reference data for all routes.
144
+ Returns mapping: route → {generic → (δ, drug_class)}
145
+ """
146
+ if use_pandas is None:
147
+ use_pandas = pd is not None
148
+
149
+ all_routes: Dict[str, Dict[str, Tuple[float, str]]] = {}
150
+
151
+ if use_pandas:
152
+ df = pd.read_csv(ref_path)
153
+ for _, row in df.iterrows():
154
+ route = row["route"].strip().lower()
155
+ generic = _normalise_name(row["generic_name"])
156
+
157
+ if route not in all_routes:
158
+ all_routes[route] = {}
159
+
160
+ all_routes[route][generic] = (
161
+ float(row["min_daily_dose_mg"]),
162
+ row["drug_class"].strip().lower(),
163
+ )
164
+ else:
165
+ with ref_path.open(newline="") as f:
166
+ rdr = csv.DictReader(f)
167
+ for row in rdr:
168
+ route = row["route"].strip().lower()
169
+ generic = _normalise_name(row["generic_name"])
170
+
171
+ if route not in all_routes:
172
+ all_routes[route] = {}
173
+
174
+ all_routes[route][generic] = (
175
+ float(row["min_daily_dose_mg"]),
176
+ row["drug_class"].strip().lower(),
177
+ )
178
+
179
+ return all_routes
180
+
181
  def calculate_dbi(
182
  patient_meds: Mapping[str, float],
183
  reference: Mapping[str, Tuple[float, str]],
 
247
  return 24 / hrs if hrs else 1
248
  return 1
249
 
250
+ Parsed = Tuple[str, float, bool, str] # Added route detection
251
 
252
  @functools.lru_cache(maxsize=2048)
253
  def _parse_line(line: str) -> Optional[Parsed]:
 
256
  return None
257
 
258
  is_prn = "prn" in original.lower()
259
+ detected_route = detect_route_from_text(original)
260
 
261
  m_patch = PATCH_PAT.search(original)
262
  if m_patch:
263
  mcg_hr = float(m_patch.group("val").replace(",", "."))
264
  mg_day = (mcg_hr * 24) / 1_000 # µg/hr → mg/day
265
  name_part = PATCH_PAT.sub("", original).split()[0]
266
+ # Override route detection for patches
267
+ return (name_part, mg_day, is_prn, "transdermal")
268
 
269
  m_conc = CONC_PAT.search(original)
270
  m_vol = VOL_PAT.search(original)
 
283
  freq = _freq_to_per_day(m_freq.group(0))
284
  mg_day = mg_per_dose * freq
285
  name_part = CONC_PAT.split(original)[0].strip()
286
+ return (name_part, mg_day, is_prn, detected_route)
287
 
288
  m = UNIT_PAT.search(original)
289
  if m:
 
300
  name_part = original[:m.start()].strip()
301
  name_part = re.sub(r"[^A-Za-z0-9\s]", " ", name_part)
302
  name_part = re.sub(r"\s+", " ", name_part).strip()
303
+ return (name_part, mg_day, is_prn, detected_route)
304
 
305
  logger.debug("unhandled line: %s", original)
306
  return None
307
 
308
+ def _smart_drug_lookup(raw_name: str, all_routes_reference: Dict[str, Dict[str, Tuple[float, str]]]) -> str:
309
  """
310
  Smart drug name resolution that avoids unnecessary API calls.
311
+ Works with multi-route reference data.
312
 
313
+ 1. First checks if the name (or close variant) exists in any route's reference data
314
  2. Only calls brand_lookup API if not found in reference
315
  3. Returns the best generic name match
316
  """
317
  clean_name = raw_name.strip().lower()
318
 
319
+ # Check all routes for direct match
320
+ for route_data in all_routes_reference.values():
321
+ if clean_name in route_data:
322
+ logger.debug(f"Direct match found for '{raw_name}' in reference data")
323
+ return clean_name
324
 
325
+ # Check all routes for partial match
326
+ for route_data in all_routes_reference.values():
327
+ for ref_name in route_data.keys():
328
+ if len(clean_name) >= 4 and len(ref_name) >= 4:
329
+ if clean_name in ref_name or ref_name in clean_name:
330
+ logger.debug(f"Partial match found: '{raw_name}' -> '{ref_name}' in reference data")
331
+ return ref_name
332
 
333
  common_variations = {
334
  'acetaminophen': 'paracetamol',
 
341
 
342
  if clean_name in common_variations:
343
  alt_name = common_variations[clean_name]
344
+ # Check all routes for the alternative name
345
+ for route_data in all_routes_reference.values():
346
+ if alt_name in route_data:
347
+ logger.debug(f"Found common variation: '{raw_name}' -> '{alt_name}' in reference data")
348
+ return alt_name
349
 
350
  logger.debug(f"'{raw_name}' not found in reference data, trying brand lookup API")
351
  try:
 
378
  meds_with: Dict[str, float] = {}
379
  meds_without: Dict[str, float] = {}
380
 
381
+ # Load all routes for smart lookup (backward compatibility)
382
+ all_routes_ref = load_all_routes_reference(Path(ref_csv))
383
+
384
+ for raw_name, mg_day, is_prn, detected_route in parsed:
385
+ generic = _smart_drug_lookup(raw_name, all_routes_ref)
386
 
387
  meds_with[generic] = meds_with.get(generic, 0.0) + mg_day
388
  if not is_prn:
 
404
  }
405
 
406
 
407
+ def dbi_mcp_mixed_routes(text_block: str, *, ref_csv: Union[str, Path] = "dbi_reference_by_route.csv") -> dict:
408
+ """
409
+ Enhanced DBI calculator that handles mixed routes automatically.
410
+
411
+ This function:
412
+ 1. Detects the route for each medication from the text
413
+ 2. Uses the appropriate reference data for each route
414
+ 3. Provides detailed breakdown by route and medication
415
+ """
416
+ all_routes_ref = load_all_routes_reference(Path(ref_csv))
417
+
418
+ parsed: List[Parsed] = []
419
+ unmatched: List[str] = []
420
+ route_stats: Dict[str, int] = {}
421
+
422
+ for ln in text_block.splitlines():
423
+ res = _parse_line(ln)
424
+ if res:
425
+ parsed.append(res)
426
+ route = res[3] # detected route
427
+ route_stats[route] = route_stats.get(route, 0) + 1
428
+ else:
429
+ unmatched.append(ln)
430
+
431
+ # Organize medications by route and PRN status
432
+ meds_by_route_with: Dict[str, Dict[str, float]] = {}
433
+ meds_by_route_without: Dict[str, Dict[str, float]] = {}
434
+ medication_details: List[Dict] = []
435
+
436
+ for raw_name, mg_day, is_prn, detected_route in parsed:
437
+ generic = _smart_drug_lookup(raw_name, all_routes_ref)
438
+
439
+ # Initialize route dictionaries if needed
440
+ if detected_route not in meds_by_route_with:
441
+ meds_by_route_with[detected_route] = {}
442
+ meds_by_route_without[detected_route] = {}
443
+
444
+ # Add to appropriate dictionaries
445
+ meds_by_route_with[detected_route][generic] = meds_by_route_with[detected_route].get(generic, 0.0) + mg_day
446
+ if not is_prn:
447
+ meds_by_route_without[detected_route][generic] = meds_by_route_without[detected_route].get(generic, 0.0) + mg_day
448
+
449
+ # Store medication details
450
+ medication_details.append({
451
+ "original_text": f"{raw_name} {mg_day}mg/day",
452
+ "generic_name": generic,
453
+ "dose_mg_day": mg_day,
454
+ "detected_route": detected_route,
455
+ "is_prn": is_prn
456
+ })
457
+
458
+ # Calculate DBI for each route
459
+ route_results = {}
460
+ total_dbi_with = 0.0
461
+ total_dbi_without = 0.0
462
+ all_details_with = []
463
+ all_details_without = []
464
+
465
+ for route in meds_by_route_with.keys():
466
+ if route in all_routes_ref:
467
+ route_ref = all_routes_ref[route]
468
+
469
+ # Calculate DBI for this route
470
+ dbi_with, details_with = calculate_dbi(meds_by_route_with[route], route_ref)
471
+ dbi_without, details_without = calculate_dbi(meds_by_route_without[route], route_ref)
472
+
473
+ total_dbi_with += dbi_with
474
+ total_dbi_without += dbi_without
475
+
476
+ # Format details
477
+ def _format_details(details, route_name):
478
+ formatted = []
479
+ for g, d, delta, dbi in details:
480
+ formatted.append({
481
+ "generic_name": g,
482
+ "dose_mg_day": d,
483
+ "delta_mg": delta,
484
+ "dbi_component": dbi,
485
+ "route": route_name
486
+ })
487
+ return formatted
488
+
489
+ route_details_with = _format_details(details_with, route)
490
+ route_details_without = _format_details(details_without, route)
491
+
492
+ all_details_with.extend(route_details_with)
493
+ all_details_without.extend(route_details_without)
494
+
495
+ route_results[route] = {
496
+ "dbi_with_prn": round(dbi_with, 2),
497
+ "dbi_without_prn": round(dbi_without, 2),
498
+ "details_with_prn": route_details_with,
499
+ "details_without_prn": route_details_without,
500
+ "medication_count": route_stats.get(route, 0)
501
+ }
502
+
503
+ return {
504
+ "mixed_routes": True,
505
+ "total_dbi_without_prn": round(total_dbi_without, 2),
506
+ "total_dbi_with_prn": round(total_dbi_with, 2),
507
+ "routes_detected": list(route_stats.keys()),
508
+ "route_statistics": route_stats,
509
+ "route_breakdown": route_results,
510
+ "all_details_without_prn": all_details_without,
511
+ "all_details_with_prn": all_details_with,
512
+ "medication_details": medication_details,
513
+ "unmatched_input": unmatched,
514
+ }
515
+
516
+
517
  if __name__ == "__main__":
518
  import sys
519
  import pprint
dbi_reference_by_route.csv CHANGED
@@ -117,3 +117,7 @@ trimipramine,oral,37.5,both
117
  triprolidine,oral,10,both
118
  zuclopenthixol,oral,20,both
119
  zuclopenthixol,parenteral,11.4,both
 
 
 
 
 
117
  triprolidine,oral,10,both
118
  zuclopenthixol,oral,20,both
119
  zuclopenthixol,parenteral,11.4,both
120
+ fentanyl,transdermal,0.3,sedative
121
+ buprenorphine,transdermal,0.12,sedative
122
+ clonidine,transdermal,0.1,both
123
+ scopolamine,transdermal,0.5,both