mirror of
https://github.com/simonmichael/hledger.git
synced 2025-01-07 11:19:32 +03:00
123 lines
4.6 KiB
Haskell
123 lines
4.6 KiB
Haskell
-- | Web handler utilities.
|
|
|
|
module Handler.Utils where
|
|
|
|
import Prelude
|
|
import Control.Applicative ((<$>))
|
|
import Control.Monad.IO.Class (liftIO)
|
|
import Data.Maybe
|
|
import Data.Text(pack,unpack)
|
|
import Data.Time.Calendar
|
|
import Data.Time.Clock
|
|
import Data.Time.Format
|
|
import System.IO.Storage (putValue, getValue)
|
|
import System.Locale (defaultTimeLocale)
|
|
#if BLAZE_HTML_0_5
|
|
import Text.Blaze.Html (toHtml)
|
|
#else
|
|
import Text.Blaze (toHtml)
|
|
#endif
|
|
import Text.Hamlet
|
|
import Yesod.Core
|
|
|
|
import Foundation
|
|
|
|
import Hledger hiding (is)
|
|
import Hledger.Cli hiding (version)
|
|
import Hledger.Web.Options
|
|
|
|
|
|
-- | A bundle of data useful for hledger-web request handlers and templates.
|
|
data ViewData = VD {
|
|
opts :: WebOpts -- ^ the command-line options at startup
|
|
,here :: AppRoute -- ^ the current route
|
|
,msg :: Maybe Html -- ^ the current UI message if any, possibly from the current request
|
|
,today :: Day -- ^ today's date (for queries containing relative dates)
|
|
,j :: Journal -- ^ the up-to-date parsed unfiltered journal
|
|
,q :: String -- ^ the current q parameter, the main query expression
|
|
,m :: Query -- ^ a query parsed from the q parameter
|
|
,qopts :: [QueryOpt] -- ^ query options parsed from the q parameter
|
|
,am :: Query -- ^ a query parsed from the accounts sidebar query expr ("a" parameter)
|
|
,aopts :: [QueryOpt] -- ^ query options parsed from the accounts sidebar query expr
|
|
,showpostings :: Bool -- ^ current p parameter, 1 or 0 shows/hides all postings where applicable
|
|
}
|
|
|
|
-- | Make a default ViewData, using day 0 as today's date.
|
|
nullviewdata :: ViewData
|
|
nullviewdata = viewdataWithDateAndParams nulldate "" "" ""
|
|
|
|
-- | Make a ViewData using the given date and request parameters, and defaults elsewhere.
|
|
viewdataWithDateAndParams :: Day -> String -> String -> String -> ViewData
|
|
viewdataWithDateAndParams d q a p =
|
|
let (querymatcher,queryopts) = parseQuery d q
|
|
(acctsmatcher,acctsopts) = parseQuery d a
|
|
in VD {
|
|
opts = defwebopts
|
|
,j = nulljournal
|
|
,here = RootR
|
|
,msg = Nothing
|
|
,today = d
|
|
,q = q
|
|
,m = querymatcher
|
|
,qopts = queryopts
|
|
,am = acctsmatcher
|
|
,aopts = acctsopts
|
|
,showpostings = p == "1"
|
|
}
|
|
|
|
-- | Gather data used by handlers and templates in the current request.
|
|
getViewData :: Handler ViewData
|
|
getViewData = do
|
|
app <- getYesod
|
|
let opts@WebOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} = appOpts app
|
|
(j, err) <- getCurrentJournal $ copts{reportopts_=ropts{no_elide_=True}}
|
|
msg <- getMessageOr err
|
|
Just here <- getCurrentRoute
|
|
today <- liftIO getCurrentDay
|
|
q <- getParameterOrNull "q"
|
|
a <- getParameterOrNull "a"
|
|
p <- getParameterOrNull "p"
|
|
return (viewdataWithDateAndParams today q a p){
|
|
opts=opts
|
|
,msg=msg
|
|
,here=here
|
|
,today=today
|
|
,j=j
|
|
}
|
|
where
|
|
-- | Update our copy of the journal if the file changed. If there is an
|
|
-- error while reloading, keep the old one and return the error, and set a
|
|
-- ui message.
|
|
getCurrentJournal :: CliOpts -> Handler (Journal, Maybe String)
|
|
getCurrentJournal opts = do
|
|
j <- liftIO $ fromJust `fmap` getValue "hledger" "journal"
|
|
(jE, changed) <- liftIO $ journalReloadIfChanged opts j
|
|
if not changed
|
|
then return (j,Nothing)
|
|
else case jE of
|
|
Right j' -> do liftIO $ putValue "hledger" "journal" j'
|
|
return (j',Nothing)
|
|
Left e -> do setMessage $ "error while reading" {- ++ ": " ++ e-}
|
|
return (j, Just e)
|
|
|
|
-- | Get the named request parameter, or the empty string if not present.
|
|
getParameterOrNull :: String -> Handler String
|
|
getParameterOrNull p = unpack `fmap` fromMaybe "" <$> lookupGetParam (pack p)
|
|
|
|
-- | Get the message set by the last request, or the newer message provided, if any.
|
|
getMessageOr :: Maybe String -> Handler (Maybe Html)
|
|
getMessageOr mnewmsg = do
|
|
oldmsg <- getMessage
|
|
return $ maybe oldmsg (Just . toHtml) mnewmsg
|
|
|
|
numbered :: [a] -> [(Int,a)]
|
|
numbered = zip [1..]
|
|
|
|
dayToJsTimestamp :: Day -> Integer
|
|
dayToJsTimestamp d = read (formatTime defaultTimeLocale "%s" t) * 1000 -- XXX read
|
|
where t = UTCTime d (secondsToDiffTime 0)
|
|
|
|
chomp :: String -> String
|
|
chomp = reverse . dropWhile (`elem` "\r\n") . reverse
|
|
|