Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Informedica.GenFORM.Lib/DoseLimit.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module DoseLimit =

/// Field labels for deterministic parsing
module FieldLabels =
let [<Literal>] DoseUnit = "[dun]"
let [<Literal>] Quantity = "[qty]"
let [<Literal>] QuantityAdjust = "[qty-adj]"
let [<Literal>] PerTime = "[per-time]"
Expand Down Expand Up @@ -161,8 +162,11 @@ module DoseLimit =
[
let perDose = "/dosis"
let emptyS = ""
let dun = dl.DoseUnit |> Units.toStringEngShortWithoutGroup
[
$"%s{dl.DoseLimitTarget |> LimitTarget.toString}"
if dun |> String.notEmpty then
$"{FieldLabels.DoseUnit} %s{dun}"

$"%s{dl.Rate |> printMinMaxDose FieldLabels.Rate emptyS}"
$"%s{dl.RateAdjust |> printMinMaxDose FieldLabels.RateAdjust emptyS}"
Expand Down
27 changes: 18 additions & 9 deletions src/Informedica.GenFORM.Lib/DoseRule.fs
Original file line number Diff line number Diff line change
Expand Up @@ -535,16 +535,25 @@ module DoseRule =
createError "getDataResult" exn



let doseRuleDataIsValid (dd: DoseRuleData) =
dd.DoseType |> String.notEmpty &&
(dd.Frequencies |> Array.length > 0 && dd.FreqUnit |> String.notEmpty ||
dd.MaxQty |> Option.isSome ||
dd.MaxQtyAdj |> Option.isSome ||
dd.MaxPerTime |> Option.isSome ||
dd.MaxPerTime |> Option.isSome ||
dd.MaxRate |> Option.isSome ||
dd.MaxRateAdj |> Option.isSome)
match dd.DoseText |> DoseType.fromString dd.DoseType with
| NoDoseType ->
// assume an empty dose type is deliberate
if dd.DoseType |> String.notEmpty then
$"Not valid dose rule data:\n{dd}\n"
|> ConsoleWriter.NewLineNoTime.writeWarningMessage
false
| Once _ -> true
| OnceTimed _ ->
dd.MaxTime.IsSome && dd.TimeUnit |> String.notEmpty
| Discontinuous _ ->
dd.Frequencies |> Array.length > 0 && dd.FreqUnit |> String.notEmpty
| Timed _ ->
dd.Frequencies |> Array.length > 0 && dd.FreqUnit |> String.notEmpty &&
dd.MaxTime.IsSome && dd.TimeUnit |> String.notEmpty
| Continuous _ ->
dd.RateUnit |> String.notEmpty &&
(dd.MaxRate.IsSome || dd.MaxRateAdj.IsSome)


let processDoseRuleData prods routeMapping (data, msgs) : GenFormResult<_> =
Expand Down
36 changes: 25 additions & 11 deletions src/Informedica.GenORDER.Lib/Medication.fs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ module Medication =
else
// All known field labels
let allLabels = [
DoseLimit.FieldLabels.DoseUnit
DoseLimit.FieldLabels.Quantity
DoseLimit.FieldLabels.QuantityAdjust
DoseLimit.FieldLabels.PerTime
Expand Down Expand Up @@ -314,17 +315,26 @@ module Medication =
let valueStr = m.Groups[2].Value.Trim().TrimEnd(',').Trim()
let fullLabel = $"[{labelContent}]"

let labelMatch =
fieldParsers
|> List.tryFind (fun (label, _, _) -> label = fullLabel)

match labelMatch with
| Some (label, parser, setter) ->
match parser valueStr with
| Ok mm -> dl <- setter dl mm
| Error e -> errors <- $"{label}: {e}" :: errors
| None ->
errors <- $"Unknown field label: {fullLabel}" :: errors
// Skip empty values - they represent default/unset fields
if valueStr |> String.IsNullOrWhiteSpace then
() // Skip this field
elif fullLabel = DoseLimit.FieldLabels.DoseUnit then
let dun = valueStr |> Units.fromString
match dun with
| Some un -> dl <- { dl with DoseUnit = un }
| None -> errors <- $"Unknown dose unit: {valueStr}"::errors
else
let labelMatch =
fieldParsers
|> List.tryFind (fun (label, _, _) -> label = fullLabel)

match labelMatch with
| Some (label, parser, setter) ->
match parser valueStr with
| Ok mm -> dl <- setter dl mm
| Error e -> errors <- $"{label}: {e}" :: errors
| None ->
errors <- $"Unknown field label: {fullLabel}" :: errors

// If no labeled matches and we have constraintsStr, return error requiring labels
if matches.Count = 0 && not (constraintsStr |> String.IsNullOrWhiteSpace) then
Expand Down Expand Up @@ -353,6 +363,10 @@ module Medication =

if not (valueStr |> String.IsNullOrWhiteSpace) then
match label with
| "qts" ->
match parseValueUnitOpt valueStr with
| Ok vuOpt -> sl <- { sl with Quantities = vuOpt }
| Error e -> errors <- $"[qts]: {e}"::errors
| "qty" ->
match parseMinMax valueStr with
| Ok mm -> sl <- { sl with Quantity = mm }
Expand Down
12 changes: 8 additions & 4 deletions src/Informedica.GenORDER.Lib/Order.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4118,9 +4118,14 @@ module Order =
[|
[|
// the orderable dose quantity
ord.Orderable
|> Orderable.Print.doseQuantityTo printMd -1
|> wrap Alert [ ord.Orderable.Dose.Quantity |> Quantity.toOrdVar ]
let doseQty =
ord.Orderable
|> Orderable.Print.doseQuantityTo printMd -1
// special case when no dose quantity
if doseQty |> String.isNullOrWhiteSpace then "eenmalig" |> Valid
else
doseQty
|> wrap Alert [ ord.Orderable.Dose.Quantity |> Quantity.toOrdVar ]

// the orderable dose adjust quantity
if useAdj then
Expand All @@ -4129,7 +4134,6 @@ module Order =
ord.Orderable
|> Orderable.Print.doseQuantityAdjustTo printMd -1
|> wrap Alert [ ord.Orderable.Dose.QuantityAdjust |> QuantityAdjust.toOrdVar ]

|]
|]
else
Expand Down
2 changes: 1 addition & 1 deletion src/Informedica.GenORDER.Lib/OrderLogging.fs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ module OrderLogging =
$"Scenario {oid}: {n |> Name.toString} = {v}"

| Events.MedicationCreated m ->
$"Medication created: {m}"
$"Medication created:\n\n{m}\n"

| Events.ComponentItemsHarmonized s -> s

Expand Down
231 changes: 228 additions & 3 deletions src/Informedica.GenORDER.Lib/Scripts/Medication.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -361,9 +361,6 @@ Components:
runTestsWithCLIArgs [] [||] tests


"10 mg/kg - 20 mg/kg"
|> MinMax.parseMinMax

// Demo: Show the new labeled output format
printfn "\n=== Demo: Labeled DoseLimit output ==="
Scenarios.amfo
Expand Down Expand Up @@ -419,3 +416,231 @@ printfn "\n=== Demo: tpn ==="
Scenarios.tpn
|> Medication.toString
|> print


module MedicationTexts =


let onceSingleComponentSingleItemNoDose = """
Id: 4b73efb9-97b3-44b1-af51-bfad909a9371
Name: chloorhexidine
Quantity:
Quantities:
Route: CUTAAN
OrderType: OnceOrder
Adjust: 11 kg
Frequencies:
Time:
Dose:
Div:
DoseCount: 1 x
Components:

Name: chloorhexidine
Form: creme
Quantities: 1 g
Divisible:
Dose: chloorhexidine, [dun] x
Solution:
Substances:

Name: was
Concentrations: 150 mg/g
Dose:
Solution:

Name: decyloleaat
Concentrations: 200 mg/g
Dose:
Solution:

Name: sorbitol
Concentrations: 28 mg/g
Dose:
Solution:

Name: chloorhexidine
Concentrations: 10 mg/g
Dose:
Solution:
"""


// Once single component single item scenario
let onceSingleComponentSingleItem = """
Id: 93e8c175-99a1-48d8-b2f4-90005fdb8ada
Name: paracetamol
Quantity:
Quantities:
Route: RECTAAL
OrderType: OnceOrder
Adjust: 10 kg
Frequencies:
Time:
Dose: [dun], [qty] 1 stuk/dosis
Div:
DoseCount: 1 x
Components:

Name: paracetamol
Form: zetpil
Quantities: 1 stuk
Divisible: 1
Dose:
Solution:
Substances:

Name: paracetamol
Concentrations: 120;240;500;1000;125;250;60;30;360;90;750;180 mg/stuk
Dose: paracetamol, [dun] mg, [qty-adj] 40 mg/kg/dosis, [qty] max 1000 mg/dosis
Solution:
"""

// OnceTimed single component single item scenario
let onceTimedSingleComponentSingleItem = """
Id: 95c44266-84c5-4969-a815-9fbf2c9ed693
Name: paracetamol
Quantity:
Quantities:
Route: INTRAVENEUS
OrderType: OnceTimedOrder
Adjust: 10 kg
Frequencies:
Time: 15 min - 20 min
Dose: [dun], [qty-adj] max 20 ml/kg/dosis, [qty] max 1000 ml/dosis
Div:
DoseCount: 1 x
Components:

Name: paracetamol
Form: infusievloeistof
Quantities: 100;50 ml
Divisible: 10
Dose:
Solution:
Substances:

Name: paracetamol
Concentrations: 10 mg/ml
Dose: paracetamol, [dun] mg, [qty-adj] 20 mg/kg/dosis, [qty] max 1000 mg/dosis
Solution:
"""

// Discontinuous single component single item scenario
let discontinuousSingleComponentSingleItem = """
d: e043a880-70ec-4600-8e5a-109e5ef39108
Name: paracetamol
Quantity:
Quantities:
Route: RECTAAL
OrderType: DiscontinuousOrder
Adjust: 10 kg
Frequencies: 3;4 x/day
Time:
Dose: [dun], [qty] 1 stuk/dosis
Div:
DoseCount: 1 x
Components:

Name: paracetamol
Form: zetpil
Quantities: 1 stuk
Divisible: 1
Dose:
Solution:
Substances:

Name: paracetamol
Concentrations: 120;240;500;1000;125;250;60;30;360;90;750;180 mg/stuk
Dose: paracetamol, [dun] mg, [qty-adj] 10 mg/kg - 20 mg/kg/dosis
Solution:
"""

// Timed single component single item scenario
let timedSingleComponentSingleItem = """
Id: a9e18942-f879-4df1-bc21-6375c3291ed7
Name: paracetamol
Quantity:
Quantities:
Route: INTRAVENEUS
OrderType: TimedOrder
Adjust: 10 kg
Frequencies: 4 x/day
Time: 15 min - 20 min
Dose: [dun], [qty-adj] max 20 ml/kg/dosis, [qty] max 1000 ml/dosis
Div:
DoseCount: 1 x
Components:

Name: paracetamol
Form: infusievloeistof
Quantities: 100;50 ml
Divisible: 10
Dose:
Solution:
Substances:

Name: paracetamol
Concentrations: 10 mg/ml
Dose: paracetamol, [dun] mg, [per-time-adj] 60 mg/kg/day, [per-time] max 4000 mg/day, [qty] max 1000 mg/dosis
Solution:
"""


MedicationTexts.onceSingleComponentSingleItem
|> Medication.fromString
|> Result.map (fun med ->
[
OrderCommand.CalcMinMax
]
|> run (Some logger) med
)
|> ignore


MedicationTexts.discontinuousSingleComponentSingleItem
|> Medication.fromString
|> Result.map (fun med ->
[
OrderCommand.CalcMinMax
]
|> run (Some logger) med
)
|> ignore


MedicationTexts.onceTimedSingleComponentSingleItem
|> Medication.fromString
|> Result.map (fun med ->
[
OrderCommand.CalcMinMax
]
|> run (Some logger) med
)
|> ignore


MedicationTexts.timedSingleComponentSingleItem
|> Medication.fromString
|> Result.map (fun med ->
[
OrderCommand.CalcMinMax
]
|> run (Some logger) med
)
|> ignore


MedicationTexts.onceSingleComponentSingleItemNoDose
|> Medication.fromString
|> Result.bind (fun med ->
[
OrderCommand.CalcMinMax
]
|> run (Some logger) med
|> Result.mapError (fun _ -> [])
)
|> function
| Ok ord -> ord |> Order.Print.printOrderToTableFormat false true [||]
| Error _ -> "cannot run order" |> failwith
|> ignore
7 changes: 7 additions & 0 deletions src/Informedica.GenUNITS.Lib/Scripts/Api.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,10 @@ open Informedica.Utils.Lib.BCL
"0,8 mL/uur"
//|> String.replace "," "."
|> ValueUnit.fromString


"x"
|> FParsec.CharParsers.run Parser.parseUnit

"x"
|> Units.fromString
Loading