feat(courses): add NotificationCourseRegistered

This commit is contained in:
Gregor Kleen 2019-10-08 16:20:40 +02:00
parent 64b391a0fe
commit 3750da81dc
16 changed files with 124 additions and 19 deletions

View File

@ -777,6 +777,11 @@ MailSubmissionRatedIntro courseName@Text termDesc@Text: Ihre Abgabe im Kurs #{co
MailSubjectSheetActive csh@CourseShorthand sheetName@SheetName: #{sheetName} in #{csh} wurde herausgegeben MailSubjectSheetActive csh@CourseShorthand sheetName@SheetName: #{sheetName} in #{csh} wurde herausgegeben
MailSheetActiveIntro courseName@Text termDesc@Text sheetName@SheetName: Sie können nun #{sheetName} im Kurs #{courseName} (#{termDesc}) herunterladen. MailSheetActiveIntro courseName@Text termDesc@Text sheetName@SheetName: Sie können nun #{sheetName} im Kurs #{courseName} (#{termDesc}) herunterladen.
MailSubjectCourseRegistered csh@CourseShorthand: Sie wurden zu #{csh} angemeldet
MailSubjectCourseRegisteredOther displayName@Text csh@CourseShorthand: #{displayName} wurde zu #{csh} angemeldet
MailCourseRegisteredIntro courseName@Text termDesc@Text: Sie wurden im Kurs #{courseName} (#{termDesc}) angemeldet.
MailCourseRegisteredIntroOther displayName@Text courseName@Text termDesc@Text: #{displayName} wurde im Kurs #{courseName} (#{termDesc}) angemeldet.
MailSubjectExamResult csh@CourseShorthand examn@ExamName: Ergebnisse für #{examn} in #{csh} wurden herausgegeben MailSubjectExamResult csh@CourseShorthand examn@ExamName: Ergebnisse für #{examn} in #{csh} wurden herausgegeben
MailExamResultIntro courseName@Text termDesc@Text examn@ExamName: Sie können nun Ihr Ergebnis für #{examn} im Kurs #{courseName} (#{termDesc}) einsehen. MailExamResultIntro courseName@Text termDesc@Text examn@ExamName: Sie können nun Ihr Ergebnis für #{examn} im Kurs #{courseName} (#{termDesc}) einsehen.
@ -829,7 +834,7 @@ InvitationAcceptDecline: Einladung annehmen/ablehnen
InvitationFromTip displayName@Text: Sie erhalten diese Einladung, weil #{displayName} ihren Versand in Uni2work ausgelöst hat. InvitationFromTip displayName@Text: Sie erhalten diese Einladung, weil #{displayName} ihren Versand in Uni2work ausgelöst hat.
InvitationUniWorXTip: Uni2work ist ein webbasiertes Lehrverwaltungssystem der LMU München. InvitationUniWorXTip: Uni2work ist ein webbasiertes Lehrverwaltungssystem der LMU München.
MailSubjectParticipantInvitation tid@TermId ssh@SchoolId csh@CourseShorthand: [#{tid}-#{ssh}-#{csh}] Einladung zum Kursteilname MailSubjectParticipantInvitation tid@TermId ssh@SchoolId csh@CourseShorthand: [#{tid}-#{ssh}-#{csh}] Einladung zur Kursteilnahme
MailSubjectCorrectorInvitation tid@TermId ssh@SchoolId csh@CourseShorthand shn@SheetName: [#{tid}-#{ssh}-#{csh}] Einladung zum Korrektor für #{shn} MailSubjectCorrectorInvitation tid@TermId ssh@SchoolId csh@CourseShorthand shn@SheetName: [#{tid}-#{ssh}-#{csh}] Einladung zum Korrektor für #{shn}
@ -909,12 +914,14 @@ NotificationTriggerAllocationUnratedApplications: Bewertungen zu Zentralanmeldun
NotificationTriggerAllocationResults: Plätze wurden für eine meiner Zentralanmeldungen verteilt NotificationTriggerAllocationResults: Plätze wurden für eine meiner Zentralanmeldungen verteilt
NotificationTriggerExamOfficeExamResults: Ich kann neue Prüfungsergebnisse einsehen NotificationTriggerExamOfficeExamResults: Ich kann neue Prüfungsergebnisse einsehen
NotificationTriggerExamOfficeExamResultsChanged: Prüfungsergebnisse wurden verändert NotificationTriggerExamOfficeExamResultsChanged: Prüfungsergebnisse wurden verändert
NotificationTriggerCourseRegistered: Ein Kursverwalter hat mich zu einem Kurs angemeldet
NotificationTriggerKindAll: Für alle Benutzer NotificationTriggerKindAll: Für alle Benutzer
NotificationTriggerKindCourseParticipant: Für Kursteilnehmer NotificationTriggerKindCourseParticipant: Für Kursteilnehmer
NotificationTriggerKindExamParticipant: Für Prüfungsteilnehmer NotificationTriggerKindExamParticipant: Für Prüfungsteilnehmer
NotificationTriggerKindCorrector: Für Korrektoren NotificationTriggerKindCorrector: Für Korrektoren
NotificationTriggerKindLecturer: Für Dozenten NotificationTriggerKindLecturer: Für Dozenten
NotificationTriggerKindCourseLecturer: Für Kursverwalter
NotificationTriggerKindAdmin: Für Administratoren NotificationTriggerKindAdmin: Für Administratoren
NotificationTriggerKindExamOffice: Für das Prüfungsamt NotificationTriggerKindExamOffice: Für das Prüfungsamt
NotificationTriggerKindEvaluation: Für Vorlesungsumfragen NotificationTriggerKindEvaluation: Für Vorlesungsumfragen

View File

@ -169,10 +169,9 @@ addParticipantsResultMessages AddParticipantsResult{..} = execWriterT $ do
tell . pure <=< messageI Success . MsgCourseParticipantsRegistered $ length aurSuccess tell . pure <=< messageI Success . MsgCourseParticipantsRegistered $ length aurSuccess
registerUser :: (MonadHandler m, HandlerSite m ~ UniWorX, MonadCatch m) registerUser :: CourseId
=> CourseId
-> UserId -> UserId
-> WriterT AddParticipantsResult (ReaderT (YesodPersistBackend UniWorX) m) () -> WriterT AddParticipantsResult (YesodJobDB UniWorX) ()
registerUser cid uid = exceptT tell tell $ do registerUser cid uid = exceptT tell tell $ do
whenM (lift . lift . existsBy $ UniqueParticipant uid cid) $ whenM (lift . lift . existsBy $ UniqueParticipant uid cid) $
throwError $ mempty { aurAlreadyRegistered = Set.singleton uid } throwError $ mempty { aurAlreadyRegistered = Set.singleton uid }
@ -197,6 +196,7 @@ registerUser cid uid = exceptT tell tell $ do
, .. , ..
} }
lift . lift . audit $ TransactionCourseParticipantEdit cid uid lift . lift . audit $ TransactionCourseParticipantEdit cid uid
lift . lift . queueDBJob . JobQueueNotification $ NotificationCourseRegistered uid cid
return $ case courseParticipantField of return $ case courseParticipantField of
Nothing -> mempty { aurNoUniquePrimaryField = Set.singleton uid } Nothing -> mempty { aurNoUniquePrimaryField = Set.singleton uid }

