Finish filtering of ratings from a stream of Files

This commit is contained in:
Gregor Kleen 2017-10-10 19:20:46 +02:00
parent 586d411162
commit 28a65913cf
3 changed files with 48 additions and 10 deletions

View File

@ -65,6 +65,7 @@ dependencies:
- cryptoids - cryptoids
- binary - binary
- mtl - mtl
- sandi
# The library contains all of our application code. The executable # The library contains all of our application code. The executable
# defined below is just a thin wrapper. # defined below is just a thin wrapper.

View File

@ -5,6 +5,7 @@
{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-} {-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DeriveGeneric, DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric, DeriveDataTypeable #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
@ -22,6 +23,8 @@ module Handler.Utils.Zip.Rating
import Import hiding ((</>)) import Import hiding ((</>))
import qualified Codec.Binary.Base32 as Base32
import Text.PrettyPrint.Leijen.Text hiding ((<$>)) import Text.PrettyPrint.Leijen.Text hiding ((<$>))
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
@ -30,9 +33,11 @@ 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 Data.Text.Encoding.Error (UnicodeException(..))
import qualified Data.Text.Lazy.Encoding as Lazy.Text import qualified Data.Text.Lazy.Encoding as Lazy.Text
import qualified Data.CaseInsensitive as CI
import qualified Data.ByteString.Lazy as Lazy (ByteString) import qualified Data.ByteString.Lazy as Lazy (ByteString)
import qualified Data.ByteString.Lazy as Lazy.ByteString import qualified Data.ByteString.Lazy as Lazy.ByteString
@ -41,9 +46,10 @@ import Text.Read (readEither)
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Data.Typeable (Typeable) import Data.Typeable (Typeable)
import Data.Binary (encode, decode)
import qualified Data.CryptoID.Poly as Poly import qualified Data.CryptoID.Poly as Poly
import System.FilePath
instance HasResolution prec => Pretty (Fixed prec) where instance HasResolution prec => Pretty (Fixed prec) where
pretty = pretty . show pretty = pretty . show
@ -52,7 +58,7 @@ instance HasResolution prec => Pretty (Fixed prec) where
data Rating = Rating data Rating = Rating
{ ratingCourseName :: Text { ratingCourseName :: Text
, ratingSheetName :: Text , ratingSheetName :: Text
, ratingSubmissionId :: CryptoIDSubmission ByteString -- ^ 'SubmissionId' , ratingSubmissionId :: CryptoIDSubmission String -- ^ 'SubmissionId'
, ratingComment :: Maybe Text , ratingComment :: Maybe Text
, ratingPoints :: Maybe Points , ratingPoints :: Maybe Points
} deriving (Read, Show, Eq, Generic, Typeable) } deriving (Read, Show, Eq, Generic, Typeable)
@ -76,8 +82,10 @@ getRating submissionId = runMaybeT $ do
Sheet{ sheetCourseId, sheetName = ratingSheetName } <- MaybeT $ get submissionSheetId Sheet{ sheetCourseId, sheetName = ratingSheetName } <- MaybeT $ get submissionSheetId
Course{ courseName = ratingCourseName } <- MaybeT $ get sheetCourseId Course{ courseName = ratingCourseName } <- MaybeT $ get sheetCourseId
cIDKey <- getsYesod appCryptoIDKey cIDKey <- getsYesod appCryptoIDKey
ratingSubmissionId <- Poly.encrypt cIDKey . Lazy.ByteString.toStrict $ encode submissionId ratingSubmissionId <- Poly.encrypt base32 cIDKey submissionId
return Rating{..} return Rating{..}
where
base32 = return . CI.foldCase . Text.unpack . Text.dropWhileEnd (== '=') . Text.decodeUtf8 . Base32.encode
formatRating :: Rating -> Lazy.ByteString formatRating :: Rating -> Lazy.ByteString
formatRating Rating{..} = let formatRating Rating{..} = let
@ -91,7 +99,7 @@ formatRating Rating{..} = let
[ "Veranstaltung:" <+> pretty ratingCourseName [ "Veranstaltung:" <+> pretty ratingCourseName
, "Blatt:" <+> pretty ratingSheetName , "Blatt:" <+> pretty ratingSheetName
] ]
, "Abgabe-Id:" <+> pretty (show ratingSubmissionId) -- FIXME , "Abgabe-Id:" <+> pretty (ciphertext ratingSubmissionId)
, "=============================================" , "============================================="
, "Bewertung:" <+> pretty ratingPoints , "Bewertung:" <+> pretty ratingPoints
, "=========== Beginn der Kommentare ===========" , "=========== Beginn der Kommentare ==========="
@ -126,11 +134,40 @@ parseRating input = do
return (rating, comment) return (rating, comment)
extractRatings :: MonadThrow m => (FilePath -> Maybe SubmissionId) -> Conduit File m (Either File (SubmissionId, Rating')) extractRatings :: ( MonadHandler m
extractRatings isRating = void . runMaybeT $ do , HandlerSite m ~ UniWorX
, MonadCatch m
) => Conduit File m (Either File (SubmissionId, Rating'))
extractRatings = void . runMaybeT $ do
f@(File{..}) <- MaybeT await f@(File{..}) <- MaybeT await
msId <- isRatingFile fileTitle
lift $ case () of lift $ case () of
_ | Just sId <- isRating fileTitle _ | Just sId <- msId
, Just content' <- fileContent , Just content' <- fileContent
-> yieldM $ Right . (sId, ) <$> parseRating content' -> yieldM $ Right . (sId, ) <$> parseRating content'
| otherwise -> yield $ Left f | otherwise -> yield $ Left f
isRatingFile :: ( MonadHandler m
, HandlerSite m ~ UniWorX
, MonadCatch m
) => FilePath -> m (Maybe SubmissionId)
isRatingFile fName
| Just cID <- isRatingFile' fName = do
cIDKey <- getsYesod appCryptoIDKey
(Just <$> Poly.decrypt unbase32 cIDKey cID) `catch` decryptErrors
| otherwise = return Nothing
where
unbase32 = either (const $ throwM CiphertextConversionFailed) return . Base32.decode . Text.encodeUtf8 . Text.pack . toUpper . (<> "===")
decryptErrors CiphertextConversionFailed = return Nothing
decryptErrors InvalidNamespaceDetected = return Nothing
decryptErrors (DeserializationError _) = return Nothing
decryptErrors err = throwM err
isRatingFile' :: FilePath -> Maybe (CryptoIDSubmission String)
isRatingFile' (takeFileName -> fName)
| (bName, ".txt") <- splitExtension fName
, Just ciphertext <- stripPrefix "bewertung_" bName
= Just CryptoID{..}
| otherwise
= Nothing

View File

@ -10,7 +10,7 @@ extra-deps:
- colonnade-1.1.1 - colonnade-1.1.1
- yesod-colonnade-1.1.0 - yesod-colonnade-1.1.0
- zip-stream-0.1.0.1 - zip-stream-0.1.0.1
- uuid-crypto-1.1.0 - uuid-crypto-1.1.1.0
- cryptoids-0.1.0 - cryptoids-0.2.0.0
- cryptoids-types-0.0.0 - cryptoids-types-0.0.0
resolver: lts-9.3 resolver: lts-9.3