Giter VIP home page Giter VIP logo

genpres2's Introduction

The GenPRES project

The GenPRES project is an open source software initiative to enable a generic medication order entry solution for Safe and Efficient medication prescriptions, preparation and administration.

This project is initially aimed at the Dutch medical setting, but can easily be applied to any medical setting.



Background

Medication errors are one of the most common sources of medical complications. However, the medication process, prescribing, preparing and administration of medication is als one of the most thoroughly protocolized medical processes.

In order to achieve a safe and efficient medication workflow the following human error prone activities can be solved by Clinical Decision Support Software (CDSS):

  1. Looking up rules and constraints
  2. Calculations
  3. Verification of correct applications of rules and constraints and subsequent calculations

With the assumption that software will not err in basic lookup and calculation activities, given the correct implementation, it can be assumed that such CDSS can achieve a significant reduction in medical errors and increase efficiency of workflow.

The current solution runs at: http://genpres.nl.

Some more background information can be found at:

Build

GitHub Actions
GitHub Actions
Build History

Install pre-requisites

You'll need to install the following pre-requisites in order to build SAFE applications.

Current known build configuration

  • dotnet: 8.0.0
  • npm: 9.8.1
  • node: v18.18.2

For the full application to run a proprietary cache file is needed containing medication product information. Collaborators can request these cache files by contacting the owner of this repository. These cache files cannot be freely distributed!

A demo cache file with medication product data is include in this repository. This contains some sample medication data from a much larger drug formulary database.

Starting the application

Currently, the project is moving to using Vite and the current build system has been updated using package.json scripts and plain commands.

For the time being the application can be started by the following procedure:

  1. npm install will perform a tool restore and install all dependencies
  2. npm run watch-server will start the server
  3. npm start will start the fable compilation and the Vite development server
  4. npm run build will create a deployment to a deploy folder

Open a browser to http://localhost:5173 to view the site.

Additionally, an environmental variable can be set to use a different GenPRES data excel url: export GENPRES_URL_ID=<some url id>. After starting the application, the url that is used will be printed to the terminal. If no env is set, the default url will be used.

Deployment using Docker

This will create a production ready docker image:

docker build -t [USERNAME]/genpres .

Note: this will build using the local processor architecture.

To build on a MacOs M1 and still want to publish for an ARM64

docker build --platform linux/amd64 -t halcwb/genpres .

To run the docker image locally:

docker run -it -p 8080:8085 [USERNAME]/genpres

SAFE Stack Documentation

This project is based on the SAFE Stack template. This template can be used to generate a full-stack web application using the SAFE Stack. It was created using the dotnet SAFE Template. If you want to learn more about the template why not start with the quick start guide?

If you want to know more about the full Azure Stack and all of its components (including Azure) visit the official SAFE documentation.

You will find more documentation about the used F# components at the following places:

Collaboration

Any help or collaboration is welcome! You can fork this repository, post issues, ask questions or get on slack.

Some specifics:

  • An opt-in strategy is used in the .gitignore file, i.e. you have to specifically define what should be included instead or the other way around.
  • commits are tagged with
    • chore: something that needs to be done
    • feat: a new feature
    • refact: a refactoring
    • fix: a bug fix, or otherwise
    • docs: documentation
    • tests: testing code

genpres2's People

Contributors

halcwb avatar mrbliz avatar zaid-ajaj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

genpres2's Issues

Event triggering for numbers not right

When changing a weight or height, immediately an event is triggered such that if you type in 20, it triggers with "2" and the server responds to a weight of "2", which is not right ofcourse.

@MrBliz Is there a way to delay event triggering to make sure a number is entered completely? I think the right approach is to use a throttling mechanism, see: https://github.com/halcwb/PediatricRiskCalculator/blob/master/src/Client/Components/NumericInput.fs.

using Thoth.Elmish.Debouncer.

ValueUnit should not simplify concentrations

When a concentration is for example g/g, this gets simplified as a count. However, for concentrations this is not appropriate. It should still be printed out as g/g.

Maybe solve this in the order variable?

Note: introduce a percentage as a count unit.

Document special cases

