web: code cleanup, better web ui supporting full patterns & period expressions

New dependency: http.
This commit is contained in:
Simon Michael 2009-05-24 21:16:58 +00:00
parent 4517cab686
commit 71e7f2b293
2 changed files with 67 additions and 51 deletions

View File

@ -20,6 +20,7 @@ import Happstack.State.Control (waitForTermination)
import System.Cmd (system) import System.Cmd (system)
import System.Info (os) import System.Info (os)
import System.Exit import System.Exit
import Network.HTTP (urlEncode, urlDecode, urlEncodeVars)
import Ledger import Ledger
import Options import Options
@ -27,72 +28,87 @@ import BalanceCommand
import RegisterCommand import RegisterCommand
import PrintCommand import PrintCommand
import HistogramCommand import HistogramCommand
import Utils (filterAndCacheLedgerWithOpts)
tcpport = 5000 tcpport = 5000
web :: [Opt] -> [String] -> Ledger -> IO () web :: [Opt] -> [String] -> Ledger -> IO ()
web opts args l = web opts args l = do
t <- getCurrentLocalTime -- how to get this per request ?
if Debug `elem` opts if Debug `elem` opts
then do then do
putStrLn $ printf "starting web server on port %d in debug mode" tcpport -- just run the server in the foreground
simpleHTTP nullConf{port=tcpport} handlers putStrLn $ printf "starting web server on port %d in debug mode" tcpport
else do simpleHTTP nullConf{port=tcpport} $ webHandlers opts args l t
putStrLn $ printf "starting web server on port %d" tcpport else do
tid <- forkIO $ simpleHTTP nullConf{port=tcpport} handlers -- start the server (in background, so we can..) then start the web browser
putStrLn "starting web browser" putStrLn $ printf "starting web server on port %d" tcpport
openBrowserOn $ printf "http://localhost:%d/balance" tcpport tid <- forkIO $ simpleHTTP nullConf{port=tcpport} $ webHandlers opts args l t
waitForTermination putStrLn "starting web browser"
putStrLn "shutting down web server..." openBrowserOn $ printf "http://localhost:%d/" tcpport
killThread tid waitForTermination
putStrLn "shutdown complete" putStrLn "shutting down web server..."
killThread tid
putStrLn "shutdown complete"
where webHandlers :: [Opt] -> [String] -> Ledger -> LocalTime -> ServerPartT IO Response
handlers :: ServerPartT IO Response webHandlers opts args l t = msum
handlers = msum [
[methodSP GET $ withDataFn (look "a") $ \a -> templatise $ balancereport [a] methodSP GET $ view showBalanceReport
,methodSP GET $ templatise $ balancereport [] ,dir "balance" $ view showBalanceReport
,dir "print" $ withDataFn (look "a") $ \a -> templatise $ printreport [a] ,dir "register" $ view showRegisterReport
,dir "print" $ templatise $ printreport [] ,dir "print" $ view showLedgerTransactions
,dir "register" $ withDataFn (look "a") $ \a -> templatise $ registerreport [a] ,dir "histogram" $ view showHistogram
,dir "register" $ templatise $ registerreport [] ]
,dir "balance" $ withDataFn (look "a") $ \a -> templatise $ balancereport [a] where
,dir "balance" $ templatise $ balancereport [] view f = withDataFn rqdata $ render f
,dir "histogram" $ withDataFn (look "a") $ \a -> templatise $ histogramreport [a] render f (a,p) = layout (a, p) $ f opts' args' l'
,dir "histogram" $ templatise $ histogramreport [] where
] opts' = opts ++ [Period p]
printreport apats = showLedgerTransactions opts (apats ++ args) l args' = args ++ (map urlDecode $ words a)
registerreport apats = showRegisterReport opts (apats ++ args) l -- re-filter the full ledger
balancereport [] = showBalanceReport opts args l l' = filterAndCacheLedgerWithOpts opts' args' t (rawledgertext l) (rawledger l)
balancereport apats = showBalanceReport opts (apats ++ args) l'
where l' = cacheLedger apats (rawledger l) -- re-filter by account pattern each time
histogramreport [] = showHistogram opts args l
histogramreport apats = showHistogram opts (apats ++ args) l'
where l' = cacheLedger apats (rawledger l) -- re-filter by account pattern each time
templatise :: String -> ServerPartT IO Response rqdata = do
templatise s = do a <- look "a" `mplus` return "" -- filter patterns
p <- look "p" `mplus` return "" -- reporting period
return (a,p)
layout :: (String, String) -> String -> ServerPartT IO Response
layout (a,p) s = do
r <- askRq r <- askRq
return $ setHeader "Content-Type" "text/html" $ toResponse $ maintemplate r s return $ setHeader "Content-Type" "text/html" $ toResponse $ maintemplate (a,p) r s
maintemplate :: Request -> String -> String maintemplate :: (String, String) -> Request -> String -> String
maintemplate r = printf (unlines maintemplate (a,p) r = printf (unlines
["<div style=float:right>" ["<div style=\"float:right;text-align:right;\">"
,"<form action=%s>search:&nbsp;<input name=a value=%s></form>" ,"<form action=%s>"
,"&nbsp; filter by:&nbsp;<input name=a size=30 value=\"%s\">"
,"&nbsp; reporting period:&nbsp;<input name=p size=30 value=\"%s\">"
,resetlink
,"</form>"
,"</div>" ,"</div>"
,"<div align=center style=width:100%%>" ,"<div style=\"width:100%%; font-weight:bold;\">"
," <a href=balance>balance</a>" ," <a href=balance%s>balance</a>"
,"|" ,"|"
," <a href=register>register</a>" ," <a href=register%s>register</a>"
,"|" ,"|"
," <a href=print>print</a>" ," <a href=print%s>print</a>"
,"|" ,"|"
," <a href=histogram>histogram</a>" ," <a href=histogram%s>histogram</a>"
,"</div>" ,"</div>"
,"<pre>%s</pre>" ,"<pre>%s</pre>"
]) ]) u a p q q q q
(dropWhile (=='/') $ rqUri r) where
(fromMaybe "" $ queryValue "a" r) u = dropWhile (=='/') $ rqUri r
-- another way to get them
-- a = fromMaybe "" $ queryValue "a" r
-- p = fromMaybe "" $ queryValue "p" r
q' = intercalate "&" $ if null a then [] else [(("a="++).urlEncode) a] ++ if null p then [] else [(("p="++).urlEncode) p]
q = if null q' then "" else '?':q'
resetlink | null a && null p = ""
| otherwise = printf "&nbsp; <a href=%s>reset</a>" u
queryValues :: String -> Request -> [String] queryValues :: String -> Request -> [String]
queryValues q r = map (B.unpack . inputValue . snd) $ filter ((==q).fst) $ rqInputs r queryValues q r = map (B.unpack . inputValue . snd) $ filter ((==q).fst) $ rqInputs r

View File

@ -53,7 +53,7 @@ Executable hledger
Build-Depends: base, containers, haskell98, directory, parsec, Build-Depends: base, containers, haskell98, directory, parsec,
regex-compat, regexpr>=0.5.1, old-locale, time, regex-compat, regexpr>=0.5.1, old-locale, time,
HUnit, mtl, bytestring, filepath, process, testpack, HUnit, mtl, bytestring, filepath, process, testpack,
regex-pcre, csv, split, utf8-string regex-pcre, csv, split, utf8-string, http
Other-Modules: Other-Modules:
AddCommand AddCommand