View File

@ -16,6 +16,8 @@ import Text.Blaze.Html.Renderer.Text (renderHtml)
import Handler.Course.Register import Handler.Course.Register
import Jobs.Queue
getCUserR, postCUserR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDUser -> Handler Html getCUserR, postCUserR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDUser -> Handler Html
getCUserR = postCUserR getCUserR = postCUserR
@ -161,9 +163,10 @@ postCUserR tid ssh csh uCId = do
= Just featId = Just featId
| otherwise | otherwise
= Nothing = Nothing
pId <- runDB $ do pId <- runDBJobs $ do
pId <- insertUnique $ CourseParticipant cid uid now field Nothing pId <- insertUnique $ CourseParticipant cid uid now field Nothing
when (is _Just pId) $ when (is _Just pId) $ do
queueDBJob . JobQueueNotification $ NotificationCourseRegistered uid cid
audit $ TransactionCourseParticipantEdit cid uid audit $ TransactionCourseParticipantEdit cid uid
return pId return pId
case pId of case pId of

View File

@ -151,6 +151,7 @@ postEAddUserR tid ssh csh examn = do
, .. , ..
} }
lift . lift . audit $ TransactionCourseParticipantEdit cid uid lift . lift . audit $ TransactionCourseParticipantEdit cid uid
lift . lift . queueDBJob . JobQueueNotification $ NotificationCourseRegistered uid cid
lift $ lift examRegister lift $ lift examRegister
return $ case courseParticipantField of return $ case courseParticipantField of

