From ee371ff68c6ce1253df5d14c620741a79996e3ed Mon Sep 17 00:00:00 2001 From: Casper Bollen Date: Thu, 2 Nov 2023 20:02:55 +0100 Subject: [PATCH] chore: updated order comments and refactored order printing --- src/Informedica.GenOrder.Lib/Api.fs | 2 +- src/Informedica.GenOrder.Lib/Demo.fs | 2 +- src/Informedica.GenOrder.Lib/Order.fs | 856 +++++++++++------- src/Informedica.GenOrder.Lib/OrderLogger.fs | 4 +- .../Scripts/Script1.fsx | 70 +- src/Informedica.GenOrder.Lib/Solver.fs | 18 +- src/Server/ScenarioResult.fs | 2 +- 7 files changed, 569 insertions(+), 385 deletions(-) diff --git a/src/Informedica.GenOrder.Lib/Api.fs b/src/Informedica.GenOrder.Lib/Api.fs index 5a3e790..8de4dd5 100644 --- a/src/Informedica.GenOrder.Lib/Api.fs +++ b/src/Informedica.GenOrder.Lib/Api.fs @@ -378,7 +378,7 @@ module Api = ords |> Array.mapi (fun i o -> o - |> Order.Print.printPrescription sn + |> Order.Print.printOrderToString sn |> fun (pres, prep, adm) -> { No = i diff --git a/src/Informedica.GenOrder.Lib/Demo.fs b/src/Informedica.GenOrder.Lib/Demo.fs index 346e0ab..fb6d7db 100644 --- a/src/Informedica.GenOrder.Lib/Demo.fs +++ b/src/Informedica.GenOrder.Lib/Demo.fs @@ -118,7 +118,7 @@ module Demo = let prs, prp, adm = ord - |> Order.Markdown.printPrescription ns + |> Order.Print.printOrderToMd ns { No = i diff --git a/src/Informedica.GenOrder.Lib/Order.fs b/src/Informedica.GenOrder.Lib/Order.fs index f170088..22441ed 100644 --- a/src/Informedica.GenOrder.Lib/Order.fs +++ b/src/Informedica.GenOrder.Lib/Order.fs @@ -9,13 +9,14 @@ namespace Informedica.GenOrder.Lib /// stop date. module Order = + open System + open Informedica.Utils.Lib open Informedica.Utils.Lib.BCL open Informedica.GenUnits.Lib open WrappedString - /// Utility functions to /// enable mapping of a `Variable`s /// to an `Order` @@ -47,7 +48,7 @@ module Order = let [] timed = 5 - let getEquations indx = + let private getEquations_ indx = Web.getDataFromGenPres "Equations" |> Array.skip 1 |> Array.filter (fun xs -> xs[indx] = "x") @@ -55,6 +56,28 @@ module Order = |> Array.toList + /// + /// Get a string list of Equations and + /// use an index to filter out the relevant equations + /// + /// The index to filter the equations + /// + /// The indx can be 3 for discontinuous equations, 4 for continuous + /// and 5 for timed equations. + /// + let getEquations indx = + indx + |> Memoization.memoize getEquations_ + + + /// + /// Create an Equations mapping for an `Order` + /// + /// The Order to Map + /// The equations as a string list + /// + /// A tuple of `SumMapping` and `ProductMapping` + /// let getEqsMapping (ord: Order) (eqs : string list) = let sumEqs = eqs @@ -155,6 +178,7 @@ module Order = |> List.filter (String.isNullOrWhiteSpace >> not) |> List.map (String.replace "[orb]" $"{orbN |> Name.toString}") |> SumMapping + let prodEqs = es |> List.append orbEqs @@ -173,10 +197,13 @@ module Order = /// that can be ordered. module Orderable = + open Informedica.GenSolver.Lib + type Name = Types.Name + /// Contains string constants /// to create `Variable` names module Literals = @@ -206,7 +233,26 @@ module Order = module TotalAdjust = OrderVariable.TotalAdjust - let create qty ptm rte tot qty_adj ptm_adj rte_adj tot_adj = + /// + /// Create a `Dose` with + /// + /// The quantity of the dose + /// The per time of the dose + /// The rate of the dose + /// The total of the dose + /// The quantity adjust of the dose + /// The per time adjust of the dose + /// The rate adjust of the dose + /// The total adjust of the dose + let create + qty + ptm + rte + tot + qty_adj + ptm_adj + rte_adj + tot_adj = { Quantity = qty PerTime = ptm @@ -218,6 +264,11 @@ module Order = TotalAdjust = tot_adj } + + /// + /// Create a new `Dose` with + /// + /// The name of the dose let createNew n = let un = Unit.NoUnit let n = n |> Name.add Literals.dose @@ -234,7 +285,10 @@ module Order = create qty ptm rte tot qty_adj ptm_adj rte_adj tot_adj - /// Turn an `Item` to `VariableUnit`s + /// + /// Return a Dose as a list of OrderVariables + /// + /// The dose let toOrdVars (dos : Dose) = let qty = dos.Quantity |> Quantity.toOrdVar let ptm = dos.PerTime |> PerTime.toOrdVar @@ -256,6 +310,13 @@ module Order = tot_adj ] + + /// + /// Create a new Dose from a list of OrderVariables using + /// an old Dose. + /// + /// The list of OrderVariables + /// The old Dose let fromOrdVars ovars (dos: Dose) = let qty = dos.Quantity |> Quantity.fromOrdVar ovars let ptm = dos.PerTime |> PerTime.fromOrdVar ovars @@ -269,6 +330,10 @@ module Order = create qty ptm rte tot qty_adj ptm_adj rte_adj tot_adj + /// + /// Apply constraints to a Dose + /// + /// The Dose let applyConstraints (dos: Dose) = let qty = dos.Quantity |> Quantity.applyConstraints let ptm = dos.PerTime |> PerTime.applyConstraints @@ -282,10 +347,17 @@ module Order = create qty ptm rte tot qty_adj ptm_adj rte_adj tot_adj - let increaseIncrement lim incr (dos: Dose) = + /// + /// Increase the increment of a Dose to a maximum + /// count using a list of increments. + /// + /// The maximum count + /// The list of increments + /// The Dose + let increaseIncrement maxCount incrs (dos: Dose) = let qty = dos.Quantity let ptm = dos.PerTime - let rte = dos.Rate |> Rate.increaseIncrement lim incr + let rte = dos.Rate |> Rate.increaseIncrement maxCount incrs let tot = dos.Total let qty_adj = dos.QuantityAdjust let ptm_adj = dos.PerTimeAdjust @@ -295,12 +367,14 @@ module Order = create qty ptm rte tot qty_adj ptm_adj rte_adj tot_adj - /// Turn an `Item` to a list of `string`s, - /// each string containing the variable - /// `Name`, `ValueRange` and `Unit` + /// + /// Create a string list from a Dose where each string is + /// a variable name with the value range and the Unit + /// let toString = toOrdVars >> List.map (OrderVariable.toString false) + /// Functions to create a Dose Dto and vice versa. module Dto = @@ -339,33 +413,18 @@ module Order = create qty ptm rte tot qty_adj ptm_adj rte_adj tot_adj + let toDto (dos : Dose) = let dto = Dto () - dto.Quantity <- - dos.Quantity - |> Quantity.toDto - dto.PerTime <- - dos.PerTime - |> PerTime.toDto - dto.Rate <- - dos.Rate - |> Rate.toDto - dto.Total <- - dos.Total - |> Total.toDto - dto.QuantityAdjust <- - dos.QuantityAdjust - |> QuantityAdjust.toDto - dto.PerTimeAdjust <- - dos.PerTimeAdjust - |> PerTimeAdjust.toDto - dto.RateAdjust <- - dos.RateAdjust - |> RateAdjust.toDto - dto.TotalAdjust <- - dos.TotalAdjust - |> TotalAdjust.toDto + dto.Quantity <- dos.Quantity |> Quantity.toDto + dto.PerTime <- dos.PerTime |> PerTime.toDto + dto.Rate <- dos.Rate |> Rate.toDto + dto.Total <- dos.Total |> Total.toDto + dto.QuantityAdjust <- dos.QuantityAdjust |> QuantityAdjust.toDto + dto.PerTimeAdjust <- dos.PerTimeAdjust |> PerTimeAdjust.toDto + dto.RateAdjust <- dos.RateAdjust |> RateAdjust.toDto + dto.TotalAdjust <- dos.TotalAdjust |> TotalAdjust.toDto dto @@ -385,16 +444,22 @@ module Order = module Rate = OrderVariable.Rate - /// Create an item with - /// - /// * **id**: the order id - /// * **n**: the name of the item - /// * **cmp_qty**: the quantity of the item in a component - /// * **orb_qty**: the quantity of the item in an orderable - /// * **cmp_cnc**: the item concentration in a component - /// * **orb_cnc**: the item concentration in an orderable - /// * **dos**: the item dose - let create n cmp_qty orb_qty cmp_cnc orb_cnc dos = + /// + /// Create an `Item` with + /// + /// The name of the Item + /// The quantity of the item in the Component + /// The quantity of the item in the Orderable + /// The concentration of the item in the Component + /// The concentration of the item in the Orderable + /// The dose of the item + let create + n + cmp_qty + orb_qty + cmp_cnc + orb_cnc + dos = { Name = n ComponentQuantity = cmp_qty @@ -405,10 +470,13 @@ module Order = } - /// Create a new item with - /// - /// **id**: the order id - /// **n**: the string name of the item + /// + /// Create a new `Item` with + /// + /// The Id of the Item + /// The name of the Orderable + /// The name of the Component + /// The name of the Item let createNew id orbN cmpN itmN = let un = Unit.NoUnit let n = @@ -428,7 +496,7 @@ module Order = let apply f (itm: Item) = itm |> f - /// Utility method to facitilitate type inference + /// Utility method to facilitate type inference let get = apply id @@ -440,7 +508,10 @@ module Order = let getDose itm = (itm |> get).Dose - /// Turn an `Item` to `VariableUnit`s + /// + /// Return an Item as a list of OrderVariables + /// + /// The Item let toOrdVars itm = let itm_cmp_qty = (itm |> get).ComponentQuantity |> Quantity.toOrdVar let itm_orb_qty = itm.OrderableQuantity |> Quantity.toOrdVar @@ -456,6 +527,12 @@ module Order = ] + /// + /// Create a new Item from a list of OrderVariables using + /// an old Item. + /// + /// The list of OrderVariables + /// The old Item let fromOrdVars ovars itm = let cmp_qty = (itm |> get).ComponentQuantity |> Quantity.fromOrdVar ovars let orb_qty = itm.OrderableQuantity |> Quantity.fromOrdVar ovars @@ -466,6 +543,10 @@ module Order = create itm.Name cmp_qty orb_qty cmp_cnc orb_cnc dos + /// + /// Apply constraints to an Item + /// + /// The Item let applyConstraints itm = let cmp_qty = (itm |> get).ComponentQuantity |> Quantity.applyConstraints let orb_qty = itm.OrderableQuantity |> Quantity.applyConstraints @@ -476,12 +557,15 @@ module Order = create itm.Name cmp_qty orb_qty cmp_cnc orb_cnc dos - /// Turn an `Item` to a list of `string`s, - /// each string containing the variable - /// `Name`, `ValueRange` and `Unit` + + /// + /// Create a string list from a Item where each string is + /// a variable name with the value range and the Unit + /// let toString = toOrdVars >> List.map (OrderVariable.toString false) + /// Functions to create a Item Dto and vice versa. module Dto = module Units = ValueUnit.Units @@ -533,6 +617,13 @@ module Order = dto + /// + /// Create a new Item Dto + /// + /// The Id of the Item + /// The name of the Orderable + /// The name of the Component + /// The name of the Item let dto id orbN cmpN itmN = createNew id orbN cmpN itmN |> toDto @@ -545,26 +636,39 @@ module Order = /// of `Item`s module Component = + module Name = Name module Quantity = OrderVariable.Quantity module Concentration = OrderVariable.Concentration module Count = OrderVariable.Count - /// Create a component with - /// - /// * `id`: the order id - /// * `n`: the name of the component - /// * `cmp_qty`: quantity of component - /// * `orb_qty`: quantity of component in orderable - /// * `orb_cnt`: count of component in orderable - /// * `ord_qty`: quantity of component in order - /// * `ord_cnt`: count of component in order - /// * `orb_cnc`: concentration of component in orderable - /// * `dos`: component dose - /// * `dos_adj`: adjusted dose of component - /// * `ii`: list of `Item`s in a component - let create id nm sh cmp_qty orb_qty orb_cnt ord_qty ord_cnt orb_cnc dos ii = + /// + /// Create a `Component` with + /// + /// The Id of the Component + /// The name of the Component + /// The shape of the Component + /// The quantity of the Component + /// The quantity of the Component in the Orderable + /// The count of the Component in the Orderable + /// The quantity of the Component in the Order + /// The count of the Component in the Order + /// The concentration of the Component in the Orderable + /// The dose of the Component + /// The list of Items in the Component + let create + id + nm + sh + cmp_qty + orb_qty + orb_cnt + ord_qty + ord_cnt + orb_cnc + dos + ii = { Id = id Name = nm @@ -579,9 +683,14 @@ module Order = Items = ii } - /// Create a new component with - /// * `id`: the id of the component - /// * `n`: the name of the component + + /// + /// Create a new `Component` with + /// + /// The Id of the Component + /// The name of the Orderable + /// The name of the Component + /// The shape of the Component let createNew id orbN cmpN sh = let un = Unit.NoUnit let nm = [ id; orbN; cmpN ] |> Name.create @@ -614,8 +723,10 @@ module Order = let getItems cmp = (cmp |> get).Items - /// Map a `Component` **cmp** - /// to `VariableUnit`s + /// + /// Return a Component as a list of OrderVariables + /// + /// The Component let toOrdVars cmp = let cmp_qty = (cmp |> get).ComponentQuantity |> Quantity.toOrdVar let orb_qty = cmp.OrderableQuantity |> Quantity.toOrdVar @@ -636,8 +747,13 @@ module Order = ] - /// Map a `Component` **cmp** - /// to `VariableUnit`s + + /// + /// Create a new Component from a list of OrderVariables using + /// an old Component. + /// + /// The list of OrderVariables + /// The old Component let fromOrdVars ovars cmp = let cmp_qty = (cmp |> get).ComponentQuantity |> Quantity.fromOrdVar ovars let orb_qty = cmp.OrderableQuantity |> Quantity.fromOrdVar ovars @@ -652,8 +768,10 @@ module Order = |> create cmp.Id cmp.Name cmp.Shape cmp_qty orb_qty orb_cnt ord_qty ord_cnt orb_cnc dos - /// Map a `Component` **cmp** - /// to `VariableUnit`s + /// + /// Apply constraints to a Component + /// + /// The Component let applyConstraints cmp = let cmp_qty = (cmp |> get).ComponentQuantity |> Quantity.applyConstraints let orb_qty = cmp.OrderableQuantity |> Quantity.applyConstraints @@ -668,9 +786,17 @@ module Order = |> create cmp.Id cmp.Name cmp.Shape cmp_qty orb_qty orb_cnt ord_qty ord_cnt orb_cnc dos - let increaseIncrement lim incr cmp = + + /// + /// Increase the increment of a Component to a maximum + /// count using a list of increments. + /// + /// The maximum count + /// The list of increments + /// The Component + let increaseIncrement maxCount incrs cmp = let cmp_qty = (cmp |> get).ComponentQuantity - let orb_qty = cmp.OrderableQuantity |> Quantity.increaseIncrement lim incr + let orb_qty = cmp.OrderableQuantity |> Quantity.increaseIncrement maxCount incrs let orb_cnt = cmp.OrderableCount let orb_cnc = cmp.OrderableConcentration let ord_qty = cmp.OrderQuantity @@ -682,14 +808,14 @@ module Order = - /// Create a string list from a - /// component where each string is - /// a variable name with the value range - /// and the Unit + /// + /// Create a string list from a Component where each string is + /// a variable name with the value range and the Unit + /// let toString = toOrdVars >> List.map (OrderVariable.toString false) - + /// Helper functions for the Component Dto module Dto = module Units = ValueUnit.Units @@ -766,6 +892,14 @@ module Order = dto + + /// + /// Create a Component Dto + /// + /// The Id of the Component + /// The name of the Orderable + /// The name of the Component + /// The shape of the Component let dto id orbN cmpN shape = createNew id orbN cmpN shape |> toDto @@ -777,15 +911,24 @@ module Order = module Count = OrderVariable.Count + /// /// Create an `Orderable` with - /// - /// * nm: the name of the orderable - /// * orb\_qty: quantity of the orderable - /// * ord\_qty: quantity of orderable in the order - /// * orb\_cnt: the count of orderable in the order - /// * dos: the orderable dose - /// * dos\_adj: the adjusted orderable dose - let create n orb_qty ord_qty ord_cnt dos_cnt dos cc = + /// + /// The name of the Orderable + /// The quantity of the Orderable + /// The quantity of the Orderable in the Order + /// The count of the Orderable in the Order + /// The count of the Orderable dose in the Order + /// The dose of the Orderable + /// The list of Components in the Orderable + let create + n + orb_qty + ord_qty + ord_cnt + dos_cnt + dos + cc = { Name = n OrderableQuantity = orb_qty @@ -796,12 +939,12 @@ module Order = Components = cc } - /// Create a new `Orderable` with a `Component` list - /// `cl`, and - /// * `Orderable`unit `un` and - /// * component unit `cu` - /// * time unit `tu` - /// * adjust unit `adj` + + /// + /// Create a new `Orderable` with + /// + /// The Id of the Orderable + /// The name of the Orderable let createNew id orbN = let un = Unit.NoUnit let n = [id; orbN] |> Name.create @@ -827,7 +970,7 @@ module Order = let getName orb = (orb |> get).Name - /// Get the `Component`s in an `Orderable` + /// Get the Components in an `Orderable` let getComponents orb = (orb |> get).Components @@ -835,8 +978,11 @@ module Order = let getDose orb = (orb |> get).Dose - /// Map an `Orderable` **orb** to - /// `VariableUnit`s + + /// + /// Return an Orderable as a list of OrderVariables + /// + /// The Orderable let toOrdVars orb = let ord_qty = (orb |> get).OrderQuantity |> Quantity.toOrdVar let orb_qty = orb.OrderableQuantity |> Quantity.toOrdVar @@ -853,8 +999,14 @@ module Order = ] - /// Map an `Orderable` **orb** to - /// `VariableUnit`s + + /// + /// Create a new Orderable from a list of OrderVariables using + /// an old Orderable. + /// + /// The list of OrderVariables + /// The old Orderable + /// The new Orderable let fromOrdVars ovars orb = let ord_qty = (orb |> get).OrderQuantity |> Quantity.fromOrdVar ovars let orb_qty = orb.OrderableQuantity |> Quantity.fromOrdVar ovars @@ -867,8 +1019,10 @@ module Order = |> create orb.Name orb_qty ord_qty ord_cnt dos_cnt dos - /// Map an `Orderable` **orb** to - /// `VariableUnit`s + /// + /// Apply constraints to an Orderable + /// + /// The Orderable let applyConstraints orb = let ord_qty = (orb |> get).OrderQuantity |> Quantity.applyConstraints let orb_qty = orb.OrderableQuantity |> Quantity.applyConstraints @@ -881,22 +1035,31 @@ module Order = |> create orb.Name orb_qty ord_qty ord_cnt dos_cnt dos - /// Turn an `Orderable` `ord` into - /// a list of strings. + /// + /// Return a list of strings from an Orderable where each string is + /// a variable name with the value range and the Unit + /// let toString = toOrdVars >> List.map (OrderVariable.toString false) - let increaseQuantityIncrement lim incr orb = + /// + /// Increase the Quantity increment of an Orderable to a maximum + /// count using a list of increments. + /// + /// The maximum count + /// The list of increments + /// The Orderable + let increaseQuantityIncrement maxCount incrs orb = // first calculate the minimum increment increase for the orderable and components let incr = let ord_qty = (orb |> get).OrderQuantity - let orb_qty = orb.OrderableQuantity |> Quantity.increaseIncrement lim incr + let orb_qty = orb.OrderableQuantity |> Quantity.increaseIncrement maxCount incrs let ord_cnt = orb.OrderCount let dos_cnt = orb.DoseCount let dos = orb.Dose //|> Dose.increaseIncrement incr orb.Components - |> List.map (Component.increaseIncrement lim incr) + |> List.map (Component.increaseIncrement maxCount incrs) |> create orb.Name orb_qty ord_qty ord_cnt dos_cnt dos |> fun orb -> @@ -914,31 +1077,37 @@ module Order = |> ValueUnit.getBaseValue ) - printfn $"the minimum increment is {incr}" - // apply the minimum increment increase to the orderable and components let ord_qty = (orb |> get).OrderQuantity - let orb_qty = orb.OrderableQuantity |> Quantity.increaseIncrement lim [incr] + let orb_qty = orb.OrderableQuantity |> Quantity.increaseIncrement maxCount [incr] let ord_cnt = orb.OrderCount let dos_cnt = orb.DoseCount let dos = orb.Dose //|> Dose.increaseIncrement incr orb.Components - |> List.map (Component.increaseIncrement lim [incr]) + |> List.map (Component.increaseIncrement maxCount [incr]) |> create orb.Name orb_qty ord_qty ord_cnt dos_cnt dos - let increaseRateIncrement lim incr orb = + /// + /// Increase the Rate increment of an Orderable to a maximum + /// count using a list of increments. + /// + /// The maximum count + /// The list of increments + /// The Orderable + let increaseRateIncrement maxCount incrs orb = let ord_qty = (orb |> get).OrderQuantity let orb_qty = orb.OrderableQuantity //|> Quantity.increaseIncrement incr let ord_cnt = orb.OrderCount let dos_cnt = orb.DoseCount - let dos = orb.Dose |> Dose.increaseIncrement lim incr + let dos = orb.Dose |> Dose.increaseIncrement maxCount incrs orb.Components |> create orb.Name orb_qty ord_qty ord_cnt dos_cnt dos + /// Helper functions for the Orderable Dto module Dto = module Units = ValueUnit.Units @@ -948,6 +1117,7 @@ module Order = module Concentration = OrderVariable.Concentration module CT = OrderVariable.Count + type Dto () = member val Name = "" with get, set member val OrderableQuantity = OrderVariable.Dto.dto () with get, set @@ -974,6 +1144,7 @@ module Order = create n orb_qty ord_qty ord_cnt dos_cnt dos cc + let toDto (orb : Orderable) = let dto = Dto () @@ -1000,6 +1171,11 @@ module Order = dto + /// + /// Create a new Orderable Dto + /// + /// The Id of the Orderable + /// The name of the Orderable let dto id orbN = createNew id orbN |> toDto @@ -1013,35 +1189,43 @@ module Order = module Time = OrderVariable.Time - /// Create `Frequency` and `Time` with name generated by string list **n** + /// + /// Create a Frequency and Time + /// + /// The frequency time unit + /// The time unit + /// The name of the Frequency and Time let freqTime tu1 tu2 n = (Frequency.create n tu1, Time.create n tu2) - /// Create a continuous `Prescription` with name generated by string list **n** + /// Create a Continuous `Prescription` let continuous tu1 tu2 n = let _, _ = n |> freqTime tu1 tu2 in Continuous - /// Create a discontinuous `Prescription` with name generated by string list **n** + /// Create a Discontinuous `Prescription` let discontinuous tu1 tu2 n = let frq, _ = n |> freqTime tu1 tu2 in frq |> Discontinuous - /// Create a timed `Prescription` with name generated by string list **n** + /// Create a Timed `Prescription` let timed tu1 tu2 n = let frq, tme = n |> freqTime tu1 tu2 in (frq, tme) |> Timed - /// Check whether a `Prescription` is continuous + /// Check whether a `Prescription` is Continuous let isContinuous = function | Continuous -> true | _ -> false - /// Check whether a `Prescription` is discontinuous with a time + /// Check whether a `Prescription` is Timed let isTimed = function | Timed _ -> true | _ -> false - /// Turn `Prescription` **prs** into `VariableUnit`s to - /// be used in equations + /// + /// Return a Prescription as a Frequency OrderVariable option + /// and a Time OrderVariable option + /// + /// The Prescription let toOrdVars prs = match prs with | Continuous -> None, None @@ -1051,6 +1235,12 @@ module Order = frq |> Frequency.toOrdVar |> Some, tme |> Time.toOrdVar |> Some + /// + /// Create a new Prescription from a list of OrderVariables using + /// an old Prescription. + /// + /// The list of OrderVariables + /// The old Prescription let fromOrdVars ovars prs = match prs with | Continuous -> prs @@ -1062,6 +1252,9 @@ module Order = |> Timed + /// + /// Apply constraints to a Prescription + /// let applyConstraints prs = match prs with | Continuous -> prs @@ -1073,9 +1266,10 @@ module Order = |> Timed - - /// Turn a `Prescription` **prs** into - /// a string list + /// + /// Return a list of strings from a Prescription where each string is + /// a variable name with the value range and the Unit + /// let toString (prs: Prescription) = match prs with | Continuous -> ["Continuous"] @@ -1083,12 +1277,16 @@ module Order = | Timed(frq, tme) -> [frq |> Frequency.toString; tme |> Time.toString] + + /// Helper functions for the Prescription Dto module Dto = + module Units = ValueUnit.Units module Id = WrappedString.Id module NM = Name + type Dto () = member val IsContinuous = false with get, set member val IsDiscontinuous = false with get, set @@ -1096,6 +1294,7 @@ module Order = member val Frequency = OrderVariable.Dto.dto () with get, set member val Time = OrderVariable.Dto.dto () with get, set + let fromDto (dto : Dto) = match dto.IsContinuous, dto.IsDiscontinuous, @@ -1111,6 +1310,7 @@ module Order = | _ -> exn "dto is neither or both process, continuous, discontinuous or timed" |> raise + let toDto pres = let dto = Dto () @@ -1126,6 +1326,14 @@ module Order = dto + + /// + /// Create a Prescription Dto + /// + /// The name of the Prescription + /// + /// Defaults to a Discontinuous Prescription + /// let dto n = let dto = Dto () let f, t = @@ -1139,22 +1347,29 @@ module Order = dto + /// Make the Prescription Dto Continuous let setToContinuous (dto : Dto) = dto.IsContinuous <- true dto.IsDiscontinuous <- false dto.IsTimed <- false + dto + /// Make the Prescription Dto Discontinuous let setToDiscontinuous (dto : Dto) = dto.IsContinuous <- false dto.IsDiscontinuous <- true dto.IsTimed <- false + dto + + /// Make the Prescription Dto Timed let setToTimed (dto : Dto) = dto.IsContinuous <- false dto.IsDiscontinuous <- false dto.IsTimed <- true + dto @@ -1164,6 +1379,8 @@ module Order = /// of an `Order` module StartStop = + + /// Get the string representation of a `StartStop` let toString startStop = match startStop with | Start dt -> @@ -1209,6 +1426,7 @@ module Order = module Time = OrderVariable.Time module Units = ValueUnit.Units + type Equation = Informedica.GenSolver.Lib.Types.Equation @@ -1224,13 +1442,16 @@ module Order = let getId ord = (ord |> get).Id + /// /// Create an `Order` with - /// - /// * id: the id of the order - /// * adj: by which doses are adjusted - /// * orb: the `Orderable` - /// * prs: `Prescription`, how the orderable is prescribed - /// * rte: the route of administration of the orderable + /// + /// The id of the Order + /// The adjust quantity of the Order + /// The Orderable of the Order + /// The Prescription of the Order + /// The Route of the Order + /// The Time of the Order + /// The StartStop of the Order let create id adj_qty orb prs rte tme sts = { Id = id @@ -1243,6 +1464,13 @@ module Order = } + /// + /// Create a new `Order` with + /// + /// The id of the Order + /// The name of the Orderable + /// A function to create a Prescription with a Name + /// The Route of the Order let createNew id orbN str_prs route = let orb = Orderable.createNew id orbN let n = [id] |> Name.create @@ -1257,20 +1485,24 @@ module Order = n |> Name.add Mapping.prs |> str_prs + let sts = DateTime.Now |> StartStop.Start create (id |> Id.create) adj orb prs route tme sts + /// Get the Adjust quantity of an `Order` let getAdjust ord = (ord |> get).Adjust + /// Get the Orderable of an `Order` let getOrderable ord = (ord |> get).Orderable - /// Turn an order into a list of string - /// representing variable name, valuerange - /// and unit group + /// + /// Return an Order as a list of strings where each string is + /// a variable name with the value range and the Unit + /// let toString (ord: Order) = [ ord.Adjust |> Quantity.toString ] |> List.append (Orderable.Literals.orderable::(ord.Orderable |> Orderable.toString)) @@ -1279,8 +1511,9 @@ module Order = |> List.filter (String.isNullOrWhiteSpace >> not) - /// Map an `Orderable` **orb** to - /// `VariableUnit`s + /// + /// Return an Order as a list of OrderVariables + /// let toOrdVars (ord : Order) = let adj_qty = ord.Adjust |> Quantity.toOrdVar let ord_tme = ord.Duration |> Time.toOrdVar @@ -1299,6 +1532,12 @@ module Order = ] + /// + /// Create a new Order from a list of OrderVariables using + /// an old Order. + /// + /// The list of OrderVariables + /// The old Order let fromOrdVars ovars (ord : Order) = { ord with Adjust = ord.Adjust |> Quantity.fromOrdVar ovars @@ -1308,6 +1547,10 @@ module Order = } + /// + /// Apply constraints to an Order + /// + /// The Order let applyConstraints (ord : Order) = try { ord with @@ -1323,24 +1566,49 @@ module Order = reraise() - let increaseQuantityIncrement lim incr (ord : Order) = + /// + /// Increase the Quantity increment of an Order to a maximum + /// count using a list of increments. + /// + /// The maximum count + /// The list of increments + /// The Order + let increaseQuantityIncrement maxCount incrs (ord : Order) = { ord with - Orderable = ord.Orderable |> Orderable.increaseQuantityIncrement lim incr + Orderable = + ord.Orderable + |> Orderable.increaseQuantityIncrement + maxCount + incrs } - let increaseRateIncrement lim incr (ord : Order) = + /// + /// Increase the Rate increment of an Order to a maximum + /// count using a list of increments. + /// + /// The maximum count + /// The list of increments + /// The Order + let increaseRateIncrement maxCount incrs (ord : Order) = { ord with - Orderable = ord.Orderable |> Orderable.increaseRateIncrement lim incr + Orderable = ord.Orderable |> Orderable.increaseRateIncrement maxCount incrs } - let mapToEquations eqs (ord: Order) = + /// + /// Map an Order to a list of Equations using a Product Equation + /// mapping and a Sum Equation mapping + /// + /// The Product Equation mapping and the Sum Equation mapping + /// The Order + /// A list of OrderEquations + let mapToOrderEquations eqMapping (ord: Order) = let ovars = ord |> toOrdVars - let map repl eqs = + let map repl eqMapping = let eqs, c = - match eqs with + match eqMapping with | SumMapping eqs -> eqs, OrderSumEquation | ProductMapping eqs -> eqs, OrderProductEquation eqs @@ -1366,13 +1634,19 @@ module Order = | _ -> failwith $"cannot map {eqs}" ) - let sumEqs, prodEqs = eqs + let sumEqs, prodEqs = eqMapping - sumEqs |> map "+" + sumEqs + |> map "+" |> List.append (prodEqs |> map "*") - let mapFromEquations (ord: Order) eqs = + /// + /// Map a list of OrderEquations to an Order + /// + /// The Order + /// The list of OrderEquations + let mapFromOrderEquations (ord: Order) eqs = let ovars = eqs |> List.collect (fun e -> @@ -1383,9 +1657,19 @@ module Order = |> List.distinct |> List.map OrderVariable.setUnit - ord |> fromOrdVars ovars + ord + |> fromOrdVars ovars + /// + /// Solve an Order + /// + /// Whether to solve only for the minimum or maximum + /// Whether to print the error + /// The logger + /// The Order + /// A Result with the Order or a list error messages + /// Any exception raised by the solver let solve minMax printErr logger (ord: Order) = let ord = if minMax then ord |> applyConstraints @@ -1401,7 +1685,7 @@ module Order = let oEqs = ord - |> mapToEquations mapping + |> mapToOrderEquations mapping try oEqs @@ -1413,31 +1697,47 @@ module Order = | Ok eqs -> eqs |> Solver.mapToOrderEqs oEqs - |> mapFromEquations ord + |> mapFromOrderEquations ord |> Ok | Error (eqs, m) -> eqs |> Solver.mapToOrderEqs oEqs - |> mapFromEquations ord + |> mapFromOrderEquations ord |> fun eqs -> Error (eqs, m) with | e -> if printErr then oEqs - |> mapFromEquations ord + |> mapFromOrderEquations ord |> toString |> List.iteri (printfn "%i. %s") raise e + /// + /// Solve an Order for only the minimum and maximum values + /// + /// Whether to print the error + /// The logger let solveMinMax printErr logger = solve true printErr logger + /// + /// Solve an Order for all values + /// + /// Whether to print the error + /// The logger let solveOrder printErr logger = solve false printErr logger + /// + /// Loop through all the OrderVariables in an Order to + /// turn min incr max to values and subsequently solve the Order. + /// + /// The logger + /// The Order let minIncrMaxToValues logger (ord: Order) = let rec loop runAgain ord = if not runAgain then ord @@ -1465,40 +1765,44 @@ module Order = else ord |> fromOrdVars ovars - |> solveOrder false logger + |> solveOrder false logger // could possible restrict to solve variable |> function | Ok ord -> loop flag ord | Error _ -> ord loop true ord - // |> fun ord -> - // let s = ord |> toString |> String.concat "\n" - // printfn $"min incr max to values:\n{s}" - // ord module Print = - let printItemConcentration (c : Component) = + let itemConcentrationTo toStr (c : Component) = c.Items |> Seq.map (fun i -> i.ComponentConcentration - |> Concentration.toValueUnitString -1 + |> toStr |> fun s -> $"{s} {i.Name |> Name.toString}" ) |> String.concat " + " - let printComponentQuantity o = + let itemConcentrationToString = + itemConcentrationTo (Concentration.toValueUnitString -1) + + + let itemConcentrationToMd = + itemConcentrationTo (Concentration.toValueUnitMarkdown -1) + + + let componentQuantityTo toStr (o : Order) = o.Orderable.Components |> Seq.map (fun c -> c.OrderableQuantity - |> Quantity.toValueUnitString -1 + |> toStr |> fun q -> let s = c - |> printItemConcentration + |> itemConcentrationToString |> String.trim |> fun s -> if s |> String.isNullOrWhiteSpace then "" @@ -1509,162 +1813,40 @@ module Order = |> String.concat " + " - let printOrderableDoseQuantity o = - o.Orderable.Dose.Quantity - |> Quantity.toValueUnitString -1 - - - let printPrescription sn (o : Order) = - let on = o.Orderable.Name |> Name.toString - let sn = sn |> Array.filter String.notEmpty - - let printItem get unt o = - o.Orderable.Components - |> Seq.collect (fun c -> - c.Items - |> Seq.collect (fun i -> - let n = i.Name |> Name.toString - if sn |> Seq.exists ((=) n) then - i - |> get - |> unt - |> fun s -> - if on |> String.startsWith n then seq [ s ] - else - seq [ $"{s} {n}" ] + let componentQuantityToString = + componentQuantityTo (Quantity.toValueUnitString -1) - else Seq.empty - ) - ) - |> String.concat " + " - match o.Prescription with - | Discontinuous fr -> - // frequencies - let fr = - fr - |> Frequency.toValueUnitString -1 + let componentQuantityToMd = + componentQuantityTo (Quantity.toValueUnitMarkdown -1) - let dq = - o - |> printItem - (fun i -> i.Dose.Quantity) - (Quantity.toValueUnitString 3) - let dt = - o - |> printItem - (fun i -> i.Dose.PerTimeAdjust) - (PerTimeAdjust.toValueUnitString 2) - - let pres = $"{o.Orderable.Name |> Name.toString} {fr} {dq} ({dt})" - let prep = $"{o |> printComponentQuantity}" - let adm = $"{fr} {o |> printOrderableDoseQuantity}" - - pres, prep, adm - - | Continuous -> - // infusion rate - let rt = - o.Orderable.Dose.Rate - |> Rate.toValueUnitString -1 - - let oq = - o.Orderable.OrderableQuantity - |> Quantity.toValueUnitString -1 - - let it = - o - |> printItem - (fun i -> i.OrderableQuantity) - (Quantity.toValueUnitString 2) - - let dr = - o - |> printItem - (fun i -> i.Dose.RateAdjust) - (RateAdjust.toValueUnitString 2) - - let pres = $"""{sn |> String.concat " + "} {dr}""" - let prep = o |> printComponentQuantity - let adm = $"""{sn |> String.concat " + "} {it} in {oq}, {rt}""" - - pres, prep, adm - - | Timed (fr, tme) -> - - // frequencies - let fr = - fr - |> Frequency.toValueUnitString -1 - - let tme = - tme - |> Time.toValueUnitString 2 - - // infusion rate - let rt = - o.Orderable.Dose.Rate - |> Rate.toValueUnitString -1 - - let dq = - o - |> printItem - (fun i -> i.Dose.Quantity) - (Quantity.toValueUnitString 3) - - let dt = - o - |> printItem - (fun i -> i.Dose.PerTimeAdjust) - (PerTimeAdjust.toValueUnitString 1) - - let pres = $"{o.Orderable.Name |> Name.toString} {fr} {dq} ({dt})" - let prep = o |> printComponentQuantity - let adm = $"{fr} {o |> printOrderableDoseQuantity} in {tme} = {rt}" + let orderableDoseQuantityTo toStr (o: Order) = + o.Orderable.Dose.Quantity + |> toStr - pres, prep, adm + let orderableDoseQuantityToString = + orderableDoseQuantityTo (Quantity.toValueUnitString -1) - module Markdown = + let orderableDoseQuantityToMd = + orderableDoseQuantityTo (Quantity.toValueUnitMarkdown -1) - let printItemConcentration (c : Component) = - c.Items - |> Seq.map (fun i -> - i.ComponentConcentration - |> Concentration.toValueUnitMarkdown -1 - |> fun s -> - $"{s} {i.Name |> Name.toString}" - ) - |> String.concat " + " + let printOrderTo + freqToStr + doseQtyToStr + perTimeAdjustToStr + compQtyToStr + orbDoseQtyToStr + doseRateToStr + orbQtyToStr + orbQtyToStr2 + dosRateAdjustToStr + timeToStr + sn (o : Order) = - let printComponentQuantity o = - o.Orderable.Components - |> Seq.map (fun c -> - c.OrderableQuantity - |> Quantity.toValueUnitMarkdown -1 - |> fun q -> - let s = - c - |> printItemConcentration - |> String.trim - |> fun s -> - if s |> String.isNullOrWhiteSpace then "" - else - $" ({s})" - $"{q} {c.Name |> Name.toString}{s}" - ) - |> String.concat " + " - - - let printOrderableDoseQuantity o = - o.Orderable.Dose.Quantity - |> Quantity.toValueUnitMarkdown -1 - - - let printPrescription sn (o : Order) = let on = o.Orderable.Name |> Name.toString let sn = sn |> Array.filter String.notEmpty @@ -1693,24 +1875,24 @@ module Order = // frequencies let fr = fr - |> Frequency.toValueUnitMarkdown -1 + |> freqToStr |> String.replace "/" " per " let dq = o |> printItem (fun i -> i.Dose.Quantity) - (Quantity.toValueUnitMarkdown 3) + doseQtyToStr let dt = o |> printItem (fun i -> i.Dose.PerTimeAdjust) - (PerTimeAdjust.toValueUnitMarkdown 2) + perTimeAdjustToStr let pres = $"{o.Orderable.Name |> Name.toString} {fr} {dq} ({dt |> String.trim})" - let prep = $"{o |> printComponentQuantity}" - let adm = $"{fr} {o |> printOrderableDoseQuantity}" + let prep = $"{o |> compQtyToStr}" + let adm = $"{fr} {o |> orbDoseQtyToStr}" pres |> String.replace "()" "", prep, @@ -1720,26 +1902,26 @@ module Order = // infusion rate let rt = o.Orderable.Dose.Rate - |> Rate.toValueUnitMarkdown -1 + |> doseRateToStr let oq = o.Orderable.OrderableQuantity - |> Quantity.toValueUnitMarkdown -1 + |> orbQtyToStr let it = o |> printItem (fun i -> i.OrderableQuantity) - (Quantity.toValueUnitMarkdown 2) + orbQtyToStr2 let dr = o |> printItem (fun i -> i.Dose.RateAdjust) - (RateAdjust.toValueUnitMarkdown 2) + dosRateAdjustToStr let pres = $"""{sn |> String.concat " + "} {dr}""" - let prep = o |> printComponentQuantity + let prep = o |> compQtyToStr let adm = $"""{sn |> String.concat " + "} {it} in {oq}, {rt}""" pres, prep, adm @@ -1749,39 +1931,67 @@ module Order = // frequencies let fr = fr - |> Frequency.toValueUnitMarkdown -1 + |> freqToStr |> String.replace "/" " per " let tme = tme - |> Time.toValueUnitMarkdown 2 + |> timeToStr // infusion rate let rt = o.Orderable.Dose.Rate - |> Rate.toValueUnitMarkdown -1 + |> doseRateToStr let dq = o |> printItem (fun i -> i.Dose.Quantity) - (Quantity.toValueUnitMarkdown 3) + doseQtyToStr let dt = o |> printItem (fun i -> i.Dose.PerTimeAdjust) - (PerTimeAdjust.toValueUnitMarkdown 2) + perTimeAdjustToStr let pres = $"{o.Orderable.Name |> Name.toString} {fr} {dq} ({dt})" - let prep = o |> printComponentQuantity - let adm = $"{fr} {o |> printOrderableDoseQuantity} in {tme} = {rt}" + let prep = o |> compQtyToStr + let adm = $"{fr} {o |> orbDoseQtyToStr} in {tme} = {rt}" pres |> String.replace "()" "", prep, adm + let printOrderToString = + printOrderTo + (Frequency.toValueUnitString -1) + (Quantity.toValueUnitString 3) + (PerTimeAdjust.toValueUnitString 2) + componentQuantityToString + orderableDoseQuantityToString + (Rate.toValueUnitString -1) + (Quantity.toValueUnitString -1) + (Quantity.toValueUnitString 2) + (RateAdjust.toValueUnitString 2) + (Time.toValueUnitString 2) + + + let printOrderToMd = + printOrderTo + (Frequency.toValueUnitMarkdown -1) + (Quantity.toValueUnitMarkdown 3) + (PerTimeAdjust.toValueUnitMarkdown 2) + componentQuantityToMd + orderableDoseQuantityToMd + (Rate.toValueUnitMarkdown -1) + (Quantity.toValueUnitMarkdown -1) + (Quantity.toValueUnitMarkdown 2) + (RateAdjust.toValueUnitMarkdown 2) + (Time.toValueUnitMarkdown 2) + + module Dto = diff --git a/src/Informedica.GenOrder.Lib/OrderLogger.fs b/src/Informedica.GenOrder.Lib/OrderLogger.fs index f82b3f3..4d6dd92 100644 --- a/src/Informedica.GenOrder.Lib/OrderLogger.fs +++ b/src/Informedica.GenOrder.Lib/OrderLogger.fs @@ -62,7 +62,7 @@ module OrderLogger = try eqs - |> Solver.mapToOrderEqs (o |> Order.mapToEquations mapping) + |> Solver.mapToOrderEqs (o |> Order.mapToOrderEquations mapping) |> List.map (fun e -> match e with | OrderProductEquation (ovar, ovars) @@ -290,7 +290,7 @@ module OrderLogger = sc |> List.iteri (fun i o -> o - |> Order.Print.printPrescription n + |> Order.Print.printOrderToString n |> fun (p, a, d) -> printfn $"%i{i + 1}\tprescription:\t%s{p}" printfn $" \tdispensing:\t%s{a}" diff --git a/src/Informedica.GenOrder.Lib/Scripts/Script1.fsx b/src/Informedica.GenOrder.Lib/Scripts/Script1.fsx index 39d8b38..a794718 100644 --- a/src/Informedica.GenOrder.Lib/Scripts/Script1.fsx +++ b/src/Informedica.GenOrder.Lib/Scripts/Script1.fsx @@ -14,59 +14,17 @@ open Informedica.GenUnits.Lib open Informedica.GenSolver.Lib open Informedica.GenOrder.Lib - -let vu1 = - [|1N..1N..100N|] - |> ValueUnit.withUnit Units.Count.times -let vu2 = - [|1N..1N..10N|] - |> ValueUnit.withUnit Units.Count.times - -(vu1 * vu2) -|> ValueUnit.getValue -|> Array.length - - - -module List = - - let prune maxLength xs = - let l = xs |> List.length - - if (l - maxLength) <= 0 || l <= 2 then xs - else - let d = l / (maxLength - 2) - xs - |> List.fold (fun (i, acc) x -> - if i = 1 || i = l || i % d = 0 then - printfn $"keep {x}" - (i + 1, [x] |> List.append acc) - else - (i + 1, acc) - ) (1, []) - |> snd - - -[1..1..100] -|> List.prune 10 -|> List.length - - - -module Array = - - let prune maxLength xs = - let l = xs |> Array.length - - if (l - maxLength) <= 0 || l <= 2 then xs - else - let d = l / (maxLength - 2) - xs - |> Array.fold (fun (i, acc) x -> - if i = 1 || i = l || i % d = 0 then - printfn $"keep {x}" - (i + 1, [| x |] |> Array.append acc) - else - (i + 1, acc) - ) (1, [||]) - |> snd +module Orderable = Order.Orderable + +let ord = + [ + "paracetamol", "suppository", ["paracetamol"] + ] + |> Order.Dto.discontinuous + "1" + "PCM" + "rect" + |> Order.Dto.fromDto + +ord.Orderable.Components[0].Items[0] +|> Orderable.Item.toString \ No newline at end of file diff --git a/src/Informedica.GenOrder.Lib/Solver.fs b/src/Informedica.GenOrder.Lib/Solver.fs index a1df33e..f6650b6 100644 --- a/src/Informedica.GenOrder.Lib/Solver.fs +++ b/src/Informedica.GenOrder.Lib/Solver.fs @@ -7,10 +7,12 @@ namespace Informedica.GenOrder.Lib /// `Informedica.GenSolver.Lib` module Solver = + open Informedica.Utils.Lib open Informedica.GenUnits.Lib open Informedica.GenSolver.Lib.Types + module Variable = Informedica.GenSolver.Lib.Variable module Name = Variable.Name module ValueRange = Variable.ValueRange @@ -20,7 +22,10 @@ module Solver = module Api = Informedica.GenSolver.Lib.Api - + /// + /// Map a list of OrderEquations to a list of Equations + /// + /// The list of OrderEquations let mapToSolverEqs eqs = eqs |> List.map (fun eq -> @@ -31,6 +36,15 @@ module Solver = |> List.map Equation.nonZeroOrNegative + /// + /// Map a list of Equations to a list of OrderEquations + /// using a list of original OrderEquations + /// + /// The original list of OrderEquations + /// The list of Equations + /// + /// The new list of OrderEquations + /// let mapToOrderEqs ordEqs eqs = let vars = eqs @@ -53,9 +67,11 @@ module Solver = ) + /// Short hand for Api.solveAll true let solveMinMax = Api.solveAll true + /// Short hand for Api.solveAll false let solve = Api.solveAll false diff --git a/src/Server/ScenarioResult.fs b/src/Server/ScenarioResult.fs index bf0c167..866f8a7 100644 --- a/src/Server/ScenarioResult.fs +++ b/src/Server/ScenarioResult.fs @@ -387,7 +387,7 @@ let print (sc: ScenarioResult) = ord |> mapFromOrder |> Order.Dto.fromDto - |> Order.Markdown.printPrescription sn + |> Order.Print.printOrderToMd sn |> fun (prs, prp, adm) -> prs |> Demo.replace |> Shared.ScenarioResult.parseTextItem, prp |> Demo.replace |> Shared.ScenarioResult.parseTextItem,