    {-|

    Print some statistics for the journal.

    -}

    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE TemplateHaskell #-}
    {-# LANGUAGE NamedFieldPuns #-}
    {-# LANGUAGE RecordWildCards #-}

    module Hledger.Cli.Commands.Stats (
      statsmode
     ,stats
    )
    where

    import Control.Monad (when)
    import Data.Default (def)
    import Data.List (intercalate, nub, sortOn)
    import Data.List.Extra (nubSort)
    import Data.Map qualified as Map
    import Data.Maybe (fromMaybe)
    import Data.HashSet (size, fromList)
    import Data.Text qualified as T
    import Data.Text.Lazy qualified as TL
    import Data.Time.Calendar (Day, addDays, diffDays)
    import Data.Time.Clock.POSIX (getPOSIXTime)
    import GHC.Stats
    import GitHash (tGitInfoCwdTry)
    import System.Console.CmdArgs.Explicit hiding (Group)
    import System.FilePath (takeFileName)
    import System.Mem (performMajorGC)
    import Text.Printf (printf)
    import Text.Tabular.AsciiWide

    import Hledger
    import Hledger.Cli.CliOptions
    import Hledger.Cli.Utils (writeOutput)
    import Hledger.Cli.Version (packageversion, versionStringWith)

    statsmode :: Mode RawOpts
    statsmode = String
    -> [Flag RawOpts]
    -> [(String, [Flag RawOpts])]
    -> [Flag RawOpts]
    -> ([Arg RawOpts], Maybe (Arg RawOpts))
    -> Mode RawOpts
    hledgerCommandMode
      $(embedFileRelative "Hledger/Cli/Commands/Stats.txt")
      [ [String] -> (RawOpts -> RawOpts) -> String -> Flag RawOpts
    forall a. [String] -> (a -> a) -> String -> Flag a
    flagNone [String
    "1"] (String -> RawOpts -> RawOpts
    setboolopt String
    "") String
    "show a single line of output"
          -- Cli.hs converts -1 to --depth=1, no point giving it another name here
      , [String] -> (RawOpts -> RawOpts) -> String -> Flag RawOpts
    forall a. [String] -> (a -> a) -> String -> Flag a
    flagNone [String
    "verbose",String
    "v"] (String -> RawOpts -> RawOpts
    setboolopt String
    "verbose") String
    "show more detailed output"
      ,[String] -> Update RawOpts -> String -> String -> Flag RawOpts
    forall a. [String] -> Update a -> String -> String -> Flag a
    flagReq  [String
    "output-file",String
    "o"] (\String
    s RawOpts
    opts -> RawOpts -> Either String RawOpts
    forall a b. b -> Either a b
    Right (RawOpts -> Either String RawOpts)
    -> RawOpts -> Either String RawOpts
    forall a b. (a -> b) -> a -> b
    $ String -> String -> RawOpts -> RawOpts
    setopt String
    "output-file" String
    s RawOpts
    opts) String
    "FILE" String
    "write output to FILE."
      ]
      [(String, [Flag RawOpts])]
    cligeneralflagsgroups1
      [Flag RawOpts]
    hiddenflags
      ([], Arg RawOpts -> Maybe (Arg RawOpts)
    forall a. a -> Maybe a
    Just (Arg RawOpts -> Maybe (Arg RawOpts))
    -> Arg RawOpts -> Maybe (Arg RawOpts)
    forall a b. (a -> b) -> a -> b
    $ String -> Arg RawOpts
    argsFlag String
    "[QUERY]")

    -- like Register.summarisePostings
    -- | Print various statistics for the journal.
    stats :: CliOpts -> Journal -> IO ()
    stats :: CliOpts -> Journal -> IO ()
    stats opts :: CliOpts
    opts@CliOpts{rawopts_ :: CliOpts -> RawOpts
    rawopts_=RawOpts
    rawopts, reportspec_ :: CliOpts -> ReportSpec
    reportspec_=ReportSpec
    rspec, POSIXTime
    progstarttime_ :: POSIXTime
    progstarttime_ :: CliOpts -> POSIXTime
    progstarttime_} Journal
    j = do
      t <- IO POSIXTime
    getPOSIXTime
      -- the first lines - general journal stats for one or more periods
      let
        today = ReportSpec -> Day
    _rsDay ReportSpec
    rspec
        oneline = String -> RawOpts -> Int
    intopt String
    "depth" RawOpts
    rawopts Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    1
        verbose = String -> RawOpts -> Bool
    boolopt String
    "verbose" RawOpts
    rawopts
        q = ReportSpec -> Query
    _rsQuery ReportSpec
    rspec
        l = Query -> Journal -> Ledger
    ledgerFromJournal Query
    q Journal
    j
        intervalspans = (DateSpan, Maybe DayPartition) -> Maybe DayPartition
    forall a b. (a, b) -> b
    snd ((DateSpan, Maybe DayPartition) -> Maybe DayPartition)
    -> (DateSpan, Maybe DayPartition) -> Maybe DayPartition
    forall a b. (a -> b) -> a -> b
    $ Journal -> ReportSpec -> (DateSpan, Maybe DayPartition)
    reportSpanBothDates Journal
    j ReportSpec
    rspec
        ismultiperiod = Maybe DayPartition -> Int
    forall a. Maybe a -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length Maybe DayPartition
    intervalspans Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    1
        (txts, tnums) = unzip . map (showLedgerStats verbose l today) $ maybeDayPartitionToDateSpans intervalspans
        out1 = (if Bool
    ismultiperiod then String -> String
    forall a. a -> a
    id else String -> String
    forall a. HasCallStack => [a] -> [a]
    init) (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ [String] -> String
    unlines [String]
    txts

      -- the last line - overall performance stats, with memory info if available,
      -- in human-friendly or machine-friendly format
      -- normal:
      --  Runtime stats       : 0.14 s elapsed, 7606 txns/s
      --  Runtime stats       : 0.14 s elapsed, 7606 txns/s, 6 MB live, 18 MB alloc
      -- oneline:
      --  SHORTVERSION(<SPC><TAB>VALUE[<SPC>DESC])+
      --  1.50.99<SPC><TAB>hledger 1.50.99-g0835a2485-20251119, mac-aarch64<SPC><TAB>2025.journal<SPC><TAB>1.99 s elapsed<SPC><TAB>524 txns/s
      --  1.50.99<SPC><TAB>hledger 1.50.99-g0835a2485-20251119, mac-aarch64<SPC><TAB>2025.journal<SPC><TAB>1.99 s elapsed<SPC><TAB>524 txns/s<SPC><TAB>788 MB live<SPC><TAB>2172 MB alloc
      --
      rtsstats <- getRTSStatsEnabled
      (maxlivemb, maxinusemb) <- if rtsstats
      then do
        -- do one last garbage collection; probably little effect, hopefully little wasted time
        performMajorGC
        RTSStats{..} <- getRTSStats
        return (toMegabytes max_live_bytes, toMegabytes max_mem_in_use_bytes)
      else
        return (0,0)
      let
        (label, sep)
          | oneline   = (lstrip $ versionStringWith $$tGitInfoCwdTry False "" packageversion <> "\t", "\t")
          | otherwise = ("Runtime stats       : ", ", ")
        dt = POSIXTime
    t POSIXTime -> POSIXTime -> POSIXTime
    forall a. Num a => a -> a -> a
    - POSIXTime
    progstarttime_
        tnum = [Int] -> Int
    forall a. Num a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
    sum [Int]
    tnums
        ss =
          [ String -> String
    takeFileName (String -> String) -> String -> String
    forall a b. (a -> b) -> a -> b
    $ Journal -> String
    journalFilePath Journal
    j | Bool
    oneline ]
          [String] -> [String] -> [String]
    forall a. Semigroup a => a -> a -> a
    <> [
           String -> Float -> String
    forall r. PrintfType r => String -> r
    printf String
    "%.2f s elapsed" (POSIXTime -> Float
    forall a b. (Real a, Fractional b) => a -> b
    realToFrac POSIXTime
    dt :: Float)
          ,String -> Float -> String
    forall r. PrintfType r => String -> r
    printf String
    "%.0f txns/s" (Int -> Float
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    tnum Float -> Float -> Float
    forall a. Fractional a => a -> a -> a
    / POSIXTime -> Float
    forall a b. (Real a, Fractional b) => a -> b
    realToFrac POSIXTime
    dt :: Float)
          ]
          [String] -> [String] -> [String]
    forall a. Semigroup a => a -> a -> a
    <> if Bool
    rtsstats then [
           String -> Float -> String
    forall r. PrintfType r => String -> r
    printf String
    "%0.0f MB live" Float
    maxlivemb
          ,String -> Float -> String
    forall r. PrintfType r => String -> r
    printf String
    "%0.0f MB alloc" Float
    maxinusemb
          -- printf "%0.0f MB avg live" (toMegabytes $ fromIntegral cumulative_live_bytes / fromIntegral major_gcs)
          ]
          else [
           String
    "(add +RTS -T -RTS for more)"
          ]
        out2 = String
    label String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate String
    sep [String]
    ss String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    "\n"

      when (not oneline) $ writeOutput opts out1
      when (oneline && debugLevel>0) $ do
        let tabstops = String -> [String] -> String
    forall a. [a] -> [[a]] -> [a]
    intercalate (Int -> Char -> String
    forall a. Int -> a -> [a]
    replicate Int
    7 Char
    ' ') (Int -> String -> [String]
    forall a. Int -> a -> [a]
    replicate Int
    21 String
    ".") String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String
    "\n"
        writeOutput opts tabstops
      writeOutput opts $ (if ismultiperiod then "\n" else "") <> out2

    toMegabytes :: a -> Float
    toMegabytes a
    n = a -> Float
    forall a b. (Real a, Fractional b) => a -> b
    realToFrac a
    n Float -> Float -> Float
    forall a. Fractional a => a -> a -> a
    / Float
    1000000 ::Float  -- SI preferred definition, 10^6
    -- toMebibytes n = realToFrac n / 1048576 ::Float  -- traditional computing definition, 2^20

    -- | Generate multiline stats output, possibly verbose,
    -- for the given ledger and date period and current date.
    -- Also return the number of transactions in the period.
    showLedgerStats :: Bool -> Ledger -> Day -> DateSpan -> (String, Int)
    showLedgerStats :: Bool -> Ledger -> Day -> DateSpan -> (String, Int)
    showLedgerStats Bool
    verbose Ledger
    l Day
    today DateSpan
    spn =
        ([String] -> String
    unlines ([String] -> String) -> [String] -> String
    forall a b. (a -> b) -> a -> b
    $ ((Text, String) -> String) -> [(Text, String)] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map (Text -> String
    TL.unpack (Text -> String)
    -> ((Text, String) -> Text) -> (Text, String) -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . TableOpts -> Header Cell -> Text
    renderRow TableOpts
    forall a. Default a => a
    def{tableBorders=False, borderSpaces=False} (Header Cell -> Text)
    -> ((Text, String) -> Header Cell) -> (Text, String) -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Text, String) -> Header Cell
    showRow) [(Text, String)]
    stts
        ,Int
    tnum)
      where
        showRow :: (Text, String) -> Header Cell
    showRow (Text
    label, String
    val) = Properties -> [Header Cell] -> Header Cell
    forall h. Properties -> [Header h] -> Header h
    Group Properties
    NoLine ([Header Cell] -> Header Cell) -> [Header Cell] -> Header Cell
    forall a b. (a -> b) -> a -> b
    $ (Text -> Header Cell) -> [Text] -> [Header Cell]
    forall a b. (a -> b) -> [a] -> [b]
    map (Cell -> Header Cell
    forall h. h -> Header h
    Header (Cell -> Header Cell) -> (Text -> Cell) -> Text -> Header Cell
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Align -> Text -> Cell
    textCell Align
    TopLeft)
          [Maybe Int -> Maybe Int -> Bool -> Bool -> Text -> Text
    fitText (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    w) (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    w) Bool
    False Bool
    True Text
    label Text -> Text -> Text
    `T.append` Text
    ": ", String -> Text
    T.pack String
    val]
        w :: Int
    w = Int
    20  -- keep synced with labels above
        -- w = maximum $ map (T.length . fst) stts
        ([(Text, String)]
    stts, Int
    tnum) = ([
           (Text
    "Main file", String
    path' :: String) -- ++ " (from " ++ source ++ ")")
          ,(Text
    "Included files", if Bool
    verbose then [String] -> String
    unlines [String]
    includedpaths else Int -> String
    forall a. Show a => a -> String
    show ([String] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [String]
    includedpaths))
          ,(Text
    "Txns span", String -> String -> String -> Integer -> String
    forall r. PrintfType r => String -> r
    printf String
    "%s to %s (%d days)" (DateSpan -> String
    showstart DateSpan
    spn) (DateSpan -> String
    showend DateSpan
    spn) Integer
    days)
          ,(Text
    "Last txn", String -> (Day -> String) -> Maybe Day -> String
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe String
    "none" Day -> String
    forall a. Show a => a -> String
    show Maybe Day
    lastdate String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ Maybe Integer -> String
    forall {a} {t}.
    (IsString a, PrintfArg t, PrintfType a, Ord t, Num t) =>
    Maybe t -> a
    showelapsed Maybe Integer
    lastelapsed)
          ,(Text
    "Txns", String -> Int -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%d (%0.1f per day)" Int
    tnum Double
    txnrate)
          ,(Text
    "Txns last 30 days", String -> Int -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%d (%0.1f per day)" Int
    tnum30 Double
    txnrate30)
          ,(Text
    "Txns last 7 days", String -> Int -> Double -> String
    forall r. PrintfType r => String -> r
    printf String
    "%d (%0.1f per day)" Int
    tnum7 Double
    txnrate7)
          ,(Text
    "Payees/descriptions", Int -> String
    forall a. Show a => a -> String
    show (Int -> String) -> Int -> String
    forall a b. (a -> b) -> a -> b
    $ HashSet Text -> Int
    forall a. HashSet a -> Int
    size (HashSet Text -> Int) -> HashSet Text -> Int
    forall a b. (a -> b) -> a -> b
    $ [Text] -> HashSet Text
    forall a. (Eq a, Hashable a) => [a] -> HashSet a
    fromList ([Text] -> HashSet Text) -> [Text] -> HashSet Text
    forall a b. (a -> b) -> a -> b
    $ (Transaction -> Text) -> [Transaction] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map (Transaction -> Text
    tdescription) [Transaction]
    ts)
          ,(Text
    "Accounts", String -> Int -> Int -> String
    forall r. PrintfType r => String -> r
    printf String
    "%d (depth %d)" Int
    acctnum Int
    acctdepth)
          ,(Text
    "Commodities",   String -> String -> Text -> String
    forall r. PrintfType r => String -> r
    printf String
    "%s%s" (Int -> String
    forall a. Show a => a -> String
    show (Int -> String) -> Int -> String
    forall a b. (a -> b) -> a -> b
    $ [Text] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [Text]
    cs)        (if Bool
    verbose then Text
    " (" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> [Text] -> Text
    T.intercalate Text
    ", " [Text]
    cs Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")" else Text
    ""))
          ,(Text
    "Market prices", String -> String -> Text -> String
    forall r. PrintfType r => String -> r
    printf String
    "%s%s" (Int -> String
    forall a. Show a => a -> String
    show (Int -> String) -> Int -> String
    forall a b. (a -> b) -> a -> b
    $ [PriceDirective] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [PriceDirective]
    mktprices) (if Bool
    verbose then Text
    " (" Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text -> [Text] -> Text
    T.intercalate Text
    ", " [Text]
    mktpricecommodities Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <> Text
    ")" else Text
    ""))
        -- Txns this month     : %(monthtxns)s (last month in the same period: %(lastmonthtxns)s)
        -- Unmarked txns      : %(unmarked)s
        -- Days since reconciliation   : %(reconcileelapsed)s
        -- Days since last txn : %(recentelapsed)s
         ]
         ,Int
    tnum1)
           where
             j :: Journal
    j = Ledger -> Journal
    ljournal Ledger
    l
             path' :: String
    path' = if Bool
    verbose then String
    path else String
    ".../" String -> String -> String
    forall a. Semigroup a => a -> a -> a
    <> String -> String
    takeFileName String
    path where path :: String
    path = Journal -> String
    journalFilePath Journal
    j
             includedpaths :: [String]
    includedpaths = Int -> [String] -> [String]
    forall a. Int -> [a] -> [a]
    drop Int
    1 ([String] -> [String]) -> [String] -> [String]
    forall a b. (a -> b) -> a -> b
    $ Journal -> [String]
    journalFilePaths Journal
    j
             ts :: [Transaction]
    ts = (Transaction -> Day) -> [Transaction] -> [Transaction]
    forall b a. Ord b => (a -> b) -> [a] -> [a]
    sortOn Transaction -> Day
    tdate ([Transaction] -> [Transaction]) -> [Transaction] -> [Transaction]
    forall a b. (a -> b) -> a -> b
    $ (Transaction -> Bool) -> [Transaction] -> [Transaction]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (DateSpan -> Day -> Bool
    spanContainsDate DateSpan
    spn (Day -> Bool) -> (Transaction -> Day) -> Transaction -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Transaction -> Day
    tdate) ([Transaction] -> [Transaction]) -> [Transaction] -> [Transaction]
    forall a b. (a -> b) -> a -> b
    $ Journal -> [Transaction]
    jtxns Journal
    j
             as :: [Text]
    as = [Text] -> [Text]
    forall a. Eq a => [a] -> [a]
    nub ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (Posting -> Text) -> [Posting] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map Posting -> Text
    paccount ([Posting] -> [Text]) -> [Posting] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (Transaction -> [Posting]) -> [Transaction] -> [Posting]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap Transaction -> [Posting]
    tpostings [Transaction]
    ts
             cs :: [Text]
    cs = (String -> [Text])
    -> (Map Text AmountStyle -> [Text])
    -> Either String (Map Text AmountStyle)
    -> [Text]
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either String -> [Text]
    forall a. String -> a
    error' Map Text AmountStyle -> [Text]
    forall k a. Map k a -> [k]
    Map.keys (Either String (Map Text AmountStyle) -> [Text])
    -> Either String (Map Text AmountStyle) -> [Text]
    forall a b. (a -> b) -> a -> b
    $ [Amount] -> Either String (Map Text AmountStyle)
    commodityStylesFromAmounts ([Amount] -> Either String (Map Text AmountStyle))
    -> [Amount] -> Either String (Map Text AmountStyle)
    forall a b. (a -> b) -> a -> b
    $ (Posting -> [Amount]) -> [Posting] -> [Amount]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap (MixedAmount -> [Amount]
    amountsRaw (MixedAmount -> [Amount])
    -> (Posting -> MixedAmount) -> Posting -> [Amount]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Posting -> MixedAmount
    pamount) ([Posting] -> [Amount]) -> [Posting] -> [Amount]
    forall a b. (a -> b) -> a -> b
    $ (Transaction -> [Posting]) -> [Transaction] -> [Posting]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap Transaction -> [Posting]
    tpostings [Transaction]
    ts  -- PARTIAL:
             lastdate :: Maybe Day
    lastdate | [Transaction] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Transaction]
    ts = Maybe Day
    forall a. Maybe a
    Nothing
                      | Bool
    otherwise = Day -> Maybe Day
    forall a. a -> Maybe a
    Just (Day -> Maybe Day) -> Day -> Maybe Day
    forall a b. (a -> b) -> a -> b
    $ Transaction -> Day
    tdate (Transaction -> Day) -> Transaction -> Day
    forall a b. (a -> b) -> a -> b
    $ [Transaction] -> Transaction
    forall a. HasCallStack => [a] -> a
    last [Transaction]
    ts
             lastelapsed :: Maybe Integer
    lastelapsed = (Day -> Integer) -> Maybe Day -> Maybe Integer
    forall a b. (a -> b) -> Maybe a -> Maybe b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (Day -> Day -> Integer
    diffDays Day
    today) Maybe Day
    lastdate
             showelapsed :: Maybe t -> a
    showelapsed Maybe t
    Nothing = a
    ""
             showelapsed (Just t
    dys) = String -> t -> String -> a
    forall r. PrintfType r => String -> r
    printf String
    " (%d %s)" t
    dys' String
    direction
                                       where dys' :: t
    dys' = t -> t
    forall a. Num a => a -> a
    abs t
    dys
                                             direction :: String
    direction | t
    dys t -> t -> Bool
    forall a. Ord a => a -> a -> Bool
    >= t
    0 = String
    "days ago" :: String
                                                       | Bool
    otherwise = String
    "days from now"
             tnum1 :: Int
    tnum1 = [Transaction] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [Transaction]
    ts  -- Integer would be better
             showstart :: DateSpan -> String
    showstart (DateSpan (Just EFDay
    efd) Maybe EFDay
    _) = Day -> String
    forall a. Show a => a -> String
    show (Day -> String) -> Day -> String
    forall a b. (a -> b) -> a -> b
    $ EFDay -> Day
    fromEFDay EFDay
    efd
             showstart DateSpan
    _ = String
    ""
             showend :: DateSpan -> String
    showend (DateSpan Maybe EFDay
    _ (Just EFDay
    efd)) = Day -> String
    forall a. Show a => a -> String
    show (Day -> String) -> Day -> String
    forall a b. (a -> b) -> a -> b
    $ EFDay -> Day
    fromEFDay EFDay
    efd
             showend DateSpan
    _ = String
    ""
             days :: Integer
    days = Integer -> Maybe Integer -> Integer
    forall a. a -> Maybe a -> a
    fromMaybe Integer
    0 (Maybe Integer -> Integer) -> Maybe Integer -> Integer
    forall a b. (a -> b) -> a -> b
    $ DateSpan -> Maybe Integer
    daysInSpan DateSpan
    spn
             txnrate :: Double
    txnrate | Integer
    daysInteger -> Integer -> Bool
    forall a. Eq a => a -> a -> Bool
    ==Integer
    0 = Double
    0
                     | Bool
    otherwise = Int -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    tnum1 Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Integer -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Integer
    days :: Double
             tnum30 :: Int
    tnum30 = [Transaction] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length ([Transaction] -> Int) -> [Transaction] -> Int
    forall a b. (a -> b) -> a -> b
    $ (Transaction -> Bool) -> [Transaction] -> [Transaction]
    forall a. (a -> Bool) -> [a] -> [a]
    filter Transaction -> Bool
    withinlast30 [Transaction]
    ts
             withinlast30 :: Transaction -> Bool
    withinlast30 Transaction
    t = Day
    d Day -> Day -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Integer -> Day -> Day
    addDays (-Integer
    30) Day
    today Bool -> Bool -> Bool
    && (Day
    dDay -> Day -> Bool
    forall a. Ord a => a -> a -> Bool
    <=Day
    today) where d :: Day
    d = Transaction -> Day
    tdate Transaction
    t
             txnrate30 :: Double
    txnrate30 = Int -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    tnum30 Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    30 :: Double
             tnum7 :: Int
    tnum7 = [Transaction] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length ([Transaction] -> Int) -> [Transaction] -> Int
    forall a b. (a -> b) -> a -> b
    $ (Transaction -> Bool) -> [Transaction] -> [Transaction]
    forall a. (a -> Bool) -> [a] -> [a]
    filter Transaction -> Bool
    withinlast7 [Transaction]
    ts
             withinlast7 :: Transaction -> Bool
    withinlast7 Transaction
    t = Day
    d Day -> Day -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Integer -> Day -> Day
    addDays (-Integer
    7) Day
    today Bool -> Bool -> Bool
    && (Day
    dDay -> Day -> Bool
    forall a. Ord a => a -> a -> Bool
    <=Day
    today) where d :: Day
    d = Transaction -> Day
    tdate Transaction
    t
             txnrate7 :: Double
    txnrate7 = Int -> Double
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    tnum7 Double -> Double -> Double
    forall a. Fractional a => a -> a -> a
    / Double
    7 :: Double
             acctnum :: Int
    acctnum = [Text] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [Text]
    as
             acctdepth :: Int
    acctdepth | [Text] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [Text]
    as = Int
    0
                       | Bool
    otherwise = [Int] -> Int
    forall a. Ord a => [a] -> a
    forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
    maximum ([Int] -> Int) -> [Int] -> Int
    forall a b. (a -> b) -> a -> b
    $ (Text -> Int) -> [Text] -> [Int]
    forall a b. (a -> b) -> [a] -> [b]
    map Text -> Int
    accountNameLevel [Text]
    as
             mktprices :: [PriceDirective]
    mktprices = Journal -> [PriceDirective]
    jpricedirectives Journal
    j
             mktpricecommodities :: [Text]
    mktpricecommodities = [Text] -> [Text]
    forall a. Ord a => [a] -> [a]
    nubSort ([Text] -> [Text]) -> [Text] -> [Text]
    forall a b. (a -> b) -> a -> b
    $ (PriceDirective -> Text) -> [PriceDirective] -> [Text]
    forall a b. (a -> b) -> [a] -> [b]
    map PriceDirective -> Text
    pdcommodity [PriceDirective]
    mktprices