View File

@ -18,6 +18,8 @@ import qualified Data.Set as Set
import Text.Hamlet (ihamlet) import Text.Hamlet (ihamlet)
import Data.Aeson hiding (Result(..)) import Data.Aeson hiding (Result(..))
import Jobs.Queue
instance IsInvitableJunction ExamRegistration where instance IsInvitableJunction ExamRegistration where
@ -98,6 +100,7 @@ examRegistrationInvitationConfig = InvitationConfig{..}
invitationInsertHook (Entity eid Exam{..}) _ ExamRegistration{..} mField act = do invitationInsertHook (Entity eid Exam{..}) _ ExamRegistration{..} mField act = do
whenIsJust mField $ \cpField -> do whenIsJust mField $ \cpField -> do
insert_ $ CourseParticipant examCourse examRegistrationUser examRegistrationTime cpField Nothing insert_ $ CourseParticipant examCourse examRegistrationUser examRegistrationTime cpField Nothing
queueDBJob . JobQueueNotification $ NotificationCourseRegistered examRegistrationUser examCourse
audit $ TransactionCourseParticipantEdit examCourse examRegistrationUser audit $ TransactionCourseParticipantEdit examCourse examRegistrationUser
let doAudit = audit $ TransactionExamRegister eid examRegistrationUser let doAudit = audit $ TransactionExamRegister eid examRegistrationUser

View File

