extractRatings

This commit is contained in:
Gregor Kleen 2017-10-09 23:19:51 +02:00
parent 710fec9b18
commit 0a40a8bf70
2 changed files with 37 additions and 13 deletions

View File

@ -4,6 +4,7 @@
{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE DeriveGeneric, DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric, DeriveDataTypeable #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
@ -13,7 +14,10 @@ module Handler.Utils.Zip.Rating
( Rating(..) ( Rating(..)
, getRating , getRating
, formatRating , formatRating
, RatingException(..)
, UnicodeException(..)
, parseRating , parseRating
, extractRatings
) where ) where
import Import hiding ((</>)) import Import hiding ((</>))
@ -25,6 +29,7 @@ import Control.Monad.Trans.Maybe
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as Text import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text import qualified Data.Text.Encoding as Text
import Data.Text.Encoding.Error (UnicodeException(..))
import qualified Data.Text.Lazy.Encoding as Lazy.Text import qualified Data.Text.Lazy.Encoding as Lazy.Text
@ -48,6 +53,18 @@ data Rating = Rating
, ratingPoints :: Maybe Points , ratingPoints :: Maybe Points
} deriving (Read, Show, Eq, Generic, Typeable) } deriving (Read, Show, Eq, Generic, Typeable)
type Rating' = ( Maybe Points
, Maybe Text -- ^ Rating comment
)
data RatingException = RatingNotUnicode UnicodeException -- ^ Rating failed to parse as unicode
| RatingMissingSeparator -- ^ Could not split rating header from comments
| RatingMultiple -- ^ Encountered multiple point values in rating
| RatingInvalid String -- ^ Failed to parse rating point value
deriving (Show, Eq, Generic, Typeable)
instance Exception RatingException
getRating :: SubmissionId -> YesodDB UniWorX (Maybe Rating) getRating :: SubmissionId -> YesodDB UniWorX (Maybe Rating)
getRating ratingSubmissionId = runMaybeT $ do getRating ratingSubmissionId = runMaybeT $ do
@ -76,32 +93,39 @@ formatRating Rating{..} = let
] ]
in Lazy.Text.encodeUtf8 $ displayT doc in Lazy.Text.encodeUtf8 $ displayT doc
parseRating :: MonadThrow m => ByteString -> m Rating'
parseRating :: ByteString
-> Either Text ( Maybe Points
, Maybe Text -- ^ Rating comment
)
parseRating input = do parseRating input = do
inputText <- first tshow $ Text.decodeUtf8' input inputText <- either (throw . RatingNotUnicode) return $ Text.decodeUtf8' input
let let
(headerLines, commentLines) = break (sep `Text.isInfixOf`) $ Text.lines inputText (headerLines, commentLines) = break (sep `Text.isInfixOf`) $ Text.lines inputText
ratingLines = filter (rating `Text.isInfixOf`) headerLines ratingLines = filter (rating `Text.isInfixOf`) headerLines
sep = "Beginn der Kommentare" sep = "Beginn der Kommentare"
rating = "Bewertung:" rating = "Bewertung:"
comment' <- case commentLines of comment' <- case commentLines of
(_:commentLines') -> Right . Text.strip $ Text.unlines commentLines' (_:commentLines') -> return . Text.strip $ Text.unlines commentLines'
_ -> Left $ "Missing separator “" <> sep <> "" _ -> throw RatingMissingSeparator
let let
comment comment
| Text.null comment' = Nothing | Text.null comment' = Nothing
| otherwise = Just comment' | otherwise = Just comment'
ratingLine' <- case ratingLines of ratingLine' <- case ratingLines of
[l] -> Right l [l] -> return l
_ -> Left $ "Multiple occurances of “" <> rating <> "" _ -> throw RatingMultiple
let let
(_, ratingLine) = Text.breakOnEnd rating ratingLine' (_, ratingLine) = Text.breakOnEnd rating ratingLine'
ratingStr = Text.unpack $ Text.strip ratingLine ratingStr = Text.unpack $ Text.strip ratingLine
rating <- case () of rating <- case () of
_ | null ratingStr -> return Nothing _ | null ratingStr -> return Nothing
| otherwise -> first tshow $ Just <$> readEither ratingStr | otherwise -> either (throw . RatingInvalid) return $ Just <$> readEither ratingStr
return (rating, comment) return (rating, comment)
extractRatings :: MonadThrow m => (FilePath -> Maybe SubmissionId) -> Conduit File m (Either File (SubmissionId, Rating'))
extractRatings isRating = void . runMaybeT $ do
f@(File{..}) <- MaybeT await
lift $ case () of
_ | Just sId <- isRating fileTitle
, Just content' <- fileContent
-> yieldM $ Right . (sId, ) <$> parseRating content'
| otherwise -> yield $ Left f

View File

@ -22,7 +22,7 @@ instance Arbitrary Rating where
ratingCourseName <- arbitrary ratingCourseName <- arbitrary
ratingSheetName <- arbitrary ratingSheetName <- arbitrary
ratingSubmissionId <- SubmissionKey . SqlBackendKey <$> arbitrary ratingSubmissionId <- SubmissionKey . SqlBackendKey <$> arbitrary
ratingComment <- fmap Text.strip <$> arbitrary `suchThat` maybe True (not . Text.null) ratingComment <- (fmap Text.strip <$> arbitrary) `suchThat` maybe True (not . Text.null)
ratingPoints <- arbitrary ratingPoints <- arbitrary
return Rating{..} return Rating{..}
@ -30,4 +30,4 @@ instance Arbitrary Rating where
spec :: Spec spec :: Spec
spec = describe "Rating files" $ do spec = describe "Rating files" $ do
it "have compatible formatting/parsing" . property $ it "have compatible formatting/parsing" . property $
\rating@(Rating{..}) -> parseRating (Lazy.ByteString.toStrict $ formatRating rating) `shouldBe` Right (ratingPoints, ratingComment) \rating@(Rating{..}) -> parseRating (Lazy.ByteString.toStrict $ formatRating rating) >>= (`shouldBe` (ratingPoints, ratingComment))