2019-04-07 18:08:22 +02:00
|
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
|
|
module Timeline (
|
2019-04-14 19:20:24 +02:00
|
|
|
State(..)
|
|
|
|
, stateAt
|
2019-04-07 18:08:22 +02:00
|
|
|
) where
|
|
|
|
|
|
|
|
import Data.List (sortOn)
|
2019-04-14 19:20:24 +02:00
|
|
|
import Data.Map ((!), Map, mapWithKey)
|
2019-04-07 18:08:22 +02:00
|
|
|
import Data.Time (Day, diffDays)
|
|
|
|
import Event (Event(..), EventType(..))
|
2019-04-14 19:20:24 +02:00
|
|
|
import Medicine (MedicineName, Pharmacy)
|
|
|
|
import YAML ((.:), Value(..), YAML(..))
|
2019-04-07 18:08:22 +02:00
|
|
|
|
|
|
|
data State = State {
|
|
|
|
day :: Day
|
2019-04-14 19:20:24 +02:00
|
|
|
, stock :: Map MedicineName Float
|
|
|
|
, consumptionRate :: Map String Float
|
2019-04-07 18:08:22 +02:00
|
|
|
}
|
|
|
|
|
2019-04-14 19:20:24 +02:00
|
|
|
instance YAML State where
|
|
|
|
toYAML (State {day, stock, consumptionRate}) = Object [
|
|
|
|
"date" .: day
|
|
|
|
, "stock" .: stock
|
|
|
|
, "consumption rates" .: consumptionRate
|
|
|
|
]
|
2019-04-07 18:08:22 +02:00
|
|
|
|
2019-04-14 19:20:24 +02:00
|
|
|
initState :: Pharmacy -> State
|
|
|
|
initState pharmacy = State {
|
2019-04-07 18:08:22 +02:00
|
|
|
day = toEnum 0
|
|
|
|
, stock = const 0 <$> pharmacy
|
|
|
|
, consumptionRate = const 0 <$> pharmacy
|
|
|
|
}
|
|
|
|
|
|
|
|
applyEvent :: State -> Event -> State
|
|
|
|
applyEvent state (Event {date, amounts, eventType}) =
|
|
|
|
case eventType of
|
|
|
|
Prescription -> newState {consumptionRate = amounts}
|
|
|
|
Provisioning -> newState {stock = mapWithKey addAmount $ stock newState}
|
|
|
|
where
|
|
|
|
newState = setDay date state
|
|
|
|
addAmount medicineName = (+ (amounts ! medicineName))
|
|
|
|
|
|
|
|
setDay :: Day -> State -> State
|
|
|
|
setDay newDay state@(State {day, stock, consumptionRate}) = state {
|
|
|
|
day = newDay
|
|
|
|
, stock = mapWithKey consume stock
|
|
|
|
}
|
|
|
|
where
|
|
|
|
duration = toEnum . fromEnum $ newDay `diffDays` day
|
|
|
|
consume medicineName initialAmount =
|
|
|
|
max 0 $ initialAmount - duration * (consumptionRate ! medicineName)
|
|
|
|
|
2019-04-14 19:20:24 +02:00
|
|
|
stateAt :: Day -> Pharmacy -> [Event] -> State
|
2019-04-21 22:29:56 +02:00
|
|
|
stateAt targetDay pharmacy =
|
|
|
|
setDay targetDay . lastState
|
2019-04-14 19:20:24 +02:00
|
|
|
where
|
2019-04-21 22:29:56 +02:00
|
|
|
lastState =
|
|
|
|
foldl applyEvent (initState pharmacy)
|
|
|
|
. sortOn date
|
|
|
|
. filter ((<= targetDay) . date)
|