@ -37,6 +37,8 @@ import Database.Persist.Sql (deleteWhereCount, updateWhereCount)
import Control.Lens.Indexed ((<.), (.>)) import Control.Lens.Indexed ((<.), (.>))
import Jobs.Queue
type ExamUserTableExpr = ( E.SqlExpr (Entity ExamRegistration) type ExamUserTableExpr = ( E.SqlExpr (Entity ExamRegistration)
`E.InnerJoin` E.SqlExpr (Entity User) `E.InnerJoin` E.SqlExpr (Entity User)
@ -744,6 +746,7 @@ postEUsersR tid ssh csh examn = do
, courseParticipantField = examUserCsvActCourseField , courseParticipantField = examUserCsvActCourseField
, courseParticipantAllocated = Nothing , courseParticipantAllocated = Nothing
} }
queueDBJob . JobQueueNotification $ NotificationCourseRegistered examUserCsvActUser examCourse
audit $ TransactionCourseParticipantEdit examCourse examUserCsvActUser audit $ TransactionCourseParticipantEdit examCourse examUserCsvActUser
insert_ ExamRegistration insert_ ExamRegistration
{ examRegistrationExam = eid { examRegistrationExam = eid

View File

@ -49,6 +49,7 @@ data NotificationTriggerKind
| NTKCourseParticipant | NTKCourseParticipant
| NTKExamParticipant | NTKExamParticipant
| NTKCorrector | NTKCorrector
| NTKCourseLecturer
| NTKAllocationStaff | NTKAllocationStaff
| NTKAllocationParticipant | NTKAllocationParticipant
| NTKFunctionary SchoolFunction | NTKFunctionary SchoolFunction
@ -61,6 +62,7 @@ instance RenderMessage UniWorX NotificationTriggerKind where
NTKCourseParticipant -> mr MsgNotificationTriggerKindCourseParticipant NTKCourseParticipant -> mr MsgNotificationTriggerKindCourseParticipant
NTKExamParticipant -> mr MsgNotificationTriggerKindExamParticipant NTKExamParticipant -> mr MsgNotificationTriggerKindExamParticipant
NTKCorrector -> mr MsgNotificationTriggerKindCorrector NTKCorrector -> mr MsgNotificationTriggerKindCorrector
NTKCourseLecturer -> mr MsgNotificationTriggerKindCourseLecturer
NTKAllocationStaff -> mr MsgNotificationTriggerKindAllocationStaff NTKAllocationStaff -> mr MsgNotificationTriggerKindAllocationStaff
NTKAllocationParticipant -> mr MsgNotificationTriggerKindAllocationParticipant NTKAllocationParticipant -> mr MsgNotificationTriggerKindAllocationParticipant
NTKFunctionary SchoolAdmin -> mr MsgNotificationTriggerKindAdmin NTKFunctionary SchoolAdmin -> mr MsgNotificationTriggerKindAdmin
@ -153,6 +155,10 @@ notificationForm template = wFormToAForm $ do
, NTKExamParticipant <- nt , NTKExamParticipant <- nt
= fmap not . E.selectExists . E.from $ \examRegistration -> = fmap not . E.selectExists . E.from $ \examRegistration ->
E.where_ $ examRegistration E.^. ExamRegistrationUser E.==. E.val uid E.where_ $ examRegistration E.^. ExamRegistrationUser E.==. E.val uid
| Just uid <- mbUid
, NTKCourseLecturer <- nt
= fmap not . E.selectExists . E.from $ \lecturer ->
E.where_ $ lecturer E.^. LecturerUser E.==. E.val uid
| otherwise | otherwise
= return False = return False
@ -176,9 +182,9 @@ notificationForm template = wFormToAForm $ do
NTSubmissionRated -> Just NTKCourseParticipant NTSubmissionRated -> Just NTKCourseParticipant
NTSheetActive -> Just NTKCourseParticipant NTSheetActive -> Just NTKCourseParticipant
NTSheetSoonInactive -> Just NTKCourseParticipant NTSheetSoonInactive -> Just NTKCourseParticipant
NTSheetInactive -> Just $ NTKFunctionary SchoolLecturer NTSheetInactive -> Just NTKCourseLecturer
NTCorrectionsAssigned -> Just NTKCorrector NTCorrectionsAssigned -> Just NTKCorrector
NTCorrectionsNotDistributed -> Just $ NTKFunctionary SchoolLecturer NTCorrectionsNotDistributed -> Just NTKCourseLecturer
NTUserRightsUpdate -> Just NTKAll NTUserRightsUpdate -> Just NTKAll
NTUserAuthModeUpdate -> Just NTKAll NTUserAuthModeUpdate -> Just NTKAll
NTExamRegistrationActive -> Just NTKCourseParticipant NTExamRegistrationActive -> Just NTKCourseParticipant
@ -193,6 +199,7 @@ notificationForm template = wFormToAForm $ do
NTAllocationResults -> Just NTKAllocationParticipant NTAllocationResults -> Just NTKAllocationParticipant
NTExamOfficeExamResults -> Just $ NTKFunctionary SchoolExamOffice NTExamOfficeExamResults -> Just $ NTKFunctionary SchoolExamOffice
NTExamOfficeExamResultsChanged -> Just $ NTKFunctionary SchoolExamOffice NTExamOfficeExamResultsChanged -> Just $ NTKFunctionary SchoolExamOffice
NTCourseRegistered -> Just NTKAll
-- _other -> Nothing -- _other -> Nothing
forcedTriggers = [NTUserRightsUpdate, NTUserAuthModeUpdate] forcedTriggers = [NTUserRightsUpdate, NTUserAuthModeUpdate]

View File

@ -134,7 +134,7 @@ data InvitationConfig junction = forall formCtx. InvitationConfig
-- ^ Additional restrictions to check before allowing an user to redeem an invitation token -- ^ Additional restrictions to check before allowing an user to redeem an invitation token
, invitationForm :: Entity (InvitationFor junction) -> InvitationData junction -> Key User -> AForm (YesodDB UniWorX) (InvitableJunction junction, formCtx) , invitationForm :: Entity (InvitationFor junction) -> InvitationData junction -> Key User -> AForm (YesodDB UniWorX) (InvitableJunction junction, formCtx)
-- ^ Assimilate the additional data entered by the redeeming user -- ^ Assimilate the additional data entered by the redeeming user
, invitationInsertHook :: forall a. Entity (InvitationFor junction) -> InvitationData junction -> junction -> formCtx -> (DB a -> DB a) , invitationInsertHook :: forall a. Entity (InvitationFor junction) -> InvitationData junction -> junction -> formCtx -> (YesodJobDB UniWorX a -> YesodJobDB UniWorX a)
-- ^ Perform additional actions before or after insertion of the junction into the database -- ^ Perform additional actions before or after insertion of the junction into the database
, invitationSuccessMsg :: Entity (InvitationFor junction) -> Entity junction -> DB (SomeMessage UniWorX) , invitationSuccessMsg :: Entity (InvitationFor junction) -> Entity junction -> DB (SomeMessage UniWorX)
-- ^ What to tell the redeeming user after accepting the invitation -- ^ What to tell the redeeming user after accepting the invitation
@ -368,8 +368,8 @@ invitationR' InvitationConfig{..} = liftHandler $ do
invitee <- requireAuthId invitee <- requireAuthId
cRoute <- fromMaybe (error "invitationR' called from 404-handler") <$> getCurrentRoute cRoute <- fromMaybe (error "invitationR' called from 404-handler") <$> getCurrentRoute
(tRoute, (dataWidget, dataEnctype), heading, explanation) <- runDB $ do (tRoute, (dataWidget, dataEnctype), heading, explanation) <- runDBJobs $ do
fEnt@(Entity fid _) <- invitationResolveFor itData >>= (\k -> Entity k <$> get404 k) fEnt@(Entity fid _) <- hoist lift (invitationResolveFor itData) >>= (\k -> Entity k <$> get404 k)
dbData <- case ephemeralInvitation @junction of dbData <- case ephemeralInvitation @junction of
Nothing -> do Nothing -> do
Invitation{..} <- entityVal <$> getBy404 (UniqueInvitation itEmail $ invRef @junction fid) Invitation{..} <- entityVal <$> getBy404 (UniqueInvitation itEmail $ invRef @junction fid)
@ -380,8 +380,8 @@ invitationR' InvitationConfig{..} = liftHandler $ do
let let
iData :: InvitationData junction iData :: InvitationData junction
iData = review _InvitationData (dbData, itData) iData = review _InvitationData (dbData, itData)
guardAuthResult =<< invitationRestriction fEnt iData guardAuthResult =<< hoist lift (invitationRestriction fEnt iData)
((dataRes, dataWidget), dataEnctype) <- runFormPost . formEmbedJwtPost . renderAForm FormStandard . wFormToAForm $ do ((dataRes, dataWidget), dataEnctype) <- hoist lift . runFormPost . formEmbedJwtPost . renderAForm FormStandard . wFormToAForm $ do
dataRes <- aFormToWForm $ invitationForm fEnt iData invitee dataRes <- aFormToWForm $ invitationForm fEnt iData invitee
btnRes <- aFormToWForm . disambiguateButtons $ combinedButtonField (BtnInviteAccept : [ BtnInviteDecline | is _Nothing $ ephemeralInvitation @junction ]) (fslI MsgInvitationAction & bool id (setTooltip MsgInvitationActionTip) (is _Nothing $ ephemeralInvitation @junction)) btnRes <- aFormToWForm . disambiguateButtons $ combinedButtonField (BtnInviteAccept : [ BtnInviteDecline | is _Nothing $ ephemeralInvitation @junction ]) (fslI MsgInvitationAction & bool id (setTooltip MsgInvitationActionTip) (is _Nothing $ ephemeralInvitation @junction))
case btnRes of case btnRes of
@ -390,8 +390,8 @@ invitationR' InvitationConfig{..} = liftHandler $ do
MsgRenderer mr <- getMsgRenderer MsgRenderer mr <- getMsgRenderer
ur <- getUrlRenderParams ur <- getUrlRenderParams
heading <- invitationHeading fEnt iData heading <- hoist lift $ invitationHeading fEnt iData
explanation <- (\ihtml -> ihtml (toHtml . mr) ur) <$> invitationExplanation fEnt iData explanation <- (\ihtml -> ihtml (toHtml . mr) ur) <$> hoist lift (invitationExplanation fEnt iData)
fmap (, (dataWidget, dataEnctype), heading, explanation) . formResultMaybe dataRes $ \case fmap (, (dataWidget, dataEnctype), heading, explanation) . formResultMaybe dataRes $ \case
Nothing -> do Nothing -> do
@ -405,8 +405,8 @@ invitationR' InvitationConfig{..} = liftHandler $ do
Nothing -> invalidArgsI [MsgInvitationCollision] Nothing -> invalidArgsI [MsgInvitationCollision]
Just res -> do Just res -> do
deleteBy . UniqueInvitation itEmail $ invRef @junction fid deleteBy . UniqueInvitation itEmail $ invRef @junction fid
addMessageI Success =<< invitationSuccessMsg fEnt res addMessageI Success =<< hoist lift (invitationSuccessMsg fEnt res)
Just <$> invitationUltDest fEnt res Just <$> hoist lift (invitationUltDest fEnt res)
whenIsJust tRoute redirect whenIsJust tRoute redirect

View File

@ -120,6 +120,8 @@ import Data.Dynamic
import qualified Data.Csv as Csv import qualified Data.Csv as Csv
import Jobs.Queue
#if MIN_VERSION_base(4,11,0) #if MIN_VERSION_base(4,11,0)
type Monoid' = Monoid type Monoid' = Monoid
@ -545,7 +547,7 @@ data DBTCsvDecode r' k' csv = forall route csvAction csvActionClass csvException
, dbtCsvComputeActions :: DBCsvDiff r' csv k' -> ConduitT () csvAction (YesodDB UniWorX) () , dbtCsvComputeActions :: DBCsvDiff r' csv k' -> ConduitT () csvAction (YesodDB UniWorX) ()
, dbtCsvClassifyAction :: csvAction -> csvActionClass , dbtCsvClassifyAction :: csvAction -> csvActionClass
, dbtCsvCoarsenActionClass :: csvActionClass -> DBCsvActionMode , dbtCsvCoarsenActionClass :: csvActionClass -> DBCsvActionMode
, dbtCsvExecuteActions :: ConduitT csvAction Void (YesodDB UniWorX) route , dbtCsvExecuteActions :: ConduitT csvAction Void (YesodJobDB UniWorX) route
, dbtCsvRenderKey :: Map k' r' -> csvAction -> Widget , dbtCsvRenderKey :: Map k' r' -> csvAction -> Widget
, dbtCsvRenderActionClass :: csvActionClass -> Widget , dbtCsvRenderActionClass :: csvActionClass -> Widget
, dbtCsvRenderException :: csvException -> YesodDB UniWorX Text , dbtCsvRenderException :: csvException -> YesodDB UniWorX Text
@ -1190,7 +1192,7 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
addMessageI Info MsgCsvImportAborted addMessageI Info MsgCsvImportAborted
redirect $ tblLink id redirect $ tblLink id
| otherwise -> FormSuccess $ do | otherwise -> FormSuccess $ do
finalDest <- runConduit $ C.sourceList acts .| dbtCsvExecuteActions finalDest <- runDBJobs' . runConduit $ C.sourceList acts .| dbtCsvExecuteActions
addMessageI Success . MsgCsvImportSuccessful $ length acts addMessageI Success . MsgCsvImportSuccessful $ length acts
E.transactionSave E.transactionSave
redirect finalDest redirect finalDest

View File

@ -210,6 +210,8 @@ determineNotificationCandidates NotificationAllocationResults{..} =
E.where_ $ isStudent E.||. isLecturer E.where_ $ isStudent E.||. isLecturer
return user return user
determineNotificationCandidates NotificationCourseRegistered{..} =
maybeToList <$> getEntity nUser
classifyNotification :: Notification -> DB NotificationTrigger classifyNotification :: Notification -> DB NotificationTrigger
@ -237,3 +239,4 @@ classifyNotification NotificationAllocationUnratedApplications{} = return NTAll
classifyNotification NotificationExamOfficeExamResults{} = return NTExamOfficeExamResults classifyNotification NotificationExamOfficeExamResults{} = return NTExamOfficeExamResults
classifyNotification NotificationExamOfficeExamResultsChanged{} = return NTExamOfficeExamResultsChanged classifyNotification NotificationExamOfficeExamResultsChanged{} = return NTExamOfficeExamResultsChanged
classifyNotification NotificationAllocationResults{} = return NTAllocationResults classifyNotification NotificationAllocationResults{} = return NTAllocationResults
classifyNotification NotificationCourseRegistered{} = return NTCourseRegistered

View File

@ -18,6 +18,7 @@ import Jobs.Handler.SendNotification.ExamActive
import Jobs.Handler.SendNotification.ExamResult import Jobs.Handler.SendNotification.ExamResult
import Jobs.Handler.SendNotification.Allocation import Jobs.Handler.SendNotification.Allocation
import Jobs.Handler.SendNotification.ExamOffice import Jobs.Handler.SendNotification.ExamOffice
import Jobs.Handler.SendNotification.CourseRegistered
dispatchJobSendNotification :: UserId -> Notification -> Handler () dispatchJobSendNotification :: UserId -> Notification -> Handler ()

View File

@ -0,0 +1,35 @@
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-} -- ihamletFile discards do results
module Jobs.Handler.SendNotification.CourseRegistered
( dispatchNotificationCourseRegistered
) where
import Import
import Handler.Utils.Mail
import Jobs.Handler.SendNotification.Utils
import Text.Hamlet
import qualified Data.CaseInsensitive as CI
dispatchNotificationCourseRegistered :: UserId -> CourseId -> UserId -> Handler ()
dispatchNotificationCourseRegistered nUser nCourse jRecipient = userMailT jRecipient $ do
(User{..}, Course{..}) <- liftHandler . runDB $ (,) <$> getJust nUser <*> getJust nCourse
let isSelf = nUser == jRecipient
replaceMailHeader "Auto-Submitted" $ Just "auto-generated"
setSubjectI $ if
| isSelf -> MsgMailSubjectCourseRegistered courseShorthand
| otherwise -> MsgMailSubjectCourseRegisteredOther userDisplayName courseShorthand
MsgRenderer mr <- getMailMsgRenderer
let termDesc = mr . ShortTermIdentifier $ unTermKey courseTerm
tid = courseTerm
ssh = courseSchool
csh = courseShorthand
editNotifications <- mkEditNotifications jRecipient
addAlternatives $
providePreferredAlternative ($(ihamletFile "templates/mail/courseRegistered.hamlet") :: HtmlUrlI18n UniWorXMessage (Route UniWorX))

View File

@ -4,6 +4,7 @@ module Jobs.Queue
, queueJob, queueJob' , queueJob, queueJob'
, YesodJobDB , YesodJobDB
, runDBJobs, queueDBJob, sinkDBJobs , runDBJobs, queueDBJob, sinkDBJobs
, runDBJobs'
, queueDBJobCron , queueDBJobCron
, module Jobs.Types , module Jobs.Types
) where ) where
@ -29,6 +30,8 @@ import qualified Data.Conduit.List as C
import Data.Semigroup ((<>)) import Data.Semigroup ((<>))
import UnliftIO.Concurrent (myThreadId) import UnliftIO.Concurrent (myThreadId)
import Control.Monad.Trans.Resource (register)
data JobQueueException = JobQueuePoolEmpty data JobQueueException = JobQueuePoolEmpty
@ -128,3 +131,17 @@ runDBJobs act = do
app <- getYesod app <- getYesod
forM_ jIds $ flip runReaderT app . writeJobCtl . JobCtlPerform forM_ jIds $ flip runReaderT app . writeJobCtl . JobCtlPerform
return ret return ret
runDBJobs' :: YesodJobDB UniWorX a -> DB a
runDBJobs' act = do
(ret, jIds) <- mapReaderT runWriterT act
void . liftHandler $ do
UnliftIO{..} <- askUnliftIO
register . unliftIO . runDB $
forM_ jIds $ \jId ->
whenM (existsKey jId) $
runReaderT (writeJobCtl $ JobCtlPerform jId) =<< getYesod
return ret

