packages feed

ideas-0.6: src/Service/FeedbackText.hs

-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Service.FeedbackText 
   ( ExerciseText(..)
   , onefirsttext, submittext, derivationtext, submitHelper
   ) where

import Control.Arrow
import Common.Context
import Common.Exercise
import Common.Transformation
import Common.Utils
import Data.Maybe
import Service.Diagnose (restartIfNeeded)
import Service.Submit
import Service.TypedAbstractService

------------------------------------------------------------
-- Exercise Text data type

-- Exercise extension for textual feedback
data ExerciseText a = ExerciseText
   { ruleText              :: Rule (Context a) -> Maybe String
   , appliedRule           :: Rule (Context a) -> String
   , feedbackSyntaxError   :: String -> String
   , feedbackSame          :: String
   , feedbackBuggy         :: Bool -> [Rule (Context a)] -> String
   , feedbackNotEquivalent :: Bool -> String
   , feedbackOk            :: [Rule (Context a)] -> (String, Bool)
   , feedbackDetour        :: Bool -> Maybe (Rule (Context a)) -> [Rule (Context a)] -> (String, Bool)
   , feedbackUnknown       :: Bool -> String
   }

------------------------------------------------------------
-- Services

derivationtext :: Monad m => ExerciseText a -> State a -> Maybe String -> m [(String, Context a)]
derivationtext exText st _event = do
   xs <- derivation Nothing st
   return (map (first (showRule exText)) xs)

onefirsttext :: ExerciseText a -> State a -> Maybe String -> (Bool, String, State a)
onefirsttext exText state event =
   case allfirsts state of
      Just ((r, _, s):_) ->
         let msg = case fromContext (context s) >>= useToRewrite exText r state of
                      Just txt | event /= Just "hint button" -> txt
                      _ -> "Use " ++ showRule exText r
         in (True, msg, s)
      _ -> (False, "Sorry, no hint available", state)
      
submittext :: ExerciseText a -> State a -> String -> Maybe String -> (Bool, String, State a)
submittext exText state txt _event = 
   case parser (exercise state) txt of
      Left err -> 
         (False, feedbackSyntaxError exText err, state)
      Right a  -> 
         let result = submit state a
             (txt, b) = submitHelper exText state a result
         in case getResultState result of
               Just new | b -> (True, txt, restartIfNeeded new)
               _ -> (False, txt, state)

-- Feedback messages for submit service (free student input). The boolean
-- indicates whether the student is allowed to continue (True), or forced 
-- to go back to the previous state (False)
submitHelper :: ExerciseText a -> State a -> a -> Result a -> (String, Bool)
submitHelper exText old a result =
   case result of
      Buggy rs        -> ( fromMaybe ""  (youRewroteInto old a) ++ 
                           feedbackBuggy exText (ready old) rs
                         , False)
      NotEquivalent   -> ( fromMaybe ""  (youRewroteInto old a) ++
                           feedbackNotEquivalent exText (ready old)
                         , False)
      Ok rs _
         | null rs    -> (feedbackSame exText, False)
         | otherwise  -> feedbackOk exText rs
      Detour rs _     -> feedbackDetour exText (ready old) (expected old) rs
      Unknown _       -> ( fromMaybe ""  (youRewroteInto old a) ++ 
                           feedbackUnknown exText (ready old)
                         , False)
 where
   expected s = do
      xs <- allfirsts s
      fmap fst3 (safeHead xs)

------------------------------------------------------------
-- Helper functions

showRule :: ExerciseText a -> Rule (Context a) -> String
showRule exText r = 
   case ruleText exText r of
      Just s  -> s
      Nothing -> "rule " ++ name r

useToRewrite :: ExerciseText a -> Rule (Context a) -> State a -> a -> Maybe String
useToRewrite exText rule old = rewriteIntoText True txt old
 where
   txt = "Use " ++ showRule exText rule
         ++ " to rewrite "

youRewroteInto :: State a -> a -> Maybe String
youRewroteInto = rewriteIntoText False "You rewrote "

rewriteIntoText :: Bool -> String -> State a -> a -> Maybe String
rewriteIntoText mode txt old a = do
   let ex = exercise old
   p <- fromContext (context old)
   (p1, a1) <- difference ex mode p a 
   return $ txt ++ prettyPrinter ex p1 
         ++ " into " ++ prettyPrinter ex a1 ++ ". "