The first of several conversions from String to (strict) Text, hopefully reducing space and time usage. This one shows a small improvement, with GHC 7.10.3 and text-1.2.2.1: hledger -f data/100x100x10.journal stats string: <<ghc: 39471064 bytes, 77 GCs, 198421/275048 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.001 elapsed), 0.015 MUT (0.020 elapsed), 0.010 GC (0.014 elapsed) :ghc>> text: <<ghc: 39268024 bytes, 77 GCs, 197018/270840 avg/max bytes residency (3 samples), 2M in use, 0.000 INIT (0.002 elapsed), 0.016 MUT (0.022 elapsed), 0.009 GC (0.011 elapsed) :ghc>> hledger -f data/1000x100x10.journal stats string: <<ghc: 318555920 bytes, 617 GCs, 2178997/7134472 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.001 elapsed), 0.129 MUT (0.136 elapsed), 0.067 GC (0.077 elapsed) :ghc>> text: <<ghc: 314248496 bytes, 612 GCs, 2074045/6617960 avg/max bytes residency (7 samples), 16M in use, 0.000 INIT (0.003 elapsed), 0.137 MUT (0.145 elapsed), 0.067 GC (0.079 elapsed) :ghc>> hledger -f data/10000x100x10.journal stats string: <<ghc: 3114763608 bytes, 6026 GCs, 18858950/75552024 avg/max bytes residency (11 samples), 201M in use, 0.000 INIT (0.000 elapsed), 1.331 MUT (1.372 elapsed), 0.699 GC (0.812 elapsed) :ghc>> text: <<ghc: 3071468920 bytes, 5968 GCs, 14120344/62951360 avg/max bytes residency (9 samples), 124M in use, 0.000 INIT (0.003 elapsed), 1.272 MUT (1.349 elapsed), 0.513 GC (0.578 elapsed) :ghc>> hledger -f data/100000x100x10.journal stats string: <<ghc: 31186579432 bytes, 60278 GCs, 135332581/740228992 avg/max bytes residency (13 samples), 1697M in use, 0.000 INIT (0.008 elapsed), 14.677 MUT (15.508 elapsed), 7.081 GC (8.074 elapsed) :ghc>> text: <<ghc: 30753427672 bytes, 59763 GCs, 117595958/666457240 avg/max bytes residency (14 samples), 1588M in use, 0.000 INIT (0.008 elapsed), 13.713 MUT (13.966 elapsed), 6.220 GC (7.108 elapsed) :ghc>>
		
			
				
	
	
		
			179 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