Should document special medication scenarios:

  • paracetamol supp with product selection
  • trimethoprim/sufla with multi component calculation
  • gentamicin 1/36 hour, with special time unit
  • benzylpenicillin with different dosing units and readability of numbers (i.e. number formatting)
  • continuous infusions
  • medication reconstitution
  • medication in solutions

Npm install very slow and stalls

Probably a problem with a very large package-lock.json.

I had to:

  • Do a clean clone
  • Remove the package-lock.json

Still slow, but may should remove all dependencies on MUI v4.
@MrBliz : is this something you can do?

Find a way to display multi drug component prescription in a nice way

Currently a multi component drug prescription is displayed like:

3 keer per dag 900 mg amoxicilline + 90 mg clavulaanzuur = 100 mg/kg/dag amoxicilline + 10 mg/kg/dag clavulaanzuur (95 - 110 mg/kg/dag)

Everything on a row. A better view is:

3 keer per dag 900 mg amoxicilline +   = 100 mg/kg/dag (95 - 110 mg/kg/dag)
               90 mg clavulaanzuur     = 10 mg/kg/dag 

I think this needs to be a column stack with each column a row stack of typography elements.

See current display code:

    [<JSX.Component>]
    let View (props:
        {|
            scenarios: Deferred<Types.ScenarioResult>
            updateScenario: Types.ScenarioResult -> unit
            selectOrder : (Types.Scenario * Shared.Types.Order option) -> unit
            order: Deferred<(bool * string option * Order) option>
            loadOrder: (string option * Order) -> unit
            updateScenarioOrder : unit -> unit
            localizationTerms : Deferred<string [] []>
        |}) =

        let context = React.useContext(Global.context)
        let lang = context.Localization
        let isMobile = Mui.Hooks.useMediaQuery "(max-width:1200px)"

        let getTerm defVal term =
            props.localizationTerms
            |> Deferred.map (fun terms ->
                Localization.getTerm terms lang term
                |> Option.defaultValue defVal
            )
            |> Deferred.defaultValue defVal

        let state, dispatch =
            React.useElmish (
                init props.order props.scenarios,
                update props.scenarios props.updateScenario,
                [| box props.order; box props.scenarios; box props.updateScenario; box props.selectOrder |]
            )

        let modalOpen, setModalOpen = React.useState(false)

        let handleModalClose = fun () -> setModalOpen false

        let select isLoading lbl selected dispatch xs =
            Components.SimpleSelect.View({|
                updateSelected = dispatch
                label = lbl
                selected = selected
                values = xs
                isLoading = isLoading
            |})

        let autoComplete isLoading lbl selected dispatch xs =
            Components.Autocomplete.View({|
                updateSelected = dispatch
                label = lbl
                selected = selected
                values = xs
                isLoading = isLoading
            |})

        let progress =
            match props.scenarios with
            | Resolved _ -> JSX.jsx $"<></>"
            | _ ->
                JSX.jsx
                    $"""
                import CircularProgress from '@mui/material/CircularProgress';

                <Box sx={ {| mt = 5; display = "flex"; p = 20 |} }>
                    <CircularProgress />
                </Box>
                """

        let typoGraphy (items : Types.TextItem[]) =
            let print item =
                match item with
                | Normal s ->
                    JSX.jsx
                        $"""
                    <Typography color={Mui.Colors.Grey.``700``} display="inline">{s}</Typography>
                    """
                | Bold s ->
                    JSX.jsx
                        $"""
                    <Typography
                    color={Mui.Colors.BlueGrey.``700``}
                    display="inline"
                    >
                    <strong> {s} </strong>
                    </Typography>
                    """
                | Italic s ->
                    JSX.jsx
                        $"""
                    <Typography
                    color={Mui.Colors.Grey.``700``}
                    display="inline"
                    >
                    <strong> {s} </strong>
                    </Typography>
                    """

            JSX.jsx
                $"""
            import Typography from '@mui/material/Typography';

            <Box display="inline" >
                {items |> Array.map print |> unbox |> React.fragment}
            </Box>
            """

        let displayScenario med (sc : Types.Scenario) =
            if med |> Option.isNone then JSX.jsx $"""<></>"""
            else
                let med =
                    med |> Option.defaultValue ""
                    |> fun s -> 
                        if s.Contains(sc.Shape) then $"{s} {sc.DoseType}"
                        else
                            $"{s} {sc.Shape} {sc.DoseType}"

                let ord =
                    sc.Order

                let item icon prim sec =
                    JSX.jsx
                        $"""
                    <ListItem>
                        <ListItemIcon>
                            {icon}
                        </ListItemIcon>
                        <Box sx={ {| display="flex"; flexDirection="column" |} }>
                            {prim}
                            {sec |> typoGraphy}
                        </Box>
                    </ListItem>
                    """

                let content =
                    JSX.jsx
                        $"""
                    <React.Fragment>
                        <Typography variant="h6" >
                            {med}
                        </Typography>
                        <List sx={ {| width="100%"; maxWidth=800; bgcolor=Mui.Colors.Grey.``50`` |} }>
                            {
                                [|
                                    item Mui.Icons.Notes (Terms.``Prescribe Prescription`` |> getTerm "Voorschrift") sc.Prescription
                                    if sc.Preparation |> Array.length > 0 then
                                        item Mui.Icons.Vaccines (Terms.``Prescribe Preparation`` |> getTerm "Bereiding") sc.Preparation
                                    item Mui.Icons.MedicationLiquid (Terms.``Prescribe Administration`` |> getTerm "Toediening") sc.Administration
                                |]
                                |> unbox
                                |> React.fragment
                            }
                        </List>
                    </React.Fragment>
                    """

                JSX.jsx
                    $"""
                import React from 'react';
                import Card from '@mui/material/Card';
                import CardActions from '@mui/material/CardActions';
                import CardContent from '@mui/material/CardContent';
                import Button from '@mui/material/Button';
                import Typography from '@mui/material/Typography';
                import Box from '@mui/material/Box';
                import List from '@mui/material/List';
                import ListItem from '@mui/material/ListItem';
                import Divider from '@mui/material/Divider';
                import ListItemText from '@mui/material/ListItemText';
                import ListItemIcon from '@mui/material/ListItemIcon';
                import Avatar from '@mui/material/Avatar';
                import Typography from '@mui/material/Typography';

                <Box sx={ {| mt=3; height="100%" |} } >
                    <Card sx={ {| minWidth = 275 |}  }>
                        <CardContent>
                            {content}
                            {progress}
                        </CardContent>
                        <CardActions>
                            <Button
                                size="small"
                                onClick={fun () -> setModalOpen true; (sc, ord) |> props.selectOrder}
                            >{Terms.Edit |> getTerm "bewerken"}</Button>
                        </CardActions>
                    </Card>
                </Box>
                """

        let stackDirection =
            if  Mui.Hooks.useMediaQuery "(max-width:900px)" then "column" else "row"

        let cards =
            JSX.jsx
                $"""
            import CardContent from '@mui/material/CardContent';
            import Typography from '@mui/material/Typography';
            import Stack from '@mui/material/Stack';

            <React.Fragment>
                <Stack direction="column" spacing={3}>
                    <Typography sx={ {| fontSize=14 |} } color="text.secondary" gutterBottom>
                        {Terms.``Prescribe Scenarios`` |> getTerm "Medicatie scenario's"}
                    </Typography>
                    {
                        match props.scenarios with
                        | Resolved scrs -> false, scrs.Indication, scrs.Indications
                        | _ -> true, None, [||]
                        |> fun (isLoading, sel, items) ->
                            let lbl = (Terms.``Prescribe Indications`` |> getTerm "Indicaties") 
                            if isMobile then
                                items
                                |> Array.map (fun s -> s, s)
                                |> select isLoading lbl sel (IndicationChange >> dispatch)
                            else
                                items
                                |> autoComplete isLoading lbl sel (IndicationChange >> dispatch)
                    }
                    <Stack direction={stackDirection} spacing={3} >
                        {
                            match props.scenarios with
                            | Resolved scrs -> false, scrs.Medication, scrs.Medications
                            | _ -> true, None, [||]
                            |> fun (isLoading, sel, items) ->
                                let lbl = (Terms.``Prescribe Medications`` |> getTerm "Medicatie")
                                if isMobile then
                                    items
                                    |> Array.map (fun s -> s, s)
                                    |> select isLoading lbl sel (MedicationChange >> dispatch)
                                else
                                    items
                                    |> autoComplete isLoading lbl sel (MedicationChange >> dispatch)

                        }
                        {
                            match props.scenarios with
                            | Resolved scrs -> false, scrs.Route, scrs.Routes
                            | _ -> true, None, [||]
                            |> fun (isLoading, sel, items) ->
                                let lbl = (Terms.``Prescribe Routes`` |> getTerm "Routes")
                                if isMobile then
                                    items
                                    |> Array.map (fun s -> s, s)
                                    |> select isLoading lbl sel (RouteChange >> dispatch)
                                else
                                    items
                                    |> autoComplete isLoading lbl sel (RouteChange >> dispatch)
                                
                        }
                        {
                            match props.scenarios with
                            | Resolved scrs when scrs.Indication.IsSome &&
                                                 scrs.Medication.IsSome &&
                                                 scrs.Route.IsSome -> 
                                (false, scrs.DoseType, scrs.DoseTypes)
                                |> fun (isLoading, sel, items) ->
                                    let lbl = "Doseer types"
                                    if isMobile then
                                        items
                                        |> Array.map (fun s -> s, s)
                                        |> select isLoading lbl sel (DoseTypeChange >> dispatch)
                                    else
                                        items
                                        |> autoComplete isLoading lbl sel (DoseTypeChange >> dispatch)                                
                            | _ -> JSX.jsx $"<></>"
                        }

                        <Box sx={ {| mt=2 |} }>
                            <Button variant="text" onClick={fun _ -> Clear |> dispatch } fullWidth startIcon={Mui.Icons.Delete} >
                                {Terms.Delete |> getTerm "Verwijder"}
                            </Button>
                        </Box>

                    </Stack>
                    <Stack direction="column" >
                        {
                            match props.scenarios with
                            | Resolved sc ->
                                sc.Medication,
                                sc.Scenarios
                            | _ -> None, [||]
                            |> fun (med, scs) ->
                                scs
                                |> Array.map (displayScenario med)
                            |> unbox |> React.fragment
                        }
                    </Stack>
                </Stack>
            </React.Fragment>
            """

        let modalStyle =
            {|
                position="absolute"
                top= "50%"
                left= "50%"
                transform= "translate(-50%, -50%)"
                width= 400
                bgcolor= "background.paper"
                boxShadow= 24
            |}

        JSX.jsx
            $"""
        import Box from '@mui/material/Box';
        import Modal from '@mui/material/Modal';

        <div>
            <Box sx={ {| height="100%" |} }>
                {cards}
                {progress}
            </Box>
            <Modal open={modalOpen} onClose={handleModalClose} >
                <Box sx={modalStyle}>
                    {
                        Order.View {|
                            order = props.order
                            loadOrder = props.loadOrder
                            updateScenarioOrder = props.updateScenarioOrder
                            closeOrder = handleModalClose
                            localizationTerms = props.localizationTerms
                        |}
                    }
                </Box>
            </Modal>
        </div>
        """

Case when component quantity is zero

In a solution when the solvent quantity is zero, i.e. the medication cannot be further diluted. This is a special case that is not handled correctly currently. Need to resolve this.

Server build will fail for a net7 update

When trying to update using paket update, this will move to net7, however, then the build fails.

Restrict the framework to net6 in the paket dependencies to prevent upgrade to net7.

Resolved in: a855b4e.

Merge with libsupdate branch

@MrBliz I think you can relatively easy merge the libsupdate branch. A couple of things to beware of:

  1. ClosedXML should be pinned to 0.97.
  2. I had to pin all the Feliz and Fable libs, otherwise I got into trouble with the installed react libs.
  3. I only made some minor code changes in the client and server. But the endpoints are the same!
  4. You'll need to manually add the cache files to the data folders.

Then you will have a full blown drug prescription system!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.