gonito/Handler/Query.hs

837 lines
37 KiB
Haskell
Raw Normal View History

2020-09-05 23:26:53 +02:00
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DoAndIfThenElse #-}
2020-09-05 23:26:53 +02:00
2016-02-12 13:00:33 +01:00
module Handler.Query where
2021-02-15 12:51:24 +01:00
import Import hiding (fromList, Proxy)
2016-02-12 13:00:33 +01:00
2017-02-25 19:13:55 +01:00
import Handler.SubmissionView
2018-01-25 16:34:05 +01:00
import Handler.Shared
2018-11-10 11:20:17 +01:00
import Handler.TagUtils
2018-11-12 14:12:51 +01:00
import PersistSHA1
2020-09-05 23:26:53 +02:00
import Data.Diff
2018-11-12 14:12:51 +01:00
import Handler.Tables
import Text.Blaze
2018-11-12 14:12:51 +01:00
import qualified Yesod.Table as Table
2016-02-12 13:00:33 +01:00
import Database.Persist.Sql
2018-01-25 16:34:05 +01:00
import qualified Database.Esqueleto as E
import Database.Esqueleto ((^.))
2016-02-12 13:00:33 +01:00
2020-09-05 23:26:53 +02:00
import Data.Maybe (fromJust)
2018-11-12 14:12:51 +01:00
import qualified Data.Text as T
2020-01-04 22:34:03 +01:00
import Data.List (nub, (!!))
import Data.List.Extra (groupOn)
2020-01-04 22:34:03 +01:00
import qualified Data.Map.Lazy as LM
2018-11-12 14:12:51 +01:00
2018-01-25 16:34:05 +01:00
import Yesod.Form.Bootstrap3 (BootstrapFormLayout (..), renderBootstrap3)
2019-12-14 18:21:47 +01:00
import Data.Conduit.SmartSource (lookForCompressedFiles)
import GEval.Core (GEvalSpecification(..), GEvalOptions(..), ResultOrdering(..))
2020-09-05 23:26:53 +02:00
import GEval.LineByLine (runLineByLineGeneralized, runDiffGeneralized, LineRecord(..))
import GEval.Common (FormattingOptions(..), MetricValue)
import GEval.OptionsParser (readOptsFromConfigFile)
2019-12-14 18:21:47 +01:00
import qualified Data.Conduit.List as CL
import System.FilePath (takeFileName)
2020-09-05 23:26:53 +02:00
import System.Directory (makeAbsolute)
2019-12-14 18:21:47 +01:00
2020-01-04 22:34:03 +01:00
import Data.SplitIntoCrossTabs
2021-02-15 12:51:24 +01:00
import Data.Swagger hiding (get)
import qualified Data.Swagger as DS
import Data.Swagger.Declare
import Control.Lens hiding ((.=), (^.), (<.>))
import Data.Proxy as DPR
import Data.HashMap.Strict.InsOrd (fromList)
import qualified System.Directory as D
2021-02-15 12:51:24 +01:00
import Handler.ShowChallenge
data VariantView = VariantView {
variantViewId :: Int64,
variantViewName :: Text,
2021-02-15 21:39:06 +01:00
variantViewRank :: Int,
2021-02-15 12:51:24 +01:00
variantViewEvaluations :: [EvaluationView],
variantViewParams :: [Parameter]
}
instance ToJSON Parameter where
toJSON entry = object
[ "name" .= parameterName entry,
"value" .= parameterValue entry
]
instance ToSchema Parameter where
declareNamedSchema _ = do
stringSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy String)
2021-02-15 13:48:58 +01:00
return $ NamedSchema (Just "Parameter") $ mempty
2021-02-15 12:51:24 +01:00
& type_ .~ SwaggerObject
& properties .~
fromList [ ("name", stringSchema),
("value", stringSchema)
]
& required .~ [ "name", "value" ]
instance ToJSON VariantView where
toJSON entry = object
[ "id" .= variantViewId entry,
"name" .= variantViewName entry,
2021-02-15 21:39:06 +01:00
"rank" .= variantViewRank entry,
2021-02-15 12:51:24 +01:00
"evaluations" .= variantViewEvaluations entry,
"params" .= variantViewParams entry
]
instance ToSchema VariantView where
declareNamedSchema _ = do
intSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy [Int64])
stringSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy [String])
evaluationsSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy [EvaluationView])
paramsSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy [Parameter])
2021-02-15 13:48:58 +01:00
return $ NamedSchema (Just "Variant") $ mempty
2021-02-15 12:51:24 +01:00
& type_ .~ SwaggerObject
& properties .~
fromList [ ("id", intSchema),
("name", stringSchema),
2021-02-15 21:39:06 +01:00
("rank", intSchema),
2021-02-15 12:51:24 +01:00
("evaluations", evaluationsSchema),
("params", paramsSchema)
]
& required .~ [ "evaluations" ]
data QueryResultView = QueryResultView {
queryResultViewSubmissionInfo :: FullSubmissionInfo,
queryResultViewVariants :: [VariantView],
queryResultSharedParams :: [Parameter]
2021-02-15 12:51:24 +01:00
}
instance ToJSON QueryResultView where
toJSON entry = object
[ "submissionInfo" .= queryResultViewSubmissionInfo entry,
"variants" .= queryResultViewVariants entry,
"sharedParams" .= queryResultSharedParams entry
2021-02-15 12:51:24 +01:00
]
instance ToSchema QueryResultView where
declareNamedSchema _ = do
submissionInfoSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy FullSubmissionInfo)
2021-02-15 13:48:58 +01:00
return $ NamedSchema (Just "QueryResult") $ mempty
2021-02-15 12:51:24 +01:00
& type_ .~ SwaggerObject
& properties .~
fromList [ ("submissionInfo", submissionInfoSchema),
("variants", Inline $ toSchema (DPR.Proxy :: DPR.Proxy [VariantView])
& description .~ Just "A list of outputs (variants) associated with the given submission, usually one, but could be more"),
("sharedParams", Inline $ toSchema (DPR.Proxy :: DPR.Proxy [Parameter])
& description .~ Just "Parameters shared by all variants; if there is only one variant, all parameters will be given here")
2021-02-15 12:51:24 +01:00
]
& required .~ [ "submissionInfo", "variants" ]
2018-01-25 16:34:05 +01:00
rawCommitQuery :: (MonadIO m, RawSql a) => Text -> ReaderT SqlBackend m [a]
2018-11-17 13:49:44 +01:00
rawCommitQuery sha1Prefix =
rawSql "SELECT ?? FROM submission WHERE cast(commit as text) like ?" [PersistText $ "\\\\x" ++ sha1Prefix ++ "%"]
2016-02-12 13:00:33 +01:00
rawOutQuery :: (MonadIO m, RawSql a) => Text -> ReaderT SqlBackend m [a]
rawOutQuery sha1Prefix =
rawSql "SELECT ?? FROM out WHERE cast(checksum as text) like ?" [PersistText $ "\\\\x" ++ sha1Prefix ++ "%"]
groupBySecond :: Eq b => [(FullSubmissionInfo, b)] -> [(FullSubmissionInfo, [b])]
groupBySecond lst = map putOut $ groupOn (fsiSubmissionId . fst) lst
where putOut ((ha, hb):t) = (ha, hb:nub (map snd t))
putOut [] = error "should not be here"
findSubmissions :: Text -> Handler [(FullSubmissionInfo, [SHA1])]
2016-02-12 13:00:33 +01:00
findSubmissions sha1Prefix = do
allSubmissions <- runDB $ rawCommitQuery sha1Prefix
justSubmissions' <- mapM getFullInfo allSubmissions
let justSubmissions = map (\s -> (s, [])) justSubmissions'
outs <- runDB $ rawOutQuery sha1Prefix
submissionsByOuts <- mapM fetchSubmissionByOut outs
return (justSubmissions ++ groupBySecond submissionsByOuts)
fetchSubmissionByOut :: Entity Out -> HandlerFor App (FullSubmissionInfo, SHA1)
fetchSubmissionByOut (Entity _ out) = do
variant <- runDB $ get404 $ outVariant out
let theSubmissionId = variantSubmission variant
theSubmission <- runDB $ get404 theSubmissionId
let theSubmissionEnt = Entity theSubmissionId theSubmission
fsi <- getFullInfo theSubmissionEnt
return (fsi, outChecksum out)
2016-02-12 13:00:33 +01:00
2019-11-30 20:47:19 +01:00
getApiTxtScoreR :: Text -> Handler Text
getApiTxtScoreR query =
if T.null postT
then getApiTxtScore Nothing preT
else getApiTxtScore (Just $ T.tail postT) preT
where (preT, postT) = T.breakOn "-" query
getApiTxtScore :: Maybe Text -> Text -> Handler Text
getApiTxtScore mMetricName sha1Prefix = do
submissions <- findSubmissions sha1Prefix
2018-01-25 16:34:05 +01:00
case submissions of
[] -> return noneMessage
((fsi, _):_) -> case submissions of
[(_, [])] -> doGetScore mMetricName (Entity (fsiSubmissionId fsi)
(fsiSubmission fsi))
_ -> do
let hashes = nub $ concat $ map snd submissions
case hashes of
[h] -> doGetScoreForOut mMetricName
(Entity (fsiSubmissionId fsi)
(fsiSubmission fsi))
h
[] -> return noneMessage
_ -> return ambiguousArgumentMessage
where ambiguousArgumentMessage = "AMBIGUOUS ARGUMENT"
noneMessage = "NONE"
2018-01-25 16:34:05 +01:00
doGetScore :: (BaseBackend (YesodPersistBackend site) ~ SqlBackend, PersistUniqueRead (YesodPersistBackend site), BackendCompatible SqlBackend (YesodPersistBackend site), YesodPersist site, PersistQueryRead (YesodPersistBackend site)) => Maybe Text -> Entity Submission -> HandlerFor site Text
doGetScore mMetricName submission = do
2018-01-25 16:34:05 +01:00
let challengeId = submissionChallenge $ entityVal submission
mTestEnt <- runDB $ fetchTestByName mMetricName challengeId
case mTestEnt of
Just testEnt -> do
let theTestId = entityKey testEnt
let submissionId = entityKey submission
evals <- runDB $ E.select
2021-11-13 12:16:07 +01:00
$ E.from $ \(out, evaluation, variant, ver) -> do
E.where_ (variant ^. VariantSubmission E.==. E.val submissionId
E.&&. out ^. OutVariant E.==. variant ^. VariantId
E.&&. out ^. OutTest E.==. E.val theTestId
E.&&. evaluation ^. EvaluationTest E.==. E.val theTestId
2021-11-13 12:16:07 +01:00
E.&&. out ^. OutChecksum E.==. evaluation ^. EvaluationChecksum
E.&&. evaluation ^. EvaluationVersion E.==. ver ^. VersionCommit)
E.orderBy [E.desc (ver ^. VersionStamp)]
return (evaluation, ver)
2021-11-13 12:16:07 +01:00
case onlyWithHeadVersions evals of
2020-09-05 16:45:09 +02:00
[eval] -> return $ formatTruncatedScore (getTestFormattingOpts $ entityVal testEnt) (Just $ entityVal eval)
_ -> return "NONE"
Nothing -> return "NONE"
2021-11-13 12:16:07 +01:00
where onlyWithHeadVersions [] = []
onlyWithHeadVersions ((he,hv):t) = he:(onlyWithHeadVersions' hv t)
onlyWithHeadVersions' _ [] = []
onlyWithHeadVersions' v ((he,hv):t)
| entityKey hv == entityKey v = (he:(onlyWithHeadVersions' v t))
| otherwise = []
2018-01-25 16:34:05 +01:00
doGetScoreForOut :: (BaseBackend (YesodPersistBackend site) ~ SqlBackend, PersistUniqueRead (YesodPersistBackend site), BackendCompatible SqlBackend (YesodPersistBackend site), YesodPersist site, PersistQueryRead (YesodPersistBackend site)) => Maybe Text -> Entity Submission -> SHA1 -> HandlerFor site Text
doGetScoreForOut mMetricName submission sha1code = do
let submissionId = entityKey submission
evals <- runDB $ E.select
$ E.from $ \(out, evaluation, variant, test, theVersion) -> do
E.where_ (variant ^. VariantSubmission E.==. E.val submissionId
E.&&. out ^. OutVariant E.==. variant ^. VariantId
E.&&. out ^. OutTest E.==. test ^. TestId
E.&&. evaluation ^. EvaluationTest E.==. test ^. TestId
E.&&. out ^. OutChecksum E.==. evaluation ^. EvaluationChecksum
2019-12-14 14:10:50 +01:00
E.&&. out ^. OutChecksum E.==. E.val sha1code
E.&&. (evaluation ^. EvaluationVersion E.==. theVersion ^. VersionCommit))
2019-12-14 14:10:50 +01:00
E.orderBy [E.asc (test ^. TestPriority),
E.desc (theVersion ^. VersionMajor),
E.desc (theVersion ^. VersionMinor),
E.desc (theVersion ^. VersionPatch)]
return (evaluation, test)
let evalSelected = case evals of
[] -> Nothing
((eval, test):_) -> case mMetricName of
Nothing -> Just (eval, test)
Just mn -> find (\(_, t) -> formatTestEvaluationScheme (entityVal t) == mn) evals
case evalSelected of
Nothing -> return "None"
2020-09-05 16:45:09 +02:00
Just (eval, testEnt) -> return $ formatTruncatedScore (getTestFormattingOpts $ entityVal testEnt)
(Just $ entityVal eval)
2016-02-12 13:00:33 +01:00
getQueryFormR :: Handler Html
getQueryFormR = do
(formWidget, formEnctype) <- generateFormPost queryForm
defaultLayout $ do
setTitle "Searching for submissions"
$(widgetFile "query-form")
postQueryFormR :: Handler Html
postQueryFormR = do
((result, formWidget), formEnctype) <- runFormPost queryForm
case result of
FormSuccess query -> processQuery query
_ -> defaultLayout $ do
setTitle "Searching for submissions"
$(widgetFile "query-form")
getQueryResultsR :: Text -> Handler Html
getQueryResultsR = processQuery
2018-11-10 11:20:17 +01:00
isFullQuery :: Text -> Bool
isFullQuery query = length query == 40
detectSingleVariantSubmissionQuery :: (PersistQueryRead backend, MonadIO m, BaseBackend backend ~ SqlBackend) => [FullSubmissionInfo] -> ReaderT backend m (Maybe (Key Variant))
detectSingleVariantSubmissionQuery [submissionInfo] = do
if (null $ fsiSuperSubmissions submissionInfo)
then
do
variants <- selectList [VariantSubmission ==. fsiSubmissionId submissionInfo] []
case variants of
[Entity variantId _] -> return $ Just variantId
_ -> return Nothing
else
return Nothing
detectSingleVariantSubmissionQuery _ = return Nothing
2016-02-12 13:00:33 +01:00
processQuery :: Text -> Handler Html
processQuery query = do
2021-08-21 16:54:54 +02:00
mUserId <- maybeAuthId
submissions' <- findSubmissions query
let submissions = map fst submissions'
mSingleVariant <- runDB $ detectSingleVariantSubmissionQuery submissions
case mSingleVariant of
Just singleVariantId -> redirect $ ViewVariantR singleVariantId
Nothing -> do
defaultLayout $ do
setTitle "query results"
$(widgetFile "query-results")
2016-02-12 13:00:33 +01:00
2021-02-15 12:51:24 +01:00
toQueryResultView :: FullSubmissionInfo -> Handler QueryResultView
toQueryResultView fsi = do
let submissionId = fsiSubmissionId fsi
let submission = fsiSubmission fsi
theVersion <- realSubmissionVersion $ Entity submissionId submission
2021-02-15 12:51:24 +01:00
(tableEntries, tests) <- runDB
$ getChallengeSubmissionInfosForVersion 2
(\s -> entityKey s == submissionId)
(const True)
id
(submissionChallenge submission)
theVersion
let (commonParams, strippedTableEntries) = extractCommonParams tableEntries
2021-02-15 12:51:24 +01:00
let evaluations = map (\entry ->
VariantView {
variantViewId = fromSqlKey $ entityKey $ tableEntryVariant entry,
variantViewName = variantName $ entityVal $ tableEntryVariant entry,
2021-02-15 21:39:06 +01:00
variantViewRank = tableEntryRank entry,
2021-02-15 12:51:24 +01:00
variantViewEvaluations = catMaybes $ Import.map (convertEvaluationToView $ tableEntryMapping entry) tests,
variantViewParams = Import.map entityVal $ tableEntryParams entry
}) strippedTableEntries
2021-02-15 12:51:24 +01:00
return $ QueryResultView {
queryResultViewSubmissionInfo = fsi,
queryResultViewVariants = evaluations,
queryResultSharedParams = map entityVal commonParams }
2021-02-15 12:51:24 +01:00
getQueryJsonR :: Text -> Handler Value
getQueryJsonR query = do
submissions' <- findSubmissions query
let submissions = map fst submissions'
qrvs <- mapM toQueryResultView submissions
return $ array qrvs
declareQuerySwagger :: Declare (Definitions Schema) Swagger
declareQuerySwagger = do
-- param schemas
let querySchema = toParamSchema (Proxy :: Proxy String)
queryResponse <- declareResponse (Proxy :: Proxy [QueryResultView])
return $ mempty
& paths .~
fromList [ ("/api/query/{query}",
mempty & DS.get ?~ (mempty
& parameters .~ [ Inline $ mempty
& name .~ "query"
& required ?~ True
& schema .~ ParamOther (mempty
& in_ .~ ParamPath
& paramSchema .~ querySchema) ]
& produces ?~ MimeList ["application/json"]
& description ?~ "For a SHA1 hash prefix returns all the submissions matching"
& at 200 ?~ Inline queryResponse))
]
queryApi :: Swagger
queryApi = spec & definitions .~ defs
where
2021-02-15 13:48:58 +01:00
(defs, spec) = runDeclare declareQuerySwagger mempty
2021-02-15 12:51:24 +01:00
priorityLimitForViewVariant :: Int
priorityLimitForViewVariant = 4
2020-09-05 23:26:53 +02:00
getViewVariantDiffR :: VariantId -> VariantId -> TestId -> Handler Html
getViewVariantDiffR oldVariantId newVariantId testId = do
doViewVariantTestR (TwoThings oldVariantId newVariantId) testId
getViewVariantTestR :: VariantId -> TestId -> Handler Html
getViewVariantTestR variantId testId = do
2020-09-05 23:26:53 +02:00
doViewVariantTestR (OneThing variantId) testId
data ViewVariantData = ViewVariantData {
viewVariantDataFullSubmissionInfo :: (FullSubmissionInfo, Maybe Text),
viewVariantDataTableEntry :: TableEntry,
viewVariantDataTests :: [Entity Test],
viewVariantDataOuts :: [(SHA1, Text)]
}
-- Return the submission version for which the tests are available.
-- It should be simply submissionVersion but the problem is that
-- we change the update the change version when updating a challenge
-- (if the test did not change), and we are left sometimes with dangling
-- versions for which no tests are available.
realSubmissionVersion :: Entity Submission -> Handler SHA1
realSubmissionVersion (Entity submissionId _) = do
testOutputs <- runDB $ E.select
2021-11-13 12:16:07 +01:00
$ E.from $ \(variant, out, test, evaluation, ver) -> do
E.where_ (variant ^. VariantSubmission E.==. E.val submissionId
E.&&. out ^. OutVariant E.==. variant ^. VariantId
2021-11-13 12:16:07 +01:00
E.&&. out ^. OutTest E.==. test ^. TestId
E.&&. out ^. OutChecksum E.==. evaluation ^. EvaluationChecksum
E.&&. evaluation ^. EvaluationVersion E.==. ver ^. VersionCommit)
E.orderBy [E.desc (ver ^. VersionStamp)]
return test
let (t:_) = testOutputs
return $ testCommit $ entityVal t
2020-09-05 23:26:53 +02:00
fetchViewVariantData :: VariantId -> Handler ViewVariantData
fetchViewVariantData variantId = do
2019-11-30 08:36:21 +01:00
variant <- runDB $ get404 variantId
let theSubmissionId = variantSubmission variant
theSubmission <- runDB $ get404 theSubmissionId
theVersion <- realSubmissionVersion $ Entity theSubmissionId theSubmission
([entry], tests') <- runDB $ getChallengeSubmissionInfosForVersion priorityLimitForViewVariant
(\e -> entityKey e == theSubmissionId)
(\e -> entityKey e == variantId)
id
(submissionChallenge theSubmission)
theVersion
let tests = sortBy (flip testComparator) tests'
let isViewable = True
if isViewable
2019-11-30 08:36:21 +01:00
then
do
fullSubmissionInfo <- getFullInfo (Entity theSubmissionId theSubmission)
testOutputs <- runDB $ E.select
$ E.from $ \(out, test) -> do
E.where_ (out ^. OutTest E.==. test ^. TestId
E.&&. out ^. OutVariant E.==. E.val variantId)
E.orderBy []
return (out, test)
let outputs =
sortBy (\a b -> ((snd b) `compare` (snd a)))
$ nub
$ map (\(out, test) -> (outChecksum $ entityVal out, testName $ entityVal test)) testOutputs
2020-09-05 23:26:53 +02:00
return $ ViewVariantData (fullSubmissionInfo, Just $ variantName variant) entry tests outputs
2019-11-30 08:36:21 +01:00
else
error "Cannot access this submission variant"
2020-09-05 23:26:53 +02:00
instance Diffable SHA1 where
type DiffSettings SHA1 = ()
type DiffResult SHA1 = Diff SHA1
single u = OneThing u
diff _ old new
| old == new = OneThing new
| otherwise = TwoThings old new
postCompareFormR :: VariantId -> TestId -> Handler Html
postCompareFormR variantId testId = do
((result, _), _) <- runFormPost outQueryForm
case result of
FormSuccess outQuery -> do
(out:_) <- runDB $ rawOutQuery outQuery
let otherVariantId = outVariant $ entityVal out
doViewVariantTestR (TwoThings otherVariantId variantId) testId
2020-09-05 23:26:53 +02:00
nullSHA1 :: SHA1
nullSHA1 = fromTextToSHA1 "da39a3ee5e6b4b0d3255bfef95601890afd80709"
doViewVariantTestR :: Diff VariantId -> TestId -> Handler Html
doViewVariantTestR variantId testId = do
2021-08-21 16:54:54 +02:00
mUserId <- maybeAuthId
2020-09-05 23:26:53 +02:00
testSelected <- runDB $ get404 testId
let testSelectedEnt = Entity testId testSelected
variantInfos <- mapM (fetchViewVariantData) variantId
let fullSubmissionInfo = viewVariantDataFullSubmissionInfo <$> variantInfos
let entry = viewVariantDataTableEntry <$> variantInfos
let tests' = viewVariantDataTests <$> variantInfos
let outputs' = viewVariantDataOuts <$> variantInfos
let testIds = map fst $ runDiff () $ fmap (map entityKey) tests'
testEnts <- mapM (runDB . get404) testIds
let tests = map (\(i,e) -> Entity i e) $ zip testIds testEnts
let outputs :: [(Diff SHA1, Text)] =
sortBy (\a b -> ((snd b) `compare` (snd a)))
$ map swap $ LM.toList $ runDiff (nullSHA1, ()) $ fmap (LM.fromList . map swap) outputs'
(formWidget, formEnctype) <- generateFormPost outQueryForm
2020-09-05 23:26:53 +02:00
defaultLayout $ do
setTitle "Variant"
$(widgetFile "view-variant")
mergeEntryParams :: Diff [Parameter] -> [(Text, Diff Text)]
mergeEntryParams (OneThing u) = map (\(Parameter _ pname pval) -> (pname, OneThing pval)) u
2020-09-05 23:26:53 +02:00
mergeEntryParams (TwoThings old new) = LM.toList $ diff ("", ()) oldMap newMap
where oldMap = mapify old
newMap = mapify new
mapify l = LM.fromList $ map (\(Parameter _ pname pval) -> (pname, pval)) l
2020-09-05 23:26:53 +02:00
getViewVariantR :: VariantId -> Handler Html
getViewVariantR variantId = do
variant <- runDB $ get404 variantId
let theSubmissionId = variantSubmission variant
theSubmission <- runDB $ get404 theSubmissionId
theVersion <- realSubmissionVersion $ Entity theSubmissionId theSubmission
(_, tests') <- runDB $ getChallengeSubmissionInfosForVersion priorityLimitForViewVariant
(\e -> entityKey e == theSubmissionId)
(\e -> entityKey e == variantId)
id
(submissionChallenge theSubmission)
theVersion
let (mainTest:_) = sortBy (flip testComparator) tests'
getViewVariantTestR variantId (entityKey mainTest)
linkedWithAnchor :: (Text.Blaze.ToMarkup a1, Text.Blaze.ToMarkup a2)
=> Text -> (t -> a2) -> (t -> Route site) -> (t -> a1) -> Table.Table site t
linkedWithAnchor h propFunc routeFunc anchorFunc =
Table.widget h (
\v -> [whamlet|<a href=@{routeFunc v}\\##{anchorFunc v}>#{propFunc v}|])
2020-09-05 23:26:53 +02:00
getVariantTestLink :: Diff VariantId -> TestId -> Route App
getVariantTestLink (OneThing u) testId = ViewVariantTestR u testId
getVariantTestLink (TwoThings old new) testId = ViewVariantDiffR old new testId
crossTableDefinition :: Diff VariantId -> TableWithValues (Entity Test, Diff Text) -> Table.Table App (Text, [(Entity Test, Diff Text)])
crossTableDefinition variantId (TableWithValues (headerH : headerR) _) = mempty
2020-01-04 22:34:03 +01:00
++ Table.text headerH fst
++ mconcat (map (\(i, h) -> linkedWithAnchor h
(snd . (!! i) . snd)
((\(e, _) -> getVariantTestLink variantId (entityKey e)) . (!! i) . snd)
(("worst-items-" <>) . testName . entityVal . fst . (!! i) . snd))
2020-08-14 21:07:19 +02:00
$ zip [0..] headerR)
crossTableDefinition _ _ = error $ "cross-tab of an unexpected size"
2020-09-05 23:26:53 +02:00
crossTableBody :: TableWithValues (Entity Test, Diff Text) -> [(Text, [(Entity Test, Diff Text)])]
2020-01-04 22:34:03 +01:00
crossTableBody (TableWithValues _ rows) = rows
2020-09-05 23:26:53 +02:00
paramsTable :: Table.Table App (Text, Diff Text)
paramsTable = mempty
2020-09-05 23:26:53 +02:00
++ Table.text "Parameter" fst
++ Table.widget "Value" ((\t -> [whamlet|#{t}|]) . snd)
2020-09-05 23:26:53 +02:00
viewOutput :: Diff TableEntry -> [Entity Test] -> (SHA1, Text) -> WidgetFor App ()
2019-12-14 18:21:47 +01:00
viewOutput entry tests (outputHash, testSet) = do
let (mainTest:_) = filter (\e -> (testName $ entityVal e) == testSet) tests
2020-09-05 23:26:53 +02:00
viewOutputWithNonDefaultTestSelected entry tests mainTest (OneThing outputHash, testSet)
2020-09-05 16:56:59 +02:00
maximumNumberOfItemsToBeShown :: Int
maximumNumberOfItemsToBeShown = 40
2020-09-05 23:26:53 +02:00
getOut :: Maybe UserId -> TableEntry -> WidgetFor App (Maybe (FilePath, FilePath))
2021-08-21 16:54:54 +02:00
getOut _ entry = do
2020-09-05 23:26:53 +02:00
let variant = variantName $ entityVal $ tableEntryVariant entry
let isViewable = True
2020-09-05 23:26:53 +02:00
if isViewable
then
do
mRepoDir <- handlerToWidget $ justGetSubmissionRepoDir $ entityKey $ tableEntrySubmission entry
case mRepoDir of
Just repoDir -> do
outFilePath <- liftIO $ lookForCompressedFiles (repoDir </> (T.unpack variant) <.> "tsv")
return $ Just (repoDir, outFilePath)
Nothing -> return Nothing
else
do
return Nothing
data DiffLineRecord = DiffLineRecord Text Text (Diff (Text, MetricValue)) Word32
deriving (Show)
getUniLineRecord :: LineRecord -> DiffLineRecord
getUniLineRecord (LineRecord inp expect out lineNo val) = DiffLineRecord inp expect (OneThing (out, val)) lineNo
2020-09-05 23:26:53 +02:00
getBiLineRecord :: (LineRecord, LineRecord) -> DiffLineRecord
getBiLineRecord ((LineRecord oldInp oldExp oldOut oldLineNo oldVal), (LineRecord newInp newExp newOut newLineNo newVal))
| oldInp == newInp && oldExp == newExp && oldLineNo == newLineNo = DiffLineRecord newInp
newExp
(TwoThings (oldOut, oldVal)
(newOut, newVal))
newLineNo
| otherwise = error "inconsistent line records when diffing"
getScoreFromDiff :: DiffLineRecord -> MetricValue
getScoreFromDiff (DiffLineRecord _ _ (OneThing (_, s)) _) = s
getScoreFromDiff (DiffLineRecord _ _ (TwoThings (_, oldS) (_, newS)) _) = newS - oldS
data SourceOfExpected = NoExpectedFound | ExpectedFromSubmission | ExpectedFromChallenge
deriving (Eq, Show)
checkForExpected :: FilePath -> FilePath -> IO Bool
checkForExpected repoDir testName = do
expFile <- lookForCompressedFiles (repoDir </> testName </> "expected.tsv")
expFileExists <- D.doesFileExist expFile
return expFileExists
2020-09-05 23:26:53 +02:00
viewOutputWithNonDefaultTestSelected :: Diff TableEntry
-> [Entity Test]
-> Entity Test
-> (Diff SHA1, Text)
-> WidgetFor App ()
viewOutputWithNonDefaultTestSelected entry tests mainTest (outputHash, testSet) = do
let tests' = filter (\e -> (testName $ entityVal e) == testSet) tests
mauthId <- maybeAuthId
2020-09-05 23:26:53 +02:00
let outputSha1AsText = fromSHA1ToText $ current outputHash
2019-12-14 18:21:47 +01:00
2020-09-05 23:26:53 +02:00
let variantId = entityKey <$> tableEntryVariant <$> entry
2019-12-14 18:21:47 +01:00
2020-09-05 23:26:53 +02:00
let theStamp = submissionStamp $ entityVal $ tableEntrySubmission $ current entry
let theVersion = submissionVersion $ entityVal $ tableEntrySubmission $ current entry
2020-09-05 23:26:53 +02:00
challenge <- handlerToWidget $ runDB $ get404 $ submissionChallenge $ entityVal $ tableEntrySubmission $ current entry
2019-12-14 18:21:47 +01:00
let isNonSensitive = challengeSensitive challenge == Just False
let shouldBeShown = testSet /= (testName $ entityVal mainTest) && isNonSensitive
2019-12-14 18:21:47 +01:00
let mainMetric = testMetric $ entityVal mainTest
2020-01-04 22:34:03 +01:00
let testLabels = map (formatTestEvaluationScheme . entityVal) tests'
2021-11-13 12:16:07 +01:00
let theMapping = LM.fromList $ map (\test -> (formatTestEvaluationScheme $ entityVal test,
(test,
2020-09-05 16:45:09 +02:00
(formatTruncatedScore (getTestFormattingOpts $ entityVal test)
2020-09-05 23:26:53 +02:00
<$> extractScore (getTestReference test) <$> entry)))) tests'
2021-11-13 12:16:07 +01:00
let crossTables = splitIntoTablesWithValues "Metric" "Score" theMapping testLabels
2020-01-04 22:34:03 +01:00
2019-12-14 18:21:47 +01:00
mResult <-
if shouldBeShown
then
2020-01-04 22:34:03 +01:00
do
2020-09-05 23:26:53 +02:00
outPaths <- mapM (getOut mauthId) entry
case current outPaths of
Just _ -> do
let repoDir = fst <$> fromJust <$> outPaths
let outFilePath = snd <$> fromJust <$> outPaths
let outFile = takeFileName $ current outFilePath
let testName = T.unpack testSet
challengeRepoDir <- handlerToWidget $ getRepoDir (challengePublicRepo challenge)
expFileExistsInChallengeRepo <- liftIO $ checkForExpected challengeRepoDir testName
expFileExists <- liftIO $ checkForExpected (current repoDir) testName
let expFileStatus = if expFileExists
then ExpectedFromSubmission
else
if expFileExistsInChallengeRepo
then ExpectedFromChallenge
else NoExpectedFound
if expFileStatus /= NoExpectedFound
then do
let theRepoDir = case expFileStatus of
ExpectedFromSubmission -> (current repoDir)
ExpectedFromChallenge -> challengeRepoDir
NoExpectedFound -> error "Should not be here"
Right opts <- liftIO $ readOptsFromConfigFile [] (theRepoDir </> "config.txt")
let spec = GEvalSpecification {
2020-09-05 23:26:53 +02:00
gesOutDirectory = current repoDir,
gesExpectedDirectory = Just theRepoDir,
2020-09-05 23:26:53 +02:00
gesTestName = testName,
gesSelector = Nothing,
gesOutFile = outFile,
gesAltOutFiles = Nothing,
gesExpectedFile = "expected.tsv",
gesInputFile = "in.tsv",
gesMetrics = [mainMetric],
gesFormatting = FormattingOptions {
decimalPlaces = Nothing,
asPercentage = False },
gesTokenizer = Nothing,
gesGonitoHost = Nothing,
gesToken = Nothing,
gesGonitoGitAnnexRemote = Nothing,
gesReferences = Nothing,
gesBootstrapResampling = Nothing,
gesInHeader = gesInHeader $ geoSpec opts,
gesOutHeader = gesOutHeader $ geoSpec opts,
2020-09-05 23:26:53 +02:00
gesShowPreprocessed = True }
case outPaths of
OneThing _ -> do
result <- liftIO $ runLineByLineGeneralized FirstTheWorst
spec
(\_ -> CL.take maximumNumberOfItemsToBeShown)
return $ Just (expFileStatus, zip [1..] $ map getUniLineRecord result)
TwoThings (Just (oldRepoDir, oldOutFilePath)) _ -> do
absOldOutFilePath <- liftIO $ makeAbsolute (oldRepoDir </> testName </> (takeFileName oldOutFilePath))
result <- liftIO $ runDiffGeneralized FirstTheWorst
absOldOutFilePath
spec
(\_ -> CL.take maximumNumberOfItemsToBeShown)
return $ Just (expFileStatus, zip [1..] $ map getBiLineRecord result)
else
do
return Nothing
2020-09-05 23:26:53 +02:00
Nothing -> return Nothing
2019-12-14 18:21:47 +01:00
else
return Nothing
2019-11-30 08:36:21 +01:00
$(widgetFile "view-output")
lineByLineTable :: Entity Test -> SHA1 -> UTCTime -> Table.Table App (Int, DiffLineRecord)
lineByLineTable (Entity testId test) theVersion theStamp = mempty
2019-12-14 18:21:47 +01:00
++ Table.int "#" fst
2020-09-05 23:26:53 +02:00
++ theLimitedTextCell "input" (((\(DiffLineRecord inp _ _ _) -> inp) . snd))
++ theLimitedTextCell "expected output" ((\(DiffLineRecord _ expected _ _) -> expected) . snd)
++ theLimitedDiffTextCell "actual output" (fmap fst . (\(DiffLineRecord _ _ out _) -> out) . snd)
++ resultCell test (fakeEvaluation . getScoreFromDiff . snd)
2019-12-14 18:21:47 +01:00
where fakeEvaluation score = Just $ Evaluation {
evaluationTest = testId,
evaluationChecksum = testChecksum test,
evaluationScore = Just score,
2020-01-28 23:14:46 +01:00
evaluationErrorBound = Nothing,
2019-12-14 18:21:47 +01:00
evaluationErrorMessage = Nothing,
evaluationStamp = theStamp,
evaluationVersion = theVersion }
2019-12-14 18:21:47 +01:00
2018-11-12 14:12:51 +01:00
resultTable :: Entity Submission -> WidgetFor App ()
resultTable entSubmission@(Entity submissionId submission) = do
theVersion <- handlerToWidget $ realSubmissionVersion entSubmission
(tableEntries, tests') <- handlerToWidget
$ runDB
$ getChallengeSubmissionInfosForVersion 2
(\s -> entityKey s == submissionId)
(const True)
id
(submissionChallenge submission)
theVersion
let (commonParams', strippedTableEntries) = extractCommonParams tableEntries
let commonParams = map (\(Entity _ p) -> (parameterName p, OneThing $ parameterValue p)) commonParams'
2018-11-12 14:12:51 +01:00
let paramNames =
nub
$ map (parameterName . entityVal)
$ concat
$ map tableEntryParams strippedTableEntries
2018-11-12 14:12:51 +01:00
let maximumNumberOfColumns = 10
let tests = adjustNumberOfColumnsShown (maximumNumberOfColumns - length paramNames) tests'
2018-11-12 14:12:51 +01:00
let resultId = show $ fromSqlKey submissionId
let jsSelector = String $ T.pack ("#t" ++ resultId ++ " > table")
let delta = Number $ fromIntegral ((length paramNames) + 1)
let higherTheBetterArray = getIsHigherTheBetterArray $ map entityVal tests
$(widgetFile "result-table")
adjustNumberOfColumnsShown :: Int -> [Entity Test] -> [Entity Test]
adjustNumberOfColumnsShown maximumNumberOfColumns tests = adjustNumberOfColumnsShown' (max maximumNumberOfColumns minimumNumberOfTests) tests
where adjustNumberOfColumnsShown' maximumNumberOfColumns' tests'
| length tests <= maximumNumberOfColumns' = tests'
| otherwise = let filteredTests = filter (\t -> not ("dev" `isInfixOf` (testName $ entityVal t))) tests'
in if null filteredTests
then tests'
else
if length filteredTests <= maximumNumberOfColumns'
then filteredTests
else take maximumNumberOfColumns' filteredTests
minimumNumberOfTests = 2
2019-11-29 22:10:48 +01:00
2020-05-31 21:26:29 +02:00
canFullInfoBeShown :: (MonadIO m, BackendCompatible SqlBackend backend, PersistQueryRead backend, PersistUniqueRead backend) => Diff (FullSubmissionInfo, b) -> Maybe (Key User) -> ReaderT backend m Bool
2021-08-21 16:54:54 +02:00
canFullInfoBeShown (OneThing (fsi, _)) mUserId = checkWhetherVisible (fsiSubmission fsi) mUserId
canFullInfoBeShown (TwoThings (fsiA, _) (fsiB, _)) mUserId = do
checkA <- checkWhetherVisible (fsiSubmission fsiA) mUserId
checkB <- checkWhetherVisible (fsiSubmission fsiB) mUserId
return (checkA && checkB)
submissionHeader :: Maybe UserId -> Diff (FullSubmissionInfo, Maybe Text) -> WidgetFor App ()
submissionHeader mUserId param = do
showFullInfo <- handlerToWidget $ runDB $ canFullInfoBeShown param mUserId
2021-10-01 13:30:01 +02:00
app <- getYesod
let repoHost = appRepoHost $ appSettings app
let submissionToSubmissionUrl submission'
= getReadOnlySubmissionUrl (fsiScheme submission')
repoHost
(fsiChallengeRepo submission')
(challengeName $ fsiChallenge submission')
let publicSubmissionRepo = submissionToSubmissionUrl <$> submission
let submissionToBrowsableUrl submission'
= browsableGitRepoBranch (fsiScheme submission')
repoHost
(fsiChallengeRepo submission')
(challengeName $ fsiChallenge submission')
(getPublicSubmissionBranch $ fsiSubmissionId submission')
let browsableUrl = submissionToBrowsableUrl <$> submission
2019-11-29 22:10:48 +01:00
$(widgetFile "submission-header")
2020-09-05 23:26:53 +02:00
where variantSettings = ("out", ())
submission = fst <$> param
mVariantName = snd <$> param
commitSha1AsText = fromSHA1ToText <$> submissionCommit <$> fsiSubmission <$> submission
submitter = formatSubmitter <$> fsiUser <$> submission
publicSubmissionBranch = getPublicSubmissionBranch <$> fsiSubmissionId <$> submission
stamp = T.pack <$> show <$> submissionStamp <$> fsiSubmission <$> submission
2021-08-21 16:54:54 +02:00
queryResult :: Maybe UserId -> FullSubmissionInfo -> WidgetFor App ()
queryResult mUserId submission = do
2019-11-29 22:10:48 +01:00
$(widgetFile "query-result")
2016-02-12 13:00:33 +01:00
queryForm :: Form Text
queryForm = renderBootstrap3 BootstrapBasicForm $ areq textField (fieldSettingsLabel MsgGitCommitSha1) Nothing
outQueryForm :: Form Text
outQueryForm = renderBootstrap3 BootstrapBasicForm $ areq textField (fieldSettingsLabel MsgOutSha1) Nothing