module Reviews ( ReviewAssignment(ReviewAssignment), Review(Review), Role(Student, Staff), assignNReviews, assignReviews, storeAssigments, assignedReviews, assignmentsBy, assignmentsFor, saveReview, reviews, reviewsBy, reviewsFor) where import System.Random import Assignments import Data.List import Control.Monad import System.Directory import qualified Data.Text as T -- | A user’s role or authorization level as a reviewer data Role = Student | Staff deriving (Eq, Ord, Show) -- | A review assignment representation data ReviewAssignment = ReviewAssignment{ reviewer :: UserIdentifier , reviewee :: UserIdentifier , role :: Role , assignment :: Assignment } deriving (Show) -- | A finished review data Review = Review{ reviewAssignment :: ReviewAssignment , score :: Double , text :: T.Text } deriving Show assignmentsFile = "Assignments.txt" reviewsFile = "Reviews.txt" -- | Takes an Assignment, a list of reviewer identifiers and a -- | list of reviewee identifiers and assigns N reviewees for each -- | reviewer. It makes sure that a user never reviews themselves. -- | The reviewer is assigned the reviews with the provided role. assignNReviews :: Assignment -> [UserIdentifier] -> [UserIdentifier] -> Int -> Role -> IO [ReviewAssignment] assignNReviews asg reviewers reviewees n role = do let reviewees' = take (n * (length reviewers)) (concat $ repeat reviewees) let pairs = getP reviewers reviewees' n let rez = map (\(x,y) -> (ReviewAssignment x y role asg)) pairs return rez shuffle :: Eq a => [a] -> [a] -> IO ([a]) shuffle [] rez = do return rez shuffle xs rez = do el <- randomRIO (0, (length xs)-1) let el' = xs !! el let xs' = delete el' xs shuffle xs' (el':rez) getP :: Eq a => [a] -> [a] -> Int -> [(a,a)] getP x0 y0 n0 = getPairs x0 y0 n0 where getPairs [] _ _ = [] getPairs (x:xs) ys 0 = getPairs xs ys n0 getPairs xs@(x:_) (y:ys) n | x /= y = (x,y):(getPairs xs ys (n-1)) | otherwise = getPairs xs (ys ++ [y]) n assignReviews :: Assignment -> [UserIdentifier] -> [UserIdentifier] -> Role -> IO [ReviewAssignment] assignReviews asg reviewers reviewees role = do reviewees' <- shuffle reviewees [] let n = div (length reviewees) (length reviewers) let reviewers' = concat $ replicate (n+1) reviewers let rez = map (\(x,y) -> (ReviewAssignment x y role asg)) $ zip reviewers' reviewees' return rez -- | Stores a list of review assignments into a database or -- | file system. storeAssigments :: [ReviewAssignment] -> IO () storeAssigments xs = do let xs' = concatMap ((++"\n") . show) xs writeFile assignmentsFile xs' return () -- | Retrieves all ReviewAssignments for an Assignment from -- | a database or file system. assignedReviews :: Assignment -> IO [ReviewAssignment] assignedReviews asg = do fileExist <- doesFileExist assignmentsFile if not fileExist then return [] else do input <- readFile reviewsFile return $ filter ((==asg) . assignment) $map (getReviewAsgFromStr) $ lines input -- Parse ReviewAssignment from string getReviewAsgFromStr str = ReviewAssignment (read $ w !! 3) (read $ w !! 6) (getRole $ w !! 9) (Assignment (read (w !! 15) :: Integer) (getaType (w !! 18)) (read (w !! 21) :: Int)) where w = words $ filter (\x -> x/=',' && x /= '}') str -- Returns Role from given String getRole :: String -> Role getRole "Student" = Student getRole "Staff" = Staff getRole _ = error "Can't parse role" -- Returns assignment type from given String getaType :: String -> Type getaType "homework" = Homework getaType "exam" = Exam getaType "project" = Project getaType _ = error "Can't parse assignment type" -- | Retrieves all ReviewAssignments for an Assignment and -- | a UserIdentifier, i.e. all the reviews for that assigment -- | the user has to perform. assignmentsBy :: Assignment -> UserIdentifier -> IO [ReviewAssignment] assignmentsBy asg userId = do filteredAsgn <- assignedReviews asg let rez = filter ((==userId) . reviewer) filteredAsgn return rez -- | Retrieves all ReviewAssignments for an Assignment and -- | a UserIdentifier, i.e. all the reviews for that assignment -- | where the user is a reviewee. assignmentsFor :: Assignment -> UserIdentifier -> IO [ReviewAssignment] assignmentsFor asg userId = do filteredAsgn <- assignedReviews asg let rez = filter ((==userId) . reviewee) filteredAsgn return rez getReviewFromStr :: String -> IO Review getReviewFromStr str = do let con = getReAsg str " " let rez = Review{ reviewAssignment = getReviewAsgFromStr $ drop 27 $ fst con, score = read (init ((words $ snd con) !! 3)) :: Double, text = T.pack $ init $ init $ getText $ snd con } return rez where getReAsg [] acc = (reverse acc, []) getReAsg (a:b:xs) acc | a == '}' && b == '}' = (reverse acc,xs) | otherwise = getReAsg (b:xs) (a:acc) getText [] = [] getText (x:xs) | x == '"' = xs | otherwise = getText xs -- | Completes a review assignment and stores the result in a -- | file system or database. saveReview :: Review -> IO () saveReview r = do fileExist <- doesFileExist reviewsFile if not fileExist then writeFile reviewsFile $ show r ++ "\n" else do input <- readFile reviewsFile writeFile reviewsFile $ input ++ show r ++ "\n" -- | Loads all the completed review results for an assignment reviews :: Assignment -> IO [Review] reviews asg = do fileExist <- doesFileExist reviewsFile if not fileExist then return [] else do input <- readFile reviewsFile revs <- mapM getReviewFromStr $ lines input return $ filter ((==asg) . assignment . reviewAssignment) revs -- | Loads all the completed review results for an assignment -- | that were performed by a user. reviewsBy :: Assignment -> UserIdentifier -> IO [Review] reviewsBy asg user = do revs <- reviews asg return $ filter ((==user) . reviewer . reviewAssignment) revs -- | Loads all the completed review results for an assignment -- | that were performed by a user. reviewsFor :: Assignment -> UserIdentifier -> IO [Review] reviewsFor asg user = do revs <- reviews asg return $ filter ((==user) . reviewee . reviewAssignment) revs asgn = Assignment 2016 Homework 1 rev = ReviewAssignment { reviewer = "Luka124", reviewee = "3984344", role = Student, assignment = asgn } re = Review{ reviewAssignment = rev, score = 9.5, text = T.pack "Good job" }