forked from filipg/gonito
Introduce course-specific announcement hooks
This commit is contained in:
parent
36ad24dba5
commit
270d4b2607
@ -167,6 +167,7 @@ instance Yesod App where
|
|||||||
isAuthorized ExtraPointsR _ = isAdmin
|
isAuthorized ExtraPointsR _ = isAdmin
|
||||||
|
|
||||||
isAuthorized TestAnnouncementsR _ = isAdmin
|
isAuthorized TestAnnouncementsR _ = isAdmin
|
||||||
|
isAuthorized (TestChallengeAnnouncementsR _) _ = isAdmin
|
||||||
|
|
||||||
isAuthorized DashboardR _ = regularAuthorization
|
isAuthorized DashboardR _ = regularAuthorization
|
||||||
|
|
||||||
|
@ -3,7 +3,44 @@ module Handler.Announcements where
|
|||||||
import Import
|
import Import
|
||||||
|
|
||||||
import Handler.Shared
|
import Handler.Shared
|
||||||
import Web.Announcements (sendAnnouncement)
|
import Web.Announcements (sendAnnouncement,
|
||||||
|
AnnouncementPiece(..),
|
||||||
|
AnnouncementMessage,
|
||||||
|
AnnouncementHook,
|
||||||
|
toAnnouncementHook)
|
||||||
|
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
import Database.Esqueleto ((^.))
|
||||||
|
|
||||||
|
sendChallengeAnnouncement :: ChallengeId -> AnnouncementMessage -> Handler ()
|
||||||
|
sendChallengeAnnouncement challengeId announcementMsg = do
|
||||||
|
hooks <- fetchChallengeHooks challengeId
|
||||||
|
liftIO $ mapM_ (\hook -> sendAnnouncement hook announcementMsg) hooks
|
||||||
|
|
||||||
|
fetchChallengeHooks :: ChallengeId -> Handler [AnnouncementHook]
|
||||||
|
fetchChallengeHooks challengeId = do
|
||||||
|
courses <- runDB $ E.select $ E.from $ \(course, course_challenge) -> do
|
||||||
|
E.where_ (course_challenge ^. CourseChallengeChallenge E.==. E.val challengeId
|
||||||
|
E.&&. course_challenge ^. CourseChallengeCourse E.==. course ^. CourseId)
|
||||||
|
return course
|
||||||
|
|
||||||
|
case catMaybes $ map (courseAnnouncementHook . entityVal) courses of
|
||||||
|
[] -> do
|
||||||
|
app <- getYesod
|
||||||
|
let mWebHook = appAnnouncementHook $ appSettings app
|
||||||
|
return $ maybeToList mWebHook
|
||||||
|
hooks -> return $ map toAnnouncementHook hooks
|
||||||
|
|
||||||
|
getTestChallengeAnnouncementsR :: Text -> Handler Html
|
||||||
|
getTestChallengeAnnouncementsR challengeName = do
|
||||||
|
(Entity challengeId _) <- runDB $ getBy404 $ UniqueName challengeName
|
||||||
|
|
||||||
|
app <- getYesod
|
||||||
|
sendChallengeAnnouncement challengeId (testMessage app)
|
||||||
|
|
||||||
|
defaultLayout $ do
|
||||||
|
setTitle "Test announcements"
|
||||||
|
$(widgetFile "test-challenge-announcements")
|
||||||
|
|
||||||
getTestAnnouncementsR :: Handler Html
|
getTestAnnouncementsR :: Handler Html
|
||||||
getTestAnnouncementsR = do
|
getTestAnnouncementsR = do
|
||||||
@ -11,14 +48,17 @@ getTestAnnouncementsR = do
|
|||||||
|
|
||||||
let webHook = appAnnouncementHook $ appSettings app
|
let webHook = appAnnouncementHook $ appSettings app
|
||||||
|
|
||||||
let name = case appLocation $ appSettings app of
|
|
||||||
Just loc -> "Gonito@" ++ loc
|
|
||||||
Nothing -> "Gonito"
|
|
||||||
|
|
||||||
case webHook of
|
case webHook of
|
||||||
Just hook -> liftIO $ sendAnnouncement hook ("Test message from " ++ (linkInAnnouncement (Just hook) app name ""))
|
Just hook -> liftIO $ sendAnnouncement hook (testMessage app)
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
|
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitle "Test announcements"
|
setTitle "Test announcements"
|
||||||
$(widgetFile "test-announcements")
|
$(widgetFile "test-announcements")
|
||||||
|
|
||||||
|
testMessage :: App -> AnnouncementMessage
|
||||||
|
testMessage app = [AnnouncementText "Test message from ",
|
||||||
|
(linkInAnnouncement app name "")]
|
||||||
|
where name = case appLocation $ appSettings app of
|
||||||
|
Just loc -> "Gonito@" ++ loc
|
||||||
|
Nothing -> "Gonito"
|
||||||
|
@ -2,7 +2,6 @@ module Handler.Course where
|
|||||||
|
|
||||||
import Import
|
import Import
|
||||||
|
|
||||||
import Handler.Shared
|
|
||||||
import Handler.ListChallenges
|
import Handler.ListChallenges
|
||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
@ -13,6 +13,7 @@ import Yesod.WebSockets
|
|||||||
import Handler.Runner
|
import Handler.Runner
|
||||||
import System.Exit
|
import System.Exit
|
||||||
|
|
||||||
|
import Web.Announcements
|
||||||
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
|
||||||
@ -753,8 +754,8 @@ compareFun :: MetricOrdering -> Double -> Double -> Ordering
|
|||||||
compareFun TheLowerTheBetter = flip compare
|
compareFun TheLowerTheBetter = flip compare
|
||||||
compareFun TheHigherTheBetter = compare
|
compareFun TheHigherTheBetter = compare
|
||||||
|
|
||||||
linkInAnnouncement :: Maybe AnnouncementHook -> App -> Text -> Text -> Text
|
linkInAnnouncement :: App -> Text -> Text -> AnnouncementPiece
|
||||||
linkInAnnouncement hook app title addr = formatLink hook slink title
|
linkInAnnouncement app title addr = AnnouncementLink slink title
|
||||||
where slink = (appRoot $ appSettings app) ++ "/" ++ addr
|
where slink = (appRoot $ appSettings app) ++ "/" ++ addr
|
||||||
|
|
||||||
formatVersion :: (Int, Int, Int) -> Text
|
formatVersion :: (Int, Int, Int) -> Text
|
||||||
|
@ -32,6 +32,7 @@ import Handler.Common
|
|||||||
import Handler.Evaluate
|
import Handler.Evaluate
|
||||||
import Handler.JWT
|
import Handler.JWT
|
||||||
import Handler.Team
|
import Handler.Team
|
||||||
|
import Handler.Announcements
|
||||||
|
|
||||||
import Database.Persist.Sql (fromSqlKey)
|
import Database.Persist.Sql (fromSqlKey)
|
||||||
|
|
||||||
@ -87,7 +88,6 @@ instance ToJSON Import.Tag where
|
|||||||
instance ToSchema Import.Tag where
|
instance ToSchema Import.Tag where
|
||||||
declareNamedSchema _ = do
|
declareNamedSchema _ = do
|
||||||
stringSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy String)
|
stringSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy String)
|
||||||
boolSchema <- declareSchemaRef (DPR.Proxy :: DPR.Proxy Bool)
|
|
||||||
return $ NamedSchema (Just "Tag") $ mempty
|
return $ NamedSchema (Just "Tag") $ mempty
|
||||||
& type_ .~ SwaggerObject
|
& type_ .~ SwaggerObject
|
||||||
& properties .~
|
& properties .~
|
||||||
@ -1000,9 +1000,7 @@ doCreateSubmission' _ userId challengeId challengeSubmissionData chan = do
|
|||||||
msg chan "SUBMISSION CREATED"
|
msg chan "SUBMISSION CREATED"
|
||||||
|
|
||||||
app <- getYesod
|
app <- getYesod
|
||||||
let mHook = appAnnouncementHook $ appSettings app
|
let submissionLink = linkInAnnouncement app "submission" ("q/" ++ (fromSHA1ToText (repoCurrentCommit repo)))
|
||||||
|
|
||||||
let submissionLink = linkInAnnouncement mHook app "submission" ("q/" ++ (fromSHA1ToText (repoCurrentCommit repo)))
|
|
||||||
|
|
||||||
case mMainEnt of
|
case mMainEnt of
|
||||||
Just (Entity mainTestId mainTest) -> do
|
Just (Entity mainTestId mainTest) -> do
|
||||||
@ -1020,13 +1018,13 @@ doCreateSubmission' _ userId challengeId challengeSubmissionData chan = do
|
|||||||
(s:_) -> if compOp s b
|
(s:_) -> if compOp s b
|
||||||
then
|
then
|
||||||
do
|
do
|
||||||
let challengeLink = linkInAnnouncement mHook app (challengeTitle challenge) ("challenge/"
|
let challengeLink = linkInAnnouncement app (challengeTitle challenge) ("challenge/"
|
||||||
++ (challengeName challenge))
|
++ (challengeName challenge))
|
||||||
let formattingOpts = getTestFormattingOpts mainTest
|
let formattingOpts = getTestFormattingOpts mainTest
|
||||||
|
|
||||||
let message = ("Whoa! New best result for "
|
let message = [AnnouncementText "Whoa! New best result for ",
|
||||||
++ challengeLink
|
challengeLink,
|
||||||
++ " challenge by "
|
AnnouncementText (" challenge by "
|
||||||
++ (fromMaybe "???" $ userName user)
|
++ (fromMaybe "???" $ userName user)
|
||||||
++ ", "
|
++ ", "
|
||||||
++ (T.pack $ evaluationSchemeName $ testMetric mainTest)
|
++ (T.pack $ evaluationSchemeName $ testMetric mainTest)
|
||||||
@ -1038,13 +1036,11 @@ doCreateSubmission' _ userId challengeId challengeSubmissionData chan = do
|
|||||||
else "")
|
else "")
|
||||||
++ (T.pack $ formatTheResult formattingOpts (SimpleRun (s-b)))
|
++ (T.pack $ formatTheResult formattingOpts (SimpleRun (s-b)))
|
||||||
++ ")."
|
++ ")."
|
||||||
++ " See " ++ submissionLink ++ "."
|
++ " See "),
|
||||||
++ " :clap:")
|
submissionLink,
|
||||||
msg chan message
|
AnnouncementText ("." ++ " :clap:")]
|
||||||
case mHook of
|
msg chan $ renderAnnouncementMessage Nothing message
|
||||||
Just hook -> liftIO $ sendAnnouncement hook message
|
sendChallengeAnnouncement challengeId message
|
||||||
|
|
||||||
Nothing -> return ()
|
|
||||||
else return ()
|
else return ()
|
||||||
[] -> return ()
|
[] -> return ()
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
@ -1064,34 +1060,35 @@ doCreateSubmission' _ userId challengeId challengeSubmissionData chan = do
|
|||||||
|
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
|
|
||||||
checkIndicators :: User -> ChallengeId -> SubmissionId -> Text -> [IndicatorEntry] -> Channel -> Handler ()
|
checkIndicators :: User -> ChallengeId -> SubmissionId -> AnnouncementPiece -> [IndicatorEntry] -> Channel -> Handler ()
|
||||||
checkIndicators user challengeId submissionId submissionLink relevantIndicators chan = do
|
checkIndicators user challengeId submissionId submissionLink relevantIndicators chan = do
|
||||||
msg chan "Checking indicators..."
|
msg chan "Checking indicators..."
|
||||||
theNow <- liftIO $ getCurrentTime
|
theNow <- liftIO $ getCurrentTime
|
||||||
mapM_ (\indicator -> checkIndicator theNow user challengeId submissionId submissionLink indicator chan) relevantIndicators
|
mapM_ (\indicator -> checkIndicator theNow user challengeId submissionId submissionLink indicator chan) relevantIndicators
|
||||||
|
|
||||||
checkIndicator :: UTCTime -> User -> ChallengeId -> SubmissionId -> Text -> IndicatorEntry -> Channel -> Handler ()
|
checkIndicator :: UTCTime -> User -> ChallengeId -> SubmissionId -> AnnouncementPiece -> IndicatorEntry -> Channel -> Handler ()
|
||||||
checkIndicator theNow user challengeId submissionId submissionLink indicator chan = do
|
checkIndicator theNow user challengeId submissionId submissionLink indicator chan = do
|
||||||
(entries, _) <- runDB $ getChallengeSubmissionInfos 1 (\(Entity sid _) -> sid == submissionId) (const True) id challengeId
|
(entries, _) <- runDB $ getChallengeSubmissionInfos 1 (\(Entity sid _) -> sid == submissionId) (const True) id challengeId
|
||||||
mapM_ (\t -> checkTarget theNow user submissionLink entries indicator t chan) (indicatorEntryTargets indicator)
|
mapM_ (\t -> checkTarget theNow user submissionLink entries indicator t chan) (indicatorEntryTargets indicator)
|
||||||
|
|
||||||
checkTarget :: UTCTime -> User -> Text -> [TableEntry] -> IndicatorEntry -> Entity Target -> Channel -> Handler ()
|
checkTarget :: UTCTime -> User -> AnnouncementPiece -> [TableEntry] -> IndicatorEntry -> Entity Target -> Channel -> Handler ()
|
||||||
checkTarget theNow user submissionLink entries indicator target chan = do
|
checkTarget theNow user submissionLink entries indicator target chan = do
|
||||||
app <- getYesod
|
let challengeId = entityKey $ indicatorEntryChallenge indicator
|
||||||
|
|
||||||
let status = getTargetStatus theNow entries indicator target
|
let status = getTargetStatus theNow entries indicator target
|
||||||
if status == TargetPassed
|
if status == TargetPassed
|
||||||
then
|
then
|
||||||
do
|
do
|
||||||
let message = "Congratulations!!! The target " ++ indicatorText
|
let message = [AnnouncementText ("Congratulations!!! The target " ++ indicatorText
|
||||||
++ " was beaten by "
|
++ " was beaten by "
|
||||||
++ (fromMaybe "???" $ userName user)
|
++ (fromMaybe "???" $ userName user)
|
||||||
++ ", "
|
++ ", "
|
||||||
++ " See " ++ submissionLink ++ "."
|
++ " See "),
|
||||||
++ (T.replicate 10 " :champagne: ") ++ " :mleczko: "
|
submissionLink,
|
||||||
msg chan message
|
AnnouncementText ("."
|
||||||
case appAnnouncementHook $ appSettings app of
|
++ (T.replicate 10 " :champagne: ") ++ " :mleczko: ")]
|
||||||
Just hook -> liftIO $ sendAnnouncement hook message
|
msg chan $ renderAnnouncementMessage Nothing message
|
||||||
Nothing -> return ()
|
sendChallengeAnnouncement challengeId message
|
||||||
else
|
else
|
||||||
return ()
|
return ()
|
||||||
where indicatorText = prettyIndicatorEntry indicator
|
where indicatorText = prettyIndicatorEntry indicator
|
||||||
|
@ -5,6 +5,9 @@ module Web.Announcements
|
|||||||
(sendAnnouncement,
|
(sendAnnouncement,
|
||||||
formatLink,
|
formatLink,
|
||||||
AnnouncementHook(..),
|
AnnouncementHook(..),
|
||||||
|
AnnouncementPiece(..),
|
||||||
|
AnnouncementMessage,
|
||||||
|
renderAnnouncementMessage,
|
||||||
toAnnouncementHook)
|
toAnnouncementHook)
|
||||||
where
|
where
|
||||||
|
|
||||||
@ -18,15 +21,29 @@ import Data.Default
|
|||||||
|
|
||||||
data AnnouncementHook = SlackWebHook Text | DiscordWebHook Text
|
data AnnouncementHook = SlackWebHook Text | DiscordWebHook Text
|
||||||
|
|
||||||
|
data AnnouncementPiece = AnnouncementText Text | AnnouncementLink Text Text
|
||||||
|
|
||||||
|
type AnnouncementMessage = [AnnouncementPiece]
|
||||||
|
|
||||||
|
renderAnnouncementMessage :: Maybe AnnouncementHook -> AnnouncementMessage -> Text
|
||||||
|
renderAnnouncementMessage hook pieces = Data.Text.concat $ Prelude.map (renderAnnouncementPiece hook) pieces
|
||||||
|
|
||||||
|
renderAnnouncementPiece :: Maybe AnnouncementHook -> AnnouncementPiece -> Text
|
||||||
|
renderAnnouncementPiece _ (AnnouncementText t) = t
|
||||||
|
renderAnnouncementPiece mHook (AnnouncementLink url title) = formatLink mHook url title
|
||||||
|
|
||||||
toAnnouncementHook :: Text -> AnnouncementHook
|
toAnnouncementHook :: Text -> AnnouncementHook
|
||||||
toAnnouncementHook url
|
toAnnouncementHook url
|
||||||
| ".slack." `isInfixOf` url = SlackWebHook url
|
| ".slack." `isInfixOf` url = SlackWebHook url
|
||||||
| "discord.com" `isInfixOf` url = DiscordWebHook url
|
| "discord.com" `isInfixOf` url = DiscordWebHook url
|
||||||
| otherwise = error $ unpack $ "unknown hook type '" <> url <> "'"
|
| otherwise = error $ unpack $ "unknown hook type '" <> url <> "'"
|
||||||
|
|
||||||
sendAnnouncement :: AnnouncementHook -> Text -> IO ()
|
sendAnnouncement :: AnnouncementHook -> AnnouncementMessage -> IO ()
|
||||||
sendAnnouncement (SlackWebHook hook) message = sendAnnouncementViaJson hook "text" message
|
sendAnnouncement hook message = sendAnnouncement' hook $ renderAnnouncementMessage (Just hook) message
|
||||||
sendAnnouncement (DiscordWebHook hook) message = sendAnnouncementViaJson hook "content" message
|
|
||||||
|
sendAnnouncement' :: AnnouncementHook -> Text -> IO ()
|
||||||
|
sendAnnouncement' (SlackWebHook hook) message = sendAnnouncementViaJson hook "text" message
|
||||||
|
sendAnnouncement' (DiscordWebHook hook) message = sendAnnouncementViaJson hook "content" message
|
||||||
|
|
||||||
sendAnnouncementViaJson :: Text -> Text -> Text -> IO ()
|
sendAnnouncementViaJson :: Text -> Text -> Text -> IO ()
|
||||||
sendAnnouncementViaJson hook fieldName message = do
|
sendAnnouncementViaJson hook fieldName message = do
|
||||||
|
@ -195,6 +195,7 @@ Course
|
|||||||
name Text
|
name Text
|
||||||
code Text
|
code Text
|
||||||
closed Bool
|
closed Bool
|
||||||
|
announcementHook Text Maybe
|
||||||
UniqueCourseName name
|
UniqueCourseName name
|
||||||
UniqueCourseCode code
|
UniqueCourseCode code
|
||||||
ExtraPoints
|
ExtraPoints
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
/course/#Text CourseR GET
|
/course/#Text CourseR GET
|
||||||
|
|
||||||
/test-announcements TestAnnouncementsR GET
|
/test-announcements TestAnnouncementsR GET
|
||||||
|
/test-challenge-announcements/#Text TestChallengeAnnouncementsR GET
|
||||||
|
|
||||||
/api/list-challenges ListChallengesJsonR GET
|
/api/list-challenges ListChallengesJsonR GET
|
||||||
/api/leaderboard/#Text LeaderboardJsonR GET
|
/api/leaderboard/#Text LeaderboardJsonR GET
|
||||||
|
@ -109,3 +109,4 @@ NoTests: SOMETHING IS WRONG WITH THE CHALLENGE, THERE ARE NO TESTS DEFINED. MAYB
|
|||||||
TestAnnouncements: test announcements
|
TestAnnouncements: test announcements
|
||||||
Color: color name or hex value
|
Color: color name or hex value
|
||||||
Phase: competition phase (use a pre-existing tag)
|
Phase: competition phase (use a pre-existing tag)
|
||||||
|
TestChallengeAnnouncements: Test announcements
|
||||||
|
@ -13,6 +13,8 @@ $if (checkIfAdmin mUserEnt)
|
|||||||
<button>_{MsgArchive}
|
<button>_{MsgArchive}
|
||||||
<form method=get action=@{ChallengeUpdateR challengeId}#form enctype="text/plain">
|
<form method=get action=@{ChallengeUpdateR challengeId}#form enctype="text/plain">
|
||||||
<button>_{MsgUpdate}
|
<button>_{MsgUpdate}
|
||||||
|
<form method=get action=@{TestChallengeAnnouncementsR (challengeName challenge)}#form enctype="text/plain">
|
||||||
|
<button>_{MsgTestChallengeAnnouncements}
|
||||||
|
|
||||||
$if (challengeArchived challenge == Just True)
|
$if (challengeArchived challenge == Just True)
|
||||||
<form method=post action=@{UnarchiveR challengeId}#form enctype="text/plain">
|
<form method=post action=@{UnarchiveR challengeId}#form enctype="text/plain">
|
||||||
|
1
templates/test-challenge-announcements.hamlet
Normal file
1
templates/test-challenge-announcements.hamlet
Normal file
@ -0,0 +1 @@
|
|||||||
|
<p>Activated!
|
Loading…
Reference in New Issue
Block a user