View File

@ -88,6 +88,7 @@ data Notification = NotificationSubmissionRated { nSubmission :: SubmissionId }
| NotificationExamOfficeExamResults { nExam :: ExamId } | NotificationExamOfficeExamResults { nExam :: ExamId }
| NotificationExamOfficeExamResultsChanged { nExamResults :: Set ExamResultId } | NotificationExamOfficeExamResultsChanged { nExamResults :: Set ExamResultId }
| NotificationAllocationResults { nAllocation :: AllocationId } | NotificationAllocationResults { nAllocation :: AllocationId }
| NotificationCourseRegistered { nUser :: UserId, nCourse :: CourseId }
deriving (Eq, Ord, Show, Read, Generic, Typeable) deriving (Eq, Ord, Show, Read, Generic, Typeable)
instance Hashable Job instance Hashable Job

View File

@ -43,6 +43,7 @@ data NotificationTrigger
| NTAllocationResults | NTAllocationResults
| NTExamOfficeExamResults | NTExamOfficeExamResults
| NTExamOfficeExamResultsChanged | NTExamOfficeExamResultsChanged
| NTCourseRegistered
deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable) deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable)
instance Universe NotificationTrigger instance Universe NotificationTrigger

View File

@ -0,0 +1,21 @@
$newline never
\<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<style>
h1 {
font-size: 1.25em;
font-variant: small-caps;
font-weight: normal;
}
<body>
<h1>
$if isSelf
_{MsgMailCourseRegisteredIntro (CI.original courseName) termDesc}
$else
_{MsgMailCourseRegisteredIntroOther userDisplayName (CI.original courseName) termDesc}
<p>
<a href=@{CourseR tid ssh csh CShowR}>
#{courseName}
^{editNotifications}