module ResultXHtml (statistics, writeOutput) where import Syntax import Sem import Mosg import Problem import Input_XHtml import PGF import Data.List import Data.Maybe import Data.Time import System.Directory import System.Locale import System.FilePath import Text.Printf import Text.XHtml.Strict statistics :: [ProblemResult] -> [(String, [Problem])] statistics rs = let goldYes = [ problem r | r <- rs, problemAnswer (problem r) == Problem.Yes ] goldUnknown = [ problem r | r <- rs, isUnknown (problemAnswer (problem r)) ] goldNo = [ problem r | r <- rs, problemAnswer (problem r) == Problem.No ] answered = [ problem r | r <- rs, isJust (getAnswer r)] failed = [ problem r | r <- rs, isNothing (getAnswer r)] correctYes = filterAnswers rs (== Mosg.Yes) (== Problem.Yes) correctUnknown = filterAnswers rs (== Mosg.DontKnow) isUnknown correctNo = filterAnswers rs (== Mosg.No) (== Problem.No) incorrectYes = filterAnswers rs (== Mosg.Yes) (/= Problem.Yes) incorrectUnknown = filterAnswers rs (== Mosg.DontKnow) (not . isUnknown) incorrectNo = filterAnswers rs (== Mosg.No) (/= Problem.No) report' = report (length rs) xs = [(problem r,res) | r <- rs, res <- premiseResults r ++ [questionResult r]] reportError = report (length xs) in [ report' "answered" answered, report' "failed" failed, report' "correct yes" correctYes, report' "correct no" correctNo, report' "correct unknown" correctUnknown, report' "incorrect yes" incorrectYes, report' "incorrect no" incorrectNo, report' "incorrect unknown" incorrectUnknown, ("",[]), reportError "parse errors" [ p | (p,r) <- xs, resOutput r == NoParse], reportError "interpretation errors" [ p | (p,r) <- xs, resOutput r == NoInterpretation], reportError "inconsistent" [ p | (p,r) <- xs, resOutput r == NoConsistent], ("",[]), proportion "precision (yes)" correctYes (correctYes ++ incorrectYes), proportion "precision (no)" correctNo (correctNo ++ incorrectNo), proportion "recall (yes)" correctYes goldYes, proportion "recall (no)" correctNo goldNo ] where filterAnswers rs f g = [ problem r | r <- rs, maybe False f (getAnswer r), g (problemAnswer (problem r))] report :: Int -> String -> [Problem] -> (String, [Problem]) report t s xs = (printf "%5.1f%% (%3d / %3d) %s" (percentage (length xs) t) (length xs) t s, xs) proportion :: String -> [Problem] -> [Problem] -> (String, [Problem]) proportion s xs ys = (printf "%5.1f%% (%3d / %3d) %s" (percentage (length xs) (length ys)) (length xs) (length ys) s, []) percentage :: Int -> Int -> Double percentage x t | t == 0 = 0 | otherwise = 100 * fromIntegral x / fromIntegral t mkOutputDir :: Mode -> IO FilePath mkOutputDir mode = do t <- getZonedTime let dir = formatTime defaultTimeLocale "output-%Y%m%d-%H%M%S" t ++ "-" ++ show mode createDirectory dir return dir writeOutput :: Mode -> [ProblemResult] -> IO () writeOutput mode rs = do dir <- mkOutputDir mode let htmlFile = dir "index.html" copyFile "style.css" (dir "style.css") copyFile "ui.js" (dir "ui.js") writeFile htmlFile $ renderHtml $ resultsPage mode rs printf "Output written to %s\n" htmlFile resultsPage :: Mode -> [ProblemResult] -> Html resultsPage mode rs = header << [thetitle << "MOSG results", cssLink "style.css", javascriptLink "ui.js"] +++ body << [h1 << "MOSG Output", p << ("Mode: " +++ show mode), h2 << "Overall statistics", statsHtml (statistics rs), h2 << "Problems", toHtml rs] statsHtml :: [(String,[Problem])] -> Html statsHtml ls = table << (colgroup << map (\c -> col ! [identifier c] << noHtml) ["stats_col_text","stats_col_problems"] +++ map statsLine ls) where statsLine (s,xs) = tr << [td << s, td << intersperse (toHtml " ") (probs xs)] probs xs = [anchor ! [href ("#" ++ pid)] << pid | pid <- nub $ map problemId xs] instance HTML ProblemResult where toHtml r = tbody << [tr ! [theclass "problem_result", identifier ("result_" ++ pid)] << [expandCell, problemIdCell, problemAnswerCell, answerCell], tr ! [classes ["problem_details", "expandable"], identifier ("details_" ++ pid)] << td ! [colspan 3] << details] where pid = problemId (problem r) expandCell = td << showHide ("details_"++pid) problemIdCell = td ! [theclass "problem_id"] << [anchor ! [name pid] << pid] problemAnswerCell = td ! [theclass ("problem_answer_"++s)] << s where s = case problemAnswer (problem r) of Problem.Yes -> "yes" Problem.No -> "no" Problem.Unknown -> "unknown" Problem.Undef -> "undefined" answerCell = td ! [classes ["answer_"++s,corr]] << s where s = case getAnswer r of Just Mosg.Yes -> "yes" Just Mosg.No -> "no" Just Mosg.DontKnow -> "unknown" Nothing -> "failed" corr = maybe "failed" (\c -> if c then "correct" else "incorrect") (isCorrect r) details = [ordList (zipWith result [1..] (premiseResults r ++ [questionResult r]))] result i x = [p << resInputText x, expandable (rid ++ "_trees") (show countTrees ++ " parse results") (either (unordList . (:[]) . toHtml) (nonEmptyDefList . zipWith inter [1..]) (resInterpretations x)), inters "unique" "different interpretations" (resDifferentInterpretations x), inters "consistent" "consistent statement interpretations" (resConsistent x), inters "informative" "consistent and informative statement interpretations" (resConsistentInformative x) ] where rid = "problem_" ++ pid ++ "_" ++ show i countTrees = either (const 1) length (resInterpretations x) countUnique = length (resDifferentInterpretations x) inters i s is = expandable (rid ++ "_" ++ i) (show (length is) ++ " " ++ s) (nonEmptyList is) inter _ (t,Left err) = (toHtml t, toHtml err) inter n (t,Right is) = (toHtml t, inters ("tree_"++show n) "Interpretations" is) toHtmlFromList rs = table ! [theclass "results"] << (colgroup << map (\c -> col ! [identifier c] << noHtml) ["results_col_details","results_col_id","results_col_correct","results_col_answer"] +++ thead << tr << map (th <<) ["Details","ID","Correct answer","Answer"] +++ map toHtml rs) instance HTML UnhandledTree where toHtml u = toHtml $ "Missing case in " ++ unhandledFunction u ++ ": " ++ showTree (unhandledSubtree u) instance HTML GText where toHtml t = toHtml $ show t expandable :: (HTML a, HTML b) => String -> a -> b -> Html expandable id x y = p << [toHtml x, if isNoHtml y' then noHtml else " " +++ showHide id] +++ y' ! [identifier id, theclass "expandable"] where y' = toHtml y showHide :: String -> Html showHide i = anchor ! [theclass "show_hide", href "#", strAttr "onclick" ("return toggle(this,'"++ i++"')")] << "(+)" -- XHtml utilities cssLink :: URL -> Html cssLink url = thelink ! [href url, rel "stylesheet", thetype "text/css"] << noHtml javascriptLink :: URL -> Html javascriptLink s = tag "script" ! [src s, thetype "text/javascript"] << noHtml classes :: [String] -> HtmlAttr classes = theclass . unwords nonEmptyList :: HTML a => [a] -> Html nonEmptyList = nonEmptyTag unordList nonEmptyDefList :: (HTML a, HTML b) => [(a,b)] -> Html nonEmptyDefList ds = if null ds' then noHtml else defList ds' where ds' = [(t',d') | (t,d) <- ds, let t' = toHtml t, let d' = toHtml d, not (isNoHtml t' && isNoHtml d')] nonEmptyTag :: HTML a => (a -> Html) -> a -> Html nonEmptyTag t x = if isNoHtml (toHtml x) then noHtml else t x