| {-# LANGUAGE OverloadedStrings, QuasiQuotes, RecordWildCards #-}
 | |
| -- | /register handlers.
 | |
| 
 | |
| module Handler.RegisterR where
 | |
| 
 | |
| import Import
 | |
| 
 | |
| import Data.List
 | |
| import Data.Maybe
 | |
| -- import Data.Text (Text)
 | |
| import qualified Data.Text as T
 | |
| import Safe
 | |
| 
 | |
| import Handler.AddForm
 | |
| import Handler.Common
 | |
| import Handler.Utils
 | |
| 
 | |
| import Hledger.Data
 | |
| import Hledger.Query
 | |
| import Hledger.Reports
 | |
| import Hledger.Utils
 | |
| import Hledger.Cli.CliOptions
 | |
| import Hledger.Web.WebOptions
 | |
| 
 | |
| -- | The main journal/account register view, with accounts sidebar.
 | |
| getRegisterR :: Handler Html
 | |
| getRegisterR = do
 | |
|   vd@VD{..} <- getViewData
 | |
|   -- staticRootUrl <- (staticRoot . settings) <$> getYesod
 | |
|   let -- injournal = isNothing inacct
 | |
|       filtering = m /= Any
 | |
|       -- title = "Transactions in "++a++s1++s2
 | |
|       title = T.unpack a++s1++s2
 | |
|                where
 | |
|                  (a,inclsubs) = fromMaybe ("all accounts",True) $ inAccount qopts
 | |
|                  s1 = if inclsubs then "" else " (excluding subaccounts)"
 | |
|                  s2 = if filtering then ", filtered" else ""
 | |
|       maincontent = registerReportHtml opts vd $ accountTransactionsReport (reportopts_ $ cliopts_ opts) j m $ fromMaybe Any $ inAccountQuery qopts
 | |
|   hledgerLayout vd "register" [hamlet|
 | |
|        <h2#contenttitle>#{title}
 | |
|        <!-- p>Transactions affecting this account, with running balance. -->
 | |
|        ^{maincontent}
 | |
|      |]
 | |
| 
 | |
| postRegisterR :: Handler Html
 | |
| postRegisterR = postAddForm
 | |
| 
 | |
| -- Generate html for an account register, including a balance chart and transaction list.
 | |
| registerReportHtml :: WebOpts -> ViewData -> TransactionsReport -> HtmlUrl AppRoute
 | |
| registerReportHtml opts vd r = [hamlet|
 | |
|  ^{registerChartHtml $ transactionsReportByCommodity r}
 | |
|  ^{registerItemsHtml opts vd r}
 | |
| |]
 | |
| 
 | |
| -- Generate html for a transaction list from an "TransactionsReport".
 | |
| registerItemsHtml :: WebOpts -> ViewData -> TransactionsReport -> HtmlUrl AppRoute
 | |
| registerItemsHtml _ vd (balancelabel,items) = [hamlet|
 | |
| <table.registerreport>
 | |
|  <tr.headings>
 | |
|   <th.date style="text-align:left;">
 | |
|    Date
 | |
|    <span .glyphicon .glyphicon-chevron-up>
 | |
|   <th.description style="text-align:left;">Description
 | |
|   <th.account style="text-align:left;">To/From Account(s)
 | |
|   <th.amount style="text-align:right; white-space:normal;">Amount Out/In
 | |
|   <th.balance style="text-align:right; white-space:normal;">#{balancelabel'}
 | |
|  $forall i <- numberTransactionsReportItems items
 | |
|   ^{itemAsHtml vd i}
 | |
|  |]
 | |
|  where
 | |
|    insomeacct = isJust $ inAccount $ qopts vd
 | |
|    balancelabel' = if insomeacct then balancelabel else "Total"
 | |
| 
 | |
|    -- filtering = m /= Any
 | |
|    itemAsHtml :: ViewData -> (Int, Bool, Bool, Bool, TransactionsReportItem) -> HtmlUrl AppRoute
 | |
|    itemAsHtml VD{..} (n, newd, newm, _, (torig, tacct, split, acct, amt, bal)) = [hamlet|
 | |
| 
 | |
| <tr ##{tindex torig} .item.#{evenodd}.#{firstposting}.#{datetransition} title="#{show torig}" style="vertical-align:top;">
 | |
|  <td.date>
 | |
|   <a href="@{JournalR}##{tindex torig}">#{date}
 | |
|  <td.description title="#{show torig}">#{elideRight 30 desc}
 | |
|  <td.account>#{elideRight 40 acct}
 | |
|  <td.amount style="text-align:right; white-space:nowrap;">
 | |
|   $if showamt
 | |
|    \#{mixedAmountAsHtml amt}
 | |
|  <td.balance style="text-align:right;">#{mixedAmountAsHtml bal}
 | |
| |]
 | |
| 
 | |
|      where
 | |
|        evenodd = if even n then "even" else "odd" :: String
 | |
|        datetransition | newm = "newmonth"
 | |
|                       | newd = "newday"
 | |
|                       | otherwise = "" :: String
 | |
|        (firstposting, date, desc) = (False, show $ tdate tacct, tdescription tacct)
 | |
|        -- acctquery = (here, [("q", pack $ accountQuery acct)])
 | |
|        showamt = not split || not (isZeroMixedAmount amt)
 | |
| 
 | |
| -- | Generate javascript/html for a register balance line chart based on
 | |
| -- the provided "TransactionsReportItem"s.
 | |
|                -- registerChartHtml :: forall t (t1 :: * -> *) t2 t3 t4 t5.
 | |
|                --                      Data.Foldable.Foldable t1 =>
 | |
|                --                      t1 (Transaction, t2, t3, t4, t5, MixedAmount)
 | |
|                --                      -> t -> Text.Blaze.Internal.HtmlM ()
 | |
| registerChartHtml :: [(CommoditySymbol, (String, [TransactionsReportItem]))] -> HtmlUrl AppRoute
 | |
| registerChartHtml percommoditytxnreports =
 | |
|  -- have to make sure plot is not called when our container (maincontent)
 | |
|  -- is hidden, eg with add form toggled
 | |
|  [hamlet|
 | |
| <label#register-chart-label style=""><br>
 | |
| <div#register-chart style="width:85%; height:150px; margin-bottom:1em; display:block;">
 | |
| <script type=text/javascript>
 | |
|  \$(document).ready(function() {
 | |
|    var $chartdiv = $('#register-chart');
 | |
|    if ($chartdiv.is(':visible')) {
 | |
|      \$('#register-chart-label').text('#{charttitle}');
 | |
|      var seriesData = [
 | |
|       $forall (c,(_,items)) <- percommoditytxnreports
 | |
|        /* we render each commodity using two series:
 | |
|         * one with extra data points added to show a stepped balance line */
 | |
|        {
 | |
|         data: [
 | |
|           $forall i <- reverse items
 | |
|            [
 | |
|             #{dayToJsTimestamp $ triDate i},
 | |
|             #{simpleMixedAmountQuantity $ triCommodityBalance c i}
 | |
|            ],
 | |
|           /* [] */
 | |
|         ],
 | |
|         label: '#{shownull c}',
 | |
|         color: #{colorForCommodity c},
 | |
|         lines: {
 | |
|           show: true,
 | |
|           steps: true,
 | |
|         },
 | |
|         points: {
 | |
|           show: false,
 | |
|         },
 | |
|         clickable: false,
 | |
|         hoverable: false,
 | |
|        },
 | |
|        /* and one with the original data, showing one clickable, hoverable point per transaction */
 | |
|        {
 | |
|         data: [
 | |
|           $forall i <- reverse items
 | |
|            [
 | |
|             #{dayToJsTimestamp $ triDate i},
 | |
|             #{simpleMixedAmountQuantity $ triCommodityBalance c i},
 | |
|             '#{showMixedAmountWithZeroCommodity $ triCommodityAmount c i}',
 | |
|             '#{showMixedAmountWithZeroCommodity $ triCommodityBalance c i}',
 | |
|             '#{concat $ intersperse "\\n" $ lines  $ show $ triOrigTransaction i}',
 | |
|             #{tindex $ triOrigTransaction i}
 | |
|            ],
 | |
|           /* [] */
 | |
|         ],
 | |
|         label: '',
 | |
|         color: #{colorForCommodity c},
 | |
|         lines: {
 | |
|           show: false,
 | |
|         },
 | |
|         points: {
 | |
|           show: true,
 | |
|         },
 | |
|        },
 | |
|      ]
 | |
|      var plot = registerChart($chartdiv, seriesData);
 | |
|      \$chartdiv.bind("plotclick", registerChartClick);
 | |
|    };
 | |
|  });
 | |
| |]
 | |
|            -- [#{dayToJsTimestamp $ ltrace "\ndate" $ triDate i}, #{ltrace "balancequantity" $ simpleMixedAmountQuantity $ triCommodityBalance c i}, '#{ltrace "balance" $ show $ triCommodityBalance c i}, '#{ltrace "amount" $ show $ triCommodityAmount c i}''],
 | |
|  where
 | |
|    charttitle = case maybe "" (fst.snd) $ headMay percommoditytxnreports
 | |
|            of "" -> ""
 | |
|               s  -> s++":"
 | |
|    colorForCommodity = fromMaybe 0 . flip lookup commoditiesIndex
 | |
|    commoditiesIndex = zip (map fst percommoditytxnreports) [0..] :: [(CommoditySymbol,Int)]
 | |
|    simpleMixedAmountQuantity = maybe 0 aquantity . headMay . amounts
 | |
|    shownull c = if null c then " " else c
 |