gonito/Handler/Tables.hs

170 lines
9.3 KiB
Haskell
Raw Normal View History

2015-12-12 18:53:20 +01:00
{-# LANGUAGE ScopedTypeVariables #-}
module Handler.Tables where
import Import
2016-02-16 21:26:57 +01:00
import Handler.Shared
2017-02-25 22:53:17 +01:00
import Handler.SubmissionView
2017-03-18 15:57:27 +01:00
import Handler.TagUtils
2015-12-12 18:53:20 +01:00
import qualified Yesod.Table as Table
import Yesod.Table (Table)
import qualified Data.Map as Map
2016-02-16 19:00:26 +01:00
import Data.Text (pack)
2016-02-16 21:10:10 +01:00
import PersistSHA1
2018-01-25 16:34:05 +01:00
import qualified Data.List as DL
2015-12-12 18:53:20 +01:00
import GEval.Core
2016-02-17 09:34:34 +01:00
2015-12-12 18:53:20 +01:00
data LeaderboardEntry = LeaderboardEntry {
leaderboardUser :: User,
2016-02-16 21:10:10 +01:00
leaderboardUserId :: UserId,
2015-12-12 18:53:20 +01:00
leaderboardBestSubmission :: Submission,
2016-02-16 21:10:10 +01:00
leaderboardBestSubmissionId :: SubmissionId,
2015-12-12 18:53:20 +01:00
leaderboardEvaluation :: Evaluation,
2017-03-18 15:57:27 +01:00
leaderboardNumberOfSubmissions :: Int,
2017-05-27 22:31:53 +02:00
leaderboardTags :: [(Entity Tag, Entity SubmissionTag)]
2015-12-12 18:53:20 +01:00
}
submissionsTable :: Maybe UserId -> Text -> RepoScheme -> Repo -> [Entity Test] -> Table App (Entity Submission, Entity User, Map (Key Test) Evaluation, [(Entity Tag, Entity SubmissionTag)])
submissionsTable mauthId challengeName repoScheme challengeRepo tests = mempty
2017-02-25 22:53:17 +01:00
++ Table.text "submitter" (formatSubmitter . (\(_, Entity _ submitter, _, _) -> submitter))
++ timestampCell "when" (submissionStamp . (\(Entity _ s, _, _, _) -> s))
++ descriptionCell
2016-02-17 09:43:25 +01:00
++ mconcat (map (\(Entity k t) -> resultCell t (extractScore k)) tests)
++ statusCell challengeName repoScheme challengeRepo (\(Entity submissionId submission, Entity userId _, _, _) -> (submissionId, submission, userId, mauthId))
2017-02-25 22:53:17 +01:00
descriptionCell :: Foldable t => Table site (Entity Submission, b, c, t (Entity Tag, Entity SubmissionTag))
2017-02-25 22:53:17 +01:00
descriptionCell = Table.widget "description" (
2017-05-27 22:31:53 +02:00
\(Entity _ s, _, _ ,tagEnts) -> fragmentWithSubmissionTags (submissionDescription s) tagEnts)
2017-02-25 22:53:17 +01:00
2017-05-27 22:31:53 +02:00
extractScore :: Key Test -> (Entity Submission, Entity User, Map (Key Test) Evaluation, [(Entity Tag, Entity SubmissionTag)]) -> Maybe Evaluation
2017-02-25 22:53:17 +01:00
extractScore k (_, _, m, _) = lookup k m
2016-02-17 09:34:34 +01:00
leaderboardTable :: Maybe UserId -> Text -> RepoScheme -> Repo -> Test -> Table App (Int, LeaderboardEntry)
leaderboardTable mauthId challengeName repoScheme challengeRepo test = mempty
2016-02-17 09:43:25 +01:00
++ Table.int "#" fst
++ Table.text "submitter" (formatSubmitter . leaderboardUser . snd)
++ timestampCell "when" (submissionStamp . leaderboardBestSubmission . snd)
2017-03-18 15:57:27 +01:00
++ leaderboardDescriptionCell
2016-02-17 09:43:25 +01:00
++ resultCell test ((\e -> Just e) . leaderboardEvaluation . snd)
++ Table.int "×" (leaderboardNumberOfSubmissions . snd)
++ statusCell challengeName repoScheme challengeRepo (\(_, e) -> (leaderboardBestSubmissionId e,
2016-02-16 21:10:10 +01:00
leaderboardBestSubmission e,
leaderboardUserId e,
mauthId))
2015-12-12 18:53:20 +01:00
leaderboardDescriptionCell :: Table site (a, LeaderboardEntry)
2017-03-18 15:57:27 +01:00
leaderboardDescriptionCell = Table.widget "description" (
2017-05-27 22:31:53 +02:00
\(_,entry) -> fragmentWithSubmissionTags (submissionDescription $ leaderboardBestSubmission entry) (leaderboardTags entry))
2017-03-18 15:57:27 +01:00
2016-02-11 21:54:22 +01:00
2016-02-16 19:00:26 +01:00
hoverTextCell :: Text -> (a -> Text) -> (a -> Text) -> Table site a
hoverTextCell h mainTextFun hoverTextFun = Table.widget h (
\v -> [whamlet|<span title="#{hoverTextFun v}">#{mainTextFun v}|])
timestampCell :: Text -> (a -> UTCTime) -> Table site a
timestampCell h timestampFun = hoverTextCell h (Data.Text.pack . shorterFormat . timestampFun) (Data.Text.pack . show . timestampFun)
where shorterFormat = formatTime defaultTimeLocale "%Y-%m-%d %H:%M"
statusCell :: Text -> RepoScheme -> Repo -> (a -> (SubmissionId, Submission, UserId, Maybe UserId)) -> Table App a
statusCell challengeName repoScheme challengeRepo fun = Table.widget "" (statusCellWidget challengeName repoScheme challengeRepo . fun)
2016-02-16 21:10:10 +01:00
2016-02-17 09:34:34 +01:00
resultCell :: Test -> (a -> Maybe Evaluation) -> Table App a
resultCell test fun = hoverTextCell ((testName test) ++ "/" ++ (Data.Text.pack $ show $ testMetric test)) (formatTruncatedScore (testPrecision test) . fun) (formatFullScore . fun)
statusCellWidget :: Eq a => Text -> RepoScheme -> Repo -> (SubmissionId, Submission, a, Maybe a) -> WidgetFor App ()
statusCellWidget challengeName repoScheme challengeRepo (submissionId, submission, userId, mauthId) = $(widgetFile "submission-status")
2016-02-16 21:10:10 +01:00
where commitHash = fromSHA1ToText $ submissionCommit submission
isPublic = submissionIsPublic submission
isOwner = (mauthId == Just userId)
isVisible = isPublic || isOwner
2016-02-16 21:26:57 +01:00
publicSubmissionBranch = getPublicSubmissionBranch submissionId
maybeBrowsableUrl = if isPublic
then
Just $ browsableGitRepoBranch repoScheme challengeRepo challengeName publicSubmissionBranch
2016-02-16 21:26:57 +01:00
else
Nothing
2016-02-16 21:10:10 +01:00
2016-02-11 21:54:22 +01:00
getAuxSubmissions :: Key Test -> [(Entity Submission, Entity User, Map (Key Test) Evaluation)] -> [(Key User, (User, [(Submission, Evaluation)]))]
getAuxSubmissions testId evaluationMaps = map processEvaluationMap evaluationMaps
where processEvaluationMap ((Entity _ s), (Entity ui u), m) = (ui, (u, case Map.lookup testId m of
2016-02-11 21:54:22 +01:00
Just e -> [(s, e)]
Nothing -> []))
2016-02-12 23:21:26 +01:00
2017-05-27 22:31:53 +02:00
getAuxSubmissionEnts :: Key Test -> [(Entity Submission, Entity User, Map (Key Test) Evaluation, [(Entity Tag, Entity SubmissionTag)])] -> [(Key User, (User, [((Entity Submission), Evaluation)]))]
getAuxSubmissionEnts testId evaluationMaps = map processEvaluationMap evaluationMaps
where processEvaluationMap (s, (Entity ui u), m, _) = (ui, (u, case Map.lookup testId m of
2016-02-12 23:21:26 +01:00
Just e -> [(s, e)]
Nothing -> []))
2016-02-17 09:34:34 +01:00
getLeaderboardEntries :: Key Challenge -> Handler (Test, [LeaderboardEntry])
2015-12-12 18:53:20 +01:00
getLeaderboardEntries challengeId = do
(evaluationMaps, tests) <- getChallengeSubmissionInfos (\_ -> True) challengeId
2016-02-11 21:54:22 +01:00
let mainTestEnt = getMainTest tests
2015-12-12 18:53:20 +01:00
let (Entity mainTestId mainTest) = mainTestEnt
2016-02-16 21:10:10 +01:00
let auxSubmissions = getAuxSubmissionEnts mainTestId evaluationMaps
2015-12-12 18:53:20 +01:00
let submissionsByUser = Map.fromListWith (\(u1, l1) (_, l2) -> (u1, l1++l2)) auxSubmissions
let entryComparator a b = (compareResult mainTest) (evaluationScore $ leaderboardEvaluation a) (evaluationScore $ leaderboardEvaluation b)
entries' <- mapM (toEntry challengeId mainTest) $ filter (\(_, (_, s)) -> not (null s)) $ Map.toList submissionsByUser
2017-03-18 15:57:27 +01:00
let entries = sortBy (flip entryComparator) entries'
2016-02-17 09:34:34 +01:00
return (mainTest, entries)
2017-03-18 15:57:27 +01:00
toEntry :: (BaseBackend (YesodPersistBackend site) ~ SqlBackend, PersistQueryRead (YesodPersistBackend site), YesodPersist site, Foldable t) => Key Challenge -> Test -> (Key User, (User, t (Entity Submission, Evaluation))) -> HandlerFor site LeaderboardEntry
toEntry challengeId mainTest (ui, (u, ss)) = do
let bestOne = DL.maximumBy submissionComparator ss
2017-03-18 15:57:27 +01:00
let submissionId = entityKey $ fst bestOne
tagEnts <- runDB $ getTags submissionId
-- get all user submissions, including hidden ones
allUserSubmissions <- runDB $ selectList [SubmissionChallenge ==. challengeId, SubmissionSubmitter ==. ui] [Desc SubmissionStamp]
2017-03-18 15:57:27 +01:00
return $ LeaderboardEntry {
2015-12-12 18:53:20 +01:00
leaderboardUser = u,
2016-02-16 21:10:10 +01:00
leaderboardUserId = ui,
leaderboardBestSubmission = (\(Entity _ s) -> s) $ fst bestOne,
leaderboardBestSubmissionId = (\(Entity si _) -> si) $ fst bestOne,
2015-12-12 18:53:20 +01:00
leaderboardEvaluation = snd bestOne,
leaderboardNumberOfSubmissions = length allUserSubmissions,
2017-03-18 15:57:27 +01:00
leaderboardTags = tagEnts
2017-02-25 22:53:17 +01:00
}
where submissionComparator (_, e1) (_, e2) = (compareResult mainTest) (evaluationScore e1) (evaluationScore e2)
2017-02-25 22:53:17 +01:00
2015-12-12 18:53:20 +01:00
compareResult :: Test -> Maybe Double -> Maybe Double -> Ordering
compareResult test (Just x) (Just y) = (compareFun $ getMetricOrdering $ testMetric test) x y
2015-12-12 18:53:20 +01:00
compareResult _ (Just _) Nothing = GT
compareResult _ Nothing (Just _) = LT
compareResult _ Nothing Nothing = EQ
compareFun :: MetricOrdering -> Double -> Double -> Ordering
compareFun TheLowerTheBetter = flip compare
compareFun TheHigherTheBetter = compare
2017-05-27 22:31:53 +02:00
getChallengeSubmissionInfos :: ((Entity Submission) -> Bool) -> Key Challenge -> Handler ([(Entity Submission, Entity User, Map (Key Test) Evaluation, [(Entity Tag, Entity SubmissionTag)])], [Entity Test])
2015-12-12 18:53:20 +01:00
getChallengeSubmissionInfos condition challengeId = do
allSubmissions <- runDB $ selectList [SubmissionChallenge ==. challengeId, SubmissionIsHidden !=. Just True] [Desc SubmissionStamp]
2015-12-12 18:53:20 +01:00
let submissions = filter condition allSubmissions
tests <- runDB $ selectList [TestChallenge ==. challengeId, TestActive ==. True] []
evaluationMaps <- mapM getEvaluationMap submissions
return (evaluationMaps, tests)
2017-05-27 22:31:53 +02:00
getEvaluationMap :: Entity Submission -> Handler (Entity Submission, Entity User, Map (Key Test) Evaluation, [(Entity Tag, Entity SubmissionTag)])
2015-12-12 18:53:20 +01:00
getEvaluationMap s@(Entity submissionId submission) = do
outs <- runDB $ selectList [OutSubmission ==. submissionId] []
user <- runDB $ get404 $ submissionSubmitter submission
maybeEvaluations <- runDB $ mapM (\(Entity _ o) -> getBy $ UniqueEvaluationTestChecksum (outTest o) (outChecksum o)) outs
let evaluations = catMaybes maybeEvaluations
let m = Map.fromList $ map (\(Entity _ e) -> (evaluationTest e, e)) evaluations
2017-02-25 22:53:17 +01:00
tagEnts <- runDB $ getTags submissionId
return (s, Entity (submissionSubmitter submission) user, m, tagEnts)